Windows环境下的调试器探究——硬件断点

  1. 与软件断点与内存断点不同,硬件断点不依赖被调试程序,而是依赖于CPU中的调试寄存器

  2. 调试寄存器有7个,分别为Dr0~Dr7

  3. 用户最多能够设置4个硬件断点,这是由于只有Dr0~Dr3用于存储线性地址。

  4. 其中,Dr4和Dr5是保留的。

假如在Dr0寄存器中写入线性地址,是否所有线程都会受影响?其实不会,每个线程都拥有一份独立的寄存器,切换线程时,寄存器的值也会被切换。

设置硬件断点

Dr0~Dr3用于设置硬件断点,由于只有4个断点寄存器,所以最多只能设置4个硬件调试断点。在7个寄存器里面,Dr7是最重要的寄存器

L0/G0 ~ L3/G3:控制Dr0~Dr3是否有效,局部还是全局;每次异常后,Lx都被清零,Gx不清零。

若Dr0有效,L0=1则为局部,G0=1则为全局,以此类推

断点长度(LENx):00(1字节)、01(2字节)、11(4字节)

通过DR7的LEN控制

断点类型(R/Wx):00(执行断点)、01(写入断点)、11(访问断点)

流程

被调试进程:

1)CPU执行时检测当前线性地址与调试寄存器(Dr0~Dr3)中的线性地址相等。

2)查IDT表找到对应的中断处理函数(nt!_KiTrap01) 

3)CommonDispatchException 

4)KiDispatchException 

5)DbgkForwardException收集并发送调试事件

最终调用DbgkpSendApiMessage(x, x),第一个参数:消息类型,第二个参数:是否挂起其它线程

调试器进程:

1)循环判断

2)取出调试事件 

3)列出信息:寄存器、内存 

4)用户处理

处理硬件断点

1)硬件调试断点产生的异常是 STATUS_SINGLE_STEP(单步异常) 2)检测Dr6寄存器的B0~B3:哪个寄存器触发的异常

这里硬件断点有两种情况,一种情况是dr0-dr3寄存器引发的异常,另外一种情况就是TF=1引发的异常

这里如果是DR0寄存器引发的异常,那么B0=1,以此类推,如果是TF=1引发的异常,那么DR6的低4位为全0

首先看一下异常处理函数

之前我们是在创建进程的时候进行断点,但是因为硬件断点需要在线程创建完成之后,设置在被调试程序的上下文中

因此当被调试程序触发调试器设置的INT 3断点时,此时设置硬件断点较为合理

再就是硬件断点的代码,这里把Dr0寄存器置1,然后把16、17为置0为执行断点,异常长度为1字节(18、19位置0),地址的话就是int3断点的地址+1

完整代码如下

// Debug4.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>#define DEBUGGEE "C:\\ipmsg.exe"//被调试进程ID,进程句柄,OEP
DWORD dwDebuggeePID = 0;//被调试线程句柄
HANDLE hDebuggeeThread = NULL;
HANDLE hDebuggeeProcess = NULL;//系统断点
BOOL bIsSystemInt3 = TRUE;//被INT 3覆盖的数据
CHAR OriginalCode = 0;//原始内存属性
DWORD dwOriginalProtect;//线程上下文
CONTEXT Context;typedef HANDLE (__stdcall *FnOpenThread) (DWORD, BOOL, DWORD);VOID InitDebuggeeInfo(DWORD dwPID, HANDLE hProcess)
{dwDebuggeePID = dwPID;hDebuggeeProcess = hProcess;
}DWORD GetProcessId(LPTSTR lpProcessName)
{HANDLE hProcessSnap = NULL;PROCESSENTRY32 pe32 = {0};hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if(hProcessSnap == (HANDLE)-1){return 0;}pe32.dwSize = sizeof(PROCESSENTRY32);if(Process32First(hProcessSnap, &pe32)){do {if(!strcmp(lpProcessName, pe32.szExeFile))return (int)pe32.th32ProcessID;} while (Process32Next(hProcessSnap, &pe32));}else{CloseHandle(hProcessSnap);}return 0;
}BOOL WaitForUserCommand()
{BOOL bRet = FALSE;CHAR command;printf("COMMAND>");command = getchar();switch(command){case 't':bRet = TRUE;break;case 'p':bRet = TRUE;break;case 'g':bRet = TRUE;break;}getchar();return bRet;
}VOID SetHardBreakPoint(PVOID pAddress)
{//1. 获取线程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 设置断点位置Context.Dr0 = (DWORD)pAddress;Context.Dr7 |= 1;//3. 设置断点长度和类型Context.Dr7 &= 0xfff0ffff; //执行断点(16、17位 置0) 1字节(18、19位 置0)//5. 设置线程上下文SetThreadContext(hDebuggeeThread, &Context);
}BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo)
{BOOL bRet = FALSE;//1. 将INT 3修复为原来的数据(如果是系统断点,不用修复)if(bIsSystemInt3){bIsSystemInt3 = FALSE;return TRUE;}else{WriteProcessMemory(hDebuggeeProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &OriginalCode, 1, NULL);}//2. 显示断点位置printf("Int 3断点:0x%p \r\n", pExceptionInfo->ExceptionRecord.ExceptionAddress);//3. 获取线程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//4. 修正EIPContext.Eip--;SetThreadContext(hDebuggeeThread, &Context);//5. 显示反汇编代码、寄存器等/*硬件断点需要设置在被调试进程的的线程上下文中。因此当被调试程序触发调试器设置的INT 3断点时,此时设置硬件断点较为合理。*/SetHardBreakPoint((PVOID)((DWORD)pExceptionInfo->ExceptionRecord.ExceptionAddress+1));//6. 等待用户命令while(bRet == FALSE){bRet = WaitForUserCommand();}return bRet;
}BOOL AccessExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo)
{BOOL bRet = FALSE;DWORD dwAccessFlag;  //访问类型 0为读 1为写DWORD dwAccessAddr;  //访问地址DWORD dwProtect;  //内存属性//1. 获取异常信息,修改内存属性dwAccessFlag = pExceptionInfo->ExceptionRecord.ExceptionInformation[0];dwAccessAddr = pExceptionInfo->ExceptionRecord.ExceptionInformation[1];printf("内存断点 : dwAccessFlag - %x dwAccessAddr - %x \n", dwAccessFlag, dwAccessAddr);VirtualProtectEx(hDebuggeeProcess, (VOID*)dwAccessAddr, 1, dwOriginalProtect, &dwProtect);//2. 获取线程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//3. 修正EIP(内存访问异常,不需要修正EIP)printf("Eip: 0x%p \n", Context.Eip);//4. 显示汇编/寄存器等信息//5. 等待用户命令while(bRet == FALSE){bRet = WaitForUserCommand();}return bRet;
}BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo)
{BOOL bRet = FALSE;//1. 获取线程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 判断是否是硬件断点导致的异常if(Context.Dr6 & 0xF) //B0~B3不为空 硬件断点{//2.1 显示断点信息printf("硬件断点:%x 0x%p \n", Context.Dr7&0x00030000, Context.Dr0);//2.2 将断点去除Context.Dr0 = 0;Context.Dr7 &= 0xfffffffe;}else //单步异常{//2.1 显示断点信息printf("单步:0x%p \n", Context.Eip);//2.2 将断点去除Context.Dr7 &= 0xfffffeff;}SetThreadContext(hDebuggeeThread, &Context);//6. 等待用户命令while(bRet == FALSE){bRet = WaitForUserCommand();}return bRet;
}BOOL ExceptionHandler(DEBUG_EVENT *pDebugEvent)
{ BOOL bRet = TRUE;EXCEPTION_DEBUG_INFO *pExceptionInfo = NULL;pExceptionInfo = &pDebugEvent->u.Exception;//得到线程句柄,后面要用FnOpenThread MyOpenThread = (FnOpenThread)GetProcAddress(LoadLibrary("kernel32.dll"), "OpenThread");hDebuggeeThread = MyOpenThread(THREAD_ALL_ACCESS, FALSE, pDebugEvent->dwThreadId);switch(pExceptionInfo->ExceptionRecord.ExceptionCode){//INT 3异常case EXCEPTION_BREAKPOINT:bRet = Int3ExceptionProc(pExceptionInfo);break;//访问异常case EXCEPTION_ACCESS_VIOLATION:bRet = AccessExceptionProc(pExceptionInfo);break;//单步执行case EXCEPTION_SINGLE_STEP:bRet = SingleStepExceptionProc(pExceptionInfo);break;}return bRet;
}VOID SetInt3BreakPoint(LPVOID addr)
{CHAR int3 = 0xCC;//1. 备份ReadProcessMemory(hDebuggeeProcess, addr, &OriginalCode, 1, NULL);//2. 修改WriteProcessMemory(hDebuggeeProcess, addr, &int3, 1, NULL);
}VOID SetMemBreakPoint(PCHAR pAddress)
{//1. 访问断点VirtualProtectEx(hDebuggeeProcess, pAddress, 1, PAGE_NOACCESS, &dwOriginalProtect); //PTE P=0//2. 写入断点//VirtualProtectEx(hDebuggeeProcess, pAddress, 1, PAGE_EXECUTE_READ, &dwOriginalProtect); //PTE R/W=0
}int main(int argc, char* argv[])
{BOOL nIsContinue = TRUE;DEBUG_EVENT debugEvent = {0};BOOL bRet = TRUE;DWORD dwContinue = DBG_CONTINUE;//1.创建调试进程STARTUPINFO startupInfo = {0};PROCESS_INFORMATION pInfo = {0};GetStartupInfo(&startupInfo);bRet = CreateProcess(DEBUGGEE, NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &pInfo);if(!bRet){printf("CreateProcess error: %d \n", GetLastError());return 0;}hDebuggeeProcess = pInfo.hProcess;//2.调试循环while(nIsContinue){bRet = WaitForDebugEvent(&debugEvent, INFINITE);if(!bRet){printf("WaitForDebugEvent error: %d \n", GetLastError());return 0;}switch(debugEvent.dwDebugEventCode){//1.异常case EXCEPTION_DEBUG_EVENT:bRet = ExceptionHandler(&debugEvent);if(!bRet)dwContinue = DBG_EXCEPTION_NOT_HANDLED;break;//2.case CREATE_THREAD_DEBUG_EVENT:break;//3.创建进程case CREATE_PROCESS_DEBUG_EVENT://int3 断点SetInt3BreakPoint((PCHAR)debugEvent.u.CreateProcessInfo.lpStartAddress);//内存断点//SetMemBreakPoint((PCHAR)debugEvent.u.CreateProcessInfo.lpStartAddress);break;//4.case EXIT_THREAD_DEBUG_EVENT:break;//5.case EXIT_PROCESS_DEBUG_EVENT:
break;//6.case LOAD_DLL_DEBUG_EVENT:break;//7.case UNLOAD_DLL_DEBUG_EVENT:break;//8.case OUTPUT_DEBUG_STRING_EVENT:break;}bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);}return 0;
}

实现效果如下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/711289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java中容器继承体系

首先上图 源码解析 打开Collection接口源码&#xff0c;能够看到Collection接口是继承了Iterable接口。 public interface Collection<E> extends Iterable<E> { /** * ...... */ } 以下是Iterable接口源码及注释 /** * Implementing this inte…

makefileGDB使用

一、makefile 1、make && makefile makefile带来的好处就是——自动化编译&#xff0c;一旦写好&#xff0c;只需要一个make命令&#xff0c;整个工程完全自动编译&#xff0c;极大的提高了软件开发的效率 下面我们通过如下示例来进一步体会它们的作用&#xff1a; ①…

使用 Python 实现一个飞书/微信记账机器人,酷B了!

Python飞书文档机器人 今天的主题是&#xff1a;使用Python联动飞书文档机器人&#xff0c;实现一个专属的记账助手&#xff0c;这篇文章如果对你帮助极大&#xff0c;欢迎你分享给你的朋友、她、他&#xff0c;一起成长。 也欢迎大家留言&#xff0c;说说自己想看什么主题的…

代码随想录第天 78.子集 90.子集II

LeetCode 78 子集 题目描述 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&…

LeetCode 2581.统计可能的树根数目:换根DP(树形DP)

【LetMeFly】2581.统计可能的树根数目&#xff1a;换根DP(树形DP) 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-number-of-possible-root-nodes/ Alice 有一棵 n 个节点的树&#xff0c;节点编号为 0 到 n - 1 。树用一个长度为 n - 1 的二维整数数组 edges…

【通信基础知识】完整通信系统的流程图及各模块功能详解

2024.2.29 抱歉最近在写毕设大论文&#xff0c;因此没有太多时间更新。然而&#xff0c;在写论文的过程中&#xff0c;发现自己对通信系统的了解还不够全明白&#xff0c;因此差了一些硕博论文总结了一个完整的通信系统流程图。若有不对的地方请多多指正//部分内容有参考ChatGP…

YOLOv7基础 | 第2种方式:简化网络结构之yolov7.yaml(由104层简化为30层)

前言:Hello大家好,我是小哥谈。通过下载YOLOv7源码可知,原始的yolov7.yaml文件是拆开写的,比较混乱,也不好理解,并且为后续改进增添了很多困难。基于此种情况,笔者就给大家介绍一种将yolov7.yaml文件简化的方法,将104层简化为30层,并且参数量和计算量和原来是一致的,…

内存占用构造方法

#使用虚拟内存构造内存消耗 mkdir /tmp/memory mount -t tmpfs -o size5G tmpfs /tmp/memory dd if/dev/zero of/tmp/memory/block #释放消耗的虚拟内存 rm -rf /tmp/memory/block umount /tmp/memory rmdir /tmp/memory #内存占用可直接在/dev/shm目录下写文件

NLP(一)——概述

参考书: 《speech and language processing》《统计自然语言处理》 宗成庆 语言是思维的载体&#xff0c;自然语言处理相比其他信号较为特别 word2vec用到c语言 Question 预训练语言模型和其他模型的区别? 预训练模型是指在大规模数据上进行预训练的模型&#xff0c;通常…

测试环境搭建整套大数据系统(七:集群搭建kafka(2.13)+flink(1.13.6)+dinky(0.6)+iceberg)

一&#xff1a;搭建kafka。 1. 三台机器执行以下命令。 cd /opt wget wget https://dlcdn.apache.org/kafka/3.6.1/kafka_2.13-3.6.1.tgz tar zxvf kafka_2.13-3.6.1.tgz cd kafka_2.13-3.6.1/config vim server.properties修改以下俩内容 1.三台机器分别给予各自的broker_id…

Python+neo4j构建豆瓣电影知识图谱

文章目录 数据来源数据整理导入节点和关系导入使用Subgraph批量导入节点和关系 多标签实体和实体去重 数据来源 http://www.openkg.cn/dataset/douban-movie-kg 该网址拥有丰富的中文知识图谱数据集&#xff0c;OpenKG(Open Knowledge Graph)&#xff0c;可供研究人员使用研究…

【golang】25、图片操作

用 “github.com/fogleman/gg” 可以画线, 框 用 “github.com/disintegration/imaging” 可以变换颜色 一、渲染 1.1 框和字 import "github.com/fogleman/gg"func DrawRectangles(inPath string, cRects []ColorTextRect, fnImgNameChange FnImgNameChange) (st…

Python爬虫——Urllib库-3

目录 ajax的get请求 获取豆瓣电影第一页的数据并保存到本地 获取豆瓣电影前十页的数据 ajax的post请求 总结 ajax的get请求 获取豆瓣电影第一页的数据并保存到本地 首先可以在浏览器找到发送数据的接口 那么我们的url就可以在header中找到了 再加上UA这个header 进行请…

Facebook的元宇宙实践:数字化社交的新前景

近年来&#xff0c;元宇宙&#xff08;Metaverse&#xff09;这一概念备受瞩目&#xff0c;被认为是数字化社交的未来趋势之一。而在众多科技巨头中&#xff0c;Facebook&#xff08;现更名为Meta&#xff09;一直处于元宇宙发展的前沿。在本文中&#xff0c;我们将深入探讨Fac…

万字带你走过数据库的这激荡的三年

本文收集了卡内基梅隆大学计算机科学系数据库学副教授 Andy Pavlo 从 2021 到 2023 连续三年对数据库领域的回顾&#xff0c;希望通过连续三年的回顾让你对数据库领域的技术发展有所了解。 关于 Andy Pavlo&#xff1a;卡内基梅隆大学计算机科学系数据库学副教授&#xff0c;数…

《springcloud alibaba》 三 sentinel流量控制

目录 sentinel准备流控规则 qpspom.xmlapllication.yml启动类controller查看结果流控提示不太友好 流控规则 线程数全局异常处理pom.xmlapplication.yml启动类实体类controller类异常类测试 关联流控模式关联jmeter 链路servicecontroller代码调整 流控效果Warm UP 熔断降级规则…

本科毕业设计:计及并网依赖性的分布式能源系统优化研究。(C语言实现)(内包含NSGA II优化算法)(二)

目录 前言 1、sofc函数 2、光伏板函数 3、集热场函数 4、sofc电跟随策略函数 5、二分法找sofc运行点函数 6、目标函数&#xff1a;成本 7、目标函数&#xff1a;二氧化碳排放量 8、目标函数&#xff1a;并网依赖性 前言 本篇文章介绍的是我的毕业设计&#xff0c;我将C…

JavaScript DOM操作笔记记录回忆总结

一、什么是DOM&#xff1f; 1、通过 HTML DOM&#xff0c;可访问 JavaScript HTML 文档的所有元素。 2、当网页被加载时&#xff0c;浏览器会创建页面的文档对象模型&#xff08;Document Object Model&#xff09; 二、操作DOM 1、在操作DOM之前&#xff0c;我们需要先获取到…

DOM 创建节点、添加节点和删除节点

创建元素节点 document.createElement(‘标签名’) 创建文本节点document.createTextNode ( 内容 ) 根据传入的标签名创建出一个空的元素对象创建出来的默认不显示&#xff0c;要成为别人的子元素才能显示&#xff0c;所以要结合appendChild使用 添加节点&#xff08;后面&am…

【复现】蓝凌OA SQL注入漏洞_61

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 蓝凌智能OA是由深圳市蓝凌软件股份有限公司开发&#xff0c;是一款针对中小企业的移动化智能办公产品&#xff0c;融合了钉钉数字…