第 6 章:识别汇编中的 C 代码结构
Last updated
Last updated
在这个实验中,你将分析在文件 Lab06-01.exe 中发现的恶意代码。
答案:首先进行基础静态分析。
二话不说,先上 VirusTotal。OK,无毒。其 Behavior 的说明为 “Network Communication”,也即存在网络通信行为:
PEiD 表明是 VC 6.0 编译链接。使用 Dependency Walker 查看导入表。注意到 wininet.dll 中的 InternetGetConeectedState,该函数的作用是获得本地系统的网络连接状态(Retrieves the connected state of the local system)。
InternetGetConnectedState function (wininet.h) - Win32 apps | Microsoft Docs
当然,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 的作用是打印字符串。
在前一问中我们推测该子过程的作用是打印字符串。更详细的分析可以看参考链接中的说明。
能深入 C 源码分析其和汇编代码的相似性,固然最好。如果一时半会找不到,也不用担心,我们应当具备高层次的猜测和抽象思维,从而更快地分析出程序的意图。
该函数会检查是否存在一个可用的 Internet 连接,如果存在,打印结果并返回 1, 否则返回 0。恶意代码经常做类似于这样的检查以确定是否可以联网 。
恶意代码分析实战 Lab 6-1 习题笔记_isinstance的博客-CSDN博客
分析在文件 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 也没有监听到书中所说的报文。
答案:首先查看一下 main 函数(红色)的结构。调用的第一个子过程为 sub_401000(粉色)。
图模式看得更清晰一些:
点开 sub_401000,和 Lab 6-1 的很像,所以第一个子过程应该是判断本地系统是否有网络连接,有就打印 “Sucess…” 并返回 1,没有就打印 “Error…” 并返回 0。
答案:看着眼熟,实际上在 Lab 6-1 中已经分析过,该子过程作用是打印字符串,相当于 printf。
答案:第 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 注释隐藏指令的方法经常被攻击者用于向恶意代码发送指令,这让恶意代码看起来就像是在访问一个正常网页 。
答案:字符数组。
答案:有。
InternetOpen 中 使用 User-Agent:“Internet Explorer 7.5/pma”
InternetOpenUrl 从远程主机下载文件:http://www.practicalmalwareanalysis.com/cc.htm
答案:程序首先判断是否存在一个可用的 Internet 连接,如果不存在就终止运行,如果存在,则使用一个独特的用户代理尝试下载一个网页。该网页包含了一段由 “<!--” 开始的 HTML 注释,程序解析之后的那个字符,并打印 “Success: Parsedcommand is %c\n”,其中 %c 就是从该字符。如果解析成功,程序会休眠 60 秒,然后终止运行。
恶意代码分析实战 Lab 6-2 习题笔记_isinstance的博客-CSDN博客
在这个实验中,我们会分析在文件 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
猜测可能要读写注册表,并执行远程下载的恶意程序。
答案:其他基本一致,只出现了一个新函数 sub_401130(粉色)。
答案:传入的第一个参数是 char 类型,其实就是此前读出的 HTML 字符。第二个参数是指向文件名字符串的指针(实际上是标准 main 函数的 argv[0],也即该程序自己的文件名):
答案:双击进入函数,进一步查看。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 个分支,和上图橙色部分正好对应。
答案:函数的重点是整个 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”。
答案:注册表键 Software\Microsoft\Windows\CurrentVersion\Run\Malware 和本地文件 C:\Temp\cc.exe。
答案:该程序先检查是否存在有效的 Internet 连接。如果找不到,程序直接终止。否则,该程序会尝试下载一个网页,该网页包含了一段以 “<!--” 开头的 HTML 注释。该注释的第一个字符被用于 switch 语句来决定程序在本地系统运行的下一步行为,包括是否删除一个文件、创建一个目录、设置一个注册表 run 键、复制一个文件或者休眠 100 秒。
在这个实验中,我们会分析在文件 Lab06-04.exe 中发现的恶意代码。
静态分析一波。
VirusTotal 报告,报毒:
IDA 中查看 Strings,发现和 Lab06-03.exe 中有一句新增的字符串,“Internet Explorer 7.50/pma%d”。
答案:用 IDA 打开,文本模式查看好像很多函数都和 Lab 6-3 很类似,不过用图模式打开,就可以看到 Lab 6-4 中有一个明显的循环结构(最左边蓝色向上的箭头)。
该 for 循环语句的文本模式如下图。当 eax 超过 5A0h=1440 时,跳出循环:
答案:main 函数中添加了 for 循环语句。
答案:注意绿色部分,在调用 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” 实际上就被替换成计数器的数字了。
sprintf - C++ Reference
答案:由前面的分析可知,要循环 5A0h = 1440 次,每次循环除了调用正常的函数(比如 switch 的六个分支)外,最后还要休眠 0EA60h = 60000 毫秒,也即一分钟。所以这个程序会运行 1440 分钟 = 24 小时。
答案:对 User-Agent 进行了改造,内容是 “Internet Explorer 7.50/pma%d”,其中 %d 是 for 循环的计数器数字,其实也相当于程序已经运行的分钟数。
答案:类似 Lab 6-3,只不过多了一个 for 循环了 1440 次的语句,并且每次 InternetOpen 的 User-Agent 都会和计数器的数字相关。