首页 > 代码库 > OD中的断点与异常
OD中的断点与异常
文章一:http://blog.csdn.net/wowolook/article/details/7607206
1.前言
在我跨入ollydbg的门的时候,就对ollydbg里面的各种断点充满了疑问,以前我总是不明白普通断点,内存断点,硬件断点有什么区别,他们为什么有些时候不能混用,他们的原理是什么,在学习了前辈们的文章以后,终于明白了一些东西。希望这篇文章能让你对硬件断点的原理和使用有一些帮助
2.正文
--------------------------------------------------
i.硬件断点的原理
在寄存器中,有这么一些寄存器,它们用于调试。人们把他们称为调试寄存器,调试寄存器一共有8个名字分别从Dr0-Dr7。所以我们也把调试寄存器简单的称为Drx。
对于Dr0-Dr3的四个调试寄存器,他们的作用是存放中断的地址,例如:401000
对于Dr4,Dr5这两个寄存器我们一般不使用他们,保留
对于Dr6,Dr7这两个寄存器的作用是用来记录你在Dr0-Dr3中下断的地址的属性,比如:对这个401000是硬件读还是写,或者是执行;是对字节还是对字,或者是双字。
好了,从这里你可能明白一些东西。
1. 为什么在OD里面只能下4个硬件断点?
2. 为什么下硬件断点有byte,word,dword只分?
3. 为什么下硬件断点有读,写,执行只分?
ii.关于F4,F8,F7,F2的区别
在ollydbug的help里面只是提到如何使用F7和F8的使用,并没说明他们的实现原理
现在我们来做一个实验
实验一(F4的原理)
1.随便找一个程序,载入OD,构造一个死循环
就象这样:
00400154 > 90 nop //EP停在这里
00400155 90 nop
00400156 90 nop
00400157 90 nop
00400158 ^ EB FA jmp short 天2国际.<ModuleEntryPoint> //构造一个死循环
0040015A 61 popad
0040015B 94 xchg eax,esp
2.对0040015A这一行按下F4,由于死循环,程序一直运行
3.调试器的窗口里,右键--查看调试寄存器
结果在Drx里面显示:
DR0 0040015A //地址
DR1 00000000
DR2 00000000
DR3 00000000
DR6 FFFF0FF0 //断点属性
DR7 00000401
实验二(F8原理)
1.随便找一个程序,载入OD,构造一个子程序的死循环
就像这样
00400154 t> E8 0100D03F call 4010015A //EP,停在这里
00400159 90 nop
0040015A 90 nop
0040015B 90 nop
0040015C 90 nop //对这里下F2断点
0040015D C3 retn // 返回
2.按下F8,由于INT3断点,程序中断在0040015C
3.调试器的窗口里,右键--查看调试寄存器
结果在Drx里面显示:
DR0 00400159 //call的返回地址
DR1 00000000
DR2 00000000
DR3 00000000
DR6 FFFF4FF1 //断点属性
DR7 00000401
实验三(F7原理)
1.随便找一个程序,载入OD
2.双击调试器的窗口里的T标志,将TF从原来的0变成1
3.F9运行
结果程序断在了下面的一行
实验四(F2的原理)
1.用98的notepad吧,载入OD,构造一个死循环
004010CC N> 90 nop //EP,挺在这里
004010CD 90 nop
004010CE ^ EB FC jmp short NOTEPAD.<ModuleEntryPoint> //死循环
004010D0 90 nop //在这里按下F2,普通断点
004010D1 90 nop
2.按下F9,由于死循环,程序一直运行着
3.使用LordPE(不要用ollydump)将这个程序dump下来
4.重新载入OD
来看看成什么样子了
004010CC d> $ 90 nop
004010CD . 90 nop
004010CE .^ EB FC jmp short dumped.<ModuleEntryPoint>
004010D0 CC int3 //这里变成了CC了
004010D1 90 nop
--------------------------------------------------
3.总结
从实验一和实验二我们能清楚的看到,F4是直接将该行的地址放入drx里面,F8是将下一行的地址放入到drx里面,他们都使用了调试寄存器。从实验三中我们知道对于F7来说很可能使用的是将TF置一的办法,也就是说当我们按下F7的时候OD把TF置一。对于F2来说他是将,第一个字节悄悄的修改成了CC,虽然并没有显示给我看到这个是一个CC,当我们按下F2的时候,OD还没有运行,只是把这个表示记录下来,当运行的时候他就把所有标记的字节修改了,尽管还是显示原来的代码,当然当他一暂停下来就又修改回来了。
上面的是实验中,F7的原理只是猜测,还没有很好的办法能证明他就是使用TF,下面我继续猜测一下内存断点的原理
1.将设置的内存断点的地址记录下来
2.对这个地址的内存页面修改其属性
如果是内存写断点,就修改为RE(可读,可执行)
如果是内存访问断点,就修改为NO ACCESS(不可访问)
3.只要访问到这个页面就会产生相应的异常,然后由OD来判断是否与记录的断点一致,从而是否中断下来
--------------------------------------------------
4.后话
对于上面的F7和内存断点的原理,我还没想出什么好的办法去找出OD的原理,或许去调试一下ollydbg.exe是一个不错的建议。如果有哪位兄弟知道有什么好办法,希望能告诉我。当然也很欢迎各位和我讨论。
文章二:http://www.voidcn.com/blog/nightsay/article/p-3414045.html
今天继续接着上次的分析,来继续分析OD原理,这次先分析OD最基本的功能,断点,单步和运行到指定位置。
类似的文章前辈们已经写了很多了,这里只是将前辈们的思路实践了一下,顺便加入一点自己的理解和认识!
一般载入OD之后,往往大家会先下断点,或者直接单步运行。
一般当我们着手分析一个程序的时候总是会下一个INT 3断点,其实这个就是F2的原理
随便载入一个程序,点一下F2,然后用ollydbg保存的时候,发现提示说是文件未被修改,其实这个也能理解,因为我们按下F2的时候,程序还没被运行,所以dump下来吧,但是dump需要要求程序连同f2能够断下,所以改一下程序:
构建一个这样的死循环,这样,程序在运行的时候会一直在013B2165前运行,而断点在后,内存中断点被记录,所以dump下来就可以看到程序的变化
Dump下重新加载:
Int 3粗线了,原理得证明(基址加载的时候肯定会变化)
F4原理:
改一个这样的程序
看一下调试寄存器
F4后将F4地址放在了调试寄存器里,运行程序的时候就会中断到那个地方
F8(步过)原理:
随便写一个程序,然后修改一下汇编,使之构成一个循环
在00F11DE8处F2断点
在函数的时候F8步过,发现函数停在00F11DE8处,然后在调试窗口右击查看调试寄存器,发现
关于调试寄存器,具体说明请看装载的这篇文章,
有了对调试寄存器大致的了解之后,貌似我们弄懂了一些在逆向时候的问题
比如为什么硬件断点只能有4个(只有4个调试寄存器)
我们发现在DR0处存放了一个中断地址,但是这个地址不是F2断的地方,而是函数的下一条地址,那么在单步的时候,我们在查看一下调试寄存器:
发现调试寄存器根本没有变化,其实这个很好理解,因为在没有执行到函数之前的F8其实就是相当于F2功能
F7原理:步入
这个说法不一,查询了前辈们的说法,认为是标志寄存器T的原因:
当TF被设置位1时,CPU进入单步模式,所谓单步模式就是CPU在每执行一步指令后都产生一个单步中断。主要用于程序的调试。8086/8088中没有专门用来置位和清零TF的命令,需要用其他办法。
自己想了一个方法证明:
构造一个这样的循环:
在运行到call的时候,改变T的标志位,然后直接让程序运行,发现程序停在开始处,得证。
在这和之后,在贴一段前辈们的总结:
将TF置1:程序将断在下一条原子操作处,并将TF位清零。原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行倒结束,中间不会有任何 context switch (切换到另一个线程)。call指令是原子操作,它的作用是:将返回地址压栈,并跳转。所以,在调用call的时候,如果我们将TF置1,那么程序将在call指令的功能完成以后(即将返回地址压栈并跳转)断下来。而执行call内的语句则仅仅是call的逻辑功能(我们理解的功能),并非实际的功能(CPU理解的功能)。
以上差不多硬件断点的一些原理都介绍了,验证的方法其实就是怎么去构造那段汇编代码,其实这个也很好想啊,想验证对函数的操作,步入或者步过,就要构造一个call啊,想要了解运行到断点处和光标处,就要想着让他跳转啊,所以有jmp,然后之所以都构成循环,是因为让程序停在我们需要验证的地方啊。
OD中的断点与异常