首页
社区
课程
招聘
调试器编写-01调试框架
发表于: 2024-8-18 12:51 2168

调试器编写-01调试框架

2024-8-18 12:51
2168

调试框架

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

断点分为三类

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

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

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

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

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

步骤

建立调试会话

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

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

启动 CreateProcess

img

调试标志主要2种

img

附加 DebugActiveProcess

img

脱离 DebugActiveProcessStop

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

img

循环接受调试事件
WaitForDebugEvent

img

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

img

img

处理调试事件
提交处理结果

结果提交给系统

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

ContinueDebugEvent

img

第3个参数: 继续状态

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

DBG_EXCEPTION_NOT_HANDLED 只对异常事件有用

代码实现

新建工程

img

完成代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
.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
     
    ;初始化变量
    invoke  RtlZeroMemory,addr @si, size @si
    invoke  RtlZeroMemory,addr @pi, size @pi
    invoke  RtlZeroMemory,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
           
    ;循环接受调试事件
    .while  TRUE
         
            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
        invoke  RtlZeroMemory,addr @de, size @de 
         
    .endw
     
    ret
 
main endp
 
start:
    
   invoke main
 
     
end start
处理 LOAD_DLL_DEBUG_EVENT 事件

img

img

img

img

img

img

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

img

img

img

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

img

img

img

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

img

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
.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 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
  
  
.code
 
OnLoadDll proc uses esi pDE:ptr DEBUG_EVENT
        LOCAL  @dwAddr:DWORD
        LOCAL  @dwByteReaded:DWORD
        LOCAL  @szwPath[512]:WORD
          
 
    invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT 
    invoke  RtlZeroMemory,addr @szwPath, size @szwPath
 
    mov esi,pDE
    assume esi: ptr DEBUG_EVENT
     
    invoke ReadProcessMemory,g_hExe,[esi].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 [esi].u.LoadDll.fUnicode   ;如果是 unicode
     
        invoke crt_wprintf, offset g_szwLoadDllFmt,[esi].u.LoadDll.lpBaseOfDll,addr @szwPath
    .elseif
     
        invoke crt_printf, offset g_szwLoadDllFmt,[esi].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
     
    ;初始化变量
    invoke  RtlZeroMemory,addr @si, size @si
    invoke  RtlZeroMemory,addr @pi, size @pi
    invoke  RtlZeroMemory,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
    ;循环接受调试事件
    .while  TRUE
         
            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
        invoke  RtlZeroMemory,addr @de, size @de 
         
    .endw
     
    ret
 
main endp
 
start:
    
   invoke main
 
     
end start

NtQueryProcessInfomation 获取进程信息

作业

解析 CREATE_PROCESS_DEBUG_EVENT OUTPUT_DEBUG_STRING_EVENT

image.png


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 5617
活跃值: (55)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
已有多款现成的调试工具,没必要自己写一个。要把汇编代码用在其他地方上。至于什么地方,搞汇编的就不用说了,懂得都懂。
2024-8-18 23:42
0
游客
登录 | 注册 方可回帖
返回
//