logo资料库

IDA PRO 教程-使用IDA的跟踪调试功能.pdf

第1页 / 共7页
第2页 / 共7页
第3页 / 共7页
第4页 / 共7页
第5页 / 共7页
第6页 / 共7页
第7页 / 共7页
资料共7页,全文预览结束
使用 IDA 的跟踪功能 上一篇调试器教程给出了如何用常用的调试命令来调试一个小程序。本教程将给出另一种方 法。下面我们利用 IDA 调试器的跟踪功能来调试这个小程序。 一个小 bug 程序 这个小程序只是简单的计算了一下一组数据(1,2,3,4,5)的平均值。这组数据被保存在两个 数组里,一个是 8bit 的数值,一个是 32bit 数值表示。 #include char char_average(char array[], int count) { int i; char average; average = 0; for (i = 0; i < count; i++) average += array[i]; average /= count; return average; } int int_average(int array[], int count) { int i, average; average = 0; for (i = 0; i < count; i++) average += array[i]; average /= count; return average; } void main(void) { char chars[] = { 1, 2, 3, 4, 5 }; int integers[] = { 1, 2, 3, 4, 5 }; printf("chars[] - average = %d\n", char_average(chars, sizeof(chars))); printf("integers[] - average = %d\n", int_average(integers, sizeof(integers))); }
运行它,我们得到如下结果: chars[] - average = 3 integers[] - average = 1054228 可以看出在整数方式下计算的结果是错误的,我们用 IDA 的调试器来分析一下吧。 什么是跟踪 跟踪允许你记录应用程序运行时的变化信息。我们把跟踪信息称为“跟踪事件”。 IDA 记录把跟踪事件记录在一个跟踪缓存区中。跟踪缓存区的大小可以设定为无限大(此时 你需要很多内存),或者固定的大小(这种情况下,新的跟踪事件会覆盖老的跟踪事件)。在 我们这个例子中,由于我们调试的程序非常小,我们把它设定为无穷大:选择在 Debugger 主菜单下的 Trace 子菜单中的 Tracing options,设定 Tracing Buffer Size 为 0。 IDA 提供几种不同的跟踪机制: 1、 指令跟踪:IDA 将会记录每一条指令的执行,并保存寄存器数值,通过使用这些信息, 你可以找出应用程序的执行过程,并可找出哪条指令修改了哪个寄存器。 2、 函数跟踪:IDA 将会记录所有的函数调用和函数返回。 3、 读写-写-执行跟踪:IDA 将会记录一个对指定地址的所有访问。这种机制相当于是不停 止的断点。 对每种跟踪机制,都会记录相应的跟踪事件到跟踪缓存区,也可以保存到一个txt 的文件中, 同样可以通过 Tracing options 里的选项来设定。
指令和函数跟踪 为了定位在程序中的 bug 所在,我们要记录所有程序的执行指令,函数调用和函数返回。我 们 不 想 记 录 在 main() 函 数 之 前 的 指 令 。 因 此 我 们 把 光 标 放 置 在 main() 的 开 始 位 置 (0x4011A1),使用快捷键F4,开始运行程序并执行到光标位置。我们再通过点按跟踪工具 条上的相应图标打开指令和函数跟踪功能,然后我们继续运行程序直到到达 main()函数的 结尾(0x40120A)。注意Runto(执行到… )命令在调试菜单和鼠标右键菜单中都可选择。
跟踪回溯 IDA 现在已记录了程序的运行。我们点击 Tracing 跟踪工具条上 Tracing Window 跟踪窗口 的按钮,来查看跟踪事件。 如果我们在 Tracing Window 跟踪窗口中点中了一条跟踪事件,IDA 更新了屏幕上相应信息, 以显示跟踪事件发生时,程序的运行状态。下面是几条我们通常关心的信息: IDA’s titlebar(IDA 工具条):Backtracing 代表前面跟踪事件的屏幕信息。 Trace event icons(跟踪事件图标,在跟踪窗口的第一列):代表记录的跟踪事件类型:指 令执行,函数调用,函数返回… 例如,我们在_printf()函数的结尾,我们看两个不同的跟踪事 件,一个代表指令,一个代表函数返回。 Result column(结果列):将包含跟踪事件指定的信息,当是指令事件的时候,它显示被修 改的寄存器,注意IP寄存器,由于会被所有指令修改,因此不会显示IP寄存器。 Register arrows(寄存器箭头指示,在反汇编窗口中):反应在指令执行之前的寄存器数值。 Registers windows(寄存器窗口):对指令跟踪事件,每个寄存器窗口会显示指令执行前的 寄存器数值,刚被更改的寄存器也会用颜色标记出来。
跟踪缓存区内搜索 还记得那个奇怪的平均值结果吗?它是18259104 (0x1169CA0),在Search搜索菜单中的Trace window's Search(跟踪窗口搜索)命令,会至少找到一条与这个数值相关的跟踪事件。事 实上,如果我们启动搜索,会发现这个数值在int_average+1E这个地址: 通过观察跟踪,我们试着看一下,程序是如何使用这个值的。 在int_average+1E:我们发现一条"idiv esi"指令,它的EAX中包含这个数值。 在int_average+27:我们发现int_average()返回到它的调用程序main(),这个数值是 int_average()的返回值。 在 main+5c:这个数值被 printf()打印到屏幕上。 这是一个错误的计算结果!
错误的循环 现在我们在看一下在 idiv esi 指令之前的指令跟踪情况。 我们可以观察到一个小的循环来累加我们要计算的数据,跟踪窗口内显示了 3 个最新的循 环。我们看一下循环中止的条件,它是比较ESI 和 EAX 中的数值,那么在ESI 中是什么呢? 寄存器窗口给出了这个数值,它显示 ESI=0X14(20),我们不是希望它执行 5 次吗?怎么是 20 次?
Bug 修复 我们再浏览一下这个问题循环之前的指令,以查找 ESI 中的这个奇怪的数值从哪里来。 监视 我们可以添加监视,通过监视观察在程序执行过程中数据的变化。 在第一个循环之前,int_average+F,我们看到 ESI 从 int_average()的 count 参数中获得。 我们通过 IDA 的参数识别跟踪技术(PIT),很容易定位到 PUSH 14h 这条指令,它将错误 的参数传递了进来。现在我们在仔细看一下我们的 C 源程序,就能找到这个错误了:我们 使用了 sizeof()这个函数,它返回的是数组中的字节数,而不是数组的成员数。 风暴译 2007-09-21 QQ:719110750
分享到:
收藏