6.硬件断点
調(diào)試寄存器
- DR0~DR3:調(diào)試地址寄存器,用于設置硬件斷點
- DR4~DR5:保留,未公開具體作用
- DR6:調(diào)試寄存器組狀態(tài)寄存器
- DR7:調(diào)試寄存器組控制寄存器
硬件斷點的原理是使用DR0~DR3設置地址,使用DR7設置狀態(tài),因此最多設置4個。
<1> L0/G0 ~ L3/G3: L0/G0對應DR0,L1/G1對應DR1,以此類推…,用于控制Dr0~Dr3是否有效,Lx是局部的僅影響當前線程,Gx全局的影響當前進程中所有線程。
每次異常后,Lx都被清零,Gx不清零。
<2> 斷點長度(LENx):x的下標為幾就對應那個控制寄存器,00(1字節(jié)) 01(2字節(jié)) 11(4字節(jié))
<3> 斷點類型(R/Wx):x的下標為幾就對應那個控制寄存器,00(執(zhí)行斷點) 01(寫入斷點) 11(訪問斷點)
被調(diào)試進程
這個過程到第3步時,所有斷點都一樣執(zhí)行流程都一樣。
處理硬件斷點:
設置硬件斷點要設置到所屬線程的上下文。
單步異常
不單只有硬件斷點能觸發(fā)單步異常,EFLAGS寄存器也可以
設置單步運行:
TF位置1處理單步異常:
單步產(chǎn)生的異常是 STATUS_SINGLE_STEP(單步異常)TF位置1的時候每走完一步就會觸發(fā)單步異常。
單步步入:
遇到CALL指令會跟進去
單步步過:
遇到CALL不會跟進去,實現(xiàn)原理是計算當指令長度,當前指令長度+當前eip的地方下斷(也就是call指令的下一行)。
如果寫代碼修改返回地址,單步步入沒事,單步步過就會跑飛。
下面代碼只有單步步入
//被調(diào)試進程句柄 HANDLE hDebuggeeProcess = NULL; //被調(diào)試線程句柄 HANDLE hDebuggeeThread; //系統(tǒng)斷點 BOOL bIsSystemInt3 = TRUE; //被INT 3覆蓋的數(shù)據(jù) char OriginalCode = 0; //線程上下文 CONTEXT Context = { 0 };BOOL WaitForUserCommand() {BOOL bRet = FALSE;char cContext = 0;printf("COMMAND> ");scanf("%c",&cContext);switch (cContext){case 't':bRet = TRUE;//1.獲取線程上下文Context.ContextFlags = CONTEXT_FULL || CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2.設置單步執(zhí)行Context.EFlags |= 0x100;//3.設置線程上下文SetThreadContext(hDebuggeeThread, &Context);break;case 'p':bRet = TRUE;break;case 'g':bRet = TRUE;break;}return bRet; }VOID setHardBreakPoint(PVOID addr) {//1.獲取線程上下文Context.ContextFlags = CONTEXT_ALL;SuspendThread(hDebuggeeThread);//掛起線程GetThreadContext(hDebuggeeThread,&Context);//2.設置斷點位置Context.Dr0 = (DWORD)addr;Context.Dr7 |= 1;//3.設置斷點長度Context.Dr7 &= 0xfff0ffff;//4.設置線程上下文BOOL a = SetThreadContext(hDebuggeeThread, &Context);ResumeThread(hDebuggeeThread);//恢復線程 }BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO* excp) {BOOL bRet = FALSE;//1將INT 3修復為原來的數(shù)據(jù)(如果是系統(tǒng)斷點不用修復)if (bIsSystemInt3){bIsSystemInt3 = FALSE;return TRUE;}else{//還原WriteProcessMemory(hDebuggeeProcess, excp->ExceptionRecord.ExceptionAddress, &OriginalCode, 1, NULL);}//2.顯示斷點位置printf("int3 斷點 0x%p\n", excp->ExceptionRecord.ExceptionAddress);//3.獲取線程上下文Context.ContextFlags = CONTEXT_FULL || CONTEXT_DEBUG_REGISTERS;bool a = GetThreadContext(hDebuggeeThread, &Context);//4.修正EIP(0xcc斷下已經(jīng)走了一個字節(jié),Eip要-1)Context.Eip --;SetThreadContext(hDebuggeeThread, &Context);//5.顯示反匯編代碼//省略..//硬件斷點setHardBreakPoint((PVOID)((DWORD)excp->ExceptionRecord.ExceptionAddress+1));//6.等待調(diào)試者命令while (bRet == FALSE){bRet = WaitForUserCommand();}return bRet; }BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO* excp) {BOOL bRet = FALSE;//1.取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2.判斷是否是硬件斷點導致的異常if (Context.Dr6 & 0xf)//B0-B3不為NULL{//2.1顯示斷點信息printf("硬件斷點:%X %X\n", Context.Dr7 & 0x0003000, Context.Dr0);//2.2將斷點去除Context.Dr0 = 0;Context.Dr7 = 0xfffffffe;}else{//2.1顯示斷點信息printf("單步->0x%p\n", Context.Eip);//2.2將單步去除Context.EFlags &= 0xfffffeff;}//3.設置線程上下文SetThreadContext(hDebuggeeThread, &Context);//等待用戶命令while (bRet == FALSE){bRet = WaitForUserCommand();}return bRet; }BOOL ExceptionHandler(DEBUG_EVENT* pDebugEvent) {BOOL bRet = TRUE;EXCEPTION_DEBUG_INFO* pExceptionInfo = NULL;pExceptionInfo = &pDebugEvent->u.Exception;//得到線程句柄 后面要用pMyOpenThread MyOpenThread = (pMyOpenThread)GetProcAddress(LoadLibrary(L"Kernel32.dll"),"OpenThread");hDebuggeeThread = MyOpenThread(THREAD_ALL_ACCESS,FALSE,pDebugEvent->dwThreadId);if (pExceptionInfo->ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT){bRet = Int3ExceptionProc(pExceptionInfo);return bRet;}if (pExceptionInfo->ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP){bRet = SingleStepExceptionProc(pExceptionInfo);return bRet;}return bRet; }void SetInt3BreakPoint(LPVOID addr) {ReadProcessMemory(hDebuggeeProcess, addr, &OriginalCode, 1, NULL);BYTE int3[1] = { 0xcc };WriteProcessMemory(hDebuggeeProcess, addr, int3, 1, NULL); }void CALL() {BOOL nIsConinue = TRUE;DEBUG_EVENT debugEvent = { 0 };DWORD dwContext = DBG_CONTINUE;//1.創(chuàng)建調(diào)試進程STARTUPINFO startupInfo = { 0 };PROCESS_INFORMATION pInfo = { 0 };GetStartupInfo(&startupInfo);WCHAR* _ProcessName = { L"C:\\Users\\Administrator\\Desktop\\控制臺測試\\Debug\\0000.exe" };BOOL bRet = CreateProcess(_ProcessName, NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &pInfo);if (bRet == FALSE){printf("CreateProcess error:%d\n", GetLastError());return;}hDebuggeeProcess = pInfo.hProcess;//調(diào)試循環(huán)while (nIsConinue){BOOL bRet = WaitForDebugEvent(&debugEvent, INFINITE);//取DEBUG_EVENTif (!bRet){printf("WaitForDebugEvent error:%d\n", GetLastError());return;}switch (debugEvent.dwDebugEventCode){case EXCEPTION_DEBUG_EVENT:bRet = ExceptionHandler(&debugEvent);if (!bRet)dwContext = DBG_EXCEPTION_NOT_HANDLED;break;case CREATE_THREAD_DEBUG_EVENT:break;case CREATE_PROCESS_DEBUG_EVENT://進程創(chuàng)建的時候 在DEP處設置斷點就是將OEP處的一個字節(jié)改為0xCC(INT 3)SetInt3BreakPoint((PCHAR)debugEvent.u.CreateProcessInfo.lpStartAddress);break;case EXIT_THREAD_DEBUG_EVENT:break;case EXIT_PROCESS_DEBUG_EVENT:break;case LOAD_DLL_DEBUG_EVENT:break;case UNLOAD_DLL_DEBUG_EVENT:break;}bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);}}總結(jié)