硬件断点的原理与实现

硬件断点的原理

Intel 80306以上的CPU给我们提供了调试寄存器用于软件调试,硬件断点是通过设置调试寄存器实现的。

上图为Intel手册提供的32位操作系统下8个调试寄存器的图示(Intel手册卷3 17章第二节 Debug Registers,有兴趣的朋友可以查阅),根据介绍,DR0-DR3为设置断点的地址,DR4和DR5为保留,DR6为调试异常产生后显示的一些信息,DR7保存了断点是否启用、断点类型和长度等信息。

我们在使用硬件断点的时候,就是要设置调试寄存器,将断点的位置设置到DR0-DR3中,断点的长度设置到DR7的LEN0-LEN3中,将断点的类型设置到DR7的RW0-RW3中,将是否启用断点设置到DR7的L0-L3中。设置硬件断点需要的DR0-DR3很简单,就是下断点的地址,DR7寄存器很复杂,位段信息结构体如下:

typedef struct _DBG_REG7
{
        /*
        // 局部断点(L0~3)与全局断点(G0~3)的标记位
        */
        unsigned L0 : 1;  // 对Dr0保存的地址启用 局部断点
        unsigned G0 : 1;  // 对Dr0保存的地址启用 全局断点
        unsigned L1 : 1;  // 对Dr1保存的地址启用 局部断点
        unsigned G1 : 1;  // 对Dr1保存的地址启用 全局断点
        unsigned L2 : 1;  // 对Dr2保存的地址启用 局部断点
        unsigned G2 : 1;  // 对Dr2保存的地址启用 全局断点
        unsigned L3 : 1;  // 对Dr3保存的地址启用 局部断点
        unsigned G3 : 1;  // 对Dr3保存的地址启用 全局断点
                                          /*
                                          // 【以弃用】用于降低CPU频率,以方便准确检测断点异常
                                          */
        unsigned LE : 1;
        unsigned GE : 1;
        /*
        // 保留字段
        */
        unsigned Reserve1 : 3;
        /*
        // 保护调试寄存器标志位,如果此位为1,则有指令修改条是寄存器时会触发异常
        */
        unsigned GD : 1;
        /*
        // 保留字段
        */
        unsigned Reserve2 : 2;

        unsigned RW0 : 2;  // 设定Dr0指向地址的断点类型
        unsigned LEN0 : 2;  // 设定Dr0指向地址的断点长度
        unsigned RW1 : 2;  // 设定Dr1指向地址的断点类型
        unsigned LEN1 : 2;  // 设定Dr1指向地址的断点长度
        unsigned RW2 : 2;  // 设定Dr2指向地址的断点类型
        unsigned LEN2 : 2;  // 设定Dr2指向地址的断点长度
        unsigned RW3 : 2;  // 设定Dr3指向地址的断点类型
        unsigned LEN3 : 2;  // 设定Dr3指向地址的断点长度
}DBG_REG7, *PDBG_REG7;

需要注意的是,设置硬件断点时,断点的长度、类型和地址是有要求的。

上图所示,保存DR0-DR3地址所指向位置的断点类型(RW0-RW3)与断点长度(LEN0-LEN3),状态描述如下:
?        00:执行         01:写入        11:读写
?        00:1字节       01:2字节      11:4字节

设置硬件执行断点时,长度只能为1(LEN0-LEN3设置为0时表示长度为1)

设置读写断点时,如果长度为1,地址不需要对齐,如果长度为2,则地址必须是2的整数倍,如果长度为4,则地址必须是4的整数倍。

原理大概就是这么多了,下面就是实现了。

硬件断点的实现

实现硬件断点,首先要获取当前线程环境

//获取线程环境
CONTEXT g_Context = { 0 };
g_Context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread, &g_Context);

在CONTEXT结构体中,存放了诸多当前线程环境的信息,以下是从winnt.h文件中找到的CONTEXT结构体

typedef struct _CONTEXT {

    //
    // The flags values within this flag control the contents of
    // a CONTEXT record.
    //
    // If the context record is used as an input parameter, then
    // for each portion of the context record controlled by a flag
    // whose value is set, it is assumed that that portion of the
    // context record contains valid context. If the context record
    // is being used to modify a threads context, then only that
    // portion of the threads context will be modified.
    //
    // If the context record is used as an IN OUT parameter to capture
    // the context of a thread, then only those portions of the thread‘s
    // context corresponding to set flags will be returned.
    //
    // The context record is never used as an OUT only parameter.
    //

    DWORD ContextFlags;

    //
    // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
    // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
    // included in CONTEXT_FULL.
    //

    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
    //

    FLOATING_SAVE_AREA FloatSave;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.
    //

    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_INTEGER.
    //

    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_CONTROL.
    //

    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;              // MUST BE SANITIZED
    DWORD   EFlags;             // MUST BE SANITIZED
    DWORD   Esp;
    DWORD   SegSs;

    //
    // This section is specified/returned if the ContextFlags word
    // contains the flag CONTEXT_EXTENDED_REGISTERS.
    // The format and contexts are processor specific
    //

    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;

从CONTEXT结构体中我们可以看到存放了调试寄存器 Dr0-Dr3和Dr6、Dr7,通过设置这些寄存器我们可以实现硬件断点。

已经获取了当前线程环境,接下来就是设置调试寄存器

//传入下断点的地址、类型、长度
void SetHardBP(DWORD addr, BreakPointHard type, BreakPointLen len)
{
    //利用上文中的DR7寄存器位段信息
        DBG_REG7 *pDr7 = (DBG_REG7 *)&g_Context.Dr7;

    if (len == 1)
        {
        //两字节的对齐粒度
                addr = addr - addr % 2;
        }
        else if (len == 3)
        {
        //四字节的对齐粒度
                addr = addr - addr % 4;
        }

        if (pDr7->L0 == 0)
        {
                g_Context.Dr0 = addr;   //利用Dr0寄存器存放地址
                pDr7->RW0 = type;       //Dr7寄存器中的RW0设置类型
                pDr7->LEN0 = len;                //Dr7寄存器中的LEN0设置长度
                pDr7->L0 = 1;                    //Dr7寄存器中的L0启用断点
        }
        else if (pDr7->L1 == 0)
        {
                g_Context.Dr1 = addr;
                pDr7->RW1 = type;
                pDr7->LEN1 = len;
                pDr7->L1 = 1;
        }
        else if (pDr7->L2 == 0)
        {
                g_Context.Dr2 = addr;
                pDr7->RW2 = type;
                pDr7->LEN2 = len;
                pDr7->L2 = 1;
        }
        else if (pDr7->L3 == 0)
        {
                g_Context.Dr3 = addr;
                pDr7->RW3 = type;
                pDr7->LEN3 = len;
                pDr7->L3 = 1;
        }
}

调试寄存器的信息设置好之后,我们要将当前环境保存

//设置当前环境
SetThreadContext(hThread, &g_Context);

由此,硬件断点的大致实现思路已经完成。

转自:https://www.52pojie.cn/forum.php?mod=viewthread&tid=846934&extra=page%3D3%26filter%3Dauthor%26orderby%3Ddateline

原文地址:https://www.cnblogs.com/yilang/p/12179305.html

时间: 2024-10-10 06:58:50

硬件断点的原理与实现的相关文章

如何对抗硬件断点--- 调试寄存器

1.前言 在我跨入ollydbg的门的时候,就对ollydbg里面的各种断点充满了疑问,以前我总是不明白普通断点,内存断点,硬件断点有什么区别,他们为什么 有些时候不能混用,他们的原理是什么,在学习了前辈们的文章以后,终于明白了一些东西.希望这篇文章能让你对硬件断点的原理和使用有一些帮助 2.正文-------------------------------------------------- i.硬件断点的原理 在寄存器中,有这么一些寄存器,它们用于调试.人们把他们称为调试寄存器,调试寄存器

多线程断点下载原理(java代码实例演示)

其实多线程断点下载原理,很简单的,那么我们就来先了解下,如何实现多线程的断点下载,首先:你必须明白第一点,那么就是,什么是多线程下载,该知识点可以查看本博客上一篇文章,Android之多线程下载原理,断点下载呢,其实就是在这个的基础之上添加了一些东西,那么添加了什么东西了,现在来做一个详细的了解. 1.在下载的过程中,边下载,变用一个文件来记录下载的位置,也就是下载了多少的数据 1.创建文件 2.记录下载多少数据 3.存储数据 2.第二次下载的时候,就去读取文件中是否存有数据,读取上次下载的位置

硬件断点 DrxHook

硬件断点的实现需要依赖于调试寄存器 DR0~DR7  调试寄存器 DR0~DR3-----调试地址寄存器DR4~DR5-----保留DR6 -----调试状态寄存器 指示哪个调试寄存器被命中DR7 -----调试控制寄存器 关于Dr7寄存器每个标志位的解释: 总结如下 DR7调试控制寄存器: R/W0~R/W3:与DR0~DR3相对应,用来指定监控地址的访问类型,表示意义如下:              00:仅当执行对应的地址时中断              01:仅当写入对应的地址时中断 

Xcode中使用数据(硬件)断点调试

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 在Xcode的GUI界面中只能添加软断点,而无法增加硬断点.但是在dbg窗口中我们可以使用llvm的watchpoint指令添加硬件断点,也就是所谓的数据断点. 我们可以给类的实例变量设置硬件断点,可以选择监视类型是read,write或者是read_write. 下面举一个例子:比如我们的target对象中包括一个PlayerData对象,其中一个成员变量为

ring3硬件断点

4个断点寄存器DR0~DR3用来设置断点的线性地址. DR6为状态寄存器,DR7为控制寄存器. DR4和DR5保留.当CR4.DE==1时,访问DR4和DR5产生#UD异常:IF CR4.DE==0,访问DR4和DR5将是对DR6和DR7的访问. 下面这张表非常清楚: |---------------|----------------| Dr0|                 用于一般断点的线性地址 |---------------|----------------| Dr1|        

windbg-bp、 bm、 bu、 bl、 bc、 ba(断点、硬件断点)

bp bp 命令是在某个地址下断点, 可以 bp 0x7783FEB 也可以 bp MyApp!SomeFunction . 对于后者,WinDBG 会自动找到MyApp!SomeFunction 对应的地址并设置断点. 但是使用bp的问题在于: 1)当代码修改之后,函数地址改变,该断点仍然保持在相同位置,不一定继续有效: 2)WinDBG 不会把bp断点保存工作空间中 bp  Address或bp 伪寄存器或bp符号名称: 0:000> x Simple1Demo!CSimple1DemoAp

java多线程断点下载原理(代码实例演示)

原文:http://www.open-open.com/lib/view/open1423214229232.html 其实多线程断点下载原理,很简单的,那么我们就来先了解下,如何实现多线程的断点下载,首先:你必须明白第一点,那么就是,什么是多线程下载,该知识点可以查看本博客上一篇文章,Android之多线程下载原理,断点下载呢,其实就是在这个的基础之上添加了一些东西,那么添加了什么东西了,现在来做一个详细的了解. 1.在下载的过程中,边下载,变用一个文件来记录下载的位置,也就是下载了多少的数据

硬件断点、陷阱和JTAG

R/W 0 和LEN 0对应断点0线性地址: DR则是调试寄存器 硬件断点: 基于CPU的调试寄存器 可以对代码.数据访问和IO访问设置断点 断点被触发时,CPU产生的是1号异常 受调试寄存器的数量限制 WinDbg 的ba命令设置的便是硬件断点 在多处理器系统中,硬件断点是与CPU相关的,也就是说针对一个CPU设置的硬件断点并适用于其他CPU X86 经典异常 比如说INT 3 (CC)断点最终会调用 3Breakpoint 单步执行则是 1号表Debug Exception 代码除0则触发

(转)调试程序时设置断点的原理

简单总结:有软件断点和硬件断点 软件断点:软件断点在X86系统中为中断指令INT 3,其二进制代码opcode是0xCC.当程序执行到INT 3指令时,会引发软件中断.操作系统的INT 3中断处理器会寻找注册在该进程上的调试处理程序.从而像Windbg和VS等等调试器就有了上下其手的机会.程序出错时常看到的”烫烫烫“.”锟斤拷“.”屯屯屯“等与这个终端指令有关 硬件断点:X86系统提供8个调试寄存器(DR0~DR7)和2个MSR用于硬件调试. 转自:https://zhuanlan.zhihu.