【技术分享】使用FireEye实验室的“面向查询的调试器”研究程序的动态状态

https://p0.ssl.qhimg.com/t01be8f8a3abfa75972.png

译者:She11c0d3@XDSEC

预估稿费:200RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


介绍

本文是FireEye实验室高级逆向工程(FLARE,FireEye Labs Advanced Reverse Engineering)脚本系列的续作。

flare-qdb是一款用Python编写的命令行工具,使用Vivisect在应用程序上设定断点,并在断点触发时执行回调代码。flare-qdb旨在给逆向工程师提供一种快速的、方便的途径,来对应用程序的执行环境进行大规模的、半自动或全自动化的修改。flare-qdb可以在Windows或者Linux上工作,你可以在我们的Github页面上查看更多信息。


初衷

要对复杂的或精心混淆过的恶意软件进行分析,动态调试是必不可少的一步。在调试过程中,单纯的按照指令流向进行分析,往往会对多线程执行、寄存器修改、循环内状态或是一些特定指令的执行情况。比如,工程师可能会忽视某个寄存器的值,而当他注意到这个值是已经执行过的某个函数的输入参数,而这个函数又对逆向过程起着关键的作用,就为时已晚了。如果要从头开始整个调试过程,或对结果做详细的日志记录,无疑会打断分析者的思路。

因此,分析人员需要注意所有指令或函数,并判断这些指令到底是对执行过程具有关键性的影响,还是仅仅会成为分析过程中的一个小插曲。错误的决定,往往意味着浪费时间。因此,如果我们像查询数据库一样,查询软件执行过程中的状态,那就方便多了,如:

sql
SELECT eax, poi(ebp-0x14) FROM malware.exe WHERE eip = 0x401072

因此,我们开发了一款命令行工具,能够高效的进行这样的查询。下面的介绍将为您展示,这款工具如何在恶意软件逆向、场景重建以及CTF比赛中应用。

用法

flare-qdb的基础用法是这样的:

flareqdb "命令行参数" -at 地址 "python命令"

分析人员可以随心所欲的选择,他们是想执行命令、修改PC数值、检查条件,还是显示程序执行状态。flare-qdb自身提供了一些函数,可以用来修改执行状态。如:

向kernel32!Beep函数传递的两个DWORD参数是什么?

-at kernel32.Beep –eval "dd('esp+4', 2)"

如果执行0x401072时eax为0,则终止程序

-at 0x401072 -if eax==0 -eval "kill()"

在特定地址处,自动修改ecx的值

-at malwaremodule+0x102a -eval "r('ecx', '(ebp-0x14)*eax')

在特定地址处,自动修改某个内存的值

-at 0x401003 -eval "memset('ebp-0x14', 0x2a, 4)"

命令行调用

flare-qdb的一种用法是,直接在命令行中运行。比如,当程序中某个循环涉及到字符串时,flare-qdb会很有用。下图展示了这个工具如何在函数的每一次迭代中,将栈上的一个字符串dump出来。从输出可以推测,这个变量应该指向将要处理的字符串,这个字符串的来源是程序的首个参数(即argv[1])。

https://p4.ssl.qhimg.com/t0184e9531527e8555b.png

下面的这个例子是2016年FLARE-On Challenge的第四题(警告:还没做的最好别看,想看完整解法的看这里)。在flareon2016challenge.dll这个文件中,一个解码后的PE文件有一连串对kernel32!Beep的调用。分析人员需要按顺序截获这些调用,从而找到正确的flag。下图就是如何用一条命令完成这些工作。

https://p0.ssl.qhimg.com/t015b8de27ede6a0c3f.png

flareqdb还可以强制执行某个分支,对函数指针解引用,或验证反汇编中找到的可疑函数是否有意义。以下图中的程序为例,这个程序只有在满足一些条件之后才会去调用一个C++虚函数,而了解这个虚函数是什么,或许能帮助分析人员确定这个函数的调用者,并了解攻击者的指令服务器向肉鸡发送了什么数据。

https://p2.ssl.qhimg.com/t016966b404d1997f03.png

我们使用flareqdb,就可以将前面的判断过程绕过,并逐步跳转到被0x4029A4处执行的函数指针。由于有了vivisect的协助,flareqdb可以将这块地址中的指令反编译,来验证这里存放的到底是不是一个函数,如下图所示。控制流从0x4016b5被强制定向到0x4016bb,之后将0x4029a4处的指令dump出来。

https://p3.ssl.qhimg.com/t01f335eca5a5fd92b3.png

结合下图中IDA反编译的结果,我们可以发现,这个函数是`basic_streambuf::xputn`,这个函数会向文件中插入数个字符。这也意味着,CC下发的可能是一个写文件的命令。

https://p4.ssl.qhimg.com/t01b7952c8bef236617.png

在Python中使用flareqdb模块

flareqdb还可作为一个python模块被调用,从而在更复杂的场合中发挥他的威力。下图是某个提权工具的一部分,这个工具检查了GetVersionExWNetWkstaGetInfoIsWow64Process函数的返回,之后通过WMI,利用CVE-2016-0040进行提权。

https://p5.ssl.qhimg.com/t0194d0a190145b214a.png

看起来这个工具可以在版本号为5.1、6.0或6.1的32位Windows上运行。下图则演示了一个脚本,通过反复的执行这个工具,模拟GetVersionExW、NetWkstaGetInfo的返回值,检查程序是否执行到了指定位置,来判断这个工具在哪些版本的Windows中可用。脚本将版本信息等参数传递给Qdb的实例,这些参数会被用于回调两个用来修改返回值的参数。其中,GetVersionExW的返回值使用OSVERSIONINFOEXW结构体的定义修改,而NetWkstaGetInfo的返回值则用WKSTA_INFO_100结构体中的内容手动修改。

https://p0.ssl.qhimg.com/t0172ff9b40e76b8f8f.png

下图的输出刚好验证了我们的判断:

https://p3.ssl.qhimg.com/t011eddc47fef719df7.png

下一个例子则较为复杂。工程师需要将某个二进制文件多次脱壳,并探明脱壳后的文件镜像被注入到了内存的何处,下图的脚本则会帮助完成这个工作。脚本在脱壳后清理现场的调用处设置断电,并使用vivisect的envi模块来检查内存中有哪些被设置为RWX权限的内存区域不是由文件映射而来的。之后,在调用`detach()`之前,脚本调用了qdb内置的`park`函数,从而让文件陷入死循环。这样,分析人员就可以附加上调试器,并继续分析了。

https://p5.ssl.qhimg.com/t0108129cbed8cdd707.png

下图展示了这个脚本运行的情况:

https://p2.ssl.qhimg.com/t018f82d0e8df87ec59.png

图11中可以看到,用WinDbg+IDA Pro挂载到待调试的程序后,可以发现它已经在死循环中了。qdb还将旧的EIP保存下来,以便继续分析。

https://p2.ssl.qhimg.com/t01c3d488cb978cb651.png

被这样终止的程序,我们称其为处于“空转”状态的程序。这时候,我们很方便的对恶意软件的执行情况做一个快照,也可以多次附加到该程序来检查和标注代码。此外,因为同一个操作系统进程可以被多个调试会话多次跟踪,而在多个调试器中,由Qdb所执行脚本申请的内存是不变的。因此,在IDA Pro中为这段代码做的标记仍然会保存。而如果像之前一样多次重复执行一个程序的话,由于VirtualAlloc返回的内存地址不同,标注也会被丢掉。

总结

如果你不想让被调试进程停止正常运行,那么flare-qdb这一命令行工具能够让你在这一前提下,快速探明二进制程序的执行状态。此外,这一工具还可以对程序的运行状态进行更改,并模拟新的运行环境。在进行一些复杂的研究工作时,flare-qdb通过提供一个脚本环境,让研究人员可以任意伪造程序的运行环境。在字符串解码、恶意软件解包,和一些通用的软件分析工作中,这一功能可以说是很重要的。

你可以到flare-qdb的Github页面中了解这一工具的更多信息。

(完)