【编程技术-Windows平台调试器原理与编写04.硬件断点】此文章归类为:编程技术。
每个线程最多只能四个硬件断点,每一个可以设3种类型 ,硬件断点是由 CPU 支持的 硬件断点是为了解决某些情况下软件断点用不了的情况(例如软件中带有自修改,下断点处的代码被软件自身执行过程中把值改了) 硬件断点有3种类型
硬件断点与调试寄存器
代码实现 获取和设置寄存器环境;获取寄存器环境 GetContext proc uses esi dwTID:DWORD, pCtx:ptr CONTEXT LOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTID mov @hThread, eax mov esi, pCtx assume esi:ptr CONTEXT mov [esi].ContextFlags, CONTEXT_ALL invoke GetThreadContext, @hThread, esi ;获取寄存器环境信息 assume esi:nothing invoke CloseHandle, @hThread ret GetContext endp ;设置寄存器环境 SetContext proc dwTID:DWORD, pCtx:ptr CONTEXT LOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTID mov @hThread, eax invoke SetThreadContext, @hThread, pCtx ;设置寄存器环境信息 invoke CloseHandle, @hThread ret SetContext endp 硬件执行断点硬件断点触发 单步800000004异常,所以需要在单步异常里面区分一下。 如果有多个硬件断点,在设置DR7的时候,需要保留原有的,然后在清空自己的位,然后在设置。 注意:系统断点不是主线程,不能设置硬件断点。当有新进程创建,将所有的硬件断点寄存器重新设置一遍。 硬件断点在抛出异常的时候,此时eip的指令没有执行。执行不过去。解决办法:断步配合。 因为这个断点是CPU管理的,当CPU发现此时的eip和硬件断点地址相同时,就不执行了。此时的eip当前指令还没有执行,此时g和p等都过不去,如何才能让他继续执行呢? 解决办法:断步配合。再抛出硬件断点异常的时候清除当前断点,执行完这条语句后,通过单步重新设置硬件断点。 硬件执行断点 ;设置硬件执行断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 mov @ctx.iDr0, 01001BD2h ;设置下硬件执行断点的地址和寄存器 or @ctx.iDr7, 1 ; L0 置 1 ,表示 在 dr0 下了断点and @ctx.iDr7, 0fff0ffffh ;R/W0和LEN0都是0 (dr7 的 16 - 19位) invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 ;处理硬件执行断点异常 ;重设硬件断点 .if g_bIsResetHardBpStep == TRUE mov g_bIsResetHardBpStep, FALSE ;设置硬件执行断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 or @ctx.iDr7, 1 ;L0置1,说明在 dr0 设置了硬件断点 (其他的前面已经设置过了,因此这里不需要在设置) invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 mov eax, DBG_CONTINUE ret .endif ;处理硬件断点的单步 invoke GetContext, [esi].dwThreadId, addr @ctx .if @ctx.iDr6 & 1 ; 执行断点的异常来了dr0 上的断点 ;取消硬件断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 and @ctx.iDr7, 0fffffffeh ;L0 置 0 取消 dr0 上的断点 invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 ;设置单步 invoke SetTF, [esi].dwThreadId ;下个单步重设硬件断点 mov g_bIsResetHardBpStep, TRUE ;等待命令 invoke InputCmd, pDE .endif 硬件访问断点硬件访问断点不需要做断步配合 硬件访问断点原理:当cpu译码,执行的时候,才知道访问的地方是不是硬件访问断点,说以都会断到该条的下一行。 硬件访问断点 ;设置硬件访问断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 mov @ctx.iDr1, 1005000h ;设置下硬件访问断点的地址和寄存器 or @ctx.iDr7, 100b ;L1 置 1 ,表示 在 dr1 下了断点 or @ctx.iDr7, 00f00000h ;R/W1和LEN1都是11b,访问,长度为四个字节 (dr7 的 20 - 23位) mov @ctx.iDr6, 0 ;dr6清0invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 处理硬件访问异常 invoke GetContext, [esi].dwThreadId, addr @ctx.if @ctx.iDr6 & 10b ;访问断点的异常来了dr1 上的断点 invoke crt_printf, offset g_szHardbpTip ;等待命令 invoke InputCmd, pDE.endif 完整代码.586.model flat,stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc include msvcrt.inc include udis86.inc includelib user32.lib includelib kernel32.lib includelib msvcrt.lib includelib libudis86.lib .data g_szExe db "winmine.exe", 0 g_hExe dd 0 g_szEXCEPTION_DEBUG_EVENT db "EXCEPTION_DEBUG_EVENT", 0dh, 0ah, 0 g_szCREATE_THREAD_DEBUG_EVENT db "CREATE_THREAD_DEBUG_EVENT", 0dh, 0ah, 0 g_szCREATE_PROCESS_DEBUG_EVENT db "CREATE_PROCESS_DEBUG_EVENT", 0dh, 0ah, 0 g_szEXIT_THREAD_DEBUG_EVENT db "EXIT_THREAD_DEBUG_EVENT", 0dh, 0ah, 0 g_szEXIT_PROCESS_DEBUG_EVENT db "EXIT_PROCESS_DEBUG_EVENT", 0dh, 0ah, 0 g_szLOAD_DLL_DEBUG_EVENT db "LOAD_DLL_DEBUG_EVENT", 0dh, 0ah, 0 g_szUNLOAD_DLL_DEBUG_EVENT db "UNLOAD_DLL_DEBUG_EVENT", 0dh, 0ah, 0 g_szOUTPUT_DEBUG_STRING_EVENT db "OUTPUT_DEBUG_STRING_EVENT", 0dh, 0ah, 0 g_szHardbpTip db "硬件访问断点", 0 g_szLoadDllFmt db "%08X %s", 0dh, 0ah, 0 g_szwLoadDllFmt dw '%', '0', '8', 'X', ' ', '%', 's', 0dh, 0ah, 0 g_szBpFmt db "CC异常 %08X", 0dh, 0ah, 0 g_szSsFmt db "单步异常 %08X", 0dh, 0ah, 0 g_szOutPutAsmFmt db "%08x %-20s %-20s", 0dh, 0ah, 0 g_szInputCmd db "选择命令:", 0dh, 0ah db "是:设置硬件执行断点", 0dh, 0ah db "否:设置硬件访问断点", 0dh, 0ah db "取消:直接运行", 0dh, 0ah,0 g_btOldCode db 0 g_dwBpAddr dd 010021a9h g_byteCC db 0CCh g_szOutPutAsm db 64 dup(0) g_ud_obj db 1000h dup(0) g_bIsCCStep dd FALSE g_bIsStepStep dd FALSE g_bIsResetHardBpStep dd FALSE .code IsCallMn proc uses esi edi pDE:ptr DEBUG_EVENT, pdwCodeLen:DWORD LOCAL @dwBytesOut:DWORD LOCAL @dwOff:DWORD LOCAL @pHex:LPSTR LOCAL @pAsm:LPSTR mov esi, pDE assume esi:ptr DEBUG_EVENT ;显示下一条即将执行的指令 invoke ReadProcessMemory, g_hExe, [esi].u.Exception.pExceptionRecord.ExceptionAddress, \ offset g_szOutPutAsm, 20, addr @dwBytesOut invoke ud_init, offset g_ud_obj invoke ud_set_input_buffer, offset g_ud_obj, offset g_szOutPutAsm, 20 invoke ud_set_mode, offset g_ud_obj, 32 invoke ud_set_syntax, offset g_ud_obj, offset ud_translate_intel invoke ud_set_pc, offset g_ud_obj, [esi].u.Exception.pExceptionRecord.ExceptionAddress invoke ud_disassemble, offset g_ud_obj invoke ud_insn_off, offset g_ud_obj mov @dwOff, eax invoke ud_insn_hex, offset g_ud_obj mov @pHex, eax invoke ud_insn_asm, offset g_ud_obj mov @pAsm, eax invoke ud_insn_len, offset g_ud_obj mov edi, pdwCodeLen mov [edi], eax invoke crt_printf, offset g_szOutPutAsmFmt, @dwOff, @pHex, @pAsm mov eax, @pAsm .if dword ptr [eax] == 'llac' mov eax, TRUE ret .endif mov eax, FALSE ret IsCallMn endp SetTF proc dwTID:DWORD LOCAL @hThread:HANDLE LOCAL @ctx:CONTEXT invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTID mov @hThread, eax mov @ctx.ContextFlags, CONTEXT_FULL invoke GetThreadContext, @hThread, addr @ctx or @ctx.regFlag, 100h invoke SetThreadContext, @hThread, addr @ctx invoke CloseHandle, @hThread ret SetTF endp DecEIP proc dwTID:DWORD LOCAL @hThread:HANDLE LOCAL @ctx:CONTEXT invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTID mov @hThread, eax mov @ctx.ContextFlags, CONTEXT_FULL invoke GetThreadContext, @hThread, addr @ctx dec @ctx.regEip invoke SetThreadContext, @hThread, addr @ctx invoke CloseHandle, @hThread ret DecEIP endp ;获取寄存器环境 GetContext proc uses esi dwTID:DWORD, pCtx:ptr CONTEXT LOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTID mov @hThread, eax mov esi, pCtx assume esi:ptr CONTEXT mov [esi].ContextFlags, CONTEXT_ALL invoke GetThreadContext, @hThread, esi ;获取寄存器环境信息 assume esi:nothing invoke CloseHandle, @hThread ret GetContext endp ;设置寄存器环境 SetContext proc dwTID:DWORD, pCtx:ptr CONTEXT LOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTID mov @hThread, eax invoke SetThreadContext, @hThread, pCtx ;设置寄存器环境信息 invoke CloseHandle, @hThread ret SetContext endp SetBp proc LOCAL @dwBytesOut:DWORD ;保存原来的指令, 在 01001BCF写入CC invoke ReadProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOut invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOut ret SetBp endp InputCmd proc uses esi pDE:ptr DEBUG_EVENT LOCAL @bIsCall:BOOL LOCAL @dwCodeLen:DWORD LOCAL @ctx:CONTEXT mov esi, pDE assume esi:ptr DEBUG_EVENT invoke IsCallMn, pDE, addr @dwCodeLen mov @bIsCall, eax invoke MessageBox, NULL, offset g_szInputCmd, NULL, MB_YESNOCANCEL .if eax == IDYES ;设置硬件执行断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 mov @ctx.iDr0, 01001BD2h ;设置下硬件执行断点的地址和寄存器 or @ctx.iDr7, 1 ; L0 置 1 ,表示 在 dr0 下了断点 and @ctx.iDr7, 0fff0ffffh ;R/W0和LEN0都是0 (dr7 的 16 - 19位) invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 .elseif eax == IDNO ;设置硬件访问断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 mov @ctx.iDr1, 1005000h ;设置下硬件访问断点的地址和寄存器 or @ctx.iDr7, 100b ;L1 置 1 ,表示 在 dr1 下了断点 or @ctx.iDr7, 00f00000h ;R/W1和LEN1都是11b,访问,长度为四个字节 (dr7 的 20 - 23位) mov @ctx.iDr6, 0 ;dr6清0 invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 .else ;直接运行 .endif ret InputCmd endp OnException proc uses esi pDE:ptr DEBUG_EVENT LOCAL @dwBytesOut:DWORD LOCAL @ctx:CONTEXT mov esi, pDE assume esi:ptr DEBUG_EVENT .if [esi].u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT ;判断是否是自己的CC mov eax, [esi].u.Exception.pExceptionRecord.ExceptionAddress .if eax != g_dwBpAddr ;不是自己的CC异常,不处理 mov eax, DBG_EXCEPTION_NOT_HANDLED ret .endif ;处理自己的CC异常 invoke crt_printf, offset g_szBpFmt, [esi].u.Exception.pExceptionRecord.ExceptionAddress ;恢复指令 invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOut ;设置单步 invoke SetTF, [esi].dwThreadId invoke DecEIP, [esi].dwThreadId ;单步中需要处理CC的单步 mov g_bIsCCStep, TRUE ;输入命令 invoke InputCmd, pDE mov eax, DBG_CONTINUE ret .endif ;单步来了 .if [esi].u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP ;处理自己的单步 invoke crt_printf, offset g_szSsFmt, [esi].u.Exception.pExceptionRecord.ExceptionAddress invoke GetContext, [esi].dwThreadId, addr @ctx ;处理CC的单步 .if g_bIsCCStep == TRUE mov g_bIsCCStep, FALSE ;重设断点, 重新写入CC ;invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOut mov eax, DBG_CONTINUE ret .endif ;重设硬件断点 .if g_bIsResetHardBpStep == TRUE mov g_bIsResetHardBpStep, FALSE ;设置硬件执行断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 or @ctx.iDr7, 1 ;L0置1,说明在 dr0 设置了硬件断点 (其他的前面已经设置过了,因此这里不需要在设置) invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 mov eax, DBG_CONTINUE ret .endif ;处理硬件断点的单步 invoke GetContext, [esi].dwThreadId, addr @ctx .if @ctx.iDr6 & 1 ;执行断点的异常来了dr0 上的断点 ;取消硬件断点 invoke GetContext, [esi].dwThreadId, addr @ctx ;获取寄存器环境 and @ctx.iDr7, 0fffffffeh ;L0 置 0 取消 dr0 上的断点 invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 ;设置单步 invoke SetTF, [esi].dwThreadId ;下个单步重设硬件断点 mov g_bIsResetHardBpStep, TRUE ;等待命令 invoke InputCmd, pDE .elseif @ctx.iDr6 & 10b ;访问断点的异常来了dr1 上的断点 invoke crt_printf, offset g_szHardbpTip ;硬件访问断点 invoke InputCmd, pDE .endif mov eax, DBG_CONTINUE ret .endif assume esi:nothing mov eax, DBG_EXCEPTION_NOT_HANDLED ret OnException endp OnCreateProcess proc ;保存原来的指令, 在 01001BCF写入CC invoke SetBp ret OnCreateProcess endp main proc LOCAL @si:STARTUPINFO LOCAL @pi:PROCESS_INFORMATION LOCAL @de:DEBUG_EVENT LOCAL @dwStatus:DWORD invoke RtlZeroMemory, addr @si, size @si invoke RtlZeroMemory, addr @pi, size @pi invoke RtlZeroMemory, addr @de, size @de mov @dwStatus, DBG_CONTINUE ;建立调试会话 invoke CreateProcess, NULL, offset g_szExe, NULL, NULL, FALSE, \ DEBUG_ONLY_THIS_PROCESS,\ NULL, NULL,\ addr @si,\ addr @pi .if !eax ret .endif mov eax, @pi.hProcess mov g_hExe, eax ;循环接受调试事件 .while TRUE invoke WaitForDebugEvent, addr @de, INFINITE ;处理调试事件 .if @de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT ;invoke crt_printf, offset g_szEXCEPTION_DEBUG_EVENT invoke OnException, addr @de mov @dwStatus, eax .elseif @de.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT invoke crt_printf, offset g_szCREATE_THREAD_DEBUG_EVENT .elseif @de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT ;invoke crt_printf, offset g_szCREATE_PROCESS_DEBUG_EVENT invoke OnCreateProcess .elseif @de.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT invoke crt_printf, offset g_szEXIT_THREAD_DEBUG_EVENT .elseif @de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT invoke crt_printf, offset g_szEXIT_PROCESS_DEBUG_EVENT .elseif @de.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT ;invoke OnLoadDll, addr @de .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT invoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT .elseif @de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT invoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT .endif ;提交事件处理结果 invoke ContinueDebugEvent, @de.dwProcessId, @de.dwThreadId, @dwStatus invoke RtlZeroMemory, addr @de, size @de .endw ret main endpstart: invoke main end start 作业 |
更多【编程技术-Windows平台调试器原理与编写04.硬件断点】相关视频教程:www.yxfzedu.com