Last active
September 5, 2023 16:38
-
-
Save Autoplay1999/8b90fdeab5775be47ebd18c3f628a007 to your computer and use it in GitHub Desktop.
Hardware Breakpoint
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "hwbp.h" | |
#include <deque> | |
#include <set> | |
#include <assert.h> | |
#include <TlHelp32.h> | |
#define DEBUG_GET_LOCAL_ENABLE(i,dr7) ((dr7 >> (i * 2)) & 0x1) | |
#define DEBUG_GET_CONDITION(i,dr7) ((dr7 >> (16 + i * 4)) & 0x3) | |
#define DEBUG_GET_LENGTH(i,dr7) ((dr7 >> (18 + i * 4)) & 0x3) | |
#define DEBUG_SET_LOCAL_ENABLE(i,v,dr7) dr7 = (v ? dr7 | ((ULONG_PTR)1 << (i * 2)) : dr7 & ~((ULONG_PTR)1 << (i * 2))) | |
#define DEBUG_SET_CONDITION(i,v,dr7) dr7 = (v ? dr7 | ((ULONG_PTR)v << (16 + i * 4)) : dr7 & ~((ULONG_PTR)3 << (16 + i * 4))) | |
#define DEBUG_SET_LENGTH(i,v,dr7) dr7 = (v ? dr7 | ((ULONG_PTR)v << (18 + i * 4)) : dr7 & ~((ULONG_PTR)3 << (18 + i * 4))) | |
namespace hwbp { | |
typedef struct _DebugControl { | |
// Bits Description | |
unsigned int L0 : 1; // 00 - Local enable for breakpoint #0 | |
unsigned int G0 : 1; // 01 - Global enable for breakpoint #0 | |
unsigned int L1 : 1; // 02 - Local enable for breakpoint #1 | |
unsigned int G1 : 1; // 03 - Global enable for breakpoint #1 | |
unsigned int L2 : 1; // 04 - Local enable for breakpoint #2 | |
unsigned int G2 : 1; // 05 - Global enable for breakpoint #2 | |
unsigned int L3 : 1; // 06 - Local enable for breakpoint #3 | |
unsigned int G3 : 1; // 07 - Global enable for breakpoint #3 | |
unsigned int LE : 1; // 08 - (386 only) Local Exact Breakpoint Enable | |
unsigned int GE : 1; // 09 - (386 only) Global Exact Breakpoint Enable | |
unsigned int Reserved0 : 1; // 10 - Reserved, read-only, read as 1 and should be written as 1. | |
unsigned int RTM : 1; // 11 - (Processors with Intel TSX only) Enable advanced debugging of RTM transactions (only if DEBUGCTL bit 15 is also set) On other processors: reserved, read-only, read as 0 and should be written as 0. | |
unsigned int IRSIME : 1; // 12 - (386/486 processors only) Action on breakpoint match: 0 = INT 1 (#DB exception, default), 1 = Break to ICE/SMM[b] On other processors: Reserved, read-only, read as 0 and should be written as 0. | |
unsigned int GD : 1; // 13 - General Detect Enable. If set, will cause a debug exception on any attempt at accessing the DR0-DR7 registers | |
unsigned int Reserved1 : 2; // 14 - Reserved, should be written as all-0s. | |
unsigned int RW0 : 2; // 16 - Breakpoint condition for breakpoint #0 | |
unsigned int LEN0 : 2; // 18 - Breakpoint length for breakpoint #0 | |
unsigned int RW1 : 2; // 20 - Breakpoint condition for breakpoint #1 | |
unsigned int LEN1 : 2; // 22 - Breakpoint length for breakpoint #1 | |
unsigned int RW2 : 2; // 24 - Breakpoint condition for breakpoint #2 | |
unsigned int LEN2 : 2; // 26 - Breakpoint length for breakpoint #2 | |
unsigned int RW3 : 2; // 28 - Breakpoint condition for breakpoint #3 | |
unsigned int LEN3 : 2; // 30 - Breakpoint length for breakpoint #3 | |
#ifdef _WIN64 | |
unsigned int Reserved2 : 32; // 32 - (x86-64 only) Reserved. Read as all-0s. Must be written as all-0s | |
#endif | |
}DEBUGCONTROL, *PDEBUGCONTROL; | |
typedef struct _BPState { | |
ULONG Index; | |
BOOL Enable; | |
CONDITION Condition; | |
LENGTH Length; | |
ULONG_PTR Address; | |
ULONG_PTR ThreadID; | |
_BPState() : Index(), Enable(), Condition(), Length(), Address(), ThreadID() {} | |
}BPSTATE, *PBPSTATE; | |
static std::deque<BPSTATE> gLastBPStates; | |
static std::deque<BPSTATE> gBPStates; | |
static std::set<DWORD_PTR> gThreadIds; | |
bool update_thread() { | |
HANDLE hSnapshot; | |
DWORD curPID = GetCurrentProcessId(); | |
gThreadIds.clear(); | |
if (!(hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, curPID))) | |
return false; | |
THREADENTRY32 threadEntry{sizeof(THREADENTRY32)}; | |
if (Thread32First(hSnapshot, &threadEntry)) { | |
do { | |
if (threadEntry.th32OwnerProcessID != curPID) | |
continue; | |
gThreadIds.insert(threadEntry.th32ThreadID); | |
} while (Thread32Next(hSnapshot, &threadEntry)); | |
} | |
CloseHandle(hSnapshot); | |
return true; | |
} | |
LONG commit(BOOL suspendThread) { | |
HANDLE hSnapshot, hThread; | |
ULONG_PTR curPID, curTID; | |
THREADENTRY32 threadEntry; | |
CONTEXT context; | |
LONG status = 0; | |
if (gBPStates.empty()) | |
return 1; | |
curPID = GetCurrentProcessId(); | |
curTID = GetCurrentThreadId(); | |
if (!(hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, curPID))) | |
return 2; | |
threadEntry.dwSize = sizeof(THREADENTRY32); | |
if (!Thread32First(hSnapshot, &threadEntry)) { | |
CloseHandle(hSnapshot); | |
return 3; | |
} | |
do { | |
if (threadEntry.th32OwnerProcessID != curPID) | |
continue; | |
if (suspendThread && threadEntry.th32ThreadID == curTID) | |
continue; | |
if (!(hThread = OpenThread(THREAD_ALL_ACCESS, false, threadEntry.th32ThreadID))) { | |
status = 4; | |
break; | |
} | |
if (suspendThread && SuspendThread(hThread) == -1) { | |
CloseHandle(hThread); | |
status = 5; | |
break; | |
} | |
context.ContextFlags = CONTEXT_DEBUG_REGISTERS; | |
if (!GetThreadContext(hThread, &context)) { | |
if (suspendThread) | |
ResumeThread(hThread); | |
CloseHandle(hThread); | |
status = 6; | |
break; | |
} | |
auto bpAddr = &context.Dr0; | |
auto& bpState = context.Dr7; | |
for (auto& bp : gBPStates) { | |
bpAddr[bp.Index] = bp.Address; | |
DEBUG_SET_LOCAL_ENABLE(bp.Index, bp.Enable, bpState); | |
DEBUG_SET_CONDITION(bp.Index, bp.Condition, bpState); | |
DEBUG_SET_LENGTH(bp.Index, bp.Length, bpState); | |
} | |
if (!SetThreadContext(hThread, &context)) { | |
if (suspendThread) | |
ResumeThread(hThread); | |
CloseHandle(hThread); | |
status = 7; | |
break; | |
} | |
if (suspendThread) | |
ResumeThread(hThread); | |
CloseHandle(hThread); | |
} while (Thread32Next(hSnapshot, &threadEntry)); | |
CloseHandle(hSnapshot); | |
gLastBPStates = gBPStates; | |
gBPStates.clear(); | |
return status; | |
} | |
LONG re_commit(BOOL suspendThread) { | |
if (gLastBPStates.empty()) | |
return 1; | |
for (auto& bp : gLastBPStates) { | |
if (bp.Enable) | |
gBPStates.push_back(bp); | |
} | |
return commit(suspendThread); | |
} | |
LONG set(ULONG index, ULONG_PTR addr, CONDITION condi, LENGTH length, ULONG threadId, BOOL setCommit, BOOL suspendThread) { | |
assert(addr); | |
assert(index < 4); | |
assert(condi < 4); | |
assert(length < 4); | |
auto& bp = gBPStates.emplace_back(); | |
bp.Index = index; | |
bp.Condition = condi; | |
bp.Length = length; | |
bp.Address = addr; | |
bp.ThreadID = threadId; | |
bp.Enable = TRUE; | |
if (setCommit) | |
return commit(suspendThread); | |
return 0; | |
} | |
LONG unset(ULONG index, ULONG threadId, BOOL setCommit, BOOL suspendThread) { | |
assert(index < 4); | |
auto& bp = gBPStates.emplace_back(); | |
bp.Index = index; | |
bp.Condition = C_EXECUTE; | |
bp.Length = L_BYTE; | |
bp.Address = 0; | |
bp.ThreadID = threadId; | |
bp.Enable = FALSE; | |
if (setCommit) | |
return commit(suspendThread); | |
return 0; | |
} | |
LONG unset_all(BOOL suspendThread) { | |
for (int i = 0; i < 4; ++i) { | |
auto& bp = gBPStates.emplace_back(); | |
bp.Index = i; | |
bp.Condition = C_EXECUTE; | |
bp.Length = L_BYTE; | |
bp.Address = 0; | |
bp.ThreadID = 0; | |
bp.Enable = FALSE; | |
} | |
return commit(suspendThread); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <Windows.h> | |
namespace hwbp { | |
enum CONDITION_ { | |
C_EXECUTE, | |
C_WRITE, | |
C_IOREADWRITE, | |
C_READWRITE | |
}; | |
enum LENGTH_ { | |
L_BYTE, | |
L_WORD, | |
L_QWORD, | |
L_DWORD | |
}; | |
typedef ULONG CONDITION; | |
typedef ULONG LENGTH; | |
LONG set(ULONG index, ULONG_PTR addr, CONDITION condi = C_EXECUTE, LENGTH length = L_BYTE, ULONG threadId = 0, BOOL setCommit = FALSE, BOOL suspendThread = FALSE); | |
LONG unset(ULONG index, ULONG threadId = 0, BOOL setCommit = FALSE, BOOL suspendThread = FALSE); | |
LONG unset_all(BOOL suspendThread = FALSE); | |
LONG commit(BOOL suspendThread = FALSE); | |
LONG re_commit(BOOL suspendThread = FALSE); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment