吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 25807|回复: 37
上一主题 下一主题
收起左侧

[调试逆向] 硬件断点的原理与实现

  [复制链接]
跳转到指定楼层
楼主
VicZ 发表于 2018-12-31 16:40 回帖奖励
本帖最后由 VicZ 于 2019-1-1 16:18 编辑

硬件断点的原理

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


​                                        图1 调试寄存器

图1为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;

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

​                                                        图2 调试寄存器的设置要求

如图2所示,保存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);

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

本人理解有限,如有错误,请批评指正!

点评

支持一下 花20CB  发表于 2019-3-11 10:58

免费评分

参与人数 6威望 +2 吾爱币 +11 热心值 +5 收起 理由
zozobreak + 1 我很赞同!
Hmily + 2 + 8 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
TuziF + 1 + 1 我很赞同!
lookerJ + 1 热心回复!
fangchang819 + 1 谢谢@Thanks!
小小小英雄啦 + 1 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
talenth 发表于 2021-8-4 11:17
ContextFlags设置的有问题, 应该设置如下

ContextFlags = CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS;

CONTEXT_CONTROL : 获取 eip
CONTEXT_DEBUG_REGISTERS: 获取 dr0-dr7

该值的定义不在msdn上, 在 winnt.h的开头有说明, 说明如下:
// 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 is modified.
//
// If the context record is used as an output parameter to capture the
// context of a thread, then only those portions of the thread's context
// corresponding to set flags will be returned.
//
// CONTEXT_CONTROL specifies SegSs, Rsp, SegCs, Rip, and EFlags.
//
// CONTEXT_INTEGER specifies Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, and R8-R15.
//
// CONTEXT_SEGMENTS specifies SegDs, SegEs, SegFs, and SegGs.
//
// CONTEXT_FLOATING_POINT specifies Xmm0-Xmm15.
//
// CONTEXT_DEBUG_REGISTERS specifies Dr0-Dr3 and Dr6-Dr7.
推荐
rixo 发表于 2019-6-17 11:11
你获取的时候用的是
g_Context.ContextFlags = CONTEXT_CONTROL;
我在查看微软帮助文档时没有发现对ContextFlags 这个值的详细说明,不知道这个值的设置有哪些类型,代表什么含义啊
沙发
Godfather.Cr 发表于 2019-1-1 01:10
3#
tzblue 发表于 2019-1-1 07:12
个别图显示“内部交流……”


4#
gunxsword 发表于 2019-1-1 08:41
代码写的挺好,就是不全.还没转好,图挂了.
5#
涛之雨 发表于 2019-1-1 09:50
链接挂了楼主
6#
afanti 发表于 2019-1-1 12:52
图片飞了
7#
无为无敌 发表于 2019-1-1 12:53
呵呵,不错啊
8#
小小小英雄啦 发表于 2019-1-1 13:58
写得不错的,图挂了~
9#
 楼主| VicZ 发表于 2019-1-1 16:20 |楼主
Godfather.Cr 发表于 2019-1-1 01:10
图片盗链了,修复一下

已经修复
10#
yjn866y 发表于 2019-1-2 08:04
高手!多谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 10:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表