花指令
花指令
https://www.anquanke.com/post/id/208682
¶花指令原理
ida采用的是线性扫描反汇编算法,也就是一步一步往下识别,一旦在中间插入奇怪的立即数,ida可能就会识别出错,进而不能正确反编译
¶花指令实现
首先,我们插入花指令不能阻碍我们程序的正常运行,这是最基本的
下面使用一段常规代码进行花指令的添加
1 |
|
没插花指令前的反编译
¶永真跳转
1 | __asm { |
先来看看效果
可以看到ida不能正确反编译
¶原理
xor eax,eax能够保证eax的值为0,异或自己,相同为0
根据目标操作数修改符号标志位、奇偶标志位、零标志位,经过这一步后,ZF为1
test eax,eax是按位与操作,并且会根据值修改ZF标志位,并且只有当位都清 0 时,零标志位才置 1
ZF为1时,jz进行跳转,到LABEL1,因为里面没有其他值,所以代码正常执行,但是在 反编译的时候,因为是线性扫描,所以遇到了LABEL2里面的0xe8(call的机器码),就会将其后面的机器码当作要调用的地址,这样一来,jz就直接跳转到被错误识别的汇编代码中,所以会出现错误
¶去除
可以看到这里面的jz和jnz后面的地址存在+1,这就基本可以看出是花指令了,找到对应的地址,按u取消定义
可以看到这里也是两个函数,对下面的按C弄成代码
然后为了反编译,要把0xe8使用90填充,也就是nop,填充后按C即可
按P弄成函数后反编译,和上面的基本一致
¶插入立即数
1 | __asm { |
效果
识别出错,原理和上面一样,感觉这种稍微恶心一点,因为有一段可能是正常的,需要一个一个试
直接nop掉即可
¶破坏堆栈
我们可以插入对esp和eip的操作进而破坏堆栈
1 | __asm { |
ida能够正常反编译,但是在函数末端会发现堆栈错误提示
¶call&ret构造花指令
call本质是先将其 下一条指令压入栈中,再jmp函数地址
ret则是pop eip
所以我们可以修改返回地址,然后在中前插入垃圾数
1 | __asm { |
效果
这里我们插入的不是机器码,所以没有识别成奇怪的代码,但是还是让ida出现了错误
¶去除
全部nop掉即可
¶原理
这里之所以程序能够进行正常的运行,是因为call会把其下一条指令的地址压入栈中,也就是41459F,(不知道为什么这里会多出一个db 36h -.-),接下来执行将当前esp地址的值+8,也就是栈顶存储的值+8,也就是返回地址加8,正好对应4145A7,下面的代码c之后是正常的代码,所以不会出错
在此基础上,我们可以构造各种花指令
下面连接有一些花指令
https://www.bilibili.com/read/cv13177757
¶call嵌套
1 | __asm { |
效果
¶原理
这里之所以程序能够正常运行,是因为只有两个call对堆栈有影响,所以只需要在最后esp+8就可以回到原来的堆栈
这里先callA9,然后0xE8和后面的结合成了call指令,这里可以看到call到了全是int 3的地方
然后这个9f一直在call自己,所以可以判断是花指令
把这两个函数nop掉之后
所以这个调用也没用了
同理这个也可以nop掉,那么堆栈会不平衡,add esp,8也要nop
得到正常代码
¶jmp变形-SUSCTF2022-tttree
感觉很巧妙,拿出来看看
动调可以看得更清楚
前面的push没什么好说的,关键在call之后的
当前的栈顶,call会将其之后的地址压入栈顶
可以看到是B7D82FF9B0
下面两步就是将当前栈顶的元素先+0x191b给到rax
下一步是把rax里的值给到rsp+16
经过两步pop,可以发现来到了存储原本返回值+0x191b的栈的位置
retn先将栈顶元素pop到eip,再jmp,而eip正好记录我们下一步执行的指令
这样就是一个简单的jmp
解决方法就是先计算跳转的地址,然后改成jmp即可