# Reverse 花指令

# 花指令

花指令是反调试的一种基本的方法。其存在是干扰选手静态分析,但不会影响程序的运行。实质就是一串垃圾指令,它与程序本身的功能无关,并不影响程序本身的逻辑。在软件保护中,花指令被作为一种手段来增加静态分析的难度。IDA 并不能正常识别花指令,导致可看可分析代码被破坏,因此需要我们自己去分析一下。

花指令⼀般分两类,会被执行和不会被执行(垃圾指令)。

# 会被执行

这类花指令本身是正常的汇编指令,它们运行完后不会改变原来程序的堆栈 / 寄存器,但能起到干扰静态分析的作用。一般分为两种:
形式一:改变堆栈操作
形式二:利用 call 指令或 jmp 指令增加执行流复杂度

# 形式一

1
2
3
4
5
6
7
int main(){
_asm{
push eax;//随便压一个入栈
add esp,4;//把他弹出来
}
printf("hello word\n");
}

1

# 形式二

执行 call 指令时会向堆栈中压入返回地址,我们可以修改这个返回地址,配合 ret 指令跳转到任意一个想去的地方。
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main(){
_asm{
call xxx //call进去xxx
xxx:
add [esp],0x7 //修改他的esp的返回地址,直接跳到+7的位置
retn//ret直接返回不执行下面的代码
_emit 0x12 //无意义指令,就是告诉cpu我要在这放这个字节
_emit 0x34
}
printf("hello world\n");
}

2

可以看到 ida 把 12h 34h 68h 这三个指令认在⼀起了,68h 是下⼀个的⼆进制指令,⽽ 12h 34h 是⽆意义的,原本应该是 68 00 21 40 00 是⼀条 push 指令,所以就导致 ida 把这个识别成数据了。
add esp+7 可以数⼀下也是到 68 这个地址

3
按 u 可以把这个地址切换⼀下
4

然后在 68 按 c,就能正常的识别 push 指令
5
把这一串全部 nop 掉
6

再把 main 函数修改到正确的地址 (直接按 u 和 p 创建函数)

# 不会被执行

# 形式一

⽰例代码 (VS 中 Release32 位下编译)

1
2
3
4
5
6
7
8
9
10
#include<stdio.h>
int main(){
_asm{//内联汇编
xor eax,eax; //亦或就是0 ZF=1
jz s; //jz判断zf=1就会跳转
add esp,0x11; //这里不会被执行
s:
}
printf("hello world\n");
}

ida 打开会反编译失败
因为 ida 会认为你这个 add esp +11h 到⼀个很下⾯的位置了
导致 sub_401010 不知道有⼏个参数,从⽽导致反编译失败
7

解决⽅法⼀
进⼊ sub_401010 函数,F5 反编译⼀下,让 ida 重新认识这个函数
再进 main 函数就能识别了
8

解决方法二
把 add 直接 nop 掉
9

# 形式⼆

1
2
3
4
5
6
7
8
9
10
11
int main(){
_asm{
xor eax,eax;//亦或完=0 ZF标志位=1
jz xxx ;ZF//标志位=1跳转
_emit 0x11;
_emit 0x22;
_emit 0x33; //垃圾代码,但是这个是xor的操作码,但是这里不会被执行
xxx:
}
print("helloworld \n");
}

因为这个 xor 是操作码,ida 向下分析会把下⾯的操作码给吸上来
看到有个红色
有个 xor jz +1
就可以想到是花指令
10

把 jz 给按 u 掉就能正常识别了,再把 11 22 33 nop 掉
11
再修改 main 的范围
如果修改 main 函数范围,中间有个函数识别错了
可以按 u 把 main 函数 u
再按 p 创建函数应该就能识别了
对抗花指令最好的办法就是找到花指令给他 nop 掉

更新于 阅读次数