本文为笔者从零基础学习系统安全相关内容的笔记,如果您对系统安全、逆向分析等内容感兴趣或者想要了解一些内容,欢迎关注。本系列文章将会随着笔者在未来三年的读研过程中持续更新,由于笔者现阶段还处于初学阶段,不可避免参照复现各类书籍内容,如书籍作者认为侵权请告知,笔者将立刻删除。强调本系列所有内容仅作为学习研究使用,作者对此文章中的代码造成的任何后果不负法律责任。
前文链接
[系统安全] PE文件格式详解1
[系统安全] PE文件格式详解2
[系统安全] Windbg Preview调试记录
[系统安全]《黑客免杀攻防》MFC逆向基础实战
[系统安全] windows下C++编写第一个加壳程序
[系统安全] PE文件格式分析实战基础—分析helloworld文件
本篇文章为《黑客免杀攻防》笔记,如书籍作者认为侵权请告知,笔者将立刻删除。
文章目录
什么是脱壳技术
前面学习了简单的加壳技术,简单通俗点来说,就是给定目标程序,先拿到它的OEP,然后对其代码段(或者其他区段)加密或者压缩等操作后,把自己写的引导程序Stub融合进这个PE文件,并修改这个Stub最后要转到的执行地址为刚才拿到的OEP,然后把这个PE文件的OEP修改为自己编写的Stub程序的开始地址。
脱壳技术反过来,就是找到真正的程序OEP地址,然后让程序执行到这个地址,然后dump下来,再根据具体情况进行修复。这个过程就是脱壳。
脱壳的第一步就是寻找程序的真正的OEP地址。
第一步,寻找程序真正的OEP
利用跨区段跳转获取目标程序OEP
原理是加壳程序通常会把加载模块放到不同于原程序代码段的另一个区段中,等程序运行时先运行这个加载模块,最后跳转到原程序代码段,所以这是一个跨段并且地址相差比较大的跳转。
这里以Demo13-1.exe程序为例,先用LordPE查看程序的区段信息。
可以看到多了一个.A1Pass段,这个应该就是编写的Stub加载模块部分。结合前面的加壳知识程序都会从代码段开始执行,所以如果有从.A1Pass段向.text段跳转,那么这个跳转地址就很有可能就是原来的OEP。
把程序载入OD中,alt+M调出模块界面,在.text段设置断点。
然后按F9运行,停到对text段修改的地方,如下所示
然后再retn的地方F2下断点,并取消刚才设置的text的写断点,重新运行程序,程序就停到了retn的地方,F8单步执行得到返回的地方,如下所示
可以看到再往下的位置有一处是跳转到0x00401357,由上面LordPE中看到程序加载基址为0x00400000可知,实际地址为1357,也就是代码段所在地址范围。得出结论这个地址就是程序原来的OEP。
利用堆栈平衡获取目标程序OEP
为了保证加壳之后的宿主程序能够正常运行,会在Stub部分入口和出口都加上pushad和popad类指令,其作用是保存和恢复所有寄存器状态。比如经典的upx壳就有这种特征。
于是有ESP定律:即可加壳程序开始运行时的栈状态与解压解密后宿主程序开始运行时的栈状态完全一致,因此加壳程序开始运行时ESP的值与解压解密后宿主程序开始运行时的ESP值完全一致。
以Demo13-2.exe程序为例,运行到pushad下一条指令时,右键在esp值处下硬件断点。
再按F9执行到下次中断时,查看附近汇编代码,如下
可以看到retn之前的push 1357
就是程序的OEP地址。
利用关键API定位OEP
不同编译器的OEP附近调用的第一个api函数是不同,他们通常的作用是初始化或者获取系统环境信息,而这些api在壳的Stub部分通常不会出现。所以可以借此来定位OEP,但需要注意的是这些api有时会被放在EOP附近比较深的函数调用栈里。下面记录一些编译器的关键api对照表。
编译器 | API |
---|---|
VC++6.0 | GetVersion |
VC++7.1 (VS 2003) | msvcrt.__set_app_type |
VC++8.0 (VS 2005) | GetSystemTimeAsFileTime |
VC++9.0 (VS 2008) | GetSystemTimeAsFileTime |
VC++10.0 (VS 2010) | GetSystemTimeAsFileTime |
第二步,转储内存映像dump
dump的前提是先要让程序运行到程序原来的OEP的位置,然后再dump下来,这样才能保证dump下来的可执行文件是被解密或者解压之后的,并且执行时会执行所有的程序所需要的前备工作。
可是使用LordPE进行dump,但是这个软件听说好像只能显示60个进程。win7以上开机貌似就不止60个进程了,所以获取不到,这里使用OD的插件dump。依旧以Demo13-2.exe程序为例,让再od里让程序运行的OEP处。
默认选项保存为exe文件即可。
第三步,重建导入表(修复IAT)
在PE文件运行开始时,系统会先根据导入表内的导入模块名和api函数名获取相应的api的当前地址,并把这些地址按照相应的顺序保存在IAT中。
等到程序运行时只需要IAT提供的api地址,因此如果有正确的IAT而没有导入表也是可以的。
重建导入表就是从IAT信息逆向推出导入导出表,需要遍历进程空间内所有已经加载的模块的导出信息就能获得重建导入表所需要的模块和api信息,这就是重建导入表需要的所有信息。
使用ImportREC重建导入表
目前OD插件的dump功能可以自动修复IAT(导入地址表),但是这里还是做一下ImportREC的实验。
要将目标文件完全脱壳,并停留在OEP处;而且还要OD先dump下来一份没有修复的exe文件。
管理员打开,选择od载入的正在执行的进程,写入刚才获取的oep,此时od也停留在程序的oep处。点击自动搜索,然后点击获取输入表
可以看到显示有效,然后点击修复转存文件,选择刚才dump下来的那个没有修复并且无法运行的exe文件即可。
ps:逆向这条路也很长,不知道自己能走多远。可能会一直走下去,也可能最后会重新回到渗透的方向上。