标签:
一、构造 C 数据类型
二、通用 CPU 寄存器
八个通用寄存器:EAX, EDX, ECX, ESI, EDI, EBP, ESP 和 EBX。还有很多别的寄存器,遇
到的时候具体讲解。 这八个通用寄存器各有不同的用途, 了解它们的作用对于我们设计调试
器是至关重要的。 让我们先简略的看一看每个寄存器和功能。 最后我们将通过一个简单的实
验来说明他它们的使用方法。
操作。许多优化的 x86 指令集都专门设计了针对 EAX 寄存器的读写和计算指令。列如从最
基本的加减,比较到特殊的乘除操作都有专门的 EAX 优化指令。
前面我们说了,函数的返回值也是存储在 EAX 寄存器里。这一点很重要,因为通过返
回的 EAX 里的值我们可以判断函数是执行成功与否,或者得到确切返回值。
它辅助 EAX 完成更多复杂的计算操作像乘法和除法。它虽然也能当作通用寄存器使用,不
过更多的是结合 EAX 寄存器进行计算操作。
者数字统计。有一点很重要,ECX 寄存器的计算是向下而不是向上的(简单理解就是用于
循环操作时是由大减到小的)
ESI 寄存器是源操作数指针,存储着输入的数据流的位置。EDI 寄存器是目的操作数指针 ,
存储了计算结果存储的位置。简而言之,ESI(source index)用于读,EDI(destination index )
用于写。用源操作数指针和目的操作数指针,极大的提高了程序处理数据的效率。
作。 当一个函数被调用的时候, 函数需要的参数被陆续压进栈内最后函数的返回地址也被压
进。ESP 指着栈顶,也就是返回地址。EBP 则指着栈的底端。有时候,编译器能够做出优
化,释放 EBP,使其不再用于栈的操作,只作为普通的寄存器使用。
执行一个程序的成千上万的代码的时候,EIP 会实时的指向当前 CPU 马上要执行到的位置。
了一个接口让调试器和 CPU 交互,以便能够获取和修改这些值。我们将在后面的操作系统
章节详细的单独的讲解。
三、栈
在开发调试器的时候, 栈是一个非常重要的结构。 栈存储了与函数调用相关的各种信息 ,
包括函数的参数和函数执行完成后返回的方法。ESP 负责跟踪栈顶,EBP 负责跟踪栈底。
栈从内存的高地址像低地址增长。 让我们用前面编写的函数 my_sock()作为例子讲解栈是如
何工作的。
Function Call in C
_____________________________________________________________________
int my_socks(color_one, color_two, color_three);
_____________________________________________________________________
Function Call in x86 Assembly
_____________________________________________________________________
push color_three
push color_two
push color_one
call my_socks
四、断点
当我们需要让被调试程序暂停的时候就需要用到断点。 通过暂停进程, 我们能观察
变量,堆栈参数以及内存数据,并且记录他们。断点有非常多的好处,当你调试进程的时候
这些功能会让你觉得很舒爽。断点主要分成三种:软件断点,硬件断点,内存断点。他们有
非常相似的工作方式,但实现的手段却各不相同。
1、软件断点
如果我们先前讲解的指令发生在 0x4433221 这个地址,一般是这样显示的:
_______________________________________________________________________________
0x44332211: 8BC3 MOV EAX, EBX
_______________________________________________________________________________
这里显示了地址,操作码,和高级的汇编指令。为了在这个地址设置断点,暂停 CPU ,
我们将从 2 个字节的 8BC3 操作码中换出一个单字节的操作码。这个单字节的操作码也就
是 3 号中断指令(INT 3) ,一条能让 CPU 暂停的指令。3 号中断转换成操作码就是 0xCC 。
这里是设置断点前和设置断点后的对比:
在断点被设置前的操作码
_______________________________________________________________________________
0x44332211: 8BC3 MOV EAX, EBX
_______________________________________________________________________________
断点被设置后的操作码
_______________________________________________________________________________
0x44332211: CCC3 MOV EAX, EBX
_______________________________________________________________________________
2、硬件断点
硬件断点非常有用, 尤其是当想在一小块区域内设置断点, 但是又不能修改它们的时候 。
这种类型的断点被设置在 CPU 级别,并用特定的寄存器:调试寄存器。一个 CPU 一般会有
8 个调试寄存器(DR0 寄存器到 DR7 寄存器) ,它们被用于管理硬件断点。调试寄存器 DR0
到调试寄存器 DR3 存储硬件断点地址。这意味着你同一时间内最多只能有 4 个硬件断点。
DR4 和 DR5 保留。DR6 是状态寄存器,说明了被断点触发的调试事件的类型。DR7 本质上
是一个硬件断点的开关寄存器,同时也存储了断点的不同类型。通过在 DR7 寄存器里设置
不同标志,能够创建以下几种断点:
· 当特定的地址上有指令执行的时候中断
. 当特定的地址上有数据可以写入的时候
· 当特定的地址上有数据读或者写但不执行的时候
这非常有用,当你要设置特定的断点(至多 4 个) ,又不能修改运行的进程的时候。
3、内存断点
内存断点其实不是真正的断点。当一个调试器设置了一个内存断点的时候,它其实是
改变了内存中某个块或者页的权限。 一个内存页是操作系统处理的最小的内存单位。 一个内
存页被申请成功以后,就拥有了一个权限集,它决定了内存该如何被访问。下面是一些内存
页的访问权限的例子:
可执行页 允许执行但不允许读或写,否则抛出访问异常
可读页 只允许从页面中读取数据,其余的则抛出访问异常
可写页 允许将数据写入页面
任何对保护页的访问都会引发异常,之后页面恢复访问前的状态
大多数系统允许你综合这些权限。举个例子,你能有在内存中创建一个页面,既能
读又能写, 同时另一个页面既能读又能执行。 每一个操作系统都有内建的函数让你查询当前
内存页(并不是所有的)的权限,并且修改它们。
这里我们感兴趣的是保护页(Guard Page) 。这种类型的页面常被用于:分离堆和栈或者
确保一部分内存数据不会增长出边界。 另一种情况, 就是当一个特定的内存块被进程命中 (访
问)了,就暂停进程。举个例子,如果我们在逆向一个网络服务程序,在其接收到网络数据
包以后,我们在存储数据包的内存上设置保护页,接着运行程序,一旦有任何对保护页的访
问,都会使 CPU 暂停,抛出一个保护页调试异常,这时候我们就能确定程序是在什么时候
用什么方式访问接收到的数据了。 之后再进一步跟踪观察访问内存的指令, 继而确定程序对
数据做了什么操作。 这种断点同时也解决了软件断点数据更新的问题, 因为我们没有修改任
何运行着的代码。
标签:
原文地址:http://www.cnblogs.com/duwanjiang/p/5483751.html