脱壳实践之寻找OEP——两次内存断点法
0x00 前言
对于加壳程序第一件事就是要找到OEP(oringinal Entry point),由于加壳的缘故,当PE文件载入OD或者其他调试软件时进入的的往往是壳程序的入口地址。所以要进行逆向分析第一步就必须找到PE程序的原始入口点。
0x01 壳的加载过程
壳和病毒在某些方面比较类似,都需要比原程序更早获得控制权。壳修改了原程序的的执行文件的组织结构,从而比原程序更早获得控制权,并且并不会影响原程序的正常运行。了解的壳的加载过程对于脱壳加壳异常重要。壳的加载过程如下:
1)保存入口参数
加壳程序初始化时保存各个寄存器的值,外壳执行完毕,再恢复各个寄存器内容,最后再跳到源程序的入口处执行。通常,用pushad/popad,pushfd/popfd指令来保存的和恢复现场环境。
2)获取壳自己所需的API函数
一般的壳输入表只有GetProAddress,GetMoudleHanle,LoadLibrary这几个API函数,甚至只有Kernel32.DLL以及GetProAddress。如果需要其他的API函数,可以通过LoadLibraryA(W)或者LoadLibraryExa(W)将DLL文件映像映射到调用进程的地址空间,函数返回的HINSTANCE值用于标识文件映像到虚拟内存地址。
LoadLibrary函数的原型如下:
HINSTANCE LoadLibrary{
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等工具查看一下区段信息)
脱壳实践之寻找OEP——两次内存断点法的更多相关文章
- 脱壳实践之寻找OEP——堆栈平衡法
0x00 前言 上一篇介绍了壳程序的加载过程以及通过两次内存断点法寻找OEP,这篇我们将利用新的的方法——堆栈平衡法来寻找OEP. 0x01 堆栈平衡法原理 堆栈平衡原理就是利用壳程序在运行前后需要 ...
- 菜鸟脱壳之脱壳的基础知识(五)——利用内存断点寻找OEP
经过第一节的基础知识,我们都知道了,加壳程序首先解把原来压缩的代码解压,然后放到所对应的区块中,当外壳程序执行完毕后,跳回到OEP执行,我们都知道,OEP是放在代码段中,也就是当外壳程序处理完毕后,跳 ...
- 两次内存断点法寻找OEP
所谓“两次内存断点法寻找OEP”,按照<加密与解密*第三版>上的解释来说,就是这样的.一般的外壳会依次对.text..rdata..data..rsrc区块进行解压(解密)处理,所以,可以 ...
- 脱壳入门----常见的寻找OEP的方法
一步直达法 所谓的一步直达法就是利用壳的特征.壳一般在执行完壳代码之后需要跳转到OEP处,而这个跳转指令一般是call ,jmp ,push retn类型的指令,而且因为壳代码所在的区段和OEP代码所 ...
- 破解之寻找OEP[手动脱壳](2)
1.使用ESP定律 OD载入后,F8一次,在寄存器窗口的ESP的内容上(如0012FFA4)右键:“在数据窗口中跟随”,到内存数据窗口,将内存数据窗口以HEX 数据形式显示,在刚才的地址起始位置上(如 ...
- 破解之寻找OEP[手动脱壳](1)
OEP:(Original Entry Point),程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP), 只要我们找到程序真正的OEP,就可以立刻脱壳. PUSHAD (压栈) 代表程序的 ...
- 寻找OEP
1.使用ESP定律 OD载入后,F8一次,在寄存器窗口的ESP的内容上(如0012FFA4)右键:"在数据窗口中跟随",到内存数据窗口,将内存数据窗口以HEX数据形式显示,在刚才的 ...
- JVM高级特性与实践(一):Java内存区域 与 内存溢出异常
套用<围城>中的一句话,“墙外面的人想进去,墙里面的人想出来”,用此来形容Java与C++之间这堵内存动态分配和垃圾收集技术所围成的“围墙”就再合适不过了. 对于从事C.C++的开发人员而 ...
- C++中两块内存重叠的string的copy方法
如果两段内存重叠,用memcpy函数可能会导致行为未定义. 而memmove函数能够避免这种问题,下面是一种实现方式: #include <iostream> using namespac ...
随机推荐
- 我去,你竟然还不会用 Java final 关键字
写一篇文章容易吗?太不容易了,首先,需要一个安静的环境,这一点就非常不容易.很多小伙伴的办公室都是开放式的,非常吵,况且上班时间写的话,领导就不高兴了:只能抽时间写.其次,环境有了,还要有一颗安静的心 ...
- 机器学习——手把手教你用Python实现回归树模型
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天这篇是机器学习专题的第24篇文章,我们来聊聊回归树模型. 所谓的回归树模型其实就是用树形模型来解决回归问题,树模型当中最经典的自然还是决 ...
- IE6、IE7兼容querySelectorAll和querySelector方法
querySelector 和 querySelectorAll 方法是 W3C Selectors API 规范中定义的.他们的作用是根据 CSS 选择器规范,便捷定位文档中指定元素. 目前几乎主流 ...
- Com训练_1
初次接触Com大概是2001年,离现在多年了,那时认识比较肤浅,再次接触学习下. //ComPort + D7, 稍后将训练ComPort6 + D10.3.2环境 //MSComm + D7,D10 ...
- Arduino控制超声波检测与0.96OLED及串口显示
Arduino控制超声波检测与0.96OLED及串口显示代码使用库共享(包括超声波检测与U8glib): 使用元件: 0.96寸 12864 I2C OLED 128x64规格 超声波检测模块 湿度模 ...
- cb49a_c++_STL_算法_对所有元素排序_sort_stable_sort
cb49a_c++_STL_算法_对所有元素排序_sort_stable_sort sort(b,e) sort(b,e,p) stable_sort(b,e) stable_sort(b,e,p) ...
- 面试问Redis集群,被虐的不行了......
哨兵主要针对单节点故障无法自动恢复的解决方案,集群主要针对单节点容量.并发问题.线性可扩展性的解决方案.本文使用官方提供的redis cluster.文末有你们想要的设置ssh背景哦! 本文主要围绕如 ...
- 如何从OutLook正确取得定期会议的时间?(待解决)
背景: 用Microsoft.Office.Interop.Outlook取得日历项,然后根据业务要求筛选. 现象: 如果是定期会议,使用AppointmentItem.Start/End取得的是该定 ...
- 十万同时在线用户,需要多少内存?——Newbe.Claptrap 框架水平扩展实验
Newbe.Claptrap 项目是笔者正在构建以反应式.Actor模式和事件溯源为理论基础的一套服务端开发框架.本篇我们将来了解一下框架在水平扩展方面的能力. 前情提要 时隔许久,今日我们再次见面. ...
- 3、尚硅谷_SSM高级整合_创建Maven项目.avi
Maven中dependencyManagement作用说明 在Maven多模块的时候,管理依赖关系是非常重要的,各种依赖包冲突,查询问题起来非常复杂,于是就用到了<dependencyMana ...