Hardware-Breakpoints-For-Hooking
What is it?
A hooking library that uses hardware debug registers (Dr0–Dr3) and a Vectored Exception Handler to intercept function calls without patching any bytes. Instead of writing a JMP at the start of the target function like inline hooking, it sets a hardware breakpoint on the function’s address — the CPU raises an exception the moment execution hits it, which the VEH catches and routes to your detour function.
How it works
1
2
3
4
Globals:
g_VectorHandler → handle to the registered VEH
g_CriticalSection → protects g_DetourFuncs from race conditions
g_DetourFuncs[4] → one detour function pointer per Dr0-Dr3
Step 1: Initialize
1
2
3
4
InitializeHardwareBPVariables()
InitializeCriticalSection(&g_CriticalSection)
AddVectoredExceptionHandler(1, VectorHandler)
→ VEH registered — any exception in the process now hits VectorHandler first
Step 2: Install a hook
1
2
3
4
5
6
7
SetHardwareBreakingPnt(MessageBoxA, Detour_MessageBoxA, Dr0)
GetThreadContext(-2, CONTEXT_DEBUG_REGISTERS)
ThreadCtx.Dr0 = MessageBoxA ← write target address into debug register
g_DetourFuncs[Dr0] = Detour_MessageBoxA
ThreadCtx.Dr7 = SetDr7Bits(Dr7, 0, 1, 1) ← enable G0 bit in Dr7
SetThreadContext(-2, ThreadCtx)
→ CPU will raise EXCEPTION_SINGLE_STEP next time MessageBoxA executes
Step 3: Execution hits the breakpoint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CPU hits MessageBoxA entry
→ EXCEPTION_SINGLE_STEP raised
→ VectorHandler fires
VectorHandler:
ExceptionCode == EXCEPTION_SINGLE_STEP? ← check it's our type
ExceptionAddress == Dr0/1/2/3? ← check it's our breakpoint
Identify which register triggered (Dr0 = MessageBoxA)
RemoveHardwareBreakingPnt(Dr0) ← lift temporarily so detour
can call the real function
fnHookFunc = g_DetourFuncs[Dr0]
fnHookFunc(pCtx) ← call Detour_MessageBoxA
SetHardwareBreakingPnt(addr, detour, Dr0) ← re-arm for future calls
return EXCEPTION_CONTINUE_EXECUTION
Step 4: Inside the detour
1
2
3
4
5
6
7
Detour_MessageBoxA(PCONTEXT pCtx):
GETPARM_2(pCtx) → reads pCtx->Rdx (lpText, x64 arg 2)
GETPARM_3(pCtx) → reads pCtx->R8 (lpCaption, x64 arg 3)
RETURN_VALUE(pCtx, MessageBoxA(...)) → writes result to pCtx->Rax
BLOCK_REAL(pCtx) → sets pCtx->Rip = &ucRet (a single 0xC3 byte)
→ original MessageBoxA body never executes
CONTINUE_EXECUTION(pCtx) → sets trap flag so execution resumes cleanly
Step 5: Remove hook
1
2
3
4
5
RemoveHardwareBreakingPnt(Dr0)
GetThreadContext → clear Dr0 = 0
SetDr7Bits → clear G0 bit in Dr7
SetThreadContext
→ Breakpoint gone, MessageBoxA runs normally again
Step 6: Cleanup
1
2
3
4
UnintializeHardwareBPVariables()
RemoveHardwareBreakingPnt × 4 ← clear all registers
DeleteCriticalSection
RemoveVectoredExceptionHandler
The Dr7 register is the control register that enables or disables individual breakpoints. Each debug register has a corresponding “global enable” bit (G0 for Dr0, G1 for Dr1, etc.) at position Drx * 2 in Dr7. SetDr7Bits uses a bitmask to flip only that bit, leaving the rest of Dr7 untouched.
The BLOCK_REAL trick works because the RET stub (0xC3) is allocated in an executable .text section at compile time. Pointing RIP there makes the CPU “return” from the function immediately — from the caller’s perspective the hooked function simply returned without doing anything.
HardwareHooking.c
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#include <Windows.h>
#include <stdio.h>
#include "HardwareHooking.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// RET stub — placed in an executable section so BLOCK_REAL() can point RIP/EIP here,
// causing the hooked function to return immediately without executing its real body
#pragma section(".text")
__declspec(allocate(".text")) const unsigned char ucRet[] = { 0xC3 };
VOID BLOCK_REAL(IN PCONTEXT pThreadCtx) {
#ifdef _WIN64
pThreadCtx->Rip = (ULONG_PTR)&ucRet;
#elif _WIN32
pThreadCtx->Eip = (DWORD)&ucRet;
#endif
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// Global Variables
PVOID g_VectorHandler = NULL;
CRITICAL_SECTION g_CriticalSection = { 0 };
PVOID g_DetourFuncs[4] = { 0 }; // one slot per Dr0-Dr3
//---------------------------------------------------------------------------------------------------------------------------------------------------------
BOOL ReportError(IN PCWSTR szApiFuncName, IN OPTIONAL ULONGLONG uError) {
if (uError)
printf("[!] %ws Failed With Error: 0x%0.8llX \n", szApiFuncName, uError);
else
printf("[!] %ws Failed With Error: %lu \n", szApiFuncName, GetLastError());
return FALSE;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
Modify a run of bits inside the Dr7 control register
Dr7 layout (per-breakpoint enable bits):
Bit 0 (L0), Bit 1 (G0) — Dr0
Bit 2 (L1), Bit 3 (G1) — Dr1
Bit 4 (L2), Bit 5 (G2) — Dr2
Bit 6 (L3), Bit 7 (G3) — Dr3
StartingBitPosition -> First bit to modify
NmbrOfBitsToModify -> How many bits to overwrite
NewBitValue -> New value to write into those bits
*/
ULONG_PTR SetDr7Bits(ULONG_PTR CurrentDr7, int StartingBitPosition, int NmbrOfBitsToModify, ULONG_PTR NewBitValue) {
ULONG_PTR mask = ((ULONG_PTR)1 << NmbrOfBitsToModify) - 1;
return (CurrentDr7 & ~(mask << StartingBitPosition)) | (NewBitValue << StartingBitPosition);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
Returns the hooked function's argument at dwParmIndex from the thread context
Called through the GETPARM_X macros
pThreadCtx -> Thread context captured by the VEH
dwParmIndex -> 1-based argument index
*/
PBYTE GetFunctionArgument(IN PCONTEXT pThreadCtx, IN DWORD dwParmIndex) {
#ifdef _WIN64
switch (dwParmIndex) {
case 0x1: return (PBYTE)pThreadCtx->Rcx;
case 0x2: return (PBYTE)pThreadCtx->Rdx;
case 0x3: return (PBYTE)pThreadCtx->R8;
case 0x4: return (PBYTE)pThreadCtx->R9;
default: break;
}
return (PBYTE)(*(ULONG_PTR*)(pThreadCtx->Rsp + dwParmIndex * sizeof(PVOID)));
#else
return (PBYTE)(*(DWORD_PTR*)(pThreadCtx->Esp + dwParmIndex * sizeof(PVOID)));
#endif
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
Overwrites the hooked function's argument at dwParmIndex with uValue
Called through the SETPARM_X macros
pThreadCtx -> Thread context captured by the VEH
uValue -> New argument value
dwParmIndex -> 1-based argument index
*/
VOID SetFunctionArgument(IN PCONTEXT pThreadCtx, IN ULONG_PTR uValue, IN DWORD dwParmIndex) {
#ifdef _WIN64
switch (dwParmIndex) {
case 0x1: pThreadCtx->Rcx = uValue; return;
case 0x2: pThreadCtx->Rdx = uValue; return;
case 0x3: pThreadCtx->R8 = uValue; return;
case 0x4: pThreadCtx->R9 = uValue; return;
default: break;
}
*(ULONG_PTR*)(pThreadCtx->Rsp + dwParmIndex * sizeof(PVOID)) = uValue;
#else
*(DWORD_PTR*)(pThreadCtx->Esp + dwParmIndex * sizeof(PVOID)) = uValue;
#endif
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
Install a hardware breakpoint on the current thread
pAddress -> Address to break on
fnHookFunc -> Detour function to call when the breakpoint fires
Drx -> Debug register to use (Dr0-Dr3)
*/
BOOL SetHardwareBreakingPnt(IN PVOID pAddress, IN PVOID fnHookFunc, IN enum DRX Drx) {
if (!g_VectorHandler || !pAddress || !fnHookFunc)
return FALSE;
CONTEXT ThreadCtx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
if (!GetThreadContext((HANDLE)-2, &ThreadCtx))
return ReportError(L"GetThreadContext", 0);
// Write the target address into the selected debug register
switch (Drx) {
case Dr0: if (!ThreadCtx.Dr0) ThreadCtx.Dr0 = (ULONG_PTR)pAddress; break;
case Dr1: if (!ThreadCtx.Dr1) ThreadCtx.Dr1 = (ULONG_PTR)pAddress; break;
case Dr2: if (!ThreadCtx.Dr2) ThreadCtx.Dr2 = (ULONG_PTR)pAddress; break;
case Dr3: if (!ThreadCtx.Dr3) ThreadCtx.Dr3 = (ULONG_PTR)pAddress; break;
default: return FALSE;
}
EnterCriticalSection(&g_CriticalSection);
g_DetourFuncs[Drx] = fnHookFunc;
LeaveCriticalSection(&g_CriticalSection);
// Enable the breakpoint — set G-bit for this register in Dr7 (Drx * 2 = bit position)
ThreadCtx.Dr7 = SetDr7Bits(ThreadCtx.Dr7, (Drx * 2), 1, 1);
if (!SetThreadContext((HANDLE)-2, &ThreadCtx))
return ReportError(L"SetThreadContext", 0);
return TRUE;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
Remove a hardware breakpoint from the current thread
Drx -> Debug register to clear (Dr0-Dr3)
*/
BOOL RemoveHardwareBreakingPnt(IN enum DRX Drx) {
if (!g_VectorHandler)
return FALSE;
CONTEXT ThreadCtx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
if (!GetThreadContext((HANDLE)-2, &ThreadCtx))
return ReportError(L"GetThreadContext", 0);
// Clear the debug register
switch (Drx) {
case Dr0: ThreadCtx.Dr0 = 0x00; break;
case Dr1: ThreadCtx.Dr1 = 0x00; break;
case Dr2: ThreadCtx.Dr2 = 0x00; break;
case Dr3: ThreadCtx.Dr3 = 0x00; break;
default: return FALSE;
}
// Clear the G-bit for this register in Dr7
ThreadCtx.Dr7 = SetDr7Bits(ThreadCtx.Dr7, (Drx * 2), 1, 0);
if (!SetThreadContext((HANDLE)-2, &ThreadCtx))
return ReportError(L"SetThreadContext", 0);
return TRUE;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
VEH callback — fires on every exception in the process
Only handles EXCEPTION_SINGLE_STEP raised by our hardware breakpoints:
1. Identify which Dr register triggered
2. Temporarily remove the breakpoint so the detour can call the real function
3. Call the detour function with the captured thread context
4. Re-install the breakpoint
5. Resume execution
*/
LONG WINAPI VectorHandler(PEXCEPTION_POINTERS pExceptionInfo) {
if (pExceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP)
return EXCEPTION_CONTINUE_SEARCH;
PVOID pExAddr = pExceptionInfo->ExceptionRecord->ExceptionAddress;
PCONTEXT pCtx = pExceptionInfo->ContextRecord;
// Only handle breakpoints we installed
if (pExAddr != (PVOID)pCtx->Dr0 && pExAddr != (PVOID)pCtx->Dr1 &&
pExAddr != (PVOID)pCtx->Dr2 && pExAddr != (PVOID)pCtx->Dr3)
return EXCEPTION_CONTINUE_SEARCH;
// Identify which register fired
enum DRX dwDrx = -1;
if (pExAddr == (PVOID)pCtx->Dr0) dwDrx = Dr0;
if (pExAddr == (PVOID)pCtx->Dr1) dwDrx = Dr1;
if (pExAddr == (PVOID)pCtx->Dr2) dwDrx = Dr2;
if (pExAddr == (PVOID)pCtx->Dr3) dwDrx = Dr3;
VOID (*fnHookFunc)(PCONTEXT) = NULL;
EnterCriticalSection(&g_CriticalSection);
// Temporarily remove the breakpoint so the detour can call the real function
RemoveHardwareBreakingPnt(dwDrx);
fnHookFunc = g_DetourFuncs[dwDrx];
fnHookFunc(pCtx);
// Re-install the breakpoint for future calls
SetHardwareBreakingPnt(pExceptionInfo->ExceptionRecord->ExceptionAddress, g_DetourFuncs[dwDrx], dwDrx);
LeaveCriticalSection(&g_CriticalSection);
return EXCEPTION_CONTINUE_EXECUTION;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
BOOL InitializeHardwareBPVariables() {
RtlSecureZeroMemory(&g_CriticalSection, sizeof(CRITICAL_SECTION));
RtlSecureZeroMemory(g_DetourFuncs, sizeof(g_DetourFuncs));
if (g_CriticalSection.DebugInfo == NULL)
InitializeCriticalSection(&g_CriticalSection);
if (!g_VectorHandler) {
if ((g_VectorHandler = AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)&VectorHandler)) == NULL)
return ReportError(L"AddVectoredExceptionHandler", 0);
}
return (g_VectorHandler && g_CriticalSection.DebugInfo);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
VOID UnintializeHardwareBPVariables() {
for (int i = 0; i < 4; i++)
RemoveHardwareBreakingPnt(i);
if (g_CriticalSection.DebugInfo)
DeleteCriticalSection(&g_CriticalSection);
if (g_VectorHandler)
RemoveVectoredExceptionHandler(g_VectorHandler);
RtlSecureZeroMemory(&g_CriticalSection, sizeof(CRITICAL_SECTION));
RtlSecureZeroMemory(g_DetourFuncs, sizeof(g_DetourFuncs));
g_VectorHandler = NULL;
}
main.c
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
#include <Windows.h>
#include <stdio.h>
#include "HardwareHooking.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// Detour Functions
//
// Each detour receives the full thread context of the hooked function.
// Use GETPARM_X to read args, SETPARM_X to modify them,
// RETURN_VALUE to set a custom return value, BLOCK_REAL to skip the original.
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
Detour for MessageBoxA
Logs the original parameters, replaces the call with our own MessageBox
*/
VOID Detour_MessageBoxA(PCONTEXT pThreadCtx) {
printf("[i] MessageBoxA Intercepted! \n");
printf("\t> lpText: %s \n", (char*)GETPARM_2(pThreadCtx));
printf("\t> lpCaption: %s \n", (char*)GETPARM_3(pThreadCtx));
// Replace original call with our own
RETURN_VALUE(pThreadCtx, MessageBoxA(NULL, "This Is The Hook", "Detour_MessageBoxA", MB_OK | MB_ICONEXCLAMATION));
// Skip the real MessageBoxA body
BLOCK_REAL(pThreadCtx);
CONTINUE_EXECUTION(pThreadCtx);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/*
Detour for Sleep
Logs the original sleep duration and suppresses the call entirely
*/
VOID Detour_Sleep(PCONTEXT pThreadCtx) {
printf("[i] Sleep Intercepted! \n");
printf("\t> dwMilliseconds: %lu ms \n", (DWORD)GETPARM_1(pThreadCtx));
printf("\t> Skipping sleep... \n");
// Skip sleep — return immediately
BLOCK_REAL(pThreadCtx);
CONTINUE_EXECUTION(pThreadCtx);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
int main() {
printf("[i] Initializing Hardware Breakpoint Engine ... \n");
if (!InitializeHardwareBPVariables()) {
printf("[!] InitializeHardwareBPVariables Failed \n");
return -1;
}
printf("[+] VEH Handler Registered \n\n");
// Not hooked
printf("[i] Calling MessageBoxA Before Hooking ... \n");
MessageBoxA(NULL, "Original Call - Not Hooked", "Before Hook", MB_OK);
// Install hooks
printf("\n[i] Installing Hooks ... \n");
if (!SetHardwareBreakingPnt(MessageBoxA, Detour_MessageBoxA, Dr0)) {
printf("[!] Failed To Hook MessageBoxA \n");
return -1;
}
printf("\t[+] MessageBoxA -> Dr0 \n");
if (!SetHardwareBreakingPnt(Sleep, Detour_Sleep, Dr1)) {
printf("[!] Failed To Hook Sleep \n");
return -1;
}
printf("\t[+] Sleep -> Dr1 \n\n");
// Hooked calls
printf("[i] Calling MessageBoxA While Hooked ... \n");
MessageBoxA(NULL, "This Should Not Appear", "Hooked?", MB_OK);
printf("\n[i] Calling Sleep(-1) While Hooked ... \n");
Sleep(-1);
printf("[+] Sleep Returned Immediately \n\n");
// Remove MessageBoxA hook only
printf("[i] Removing Hook On Dr0 (MessageBoxA) ... \n");
if (!RemoveHardwareBreakingPnt(Dr0)) {
printf("[!] RemoveHardwareBreakingPnt Failed \n");
return -1;
}
printf("[+] Dr0 Cleared \n\n");
// Post-unhook
printf("[i] Calling MessageBoxA After Unhooking ... \n");
MessageBoxA(NULL, "Back To The Original MessageBoxA", "After Unhook", MB_OK);
printf("\n[i] Calling Sleep(-1) - Still Hooked On Dr1 ... \n");
Sleep(-1);
printf("[+] Sleep Returned Immediately \n\n");
// Cleanup
printf("[i] Cleaning Up ... \n");
UnintializeHardwareBPVariables();
printf("[+] All Hooks Removed, VEH Deregistered \n\n");
printf("[#] Press <Enter> To Quit ... \n");
getchar();
return 0;
}
HardwareHooking.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
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
#pragma once
#include <Windows.h>
#ifndef HARDWAREBP_H
#define HARDWAREBP_H
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// Hardware Breakpoint Hooking Library
// Uses hardware debug registers (Dr0-Dr3) and VEH to intercept function calls without patching.
// Maximum 4 concurrent hooks — one per debug register.
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// Debug register selector
typedef enum _DRX {
Dr0 = 0,
Dr1 = 1,
Dr2 = 2,
Dr3 = 3
} DRX, *PDRX;
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// MACROS — call from within detour functions to read/write hooked function arguments
// x64: first 4 args in RCX/RDX/R8/R9, rest on stack
// x86: all args on stack
// Read parameters
#define GETPARM_1(CTX) ( GetFunctionArgument(CTX, 0x1) )
#define GETPARM_2(CTX) ( GetFunctionArgument(CTX, 0x2) )
#define GETPARM_3(CTX) ( GetFunctionArgument(CTX, 0x3) )
#define GETPARM_4(CTX) ( GetFunctionArgument(CTX, 0x4) )
#define GETPARM_5(CTX) ( GetFunctionArgument(CTX, 0x5) )
#define GETPARM_6(CTX) ( GetFunctionArgument(CTX, 0x6) )
#define GETPARM_7(CTX) ( GetFunctionArgument(CTX, 0x7) )
#define GETPARM_8(CTX) ( GetFunctionArgument(CTX, 0x8) )
// Write parameters
#define SETPARM_1(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x1) )
#define SETPARM_2(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x2) )
#define SETPARM_3(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x3) )
#define SETPARM_4(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x4) )
#define SETPARM_5(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x5) )
#define SETPARM_6(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x6) )
#define SETPARM_7(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x7) )
#define SETPARM_8(CTX, VAL) ( SetFunctionArgument(CTX, (ULONG_PTR)(VAL), 0x8) )
// Set return value (RAX/EAX)
#ifdef _WIN64
#define RETURN_VALUE(CTX, VAL) ( (ULONG_PTR)(CTX)->Rax = (ULONG_PTR)(VAL) )
#else
#define RETURN_VALUE(CTX, VAL) ( (ULONG_PTR)(CTX)->Eax = (ULONG_PTR)(VAL) )
#endif
// Resume normal execution after the detour
#define CONTINUE_EXECUTION(CTX) ( (CTX)->EFlags |= (1 << 16) )
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// PUBLIC
// Initialize VEH handler and critical section - call once before installing any hooks
BOOL InitializeHardwareBPVariables();
// Remove all breakpoints and deregister the VEH handler
VOID UnintializeHardwareBPVariables();
/*
Install a hardware breakpoint
pAddress -> Address to break on (where to hook)
fnHookFunc -> Detour function called when the breakpoint fires
Drx -> Debug register to use (Dr0-Dr3)
*/
BOOL SetHardwareBreakingPnt(IN PVOID pAddress, IN PVOID fnHookFunc, IN enum DRX Drx);
/*
Remove a hardware breakpoint
Drx -> Debug register to clear (Dr0-Dr3)
*/
BOOL RemoveHardwareBreakingPnt(IN enum DRX Drx);
// Skip the real function body — redirects RIP/EIP to a single RET stub
VOID BLOCK_REAL(IN PCONTEXT pThreadCtx);
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// PRIVATE — used internally, do not call directly
PBYTE GetFunctionArgument(IN PCONTEXT pThreadCtx, IN DWORD dwParmIndex);
VOID SetFunctionArgument(IN PCONTEXT pThreadCtx, IN ULONG_PTR uValue, IN DWORD dwParmIndex);
#endif // !HARDWAREBP_H