Windows软件调试学习笔记(五)

您所在的位置:网站首页 在金融会议上的讲话优秀4篇内容 Windows软件调试学习笔记(五)

Windows软件调试学习笔记(五)

2023-07-18 06:16| 来源: 网络整理| 查看: 265

Windows软件调试学习笔记(五)—— 软件断点&内存断点 调试的本质软件断点软件断点的执行流程分析INT 3执行流程实验:处理软件断点 内存断点内存断点的执行流程实验:处理内存断点

调试的本质

描述: 1)调试的本质是触发异常与调试器接管异常的过程。 2)不论是软件断点,硬件断点还是INT 3断点,本质都是触发异常。 在这里插入图片描述

软件断点

当使用调试器在任意代码位置设置断点时,本质上是将当前代码位置的字节码改为0xCC,对应的汇编指令为INT 3。 在这里插入图片描述 调试器为了界面的美观,不会直接在反汇编界面将修改后的数据显示出来。

可以使用WinHex查看内存数据进行验证。 在这里插入图片描述

软件断点的执行流程

被调试进程: 1)CPU检测到INT 3指令 2)查IDT表找到对应的中断处理函数 3)CommonDispatchException 4)KiDispatchException 5)DbgkForwardException收集并发送调试事件

最终调用DbgkpSendApiMessage(x, x) 第一个参数:消息类型 第二个参数:是否挂起其它线程

调试器进程: 1)循环判断 2)取出调试事件 3)列出信息:寄存器、内存 4)用户处理

分析INT 3执行流程

1)在内核文件中定位IDT表 在这里插入图片描述 2)定位3号中断 在这里插入图片描述 3)所有的异常最终都会走向CommonDispatchException 在这里插入图片描述 4)再跟入0环异常分发函数KiDispatchException 在这里插入图片描述 5)分析KiDispatchException 在这里插入图片描述在这里插入图片描述在这里插入图片描述DbgkForwardException最终通过DbgkpSendApiMessage发送调试事件

实验:处理软件断点

1)编译并运行以下代码

#include #include #include #define DEBUGGEE "C:\\驱动管理.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; break; case 'p': bRet = TRUE; break; case 'g': bRet = TRUE; break; } getchar(); return bRet; } 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. 修正EIP Context.Eip--; SetThreadContext(hDebuggeeThread, &Context); //5. 显示反汇编代码、寄存器等 //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 = TRUE; 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) { ReadProcessMemory(hDebuggeeProcess, addr, &OriginalCode, 1, NULL); BYTE int3[1] = { 0xcc }; 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: 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)输入g并回车,使程序继续运行 在这里插入图片描述

内存断点

描述:当需要在某块内存被访问时产生中断,可以使用内存断点。

内存断点能够分为两种类型:

内存访问:内存被读写时产生中断。内存写入:内存被写入时产生中断。

原理:VirtualProtectEx

BOOL VirtualProtectEx( HANDLE hProcess, // handle to process LPVOID lpAddress, // region of committed pages SIZE_T dwSize, // size of region DWORD flNewProtect, // desired access protection PDWORD lpflOldProtect // old protection );

内存访问:将指定内存的属性修改为PAGE_NOACCESS(修改后,PTE的P位等于0) 内存写入:将指定内存的属性修改为PAGE_EXECUTE_READ(修改后,PTE的P位等于1,R/W位等于0)

内存断点的执行流程

被调试进程: 1)CPU访问错误的内存地址,触发页异常 2)查IDT表找到对应的中断处理函数(nt!_KiTrap0E) 3)CommonDispatchException 4)KiDispatchException 5)DbgkForwardException收集并发送调试事件

最终调用DbgkpSendApiMessage(x, x) 第一个参数:消息类型,共有7种类型 第二个参数:是否挂起其它线程

调试器进程: 1)循环判断 2)取出调试事件 3)列出消息(寄存器/内存) 4)用户处理

实验:处理内存断点 #include #include #include #define DEBUGGEE "C:\\驱动管理.exe" //被调试进程ID,进程句柄,OEP DWORD dwDebuggeePID = 0; //被调试线程句柄 HANDLE hDebuggeeThread = NULL; HANDLE hDebuggeeProcess = NULL; //系统断点 BOOL bIsSystemInt3 = TRUE; //被INT 3覆盖的数据 CHAR OriginalCode = 0; //原始内存属性 DWORD dwOriginalProtect; //线程上下文 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; } 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. 修正EIP Context.Eip--; SetThreadContext(hDebuggeeThread, &Context); //5. 显示反汇编代码、寄存器等 //6. 等待用户命令 while(bRet == FALSE) { bRet = WaitForUserCommand(); } return bRet; } BOOL AccessExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo) { BOOL bRet = FALSE; DWORD dwAccessFlag; //访问类型 0为读 1为写 DWORD dwAccessAddr; //访问地址 DWORD dwProtect; //内存属性 //1. 获取异常信息,修改内存属性 dwAccessFlag = pExceptionInfo->ExceptionRecord.ExceptionInformation[0]; dwAccessAddr = pExceptionInfo->ExceptionRecord.ExceptionInformation[1]; printf("内存断点: %x %x \n", dwAccessFlag, dwAccessAddr); VirtualProtectEx(hDebuggeeProcess, (VOID*)dwAccessAddr, 1, dwOriginalProtect, &dwProtect); //2. 获取线程上下文 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; GetThreadContext(hDebuggeeThread, &Context); //3. 修正EIP(内存访问异常,不需要修正EIP) printf("Eip: 0x%p \n", Context.Eip); //4. 显示汇编/寄存器等信息 //5. 等待用户命令 while(bRet == FALSE) { bRet = WaitForUserCommand(); } return bRet; } BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo) { BOOL bRet = TRUE; 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); } VOID SetMemBreakPoint(PCHAR pAddress) { //1. 访问断点 VirtualProtectEx(hDebuggeeProcess, pAddress, 1, PAGE_NOACCESS, &dwOriginalProtect); //2. 写入断点 //VirtualProtectEx(hDebuggeeProcess, pAddress, 1, PAGE_EXECUTE_READ, &dwOriginalProtect); } 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: //int3 断点 //SetInt3BreakPoint((PCHAR)debugEvent.u.CreateProcessInfo.lpStartAddress); //内存断点 SetMemBreakPoint((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)输入g并回车,使程序继续运行 在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3