hardware breakpoint
Description:
- Unlike software breakpoints and memory breakpoints, hardware breakpoints do not depend on the debugged program, but on the debug register in the CPU.
- There are 7 debug registers, Dr0~Dr7 respectively.
- The user can set up to 4 hardware breakpoints because only Dr0~Dr3 are used to store linear addresses.
- Among them, Dr4 and Dr5 are reserved.
Think: if you write a linear address in the Dr0 register, will all threads be affected?
Answer: No, each thread has an independent register. When switching threads, the value of the register will also be switched.
Set hardware breakpoints
1) Dr0~Dr3 are used to set hardware breakpoints. Since there are only 4 breakpoint registers, only 4 hardware debugging breakpoints can be set at most.
2) Dr7 is the most important register:
- L0/G0 ~ L3/G3: controls whether Dr0~Dr3 is effective, local or global; After each exception, Lx is cleared, but Gx is not cleared.
- Breakpoint length (LENx): 00 (1 byte), 01 (2 bytes), 11 (4 bytes)
- Breakpoint type (R/Wx): 00 (execution breakpoint), 01 (write breakpoint), 11 (access breakpoint)
Trigger hardware breakpoint
Debugged process:
1) When the CPU executes, it detects that the current linear address is equal to the linear address in the debug register (Dr0~Dr3).
2) Check the IDT table to find the corresponding interrupt processing function (nt!_KiTrap01)
3)CommonDispatchException
4)KiDispatchException
5) DbgkForwardException collects and sends debugging events
Finally call DbgkpSendApiMessage(x, x)
First parameter: message type
Second parameter: whether to suspend other threads
Debugger process:
1) Circular judgment
2) Remove debug events
3) List information: register, memory
4) User processing
Handling hardware breakpoints
1) The exception generated by the hardware debugging breakpoint is STATUS_SINGLE_STEP (single step exception)
2) Detect B0~B3 of Dr6 register: which register triggers the exception
Experiment: setting and processing of hardware breakpoints
Experimental accessories: https://pan.baidu.com/s/16p7PDlSsBJeTnIttBGaXlQ Password: 9lro
1) Compile and run the following code:
#include <stdio.h> #include <windows.h> #include <tlhelp32.h> #define DEBUGGEE "C:\\helloworld.exe" //Debugged process ID, process handle, OEP DWORD dwDebuggeePID = 0; //Debugged thread handle HANDLE hDebuggeeThread = NULL; HANDLE hDebuggeeProcess = NULL; //System breakpoint BOOL bIsSystemInt3 = TRUE; //Data overwritten by INT 3 CHAR OriginalCode = 0; //Thread context 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. Get thread context Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; GetThreadContext(hDebuggeeThread, &Context); //2. Set breakpoint position Context.Dr0 = (DWORD)pAddress; Context.Dr7 |= 1; //3. Set breakpoint length and type Context.Dr7 &= 0xfffcffff; //Execution breakpoint (16, 17 position 0) 1 byte (18, 19 position 0) //5. Set thread context SetThreadContext(hDebuggeeThread, &Context); } BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo) { BOOL bRet = FALSE; //1. Repair INT 3 to the original data (if it is a system breakpoint, it does not need to be repaired) if(bIsSystemInt3) { bIsSystemInt3 = FALSE; return TRUE; } else { WriteProcessMemory(hDebuggeeProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &OriginalCode, 1, NULL); } //2. Display breakpoint location printf("Int 3 Breakpoint: 0 x%p \r\n", pExceptionInfo->ExceptionRecord.ExceptionAddress); //3. Get thread context Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; GetThreadContext(hDebuggeeThread, &Context); //4. Revise EIP Context.Eip--; SetThreadContext(hDebuggeeThread, &Context); //5. Display disassembly code, register, etc //Set hardware breakpoints SetHardBreakPoint((PVOID)((DWORD)pExceptionInfo->ExceptionRecord.ExceptionAddress+1)); //6. Wait for user command while(bRet == FALSE) { bRet = WaitForUserCommand(); } return bRet; } BOOL AccessExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo) { BOOL bRet = TRUE; return bRet; } BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo) { BOOL bRet = FALSE; //1. Get thread context Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; GetThreadContext(hDebuggeeThread, &Context); //2. Judge whether the abnormality is caused by hardware breakpoint if(Context.Dr6 & 0xF) //B0~B3 are not empty hardware breakpoints { //2.1 display breakpoint information printf("Hardware breakpoints:%x 0x%p \n", Context.Dr7&0x00030000, Context.Dr0); //2.2 removing breakpoints Context.Dr0 = 0; Context.Dr7 &= 0xfffffffe; } else //Single step exception { //2.1 display breakpoint information printf("Single step: 0 x%p \n", Context.Eip); //2.2 removing breakpoints Context.Dr7 &= 0xfffffeff; } SetThreadContext(hDebuggeeThread, &Context); //6. Wait for user command while(bRet == FALSE) { bRet = WaitForUserCommand(); } return bRet; } BOOL ExceptionHandler(DEBUG_EVENT *pDebugEvent) { BOOL bRet = TRUE; EXCEPTION_DEBUG_INFO *pExceptionInfo = NULL; pExceptionInfo = &pDebugEvent->u.Exception; //Get the thread handle, which will be used later FnOpenThread MyOpenThread = (FnOpenThread)GetProcAddress(LoadLibrary("kernel32.dll"), "OpenThread"); hDebuggeeThread = MyOpenThread(THREAD_ALL_ACCESS, FALSE, pDebugEvent->dwThreadId); switch(pExceptionInfo->ExceptionRecord.ExceptionCode) { //INT 3 exception case EXCEPTION_BREAKPOINT: bRet = Int3ExceptionProc(pExceptionInfo); break; //Access exception case EXCEPTION_ACCESS_VIOLATION: bRet = AccessExceptionProc(pExceptionInfo); break; //Single step execution case EXCEPTION_SINGLE_STEP: bRet = SingleStepExceptionProc(pExceptionInfo); break; } return bRet; } VOID SetInt3BreakPoint(LPVOID addr) { CHAR int3 = 0xCC; //1. Backup ReadProcessMemory(hDebuggeeProcess, addr, &OriginalCode, 1, NULL); //2. Modification WriteProcessMemory(hDebuggeeProcess, addr, &int3, 1, NULL); } int main(int argc, char* argv[]) { BOOL nIsContinue = TRUE; DEBUG_EVENT debugEvent = {0}; BOOL bRet = TRUE; DWORD dwContinue = DBG_CONTINUE; //1. Create debugging process 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. Commissioning cycle while(nIsContinue) { bRet = WaitForDebugEvent(&debugEvent, INFINITE); if(!bRet) { printf("WaitForDebugEvent error: %d \n", GetLastError()); return 0; } switch(debugEvent.dwDebugEventCode) { //1. Abnormal case EXCEPTION_DEBUG_EVENT: bRet = ExceptionHandler(&debugEvent); if(!bRet) dwContinue = DBG_EXCEPTION_NOT_HANDLED; break; //2. case CREATE_THREAD_DEBUG_EVENT: break; //3. Create process case CREATE_PROCESS_DEBUG_EVENT: //Set INT 3 breakpoint SetInt3BreakPoint((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; }
Operation results:
2) Enter g and enter to trigger the hardware breakpoint
3) Enter g again and enter to continue the program