反汇编过程:
1.确定需要反汇编代码区域。
2.读取该代码区域所包含的值,并执行一次查表操作,将其机器码与其汇编语言助机符对应起来。
3.对其汇编语言等价形式进行格式化(两种主要格式:x86汇编语法:AT&T和Intel_What’smean的博客-CSDN博客),并将其在反汇编代码中输出。
线性扫描反汇编
反汇编指令位置确定:一条指令结束、另一条指令开始的地方。
反汇编从一个代码段的第一个字节开始,以线性模式扫描整个代码段,逐条反汇编每条指令,直到完成整个代码段。
优点:能够完全覆盖程序的所有代码段。
缺点:没有考虑到代码中可能混有数据。
GNU 调试器(gdb)、微软公司的 WinDbg 调试器和 objdump 实用工具的反汇编引擎均采用线
性扫描算法。
递归下降反汇编
递归下降反汇编强调控制流的概念。根据一条指令是否被另一条指令引用来决定是否进行反汇编。
1. 顺序流指令
顺序流指令将执行权传递给紧随其后的下一条指令。顺序流指令的例子包括简单算术指令,
如 add;寄存器与内存之间的传输指令,如 mov;栈操作指令,如 push 和 pop。这些指令的反汇
编过程以线性扫描方式进行。
2. 条件分支指令
条件分支指令(如 x86 jnz)提供两条可能的执行路径。如果条件为真,则执行分支,并且
必须修改指令指针,使其指向分支的目标。但是,如果条件为假,则继续以线性模式执行指令,
并使用线性扫描方法反汇编下一条指令。因为不可能在静态环境中确定条件测试的结果,递归下
降算法会反汇编上述两条路径。同时,它将分支目标指令的地址添加到稍后才进行反汇编的地址
列表中,从而推迟分支目标指令的反汇编过程。
3. 无条件分支指令
无条件分支并不遵循线性流模式,因此,它由递归下降算法以不同的方式处理。与顺序流指
令一样,执行权只能传递给一条指令,但那条指令不需要紧接在分支指令后面。
递归下降反汇编器将尝试确定无条件跳转的目标,并将目标地址添加到要反汇编的地址列表中。但某些无条件分支可能会给递归下降反汇编器造成麻烦。如果跳转指令的目标取决于一个运行时值,这时使用静态分析就无法确定跳转目标。x86 的 jmp eax 指令就证实了这个问题。只有程序确实正在运行时,eax 寄存器中才会包含一个值(通常call后函数返回值保存在eax中)。由于寄存器在静态分析过程中不包含任何值,因此无法确定跳转指令的目标,也就无法确定该从什么地方继续反汇编过程。
4. 函数调用指令
函数调用指令的运行方式与无条件跳转指令非常相似(包括反汇编器无法确定 call eax 等指
令的目标),唯一的不同在于,一旦函数完成,执行权将返回给紧跟在调用指令后面的指令。在这
方面,它们与条件分支指令类似,因为它们都生成两条执行路径。调用指令的目标地址被添加到推
迟进行反汇编的地址列表中,而紧跟在调用后面的指令则以类似于线性扫描的方式进行反汇编。
从被调用函数返回时,如果程序的运行出现异常,递归下降就有可能失败。例如,函数中的
代码可能会有意窜改该函数的返回地址,这样,在函数完成时,控制权将返回到一个反汇编器无
法预知的地址。
5. 返回指令
有时,递归下降算法访问了所有的路径。而且,函数返回指令(如 x86 ret)没有提供接下
来将要执行的指令的信息。这时,如果程序确实正在运行,则可以从运行时栈顶部获得一个地址,
并从这个地址开始恢复执行指令。但是,反汇编器并不具备访问栈的能力,因此反汇编过程会突
然终止。这时,递归下降反汇编器会转而处理前面搁置在一旁的延迟反汇编地址列表。反汇编器
从这个列表中取出一个地址,并从这个地址开始继续反汇编过程。递归下降反汇编算法正是因此
而得名。
优点:它具有区分代码与数据的强大能力。作为一种基于控制流的算法,它很少会在反汇编过程中错误地将数据值作为代码处理。
缺点:它无法处理间接代码路径,如利用指针表来查找目标地址的跳转或调用。然而,通过采用一
些用于识别指向代码的指针的启发(heuristics)式方法,递归下降反汇编器能够提供所有代码,
并清楚地区分代码与数据。