大理寺少卿 发表于 2025-1-2 22:47:00

调试器原理与编写01.调试框架

### 调试框架

调试器最基本功能:   断点,单步

断点分为三类

1. 软件断点
2. 硬件断点
3. 内存断点

window提供了一套机制,帮助用户来实现一套3环的调试器

事件驱动 : 窗口的各种操作(外在的想要对窗口做一下改动,例如点击菜单,点击按钮,按下键盘等)

消息响应 : 这些事件会被封装成结构体,叫做消息

调试器也是类似,调试器有各种跟调试相关的事情发生,这些事情会被封装成一个个结构体传给我们,我们只需要一直处理这样一个个的结构体的信息,这些结构体就是调试事件,调试事件 90% 都是处理异常,调试器本身就是依托于系统的异常机制实现的

#### 步骤

##### 建立调试会话

当调试器开始调试另一个进程的时候称为调试会话的建立,当调试器没有调试进程时,就叫没有调试会话

调试器建立会话的2种方式

###### 启动    **CreateProcess**

!(./notesimg/1654602808693-186167e5-e8e9-4af4-855c-1c47ac2ab97e.png)

调试标志主要2种

!(./notesimg/1654604216093-78e7fd94-32c9-4505-a69d-6fb5eee248ec.png)

###### 附加   **DebugActiveProcess**

!(./notesimg/1654604321371-87e6157a-de08-4208-b974-b20d1409b3ca.png)

###### 脱离   **DebugActiveProcessStop**

```
结束调试,但是被调试的进程仍然运行,一般用于测试调试时修改的有没有效果,大部分时候用不到
```


!(./notesimg/1654604551616-b7748d5f-1a1d-4c69-bdc5-f52a807d8ef7.png)

##### 循环接受调试事件

###### WaitForDebugEvent

!(./notesimg/1654604735811-6c79a806-a858-4586-8acb-1cbec4d0dbb0.png)

超时时间 :    INFINITE一直等待,只能使用控制台程序 ,不能用于窗口程序

!(./notesimg/1654604872551-cce90db3-07d6-460e-a1bd-dc4a55547b6b.png)

!(./notesimg/1654604928653-b44a5ded-6a5c-43db-a326-5fc994de1e1b.png)

##### 处理调试事件

##### 提交处理结果

结果提交给系统

当调试器调试进程时, 被调试进程处于挂起状态,当调试事件处理完之后,被调试进程是处于运行还是挂起要看我们的操作,返回结果就是告诉系统 被调试进程是 继续挂起还是运行

###### ContinueDebugEvent

!(./notesimg/1654605751329-40df7785-a09c-4041-98d7-f7c6fe337324.png)

第3个参数:   继续状态

DBG_CONTINUE                                    继续运行(如果异常应该状态表示异常已处理,进程继续运行)

DBG_EXCEPTION_NOT_HANDLED    只对异常事件有用

#### 代码实现

##### 新建工程

!(./notesimg/1654606069545-f5bf5d32-d2c6-4e2b-9e08-6a37ac7e1afa.png)

##### 完成代码

```
.386
.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_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


.code

main proc
        LOCAL@si:STARTUPINFO
        LOCAL@pi:PROCESS_INFORMATION
        LOCAL@de:DEBUG_EVENT;这个结构体使用后需要清0

        ;初始化变量
        invokeRtlZeroMemory,addr @si, size @si
        invokeRtlZeroMemory,addr @pi, size @pi
        invokeRtlZeroMemory,addr @de, size @de

        ;建立调试会话
      invoke CreateProcess,NULL,offset g_szExe,NULL,NULL,NULL,\
            DEBUG_ONLY_THIS_PROCESS,NULL,NULL,addr @si,addr @pi
      
          .if !eax
                  ret;建立调试会话失败直接退出
          .endif
      
        ;循环接受调试事件
        .whileTRUE
          
            invoke WaitForDebugEvent,addr @de,INFINITE
         
            ;处理调试事件
            .if @de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT         ;异常 90%都是在处理这个
                   invoke crt_printf, offset g_szEXCEPTION_DEBUG_EVENT
                   
            .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
         
            .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      ;dll被加载
                invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT
         
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;dll被卸载
               invoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT            
                 
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;输出信息
               invoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT
            
            .endif


          ;提交事件处理结果
          invoke ContinueDebugEvent,@de.dwProcessId,@de.dwThreadId,DBG_CONTINUE
          
          ;DEBUG_EVENT结构体使用后清0
          invokeRtlZeroMemory,addr @de, size @de
          
        .endw

        ret

main endp

start:
   
   invoke main


end start

```

##### 处理 LOAD_DLL_DEBUG_EVENT 事件

!(./notesimg/1654611103259-1f87b882-2e54-45c5-bd25-8c41da301258.png)

!(./notesimg/1654611121921-92ba1737-e52c-48a6-8fec-5cd7e424e704.png)

!(./notesimg/1654612431812-ea991c9c-3535-438a-aedf-f881279b48e8.png)

!(./notesimg/1654612476390-ff8891c5-fee3-40ca-9a46-9f086c548e32.png)

!(./notesimg/1654612653216-c116386b-a1e4-4e50-8ee4-bd11e077f732.png)

!(./notesimg/1654613666456-0328ef16-6cd6-4bfa-9157-e79fb6f52992.png)

用winhex 打开 该id 进程,查看内存数据 发现还是搜不到

!(./notesimg/1654613852724-84e9cb00-ac29-4b86-9021-0421dd593f8a.png)

!(./notesimg/1654613909539-71e12587-2f58-465a-bc75-18b091b3cc06.png)

!(./notesimg/1654613932712-e8d3c20e-31bf-4139-9010-f7fa20257bb5.png)

发现是系统dll,再看一下后面的

!(./notesimg/1654614322121-4eb9b935-e70c-4cff-8af2-24326c9754db.png)

!(./notesimg/1654614532590-2a68d27a-1137-4c34-8460-5d29365742cb.png)

!(./notesimg/1654614630731-e54479f2-5c24-4310-8473-db3aaeb3f638.png)

在F9执行几次,发现地址是固定的,但是指针指向的dll 会发生变化

!(./notesimg/1654614814838-e8c7a887-70a4-425c-b3e7-430ed0f5f59b.png)

所以   lpImageName存储的是一个二级指针,拿到 dll 地址就必须跨进程读写内存,但是第一个dll (ntdll.dll)例外,没办法拿到数据

```
.386
.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 dd0               ;进程句柄
   
    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_szwLoadDllFmtdw '%','0','8','X',' ', '%','s', 0dh,0ah, 0


.code

OnLoadDll proc uses esi pDE:ptr DEBUG_EVENT
      LOCAL@dwAddr:DWORD
      LOCAL@dwByteReaded:DWORD
      LOCAL@szwPath:WORD
      

        invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT
        invokeRtlZeroMemory,addr @szwPath, size @szwPath

        mov esi,pDE
        assume esi: ptr DEBUG_EVENT

        invoke ReadProcessMemory,g_hExe,.u.LoadDll.lpImageName,addr @dwAddr, size @dwAddr,addr@dwByteReaded
       .if !eax
                  ret
         .endif
      
        invoke ReadProcessMemory,g_hExe, @dwAddr ,addr @szwPath, sizeof@szwPath,addr@dwByteReaded
        .if !eax
                  ret
      .endif
      

        .if .u.LoadDll.fUnicode   ;如果是 unicode

          invoke crt_wprintf, offset g_szwLoadDllFmt,.u.LoadDll.lpBaseOfDll,addr @szwPath
        .elseif

          invoke crt_printf, offset g_szwLoadDllFmt,.u.LoadDll.lpBaseOfDll,addr @szwPath
       
        .endif

        assume esi:nothing
        ret

OnLoadDll endp


main proc
        LOCAL@si:STARTUPINFO
        LOCAL@pi:PROCESS_INFORMATION
        LOCAL@de:DEBUG_EVENT;这个结构体使用后需要清0

        ;初始化变量
        invokeRtlZeroMemory,addr @si, size @si
        invokeRtlZeroMemory,addr @pi, size @pi
        invokeRtlZeroMemory,addr @de, size @de

        ;建立调试会话
      invoke CreateProcess,NULL,offset g_szExe,NULL,NULL,NULL,\
            DEBUG_ONLY_THIS_PROCESS,NULL,NULL,addr @si,addr @pi
      
          .if !eax
                  ret;建立调试会话失败直接退出
          .endif
      
          mov eax,@pi.hProcess
          mov g_hExe,eax
        ;循环接受调试事件
        .whileTRUE
          
            invoke WaitForDebugEvent,addr @de,INFINITE
         
            ;处理调试事件
            .if @de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT         ;异常 90%都是在处理这个
                   invoke crt_printf, offset g_szEXCEPTION_DEBUG_EVENT
                   
            .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
         
            .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      ;dll被加载
                ;invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT
                    invoke OnLoadDll,addr @de
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;dll被卸载
               invoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT            
                 
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;输出信息
               invoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT
            
            .endif


          ;提交事件处理结果
          invoke ContinueDebugEvent,@de.dwProcessId,@de.dwThreadId,DBG_CONTINUE
          
          ;DEBUG_EVENT结构体使用后清0
          invokeRtlZeroMemory,addr @de, size @de
          
        .endw

        ret

main endp

start:
   
   invoke main


end start

```

NtQueryProcessInfomation   获取进程信息

作业

解析    CREATE_PROCESS_DEBUG_EVENT    OUTPUT_DEBUG_STRING_EVENT

!(./notesimg/1654623688817-05f31d1e-007d-4390-8144-f829ac784df7.png)
页: [1]
查看完整版本: 调试器原理与编写01.调试框架