15.调试UPX压缩的notepad程序

Pasted image 20250321123820

1. 原程序的EP代码

Pasted image 20250319214032
010073B2 地址处调用了 GetModuleHandleA API,获取 notepad.exe 程序的 ImageBase。然后在 010073B4010073C0 地址处比较MZ与PE签名。希望各位熟记原 notepad.exe 的EP代码。

2. UPX压缩后的EP代码

打开会提示被压缩了。不用管 继续打开即可
然后我们可以得到 UPX EP 代码
Pasted image 20250319214426
EP地址是 01015330 这里就是第二个节区的末端部分。实际压缩的 notepad 源代码存在于EP地址的上方

查看代码开始的地方
Pasted image 20250319214614
首先使用 PUSHAD 命令把所有通用寄存器 EAX-EDI 寄存器的值保存到栈,然后分别把第二个节区的起始地址 0x1011000 与第一个节区的起始地址(0x1001000)设置到ESI EDI寄存器。UPX文件的第一个节区仅存在于内存。该处即是解压缩后保存源文件代码的地方

调试时像这样同时设置ESI与EDI,就能预见从ESI所指缓冲区到EDI所指缓冲区的内存发生了复制。此时从Source(ESI)读取数据,解压缩后保存到Destination(EDI)。我们的目标是跟踪上图中的全部UPXEP代码,并最终找到原notepad的EP代码

Tip
  • 代码逆向分析称源文件的EP为OEP。
  • “跟踪”一词的含义是通过逐一分析代码进行追踪。
  • ·实际的代码逆向分析中并不会逐一跟踪执行压缩代码,常使用自动化脚本、特殊技巧等找到OEP。但是对于初次学习运行时压缩文件的朋友而言,逐一跟踪代码才是正确的学习方法。

3. 跟踪UPX文件

请坚持一个法则“遇到循环(loop)时,先了解作用再退出”

整个解压缩过程由无数循环组成。因此,只有适当跳出循环才能加快速度。

3.1. OllyDbg的跟踪命令

命令 快捷键 说明
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)命令。

3.2. 循环 #1

在EP代码处执行AnimateOver(Ctrl+F8)命令,开始跟踪代码。可以看到光标快速上下移动。若想停止跟踪,执行StepInto(F7)命令即可。
开始跟踪代码不久后,会遇到一个短循环。暂停跟踪,仔细查看相应循环,如图所示。
Pasted image 20250319220520
在循环第一处命令下断点,重新运行到这里。可以看到循环次数是 36B,循环内容是“从EDX中读取一个字节写入EDI”
这里EDI EDX 对应的地址就是第一个节区 UPX0 的起始地址,仅存在于内存汇总的节区(反正内容全部为NULL)
调试运行时压缩的文件时,遇到这样的循环就应该跳出来。在循环结束后第一条命令处 010153e6 下断点、然后f9跳出循环
Pasted image 20250319221238

3.3. 循环 #2

然后继续 AnimateOver(Ctrl+F8) 命令跟踪代码,然后又会遇到循环
Pasted image 20250319221745
这个循环会比之前的要大一些,这个循环就是正式的码循环(或称为解压缩循环)
分析代码:先从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

然后我们还是在循环后第一处指令下断点 然后跳出循环,
Pasted image 20250319222844
此时解压缩后的代码已经被写入第一个节区 UPX0 区域(01007000),即原来用NULL填充的区域
Pasted image 20250319223358
Pasted image 20250319223420

3.4. 循环 #3

重新跟踪代码就会遇到第三个循环
Pasted image 20250319223708
该段循环代码用于恢复源代码的CALL/JMP指令(操作码:E8/E9)的destination地址。在01015436地址处设置断点运行后即可跳出循环。
到此几乎接近尾声了,只要再设置好IAT,UPX解压缩代码就结束了。

Tip

对于普通的运行时压缩文件,源文件代码、数据、资源解压缩之后,先设置好IAT再转到OEP。

3.5. 循环 #4

继续跟踪 遇到下一个循环
Pasted image 20250319224100
这部分就是设置 IAT 的循环。在 01015436 地址处设置 EDI=01014000,它指向第二个节区(UPX1)区域,该区域中保存着原 notepad.exe 调用的API函数名称的字符串
如图是 API名称字符串 Pasted image 20250319224259
UPX压缩原notepad.exe文件时,它会分析其IAT,提取出程序中调用的API名称列表,形成API名称字符串。
用这些API名称字符串调用01015467地址处的GetProcAddress函数,获取API的起始地址,然后把API地址输人EBX寄存器所指的原notepad.exe的IAT区域。该过程会反复进行至API名称字符串结束,最终恢复原notepad.exe的IAT。
notepad.exe 全部解压缩完成后,应该将程序的控制返回到OEP处。下图显示的就是跳转到OEP的代码。
Pasted image 20250319224622
另外,010154AD 地址处的 POPAD 命令与UPX代码的第一条 PUSHAD 命令对应,用来把当前寄存器恢复原状
最终,使用 010154BB 地址处的JMP命令跳转到OEP处,要跳转到的目标地址为 0100739D,它就是原 notepad.exe 的EP地址。
Pasted image 20250319224800
与没有加壳的EP代码相同

3.6. 快速查找UPX OEP的方法

这里以UPX压缩文件为例

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

UPX压缩器的特征之一是,其EP代码被包含在 PUSHAD/POPAD 指令之间。并且,跳转到OEP代码的JMP指令紧接着出现在POPAD指令之后。只要在JMP指令处设置好断点,运行后就能直接找到OEP
Pasted image 20250319231038

Tip

PUSHAD指令将8个通用寄存器(EAX~EDI)的值保存到栈。
POPAD指令把PUSHAD命令存储在栈的值再次恢复到各个寄存器。

3.6.2. 在栈中设置硬件断点

该方法也利用UPX的 PUSHAD/POPAD 指令的特点。在执行 01015330 地址处的 PUSHAD 命令后,查看栈
Pasted image 20250319225851
EAX到EDI寄存器的值依次被存储到栈。从OllyDbg的Dump窗口进人栈地址(000DFF54)。将鼠标光标准确定位到 000DFF54 地址,使用鼠标右键菜单设置硬件断点
Pasted image 20250319230018
硬件断点是CPU支持的断点,最多可以设置4个。与普通断点不同的是,设置断点的指令执行完成后才暂停调试。在这种状态下运行,程序就会边解压缩边执行代码,在执行 POPAD 的瞬间访问设置有硬件断点的 0006FFA4 地址,然后暂停调试。其下方即是跳转到OEP的JMP指令(熟悉该方法的操作原理才能在以后调试各种文件时得心应手)

3.7. 小结

Pasted image 20250319230441