OEP是Original Entry Point缩写,即程序加壳前的真正的入口点。
外壳初始化的现场环境(各寄存器值)与原程序的现场环境是相同的。加壳程序初始化时保存各寄存器的值,外壳执行完毕,会恢复各寄存器内容。其代码形式一般如下:
PUSHFD ; 将标志寄存器入栈保存
PUSHAD ; push eax, ecx, edx, ebx, esp, ebp, esi, edi
…… ; 外壳代码部分
POPAD ; pop edi, esi, ebp, esp, ebx, edx, ecx, eax
POPFD ; 恢复标志寄存器
JMP OEP ; 大的跳转
OEP: …… ; 解压后的程序原代码
PUSHAD (压栈) 代表程序的入口点,
POPAD (出栈) 代表程序的出口点,与PUSHAD想对应,一般找到这个OEP就在附近
脱壳方法之单步跟踪找OEP
1.用OD载入,如果有提示是否分析代码,就点“ 不分析代码!”
2.F8单步向下跟踪调试,没有实现的向上的跳,向下的跳都可以步过,阻止实现的向上的跳。
3.遇到会跑飞的CALL我们就F7步入。
Btw:遇到程序无法向下跟踪调试的时候,在附近(上下均可)找一个大跳转(像下的实现的,跳的大小看线的长短)(如果找不到大的跳就找一个不跳的跳(没有线),FSG2.0就是这样),F4运行到该跳处,然后右键→跟随或者回车到达跟随的地址按F2下断点,然后shift+F9(忽略异常运行)运行到跟随的地址,然后再F2取消断点,接着就可以F8继续单步调试了(对于FSG2.0的课可能直接就到oep了,另外老版的OD可能会出现要你分析代码,解决办法:右键-->分析-->从模块删除目标文件)。另:在F8单步跟踪的时候并不一定非要是在实现的向上的跳的下一句F4,如果是像:je,jne,ja,jna,等有条件的跳也可以继续F8单步,因为你跟着循环个次它的条件一般就会改变然后向后走(它本身变成了为实现的跳了)。但是如果是像jmp这样非条件的跳,如果你还F8单步的话它会一直循环,所以这种跳就必须F4。
清除OD的使用记录:OD菜单-〉插件-〉文件清除-〉全部文件
退出OD,在OD目录里有个UDD文件夹,把里面的东西删除干净,这样就清除了OD的历史记录。
脱壳方法之根据堆栈平衡原理找OEP(ESP定律法)
堆栈平衡原理找OEP,Lenus写过一篇文章《寻找真正的入口(OEP)--广义ESP定律》我会打包给大家,有兴趣的可以自己看一下堆栈平衡原理,做免杀自己写花指令也需要用到这个原理,所以这文章对大家相当有帮助。
1.F8单步跟踪调试在关键句的下一句,在OD寄存器窗口ESP突显为红色
2.在OD命令行下输入(dd ESP突显红色的地址) 或者 (hr ESP突显红色的地址)
3.下硬件访问断点
4.F9运行(然后删除硬件断点),继续F8单步向下调试。
五:脱壳方法之内存断点法找OEP(内存镜像法)
1.OD载入程序后,我们直接对code段下内存访问断点
2.Shift+F9运行 (code段没有解压完毕,所以会不断得中断,连续按F9。当然这种方法是不可取的)
正确的方法是:我们使用两次断点法。
假设我是程序作者,一个程序有code段,data段,rsrc段....依次排列在你的内存空间中。那么我会怎么解码呢?程序会先将code段解码,然后再将data段解码,接着是rsrc段....只要你在data段或者rsrc段下内存访问断点,那么中断的时候code段就已经解压完毕了。这时我们再对code段下内存访问断点,不就可以直接到达OEP了吗?
综上正确的操作方法是:
1, 1.OD载入程序-->查看-->内存-->在程序块(属主名和程序名一样)的rsrc段F2-->Shift+F9(忽略异常运行到该断点)-->然后再对code段(一般它的地址是:00401000)F2下断-->Shift+F9(对于tElock直接就到OEP了,也不再F2-->Shift+9运行到该处了,因为这里对应的就是我们在code段设断点的地方)。注:两个断点不用删除
总结一下:如果我们知道壳在什么地方对code段解压完毕我们就可以使用内存断点,找到OEP。如果不知道,那么我们就依靠2次内存断点去找,如果还不行就用多次内存断点。总之明白了原理再多次的内存断点其实都一样。
局限性:当一个壳如果它在JMP到OEP前的一行代码仍在对code段解压,那么我们就不能再使用这种办法了。更贴切得说,是我们没必要使用这种方法。(如UPX,ASPACK)
脱壳方法之最后一次异常法
1.OD载入程序后
2.设置异常选项:选项——调试设置——异常(一般情况我们忽略所有的异常选项(去掉所有的勾选),当然后面根据猛壳的特点我们可以自己选择设置异常选项,注意:只有用这种方法是需要设置这里,其它方法一定要全部勾选上,不然不能跟踪下去的)
3.重载下OD(然后,插件-->隐藏od-->隐藏(不然OD会跑飞的),然后不用重载了),我们按SHIFT+F9,直到程序跑起来为止,我们把按的次数记住
4.再次重载OD,再次按SHIFT+F9,我们按到程序跑起来的前一次。(第一次如果按了21下程序跑起来了,那么我们第二次就按20次,跑起来的前一次)
5.在OD的右下角(堆栈窗口)看见一个"SE句柄",这时我们按CTRL+G,输入SE句柄前的数值(注:不是地址)!
6.来到SE句柄的地方我们按F2下断,F9运行至断点这,然后再F2取消断点
7.接着我们F8单步继续向下调试
经验:popad
Jmp XXXX
看到这两句就应该引起我的注意,特别当jmp 没注明short时,那么这里很可能就是壳的出口,也即OEP入口。
模拟跟踪法
1.F9运行程序,确定是否有暗桩。看程序能不能跑起来,如果程序能正常跑起来说明没有暗桩(有暗桩才能用这种方法)
2,用“脱壳方法之最后一次异常法”(到6)(目的是把暗桩给过掉)
2.打开内存镜像,找到包含有SFX,imports,relocations的区段
3.命令行下输入tc eip<区段地址(上面找到的包含有SFX,imports(输入表),relocations(重定向)的区段的地址)。回车后会自动跟踪出OEP地址。
根据编译语言特点找OEP
(一).delphi程序
1.执行程序。
2.用LordPE脱壳。
3.接着用16进制编辑器(如Hex Workshop,winhex,UE等)打开脱壳程序。
4.搜索文本“runtime”,搜到后,向前查找离“runtime”最近的十六进制数字“55 8B EC”,
数字所在的地址就是程序的OEP。
(二).Visual C程序
利用Visual C启动部分的几个函数GetCommandLineA(W)、GetVersion、GetModuleHandleA(W)、
GetStartupInfoA(W) 等来定位程序的OEP。
一步到达OEP
1.开始按ctrl+f,输入:popad(适合少数壳,如UPX,ASPACK等)
2.F2下断,F9运行到此断点处,再F2取消断点
3.F8继续向下调试
“SFX”法
1.设置OD,忽略所有异常(去掉所有勾选)
2.切换到SFX选项卡,选择“字节模式跟踪实际入口(速度非常慢)”
3.重载程序(自动走到OEP停下来)
(注:用完之后记得还原设置:停在解压入口)
内存另类断点法
1.OD载入程序
2.在内存镜像中找到包含code/text(代码)的区段,双击,然后在数据窗口第一个下内存访问断点
3.不断的按F9,直到从程序领空到系统领空,记录下按了几下(如:9次)(按得过程注意可能反应比较慢,要按一下等一会儿,即到暂停状态再按,(文件按钮下显示状态))
4.重载,继续上面的操作,不同的是我们按8次F9 。
5.然后继续用单步跟踪法调试
Btw:OEP可能会在我们到达的地方的上面,我们可以倒着走,向下跳转不让实现,实现向上跳转。也可以在程序跑起来的前两次开始F8跟踪,大家多多实验。