在我们逆向分析的过程中,经常会受到花指令的干扰,今天我就来带领大家了解了解花指令。
文章目录
一.花指令的原理
花指令是干扰反汇编引擎正常工作的指令片段,不影响程序本身的执行结果。
花指令可以干扰IDA等反汇编工具生成正确的汇编代码,CFG(控制流图),进一步干扰生成中间代码(IR)以及伪代码。对于只会用IDA F5功能进行逆向的人来说,简直就是致命杀手。
我们想要搞清楚花指令的原理,我们先来思考这样一个问题:假如有一个函数disAsm(addr),该函数可以对指定地址addr处反汇编一条指令,并将结果自动输出到屏幕,返回值是当前反汇编指令的长度。
那么我们就应该能写出这样的代码:
target=getFunctionAddress;
targetEnd=getFUnctionEnd(main);
currentAddr=target;
while(currentAddr<targetEnd){
currentLen=disAsm(currentAddr);
currentAddr+=currentLen;
}
-
线性扫描:
我们上述代码的实现方式,就叫做线性扫描
线性扫描的特点:从入口开始,一次解析每一条指令,遇到分支指令不会递归进入分支。 -
递归下降:
当使用线性扫描时,比如遇到call或者jmp的时候,不会跳转到对应地址进行反汇编,而是反汇编call指令的下一条指令,这就会导致出现很多问题。
递归下降分析当遇到分支指令时,会递归进入分支进行反汇编。
X86指令集的长度是不固定的,有一些指令很短,只有1个字节,有些指令比较长,可以达到5字节,指令长度不是固定的。如果通过巧妙的构造,引导反汇编引擎解析一条错误的指令,扰乱指令的长度,就能使反汇编引擎无法按照正常的指令长度一次解析邻接未解析的指令,最终使反汇编引擎输出错误的反汇编结果。
JMP指令字节码构成:E9(1字节),目标地址-当前地址-5(4字节)。
我们来看这样一段代码:
E9 00 00 00 00 JMP 0x005 //实际上没有跳转
0xcc int 3
0xcc int 3
这样的一段程序是正常的,但是我们来看看另一种情况:
E9 01 00 00 00 jmp 0x006
0xE9 ......
0x50 push eax
0x53 push ebx
0x5B pop ebx
B8 56 34 12 00 mov eax,0x123456
仔细分析上面这这段代码,如果反汇编引擎使用线性扫描,当反汇编引擎解析到E9的位置时,会将后面的4字节数据当作跳转的偏移来解释,导致程序反编译出错。
- 间接跳转形式的花指令(在定长指令集如arm中比较常见)
二.花指令分析方法:
调试观察法,使用动态调试观察指令的执行流:
花指令不会干扰正常代码:
- 花指令内部如果涉及到寄存器的使用,一般会将其保存在栈中,利用这个弱点,我们可以通过观察sp寄存器来判断花指令的入口和出口
- 大部分情况下,花指令可以直接使用相同长度的nop(0x90)替换。