在继续学习更多逆向课程之前,本章再通过另一个例子介绍脱壳。
这还是属于入门级别的内容。在后续章节中,研究过其他专题之后,作者还会介绍一些更加高级的脱壳内容。
使用 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 的位置。