VMProtect分析
看了forgot,bughoho,somuch的分析文章,获益菲浅,不过这都是理论基础,要实际分析一个真正的程序时,这么做的话要累死人的。我分析了一个程序,主switch跑了至少几万次啊,看的我头晕眼花,于是想实际分析程序应该不是这样的,就找API,断到到自己怀疑的地方的时候,再慢慢分析,可也跟的很辛苦,从网上看到这篇文章,觉得非常好,省了很大的劲,幸亏比较短,所以翻译了下,要不就放弃了。
与大家分享, 交流是目的。
概述
这个有害软件是个木马下载器,今天的分析重点不在程序本身的逻辑,而在看这个文件是怎么针对逆向工程被保护的。这个文件是被VMProtect保护的,这个保护把汇编码变成伪码。本二进制文件中嵌入了一个VM来解释新创建的代码,反汇编器就没有用了,因为她不支持Virtual CPU指令。VMProtect用了个非常复杂的VM,分析她很费时间。VM在每个被保护的文件中是不同的,以至于在分析这个文件得出的结果在以后别的分析用不上。因为输入表没有被搞乱,所以我用另外一个方法来分析代码。你将发现我用一个OD的脚本来在每个输入的API设断。简单分析IAT。然后就会发现很容易搞清楚程序流程和她到底干了什么。
技术细节
如果我们用个PE Editor来查看文件,就会看到一个带vmp的段,这就提示了我们的文件是被VMProtect保护了的。用IDA Pro来反汇编,我们看到程序开始的是汇编码,但又不是纯汇编,显然是故意搞乱的。不过能看到一个重要信息,本可执行文件的ImageBase是0x13140000。
程序入口的汇编:
稍微懂点汇编的人都能看出程序不是干净的,就象我前面说过的,我要用另外个技术来跟这个例子程序背后的流程和理解代码逻辑。看一下IAT,看到几个感兴趣的API,这个程序很有可能干了些什么,她们给了我们很多信息。
CreateRemoteThread, WriteProcessMemory 。。。等,我们不知道哪个进程或dll被插入了,或在汇编的基础上加了一块,或是用什么其他别的什么技术在远程进程加了代码。为了找出来,我做了个OD脚本来扫描IAT,并且在每个输入的API上设断。IAT从13142000开始。
////////////////////////////////////////////////////
var IATstart
var IATslots
mov IATstart, 13142000 // IAT start address
mov IATslots, 15 // how many apis to handle.
breakpoint_loop:
cmp [IATstart],0 // don't set breakpoint at nulls between IID.
je skip_breakpoint
bp [IATstart]
skip_breakpoint:
add IATstart, 4
dec IATslots
cmp IATslots, 0
jne breakpoint_loop
/////////////////////////////////////////////////////
现在,我们用OD运行程序,到入口时,跑这个脚本,他会给我们设断。
我们可能已经在GetProcAddress 和 LoadLibraryA加了断点。(可是有害软件可能会模拟这2个API,请尽量在VMWare上或一个空的机器上调试有害软件)
F9,看看程序是怎么断的,得到:
0012FFBC 13145D45 /CALL to GetModuleHandleA
0012FFC0 00000000 \pModule = NULL
0012FFC4 7C816D4F RETURN to kernel32.7C816D4F
程序调了几次GetModuleHandleA,没发现什么感兴趣的。
0012FFB8 13145DB7 /CALL to FindWindowA
0012FFBC 13141152 |Class = "shell_traywnd"
0012FFC0 00000000 \Title = NULL
0012FFC4 7C816D4F RETURN to kernel32.7C816D4F
0012FFC8 7C920738 ntdll.7C920738
然后在类Shell_traywnd里调了FindWindow,类Shell_traywnd属于explorer.exe,提示:这个文件可能往explorer.exe里插入代码。
0012FFB8 13145F29 /CALL to GetWindowThreadProcessId
0012FFBC 00050054 |hWnd = 00050054 (class='Shell_TrayWnd')
0012FFC0 13141350 \pProcessID = show.13141350
0012FFC4 7C816D4F RETURN to kernel32.7C816D4F
然后用GetWindowThreadProcessID得到一些进程信息,handle是前面FindWindowA返回的。
0012FFB4 1314610B /CALL to OpenProcess
0012FFB8 001F0FFF |Access = PROCESS_ALL_ACCESS
0012FFBC 00000000 |Inheritable = FALSE
0012FFC0 000001CC \ProcessId = 1CC
打开进程,权限PROCESS_ALL_ACCESS,然后你会看到VirtualAllocEx分配了一块内存,地址为13140000
0012FFAC 13145E23 call to VirtualAllocEx
0012FFB0 00000048
0012FFB4 13140000 show.13140000 // Address of allocation.
0012FFB8 00008000
0012FFBC 00003000
0012FFC0 00000040
注意:
必须注意,我们程序想在explorer.exe里分配内存,用他自己的ImageBase做VirtualAllocEx的参数。我们程序有个故意弄的不标准的ImageBase,这么做的背后目的是如果插入程序能分配同样地址的内存,他就能把自己直接拷贝到远程进程。什么意思?简单说就是被插入代码不需要在原地址上加上个增量来加代码,不需要重定位。输入表在插入代码IAT已经解决了,这样当被插入到远程进程时,输入表还是对的。终于,这次内存分配后,调WriteProcessMemory写入8000h到explorer.exe的地址13140000。
0012FFAC 13145EB6 /CALL to WriteProcessMemory
0012FFB0 00000048 |hProcess = 00000048 (window)
0012FFB4 13140000 |Address = 13140000
0012FFB8 13140000 |Buffer = show.13140000
0012FFBC 00008000 |BytesToWrite = 8000 (32768.) ; writes 8000 bytes
0012FFC0 13141354 \pBytesWritten = show.13141354
0012FFC4 7C816D4F RETURN to kernel32.7C816D4F
最后,调CreateRemoteThread来执行被插入代码。CreateRemoteThread后的地址是13141168。
0012FFA4 13145D39 show.13145D39
0012FFA8 00000048
0012FFAC 00000000
0012FFB0 00000000
0012FFB4 13141168 show.13141168 // Start address of the remote
thread==> the interesting code.
0012FFB8 13140000 show.13140000
0012FFBC 00000000
0012FFC0 13141358 show.13141358
看下内存空间,
能看到程序逻辑很简单,被插入代码从删除插入原程序开始,为的是毁灭罪证。
然后,他从网上下了另外一个可执行程序,放到Windows目录下,这个新建的程序就执行了。
Outro(这个实在不知道是什么地方的英语土话,就当是最后吼2嗓子吧):
我希望这个小文章能给大家些分析复杂的被搞乱的程序的主意,这个例子比较简单,但这些技术会被用到不同的场合的。
原文:
http://www.websense.com/securitylabs/blog/blog.php?BlogID=77