本文是反-反汇编系列的第四篇文章,继续介绍一些最近学习到的花指令和patch技巧

源码、编译

代码如下:

#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的措施,比如取代码段上某一块的数据,patchnop以后就会改变原有的结果;也可能是代码段的校验之类,这样就会比较难以处理

补充-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的例子

点击收藏 | 0 关注 | 2 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖