notepad.exe的EP代码

![E3V@J82N[A0DX1F49IUDL0.png
在010073B2地址处调用了GetModuleHandleA() API,获取notepad.exe程序的ImageBase。然后在010073B4与10073C0地址处比较MZ与PE签名。

notepad_upx.exe的EP代码

使用OD打开notepadupx.exe时弹出警告消息框。
![ECBNBYB2S%1)N4
%FXZAAH.png](https://cdn.nlark.com/yuque/0/2020/png/554486/1583397457596-21b05bf0-1eaf-42d3-b7df-4a7b91e7d483.png#align=left&display=inline&height=176&name=ECBNBYB2S%251%29N4_%25FXZA%60AH.png&originHeight=176&originWidth=335&size=42150&status=done&style=none&width=335)<br />调试器判断该文件为压缩文件,在“是”与“否”中任选一个,显示出UPX EP代码,如图所示。<br />![}8WPJPUQPCWU8U{NFF)ZFOG.png](https://cdn.nlark.com/yuque/0/2020/png/554486/1583397570973-9555f5a4-a83a-4690-9e5c-cb10df960301.png#align=left&display=inline&height=496&name=%7D8WPJPUQPCWU8U%7BNFF%29ZFOG.png&originHeight=496&originWidth=525&size=172309&status=done&style=none&width=525)<br />Ep地址为01015330,该处即为第二个节区的末端部分。实际压缩的notepad源代码存在于EP地址(01015330)的上方。<br />代码的开始部分(01015330)。<br />![EGM4N_HP8UQJEDB]YC[@$Y.png](https://cdn.nlark.com/yuque/0/2020/png/554486/1583397726904-66d12730-fbfe-45a8-97bf-600df1847855.png#align=left&display=inline&height=54&name=EGM4N_HP8UQJEDB%5DYC%5B%60%40%24Y.png&originHeight=54&originWidth=727&size=39538&status=done&style=none&width=727)
首先使用PUSHAD命令将EAX-EDI寄存器的值保存到栈,然后分别把第二个节区的起始地址(01011000)与第一个节区的起始地址(01001000)设置到ESI与EDI寄存器。UPX文件第一个节区仅存在于内存。该处即是解压缩后保存源文件代码的地方。
调试时像这样同时设置ESI与EDI,就能遇见从ESI所指缓冲区到EDI所指缓冲区的内存发生了复制。此时从Source(ESI)读取数据,解压缩后保存到Destination(EDI)。我们的目标是跟踪图15-3中全部UPX EP代码,并最终找到原notepad的EP代码如图15-1所示。
提示: 代码逆向分析称源文件的EP为OEP。
“跟踪”一词的含义是通过逐一分析代码进行跟踪。
实际的代码逆向分析中并不会逐一跟踪执行压缩代码,常使用自动化脚本、特殊技巧等找到OEP。

跟踪UPX文件

跟踪数量庞大的代码时,要遵循如下法则。
“遇到循环(loop)时,先了解作用再跳出。”
整个解压过程由无数循环组成。因此,只有适当跳出循环才能加快速度。

OD的跟踪命令

跟踪数量庞大的代码时,通常不会使用Step Into(F7)命令,而使用OD中另外提供的跟踪调试命令,如表所示。
Z_ALJGF0IHHU6I639@(6)6T.png
除了画面显示的之外,Animate命令与跟踪命令是类似的,由于Animate命令要把跟踪过程显示在画面中,所以执行速度略微慢一些。而两者最大差别在于,跟踪命令会自动在事先设置的跟踪条件处停下来,并生成日志文件。在UPX文件跟踪中将使用Animate Over(Ctrl+F8)命令。

循环#1

在EP代码处执行Animate Over命令,开始跟踪代码。可以看到光标快速上下移动。
若想停止跟踪,执行Step Into(F7)命令即可。
开始跟踪代码不久后,会遇到一个短循环。暂停跟踪,仔细查看相应循环,如图15-4显示。
![H3UR[)LJ524O3EUUA2W}}B.png](https://cdn.nlark.com/yuque/0/2020/png/554486/1583399934963-cf68ffba-578f-4cb3-8be4-7f4e4e4246d0.png#align=left&display=inline&height=349&name=H3UR%5B%29LJ524O3EUUA2W%7D%7DB.png&originHeight=349&originWidth=594&size=171347&status=done&style=none&width=594)
循环次数ECX=36B,循环内容为“从EDX(01001000)中读取一个字节写入EDI(01001001)”。EDI寄存器所
指的01001000地址是第一个节区(UPX0)的起始地址,仅存在于内存中的节区(反正内容全部为NULL)。
调试经过运行时压缩的文件时,遇到这样的循环应该跳出来。在010153E6地址处按F2键设置好断点后,按F9跳出循环。

循环#2

在断点处再次使用Animate Over(Ctrl+F8)命令继续跟踪代码,不久后遇到图示循环。
6G6G$0}FRPPN$E}86MT]VRC.png
该循环是正式的解码循环(或称为解压缩循环)。
先从ESI所指的第二个节区(UPX1)地址中依次读取值,经过适当的运算解压缩后,将值写入EDI所指的第一个节区(UPX0)地址。该过程中使用的指令如下;
ZGZO]P2{5X7]~X`ET5I6~N1.png
只要在01015402地址处设置好断点再运行,即可跳出得个循环,如图15-5所示。运行到01015402地址后,在转储窗口中可以看到解压缩后的代码已经被写入第一个节区(UPX0)区域(01007000),如图15-5中原来用NULL填充的区域。

循环#3

重新跟踪代码稍后会遇到图示的第三个循环。
]{TG_0%`B6$728A])VEN)9L.png
该段循环代码用于恢复源代码的CALL/JMP指令(操作码:E8/E9)的destination地址。在01015436地址处设置断点运行后即可跳出循环。
提示:对于普通的运行时压缩文件,源文件代码、数据、资源解压缩后,先设置好IAT再转到OEP。

循环#4

重新跟踪代码,再稍微进行一段。
图15-7深色显示的部分即为设置IAT的循环。在01015436地址处设置EDI=01014000,它指向第二个节区(UPX1)区域,该区域中保存着原notepad.exe调用的API函数名称的字符串(参考图15-8)。
S{XJN2P8@SI2MB3LZS7`6CL.png
W[FCQUO`7~9_B%A$F[@{2~L.png](https://cdn.nlark.com/yuque/0/2020/png/554486/1583403466288-b4d90c1b-8303-4581-aa69-9d329a0c96d8.png#align=left&display=inline&height=210&name=W%5BFCQUO%607~9_B%25A%24F%5B%40%7B2~L.png&originHeight=210&originWidth=517&size=121209&status=done&style=none&width=517)<br />UPX压缩原notepad.exe文件时,它会分析其IAT,提取出程序中调用的API名称列表,形成API名称字符串。<br />用这些API名称字符串调用图15-7中01015467地址处GetProcAddress()函数,获取API的起始地址,然偶后把API地址输入EBX寄存器所指的原notepad.exe的IAT区域,该过程会反复进行至API名称字符串结束,最终恢复原notepad.exe的IAT。<br />notepad.exe全部解压缩完成后,应该将程序的控制返回到OEP处,图示即为跳转到OEP的代码。<br />![X8_]ZFT(WCYL~](4T_Y)%Z4.png
另外,010154AD地址处的POPAD命令与UPX代码的第一条PUSHAD命令对应,用来把当前寄存器恢复原状(参考图15-3)。
最终,使用010154BB地址处的JMP命令跳转到OEP处,要跳转到的目标地址为0100739D,它就是原notepad.exe的EP地址。

快速查找UPX OEP的方法

实际代码逆向分析中有一些更简单的方法可以找到OEP(以UPX压缩的文件为例)。

在POPAD指令后的JMP指令处设置断点

UPX压缩器的特征之一是,其EP代码被包含在PUSHAD/POPAD指令之间。并且跳转到OEP代码的JMP指令紧接着出现在POPAD指令之后。只要在JMP指令处设置好断点,运行后就能直接找到OEP。
提示: PUSHAD指令将8个通用寄存器(EAX~EDI)的值保存到栈。
POPAD指令把PUSHAD命令存储在栈的值再次恢复到各个寄存器。

在栈中设置硬件断点

该方法也利用UPX的PUSHAD/POPAD指令的特点。在图15-3中执行01015330地址处的PUSHAD命令后,查看栈,如图15-10所示。
![UW1)FMFUPMFSU$@%C3}G.png
EAX到EDI寄存器的值依次被存储到栈。从OD的Dump窗口进入栈地址(006FFA4)。将鼠标光标准确定位到6FFA4地址,使用鼠标右键菜单设置硬件断点,如图15-11所示。
3I24ZS6$IP{(Z5CD~26AB4E.png
硬件断点是CPU支持的断点,最多可设置4个。与普通断点不同的是,设置断点的指令执行完成后才暂停调试。在这种状态下运行,程序就会边解压缩边执行代码,在执行POPAD的瞬间访问设置有硬件断点的0006FFA4地址,然后暂停调试。其下方即是跳转到OEP的JMP指令。