0x00 前言windows
对于加壳程序第一件事就是要找到OEP(oringinal Entry point),因为加壳的缘故,当PE文件载入OD或者其余调试软件时进入的的每每是壳程序的入口地址。因此要进行逆向分析第一步就必须找到PE程序的原始入口点。函数
0x01 壳的加载过程工具
壳和病毒在某些方面比较相似,都须要比原程序更早得到控制权。壳修改了原程序的的执行文件的组织结构,从而比原程序更早得到控制权,而且并不会影响原程序的正常运行。了解的壳的加载过程对于脱壳加壳异常重要。壳的加载过程以下:加密
1)保存入口参数spa
加壳程序初始化时保存各个寄存器的值,外壳执行完毕,再恢复各个寄存器内容,最后再跳到源程序的入口处执行。一般,用pushad/popad,pushfd/popfd指令来保存的和恢复现场环境。线程
2)获取壳本身所需的API函数3d
通常的壳输入表只有GetProAddress,GetMoudleHanle,LoadLibrary这几个API函数,甚至只有Kernel32.DLL以及GetProAddress。若是须要其余的API函数,能够经过LoadLibraryA(W)或者LoadLibraryExa(W)将DLL文件映像映射到调用进程的地址空间,函数返回的HINSTANCE值用于标识文件映像到虚拟内存地址。指针
LoadLibrary函数的原型以下:调试
HINSTANCE LoadLibrary{code
LPCTSTR lpLibFileName //DLL文件名地址
}
返回值:成功时返回模块句柄,失败返回NULL。
当DLL文件已经被映射到调用进程的地址空间里,能够调用GetModuleHanleA(W)函数得到DLL模块的句柄,函数的地址原型以下:
HMODULE GetModuleHandle{
LPCTSTR lpModuleName //DLL文件地址
}
一旦模块被加载,线程就能够调用GetProcAddress函数获取输入函数地址。函数的原型以下:
FARPROC GetProcAddress{
HMODULE hModlue //DLL模块句柄
LPCSTR lpProName //函数名
}
这三个函数异常重要,对于程序加壳帮助很大。后面几篇将会详细介绍用法,这里暂且罗列出来。
3)解密原程序的各个区块的数据
壳出于保护原程序代码和数据的目的,通常都会加密原程序文件的各个区块,在程序时外壳将会对这些数据解密,以让程序可以正常运行。壳通常都是按区块加密的,那么在解密时也是按区块解密的,而且把解密的区块数据按照区块的定义放在合适的内存位置。
4)ITA的初始化
ITA填写,原本应该由PE加载器实现。可是因为加壳时,本身构造了一个输入表,而且让PE文件头输入表指针指向了自建的输入表。因此PE装载器就对自建的输入表进行填写。那么原来PE输入表只能由外壳程序来填写了。外壳所要作的就是将这个新输入表结构从头至尾扫描一遍,对每个DLL引入的全部函数从新获取地址,并填写在ITA表中。
5)重定位处理
文件执行时将被映像到指定的内存地址中,这个初始地址称为基址。对于EXE文件,windows系统会尽可能使用EXE问价所指定的内存地址,好比某EXE问价的基址为40000h,而运行时Windows系统提供给程序使用的基地址也是40000h。这种状况就不须要重定位了。对于DLL文件,windows没办法每一次提供DLL运行时提供相同的基址。对于这种状况,重定位是必须的。此时壳程序也须要提供PE文件的重定位功能。因此加壳DLL文件比加壳EXE文件多一个重定位表。
6)HOOK-API
程序文件中输入表的做用是让windows系统在程序运行时提供API的实际地址给程序使用。在程序的第一行代码执行前,windows系统就完成了这项工做。
壳程序通常都修改了原程序的输入表,而后本身模仿windows系统的工做来填充输入表的中相关数据。在填充过程当中,外壳程序可填充HOOK-API的代码地址,这样就可间接的获取程序的控制权。
7)跳转到程序入口点(OEP)
经历过以上步骤后,外壳程序的功能就完成了,随后他会把控制权交给原程序,通常的壳这里会有一个明显的“分界线”。固然如今愈来愈多的加密壳将OEP一段代码搬到外壳的地址空间里,而后将这段代码清除掉。这种方式称为StolenBytes。这样,OEP与外壳就没有明显的分界线了,这增长了脱壳的难度。
0x02 利用两次内存断点法手动找到OEP
两次内存法的原理就是利用了壳加载过程第三步时须要对各个区段进行解密并将解密后的区段写入各个区段,完毕以后会跳转至原程序的OEP处。固然,若是咱们能判断出壳什么时候跳转至OEP处最好,可是通常这并不容易。可是咱们能够先对.data区块下断后再运行程序(由于区段.code比.data先解压,运行到这个断点时.code以及解密完成),随后再对.code(有的编译器是.text)段下断在运行,这样程序就会停在OEP处(由于解密完成后壳程序必定再次返回到OEP处,将控制权交给原程序)。这个方法就是两次内存法。
0x03 实例介绍两次断点法找OEP过程
1)将文件拖入od,alt+m进入内存模板,随后对.data区块按F2下断,以下图:
2)点击F9运行,此时程序停在了下来,以下图:
这里其实立刻就要对.data区块进行解密读写操做了,此时再alt+m进入内存模块,对.text(这个就是.code区块,因为编译器不一样,有的显示.text区块)区块下断。
3)点击F9运行,此时程序中止,以下图:
其实这里就是原程序的OEP地址了,因为od对PE文件进行了分析,因此显示如上图,咱们能够右键,删除模块分析便可获得下图:
标红框的地方就是OEP地址了。
0x04 总结
两次内存断点法虽然简单,可是咱们仍是要弄清楚其中的原理。它其实就是利用壳加载过程须要对区段进行解密而后返回原程序OEP这一特性。(PS:这种方法对于那种去区段信息改的面目全非的基本无效,我昨天就碰到一个加壳程序,区段信息只留下几个本身写,连code,data这些经常使用区段都没有,因此使用以前请用lordPE等工具查看一下区段信息)