(4)脱壳必备知识要点及方法

预备知识

    

    OEPOriginal 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,如果是像:jejnejajna,等有条件的跳也可以继续F8单步,因为你跟着循环个次它的条件一般就会改变然后向后走(它本身变成了为实现的跳了)。但是如果是像jmp这样非条件的跳,如果你还F8单步的话它会一直循环,所以这种跳就必须F4

清除OD的使用记录:OD菜单-〉插件-〉文件清除-〉全部文件

退出OD,在OD目录里有个UDD文件夹,把里面的东西删除干净,这样就清除了OD的历史记录。

脱壳方法之根据堆栈平衡原理找OEPESP定律法)

    

    堆栈平衡原理找OEPLenus写过一篇文章《寻找真正的入口(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载入程序-->查看-->内存-->在程序块(属主名和程序名一样)的rsrcF2-->Shift+F9(忽略异常运行到该断点)-->然后再对code段(一般它的地址是:00401000F2下断-->Shift+F9(对于tElock直接就到OEP了,也不再F2-->Shift+9运行到该处了,因为这里对应的就是我们在code段设断点的地方)。注:两个断点不用删除

    总结一下:如果我们知道壳在什么地方对code段解压完毕我们就可以使用内存断点,找到OEP。如果不知道,那么我们就依靠2次内存断点去找,如果还不行就用多次内存断点。总之明白了原理再多次的内存断点其实都一样。

    局限性:当一个壳如果它在JMPOEP前的一行代码仍在对code段解压,那么我们就不能再使用这种办法了。更贴切得说,是我们没必要使用这种方法。(如UPXASPACK

脱壳方法之最后一次异常法

    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 Workshopwinhex,UE等)打开脱壳程序。

    4.搜索文本“runtime”,搜到后,向前查找离“runtime”最近的十六进制数字“55 8B EC”

    数字所在的地址就是程序的OEP

    (二).Visual C程序

    利用Visual C启动部分的几个函数GetCommandLineA(W)GetVersionGetModuleHandleA(W)

    GetStartupInfoA(W) 等来定位程序的OEP

 一步到达OEP

    

    1.开始按ctrl+f,输入:popad(适合少数壳,如UPXASPACK)

    2.F2下断,F9运行到此断点处,再F2取消断点

    3.F8继续向下调试

 “SFX”

    

    1.设置OD,忽略所有异常(去掉所有勾选)

    2.切换到SFX选项卡,选择字节模式跟踪实际入口(速度非常慢)

3.重载程序(自动走到OEP停下来

(注:用完之后记得还原设置:停在解压入口)

 内存另类断点法

    1.OD载入程序

    2.在内存镜像中找到包含code/text(代码)的区段,双击,然后在数据窗口第一个下内存访问断点

3.不断的按F9,直到程序领空到系统领空,记录下按了几下(如:9(按得过程注意可能反应比较慢,要按一下等一会儿,即到暂停状态再按,(文件按钮下显示状态))

    4.重载,继续上面的操作,不同的是我们按8F9 

    5.然后继续用单步跟踪法调试

    Btw:OEP可能会在我们到达的地方的上面,我们可以倒着走,向下跳转不让实现,实现向上跳转。也可以在程序跑起来的前两次开始F8跟踪,大家多多实验。


你可能感兴趣的:((4)脱壳必备知识要点及方法)