问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

X64硬件断点(指令断点)技术详解

创作时间:
作者:
@小白创作中心

X64硬件断点(指令断点)技术详解

引用
CSDN
1.
https://blog.csdn.net/weixin_43751677/article/details/145888026

硬件断点是软件调试中一种强大的工具,它允许开发者在程序执行到特定地址时触发中断,从而进行更精细的控制和观察。本文将深入探讨X64架构下的硬件断点机制,包括调试寄存器的功能、访问权限以及如何通过代码设置硬件断点。

调试寄存器概述

在X64架构中,调试寄存器主要包括断点地址寄存器(DR0-DR3)、调试状态寄存器(DR6)和调试控制寄存器(DR7)。这些寄存器用于设置和监视最多4个断点,每个断点可以指定线性地址、断点位置的长度、触发条件以及是否启用。

调试寄存器的功能

  • 断点地址寄存器(DR0-DR3):保存断点的32/64位线性地址。断点比较在物理地址转换之前进行。
  • 调试状态寄存器(DR6):报告生成调试或断点异常时生效的条件。
  • 调试控制寄存器(DR7):指定导致生成断点的内存或I/O访问形式。

调试寄存器的访问

调试寄存器是特权资源,只能在实地址模式、SMM或CPL为0的保护模式下访问。以下是32位和64位架构下的调试寄存器布局:

调试控制寄存器(DR7)详解

调试控制寄存器(DR7)用于启用或禁用断点并设置断点条件。以下是DR7寄存器中各个标志的详细说明:

  • L0-L3(本地断点使能)标志(位0, 2, 4, 6):启用当前任务的相关断点条件。处理器会在每次任务切换时自动清除这些标志。
  • G0-G3(全局断点使能)标志(位1, 3, 5, 7):启用所有任务的相关断点条件。处理器在任务切换时不会清除这些标志。
  • LE和GE(本地和全局精确断点使能)标志(位8, 9):在P6系列处理器及后续IA-32处理器中不支持。
  • RTM(受限事务性内存)标志(位11):启用RTM事务区域的高级调试。
  • GD(通用检测使能)标志(位13):启用调试寄存器保护,防止在访问调试寄存器时产生干扰。
  • R/W0-R/W3(读/写)字段(位16, 17, 20, 21, 24, 25, 28, 29):指定断点的条件。当DE标志被设置时,处理器按以下方式解释这些位:
  • 00:仅在指令执行时中断
  • 01:仅在数据写入时中断
  • 10:I/O读写中断
  • 11:中断数据读取或写入,但不中断指令获取
  • LEN0-LEN3(长度)字段(位18、19、22、23、26、27、30和31):指定断点地址寄存器中指定地址处的内存位置的大小。

调试状态寄存器(DR6)详解

调试状态寄存器(DR6)用于报告在生成最后一个调试异常时采样的调试条件。以下是DR6寄存器中各个标志的详细说明:

  • B0-B3(断点条件检测)标志(位0到3):表示其关联的断点条件在生成调试异常时满足。
  • BLD(总线锁定检测)标志(位11):表示调试异常是由于总线锁定被断言而触发的。
  • BD(调试寄存器访问检测)标志(位13):表示下一条指令流中的指令访问了一个调试寄存器。
  • BS(单步执行)标志(位14):表示调试异常是由单步执行模式触发的。
  • BT(任务切换)标志(位15):表示调试异常是由于任务切换引起的。
  • RTM(受限事务性内存)标志(位16):表示调试异常发生在RTM区域内部。

代码示例:设置硬件断点

下面是一个来自泰坦反汇编引擎的函数示例,展示了如何设置硬件断点:

__declspec(dllexport) bool TITCALL SetHardwareBreakPoint(ULONG_PTR bpxAddress, DWORD IndexOfRegister, DWORD bpxType, DWORD bpxSize, LPVOID bpxCallBack)
{
    if((bpxAddress % 2) != 0)
        if((bpxAddress % 4) != 0)
            if((bpxAddress % 8) != 0)
    if(!IndexOfRegister)
    {
        if(!DebugRegister[0].DrxEnabled)
            IndexOfRegister = UE_DR0;
        else if(!DebugRegister[1].DrxEnabled)
            IndexOfRegister = UE_DR1;
        else if(!DebugRegister[2].DrxEnabled)
            IndexOfRegister = UE_DR2;
        else if(!DebugRegister[3].DrxEnabled)
            IndexOfRegister = UE_DR3;
    }
    uint dr7;
    GetContextData(UE_DR7, &dr7);
    DebugRegister[hwbpIndex].DrxExecution = false;
    switch(bpxType)
    {
        case UE_HARDWARE_EXECUTE:
            hwbpSize = SIZE_1;
            DebugRegister[hwbpIndex].DrxExecution = true;
        case UE_HARDWARE_READWRITE:
            hwbpType = TYPE_READWRITE;
    }
    hwbpMode = MODE_LOCAL;
    dr7.HWBP_MODE[hwbpIndex] = hwbpMode;
    dr7.HWBP_SIZE[hwbpIndex] = hwbpSize;
    dr7.HWBP_TYPE[hwbpIndex] = hwbpType;
    for(unsigned int i = 0; i < hListThread.size(); i++)
    {
        SetContextDataEx(hListThread.at(i).hThread, UE_DR7, dr7);
        SetContextDataEx(hListThread.at(i).hThread, IndexOfRegister, bpxAddress);
    }
    DebugRegister[hwbpIndex].DrxBreakPointType = bpxType;
    DebugRegister[hwbpIndex].DrxBreakPointSize = bpxSize;
    DebugRegister[hwbpIndex].DrxEnabled = true;
    DebugRegister[hwbpIndex].DrxBreakAddress = (ULONG_PTR)bpxAddress;
    DebugRegister[hwbpIndex].DrxCallBack = (ULONG_PTR)bpxCallBack;
}

异常识别:陷阱类型与错误类型

硬件断点触发的异常可能是陷阱类型(trap)或错误类型(fault)。关键在于确定异常发生的时间点是在指令执行前还是指令执行后。这直接影响到栈中的返回地址以及是否需要在中断处理程序中设置EFLAGS的RF位。

参考资料

  • Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 3: System Programming Guide
  • 《软件调试 卷1》

本文主要参考了Intel白皮书的VOLUME 3系统编程指南篇的CHAPTER 18,详细介绍了DEBUG、BRANCH PROFILE、TSC以及Intel资源管理技术(Intel RDT)特性。对于更深入的学习,建议参考《软件调试 卷1》,内容详实且与白皮书内容相近。此外,X64DBG使用的反汇编引擎源码中也包含了硬件异常响应的具体实现,可以通过逆向分析Windows的KiDebugTrapOrFault函数或Linux的源码来进一步了解。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号