摘要:调试器原理与编写04.硬件断点-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net每个线程最多只能四个硬件断点,每一个可以设3种类型 ,硬件断点是由 CPU 支持的
调试器原理与编写04.硬件断点-C/C++基础-断点社区-专业的老牌游戏安全技术交流社区 - BpSend.net每个线程最多只能四个硬件断点,每一个可以设3种类型 ,硬件断点是由 CPU 支持的
硬件断点是为了解决某些情况下软件断点用不了的情况(例如软件中带有自修改,下断点处的代码被软件自身执行过程中把值改了)
硬件断点有3种类型
硬件断点容易被检测。硬件断点通过8个32位寄存器(调试寄存器)实现;硬件断点最多可设置4个,属性执行内存均可,调试寄存器命名从DR0-DR7:DR0-DR3:表示断点的设置地址 ;DR4、DR5:与硬件断点的实现无关,可忽略;DR6:又称状态调试寄存器,表示断点命中的状态(命中为1,未命中为0);各个位的含义如下:B0~B3,如果其中任何一个位置位,则表示是相应的Dr0~3断点引发的调试陷阱。当如果多个断点同时命中他只会把其中一个置1 ,其他的不会BD置位表示是GD位置位情况下访问调试寄存器引发的陷阱。BT位:TSS任务切换时,若设置了T标志位,会引起调试异常,并使得BT位置位。BS置位表示是单步中断引发的断点。即EFlags的TF置位时引发的调试陷阱。DR6寄存器的值, 建议在每次异常提交之前清除。DR7:表示设置到哪个寄存器上,L=局部(可忽略位项:GD=保护标志,GE,LE,G=全局):L0-L3=1表示对应值的DRx已启动,即L0=1表DR0启动,反之未启动,即L2=0表示DR2未启动。LE和GE:为了兼容性,Intel建议使用精确断点时把LE和GE都设置为1。(使用精确断点标志,P6及之后的cpu不支持该标志)16-31位:每个2位,4位一组,描述设的断点类型(R/W)和长度(LEN),长度(LEN)对执行不起作用。R/W0到R/W3:指定各个断点的触发条件。对应DR0到DR3中的地址以及DR6中的4个断点条件标志。00=只执行;01=写入数据断点;10=I/O端口断点(只用于pentium+,需设置CR4的DE位,DE是CR4的第3位 );11=读或写数据断点。LEN0到LEN3:指定在调试地址寄存器DR0到DR3中指定的地址位置的大小。如果R/Wx位为0(执行断点),则LENx位也必须为0,否则会产生不确定的行为。可能取值:**00=1字节;01=2字节;10=保留;11=4字节**。设置内存断点时,内存地址应与指定取值对齐。;获取寄存器环境GetContext proc uses esi dwTID:dword, pCtx:ptr CONTEXTLOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTIDmov @hThread, eaxmov esi, pCtxassume esi:ptr CONTEXTmov [esi].ContextFlags, CONTEXT_ALLinvoke GetThreadContext, @hThread, esi ;获取寄存器环境信息assume esi:nothinginvoke CloseHandle, @hThreadretGetContext endp;设置寄存器环境SetContext proc dwTID:DWORD, pCtx:ptr CONTEXTLOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTIDmov @hThread, eaxinvoke SetThreadContext, @hThread, pCtx ;设置寄存器环境信息invoke CloseHandle, @hThreadretSetContext 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 == TRUEmov 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_CONTINUEret.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硬件访问断点不需要做断步配合
硬件访问断点
;设置硬件访问断点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,stdcalloption casemap:noneinclude windows.incinclude user32.incinclude kernel32.incinclude msvcrt.incinclude udis86.incincludelib user32.libincludelib kernel32.libincludelib msvcrt.libincludelib libudis86.lib.datag_szExe db "winmine.exe", 0g_hExe dd 0g_szException_DEBUG_EVENT db "EXCEPTION_DEBUG_EVENT", 0dh, 0ah, 0g_szCREATE_THREAD_DEBUG_EVENT db "CREATE_THREAD_DEBUG_EVENT", 0dh, 0ah, 0g_szCREATE_PROCESS_DEBUG_EVENT db "CREATE_PROCESS_DEBUG_EVENT", 0dh, 0ah, 0g_szEXIT_THREAD_DEBUG_EVENT db "EXIT_THREAD_DEBUG_EVENT", 0dh, 0ah, 0g_szEXIT_PROCESS_DEBUG_EVENT db "EXIT_PROCESS_DEBUG_EVENT", 0dh, 0ah, 0g_szLOAD_DLL_DEBUG_EVENT db "LOAD_DLL_DEBUG_EVENT", 0dh, 0ah, 0g_szUNLOAD_DLL_DEBUG_EVENT db "UNLOAD_DLL_DEBUG_EVENT", 0dh, 0ah, 0g_szOUTPUT_DEBUG_STRING_EVENT db "OUTPUT_DEBUG_STRING_EVENT", 0dh, 0ah, 0g_szHardbpTip db "硬件访问断点", 0g_szLoadDllFmt db "X %s", 0dh, 0ah, 0g_szwLoadDllFmt dw '%', '0', '8', 'X', ' ', '%', 's', 0dh, 0ah, 0g_szBpFmt db "CC异常 X", 0dh, 0ah, 0g_szSsFmt db "单步异常 X", 0dh, 0ah, 0g_szOutPutAsmFmt db "x %-20s %-20s", 0dh, 0ah, 0g_szInputCmd db "选择命令:", 0dh, 0ahdb "是:设置硬件执行断点", 0dh, 0ahdb "否:设置硬件访问断点", 0dh, 0ahdb "取消:直接运行", 0dh, 0ah,0g_btOldCode db 0g_dwBpAddr dd 010021a9h g_byteCC db 0CChg_szOutPutAsm db 64 dup(0)g_ud_obj db 1000h dup(0)g_bIsCCStep dd FALSEg_bIsStepStep dd FALSEg_bIsResetHardBpStep dd FALSE .code IsCallMn proc uses esi edi pDE:ptr DEBUG_EVENT, pdwCodeLen:DWORDLOCAL @dwBytesOut:DWORDLOCAL @dwOff:DWORDLOCAL @pHex:LPSTRLOCAL @pAsm:LPSTRmov esi, pDEassume esi:ptr DEBUG_EVENT;显示下一条即将执行的指令invoke ReadProcessMemory, g_hExe, [esi].u.Exception.pExceptionRecord.ExceptionAddress, \offset g_szOutPutAsm, 20, addr @dwBytesOutinvoke ud_init, offset g_ud_objinvoke ud_set_input_buffer, offset g_ud_obj, offset g_szOutPutAsm, 20invoke ud_set_mode, offset g_ud_obj, 32invoke ud_set_syntax, offset g_ud_obj, offset ud_translate_intelinvoke ud_set_pc, offset g_ud_obj, [esi].u.Exception.pExceptionRecord.ExceptionAddressinvoke ud_disassemble, offset g_ud_objinvoke ud_insn_off, offset g_ud_objmov @dwOff, eaxinvoke ud_insn_hex, offset g_ud_objmov @pHex, eaxinvoke ud_insn_asm, offset g_ud_objmov @pAsm, eaxinvoke ud_insn_len, offset g_ud_objmov edi, pdwCodeLenmov [edi], eaxinvoke crt_printf, offset g_szOutPutAsmFmt, @dwOff, @pHex, @pAsmmov eax, @pAsm.if dword ptr [eax] == 'llac'mov eax, TRUEret .endifmov eax, FALSEretIsCallMn endpSetTF proc dwTID:DWORDLOCAL @hThread:HANDLE LOCAL @ctx:CONTEXTinvoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTIDmov @hThread, eaxmov @ctx.ContextFlags, CONTEXT_FULLinvoke GetThreadContext, @hThread, addr @ctxor @ctx.regFlag, 100hinvoke SetThreadContext, @hThread, addr @ctxinvoke CloseHandle, @hThreadretSetTF endpDecEIP proc dwTID:DWORDLOCAL @hThread:HANDLE LOCAL @ctx:CONTEXTinvoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTIDmov @hThread, eaxmov @ctx.ContextFlags, CONTEXT_FULLinvoke GetThreadContext, @hThread, addr @ctxdec @ctx.regEipinvoke SetThreadContext, @hThread, addr @ctxinvoke CloseHandle, @hThreadretDecEIP endp;获取寄存器环境GetContext proc uses esi dwTID:DWORD, pCtx:ptr CONTEXTLOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTIDmov @hThread, eaxmov esi, pCtxassume esi:ptr CONTEXTmov [esi].ContextFlags, CONTEXT_ALLinvoke GetThreadContext, @hThread, esi ;获取寄存器环境信息assume esi:nothinginvoke CloseHandle, @hThreadretGetContext endp;设置寄存器环境SetContext proc dwTID:DWORD, pCtx:ptr CONTEXTLOCAL @hThread:HANDLE invoke OpenThread, THREAD_ALL_ACCESS, FALSE, dwTIDmov @hThread, eaxinvoke SetThreadContext, @hThread, pCtx ;设置寄存器环境信息invoke CloseHandle, @hThreadretSetContext endpSetBp proc LOCAL @dwBytesOut:DWORD ;保存原来的指令, 在 01001BCF写入CCinvoke ReadProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOutinvoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOutretSetBp endpInputCmd proc uses esi pDE:ptr DEBUG_EVENT LOCAL @bIsCall:BOOLLOCAL @dwCodeLen:DWORDLOCAL @ctx:CONTEXTmov esi, pDEassume esi:ptr DEBUG_EVENTinvoke IsCallMn, pDE, addr @dwCodeLenmov @bIsCall, eaxinvoke 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清0invoke SetContext, [esi].dwThreadId, addr @ctx ;设置寄存器环境 .else;直接运行.endifretInputCmd endpOnException proc uses esi pDE:ptr DEBUG_EVENT LOCAL @dwBytesOut:DWORD LOCAL @ctx:CONTEXTmov esi, pDEassume esi:ptr DEBUG_EVENT.if [esi].u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT;判断是否是自己的CCmov 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].dwThreadIdinvoke DecEIP, [esi].dwThreadId;单步中需要处理CC的单步mov g_bIsCCStep, TRUE;输入命令invoke InputCmd, pDEmov eax, DBG_CONTINUEret.endif;单步来了.if [esi].u.Exception.pExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP;处理自己的单步invoke crt_printf, offset g_szSsFmt, [esi].u.Exception.pExceptionRecord.ExceptionAddressinvoke GetContext, [esi].dwThreadId, addr @ctx;处理CC的单步.if g_bIsCCStep == TRUEmov g_bIsCCStep, FALSE;重设断点, 重新写入CC;invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOutmov eax, DBG_CONTINUEret.endif;重设硬件断点.if g_bIsResetHardBpStep == TRUEmov 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_CONTINUEret.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.endifmov eax, DBG_CONTINUEret.endifassume esi:nothingmov eax, DBG_EXCEPTION_NOT_HANDLED retOnException endpOnCreateProcess proc ;保存原来的指令, 在 01001BCF写入CCinvoke SetBpretOnCreateProcess endpmain procLOCAL @si:STARTUPINFOLOCAL @pi:PROCESS_INFORMATIONLOCAL @de:DEBUG_EVENT LOCAL @dwStatus:DWORDinvoke RtlZeroMemory, addr @si, size @siinvoke RtlZeroMemory, addr @pi, size @piinvoke RtlZeroMemory, addr @de, size @demov @dwStatus, DBG_CONTINUE;建立调试会话invoke CreateProcess, NULL, offset g_szExe, NULL, NULL, FALSE, \DEBUG_ONLY_THIS_PROCESS,\NULL, NULL,\addr @si,\addr @pi.if !eaxret.endif mov eax, @pi.hProcessmov g_hExe, eax;循环接受调试事件.while TRUEinvoke WaitForDebugEvent, addr @de, INFINITE;处理调试事件.if @de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT;invoke crt_printf, offset g_szEXCEPTION_DEBUG_EVENTinvoke OnException, addr @demov @dwStatus, eax.elseif @de.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENTinvoke crt_printf, offset g_szCREATE_THREAD_DEBUG_EVENT.elseif @de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT;invoke crt_printf, offset g_szCREATE_PROCESS_DEBUG_EVENTinvoke OnCreateProcess.elseif @de.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENTinvoke crt_printf, offset g_szEXIT_THREAD_DEBUG_EVENT.elseif @de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENTinvoke 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_EVENTinvoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT.elseif @de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENTinvoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT.endif;提交事件处理结果invoke ContinueDebugEvent, @de.dwProcessId, @de.dwThreadId, @dwStatusinvoke RtlZeroMemory, addr @de, size @de.endwretmain endpstart:invoke main end start来源:断点社区-游戏逆向与辅助研究