

在 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指令(熟悉该方法的操作原理才能在以后调试各种文件时得心应手)
