-
-
[原创]Windows平台调试器原理与编写02.一般断点与反汇编引擎
-
发表于: 2025-2-23 14:41 1590
-
一般断点(软件断点)
断点的尊严
- 断的下来
- 走的过去
- 下次还来
所有合格的断点都应该满足这3个要求
OD下断点实际是把指令的第一个字节改成了CC,当程序执行到CC的时候其实是抛了一个异常(EXCEPTION_BREAKPOINT),这个异常就会进入调试器里面因为处于调试状态,调试器拿到这个异常之后就会知道程序要断到这里,写了CC之后,这条指令正常的功能就被破坏了,但是这条指令正常功能中他还是应该执行的,那怎么让他走过去呢,那就是把这条指令恢复,下次再来只需要走过去之后再把它写回CC,这条指令执行完可通过 TF 标志位 来实现, (TF置1就会抛出异常(EXCEPTION_SINGLE_STEP)),
断步配合: 断点和单步配合实现断点下次再来
调试的时候 如果 断点下次没来 就检查断步配合,如果崩了说明没有恢复,如果程序跑飞了,断点不再来,说明单步没处理好
调试器对被调试进程拥有所有的权限(除了写)
8caK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1M7s2y4W2L8X3c8Q4x3X3g2F1k6i4c8Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7J5y4e0k6Q4x3X3b7I4i4K6u0V1x3W2)9J5k6h3S2@1L8h3H3`.
第二个成员就是调试器判断异常第一个次来还是第二次
第一个成员,异常信息结构体
获取寄存器环境 GetThreadContext
第一个参数: 线程句柄 ,第二个结构体指针(结构体类型要到 vs中去看, msdn没有)
代码实现
以扫雷为例
扫雷的过程函数: 地址 机器码 助记符01001BC9 55 push ebp01001BCA 8BEC mov ebp, esp01001BCC 83EC 40 sub esp, 4001001BCF 8B55 0C mov edx, dword ptr [ebp+C] //在该行下断点01001BD2 8B4D 14 mov ecx, dword ptr [ebp+14]01001BD5 53 push ebx01001BD6 56 push esi01001BD7 33DB xor ebx, ebx
.586.model flat,stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc include msvcrt.inc includelib user32.lib includelib kernel32.lib includelib msvcrt.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_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_btOldCode db 0 ;记录没下断点之前的字符 g_dwBpAddr dd 01001BCFh ;下断点的地址 g_byteCC db 0CCh ; 短点符号 CC .code ;处理异常信息 OnException proc uses esi pDE:ptr DEBUG_EVENT LOCAL @dwOldProc:DWORD ;修改之前的内存属性 LOCAL @dwBytesOut:DWORD LOCAL @hThread:HANDLE 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 VirtualProtect, g_dwBpAddr, 1, PAGE_EXECUTE_READWRITE, addr @dwOldProc ;恢复指令 invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOut ;还原内存属性 invoke VirtualProtect, g_dwBpAddr, 1, @dwOldProc, addr @dwOldProc ;设置单步 ;获取线程句柄 invoke OpenThread, THREAD_ALL_ACCESS, FALSE, [esi].dwThreadId mov @hThread, eax ;设置结构体标志位,用来判断获取那些寄存器环境 ; CONTEXT_FULL 表示获取控制,整数,段 CONTEXT_ALL 是所有的 mov @ctx.ContextFlags, CONTEXT_FULL ;获取寄存器环境 invoke GetThreadContext, @hThread, addr @ctx ;将TF标志位置1 or @ctx.regFlag, 100h ;返回当前代码地址 @ctx.regEip 执行完CC的地址 dec @ctx.regEip ;减1,cc是一个字节. ;设置寄存器环境 invoke SetThreadContext, @hThread, addr @ctx ;关闭线程句柄 invoke CloseHandle, @hThread 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 VirtualProtect, g_dwBpAddr, 1, PAGE_EXECUTE_READWRITE, addr @dwOldProc ;重设断点, 重新写入CC invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOut ;还原内存属性 invoke VirtualProtect, g_dwBpAddr, 1, @dwOldProc, addr @dwOldProc ;异常已处理,继续执行代码 mov eax, DBG_CONTINUE ret .endif assume esi:nothing mov eax, DBG_EXCEPTION_NOT_HANDLED ret OnException endp OnCreateProcess proc LOCAL @dwBytesOut:DWORD LOCAL @dwOldProc:DWORD ;修改之前的内存属性 ;修改内存属性 invoke VirtualProtect, g_dwBpAddr, 1, PAGE_EXECUTE_READWRITE, addr @dwOldProc ;保存原来的被下断点地址 指令到 g_btOldCode ,用于恢复 invoke ReadProcessMemory, g_hExe, g_dwBpAddr, offset g_btOldCode, size g_btOldCode, addr @dwBytesOut ;在 01001BCF(断点地址)写入CC invoke WriteProcessMemory, g_hExe, g_dwBpAddr, offset g_byteCC, size g_byteCC, addr @dwBytesOut ;还原内存属性 invoke VirtualProtect, g_dwBpAddr, 1, @dwOldProc, addr @dwOldProc 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
反汇编引擎
https://bbs.pediy.com/thread-205590.htm
00513D41 8945 FC mov dword ptr [ebp-4], eax 00513D44 817D FC 4EE640B>cmp dword ptr [ebp-4], BB40E64E 00513D4B 75 09 jnz short 00513D56 00513D4D C745 FC 4FE640B>mov dword ptr [ebp-4], BB40E64F 00513D54 EB 1C jmp short 00513D72 00513D56 8B55 FC mov edx, dword ptr [ebp-4]00513D59 81E2 0000FFFF and edx, FFFF000000513D5F 75 11 jnz short 00513D7200513D61 8B45 FC mov eax, dword ptr [ebp-4]00513D64 0D 11470000 or eax, 471100513D69 C1E0 10 shl eax, 1000513D6C 0B45 FC or eax, dword ptr [ebp-4]00513D6F 8945 FC mov dword ptr [ebp-4], eax 00513D72 8B4D FC mov ecx, dword ptr [ebp-4]00513D75 890D 04A05100 mov dword ptr [__security_cookie], e> 00513D7B 8B55 FC mov edx, dword ptr [ebp-4]00513D7E F7D2 not edx00513D80 8915 00A05100 mov dword ptr [__security_cookie_com> 00513D86 8BE5 mov esp, ebp 00513D88 5D pop ebp 00513D89 C3 ret
#include "udis86.h"#pragma comment(lib, "libudis86.lib")#include <iostream>using namespace std;int main(){ unsigned char data[73] = { 0x89, 0x45, 0xFC, 0x81, 0x7D, 0xFC, 0x4E, 0xE6, 0x40, 0xBB, 0x75, 0x09, 0xC7, 0x45, 0xFC, 0x4F, 0xE6, 0x40, 0xBB, 0xEB, 0x1C, 0x8B, 0x55, 0xFC, 0x81, 0xE2, 0x00, 0x00, 0xFF, 0xFF, 0x75, 0x11, 0x8B, 0x45, 0xFC, 0x0D, 0x11, 0x47, 0x00, 0x00, 0xC1, 0xE0, 0x10, 0x0B, 0x45, 0xFC, 0x89, 0x45, 0xFC, 0x8B, 0x4D, 0xFC, 0x89, 0x0D, 0x04, 0xA0, 0x51, 0x00, 0x8B, 0x55, 0xFC, 0xF7, 0xD2, 0x89, 0x15, 0x00, 0xA0, 0x51, 0x00, 0x8B, 0xE5, 0x5D, 0xC3 }; ud_t ud_obj; //定义 ud_init(&ud_obj); //初始化 ud_set_input_buffer(&ud_obj, data, sizeof(data)); //缓冲区来源 ud_set_mode(&ud_obj, 32); //32还是64位反汇编 ud_set_syntax(&ud_obj, UD_SYN_INTEL); //默认语法 ud_set_pc(&ud_obj, 0x00513D41); //指令开始地址 while (ud_disassemble(&ud_obj)) //开始反汇编 { auto nLen = ud_insn_len(&ud_obj); //当前指令长度 auto nOff = ud_insn_off(&ud_obj); //EIP 当前指令地址 auto pHex = ud_insn_hex(&ud_obj); //机器码 auto ptr = ud_insn_ptr(&ud_obj); //在本程序内存中的地址 auto opr = ud_insn_opr(&ud_obj, 0); auto mn = ud_insn_mnemonic(&ud_obj); auto mn0 = ud_lookup_mnemonic(mn); cout << hex << nOff << " " << pHex << "\t\t" << ud_insn_asm(&ud_obj) << endl; //反汇编结果 } return 0; }
赞赏
他的文章
赞赏
雪币:
留言: