OllyDbg 即时调试功能的强大用途

使用工具

  • OllyDbg修改版,简称OD
  • OD 汉化插件均来自互联网;
  • UnPackMe来自互联网,仅供学习使用;
  • Dump 工具为PETools,来自互联网;
  • IAT 重建工具为ImportREC,来自互联网;
  • PE 编辑工具为LoadPE,来自互联网;
  • 文中特殊数字均是HEX,为了书写方便采用DEC

操作流程

  1. 最开始使用的是原版 OD,后续发现定位 OEP 比较麻烦,遂替换为修改版 OD,使用内存执行断点即可轻松到达 OEP;

  2. 运行 CM 后会发现虚拟机变得非常卡,打开任务管理器会发现当前 CM 的 CPU 占用率为 99%;

    任务管理器

  3. 使用 OD 打开 CM,会弹出错误弹窗:

    错误弹窗1

    提示不是一个有效的 32 位可执行程序,这个肯定是 PE 头有问题,点击确定后,EP 停留在系统 DLL 区段;

    内存窗口

    打开内存映射窗口,发现程序只有一个区段;

    F9 运行后,又弹出错误弹窗:

    又弹出错误弹窗

    提示不知道如何继续,因为某个地址不可读,同时查看内存窗口,发现没有提示的区段;

  4. 打开 LoadPE,在设置中勾选外壳扩展选项保存并退出:

    外壳扩展选项

    这样就将 LoadPE 添加到了系统的右键菜单;

  5. 在 CM 上右键选择在 LoadPE 的 PE 编辑器中打开:

    在 LoadPE 的 PE 编辑器

    大部分数据看起来都很正常,唯一异常的是NumberOfRvaAndSize也就是数据目录表(DataDirectory)的项数,好像这个数值自 Windows NT 发布以来一直是 16(也就是 16 进制的 10);

    修改 NumberOfRvaAndSize 的值为 10,然后保存;

  6. 运行修改的程序,发现打不开了,难道是改坏了?导入 OD 看看:

    入口点超出代码段范围

    首先是弹窗提示入口点超出代码段范围,这是加壳程序载入 OD 后的正常反应;

    EP

    点击确定后,程序暂停在 00405000 处,查看内存映射窗口,发现修改之前的 1 个区段变成了 5 个区段(不含 PE 头);

    通过区段描述判断,当前处于壳所在的区段,暂停在程序的入口点(EP),看来修改 NumberOfRvaAndSize 的值是为了反调试;

    F9 运行程序,又是访问不存在区段的弹窗:

    依然弹出错误弹窗

    既然程序都不能正常运行,常规定位 OEP 的方法肯定是不行的;

    有没有可能是把程序改坏了,恢复 NumberOfRvaAndSize 的值试试:

    恢复 NumberOfRvaAndSize

    还是不能运行

    还是不能运行,看来还是有某些反调试没有绕过;

  7. 打开 OD,设置即时调试功能:

    设置即时调试功能

    恢复按钮可用时表示即时调试功能设置成功,然后退出 OD;

  8. 尝试使用 LoadPE 设置 INT3 断点的功能直接到达 EP,看程序是否能运行:

    设置 INT3 断点

    点击确定后,OD 自动附加到了 CM 上,可以看到入口的 PUSHAD 被替换为 INT3:

    PUSHAD 被替换为 INT3

    手动修改 INT3 为 PUSHAD,然后运行程序:

    还是这个错误

    还是这个错误,只能另寻它法了;

  9. 在前面已经知道,程序正常运行后会有 5 个区段,通常情况下,外壳会依次解压各区段,然后调用代码段的代码,最后程序正常运行:

    使用 LoadPE 查看区段

    使用 LoadPE 查看程序的区段,.text 是第 1 区段,也就是说,正常情况下,外壳会首先解压该区段,至于后面的区段是运行时解压还是全部解压后运行,.text 段都已经解压完成了;

    此时如果让后面的某个区段产生异常并中断程序,然后给代码段设置访问断点,是不是就可以到达 OEP 了;

    以相邻的 .rdata 段为例,当外壳解压 .rdata 区段的数据时,肯定是要将解压的数据写入该区段,此时如果 .rdata 区段没有写入权限,程序肯定会产生异常并中断,这时, .text 段可能已经解压完成了,如果给代码段设置内存访问断点(并恢复 .rdata 段的写入权限)然后运行程序,是不是就可以到达 OEP 了;

    当然,不能保证 .rdata 段就一定是在 .text 段之后解压,如果 .rdata 不行,还有其它 3 个区段可以尝试;

    不过,作为只读数据段,像 IAT 之类的只读数值应该只会写入此区段;

    修改权限

    取消 .rdata 段的写入权限,并保存(有 save 点 save,没 save 点 ok);

  10. 修改完成后,运行修改权限后的程序,OD 自动附加到了程序上,说明程序产生异常并崩溃了:

    程序产生异常并崩溃

    通过查看 EAX 和提示信息可以得知,是这条指令尝试将 MessageBoxA 的地址写入 .rdata 区段时,由于(.rdata)没有写入权限而产生了异常;

    接下来,手动恢复 .rdata 区段的权限,当前只有 1 个区段,说明解压写入都是在这个区段中进行的,所以需要给这个区段赋予所有权限;

    恢复权限

    然后 F7 单步执行指令,可以看到数据被成功写入 .rdata 对应的地址中:

    数据被成功写入

  11. 接下来就要验证代码段是否解压完成了,goto 到 00401000:

    goto 到 00401000

    可以看到代码段已经解压了,但还有一个问题:

    还有一个问题

    按照预想,代码段解压后给代码段设置内存访问断点就可以到达 OEP 了,但现实是当前只有 1 个区段;

  12. 既然代码段已经解压,那就可以在数据窗口 goto 到代码段占用的内存空间,给代码段占用的内存单元设置内存访问断点,和直接给代码段设置内存访问断点是一样的:

    给代码段占用的内存单元设置内存访问断点

    无法保证所有程序都会顺序访问代码段的地址,所以给代码段占用的所有单元设置访问断点会更有保障;

    断点设置好后,运行程序,成功到达 OEP;

    成功到达 OEP

  13. 使用 PETools 将程序数据 Dump 下来;

  14. 跟随任意调用,定位 IAT:

    跟随任意调用

    此处为间接调用;

    定位 IAT

    IAT 没有被重定向;

  15. 使用 ImportREC 获取输入表并修复 Dumped 的程序:

    ImportREC

  16. 修复后的程序可以运行,但导入 OD 后会提示不是有效的 32 位可执行文件,需要修改 NumberOfRvaAndSize 的值为 10;

    修改 NumberOfRvaAndSize

    修改后再次导入 OD,完美!

  17. 最后,用完即时调试功能后,需要手动关闭,不然任何程序崩溃后都会被 OD 附加;

    用完即时调试功能后