最后一次异常法定位 OEP

使用工具

  • OllyDbg 1.10原版,简称OD
  • OllyDbg修改版,简称OD
  • OD 汉化插件均来自互联网;
  • UnPackMe来自互联网,仅供学习使用;
  • 文中特殊数字均是HEX,为了书写方便采用DEC

分析思路

  1. 拿到 CM 后,第 1 想法就是栈平衡,将 CM 导入原版 OD:

    首先弹出警告弹窗,说明程序有壳(这不是废话嘛)

    将 CM 导入原版 OD

    然后到达 EP 位置,行首却不是 PUSHAD/PUSHFD,这就让人很尴尬了,不过又想到了栈平衡的第 2 种方法;

  2. 大胆的猜测一下,如果 OEP 行首是压栈指令,那么压栈的地址就是:0012FFC0:

    压栈的地址

    所以,给 0012FFC0 设置硬件写入断点,然后运行程序;

  3. 程序会中断两次,第 2 次才会到达 OEP,因为第 2 中断时才处于代码段:

    OEP

  4. 然后又尝试了设置访问断点来到达 OEP,两种方法皆是可行的,不过这 3 种方法并不是今天的重点;

  5. 将程序导入改版 OD,F9运行程序,待程序主窗体出现后,打开日志窗口(请不要关闭程序主窗体):

    一大堆异常

    来到日志窗口后,发现程序运行期间,有一大堆异常,但程序却正常运行了,很是奇怪;

  6. 程序可以正常运行,是因为 OD 的调试选项忽略了所有异常,如果不忽略异常,程序会在每一次异常时暂停,而在最后一次异常后,程序正常运行了;

    如果在最后一次异常时(也可以说是程序运行前)给代码段设置内存访问断点,是否可以到达 OEP 呢?

  7. 首先设置 OD 调试选项:

    不忽略异常

    在日志窗口可以看到,最后一次异常是 INT3,所以取消忽略 INT3 异常,同时取消忽略异常范围选项,因为从 00000000 ~ FFFFFFFF 就忽略了所有异常;

  8. 然后运行程序,在几次中断后,就来到了最后一次异常所在位置:

    最后一次异常

  9. 来到内存窗口,给代码段设置内存执行断点,然后按下Shift + F9忽略异常并运行程序:

    OEP

    程序到达 OEP,Perfect!

  10. 既然有那么多方法,问什么要用这么复杂的方法呢?

    是因为,加壳不仅可以隐藏 OEP,还可以反调试,而为了绕过反调试,在执行部分代码后,再设置断点相对会更安全,这是一种好的思路;