avatar

Catalog
调试器模拟程序(持续更新)

前言

目前已经完结了Windows内核系列的入门学习,在调试篇章中,每一部分都涉及到一个入门调试器的实验。每一步都是息息相关的,如果放在一起就有太多重复代码了,因此在介绍不同断点以及单步操作时,只是单独列出相应的代码,本篇则将那些代码整合起来,模拟一个简单的调试器程序。并且会在之后保持更新,逐渐完善这个调试器程序以保持对内核的熟悉,同时也会将之前的篇章引用这一篇的内容。

调试器代码

该代码会在之后不断更新,完善功能,至全部完成时,会在最后简要概括逻辑。(目前仅软件断点功能可用,硬件断点及单步异常有一些问题,内存断点与单步步过功能暂未实现

现在的问题主要是线程发生改变,原先的设置的TF位无法形成单步异常。硬件断点也是如此。

c
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// Debugger_OpenProcess.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

//宏定义与全局变量
#define dbgProcessName "C:\\notepad.exe"
#define SystemInt3 0x7C92120E //系统断点,根据环境而改变
typedef HANDLE (WINAPI *FnOpenThread) (DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwThreadId);
HANDLE hDebugeeThread;
HANDLE hDebugeeProcess;
BYTE OriginalCode; //恢复INT3断点时用到
CREATE_PROCESS_DEBUG_INFO processInfo;

//异常分发函数
BOOL ExceptionHandler(DEBUG_EVENT* pDebugEvent);

//异常处理函数
BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO* pExceptionInfo);
BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO* pExceptionInfo);

//断点设置函数
VOID SetInt3BreakPoint();
VOID SetHardBreakPoint(PVOID pAddress);
VOID SetSingleStep();

//工具函数
BOOL WaitForUserCommand();
BOOL IsSystemInt3(EXCEPTION_DEBUG_INFO* pExceptionInfo);


int main()
{

BOOL nIsConinue = TRUE;
DEBUG_EVENT debugEvent = {0};
BOOL bRet = TRUE;
DWORD dwContinue = DBG_CONTINUE;

//1.创建调试进程;
PROCESS_INFORMATION pInfo = {0};
STARTUPINFO startupInfo = {0};
GetStartupInfo(&startupInfo);

//以Create方式创建调试链接
bRet = CreateProcess(dbgProcessName, NULL, NULL, NULL, TRUE, DEBUG_PROCESS||DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &pInfo);
if (bRet == FALSE)
{
printf("CreateProcess error:%d\n", GetLastError());
getchar();
return 0;
}

//2.调试循环
while (nIsConinue)
{
//取DEBUG_EVENT
bRet = WaitForDebugEvent(&debugEvent, INFINITE);
if (!bRet)
{
printf("WaitForDebugEvent error:%d\n", GetLastError());
return 0;
}

switch (debugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// printf("异常:发生异常的地址:%X \n",debugEvent.u.Exception.ExceptionRecord.ExceptionAddress);
// printf("发生异常调试事件\n");
bRet = ExceptionHandler(&debugEvent);

if(!bRet)
dwContinue = DBG_EXCEPTION_NOT_HANDLED;
break;

case CREATE_THREAD_DEBUG_EVENT:
// printf("创建线程调试事件\n");
break;

case CREATE_PROCESS_DEBUG_EVENT:
// printf("创建进程调试事件\n");

processInfo = debugEvent.u.CreateProcessInfo;
hDebugeeProcess = processInfo.hProcess;
//OEP处下INT3断点
SetInt3BreakPoint();

break;

case EXIT_THREAD_DEBUG_EVENT:
// printf("退出线程调试事件\n");
break;

case EXIT_PROCESS_DEBUG_EVENT:
// printf("退出进程调试事件\n");
break;

case LOAD_DLL_DEBUG_EVENT:
// printf("加载DLL调试事件\n");
break;

case UNLOAD_DLL_DEBUG_EVENT:
// printf("卸载DLL调试事件\n");
break;

default:
break;
}

//DBG_CONTINUE 表示调试器已处理该异常
//DBG_EXCEPTION_NOT_HANDLED 表示调试器没有处理该异常,转回到用户态中执行,寻找可以处理该异常的异常处理器
//ContinueDebugEvent 告诉被调试程序让被调试程序继续执行
bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
}

return 0;
}


BOOL ExceptionHandler(DEBUG_EVENT* pDebugEvent)
{
BOOL bRet = TRUE;
EXCEPTION_DEBUG_INFO* pExceptionInfo = NULL;

//获取异常事件结构体
pExceptionInfo = &pDebugEvent->u.Exception;

//通过线程ID获取线程句柄
FnOpenThread MyOpenThread = (FnOpenThread)GetProcAddress(LoadLibrary("Kernel32.dll"), "OpenThread");
hDebugeeThread = MyOpenThread(THREAD_ALL_ACCESS, FALSE, pDebugEvent->dwThreadId);

switch(pExceptionInfo->ExceptionRecord.ExceptionCode)
{
//INT3异常
case EXCEPTION_BREAKPOINT:
bRet = Int3ExceptionProc(pExceptionInfo);
break;

//访问异常
case EXCEPTION_ACCESS_VIOLATION:
break;

//单步异常
case EXCEPTION_SINGLE_STEP:
bRet = SingleStepExceptionProc(pE4xceptionInfo);
break;
}

return bRet;
}


VOID SetInt3BreakPoint()
{
BYTE INT3 = 0xCC;
ReadProcessMemory(hDebugeeProcess, processInfo.lpStartAddress, &OriginalCode, 1, NULL);
WriteProcessMemory(hDebugeeProcess, processInfo.lpStartAddress, &INT3, 1, NULL);
}


BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO* pExceptionInfo)
{
BOOL bRet = FALSE;
CONTEXT Context;

//1.将INT3修复为原来的数据(如果是系统断点,不用修复)
if(IsSystemInt3(pExceptionInfo))
return TRUE;
else
WriteProcessMemory(hDebugeeProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &OriginalCode, 1, NULL);

//2.显示断点位置
printf("int 3断点: 0x%p \n",pExceptionInfo->ExceptionRecord.ExceptionAddress);

//3.获取线程上下文
Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hDebugeeThread,&Context);

//4.修复EIP
Context.Eip--;
SetThreadContext(hDebugeeThread, &Context);

//5.显示反汇编


//这里设置硬件断点
//SetHardBreakPoint((PVOID)((DWORD)pExceptionInfo->ExceptionRecord.ExceptionAddress+1));


//6.等待用户命令
while(bRet == FALSE)
{
bRet = WaitForUserCommand();
}
return bRet;
}


BOOL IsSystemInt3(EXCEPTION_DEBUG_INFO* pExceptionInfo)
{
return (pExceptionInfo->ExceptionRecord.ExceptionAddress == (PVOID)SystemInt3);
}


VOID SetHardBreakPoint(PVOID pAddress)
{
CONTEXT Context;
//1.获取线程上下文
Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hDebugeeThread, &Context);

//2.设置断点位置
Context.Dr0 = (DWORD)pAddress;
Context.Dr6 |= 1;
Context.Dr7 |= 1;

//3.设置断点长度
Context.Dr7 &= 0xfff0ffff;

//4.设置线程上下文
SetThreadContext(hDebugeeThread, &Context);
ResumeThread(hDebugeeThread);
}


VOID SetSingleStep()
{
CONTEXT Context;
Context.ContextFlags = CONTEXT_FULL || CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hDebugeeThread, &Context);
Context.EFlags |= 0x100;
SetThreadContext(hDebugeeThread, &Context);
}


BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO* pExceptionInfo)
{
CONTEXT Context;
BOOL bRet = FALSE;

//1.获取线程上下文
Context.ContextFlags = CONTEXT_FULL || CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hDebugeeThread, &Context);

//2.判断是什么导致的单步异常
if(Context.Dr6 & 0xF){ //硬件断点导致的单步异常
//显示断点信息
printf("硬件断点 %d 0x%p\n",Context.Dr7 & 0x00030000,Context.Dr0);

//去除断点
Context.Dr0 = 0;
Context.Dr7 &= 0xfffffffe;
}else{
//显示断点信息
printf("单步异常 0x%p\n",Context.Eip);

//去除单步
Context.EFlags &= 0xfffffeff;
}
SetThreadContext(hDebugeeThread, &Context);

while(bRet == FALSE)
{
bRet = WaitForUserCommand();
}

return bRet;
}


BOOL WaitForUserCommand()
{
BOOL bRet = FALSE;
char CMD;

printf("COMMAND>");

CMD = getchar();
fflush(stdin);

switch(CMD)
{
//单步步入
case't':
bRet = TRUE;
SetSingleStep();
break;

case'g':
bRet = TRUE;
break;
}
return bRet;
}

参考资料

参考笔记:

  • 张嘉杰笔记

参考教程:

  • 海哥逆向中级预习班教程
Author: cataLoc
Link: http://cataloc.gitee.io/blog/2020/09/21/%E8%B0%83%E8%AF%95%E5%99%A8%E6%A8%A1%E6%8B%9F%E7%A8%8B%E5%BA%8F/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶