花指令(junk code)是一种专门用来迷惑反编译器的指令片段,这些指令片段不会影响程序的原有功能,但会使得反汇编器的结果出现偏差,从而使破解者分析失败。比较经典的花指令技巧有利用 jmp 、call、ret 指令改变执行流,从而使得反汇编器解析出与运行时不相符的错误代码。
花指令用于加大静态分析的难度,破坏反编译的分析,造成异常。
分类:
可执行花指令:会被执行,但是没有任何意义的代码,执行前后不会改变寄存器的值,同时会被反汇编器识别。
不可执行花指令:不会运行,但是会使反汇编得到一些错误代码
常见花指令
jmp(E9)花指令:这是一类跳转花指令
这类花指令往往会实现永真或永假总之一定会跳转的情况,实现垃圾代码不运行。
先将0x00401D92转化为数据
然后将0x00401D94处的数据转换成代码,再把0x00401D92,0x00401D93处的数据nop掉即可
E9是jmp指令对应的机器码,当反汇编器读取到E9时,接着会往下读取四个字节的数据作为跳转地址的偏移,所以才会看到错误的汇编代码。即把cmp该条指令代码错误识别为数据。
该花指令编写方式
__asm {
_emit 075h #jmp $+4//075h是jnz的机器码
_emit 2h
_emit 0E9h
_emit 0EDh
}
这段汇编再具体分析的话就是,075h 2h这两个字节码导致跳转到075h偏移4的位置,这一步是为了绕过我们编写的垃圾代码0E9和0ED,但是线性分析就会导致0E9被反编译成正常指令引发不正常,实际运行上0E9根本没有运行。
在此基础上,为了更好的混淆,常采用多层跳转。
MP Label1
Db thunkcode1
Label1:
……
JMP Label2
Db thunkcode2
Label2:
……
JMP Label1
Db thunkcode1
Label2:
……
JMP Label3
Db thunkcode3
Label1:
…….
JMP Label2
Db thunkcode2
Label3:
call&ret构造花指令
__asm {
call LABEL9;
_emit 0x83;
LABEL9:
add dword ptr ss : [esp] , 8;
ret;
__emit 0xF3;
}
代码中的esp存储的就是函数返回地址,对[esp]+8,就是函数的返回地址+8,正好盖过代码中的函数指令和垃圾数据。这个和上面的jmp差不多。
自定义花指令
_asm
{
call LABEL9;
_emit 0xE8;
_emit 0x01;
_emit 0x00;
_emit 0x00;
_emit 0x00;
LABEL9:
push eax;
push ebx;
lea eax, dword ptr ds : [ebp - 0x0];
#将ebp的地址存放于eax
add dword ptr ss : [eax-0x50] , 26;
#该地址存放的值正好是函数返回值,
#不过该地址并不固定,根据调试所得。
#加26正好可以跳到下面的mov指令,该值也是调试计算所得
pop eax;
pop ebx;
pop eax;
jmp eax;
_emit 0xE8;
_emit 0x03;
_emit 0x00;
_emit 0x00;
_emit 0x00;
mov eax,dword ptr ss:[esp-8];
#将原本的eax值返回eax寄存器
}
IDC脚本去花示例
#include <idc.idc>
static main()
{
auto x,FBin,ProcRange;
FBin = "E8 0A 00 00 00 E8 EB 0C 00 00 E8 F6 FF FF FF";
//目标 = "E8 0A tel:00 00 00 90 EB 0C tel:90 90 90 90 90 90 90";
//花指令1的特征码
for (x = FindBinary(MinEA(),0x03,FBin);x != BADADDR;x = FindBinary(x,0x03,FBin))
{
x=x+5; //返回的x是第一个E8的地址,
//加上5是第二个E8的地址
PatchByte (x,0x90);//nop掉
x = x + 3; //00
PatchByte (x,0x90);
x++; //00 E8
PatchWord (x,0x9090);
x =x + 2 ; //F6 FF FF FF
PatchDword(x,0x90909090);
}
}