楼主果然本性难移。
1. 问别人问题还遮遮掩掩的,什么“有这一段代码”,连这一段是wrk里的哪个函数的内容都不肯说出来,还要人家搜索。
好吧,这是wrk中的DbgkForwardException函数的代码。
2. 本来自己看看就能明白的东西,硬是不好好看看,这是一个判断,DbgkForwardException函数的代码你要是贴得更全,就很容易看出这个判断的LpcPort是从哪来的,如果你再仔细看KiDispatchException,更不难了解整个流程中哪个时刻是哪个被调用。这个后面详细再讲。
3. 我上次回答时明明告诉你《软件调试》中哪里有对这个过程的讲解和描述,你就是不去看(别跟我说书太贵,不用我提醒你网上有电子版流传吧),你要的答案也就是我接下来说的内容,在那部分都有提到,还是那句话,你好好看了wrk,辅以《软件调试》你就不需要在这发问了。
4. 你自己以前问过一个类似的问题:http://www.debugman.com/read.php?tid=4005
其中你自己引用的一句话:
“若有用户调试器加载,则向进程的DebugPort或 ExceptionPort发送有关异常的LPC消息并判断发送函数的返回状态,若用户调试器继续执行,则返回STATUS_SUCCESS,内核视为异常已解决继续执行。”
就是对DbgkForwardException函数所做的事情的描述,你自己引用过的话,到现在自己看wrk,就对不上号了?
下面从第2点,详细说明一下,同样还是引wrk并参考《软件调试》中的有关表述。
首先,你得把DbgkForwardException函数的代码贴更全了,至少你得能看到LpcPort从哪里来的吧:
//
// Get the address of the destination LPC port.
//
Process = PsGetCurrentProcess();
if (DebugException) {
if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
Port = NULL;
} else {
Port = Process->DebugPort;
}
LpcPort = FALSE;
} else {
Port = Process->ExceptionPort;
m.h.u2.ZeroInit = LPC_EXCEPTION;
LpcPort = TRUE;
}
//
// If the destination LPC port address is NULL, then return FALSE.
//
if (Port == NULL) {
return FALSE;
}
//
// Fill in the remainder of the debug LPC message.
//
args->ExceptionRecord = *ExceptionRecord;
args->FirstChance = !SecondChance;
//
// Send the debug message to the destination LPC port.
//
if (LpcPort) {
st = DbgkpSendApiMessageLpc(&m,Port,DebugException);
} else {
st = DbgkpSendApiMessage(&m,DebugException);
}
如果DebugException为TRUE且线程没有指定PS_CROSS_THREAD_FLAGS_HIDEFROMDBG标志,那么LpcPort为FALSE且Port有效,则会调用DbgkpSendApiMessage;
如果DebugException为TRUE但线程指定PS_CROSS_THREAD_FLAGS_HIDEFROMDBG标志,那么不通知调试器直接返回FALSE;
如果DebugException为FALSE,那么说明是要发给ExceptionPort使那里的监听者有一次处理异常的机会,那么LpcPort为TRUE,则会调用DbgkpSendApiMessageLpc。
好了,上面的代码判断逻辑就清楚了,那么到底什么时候DebugException为TRUE什么时候为FALSE呢?看KiDispatchException函数调用DbgkForwardException的代码:
if (FirstChance == TRUE) {
//
// This is the first chance to handle the exception.
//
if ((KiDebugRoutine != NULL) &&
((PsGetCurrentProcess()->DebugPort == NULL &&
!KdIgnoreUmExceptions) ||
(KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) {
//
// Now dispatch the fault to the kernel debugger.
//
if ((((KiDebugRoutine) (TrapFrame,
ExceptionFrame,
ExceptionRecord,
&ContextFrame,
PreviousMode,
FALSE)) != FALSE)) {
goto Handled1;
}
}
if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {
goto Handled2;
}
......
//
// This is the second chance to handle the exception.
//
if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {
goto Handled2;
} else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {
goto Handled2;
} else {
ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
KeBugCheckEx(
KERNEL_MODE_EXCEPTION_NOT_HANDLED,
ExceptionRecord->ExceptionCode,
(ULONG)ExceptionRecord->ExceptionAddress,
(ULONG)TrapFrame,
0);
}
}
在PreviousMode是UserMode的时候:
如果是FirstChance且内核调试引擎没有启动或没有处理异常(对KiDebugRoutine的调用返回FALSE),则调用DbgkForwardException,这时DebugException是指定为TRUE的,这样如果线程没有指定PS_CROSS_THREAD_FLAGS_HIDEFROMDBG标志,那么会调用DbgkpSendApiMessage,如指定了PS_CROSS_THREAD_FLAGS_HIDEFROMDBG标志则DbgkForwardException直接返回FALSE。
调试器没有处理,就让程序返回用户态时从KeUserExceptionDispatcher开始,进行用户态的异常处理。这部分代码上面引用时省略掉了。
如果第一次没有处理异常,那么用户态的KiUserExceptionDispatcher会再调用ZwRaiseException并将FirstChance设为FALSE,从而发起第二轮分发。
这时到了KiDispatchException,就是SecondChance的代码,首先再次调用DbgkForwardException,将DebugException和SecondChance都指定为TRUE,这样同样要不就调用DbgkpSendApiMessage要不就直接返回FALSE。
如果这次还是返回FALSE,再次调用DbgkForwardException,将DebugException指定为FALSE,SecondChance指定为TRUE,这时才会调用DbgkpSendApiMessageLpc给ExceptionPort指定的监听者一个处理异常的机会。
综上,异常分发过程中DebugPort指定的调试器可能得到的两次处理机会(注意我不是指内核调试引擎,与内核调试引擎交互是在KiDebugRoutine)均是调用DbgkpSendApiMessage,只有当前面所有的程序自身和调试器干预过程都没有能够处理异常之后,才会调用DbgkpSendApiMessageLpc给ExceptionPort指定的监听者(这里一般是Win32子系统)一个处理异常的机会(包括提示用户程序出错需要关闭)。
最后再说一句,上面我写的对wrk相关函数的流程表述,是很直白的,会看代码的应该都能看明白,我还是那句话,你真的有好好看wrk吗,如果看了,你真的有好好思考吗?