状态
Doing
花指令技术是利用反汇编工具算法的缺陷,以致反汇编工具无法准确地反编译出原有的伪代码。因此,我们首先需要了解反汇编工具的原理。
反汇编工具算法浅析:
主要的反汇编算法是线性扫描算法(Linear Sweep)和递归算法(Recursive Traversal)。
工具名 | 反汇编算法 |
OllyDbg | Linear Sweep/Recursive Traversal |
SoftICE | Linear Sweep |
WinDBG | Linear Sweep |
W32Dasm | Linear Sweep |
IDA PRO | Recursive Traversal |
线性扫描算法:
基于线性扫描算法的反汇编器按照程序执行的线性流程逐字节扫描机器码,将其翻译为汇编指令。这种方法是最简单和最直接的反汇编策略。下面是线性扫描反汇编器的基本运行原理:
- 起始地址: 反汇编器从程序的入口点,即AddressOfEntryPoint开始工作。这通常是可执行文件中定义的一个起始地址,指向代码的实际开始位置。
- 读取指令: 反汇编器逐字节或按照操作系统和硬件平台的特定指令长度读取字节码。对于x86体系结构的程序,指令长度是变化的,反汇编器必须能正确解析出指令边界。
- 解码指令: 反汇编器使用机器码到汇编指令的映射来解码读取到的字节码。例如,16进制的
0xE8
字节将被解码为CALL
指令。
- 解析操作数: 如果指令包含操作数(如
CALL
后面的地址),反汇编器将解析随后的字节来确定操作数的完整值。对于CALL
指令,通常会有4个字节用于表示调用的目标地址(在32位系统中)。
- 生成汇编代码: 反汇编器将解码的指令和解析的操作数转换为汇编语言格式,并输出为汇编代码。
- 重复过程: 反汇编器接着处理下一组字节,重复上述过程,直到整个代码段被反汇编完毕。
递归算法:
递归行进算法按照代码可能的执行顺序来反汇编程序,对每条可能的路径都进行扫描。当解码出分支指令后,反汇编器就将把这个地址记录下来,并分别反汇编各个分支中的指令。采用这种算法可以避免将代码中的数据作为指令来解码,相对比较灵活。递归算法反汇编器的基本运行原理第一步和第二步都是一样的从EP开始分析,接着与线性扫描算法不同的是:
- 识别控制流: 对于每一个解析出的指令,反汇编器分析其控制流。比如,如果是一个跳转或者调用指令,反汇编器会识别出跳转的目的地址。
- 递归遍历: 对于跳转指令,递归地跟随它们的目的地址,重复解析指令的过程。这样可以避免线性扫描中将数据或不可到达的代码区域错误解析为指令的问题。
- 构建控制流图: 在递归过程中,反汇编器可以构建出一个控制流图(CFG),它表示了程序中的所有执行路径。
- 结束条件: 当遇到已经访问过的地址或无效的指令(例如在数据段中),递归过程会停止。这样可以防止无限循环,并确保只处理有效的代码。
示例代码:
编译之后,使用线性的反汇编算法软件W32Dasm来反汇编,反汇编的结果就会出错,因为这个算法为逐行反汇编,当反汇编软件反汇编到
db 0E8h
时反汇编软件将其当成了代码分析,导致后面的反汇编结果出现错误。tips:
0xE8
是call指令的机器码识别这类花指令的方法是观察无效的跳转指令,当跳转指令的位置无效时就像第三行和第四行的跳转,
00401009
处并不是有效地址,这时可以直接nop掉00401008
处的E8
,程序就可以正常分析了。(上面这种在跳转后面加一个db定义数据的手段属于常见的干扰线性反汇编算法的方案)但这种方法并不能干扰到使用递归算法的IDA和OD(OllyDbg打开文件时使用的是Linear Sweep而分析代码功能时使用的是Recursive Traversal),在递归这类算法里面有一个重要的假设就是任意一条控制转移指令其后继的目的地址都能够确定。
迷惑这类反汇编工具的方法是让其难以确定跳转的目的地址。在跳转的目的地址处创建一个指向无效数据的跳转指令代码。如下:
使用递归算法的反汇编工具在分析到第五行时,将第七行处的数据当成代码来分析,导致后面的代买分析错误,反汇编结果如下:
恢复的办法和之前一样也是直接nop掉
00401008
处的E8
即可。常见的花指令实现方案:
方案一:
在正常代码中加入一些无用的代码,虽然执行了但是并不影响原先寄存器的值。
方案二:
jz和jnz本身就是相反的两种跳转,它们同时出现的时候表示的其实就是一条jmp指令,但是ida看到确不能正常识别,遇到这种情况还是nop掉一条后将另外一条改成jmp即可