转自:https://xz.aliyun.com/t/5097
源码、编译
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char** argv) {
puts("nop me");
puts("nop me");
puts("nop me");
puts("nop me");
puts("nop me");
system("pause");
return 0;
}
//gcc -m32 main.c -o test
x86-1
IDA视角
用IDA打开,和之前几篇类似的汇编代码
因为没开优化,对esp
的加加减减没有合并
把0x0804844F
处的puts("nop me");
全部nop
掉,也就是4条汇编指令,分别是参数压栈、调用函数、"平栈2次"
patch
接着,0x0804844F
处改成call $+6
,也就是call 0x08048455
,这条指令长度为5
最后把0x08048455
的指令改成pop eax
,效果如下:
运行时没有什么问题
F5也可以看出大致逻辑,IDA把sub_8048455
看成了子函数
点进这个子函数,F5就会失败,因为堆栈不平衡
实际上call func
== push 下一条指令地址
+ jmp func
这里通过一个pop eax
消除call
对栈的影响,效果其实就是无条件jmp
,但是IDA显示非常混乱
修复
让IDA正确识别也很简单,只要nop
掉那一块代码就行了
如果代码中有防止patch
的措施,比如取代码段上某一块的数据,patch
成nop
以后就会改变原有的结果;也可能是代码段的校验之类,这样就会比较难以处理
补充-x64
这个技巧在x64架构下实现也一样简单,只补一个图
x86-2
失败的尝试
主要关于push retn
的组合使用
尝试patch
成这样,运行到0x8048440
时压入一个代码段地址,然后直接retn
因为retn == pop eip
,就会直接跳转到0x8048449
由于IDA一般会把retn
看作函数结束的标志,所以一般分析到retn
就结束了
重新打开,尴尬的是IDA并没有被我们骗到
可能是IDA对紧邻的push + retn
有一定的识别吧
改进
作为改进方法,我们将栈弄得更复杂一些
目标地址0x804843D
刚被push
压栈,这时esp
指向它
紧接着就通过add dword ptr [esp],0x2
把存的代码段地址增加2
可以看到即将ret 0x804843f
也就是原程序流程
重新打开后发现识别效果很差,原先的很多代码段已经是红色了,这表明分析是有问题的
不过虽然看到sp-analysis failed
,但却不影响F5,不知道为什么
F5之后,后半部分的代码逻辑直接没了...效果不错
小总结
在实际上手时,一开始第一个例程用的32位exe,但是patch
之后直接运行失败,调试也直接crash,暂时没找到结果,于是用gcc编译成elf代替
另外,消除对栈的影响也可以单纯的通过add/sub esp,xxx
来完成,都差不多所以只举了pop reg
的例子