软件调试学习笔记(七)—— 单步步入单步步过
生活随笔
收集整理的這篇文章主要介紹了
软件调试学习笔记(七)—— 单步步入单步步过
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
軟件調試學習筆記(七)—— 單步步入&單步步過
- 單步步入
- 設置單步異常
- 處理單步異常
- 實驗1:單步異常的設置與處理
- 單步步過
- 實現思路
- 實驗2:實現單步步過
單步步入
描述:
設置單步異常
將TF位置1
處理單步異常
單步產生的異常與硬件斷點產生的異常一致,都是STATUS_SINGLE_STEP(單步異常)
實驗1:單步異常的設置與處理
1)編譯并運行以下代碼:
#include <stdio.h> #include <windows.h> #include <tlhelp32.h>#define DEBUGGEE "C:\\helloworld.exe"//被調試進程ID,進程句柄,OEP DWORD dwDebuggeePID = 0;//被調試線程句柄 HANDLE hDebuggeeThread = NULL; HANDLE hDebuggeeProcess = NULL;//系統斷點 BOOL bIsSystemInt3 = TRUE;//被INT 3覆蓋的數據 CHAR OriginalCode = 0;//線程上下文 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;//1. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 設置陷阱標志位Context.EFlags |= 0x100;//3. 設置線程上下文SetThreadContext(hDebuggeeThread, &Context);break;case 'p':bRet = TRUE;break;case 'g':bRet = TRUE;break;}getchar();return bRet; }VOID SetHardBreakPoint(PVOID pAddress) {//1. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 設置斷點位置Context.Dr0 = (DWORD)pAddress;Context.Dr7 |= 1;//3. 設置斷點長度和類型Context.Dr7 &= 0xfff0ffff; //執行斷點(16、17位 置0) 1字節(18、19位 置0)//5. 設置線程上下文SetThreadContext(hDebuggeeThread, &Context); }BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo) {BOOL bRet = FALSE;//1. 將INT 3修復為原來的數據(如果是系統斷點,不用修復)if(bIsSystemInt3){bIsSystemInt3 = FALSE;return TRUE;}else{WriteProcessMemory(hDebuggeeProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &OriginalCode, 1, NULL);}//2. 顯示斷點位置printf("Int 3斷點:0x%p \r\n", pExceptionInfo->ExceptionRecord.ExceptionAddress);//3. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//4. 修正EIPContext.Eip--;SetThreadContext(hDebuggeeThread, &Context);//5. 顯示反匯編代碼、寄存器等/*硬件斷點需要設置在被調試進程的的線程上下文中。因此當被調試程序觸發調試器設置的INT 3斷點時,此時設置硬件斷點較為合理。*///SetHardBreakPoint((PVOID)((DWORD)pExceptionInfo->ExceptionRecord.ExceptionAddress+1));//6. 等待用戶命令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. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 判斷是否是硬件斷點導致的異常if(Context.Dr6 & 0xF) //B0~B3不為空 硬件斷點{//2.1 顯示斷點信息printf("硬件斷點:%x 0x%p \n", Context.Dr7&0x00030000, Context.Dr0);//2.2 將斷點去除Context.Dr0 = 0;Context.Dr7 &= 0xfffffffe;}else //單步異常{//2.1 顯示斷點信息printf("單步異常:0x%p \n", Context.Eip);//2.2 將斷點去除Context.Dr7 &= 0xfffffeff;}//3.設置線程上下文SetThreadContext(hDebuggeeThread, &Context);//4. 顯示寄存器信息/反匯編代碼//...略//5. 等待用戶命令while(bRet == FALSE){bRet = WaitForUserCommand();}return bRet; }BOOL ExceptionHandler(DEBUG_EVENT *pDebugEvent) { BOOL bRet = TRUE;EXCEPTION_DEBUG_INFO *pExceptionInfo = NULL;pExceptionInfo = &pDebugEvent->u.Exception;//得到線程句柄,后面要用FnOpenThread MyOpenThread = (FnOpenThread)GetProcAddress(LoadLibrary("kernel32.dll"), "OpenThread");hDebuggeeThread = MyOpenThread(THREAD_ALL_ACCESS, FALSE, pDebugEvent->dwThreadId);switch(pExceptionInfo->ExceptionRecord.ExceptionCode){//INT 3異常case EXCEPTION_BREAKPOINT:bRet = Int3ExceptionProc(pExceptionInfo);break;//訪問異常case EXCEPTION_ACCESS_VIOLATION:bRet = AccessExceptionProc(pExceptionInfo);break;//單步執行case EXCEPTION_SINGLE_STEP:bRet = SingleStepExceptionProc(pExceptionInfo);break;}return bRet; }VOID SetInt3BreakPoint(LPVOID addr) {CHAR int3 = 0xCC;//1. 備份ReadProcessMemory(hDebuggeeProcess, addr, &OriginalCode, 1, NULL);//2. 修改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.創建調試進程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.調試循環while(nIsContinue){bRet = WaitForDebugEvent(&debugEvent, INFINITE);if(!bRet){printf("WaitForDebugEvent error: %d \n", GetLastError());return 0;}switch(debugEvent.dwDebugEventCode){//1.異常case EXCEPTION_DEBUG_EVENT:bRet = ExceptionHandler(&debugEvent);if(!bRet)dwContinue = DBG_EXCEPTION_NOT_HANDLED;break;//2.case CREATE_THREAD_DEBUG_EVENT:break;//3.創建進程case CREATE_PROCESS_DEBUG_EVENT://設置INT 3斷點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; }運行結果:
2)多次輸入t并回車,單步執行
3)輸入g并回車,程序繼續運行
單步步過
實現思路
實驗2:實現單步步過
1)編譯并運行以下代碼:
#include <stdio.h> #include <windows.h> #include <tlhelp32.h>#define DEBUGGEE "C:\\helloworld.exe"//被調試進程ID,進程句柄,OEP DWORD dwDebuggeePID = 0;//被調試線程句柄 HANDLE hDebuggeeThread = NULL; HANDLE hDebuggeeProcess = NULL;//系統斷點 BOOL bIsSystemInt3 = TRUE;//被INT 3覆蓋的數據 CHAR cOriginalCode = 0;//線程上下文 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; }VOID SetInt3BreakPoint(LPVOID addr) {CHAR int3 = 0xCC;//1. 備份ReadProcessMemory(hDebuggeeProcess, addr, &cOriginalCode, 1, NULL);//2. 修改WriteProcessMemory(hDebuggeeProcess, addr, &int3, 1, NULL); }BOOL WaitForUserCommand() {BOOL bRet = FALSE;CHAR command;WORD wBuffer;printf("COMMAND>");command = getchar();switch(command){case 't':bRet = TRUE;//1. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 設置陷阱標志位Context.EFlags |= 0x100;//3. 設置線程上下文SetThreadContext(hDebuggeeThread, &Context);break;case 'p':bRet = TRUE;bRet = TRUE;//1. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 讀取當前EIP指向的機器碼ReadProcessMemory(hDebuggeeProcess, (LPVOID)Context.Eip, &wBuffer, 2, NULL);if((wBuffer & 0xFF) == 0xE8){//3. 在當前地址之后的第5個字節設置軟件斷點(E8指令占5個字節)SetInt3BreakPoint((LPVOID)(Context.Eip+5));}else if(wBuffer == 0x15FF){//3. 在當前地址之后的第6個字節設置軟件斷點(FF15指令占6個字節)SetInt3BreakPoint((LPVOID)(Context.Eip+6));}else{//3. 不是CALL指令,設置陷阱標志位觸發單步異常即可Context.EFlags |= 0x100;//4. 設置線程上下文SetThreadContext(hDebuggeeThread, &Context);}break;case 'g':bRet = TRUE;break;}getchar();return bRet; }VOID SetHardBreakPoint(PVOID pAddress) {//1. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 設置斷點位置Context.Dr0 = (DWORD)pAddress;Context.Dr7 |= 1;//3. 設置斷點長度和類型Context.Dr7 &= 0xfff0ffff; //執行斷點(16、17位 置0) 1字節(18、19位 置0)//5. 設置線程上下文SetThreadContext(hDebuggeeThread, &Context); }BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo) {BOOL bRet = FALSE;//1. 將INT 3修復為原來的數據(如果是系統斷點,不用修復)if(bIsSystemInt3){bIsSystemInt3 = FALSE;return TRUE;}else{WriteProcessMemory(hDebuggeeProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &cOriginalCode, 1, NULL);}//2. 顯示斷點位置printf("Int 3斷點:0x%p \r\n", pExceptionInfo->ExceptionRecord.ExceptionAddress);//3. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//4. 修正EIPContext.Eip--;SetThreadContext(hDebuggeeThread, &Context);//5. 顯示反匯編代碼、寄存器等/*硬件斷點需要設置在被調試進程的的線程上下文中。因此當被調試程序觸發調試器設置的INT 3斷點時,此時設置硬件斷點較為合理。*///SetHardBreakPoint((PVOID)((DWORD)pExceptionInfo->ExceptionRecord.ExceptionAddress+1));//6. 等待用戶命令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. 獲取線程上下文Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;GetThreadContext(hDebuggeeThread, &Context);//2. 判斷是否是硬件斷點導致的異常if(Context.Dr6 & 0xF) //B0~B3不為空 硬件斷點{//2.1 顯示斷點信息printf("硬件斷點:%x 0x%p \n", Context.Dr7&0x00030000, Context.Dr0);//2.2 將斷點去除Context.Dr0 = 0;Context.Dr7 &= 0xfffffffe;}else //單步異常{//2.1 顯示斷點信息printf("單步異常:0x%p \n", Context.Eip);//2.2 將斷點去除Context.Dr7 &= 0xfffffeff;}//3.設置線程上下文SetThreadContext(hDebuggeeThread, &Context);//4. 顯示寄存器信息/反匯編代碼//...略//5. 等待用戶命令while(bRet == FALSE){bRet = WaitForUserCommand();}return bRet; }BOOL ExceptionHandler(DEBUG_EVENT *pDebugEvent) { BOOL bRet = TRUE;EXCEPTION_DEBUG_INFO *pExceptionInfo = NULL;pExceptionInfo = &pDebugEvent->u.Exception;//得到線程句柄,后面要用FnOpenThread MyOpenThread = (FnOpenThread)GetProcAddress(LoadLibrary("kernel32.dll"), "OpenThread");hDebuggeeThread = MyOpenThread(THREAD_ALL_ACCESS, FALSE, pDebugEvent->dwThreadId);switch(pExceptionInfo->ExceptionRecord.ExceptionCode){//INT 3異常case EXCEPTION_BREAKPOINT:bRet = Int3ExceptionProc(pExceptionInfo);break;//訪問異常case EXCEPTION_ACCESS_VIOLATION:bRet = AccessExceptionProc(pExceptionInfo);break;//單步執行case EXCEPTION_SINGLE_STEP:bRet = SingleStepExceptionProc(pExceptionInfo);break;}return bRet; }int main(int argc, char* argv[]) {BOOL nIsContinue = TRUE;DEBUG_EVENT debugEvent = {0};BOOL bRet = TRUE;DWORD dwContinue = DBG_CONTINUE;//1.創建調試進程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.調試循環while(nIsContinue){bRet = WaitForDebugEvent(&debugEvent, INFINITE);if(!bRet){printf("WaitForDebugEvent error: %d \n", GetLastError());return 0;}switch(debugEvent.dwDebugEventCode){//1.異常case EXCEPTION_DEBUG_EVENT:bRet = ExceptionHandler(&debugEvent);if(!bRet)dwContinue = DBG_EXCEPTION_NOT_HANDLED;break;//2.case CREATE_THREAD_DEBUG_EVENT:break;//3.創建進程case CREATE_PROCESS_DEBUG_EVENT://設置INT 3斷點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; }運行結果:
2)多次輸入p并回車單步執行,若執行CALL則會觸發INT 3異常
3)對照OD查看是否相符
總結
以上是生活随笔為你收集整理的软件调试学习笔记(七)—— 单步步入单步步过的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件调试学习笔记(六)—— 硬件断点
- 下一篇: glibc-2.23学习笔记(一)——