在 010073B2
地址处调用了 GetModuleHandleA
API,获取 notepad.exe
程序的 ImageBase
。然后在 010073B4
与 010073C0
地址处比较MZ与PE签名。希望各位熟记原 notepad.exe
的EP代码。
打开会提示被压缩了。不用管 继续打开即可
然后我们可以得到 UPX EP
代码
EP地址是 01015330
这里就是第二个节区的末端部分。实际压缩的 notepad
源代码存在于EP地址的上方
查看代码开始的地方
首先使用 PUSHAD
命令把所有通用寄存器 EAX-EDI
寄存器的值保存到栈,然后分别把第二个节区的起始地址 0x1011000
与第一个节区的起始地址(0x1001000)设置到ESI EDI寄存器。UPX文件的第一个节区仅存在于内存。该处即是解压缩后保存源文件代码的地方
调试时像这样同时设置ESI与EDI,就能预见从ESI所指缓冲区到EDI所指缓冲区的内存发生了复制。此时从Source(ESI)读取数据,解压缩后保存到Destination(EDI)。我们的目标是跟踪上图中的全部UPXEP代码,并最终找到原notepad的EP代码
请坚持一个法则“遇到循环(loop)时,先了解作用再退出”
整个解压缩过程由无数循环组成。因此,只有适当跳出循环才能加快速度。
命令 | 快捷键 | 说明 |
---|---|---|
Animate Into | Ctrl+F7 | 反复执行Step Into命令(画面显示) |
Animate Over | Ctrl+F8 | 反复执行Step Over命令(画面显示) |
Trace Into | Ctrl+F11 | 反复执行Step Into命令(画面不显示) |
Trace Over | Ctrl+F12 | 反复执行Step Over命令(画面不显示) |
由于 Animate
命令要把跟踪过程显示在画面中,所以执行速度略微慢一些。而两者最大的差别在于,跟踪命令会自动在事先设置的跟踪条件处停下来,并生成日志文件。 在UPX文件跟踪中将使用AnimateOver(Ctrl+F8)命令。
在EP代码处执行AnimateOver(Ctrl+F8)
命令,开始跟踪代码。可以看到光标快速上下移动。若想停止跟踪,执行StepInto(F7)
命令即可。
开始跟踪代码不久后,会遇到一个短循环。暂停跟踪,仔细查看相应循环,如图所示。
在循环第一处命令下断点,重新运行到这里。可以看到循环次数是 36B
,循环内容是“从EDX中读取一个字节写入EDI”
这里EDI EDX 对应的地址就是第一个节区 UPX0
的起始地址,仅存在于内存汇总的节区(反正内容全部为NULL)
调试运行时压缩的文件时,遇到这样的循环就应该跳出来。在循环结束后第一条命令处 010153e6
下断点、然后f9跳出循环
然后继续 AnimateOver(Ctrl+F8)
命令跟踪代码,然后又会遇到循环
这个循环会比之前的要大一些,这个循环就是正式的码循环(或称为解压缩循环)
分析代码:先从ESI所指的第二个节区UPX1地址中依次读取值,经过适当的运算解压缩后,将值写入EDI所指的第一个节区UPX0地址。
UPX1:0101534B mov [edi], al
UPX1:0101534D inc edi
...
UPX1:010153E0 mov [edi], al
UPX1:010153E2 inc edi
...
UPX1:010153F1 mov [edi], eax
UPX1:010153F3 add edi, 4
然后我们还是在循环后第一处指令下断点 然后跳出循环,
此时解压缩后的代码已经被写入第一个节区 UPX0
区域(01007000),即原来用NULL填充的区域
重新跟踪代码就会遇到第三个循环
该段循环代码用于恢复源代码的CALL/JMP
指令(操作码:E8/E9)的destination
地址。在01015436
地址处设置断点运行后即可跳出循环。
到此几乎接近尾声了,只要再设置好IAT,UPX解压缩代码就结束了。
对于普通的运行时压缩文件,源文件代码、数据、资源解压缩之后,先设置好IAT再转到OEP。
继续跟踪 遇到下一个循环
这部分就是设置 IAT
的循环。在 01015436
地址处设置 EDI=01014000
,它指向第二个节区(UPX1)区域,该区域中保存着原 notepad.exe
调用的API函数名称的字符串
如图是 API名称字符串
UPX压缩原notepad.exe
文件时,它会分析其IAT,提取出程序中调用的API名称列表,形成API名称字符串。
用这些API名称字符串调用01015467
地址处的GetProcAddress
函数,获取API的起始地址,然后把API地址输人EBX寄存器所指的原notepad.exe
的IAT区域。该过程会反复进行至API名称字符串结束,最终恢复原notepad.exe
的IAT。
notepad.exe
全部解压缩完成后,应该将程序的控制返回到OEP处。下图显示的就是跳转到OEP的代码。
另外,010154AD
地址处的 POPAD
命令与UPX代码的第一条 PUSHAD
命令对应,用来把当前寄存器恢复原状
最终,使用 010154BB
地址处的JMP命令跳转到OEP处,要跳转到的目标地址为 0100739D
,它就是原 notepad.exe
的EP地址。
与没有加壳的EP代码相同
这里以UPX压缩文件为例
UPX压缩器的特征之一是,其EP代码被包含在 PUSHAD/POPAD
指令之间。并且,跳转到OEP代码的JMP指令紧接着出现在POPAD指令之后。只要在JMP指令处设置好断点,运行后就能直接找到OEP
PUSHAD指令将8个通用寄存器(EAX~EDI)的值保存到栈。
POPAD指令把PUSHAD命令存储在栈的值再次恢复到各个寄存器。
该方法也利用UPX的 PUSHAD/POPAD
指令的特点。在执行 01015330
地址处的 PUSHAD
命令后,查看栈
EAX到EDI寄存器的值依次被存储到栈。从OllyDbg的Dump窗口进人栈地址(000DFF54
)。将鼠标光标准确定位到 000DFF54
地址,使用鼠标右键菜单设置硬件断点
硬件断点是CPU支持的断点,最多可以设置4个。与普通断点不同的是,设置断点的指令执行完成后才暂停调试。在这种状态下运行,程序就会边解压缩边执行代码,在执行 POPAD
的瞬间访问设置有硬件断点的 0006FFA4
地址,然后暂停调试。其下方即是跳转到OEP的JMP指令(熟悉该方法的操作原理才能在以后调试各种文件时得心应手)