在继续学习更多逆向课程之前,本章再通过另一个例子介绍脱壳。

这还是属于入门级别的内容。在后续章节中,研究过其他专题之后,作者还会介绍一些更加高级的脱壳内容。

使用 IDA 加载 unpackme_aspack 2.2.exe 程序。

在程序的入口处:

public start
start proc near

; FUNCTION CHUNK AT .aspack:0046B014 SIZE 00000050 BYTES
; FUNCTION CHUNK AT .aspack:0046B3F5 SIZE 00000026 BYTES

pusha
call    loc_46B00A

第一条指令 PUSHA,在之前使用的程序中基本没有碰到过。

在x86汇编语言中,pusha指令是一条用于将通用寄存器的当前值压入堆栈的指令。具体来说,当执行pusha时,它会按照固定的顺序将以下寄存器的值推入堆栈:AX、CX、DX、BX、SP、BP、SI、DI。值得注意的是,SP(栈指针)的值是指令执行前的值。

pusha是在16位环境下使用的指令,主要用于保存所有通用寄存器的状态。在32位环境下,有一个类似的指令叫做pushad,它的作用类似,但会处理32位的寄存器:EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI。

在大部分简单的壳中,使用了 PUSHAD 指令将寄存器的初始状态保存,然后再跳转到 OEP 执行内存中的原始代码之前使用 POPAD 恢复寄存器的初始状态。

根据这条规律,可以通过 PUSHAD-POPAD 法来轻松找到 OEP。当然,在最新的壳中,壳的作者也了解这一点,他们会避免使用这些指令。

调试程序

前面的章节已经介绍过如何调试,这里使用另一种方式。

读者可以在 Python 交互栏中输入命令,或者使用更加友好的 ipyida 插件。

输入 import idc,导入 idc 模块,再输入 idc.load_debugger ,然后输入左括号就会出现函数提示。

Load the debugger
     dbgname - debugger module name
               Examples: win32, linux, mac.
     use_remote - 0/1: use remote debugger or not
This function is needed only when running idc scripts from the command line.
In other cases IDA loads the debugger module automatically.

success load_debugger(string dbgname, long use_remote);

我们输入参数,win32 和 0,执行函数。

import idc
idc.load_debugger("win32", 0)
Out[3]: True

返回 True,执行成功。

先解释一下,PUSHAD-POPAD 法的原理。

在执行 PUSHAD 后一条指令之前,找到栈上保存寄存器值的位置,然后在该位置设置一个断点,在程序解密出原始代码之后跳转到 OEP 执行之前会通过 POPAD 恢复寄存器的初始值,然后触发该断点暂停执行,从而确定 OEP 的位置。