第 6 章:识别汇编中的 C 代码结构

Lab 6-1

在这个实验中,你将分析在文件 Lab06-01.exe 中发现的恶意代码。

1、由 main 函数调用的唯一子过程中发现的主要代码结构是什么?

答案:首先进行基础静态分析。

二话不说,先上 VirusTotal。OK,无毒。其 Behavior 的说明为 “Network Communication”,也即存在网络通信行为:

PEiD 表明是 VC 6.0 编译链接。使用 Dependency Walker 查看导入表。注意到 wininet.dll 中的 InternetGetConeectedState,该函数的作用是获得本地系统的网络连接状态(Retrieves the connected state of the local system)。

当然,wininet.dll 本身还提供了很多其他联网的功能。

再解析下字符串。除了各种 API 函数外,还注意到包含 “Error 1.1L No Internet” 和 “Success: Internet Connection” 等字符串,推测该程序会检测系统中是否存在可用的 Internet 连接。

接下来进行基础动态分析。

打开 Process Monitor,关闭捕获,清空历史,开启捕获,添加过滤规则 Process Name is Lab06-01.exe。在命令行运行,打印字符串 “Success: Internet Connection” 后就关闭了。Process Explorer 里没有任何驻留的进程或服务。Process Monitor 里也没有什么异样的信息。

再进行高级静态分析。

使用 IDA Pro 打开 Lab06-01.exe。main 函数的起始地址是 00401040。

该函数主要调用了函数 sub_401000(上图粉色)。双击点开如下图。可以看到根据 InternetGetConnectedState 的结果来进行分支。

切换成图模式看得更直观一些。猜测含义是,若存在网络连接,则将字符串 “Sucess: ...” 传给 sub_40105F,若不存在,则将字符串 “Error: ...” 传给 sub_40105F。由于我们之前运行过该程序,运行结果是打印出了上面的字符串,因此我们推测 0x40105F 的作用是打印字符串。

2、位于 0x40105F 的子过程是什么?

在前一问中我们推测该子过程的作用是打印字符串。更详细的分析可以看参考链接中的说明。

能深入 C 源码分析其和汇编代码的相似性,固然最好。如果一时半会找不到,也不用担心,我们应当具备高层次的猜测和抽象思维,从而更快地分析出程序的意图。

3、这个程序的目的是什么?

该函数会检查是否存在一个可用的 Internet 连接,如果存在,打印结果并返回 1, 否则返回 0。恶意代码经常做类似于这样的检查以确定是否可以联网 。

参考链接

Lab 6-2

分析在文件 Lab06-02.exe 中发现的恶意代码。

VirusTotal 报告,报毒。有访问远程主机的恶意行为:

PEiD 显示为 VC 6.0 编译链接。Dependency Walker 显示调用了 wininet.dll,其中导入函数均为联网相关操作。

用 IDA Pro 打开,切换到 Strings 窗口。可以看到很多和联网相关的字符串:

运行 Lab06-02.exe,得到 “Error 2.1: Fail to OpenUrl”。可能是虚拟机的网络配置问题。并且 netcat 也没有监听到书中所说的报文。

1、main 函数调用的第一个子过程执行了什么操作?

答案:首先查看一下 main 函数(红色)的结构。调用的第一个子过程为 sub_401000(粉色)。

图模式看得更清晰一些:

点开 sub_401000,和 Lab 6-1 的很像,所以第一个子过程应该是判断本地系统是否有网络连接,有就打印 “Sucess…” 并返回 1,没有就打印 “Error…” 并返回 0。

2、位于 0x40117F 的子过程是什么?

答案:看着眼熟,实际上在 Lab 6-1 中已经分析过,该子过程作用是打印字符串,相当于 printf。

3、被 main 函数调用的第二个子过程做了什么?

答案:第 2 个子过程是 sub_401040。该子过程中调用了:

  • InternetOpen(粉色):初始化应用程序对 WinINet 函数的使用,“Internet Explorer 7.5/pma”

  • InternetOpenUrl(蓝色):打开静态网页,“http://www.practicalmalwareanalysis.com/cc.htm”

  • printf(绿色):打印出错信息,“Error 2.1: Fail to OpenUrl\n”

  • InternetCloseHandle(橙色):关闭一个网络句柄

  • InternetReadFile(绿色):从前面 InternetOpenUrl 打开的句柄中读取数据

继续往下看,粉色部分标出了程序在比较字符串是否为 “<!--”(在原本的十六进制数上按下 R 键切换显示成对应的 ASCII 字符),也就是 HTML 代码中的注释开头。

同时注意到 Buffer(蓝色),以及后面的几个 var_*(青色),事实上 var_* 应当是一个偏移量,但是 IDA Pro 没有识别出来 Buffer 是 512 字节。

在函数中的任意位置,Edit > Functions >Stack variables(Ctrl+K),可以看到栈变量中的数据。

在 Buffer 出右键,Array,设置 Array size 为 512,可以看到栈变成了下面的内容,这样就能看出有意义的参数信息了。

原来的 var_* 也自动变成了 Buffer 加上一个偏移量。所以图中这段内容就是比较 Buffer[0:3] 的内容是否为注释开头 “<!--”,如果是,则将 Buffer[4] 的内容写入 al。

接着回到主函数中 sub_401040 处,继续向下执行,可以看到 al 的值赋给了 eax,并判断 eax 是否为 0,若非 0(也即 Buffer[4] 的字符有意义),则跳到 loc_40115C。然后打印 eax 对应的字符 “Success: Parsed command is %c\n”,其中 “%c” 就是 Buffer[4] 转换得到的字符。最后休眠(Sleep)0EA60h=60000 毫秒,也即 60 秒。

最后总结一下,该程序检查是否有一个可用的 Internet 连接,然后下载一个包含 “<!--” 的网页,这是 HTML 注释的开头部分,在网页浏览器中 HTML 注释不会被显示,但可以通过查看 HTML 页面源码看到。这种通过 HTML 注释隐藏指令的方法经常被攻击者用于向恶意代码发送指令,这让恶意代码看起来就像是在访问一个正常网页 。

4、在这个子过程中使用了什么类型的代码结构?

答案:字符数组。

5、在这个程序中有任何基于网络的指示吗?

答案:有。

  • InternetOpen 中 使用 User-Agent:“Internet Explorer 7.5/pma”

  • InternetOpenUrl 从远程主机下载文件:http://www.practicalmalwareanalysis.com/cc.htm

6、这个恶意代码的目的是什么?

答案:程序首先判断是否存在一个可用的 Internet 连接,如果不存在就终止运行,如果存在,则使用一个独特的用户代理尝试下载一个网页。该网页包含了一段由 “<!--” 开始的 HTML 注释,程序解析之后的那个字符,并打印 “Success: Parsedcommand is %c\n”,其中 %c 就是从该字符。如果解析成功,程序会休眠 60 秒,然后终止运行。

参考链接

Lab 6-3

在这个实验中,我们会分析在文件 Lab06-03.exe 中发现的恶意代码。

先一波静态分析。

VirusTotal,报毒,依旧是向远程主机请求下载文件:

Dependency Walker 看一下导入函数,wininet.dll 中有 InternetGetConnectedState、InternetOpen、InternetOpenUrl、InternetReadFile 和 InternetCloseHandle,同 Lab 6-2 类似。多了一个 advapi32.dll,有 RegOpenKeyEx 和 RegSetValueEx,有写注册表操作。

用 IDA Pro 打开,查看 Strings,除了 Lab 6-2 中出现的和网络请求相关的部分,还多出了注册表和命令:

  • Software\Microsoft\Windows\CurrentVersion\Run

  • C:\Temp\cc.exe

  • C:\Temp

猜测可能要读写注册表,并执行远程下载的恶意程序。

1、比较在 main 函数与实验 6-2 的 main 函数的调用。从 main 中调用的新的函数是什么?

答案:其他基本一致,只出现了一个新函数 sub_401130(粉色)。

2、这个新的函数使用的参数是什么?

答案:传入的第一个参数是 char 类型,其实就是此前读出的 HTML 字符。第二个参数是指向文件名字符串的指针(实际上是标准 main 函数的 argv[0],也即该程序自己的文件名):

3、这个函数包含的主要代码结构是什么?

答案:双击进入函数,进一步查看。arg_0 是 IDA 自动生成的标签,表示第一个参数(或者说,最后一个压入栈中的参数)。粉色的两行语句表示将 arg_0 的值赋给 var_8。绿色的前 3 行语句表示将 var_8 自减 61h(对应 ASCII 字符 ‘a’),第 4~5 行表示,若该字符减 ‘a’ 大于 4(非 ‘a’、‘b’、‘c’、‘d’、‘e’),则跳到 loc_4011E1(打印 “Error 3.2 …” 并终止 ),否则,将该值赋给 edx,进入 switch 语句。

切换到图模式看得更直观一些(粉色即为 switch 的判断语句):

off_4011F2 对应一张跳转表(‘a’~‘e’ 的分支),加上 loc_4011E1(default 分支),共有 6 个分支,和上图橙色部分正好对应。

4、这个函数能够做什么?

答案:函数的重点是整个 switch 语句的作用。下面逐一剖析。

case 0,也就是字符为 ‘a’。调用 CreateDirectory 创建了一个文件夹 “C:\Temp”。

case 1,也就是字符串为 ‘b’。调用 CopyFile 复制文件:源文件是 lpExistingFileName,前文提过是 argv[0],也即该程序自己的文件名 “Lab06-03.exe”;目标文件是是 “C:\Temp\cc.exe”。也即该分支将 Lab06-03.exe 复制到 C:\Temp\cc.exe。

case 2,也就是字符串为 ‘c’。调用 DeleteFile 删除文件,由于 Data 的内容即为 “C:\Temp\cc.exe”(见上图),因此该分支会删除 C:\Temp\cc.exe。

case 3,也就是字符串为 ‘d’。看上去复杂,其实也就两个函数。首先调用 RegOpenKeyEx 打开注册表键 “Software\Microsoft\Windows\CurrentVersion\Run”,然后再在该键下创建一个新的键 “...\Malware”,其值为 “C:\Temp\cc.exe”。这样系统启动时,如果 C:\Temp\cc.exe 存在,则也会跟随系统启动,自动运行。

case 4,也就是字符串为 ‘e’。调用 Sleep 休眠 186A0h=100000 毫秒,也即 100 秒。

default case,也就是字符串非 ‘a’~‘d’。调用 sub_401271,打印字符串 “Error 3.2: Not a valid command provided”。

5、在这个恶意代码中有什么本地特征吗?

答案:注册表键 Software\Microsoft\Windows\CurrentVersion\Run\Malware 和本地文件 C:\Temp\cc.exe。

6、这个恶意代码的目的是什么?

答案:该程序先检查是否存在有效的 Internet 连接。如果找不到,程序直接终止。否则,该程序会尝试下载一个网页,该网页包含了一段以 “<!--” 开头的 HTML 注释。该注释的第一个字符被用于 switch 语句来决定程序在本地系统运行的下一步行为,包括是否删除一个文件、创建一个目录、设置一个注册表 run 键、复制一个文件或者休眠 100 秒。

Lab 6-4

在这个实验中,我们会分析在文件 Lab06-04.exe 中发现的恶意代码。

静态分析一波。

VirusTotal 报告,报毒:

IDA 中查看 Strings,发现和 Lab06-03.exe 中有一句新增的字符串,“Internet Explorer 7.50/pma%d”。

1、在实验 6-3 和 6-4 的 main 函数中的调用之间的区别是什么?

答案:用 IDA 打开,文本模式查看好像很多函数都和 Lab 6-3 很类似,不过用图模式打开,就可以看到 Lab 6-4 中有一个明显的循环结构(最左边蓝色向上的箭头)。

该 for 循环语句的文本模式如下图。当 eax 超过 5A0h=1440 时,跳出循环:

2、什么新的代码结构已经被添加到 main 中?

答案:main 函数中添加了 for 循环语句。

3、这个实验的解析 HTML 的函数和前面实验中的那些有什么区别?

答案:注意绿色部分,在调用 fetch_url 函数前 push 进栈的最后一个变量是 ecx,而 ecx 的值来自 [ebp+var_C],再往上看,[ebp+var_C] 的值来自 eax,也即 ecx = [ebp+var_C] = eax。从第 1 问中我们也知道,eax 每次循环自增 1,所以其实 ecx 也就是计数器的数字。

双击进入 fetch_url,注意其中的 _sprintf 函数(粉色),以及第二个参数(格式化字符串)和第三个参数(字符)。

sprintf 函数的格式如下。可以看到第一个参数是指向存储 C-string 缓冲区的指针,第二个参数为格式化字符串,后面的参数是参与格式化的字符,在上图中,只有 “%d” 这一个格式,所以只有第三个参数,也即 eax。从上图橙色部分可以看出,eax = [ebp+arg_0],而 arg_0 的值即为上上图中 fetch_url 函数上一行里最后一个压入栈中的 ecx,也即计数器的数字。所以这里的 “%d” 实际上就被替换成计数器的数字了。

// int sprintf ( char * str, const char * format, ... );
char buffer [?];
int eax;
n = sprintf (buffer, "Internet Explorer 7.50/pma%d", eax);
// printf ("[%s] ... %d ...", buffer, eax)

4、这个程序会运行多久?(假设它已经连接到互联网。)

答案:由前面的分析可知,要循环 5A0h = 1440 次,每次循环除了调用正常的函数(比如 switch 的六个分支)外,最后还要休眠 0EA60h = 60000 毫秒,也即一分钟。所以这个程序会运行 1440 分钟 = 24 小时。

5、在这个恶意代码中有什么新的基于网络的迹象吗?

答案:对 User-Agent 进行了改造,内容是 “Internet Explorer 7.50/pma%d”,其中 %d 是 for 循环的计数器数字,其实也相当于程序已经运行的分钟数。

6、这个恶意代码的目的是什么?

答案:类似 Lab 6-3,只不过多了一个 for 循环了 1440 次的语句,并且每次 InternetOpen 的 User-Agent 都会和计数器的数字相关。

Last updated