花指令的基本概念:
花指令是企图隐藏掉不想被逆向工程的代码块(或其它功能)的一种方法; 个人看来 花指令就是注入一些垃圾指令来防止反编译 或者作为病毒进行攻击注入;
花指令的分类
花指令大致可以分为两类 分别是可执行花指令和不可执行花指令 可执行花指令相较于不可执行花指令来说相对简单 这里我们重点来讲不可执行花指令
可执行花指令
可执行花指令指的是花指令代码在程序的正常执行过程中会被执行 但执行这些代码没有任何意义 执行前后不改变任何寄存器的值 同时这部分代码也会被反汇编器正常识别 其目的依然是加大静态分析的难度 让你难以识别代码的真正意图 有时这种花指令可以破坏反编译的分析 使得栈指针在反编译引擎中出现异常
压栈后恢复栈地址
_asm {
push eax;
add esp, 4;
}
call&ret构造
_asm {
call label
label:
add dword ptr ss : [esp], 7;//注意变长指令
ret
}
real_code
call&ret构造相对于比较灵活 由于其特性 进程最终都会跳转real_code的位置 所以ret和real_code之间可以插入任意花指令 但是要注意添加对应的add值
上述花指令通过nop掉即可 这里不做过多叙述
不可执行花指令 不可执行花指令在程序中不会被执行 但是会通过静态分析算法的漏洞缺陷在静态分析时执行一些垃圾数据来阻碍程序进行反编译
简单的e8指令跳转
_asm {
jmp label1
_emit 0xe8
label1:
}
e8指令就是跳转指令 通过对线性扫描算法(如od)进行干扰 实现花指令的作用 去除方法就是直接nop掉就好
jz和jnz条件跳转
_asm {
jz lable2
jnz lable2
_emit 0xe8
lable2:
}
利用jz和jnz的互补条件跳转指令来代替jmp,两个跳转一个指向无效数据,一个指向正常数据来干扰递归扫描算法(如ida) 解决方法也很简单 通过nop 或者将jz和jnz换成jmp就行了
永真条件跳转
_asm{
push ebx
xor ebx,ebx
test ebx,ebx
jnz label1
jz label2
label1:
_emit junkcode
label2:
pop ebx //需要恢复ebx寄存器
}
通过设置永真或者永假条件 使程序一直运行 由于ida使用的是递归算法 导致ida反汇编会优先汇编接下去的部分
破坏堆栈平衡
function:
push ebp
mov ebp, esp
push eax ; 正常保存寄存器
push ebx ; 正常保存寄存器
push ecx ; 正常保存寄存器
; 花指令开始
push edx ; 额外压栈,破坏平衡
jmp skip_extra_pop
extra_pop:
pop edx ; 这个pop可能不会执行
skip_extra_pop:
; 花指令结束
pop ecx ; 恢复寄存器
pop ebx
pop eax
pop ebp
ret ; 返回时ESP与call前不一致!
汇编中函数如果有参数或局部变量,在调用前会对堆栈进行保护 ,在返回前要还原函数调用前的堆栈,这一过程程序在编译时会自动加上,如果反编译器检测到指令破坏了堆栈平衡,即函数返回时与调用时堆栈状态发生了变化,就会报错,可以利用这一点构造破坏堆栈平衡的花指令
这类破环堆栈平衡的指令实际不会执行,但是由于IDA在反汇编分析时会分别从两个条件跳转处开始分析,因此判定堆栈不平衡导致反汇编失败,解决方法是NOP掉破坏堆栈平衡的指令