-
-
[原创]Windows内核学习笔记之调试(下)
-
发表于: 2022-1-3 21:10 8995
-
调试器是在用户层读取和处理调试事件的,在用户层是使用DEBUG_EVENT结构来表示调试事件的,该结构定义如下:
其中dwDebugEventCode用来标识调试事件的类型,联合体u则是由9种不同的事件详细信息构成。根据dwDebugEventCode的不同,决定了u中包含的结构,具体对应关系如下:
Windows系统提供了WaitForDebugEvent来供调试器等待和接收调试事件,函数定义如下:
调用该函数会导致线程阻塞,直到调试事件发生,或等待时间已到或发生错误才返回。这也是大多数调试器使用多线程的原因,可使用其他线程处理UI更新和用户对话。
调试器在处理好调试事件以后,应该调用ContinueDebugEvent函数来向调试子系统回复处理结果,函数定义如下:
对于异常事件,dwContinueStatus的两个常量的差异如下:
DBG_CONTINUE表示调试器处理了该异常。DbgkForwardException函数收到此返回值后会向它的调用者(KiDispatchException)返回真
DBG_EXCEPTION_NOT_HANDLED表示调试器不处理该异常。DbgkForwardException会返回假给KiDispatchException
以下代码是上述内容的一个例子:
上面说到了WaitForDebugEvent被调试器用来等待和接收调试事件,该函数是kernel32.dll中的一个函数,从反汇编结果可以看到,函数会调用DbgUiWaitStateChange函数
DbgUiWaitStateChange函数是ntdll.dll中的一个函数,该函数会将在内核中表示调试事件的结构转换为DBGUI_WAIT_STATE_CHANGE结构,第一个参数就是用来接收转换以后的结构的地址,而DBGUI_WAIT_STATE_CHA NGE结构的定义如下:
根据DbgUiWaitStateChange函数的返回值来判断是否转换成功
如果转换成功,就会调用ntdll.dll中的DbgUiConvertStateChangeStruct函数将转换后的结构DBGUI_WAIT_STATE_CHANGE再转换为在用户层使用的调试事件的DEBUG_EVENT结构
ntdll.dll中的DbgUiWaitStateChange则是通过内核函数ZwWaitForDebugEvent来完成转换的,此时入栈的第一个参数是调试对象的句柄,第四个参数是要填充的结构体DBGUI_WAIT_STATE_CHANGE的地址
在内核函数中会通过调用ObReferenceObjectByHandle函数来获取调试对象
调用KeWaitForSingleObject对调试对象进行等待
判断等待的情况
如果符合条件,就会从调试对象的调试消息队列中获取调试消息
随后会对获取的调试消息进行验证;通过DbgkpConvertKernelToUserStateChange来将内核调试消息转换为DBGUI_WAIT_STATE_CHANGE结构,此时第一个参数是栈中的地址空间,用来保存DBGUI_WAIT_STATE_CHANGE结构,esi则是符合条件的内核调试消息结构
在DbgkpConvertKernelToUserStateChange首先对进程与线程ID进行赋值
由于结构中存在联合体,所以需要根据内核调试消息的ReturnedStatus来选择如何为剩下成员赋值并设置NewState的值
当函数返回的时候,此时局部变量var_StateChange中就保存了转换以后的DBGUI_WAIT_STATE_CHANGE结构,接下来就需要将其赋值到参数StateChange指定的区域中,该区域是用户层DBGUI_WAIT_STATE_CHANGE结构的地址
当函数返回到用户层的时候,也就是DbgUiWaitStateChange函数返回的时候,此时已经成功从内核中将调试对象的调试消息队列中保存的消息转换成DBGUI_WAIT_STATE_CHANGE结构。
DbgUiConvertStateChangeStructure就可以根据DBGUI_WAIT_STATE_CHANGE结构来生成用户层使用的DEBUG_EVENT结构,由于这两个结构都在用户层,并不需要进入内核。所以转换过程直接就在ntdll.dll中的DbgUiConvertStateChangeStructure中完成。
ContinueDebugEvent函数被用来向调试子系统回复调试器处理结果,该函数通过调用ntdll中的DbgUiContinue来实现
而DbgUiContinue则是通过调用内核函数ZwDebugContinue来实现
在内核函数中,会根据ContinueStatus来选择相应的操作
如果ContinueStatus是DBG_CONTINUE,则会调用函数获取调试对象
取出调试对象中的调试消息队列,遍历这个消息队列,根据传入的线程ID和从消息队列中的调试消息节点的线程ID进行比较,如果相等,则说明找到符合条件的调试消息节点,会将这个消息节点的地址保存到ebx并将这个消息节点删除,由于这部分代码比较乱就不贴出来,感兴趣的可以自己分析一下
如果找到了,调用SetEvent函数,此时的edi执行的是调试对象DEBUG_OBJECT,也就是对DEBUG_OBJECT的EventsPresent进行调用,通知调试器来读取调试消息队列中的调试消息
随后对调试消息进行赋值,调用DbgkpWakeTarget。此时ebx指向的就是从调试消息队列中获取的符合条件,也就是和传入的线程ID相等的调试消息对象
在DbgkpWakeTarget中首先会将调试线程唤醒
根据标志释放线程锁
根据标志调用SetEvent,此时esi指向的是消息队列中的消息节点,将它的地址加上8,得到的就是消息节点中的ContinueEvent,这样处于等待状态的被调试器进程就会被唤醒
调试器是基于异常的机制来对其他进程进行调试的,因为当进程产生了异常,就会进入异常分发函数KiDispatchException,该函数会判断是否存在调试器,如果存在调试器,就会通过DbgkForwardException将异常消息采集起来发送给调试子系统。调试子系统会将异常消息封装起来,挂入调试对象的调试消息队列中。调试器通过WaitForDebugEvent从调试消息队列中将调试消息取出,就可以获取此时被调试进程的各种信息,对其进行操作以后,在通过ContinueDebugEvent让被调试进程继续运行。
因此,调试器若想要接管被调试进程,对被调试的进程进行各种操作的话,就需要通过各种方法在被调试进程中触发异常。这样,被调试进程就会在异常分发的时候,通知调试器来接管被调试进程。
软件断点是通过特殊的”int 3“指令来触发异常,当CPU执行这条指令的时候,会触发CPU异常,这样就会进入到异常分发函数中,最后让调试进程来接管被调试进程。
因此,软件断点的实现就是调试器将需要调试的地方的指令修改为int 3(0xCC)来实现的。由于int 3指令是软件调试中最经常用到的指令,所以它又被称为调试指令。
内存断点的实现是通过修改内存属性实现的,可以通过VirtualProtectEx函数来实现
其中第4个参数则指定了新的内存属性,当它为PAGE_NOACCESS(0x1)的时候,此时会将PTE的有效位,也就是第一位(P位)修改为0。这样,当程序访问这段地址的时候,就会触发异常,调试进程就可以接管被调试进程。如果第四个参数为PAGE_EXECUTE_READ(0x20)的时候,此时PTE的P依然为1,但是决定是否可写的R/W位将被改成0。此时,如果程序对这段地址进行写操作就会触发异常。
调试器有时需要按顺序一行一行查看被调试进程的代码,观察其运行。如果使用软件断点或内存断点来实现的话,就需要频繁对被调试进程的内存或内存属性进行更改。为了方便调试器进行调试,处理器提供了两种方法来触发单步异常,分别是通过设置EFLAGS寄存器的TF位和硬件断点的方式。
IA-32架构专门分配了两个中断向量来支持软件调试,即向量1和向量3。向量3用于int 3指令产生的断点异常(#BP)。向量1用于其他情况的调试异常,简称调试异常(#DB)。而单步异常使用的就是向量1所对应的例程
下图是标志寄存器(EFLAGS寄存器)的各个位,其中第9位,也就是TF位为单步标志位。当该位为1的时候,程序执行一条指令以后就会触发单步异常,调试器就可以接管被调试进程。只要让TF位一直为1,就可以一直触发单步异常,这样就可以做到一行一行地查看被调试程序的执行。
硬件断点是通过调试寄存器来实现的,IA-32处理器定义了8个调试寄存器,分别称为DR0~DR7。在32位模式下,它们都是32位的;在64位模式下,它们都是64位的。下图是32位的调试寄存器的内容:
首先,DR4和DR5是保留的,当调试扩展功能被启用(CR4寄存器的DE位设为1)时,任何对DR4和DR5的引用都会产生一个非法指令异常(#UD),当此功能被禁止时,DR4和DR5分别是DR6和DR7的别名寄存器,即等价于访问后者。
其他6个寄存器分别如下:
4个32位的调试地址寄存器(DR0~DR3),64位下是64位的
1个32位调试控制寄存器(DR7),64位时,高32位保留未用
1个32位的调试状态寄存器(DR6),64位时,高32位保留未用
通过以上内容可知,硬件断点最多只能设置4个,因为只有DR0~DR3四个寄存器可以用来指定断点的内存(线性地址)或I/O地址。对于设置在内存空间中的断点,这个地址应该是断点的线性地址而不是物理地址,因为CPU是在线性地址被翻译为物理地址之前来做断点工作的。这意味着,在保护模式下,不能使用调试寄存器来针对一个物理内存地址设置断点。
在调试控制寄存器DR7中,有24位是被划分成4组分别与4个调试地址寄存器想对应的,比如L0, G0, R/W0和LEN0这6位是与DR0相对应的,L1, G1, R/W1和LEN1这6位是与DR1相对应的,其余的依次类推。下图列出了DR7中各个位域的具体函数:
由于EFLAGS寄存器的TF位和硬件断点都是触发单步异常,所以,调试器需要使用调试状态寄存器(DR6)来判断到底是什么原因触发的单步异常。
调试状态寄存器(DR6)的作用是当CPU检测到匹配断点条件的断点或其他调试事件发生时,用来向调试寄存器的断点异常处理程序传递断点异常的详细信息,以便使调试器可以很容易地识别除发生地是什么调试事件。例如,如果B0被置为1,那么就说明DR0,LEN0和R/W0所定义条件地断点发生了。下图列出了DR6各个标志位的具体含义:
下图则总结了各种导致调试异常的情况及该情况所产生异常的类型
对于错误类调试异常,因为恢复执行后断点条件仍然存在,所以,为了避免重复发生异常,调试软件必须在使用iretd指令返回重新执行触发异常的指令前将标志寄存器EFLAGS的RF位设为1,告诉CPU,不要再执行返回后的第一条指令时产生调试异常,CPU执行完该指令就会自动清除RF标志。
《软件调试》(第二版)卷1
《软件调试》(第二版)卷2
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
/
/
事件代码
DWORD dwProcessId;
/
/
发生调试事件进程的
ID
DWORD dwThreadId;
/
/
发生调试事件线程的
ID
union {
EXCEPTION_DEBUG_INFO Exception;
/
/
异常事件的详细信息
CREATE_THREAD_DEBUG_INFO CreateThread;
/
/
线程创建事件的详细信息
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
/
/
进程创建事件的详细信息
EXIT_THREAD_DEBUG_INFO ExitThread;
/
/
线程退出事件的详细信息
EXIT_PROCESS_DEBUG_INFO ExitProcess;
/
/
进程退出事件的详细信息
LOAD_DLL_DEBUG_INFO LoadDll;
/
/
映射DLL事件的详细信息
UNLOAD_DLL_DEBUG_INFO UnloadDll;
/
/
卸载DLL事件的详细信息
OUTPUT_DEBUG_STRING_INFO DebugString;
/
/
输出调试字符串事件的详细信息
RIP_INFO RipInfo;
/
/
内部错误事件的详细信息
} u;
} DEBUG_EVENT,
*
LPDEBUG_EVENT;
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
/
/
事件代码
DWORD dwProcessId;
/
/
发生调试事件进程的
ID
DWORD dwThreadId;
/
/
发生调试事件线程的
ID
union {
EXCEPTION_DEBUG_INFO Exception;
/
/
异常事件的详细信息
CREATE_THREAD_DEBUG_INFO CreateThread;
/
/
线程创建事件的详细信息
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
/
/
进程创建事件的详细信息
EXIT_THREAD_DEBUG_INFO ExitThread;
/
/
线程退出事件的详细信息
EXIT_PROCESS_DEBUG_INFO ExitProcess;
/
/
进程退出事件的详细信息
LOAD_DLL_DEBUG_INFO LoadDll;
/
/
映射DLL事件的详细信息
UNLOAD_DLL_DEBUG_INFO UnloadDll;
/
/
卸载DLL事件的详细信息
OUTPUT_DEBUG_STRING_INFO DebugString;
/
/
输出调试字符串事件的详细信息
RIP_INFO RipInfo;
/
/
内部错误事件的详细信息
} u;
} DEBUG_EVENT,
*
LPDEBUG_EVENT;
事件类型(dwDebugEventCode) | 值 | 说明 | 详细信息所使用的结构 |
---|---|---|---|
EXCEPTION_DEBUG_EVENT | 1 | 异常 | EXCEPTION_DEBUG_INFO |
CREATE_THREAD_DEBUG_EVENT | 2 | 创建线程 | CREATE_THREAD_DEBUG_INFO |
CREATE_PROCESS_DEBUG_EVENT | 3 | 创建进程 | CREATE_PROCESS_DEBUG_INFO |
EXIT_THREAD_DEBUG_EVENT | 4 | 线程退出 | EXIT_THREAD_DEBUG_INFO |
EXIT_PROCESS_DEBUG_EVENT | 5 | 进程退出 | EXIT_PROCESS_DEBUG_INFO |
LOAD_DLL_DEBUG_EVENT | 6 | 映射DLL | LOAD_DLL_DEBUG_INFO |
UNLOAD_DLL_DEBUG_EVENT | 7 | 卸载DLL | UNLOAD_DLL_DEBUG_INFO |
OUTPUT_DEBUG_STRING_EVENT | 8 | 输出调试信息 | OUTPUT_DEBUG_STRING_INFO |
RIP_EVENT | 9 | 内部错误 | RIP_INFO |
BOOL
WINAPI WaitForDebugEvent(
__out LPDEBUG_EVENT lpDebugEvent,
__in DWORD dwMilliseconds
);
BOOL
WINAPI WaitForDebugEvent(
__out LPDEBUG_EVENT lpDebugEvent,
__in DWORD dwMilliseconds
);
参数 | 含义 |
---|---|
lpDebugEvent | 指向DEBUG_EVENT结构的指针,用来保存收到的调试事件 |
dwMilliseconds | 指定要等待的毫秒数,或者使用常量INFINITE(0xFFFFFFFF),意思是无限等待 |
BOOL
WINAPI ContinueDebugEvent(
__in DWORD dwProcessId,
__in DWORD dwThreadId,
__in DWORD dwContinueStatus
);
BOOL
WINAPI ContinueDebugEvent(
__in DWORD dwProcessId,
__in DWORD dwThreadId,
__in DWORD dwContinueStatus
);
参数 | 含义 |
---|---|
dwProcessId | 接收到的调试事件(DEBUG_EVENT)中包含的进程ID |
dwThreadId | 接收到的调试事件(DEBUG_EVENT)中包含的线程ID |
dwContinueStatus | 可以为DBG_CONTINUE(0x001002L)和DBG_EXCEPTION_NOT_HANDLED(0x0001001L)两个常量之一,对于异常事件(EXCEPTION_DEBUG_EVENT)之外的其他所有事件,这两个常量没有差异,调试子系统收到后,都会恢复运行被调试进程(调用DbgkpResumeProcess) |
#include <cstdio>
#include <Windows.h>
#define PID 5180 // 要调试的进程PID
void ShowError(char
*
msg);
int
main()
{
DEBUG_EVENT DbgEvt;
/
/
用来读取调试事件的数据结构
DWORD dwContinueStatus
=
DBG_CONTINUE;
/
/
恢复继续执行用的状态代码
bool
bContinue
=
true;
/
/
是否继续
/
/
附加到被调试进程
bContinue
=
DebugActiveProcess(PID);
if
(!bContinue) ShowError(
"DebugActiveProcess"
);
while
(bContinue)
{
memset(&DbgEvt,
0
, sizeof(DbgEvt));
/
/
等待调试事件发生
bContinue
=
WaitForDebugEvent(&DbgEvt, INFINITE);
if
(!bContinue)
{
ShowError(
"WaitForDebugEvent"
);
break
;
}
switch (DbgEvt.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
{
printf(
"EXCEPTION_DEBUG_EVENT\n"
);
break
;
}
case CREATE_THREAD_DEBUG_EVENT:
{
printf(
"CREATE_THREAD_DEBUG_EVENT\n"
);
break
;
}
case CREATE_PROCESS_DEBUG_EVENT:
{
printf(
"CREATE_PROCESS_DEBUG_EVENT\n"
);
break
;
}
case EXIT_THREAD_DEBUG_EVENT:
{
printf(
"EXIT_THREAD_DEBUG_EVENT\n"
);
break
;
}
case EXIT_PROCESS_DEBUG_EVENT:
{
printf(
"EXIT_PROCESS_DEBUG_EVENT\n"
);
break
;
}
case LOAD_DLL_DEBUG_EVENT:
{
printf(
"LOAD_DLL_DEBUG_EVENT\n"
);
break
;
}
case UNLOAD_DLL_DEBUG_EVENT:
{
printf(
"UNLOAD_DLL_DEBUG_EVENT\n"
);
break
;
}
case OUTPUT_DEBUG_STRING_EVENT:
{
printf(
"OUTPUT_DEBUG_STRING_EVENT\n"
);
break
;
}
case RIP_EVENT:
{
printf(
"RIP_EVENT\n"
);
break
;
}
}
/
/
恢复被调试进程继续运行
bContinue
=
ContinueDebugEvent(DbgEvt.dwProcessId, DbgEvt.dwThreadId, dwContinueStatus);
if
(!bContinue) ShowError(
"ContinueDebugEvent"
);
}
return
0
;
}
void ShowError(char
*
msg)
{
printf(
"%s Error %d\n"
, msg, GetLastError());
}
#include <cstdio>
#include <Windows.h>
#define PID 5180 // 要调试的进程PID
void ShowError(char
*
msg);
int
main()
{
DEBUG_EVENT DbgEvt;
/
/
用来读取调试事件的数据结构
DWORD dwContinueStatus
=
DBG_CONTINUE;
/
/
恢复继续执行用的状态代码
bool
bContinue
=
true;
/
/
是否继续
/
/
附加到被调试进程
bContinue
=
DebugActiveProcess(PID);
if
(!bContinue) ShowError(
"DebugActiveProcess"
);
while
(bContinue)
{
memset(&DbgEvt,
0
, sizeof(DbgEvt));
/
/
等待调试事件发生
bContinue
=
WaitForDebugEvent(&DbgEvt, INFINITE);
if
(!bContinue)
{
ShowError(
"WaitForDebugEvent"
);
break
;
}
switch (DbgEvt.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
{
printf(
"EXCEPTION_DEBUG_EVENT\n"
);
break
;
}
case CREATE_THREAD_DEBUG_EVENT:
{
printf(
"CREATE_THREAD_DEBUG_EVENT\n"
);
break
;
}
case CREATE_PROCESS_DEBUG_EVENT:
{
printf(
"CREATE_PROCESS_DEBUG_EVENT\n"
);
break
;
}
case EXIT_THREAD_DEBUG_EVENT:
{
printf(
"EXIT_THREAD_DEBUG_EVENT\n"
);
break
;
}
case EXIT_PROCESS_DEBUG_EVENT:
{
printf(
"EXIT_PROCESS_DEBUG_EVENT\n"
);
break
;
}
case LOAD_DLL_DEBUG_EVENT:
{
printf(
"LOAD_DLL_DEBUG_EVENT\n"
);
break
;
}
case UNLOAD_DLL_DEBUG_EVENT:
{
printf(
"UNLOAD_DLL_DEBUG_EVENT\n"
);
break
;
}
case OUTPUT_DEBUG_STRING_EVENT:
{
printf(
"OUTPUT_DEBUG_STRING_EVENT\n"
);
break
;
}
case RIP_EVENT:
{
printf(
"RIP_EVENT\n"
);
break
;
}
}
/
/
恢复被调试进程继续运行
bContinue
=
ContinueDebugEvent(DbgEvt.dwProcessId, DbgEvt.dwThreadId, dwContinueStatus);
if
(!bContinue) ShowError(
"ContinueDebugEvent"
);
}
return
0
;
}
void ShowError(char
*
msg)
{
printf(
"%s Error %d\n"
, msg, GetLastError());
}
.text:
7C85B458
;
BOOL
__stdcall WaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMilliseconds)
.text:
7C85B458
public _WaitForDebugEvent@
8
.text:
7C85B458
_WaitForDebugEvent@
8
proc near ; DATA XREF: .text:off_7C802654↑o
.text:
7C85B458
.text:
7C85B458
DbgUiWaitStateCange
=
_DBGUI_WAIT_STATE_CHANGE ptr
-
68h
.text:
7C85B458
var_8
=
byte ptr
-
8
.text:
7C85B458
lpDebugEvent
=
dword ptr
8
.text:
7C85B458
dwMilliseconds
=
dword ptr
0Ch
.text:
7C85B458
.text:
7C85B458
mov edi, edi
.text:
7C85B45A
push ebp
.text:
7C85B45B
mov ebp, esp
.text:
7C85B45D
sub esp,
68h
.text:
7C85B460
push esi
.text:
7C85B461
push [ebp
+
dwMilliseconds]
.text:
7C85B464
lea eax, [ebp
+
var_8]
.text:
7C85B467
push eax
.text:
7C85B468
call _BaseFormatTimeOut@
8
; BaseFormatTimeOut(x,x)
.text:
7C85B46D
mov esi, eax
.text:
7C85B46F
.text:
7C85B46F
loc_7C85B46F: ; CODE XREF: WaitForDebugEvent(x,x)
+
26
↓j
.text:
7C85B46F
; WaitForDebugEvent(x,x)
+
2D
↓j
.text:
7C85B46F
push esi ; TimeOut
.text:
7C85B470
lea eax, [ebp
+
DbgUiWaitStateCange]
.text:
7C85B473
push eax ; DbgUiWaitStateCange
.text:
7C85B474
call _DbgUiWaitStateChange@
8
.text:
7C85B458
;
BOOL
__stdcall WaitForDebugEvent(LPDEBUG_EVENT lpDebugEvent, DWORD dwMilliseconds)
.text:
7C85B458
public _WaitForDebugEvent@
8
.text:
7C85B458
_WaitForDebugEvent@
8
proc near ; DATA XREF: .text:off_7C802654↑o
.text:
7C85B458
.text:
7C85B458
DbgUiWaitStateCange
=
_DBGUI_WAIT_STATE_CHANGE ptr
-
68h
.text:
7C85B458
var_8
=
byte ptr
-
8
.text:
7C85B458
lpDebugEvent
=
dword ptr
8
.text:
7C85B458
dwMilliseconds
=
dword ptr
0Ch
.text:
7C85B458
.text:
7C85B458
mov edi, edi
.text:
7C85B45A
push ebp
.text:
7C85B45B
mov ebp, esp
.text:
7C85B45D
sub esp,
68h
.text:
7C85B460
push esi
.text:
7C85B461
push [ebp
+
dwMilliseconds]
.text:
7C85B464
lea eax, [ebp
+
var_8]
.text:
7C85B467
push eax
.text:
7C85B468
call _BaseFormatTimeOut@
8
; BaseFormatTimeOut(x,x)
.text:
7C85B46D
mov esi, eax
.text:
7C85B46F
.text:
7C85B46F
loc_7C85B46F: ; CODE XREF: WaitForDebugEvent(x,x)
+
26
↓j
.text:
7C85B46F
; WaitForDebugEvent(x,x)
+
2D
↓j
.text:
7C85B46F
push esi ; TimeOut
.text:
7C85B470
lea eax, [ebp
+
DbgUiWaitStateCange]
.text:
7C85B473
push eax ; DbgUiWaitStateCange
.text:
7C85B474
call _DbgUiWaitStateChange@
8
typedef struct _DBGUI_WAIT_STATE_CHANGE
{
DBG_STATE NewState;
/
/
枚举常量,代表新的调试状态
CLIENT_ID AppClientId;
/
/
包含进程和线程句柄
union {
DBGKM_EXCEPTION Exception;
/
/
异常
DBGUI_CREATE_THREAD CreateThread;
/
/
创建线程
DBGUI_CREATE_PROCESS CreateProcessInfo;
/
/
创建进程
DBGKM_EXIT_THREAD ExitThread;
/
/
线程退出
DBGKM_EXIT_PROCESS ExitProcess;
/
/
进程退出
DBGKM_LOAD_DLL LoadDll;
/
/
映射模块
DBGKM_UNLOAD_DLL UnloadDll;
/
/
卸载模块
} StateInfo;
}DBGUI_WAIT_STATE_CHANGE,
*
PDBGUI_WAIT_STATE_CHANGE;
typedef struct _DBGUI_WAIT_STATE_CHANGE
{
DBG_STATE NewState;
/
/
枚举常量,代表新的调试状态
CLIENT_ID AppClientId;
/
/
包含进程和线程句柄
union {
DBGKM_EXCEPTION Exception;
/
/
异常
DBGUI_CREATE_THREAD CreateThread;
/
/
创建线程
DBGUI_CREATE_PROCESS CreateProcessInfo;
/
/
创建进程
DBGKM_EXIT_THREAD ExitThread;
/
/
线程退出
DBGKM_EXIT_PROCESS ExitProcess;
/
/
进程退出
DBGKM_LOAD_DLL LoadDll;
/
/
映射模块
DBGKM_UNLOAD_DLL UnloadDll;
/
/
卸载模块
} StateInfo;
}DBGUI_WAIT_STATE_CHANGE,
*
PDBGUI_WAIT_STATE_CHANGE;
.text:
7C85B479
cmp
eax, STATUS_ALERTED
.text:
7C85B47E
jz short loc_7C85B46F
.text:
7C85B480
cmp
eax, STATUS_USER_APC
.text:
7C85B485
jz short loc_7C85B46F
.text:
7C85B487
test eax, eax
.text:
7C85B489
jge short loc_7C85B492
.text:
7C85B48B
cmp
eax, DBG_UNABLE_TO_PROVIDE_HANDLE
.text:
7C85B490
jnz short loc_7C85B4B8
.text:
7C85B492
.text:
7C85B492
loc_7C85B492: ; CODE XREF: WaitForDebugEvent(x,x)
+
31
↑j
.text:
7C85B492
cmp
eax, STATUS_TIMEOUT
.text:
7C85B497
jnz short loc_7C85B4A7
.text:
7C85B499
push
79h
; dwErrCode
.text:
7C85B49B
call _SetLastError@
4
; SetLastError(x)
.text:
7C85B4A0
.text:
7C85B4A0
loc_7C85B4A0: ; CODE XREF: WaitForDebugEvent(x,x)
+
66
↓j
.text:
7C85B4A0
; WaitForDebugEvent(x,x)
+
6E
↓j
.text:
7C85B4A0
xor eax, eax ; jumptable
7C85B4C8
default case
.text:
7C85B4A2
.text:
7C85B4A2
loc_7C85B4A2: ; CODE XREF: WaitForDebugEvent(x,x)
+
BA↓j
.text:
7C85B4A2
pop esi
.text:
7C85B4A3
leave
.text:
7C85B4A4
retn
8
.text:
7C85B479
cmp
eax, STATUS_ALERTED
.text:
7C85B47E
jz short loc_7C85B46F
.text:
7C85B480
cmp
eax, STATUS_USER_APC
.text:
7C85B485
jz short loc_7C85B46F
.text:
7C85B487
test eax, eax
.text:
7C85B489
jge short loc_7C85B492
.text:
7C85B48B
cmp
eax, DBG_UNABLE_TO_PROVIDE_HANDLE
.text:
7C85B490
jnz short loc_7C85B4B8
.text:
7C85B492
.text:
7C85B492
loc_7C85B492: ; CODE XREF: WaitForDebugEvent(x,x)
+
31
↑j
.text:
7C85B492
cmp
eax, STATUS_TIMEOUT
.text:
7C85B497
jnz short loc_7C85B4A7
.text:
7C85B499
push
79h
; dwErrCode
.text:
7C85B49B
call _SetLastError@
4
; SetLastError(x)
.text:
7C85B4A0
.text:
7C85B4A0
loc_7C85B4A0: ; CODE XREF: WaitForDebugEvent(x,x)
+
66
↓j
.text:
7C85B4A0
; WaitForDebugEvent(x,x)
+
6E
↓j
.text:
7C85B4A0
xor eax, eax ; jumptable
7C85B4C8
default case
.text:
7C85B4A2
.text:
7C85B4A2
loc_7C85B4A2: ; CODE XREF: WaitForDebugEvent(x,x)
+
BA↓j
.text:
7C85B4A2
pop esi
.text:
7C85B4A3
leave
.text:
7C85B4A4
retn
8
.text:
7C85B4A7
loc_7C85B4A7: ; CODE XREF: WaitForDebugEvent(x,x)
+
3F
↑j
.text:
7C85B4A7
mov esi, [ebp
+
lpDebugEvent]
.text:
7C85B4AA
push esi ; DebugEvent
.text:
7C85B4AB
lea eax, [ebp
+
DbgUiWaitStateCange]
.text:
7C85B4AE
push eax ; WaitStateChange
.text:
7C85B4AF
call _DbgUiConvertStateChangeStructure@
8
; DbgUiConvertStateChangeStructure(x,x)
.text:
7C85B4B4
test eax, eax
.text:
7C85B4B6
jge short loc_7C85B4C0
.text:
7C85B4A7
loc_7C85B4A7: ; CODE XREF: WaitForDebugEvent(x,x)
+
3F
↑j
.text:
7C85B4A7
mov esi, [ebp
+
lpDebugEvent]
.text:
7C85B4AA
push esi ; DebugEvent
.text:
7C85B4AB
lea eax, [ebp
+
DbgUiWaitStateCange]
.text:
7C85B4AE
push eax ; WaitStateChange
.text:
7C85B4AF
call _DbgUiConvertStateChangeStructure@
8
; DbgUiConvertStateChangeStructure(x,x)
.text:
7C85B4B4
test eax, eax
.text:
7C85B4B6
jge short loc_7C85B4C0
.text:
7C96FF75
public DbgUiWaitStateChange
.text:
7C96FF75
DbgUiWaitStateChange proc near ; DATA XREF: .text:off_7C923428↑o
.text:
7C96FF75
.text:
7C96FF75
arg_0
=
dword ptr
8
.text:
7C96FF75
arg_4
=
dword ptr
0Ch
.text:
7C96FF75
.text:
7C96FF75
mov edi, edi
.text:
7C96FF77
push ebp
.text:
7C96FF78
mov ebp, esp
.text:
7C96FF7A
mov eax, large fs:
18h
; 获取TEB的地址
.text:
7C96FF80
push [ebp
+
arg_0]
.text:
7C96FF83
push [ebp
+
arg_4]
.text:
7C96FF86
push
1
.text:
7C96FF88
push dword ptr [eax
+
0F24h
] ; 压入调试对象句柄
.text:
7C96FF8E
call ZwWaitForDebugEvent
.text:
7C96FF93
pop ebp
.text:
7C96FF94
retn
8
.text:
7C96FF94
DbgUiWaitStateChange endp
.text:
7C96FF75
public DbgUiWaitStateChange
.text:
7C96FF75
DbgUiWaitStateChange proc near ; DATA XREF: .text:off_7C923428↑o
.text:
7C96FF75
.text:
7C96FF75
arg_0
=
dword ptr
8
.text:
7C96FF75
arg_4
=
dword ptr
0Ch
.text:
7C96FF75
.text:
7C96FF75
mov edi, edi
.text:
7C96FF77
push ebp
.text:
7C96FF78
mov ebp, esp
.text:
7C96FF7A
mov eax, large fs:
18h
; 获取TEB的地址
.text:
7C96FF80
push [ebp
+
arg_0]
.text:
7C96FF83
push [ebp
+
arg_4]
.text:
7C96FF86
push
1
.text:
7C96FF88
push dword ptr [eax
+
0F24h
] ; 压入调试对象句柄
.text:
7C96FF8E
call ZwWaitForDebugEvent
.text:
7C96FF93
pop ebp
.text:
7C96FF94
retn
8
.text:
7C96FF94
DbgUiWaitStateChange endp
PAGE:
0058BC8D
push esi ; HandleInformation
PAGE:
0058BC8E
lea eax, [ebp
+
Object
]
PAGE:
0058BC91
push eax ;
Object
PAGE:
0058BC92
push dword ptr [ebp
+
AccessMode] ; AccessMode
PAGE:
0058BC95
push _DbgkDebugObjectType ; ObjectType
PAGE:
0058BC9B
push
1
; DesiredAccess
PAGE:
0058BC9D
push [ebp
+
DebugObject] ; Handle
PAGE:
0058BCA0
call _ObReferenceObjectByHandle@
24
; ObReferenceObjectByHandle(x,x,x,x,x,x)
PAGE:
0058BCA5
mov edi, [ebp
+
Object
] ; 将调试对象赋给edi
PAGE:
0058BCA8
cmp
eax, esi
PAGE:
0058BCAA
jl loc_58BE92
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
- [原创]CVE-2022-21882提权漏洞学习笔记 16383
- [原创]CVE-2021-1732提权漏洞学习笔记 19489
- [原创]CVE-2014-1767提权漏洞学习笔记 15192
- [原创]CVE-2018-8453提权漏洞学习笔记 18526
- [原创]CVE-2020-1054提权漏洞学习笔记 13542