20241126 增加DLL,更新搜索算法极端情况搜索
硬件断点
描述:
1、与软件断点与内存断点不同,硬件断点不依赖被调试程序,而是依赖于CPU中的调试寄存器。
2、调试寄存器有8个,分别为Dr0-Dr7。
3、用户最多能够设置4个硬件断点,这是由于只有Dr0~Dr3用于存储线性地址。
4、其中,Dr4和Dr5是保留的。
设置硬件断点,硬件断点设置一点不友好,使用时有不大不小的坑
1)Dr0-Dr3用于设置硬件断点,由于只有4个断点寄存器,所以最多只能设置4个硬件调试断点。
2)Dr7是最重要的寄存器:
L0/G0 ~ L3/G3:控制Dr0-Dr3是否有效,局部还是全局;每次异常后,Lx都被清零,Gx不清零。
断点长度(LENx):00(1字节)、01(2字节)、11(4字节)、10(8字节)
断点类型(R/Wx):00(执行断点)、01(写入断点)、11(访问断点)
处理硬件断点
1)硬件调试断点产生的异常是 STATUS_SINGLE_STEP(单步异常)
2)检测Dr6寄存器的B0~B3:哪个寄存器触发的异常
硬件断点的设置与处理
硬件断点设置与线程、寄存器有关,并且还有读、写、访问、字节大小等一些设置,下面用代码实现这一系列设置和删除的操作,创建一个HardwareBreakpoint.cpp,写如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | #include "HardwareBreakpoint.h"#include <vector>#include <TlHelp32.h>#define BITSET(a,x) (a|=1<<x)#define BITGET(a,x) (a&(1<<x))enum HWBP_MODE{ MODE_DISABLED = 0, //00 MODE_LOCAL = 1, //01 MODE_GLOBAL = 2 //10};struct DR7{ BYTE HWBP_MODE[4]; BYTE HWBP_TYPE[4]; BYTE HWBP_SIZE[4];};class HardwareBreakpoint{public: ULONG_PTR hwbpAddr; // 断点位置 int threadId; // 线程ID HWBP_DRX Drx; // 第几个寄存器 HWBP_TYPE Type; // 读、写、执行 HWBP_SIZE Size; // 字节 HardwareBreakpoint() // 初始化 { hwbpAddr = 0; threadId = 0; Drx = Dr0; Type = TYPE_EXECUTE; Size = SIZE_1; } bool operator ==(const HardwareBreakpoint& p) { return hwbpAddr == p.hwbpAddr; }};ULONG_PTR dr7uint(DR7* dr7){ ULONG_PTR ret = 0; if (BITGET(dr7->HWBP_MODE[0], 0)) BITSET(ret, 0); if (BITGET(dr7->HWBP_MODE[0], 1)) BITSET(ret, 1); if (BITGET(dr7->HWBP_MODE[1], 0)) BITSET(ret, 2); if (BITGET(dr7->HWBP_MODE[1], 1)) BITSET(ret, 3); if (BITGET(dr7->HWBP_MODE[2], 0)) BITSET(ret, 4); if (BITGET(dr7->HWBP_MODE[2], 1)) BITSET(ret, 5); if (BITGET(dr7->HWBP_MODE[3], 0)) BITSET(ret, 6); if (BITGET(dr7->HWBP_MODE[3], 1)) BITSET(ret, 7); if (BITGET(dr7->HWBP_TYPE[0], 0)) BITSET(ret, 16); if (BITGET(dr7->HWBP_TYPE[0], 1)) BITSET(ret, 17); if (BITGET(dr7->HWBP_SIZE[0], 0)) BITSET(ret, 18); if (BITGET(dr7->HWBP_SIZE[0], 1)) BITSET(ret, 19); if (BITGET(dr7->HWBP_TYPE[1], 0)) BITSET(ret, 20); if (BITGET(dr7->HWBP_TYPE[1], 1)) BITSET(ret, 21); if (BITGET(dr7->HWBP_SIZE[1], 0)) BITSET(ret, 22); if (BITGET(dr7->HWBP_SIZE[1], 1)) BITSET(ret, 23); if (BITGET(dr7->HWBP_TYPE[2], 0)) BITSET(ret, 24); if (BITGET(dr7->HWBP_TYPE[2], 1)) BITSET(ret, 25); if (BITGET(dr7->HWBP_SIZE[2], 0)) BITSET(ret, 26); if (BITGET(dr7->HWBP_SIZE[2], 1)) BITSET(ret, 27); if (BITGET(dr7->HWBP_TYPE[3], 0)) BITSET(ret, 28); if (BITGET(dr7->HWBP_TYPE[3], 1)) BITSET(ret, 29); if (BITGET(dr7->HWBP_SIZE[3], 0)) BITSET(ret, 30); if (BITGET(dr7->HWBP_SIZE[3], 1)) BITSET(ret, 31); return ret;}// 保存所有下断点的线程ID和相应寄存器std::vector<HardwareBreakpoint> hwbrk;// 设置硬件断点,在线程里调用int RunHwBreakpoint(HANDLE HThread, HWBP_DRX Drx, HWBP_TYPE Type, HWBP_SIZE Size, ULONG_PTR breakpoint){ SuspendThread(HThread); CONTEXT context = { 0 }; context.ContextFlags = CONTEXT_DEBUG_REGISTERS; GetThreadContext(HThread, &context); if (Drx == Dr0) { context.Dr0 = breakpoint; } if (Drx == Dr1) { context.Dr1 = breakpoint; } if (Drx == Dr2) { context.Dr2 = breakpoint; } if (Drx == Dr3) { context.Dr3 = breakpoint; } HWBP_MODE hwbpMode; DR7 dr7; hwbpMode = MODE_LOCAL; dr7.HWBP_MODE[Drx] = hwbpMode; dr7.HWBP_TYPE[Drx] = Type; dr7.HWBP_SIZE[Drx] = Size; context.Dr7 |= dr7uint(&dr7); SetThreadContext(HThread, &context); ResumeThread(HThread); // 将当前断点信息存入 vector HardwareBreakpoint* h = new HardwareBreakpoint(); h->hwbpAddr = breakpoint; h->threadId = GetThreadId(HThread); h->Drx = Drx; hwbrk.push_back(*h); delete[] h; h = NULL; return 1;}DWORD WINAPI BPThreadProc(LPVOID lpParameter){ HardwareBreakpoint* h = (HardwareBreakpoint*)lpParameter; if (!h)return 0; if (h->threadId) { //方式一:对指定的线程下硬件断点 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, false, h->threadId); if (hThread) { RunHwBreakpoint(hThread, h->Drx, h->Type, h->Size, h->hwbpAddr); //下硬件断点 CloseHandle(hThread); return 1; } } else { //方式二:对所有线程下硬件断点,加壳程序容易出错 HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId()); if (hSnap != INVALID_HANDLE_VALUE) { THREADENTRY32 te; te.dwSize = sizeof(THREADENTRY32); if (Thread32First(hSnap, &te)) { do { // 因为线程会挂起,排除自己的线程 if (te.th32OwnerProcessID == GetCurrentProcessId() && te.th32ThreadID != GetCurrentThreadId()) { HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, false, te.th32ThreadID); if (hThread) { RunHwBreakpoint(hThread, h->Drx, h->Type, h->Size, h->hwbpAddr); //下硬件断点 CloseHandle(hThread); } } } while (Thread32Next(hSnap, &te)); } } if (hSnap) CloseHandle(hSnap); } return 1;}// 设置硬件断点ULONG_PTR SetHwBreakpoint(int ThreadId, HWBP_DRX Drx, HWBP_TYPE Type, HWBP_SIZE Size, ULONG_PTR breakpoint){ HardwareBreakpoint* h = new HardwareBreakpoint(); h->threadId = ThreadId; h->Drx = Drx; h->Type = Type; h->Size = Size; h->hwbpAddr = breakpoint; HANDLE handle = CreateThread(NULL, NULL, BPThreadProc, (LPVOID)h, NULL, NULL); WaitForSingleObject(handle, INFINITE); //等待异常消息 CloseHandle(handle); delete[] h; h = NULL; return breakpoint;}// 删除硬件断点 DelHwBreakpoint(ExceptionInfo->ContextRecord, 0x123456);bool DelHwBreakpoint(PCONTEXT context, ULONG_PTR breakpoint){ if (hwbrk.size() <= 0) return false; for (auto& h : hwbrk) { if (h.hwbpAddr == breakpoint) { int FlagBit = 0; if (h.Drx == Dr0) { FlagBit = 0; context->Dr0 = 0; } if (h.Drx == Dr1) { FlagBit = 2; context->Dr1 = 0; } if (h.Drx == Dr2) { FlagBit = 4; context->Dr2 = 0; } if (h.Drx == Dr3) { FlagBit = 6; context->Dr3 = 0; } context->Dr7 &= ~(1 << FlagBit); context->Dr7 &= ~(1 << (FlagBit + 1)); // 删除用过的元素 for (int i = hwbrk.size() - 1; i >= 0; i--) { if (hwbrk[i] == h) { hwbrk.erase(hwbrk.begin() + i); } } return true; } } return false;} |
再创建一个HardwareBreakpoint.h文件,写如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #pragma once#include <Windows.h>enum HWBP_DRX{ Dr0 = 0, Dr1 = 1, Dr2 = 2, Dr3 = 3};enum HWBP_TYPE{ TYPE_EXECUTE = 0, //00 执行 TYPE_WRITE = 1, //01 写 TYPE_READWRITE = 3 //11 读/写};enum HWBP_SIZE{ SIZE_1 = 0, //00 SIZE_2 = 1, //01 SIZE_8 = 2, //10 SIZE_4 = 3 //11};/************************************************************************函数名称: SetHwBreakpoint函数功能: 设置线程硬件断点输入参数: ThreadId 线程ID,0为所有线程下断,否则指定线程下断 Drx: 参考 enum HWBP_DRX Type: 参考 enum HWBP_TYPE Size: 参考 enum HWBP_SIZE dwAddr 断点位置返 回: 当前断点位置调 用: SetHwBreakpoint(GetCurrentThreadId(), Dr0, TYPE_EXECUTE, SIZE_1, bp1);/************************************************************************/ULONG_PTR SetHwBreakpoint(int ThreadId, HWBP_DRX Drx, HWBP_TYPE Type, HWBP_SIZE Size, ULONG_PTR dwAddr);/************************************************************************函数名称: DelHwBreakpoint函数功能: 删除硬件断点输入参数: context PCONTEXT指针:ExceptionInfo->ContextRecord dwAddr 断点地址:0x123456返 回: 成功返回ture,否则返回false调 用: DelHwBreakpoint(ExceptionInfo->ContextRecord, 0x123456);/************************************************************************/bool DelHwBreakpoint(PCONTEXT context, ULONG_PTR dwAddr); |
最后,调用示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | // HWBP_Demo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include <iostream>#include "HardwareBreakpoint.h"using namespace std;ULONG_PTR bp1, bp2, bp3;LONG NTAPI ExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo){ cout << "异常地址:" << ExceptionInfo->ExceptionRecord->ExceptionAddress << endl; if ((ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress == bp2) { cout << "bp2异常" << endl; //DelHwBreakpoint(ExceptionInfo->ContextRecord, bp2); ExceptionInfo->ContextRecord->EFlags |= 1 << 16; //0x10000 return EXCEPTION_CONTINUE_EXECUTION; } else if ((ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress == bp3) { cout << "bp3异常" << endl; //DelHwBreakpoint(ExceptionInfo->ContextRecord, bp3); ExceptionInfo->ContextRecord->EFlags |= 1 << 16; //0x10000 return EXCEPTION_CONTINUE_EXECUTION; } else if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {//STATUS_SINGLE_STEP cout << "写入异常" << endl; DelHwBreakpoint(ExceptionInfo->ContextRecord, bp1); //ExceptionInfo->ContextRecord->EFlags |= 1 << 16; //0x10000 return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH;}int main(){ AddVectoredExceptionHandler(1, ExceptionHandler); std::string str = "123456789"; bp1 = (ULONG_PTR)str.c_str(); bp2 = (ULONG_PTR)GetProcAddress(GetModuleHandle(L"user32.dll"), "IsWindow"); bp3 = (ULONG_PTR)GetProcAddress(GetModuleHandle(L"user32.dll"), "GetWindow"); cout << hex << "bp1:" << bp1 << endl; SetHwBreakpoint(0, Dr0, TYPE_WRITE, SIZE_4, bp1); //设置硬件写入断点 SetHwBreakpoint(0, Dr1, TYPE_EXECUTE, SIZE_1, bp2);//设置硬件执行断点 SetHwBreakpoint(0, Dr2, TYPE_EXECUTE, SIZE_1, bp3);//设置硬件执行断点 *(BYTE*)(bp1 + 1) = 0x35; //模拟写入数据 IsWindow(NULL);//模拟调用函数 GetWindow(NULL, 1);//模拟调用函数 *(BYTE*)(bp1 + 1) = 0x36; //模拟写入数据 IsWindow(NULL);//模拟调用函数 GetWindow(NULL, 1);//模拟调用函数 system("pause"); return 0;} |
执行效果:


工程源码使用VS2022 C++14
也可使用HijackTingMix.zip静态库,库里为加强版避免了GetThreadContext和SetThreadContext被hook了,不能设置的问题,
