Day11:花指令
花指令
花指令
又叫垃圾指令,不影响程序原始逻辑,但会干扰IDA反编译或者反汇编。
原理:
因为反编译器多是静态分析的,不会通过执行来分析逻辑,只会根据反汇编出来的指令分析,因此有空可乘:例如可以通过内联汇编不完整指令但是跳过不完整指令来模糊指令边界、内联汇编永跳永不跳分支来误导运行逻辑分析、内联汇编无意义指令误导主要逻辑
显著特征是出现红色行,而应对方法是选中花指令所在的区域改为“nop”(空指令)
恒跳型
即通过预设ZF位为1,使jz恒执行。实测IDA9.0.241217不会报红,也就是混淆失效,所以就看看源码吧
1
2
3
4
5
6
7
8
9
10
11
12
13#include<stdio.h>
#include<windows.h>
int main() {
_asm{
xor eax, eax;
jz s;
add esp, 0x11;
s:
};
printf("hello\n");
system("pause");
return 0;
}
call-ret相消型
来看效果

可以看到,在函数中间出现了sp-analysis failed,而上面一条就是retn,明显不对。
往前看,rep stosd初始化后,call了$+5,即当前位置+5=0046308C,也就是下一条的add
然后给esp的值+7(call的时候push eip即push 0046308c了)
此时esp存放值为0046308c+7=00463093,所以retn时会pop 00463093,程序跳到00463093
往下看,00463093正是没识别出来的三个数的最后一个,下面还有一个offset没有指令。
第三个数68h和offset结合刚好是push offset,对应上了下面的call(函数传参)
选中没识别的三个数,按“U”(undefine),可以看到三个数分开了

选中db 68h和dd offset aHello,按C转化为汇编指令,可以看到push offset恢复了

选中call $+5到db 34h,右键选择”Fill with NOPs“(实测db 34h会nop不干净,要选中到db 34h下一行)

可以看到函数变成这样,这是因为插入了retn导致函数范围分析错误,需要纠正

点击函数名,然后右键edit fuction,把end address改成末尾那个真正的retn的地址就OK了

jz-操作数不全型
这种情况下会出现和上一种一样的指令识别错误的问题,且看图:

这是按照MSVS C++初始化规则跳转后,在本来main函数的地方的截图。但是跳转过程中会发现,跳转到main函数的代码是jmp loc_xxxxxx而不是jmp sub_xxxxxx。但是目前还不影响,先分析报错的地方。
可以看到 xor ebp,[eax+50h] 处报红了,并且下面是一段没分析出来的数和字符串”pause”,下面进行分析
首先,rep stosd初始化后,进行了一个恒跳,跳转的地方是0046308E,在 xor ebp,[eax+50h] 这条指令里,所以这里红了
把 xor ebp,[eax+50h] 这条指令undefine一下

根据逻辑,程序是肯定会跳到0046308E的,所以前面的代码冗余,直接nop掉。选中 xor eax,eax 到0046308E(根据上一种类型的经验,要多覆盖一位才能刚好nop掉目标代码)

nop完成后(如果有db 90h就按”C“转成code)就会发现下面没分析的数立刻分析出指令了

但是还没完,此时按F5是无法反编译的,因为报错的地方是loc_xxxxxx范围里的,不是一个函数,也就是前面说的jmp loc_xxxxxx而不是jmp sub_xxxxxx的问题了。这里可以看出,IDA这类混淆会导致IDA把函数识别成一个引用而不是函数。
到最开始push ebp开辟栈帧的引用loc_463070处,把它undefine一下,再按“P”重新分析一下,就可以得到main函数了

这类有一个特征就是jz loc_xxxxxx+1,据此可以快速找到加了混淆的地方
总结
- 上述三种(实际上有混淆效果的只有两种)是比较基础的花指令。识别花指令的要点主要是看红色错误,也可以结合一些特征来识别。解决方法就是nop掉混淆代码。
- 在实际设计题目时,笔者意外发现了一些更强力的混淆,单单是在上述三种类型的基础上加一点其他指令,就会导致一个函数完全无法被识别(被放在rdata段而不是text段的那种),但是程序还能正常执行,而报错的地方在无法识别的函数的上级函数里(跳转到这个函数的那个函数),可能要结合AI才能实现快捷的分析。因此,花指令还是一个具有挑战性的混淆方式(当然也可能是笔者太菜了没石粒)