图一 :一个典型的调试网络
调试系统中Trojan有意向侵入的任何操作系统应当是全新安装的。在网络中存在第三方的系统可以使你仿效这些服务并能截取这些由Trojan产生的网络数据。与跟踪感染病毒的源头相比截取这些数据非常微不足道的。确信你的防火墙能阻挡所有与外界的联系,并只允许Trojan的控制连接。如果你不想让主控机,探测到你的试验网络在运行Trojan,你可以建立起运行木马所需要的资源模拟服务器,例如IRC或者FTP/TFTP服务器。
单步执行代码
因为我们已经建立一个恰当隔离的试验环境,我们就可以开始调试代码了。利用代码清单,在程序中寻找关键函数,例如Winsock以及I/O调用。调试器允许我们基于偏移量在程序中设置断点,因此我们可以打断程序流并察看当前点的程序的存储以及CPU的寄存器。文章的剩余部分将介绍在Linux平台下如何来实现调试的一个例子。
运行调试器
我们想知道木马如何与它的主控者如何联系的。经常地,监视网络传输就足够了。然而,许多新的木马则是以密文的形式来进行网络传输的,这就使得网络监视失去作用。然而,利用些小聪明,我们在信息编码前,就从内存中截取它们。通过在传输库调用“send”上设置断点,我们就能实现在数据包在传输前,打断代码执行。然后,通过跟踪堆栈指针,我们就可推测程序执行到哪里了。例如,木马的源程序也许如下:
/* encrypt output to master */
elen = encrypt(crypted,buf,len);
/* write crypted output to socket */
send(s, crypted, elen, 0);
在gdb中检测被编译的木马也许可给我们下面的结果[注意下面的粗体字陈述,代表作者对输出的评论]
[test@debugger test]$ gdb ./Trojan
GNU gdb 5.2.1-2mdk (Mandrake Linux)
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of
it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty"
for details.
This GDB was configured as "i586-mandrake-linux-gnu"...
(no debugging symbols found)...
(gdb) set disassembly-flavor intel [Switch syntax output from AT&T]
(gdb) b send [Set a breakpoint on the "send" library call]
Breakpoint 1 at 0x400f5c10
(gdb) run
Starting program: /home/test/Trojan
Breakpoint 1, 0x400f5c10 in send () [We hit a breakpoint]
(gdb) where [Do a stack trace to see where we are at in the program]
#0 0x400f5c10 in send () from /lib/i686/libc.so.6
#1 0x080487fa in socket ()
#2 0x40040082 in __libc_start_main () from /lib/i686/libc.so.6
在gdb中显示偏移量每个子函数的执行后返回的结果就如上面所示。我们知道“send”调用正好是在编码调用之后,因此我们只需检测前面的子函数,它的返回值包含了偏移量为0x080487fa内存器的内容。我们感兴趣仅是这个偏移量前面的汇编代码。利用gdb,我们可以在当前反汇编这些代码。
(gdb) disas 0x080487d2 0x080487fa
Dump of assembler code from 0x80487d2 to 0x80487fa:
0x80487d2 <socket+622>: call 0x8048804 <socket+672>
0x80487d7 <socket+627>: add esp,0x10
0x80487da <socket+630>: mov DWORD PTR [ebp-836],eax
0x80487e0 <socket+636>: push 0x0
0x80487e2 <socket+638>: push DWORD PTR [ebp-836]
0x80487e8 <socket+644>: lea eax,[ebp-824]
0x80487ee <socket+650>: push eax
0x80487ef <socket+651>: push DWORD PTR [ebp-828]
0x80487f5 <socket+657>: call 0x8048534 <send>
End of assembler dump.
我们看到正好在调用“send”前,有一个对0x8048804<socket+672>的调用。实际上这就是,我们需要找的编码子函数。当程序销毁它们的符号时,gdb就不能辨别出子程序开始和结束的地方了,因此它持续利用它最后识别的所有子程序的名称,通常是前面的动态连接库调用。在这种情况下,它就有可能标错“socket”函数的开始部分。
为了探测未编码的数据包的内容,我们仅需要知道这个调用函数是如何工作的。子函数的变量被压入堆栈段中,即存放临时数据和返回地址的内存。我们能通过在调用处设置断点来获得它所存储的变量内容,然后利用CPU的寄存器ESP知道堆栈指针的偏移量。ESP+4指向第一个变量,ESP+8指向第二个变量,ESP+12指向第三个变量,依次类推。就这样搜索堆栈直止有用的信息出现。在这种情况下,有用的信息(文本数据)就是编码的变量。让我们在这个编码调用设置一个断点,并检查堆栈。
(gdb) b * 0x80487d2 [Set a breakpoint on the "encrypt" call]
Breakpoint 2 at 0x80487d2
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/test/Trojan
(no debugging symbols found)...
Breakpoint 2, 0x080487d2 in socket ()
(gdb) x/x $esp+8 [Get the offset of the second argument ESP+8]
0xbffff5e4: 0x0806fe20
(gdb) x/fs 0x0806fe20 [Examine the contents of the memory at 0x0806fe20]
0x806fe20: "root pts/0 Oct 11 14:22\n"
从这个输出我们能看到木马正在向登陆这个系统的主机汇报。当然,它能传输任何数据;网络数据包,键盘输入记录等等。幸运的是,我们使用已建立的网络改变数据传输的方向。
结论
上面的木马是虚假的。假如是实际中的木马,我们也许就要增加一些操作。通常木马将建立例如IRC这样的渠道来与主控者联系。我们能利用这个事实,来跟踪攻击的来源,如果木马编写者稍有疏忽,甚至获得木马主控的整个网络的控制权。如果木马利用FTP来更新自己,你就应当在FTP服务器上搜索一些其他的代码,来获得木马编写者的线索。
尽管我们已经熟悉了逆向工程的基本步骤,你应当能汲取上面的信息并付诸实践。察看你的调试器的帮助文件;你就会发现它多么有用,可以学到很多有用的知识;即使你对汇编代码一点都不熟悉。如果它看上去很困难,但绝不要放弃希望。付出总会有回报的。在一个实际逆向工程工作中,作者发现了木马编写者的姓名,并不是有意嵌入在程序的源代码中(注意:不要在你的NT工作站在运行时,利用VB来编写木马程序)。利用搜索引擎GOOGLE来查找作者的email,就会出现VB程序的论坛网站。然后就知道他是谁,以及他的家庭地址,以及电话号码。在巴西的某个地方,木马的编写者拍着自己的前额用葡萄牙语说DOH。
关于作者
Joe Stewart是LURHQ公司的一个高级信息安全分析师,住在南Carrolina的Myrtle Beach的安全服务提供者。
参考文献
Fravia's Pages of Reverse Engineering
Detecting and Containing IRC-Controlled Trojans: When Firewalls, AV, and IDS Are
Not Enough
Reverse Engineering Malware