此文用来整理和分析做题过程中所遇到的那些花指令,必然持续更新。
关于花指令的介绍见:花指令 · 逆向工程入门指南,这里只总结例子啦~
call $+5模式
[2021 ISCC] 擂台题-ZCM
1 | ...:00815023 038 E8 00 00 00 00 call $+5 |
这一段可以完全等同于等同长度字节的nop,但是却能干扰IDA的反编译器。
首先我们知道:
1 | call addr ;将返回地址(即call的下一条指令首地址)压入栈中,等同于:push 下一条指令的首地址; mov ip,addr |
那么执行完前三条指令(到0x0081502B
时)栈区分布大概是:
在本例中,ret_addr=0x00815028
,而add dword ptr [ebp+4], 0Ah
根据栈区分布可以很明显地知道[ebp+4]就是ret_addr
,所以是ret_addr+=0x0A
即ret_addr=0x00815032
。
而0x00815032
这个返回地址正是ret
指令的下一条指令,也就是说这一整段0x00815023-0x00815031
代码是完全无意义的,甚至没有破坏寄存器值,所以可以完全用相同长度的nop
指令代替。
[划重点] 去花方法:等同于相同长度的nop
指令。
[2021 CISCN总决赛] junk
1 | .text:0000000000401641 018 E8 00 00 00 00 call $+5 |
由局部变量定义区可知var_18=-0x18
,那么0x401646
处的指令可以等同于add [rsp], 6
,同理可知call
时栈顶处仅新增了ret_addr=0x401646
,而这条指令就是将栈顶处的值+6,即将ret_addr+6=0x40164c
,正好是retn
指令后的下一条指令,所以0x401641-0x40164B
这段也可以全部nop
掉。
[划重点] 去花方法:等同于相同长度的nop
指令。
[2021 CISCN总决赛] junk
1 | .text:000000000040166F 018 49 C7 C0 0D 00 00 00 mov r8, 0Dh |
pop r9; add r9, r8; push r9
这一段就是把栈顶值(即ret_addr=0x40167B
)取出来以后自加0x0D
,结果为ret_addr=0x401688
,所以在下一步执行retn
时返回地址设置成了0x401688
,就会直接跳过下面三条指令跳到0x401688
处。
这里有破坏寄存器r8
和r9
的值,所以patch的时候不能直接nop
掉,要注意更改寄存器的值(当然如果这两个寄存器后面都没用到的话当我没说x 不过保险起见,建议还是补上最好)。
[划重点] 去花方法:patch成以下指令,多于字节用nop
填充。