那天,我遇到了destination那道题,然后遇到了seh和花指令。
然后,我苦思冥想,还是不会,遂问出题人,出题人曰:菜就多练。于是本篇博客就出现了。

参考:[原创][花指令]由易到难全面解析CTF中的花指令-软件逆向-看雪-安全社区|安全招聘|kanxue.com

前言

先了解一下花指令:

花指令是专门针对静态分析的。且不在意逆向工程师可不可以看出来,关键在于让工程师逆不出来。对于动态调试,就要看情况了,有的可以直接出来,有的又搞不出来。


反编译器的编译方法:

递归下降+线性扫描:

  • 从入口开始依次解析指令+遇到分支指令会递归进入分支进行反编译

错误原理:

  • 机器码长度不固定,插入一个有指令作用的数据会导致其被解析成指令随后的n个字节会被解释为操作数,这样之后的内容就无法反编译了

花指令分为两种:

被执行的花指令和不被执行的花指令

不被执行的花指令如:

1
2
3
4
5
__asm{
jmp Label1
db junkcode
Label1:
}

这种jmp的可以直接nop掉(不过这个会被直接识别出来)

可以这样:

jmp:

1
2
3
4
5
6
7
asm
{
Jz Label
Jnz Label
Db thunkcode;垃圾数据
Label:
}

永恒跳转,但是ida无法识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__asm{
push ebx
xor ebx,ebx
test ebx,ebx
jnz label1
jz label2
label1:
_emit junkcode
label2:
pop ebx//需要恢复ebx寄存器
}

__asm{
clc
jnz label1:
_emit junkcode
label1:
}

同永恒跳转,不过使用test强制转换标志位

call&ret:

call指令的本质:push eip,然后jmp 函数地址

ret指令的本质:pop eip

1
2
3
4
5
6
7
8
__asm {
call LABEL9;
_emit 0x83;
LABEL9:
add dword ptr ss : [esp] , 8;给栈中的eip加8,直接跳过这几条语句
ret;
__emit 0xF3;
}

那么来详细了解一下call

1
2
3
4
5
6
7
8
9
10
11
12
call near xxx;默认为near,可省,相对下一条指令位置调用函数(段不变)
E8 xx xx;

call ax;间接绝对近距离调用
call [addr];间接绝对近距离调用
ff xx;

call far 段基址:段内偏移;直接绝对远距离调用,可以不加far,返回必须用retf
0x9a xx xx xx xx;前2字节是段内偏移,后2字节是段基址,这种情况下,CS和ip均要压栈

call far [addr];间接绝对远距离调用
ff1e xx xx xx xx;前2字节是段内偏移,后2字节是段基址

这之上是我一开始对花指令的了解,但是随着我出了flower_tea这道题之后,我对花指令有了(感觉上)更深的看法

花指令junk_code的目的:

针对反编译器,干扰反编译器使其无法解析指令,只能用机器码表示。再者就是考虑针对反编译器特性,选择构造结构让它们不能把对应代码识别成一个函数