目前已经介绍了IDA加载器的一些内容,后面也还会接着讨论。本章再介绍调试器的一些内容。
IDA 支持多个调试器。
首先加载初始没有打过补丁的 CRUEHEAD’s crackme 程序,如果现在路径下有已修改程序的数据库或者说 idb 文件,当 IDA 提示时,选择创建新的数据库并覆盖已有数据库(OVERWRITE),开始重新分析。
选择调试器
不要勾选 MANUAL LOAD,直接打开 IDA 即可,打开之后,选择调试器:
我们选择 LOCAL WINDOWS DEBUGGER 作为多种调试器的开始,后续章节也会介绍其他调试器。
打开菜单 DEBUGGERS–DEBUGGERS OPTIONS ,是调试器的一些选项:
勾选 SUSPEND ON PROCESS ENTRY POINT,调试开始后会在程序入口(entrypoint)暂停下来。
加断点
跟之前静态分析章节一样,对关键判断的代码块做一些重命名以及涂色,在 0x401243 这个跳转指令上设置断点(BREAKPOINT),按 F2 即可:
然后,再另一处 0x40138B 也加上断点:
在 DEBUGGER-BREAKPOINTS-BREAKPOINT LIST 菜单中,能够查看所有的程序断点:
点击任一断点就能跳转到具体位置。
运行程序
现在可以通过菜单 DEBUGGER-START PROCESS 来运行调试器:
如果在本地调试可执行程序将会弹出这个警告窗口。
在加载器分析程序的时候,程序不会在本地执行,但是调试则不是。如果程序是一个病毒或者其他危险的恶意软件时,需要特别的当心。这时候需要使用远程调试器(REMOTE DEBUGGER)在虚拟机中执行程序。这将在后续章节进行介绍。
CRUHEAD’s crackme 这个程序是安全的,按 YES 继续。
由于设置调试在程序入口暂停,确实起作用了,停在 0x401000 这个地址。按空格键可以切换到图形模式,就和使用加载器一样:
IDA View 视图的下方是 Hex View,是当前的内存地址,以及文件偏移(FILE OFFSET)。这个偏移是指的可执行文件中的偏移:
我们看到这里有两个地址,使用 vs code 直接打开 exe 程序,用 hex 模式查看:
0x600 就是该数据在文件中的偏移。
0x401000 是 imagebase + 0x1000,0x1000 是 code 段的 VirtualAddress 的值。
在 IDA 中,默认 G 键是转向一个内存地址的快捷键。按 G 键输入 0x401389,转向我们之前设置的一个断点。可以看到,之前设置的代码块颜色还存在:
OPTIONS - GENERAL - DISASSEMBLY - Display disassembly line parts 下,勾选 Line prefixes (graph):
这样,反汇编图形界面就会显示地址前缀,非常方便。
打开菜单 VIEW-OPEN SUBVIEW-SEGMENTS,读者会发现加载器加载的三个区段,加载在 0x401000 上的 CODE 区段,以及下面的 DATA 和 IDATA 区段。对这些区段的修改都会保存,因为它们已经在加载器中加载。除此之外的修改都不会保存,因为它们是调试器加载的区段,不会保存到 IDA 数据库中。
我们之前的所有设置和修改是操作的 CODE 段。所有的静态分析、重命名等改变的内容都会被保存。
读者想要静态逆向和调试的模块必须是标记了 L 的段。如果有这个标记表示它被加载器和调试器同时加载,既能够实施静态分析,同时在动态调试时也不会丢失这些静态分析的信息。
回到之前的程序入口,按 F9 继续执行,程序出现之后,输入用户名和密码:
点击 OK。
修改标志位
程序执行到了第一个断点(0x40138B)处:
可以看到图中的箭头在闪烁,说明,下一步程序会走这边。
现在,al 也就是 eax 寄存器的值是 0x6e,cmp 指令会操作标志符。 jb 指令会根据标志位来决定是否跳转。
CMP 指令用于比较两个操作数。它的主要功能是将第一个操作数与第二个操作数相减,但不保存结果,只更新标志位。
- ZF(Zero Flag) :如果两个操作数相等,ZF 被置为 1;否则为 0。
- SF(Sign Flag) :如果结果为负数,SF 被置为 1;否则为 0。
- OF(Overflow Flag) :如果有符号数的溢出发生,OF 被置为 1;否则为 0。
- CF(Carry Flag) :如果第一个操作数小于第二个操作数(即结果为负),CF 被置为 1;否则为 0。
由于 0x6e 大于 0x41,没有借位,所以 C 标志位还是 0:
看下面的图,JB 在 C 为 1 的时候才跳转,所以会走左边的流程。
按 F8 单步走,走到绿色流之后,又会与 Z 比较,这次跳转使用的是 jnb 指令,jnb 就是 jb 的否定。
无论走哪个分支,都会发现程序在执行一个循环,每个循环读取用户名的一个字符并将它和 0x41 进行比较,如果任何一个字符小于0x41,就会显示错误信息。
如果字符在 0x41 与 0x5A 之间,无操作。
如果字符大于 0x5A,额外调用了 sub_4013D2 方法,将输入的字符串的值都减去 0x20。
打开调试器菜单,点击 TERMINATE PROCESS 结束进程,再点击 START PROCESS 开始调试,这一次输入的用户名 22ricnar,密码 98989898。输入完毕点击 OK,程序会在之前的断点上暂停。
现在绿色箭头在闪烁了,我们看 C 标志位的值,是 1,右键选择 Zero value ,改变该标志位的值:
改完发现,红色的箭头开始闪烁了,因为跳转的条件改变了,继续运行程序,当程序运行到检查 22ricnar 中第二个字符时,绿色箭头又会开始闪烁。继续翻转 CF 标志,将其设置为 0。
后面几次在这个断点上暂停的时候,“ricnar”中的字符都大于 0x41,不会触发 CF 标志,程序都会从红色箭头的路径执行。通过对用户名每个字符的检测之后,程序会执行到最后一个跳转。
对 EAX 和 EBX 比较是否相等。最终程序转向红色箭头部分报错,因为这 2 个寄存器不相等。
由于 EAX 与 EBX 不相等,ZF 标志没有触发。如果手动触发 ZF 标志,改变跳转方向,程序会转向绿色箭头,显示注册成功的信息。
在 ZF 标志上右键单击,选择 INCREMENT VALUE 将 ZF 值改为 1。
目前读者通过调试器实现了之前静态分析章节修改程序显示成功信息的目的。但本章没有对原程序进行任何修改,只是在调试器中改变了标志寄存器的值。
修改 ip
有时候读者也可以不直接修改跳转,可以将鼠标移动至想要执行的那个代码块上,右键单击,选择 SET EIP。
但是你需要确定,这样直接跳转不会引发 BUG。
重新开始调试程序,在第一个断点处(非启动断点),直接把光标移动至需要执行的位置,例如 0x40124c,按 CTRL + N,就是 SET EIP,设置完后,继续程序,就可以看到程序运行ok了:
关注我的微信公众号:二手的程序员。