快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

体育投注十大平台_酒文化网进入



前两篇座谈先容了在内核中如何使用SEH机制和宏操作_SEH_TRY、_SEH_HANDLE、以及_SEH_END为可能引起非常的代码供给保护,以及在非常果然发生时的处置惩罚历程。读者现在对付SEH机制的道理应该已经有了一些懂得。前面也曾提到,Windows操作系统在用户空间也供给了同样的功能,而且实现的要领也基真相同。然则,在前两篇座谈中读者所看到的是当非常发生于系统空间时的情景,而并没有看到当非常发生于用户空间时如何颠末内核被提交到用户空间、并在那里得到处置惩罚的历程。

我们知道,非常就像中断,不管是什么缘故原由(“软非常”除外)所引起,一旦发生首先辈入的是内核中的非常相应/处置惩罚法度榜样的进口,这便是类似于KiTrap0()那样的底层内核函数,只体育投注十大平台是由于引起非常的缘故原由不合而进入不合的进口,就像对付不合的中断向量有不合的进口一样。在内核中,仍以页面非常为例,正如读者已经看到,CPU会从KiTrap14()进入函数KiPageFaultHandler()。在那儿,假如所发生的并非如“缺页”或“写时复制(Copy-On-Write)”那样的“正常”非常,就要根据CPU在发生非常时所处的空间而分手调用KiKernelTrapHandler()或KiUserTrapHandler()。假如调用的是KiKernelTrapHandler(),就会顺着KPCR数据布局中的“非常(处置惩罚)行列步队”、即ExceptionList,依次让各个节点认领。假如被认领,就会经由过程SEHLongJmp()长程跳转到当初经由过程_SEH_HANDLE{}给定的代码中。这读者已经见到了。

然则,假如非常发生于用户空间,受到调用的便是KiUserTrapHandler()。

[_KiTrap14() > KiPageFaultHandler() > KiUserTrapHandler()]

ULONG NTAPI

KiUserTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr, PVOID Cr2)

{

EXCEPTION_RECORD Er;

if (ExceptionNr == 0)

{

Er.ExceptionCode = STATUS_INTEGER_DIVIDE_BY_ZERO;

}

else if (ExceptionNr == 1)

{

Er.ExceptionCode = STATUS_SINGLE_STEP;

}

else if (ExceptionNr == 3)

{

Er.ExceptionCode = STATUS_BREAKPOINT;

}

else if (ExceptionNr == 4)

{

Er.ExceptionCode = STATUS_INTEGER_OVERFLOW;

}

else if (ExceptionNr == 5)

{

Er.ExceptionCode = STATUS_ARRAY_BOUNDS_EXCEEDED;

}

else if (ExceptionNr == 6)

{

Er.ExceptionCode = STATUS_ILLEGAL_INSTRUCTION;

}

else

{

Er.ExceptionCode = STATUS_ACCESS_VIOLATION;

}

Er.ExceptionFlags = 0;

Er.ExceptionRecord = NULL;

Er.ExceptionAddress = (PVOID)Tf->Eip;

if (ExceptionNr == 14)

{

Er.NumberParameters = 2;

Er.ExceptionInformation[0] = Tf->ErrCode & 0x1;

Er.ExceptionInformation[1] = (ULONG)Cr2;

}

else

{

Er.NumberParameters = 0;

}

/* FIXME: Which exceptions are noncontinuable? */

Er.ExceptionFlags = 0;

KiDispatchException(&Er, 0, Tf, UserMode, TRUE);

return(0);

}

显然,这个函数不是仅为14号非常所用的。只如果发生于用户空间,其余非常也会进入这个函数。同样,这里也是先在客栈上筹备好一个“非常记录块”,然后调用KiDispatchException(),只不过这一次的第四个实际参数是UserMode。读者在上一篇座谈中看到,KiKernelTrapHandler()也调用这同一个函数,然则那里的第四个实际参数是KernelMode。

以是,KiDispatchException()是个十分紧张的函数,现在我们又要回偏激来看这个函数的代码。当然,这一次察看的角度不合了。

[_KiTrap14() > KiPageFaultHandler() > KiUserTrapHandler() > KiDispatchException()]

VOID NTAPI

KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,

PKEXCEPTION_FRAME ExceptionFrame,

PKTRAP_FRAME TrapFrame,

KPROCESSOR_MODE PreviousMode,

BOOLEAN FirstChance)

{

. . . . . .

/* Set the context体育投注十大平台 flags */

Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;

/* Check if User Mode */

if (PreviousMode == UserMode)

{

/* Add the FPU Flag */

Context.ContextFlags |= CONTEXT_FLOATING_POINT;

if (KeI386FxsrPresent) Context.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;

}

/* Get a Context */

KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);

/* Handle kernel-mode first, it's simpler */

if (PreviousMode == KernelMode)

{

. . . . . .

}

else

{

/* User mode exception, was it first-chance? */

if (FirstChance)

{

/* Enter Debugger if available */

Action = KdpEnterDebuggerException(ExceptionRecord, PreviousMode,

&Context, TrapFrame, TRUE, TRUE);

/* Exit if we're 体育投注十大平台continuing */

if (Action == kdContinue) goto Handled;

/* FIXME: Forward exception to user mode debugger */

/* Set up the user-stack */

_SEH_TRY

{

/* Align context size and get stack pointer */

Size = (sizeof(CONTEXT) + 3) & ~3;

Stack = (Context.Esp & ~3) - Size;

DPRINT("Stack: %lxn", Stack);

/* Probe stack and copy Context */

ProbeForWrite((PVOID)Stack, Size, sizeof(ULONG));

RtlCopyMemory((PVOID)Stack, &Context, sizeof(CONTEXT));

/* Align exception record size and get stack pointer */

Size = (sizeof(EXCEPTION_RECORD) -

(EXCEPTION_MAXIMUM_PARAMETERS -

ExceptionRecord->NumberParameters) * sizeof(ULONG) + 3) & ~3;

NewStack = Stack - Size;

DPRINT("NewStack: %lxn", NewStack);

/* Probe stac体育投注十大平台k and copy exception record. Don't forget to add the two params */

ProbeForWrite((PVOID)(NewStack - 2 * sizeof(ULONG_PTR)),

Size +  2 * sizeof(ULONG_PTR),

sizeof(ULONG));

RtlCopyMemory((PVOID)NewStack, ExceptionRecord, Size);

/* Now write the two params for the user-mode dispatcher */

*(PULONG_PTR)(NewStack - 1 * sizeof(ULONG_PTR)) = Stack;

*(PULONG_PTR)(NewStack - 2 * sizeof(ULONG_PTR)) = NewStack;

/* Set new Stack Pointer */

KiEspToTrapFrame(TrapFrame, NewStack - 2 * sizeof(ULONG_PTR));

/* Set EIP to the User-mode Dispathcer */

TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;

UserDispatch = TRUE;

_SEH_LEAVE;

}

_SEH_HANDLE

{

/* Do second-chance */

}

_SEH_END;

}

/* If we dispatch to user, return now */

if (UserDispatch) return;

/* FIXME: Forward the exception to the debugger for 2nd chance */

/* 3rd strike, kill the thread */

DPRINT1("Unhandled UserMode exception, terminating threadn");

ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);

KEBUGCHECKWITHTF(KMODE_EXCEPTION_NOT_HANDLED,

ExceptionRecord->ExceptionCode,

(ULONG_PTR)ExceptionRecord->ExceptionAddress,

ExceptionRecord->ExceptionInformation[0],

ExceptionRecord->ExceptionInformation[1],

TrapFrame);

}

Handled:

/* Convert the context back into Trap/Exception Frames */

KeContextToTrapFrame(&Context, NULL, TrapFrame, Context.ContextFlags, PreviousMode);

return;

}

首先经由过程KeTrapFrameToContext()从客栈上的非常框架收拾出一个高低文数据布局来。不过,对付用户空间的非常处置惩罚高低文中必要有更周全的信息,以是在调用这个函数之前把高低文布局中的CONTEXT_FLOATING_POINT等标志位设成1体育投注十大平台。这些标志位实质上便是对KeTrapFrameToContext()的指令。

这一次我们把留意集中在PreviousMode为UserMode的分支上。

读者不妨想想,对付发生于用户空间的非常,这里应该做些什么。显然,用户空间的非常不应靠内核里面的法度榜样处置惩罚,利用软件理应为此作好了筹备。前面讲过,Windows的SEH机制并不是仅为内核而设计的,用户空间的法度榜样同样可以应用类似于_SEH_TRY{} _SEH_HANDLE{} _SEH_END那样的手段为利用法度榜样供给保护。事实上,在经由过程NtCreateThread()创建的线程首次被调整运行时,全部线程的履行都是作为一个SEH域而受到保护的:

VOID STDCALL

BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress)

{

UINT uExitCode = 0;

_SEH_TRY

{

/* Set our Start Address */

NtSetInformationThread(NtCurrentThread(),ThreadQuerySetWin32StartAddress,

&lpStartAddress, sizeof(PPROCESS_START_ROUTINE));

/* Call the Start Routine */

uExitCode = (lpStartAddress)();

}

_SEH_EXCEPT(BaseExceptionFilter)

{

/* Get the SEH Error */

uExitCode = _SEH_GetExceptionCode();

}

_SEH_END;

/* Exit the Process with our error */

ExitProcess(uExitCode);

}

这里BaseProcessStartup()是所有线程在用户空间的总进口,而lpStartAddress是详细线程的代码进口。这里引用的宏操作之一是_SEH_EXCEPT,而不是_SEH_HANDLE,因而可以供给一个过滤函数。这个过滤函数是BaseExceptionFilter(),它又经由过程一个函数指针调用实际的过滤函数,默觉得UnhandledExceptionFilter()。而UnhandledExceptionFilter()在一样平常环境下都返回EXCEPTION_EXECUTE_HANDLER。不过,利用法度榜样可以经由过程一个函数SetUnhandledExceptionFilter()将其调换成自己想要的过滤函数。

与此响应,用户空间的每个线程都有一个ExceptionList,只不过这个行列步队在每个线程的TEB中,而不是在KPCR中。既然内核中的ExceptionList是由KiDispatchException()加以处置惩罚的,用户空间就应该有个类似于KiDispatchException()的函数。事实上,动态连接库ntdll.dll中的KiUserExceptionDispatcher()便是用户空间SEH处置惩罚的总进口。

可是,只管是发生于用户空间的非常,对非常的初期相应和处置惩罚终究是在内核中,现在的目的便是要从内核中的KiDispatchException()启动用户空间这个函数的履行。

对付内核中的KiDispatchException(),这便是针对用户空间非常的主要操作。不过详细的实现还要再繁杂一些,就像针对系统空间非常一样,内核中涉及用户空间非常的处置惩罚也分三步:

第一步、参数FirstChance为1时,先经由过程KdpEnterDebuggerException()交由内核调试法度榜样(Kernel Debugger)处置惩罚。假如内核调试法度榜样办理了问题、或者觉得无需提交用户空间,则返回值便是kdContinue,这就行了。否则就要把非常提交给用户空间,由用户空间的法度榜样加以处置惩罚。代码中的_SEH_TRY{}里面便是启动用户空间非常处置惩罚的历程。对付绝大年夜多半的用户空间非常,这就可以了,由于用户空间的ExceptionList中应该有节点可以认领和处置惩罚本次非常,例如经由过程预先的安排实施用户空间的长程跳转。

第二步、然而,万一用户空间处置惩罚不了,例如ExceptionList中没有安排下可以认领、处置惩罚本次非常的节点,就会经由过程RtlRaiseException()、从而经由过程系统调用ZwRaiseException()提议一次“软非常”(见后),把问题交还内核。此时CPU再次进入KiDispatchException(),然则此时的实际参数FirstChance为0,以是直接进入第二步步伐。在Windows内核中,这第二次努力是经由过程进程间通信向用户空间的调试法度榜样(Debugger)发送一个报文、将其唤醒,由调试法度榜样作进一步的处置惩罚。例如,对付由用户空间调试法度榜样设置的断点(INT3),就只能由用户空间调试法度榜样加以处置惩罚。不过,在ReactOS 0.3.0版的代码中这一步尚未实现,以是这里有个注释说:“FIXME: Forward the exception to the debugger for 2nd chance”。

第三步、假如用户空间调试法度榜样不存在,或者也不能办理,那就属于弗成规复的问题了。于是就有第三步步伐,那便是经由过程ZwTerminateThread()停止当火线程的运行。正常环境下针对当火线程本身的ZwTerminateThread()是不返回的;而假使竟然返回了,那对付全部系统都是弗成规复的问题了,以是经由过程宏操作KEBUGCHECKWITHTF()显示掉足信息、转储(Dump)当时的内存映像,并进入一个Ke386HaltProcessor()的无限轮回。换言之,全部系统就“逝世”了。

显然,这里最关键的一步、也是最有盼望的一步,是把非常提交给用户空间。怎么提交呢?首先要把高低文数据布局Context和非常记载块ExceptionRecord拷贝到用户空间客栈上去,再在用户空间客栈上安上两个指针,分手指向这两个数据布局的用户空间副本,并响应调剂非常框架中的用户空间客栈指针。下面就会看到,这两个指针将被用感化户空间的函数调用参数。着末、也是最关键的,则是把非常框架中的用户空间返回地址设置成函数指针KeUserExceptionDispatcher所指向的函数。顺利完成了这些筹备今后,就把局部量UserDispatch设成1,是以紧接着就从本次非常处置惩罚返回了。当然,这是返回到了指针KeUserExceptionDispatcher所指向的函数中。已经认识APC机制的读者应该很轻易由此遐想到对用户空间APC函数的调用。事实上也确凿异常相似,假如说APC相称于对用户空间软件的中断机制,则非常的提交就相称于对用户空间软件的非常机制。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: