虽然以前也接触过简单的花命令,但基本上是jz/jnz式的固定跳跃
在前几天的一场比赛中,出现了比较复杂的花指令,参考pizza的笔记本开始学习XD
编程指令是指没有蛋用,干扰代码的读取或逆编译,但不影响程序功能的代码。
广义上,像OLLVM、jmdj这样的代码修改型混淆也是花指令,本文指的是干扰反汇编,影响机器码分析,但不影响正常机器码的字节。
原理花命令的根本原因是x86指令集由不定长命令构成
如果跳转导致执行流在另一条指令中间命中,则会发生静态反汇编分析错误
现代反汇编有两种思路:
线性扫描
从头到尾依次读取机器码,反汇编递归下降
从程序入口向后反汇编,遇到条件跳转时分别从分支处继续反汇编,无条件跳转下尝试从目标指令开始反汇编继续线性扫描显然容易产生花指令。 如果在跳转和目标之间插入长指令的开头(例如E8,之后的4字节),则可以使所有后续指令成为分析错误。 OllyDbg和windbg使用线性扫描法
递归下降可以避免这种简单的花命令,跳过中间的脏字节。 但是,对于特定用途的花指令(如jz jnz脏字节),脏字节由独立于上下文的算法分析
此外,还有一种将call用作jmp的方法,但编译器只将call用作子程序/函数跳转的指令,因此IDA经常将call的地址视为一个函数的起始地址,从而损害整个函数的完整性
分类jx jnx的最常见的种花指令
如上所述,jnz之后是“可能的分支”,破坏后续指令的分析
call pop/add esp/add [esp] retn
call命令可以理解为jmp push ip
因此,在add esp,4中降低堆栈顶部可以消除推送IP的影响,call与jmp等价
但是,IDA将其识别为函数的边界,会错误地识别函数的范围
stx/jx
清除CLC[clearCarryflag]eflags寄存器的carry标志位
jnb [jump if cf=0]进位标志为0时跳
因此,虽然绑定也相当于jmp,但IDA无法简单访问上下文也被认为是后者字节可能的分支
花可以用IDA脚本、编程、二进制操作
因为花命令有固定的格式,所以可以用字符串匹配它进行NOP
例如
# include IDC.idcstaticmatchbytes (startaddr,match ) { auto Len,I,PatSub,SrcSub; len=strlen (匹配; wile(Ilen ) pat sub=substr (匹配,I,i 1); srcsub=form(x )、byte (startaddr ); srcsub=substr(srcsub,i % 2,) i % 2)1); if(patsub!='?' PatSub!=src sub ({返回0; (if ) I%2==1) { StartAddr; (I; }返回1; }静态主() { auto Addr,Start,End,Condition,junk_len,I; 开始=0x 740; End=0x7f3; condition=' 740 a 7508 e 81000000 e b04 e8'; junk_len=12; for(addr=start; addr结束; Addr () if )匹配字节(Addr,Condition ) ) for ) I=0; i junk_len; I ) {patchbyte(addr,0x90 ); makecode(addr; Addr; } } analyze area (开始,结束); 消息(clear fake-jmpopcodeok ); {另外,根据实际的花命令的不同,可能需要根据花命令后的长度来控制NOP的范围,但是在这里应该很难不加入脚本来实现~