NTSTATUS KiRaiseException( IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN BOOLEAN FirstChance );
/* VOID __stdcall KiDispatchException ( IN PEXCEPTION_RECORD ExceptionRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN FirstChance ) */
该函数主要功能: ① 该函数实参一为关键字throw抛出的值1,实参二为一个地址。但是在本函数中都没有用到。 ② 在函数栈里面开辟了32字节空间。 ③ 将数值 0xE06D7363、0x1、0x0、0x0、0x3、0x19930520、0x0、0x0 共32字节复制到栈里面。 ④ 调用 RaiseException 函数。
//#define APIENTRY _stdcall VOID APIENTRY RaiseException( DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, CONST ULONG_PTR *lpArguments ) /*++ Routine Description: Raising an exception causes the exception dispatcher to go through its search for an exception handler. This includes debugger notification, frame based handler searching, and system default actions. Arguments: dwExceptionCode - Supplies the exception code of the exception being raised. This value may be obtained in exception filters and in exception handlers by calling GetExceptionCode. dwExceptionFlags - Supplies a set of flags associated with the exception. dwExceptionFlags Flags: EXCEPTION_NONCONTINUABLE - The exception is non-continuable. Returning EXCEPTION_CONTINUE_EXECUTION from an exception marked in this way causes the STATUS_NONCONTINUABLE_EXCEPTION exception. nNumberOfArguments - Supplies the number of arguments associated with the exception. This value may not exceed EXCEPTION_MAXIMUM_PARAMETERS. This parameter is ignored if lpArguments is NULL. lpArguments - An optional parameter, that if present supplies the arguments for the exception. Return Value: None. --*/ { EXCEPTION_RECORD ExceptionRecord; ULONG n; PULONG_PTR s,d; ExceptionRecord.ExceptionCode = (DWORD)dwExceptionCode; ExceptionRecord.ExceptionFlags = dwExceptionFlags & EXCEPTION_NONCONTINUABLE; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.ExceptionAddress = (PVOID)RaiseException; if ( ARGUMENT_PRESENT(lpArguments) ) { n = nNumberOfArguments; if ( n > EXCEPTION_MAXIMUM_PARAMETERS ) { n = EXCEPTION_MAXIMUM_PARAMETERS; } ExceptionRecord.NumberParameters = n; s = (PULONG_PTR)lpArguments; d = ExceptionRecord.ExceptionInformation; while(n--){ *d++ = *s++; } } else { ExceptionRecord.NumberParameters = 0; } RtlRaiseException(&ExceptionRecord); }
NTSTATUS __stdcall KiRaiseException( IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN BOOLEAN FirstChance ) /*++ Routine Description: This function is called to raise an exception. The exception can be raised as a first or second chance exception. Arguments: ExceptionRecord - Supplies a pointer to an exception record. ContextRecord - Supplies a pointer to a context record. ExceptionFrame - Supplies a pointer to an exception frame. TrapFrame - Supplies a pointer to a trap frame. FirstChance - Supplies a boolean value that specifies whether this is the first (TRUE) or second (FALSE) chance for the exception. Return Value: STATUS_ACCESS_VIOLATION is returned if either the exception or the context record is not readable from user mode. STATUS_DATATYPE_MISALIGNMENT is returned if the exception record or the context record are not properly aligned. STATUS_INVALID_PARAMETER is returned if the number of exception parameters is greater than the maximum allowable number of exception parameters. STATUS_SUCCESS is returned if the exception is dispatched and handled. --*/ { CONTEXT ContextRecord2; EXCEPTION_RECORD ExceptionRecord2; ULONG Length; ULONG Params; KPROCESSOR_MODE PreviousMode;
// Establish an exception handler and probe the specified exception and // context records for read accessibility. If the probe fails, then // return the exception code as the service status. Else call the exception // dispatcher to dispatch the exception. try {
// Get the previous processor mode. If the previous processor mode // is user, then probe and copy the specified exception and context records. PreviousMode = KeGetPreviousMode(); if (PreviousMode != KernelMode) { ProbeForReadSmallStructure(ContextRecord, sizeof(CONTEXT), CONTEXT_ALIGN); ProbeForReadSmallStructure(ExceptionRecord, FIELD_OFFSET (EXCEPTION_RECORD, NumberParameters) + sizeof (ExceptionRecord->NumberParameters), sizeof(ULONG)); Params = ExceptionRecord->NumberParameters; if (Params > EXCEPTION_MAXIMUM_PARAMETERS) { return STATUS_INVALID_PARAMETER; }
// The exception record structure is defined unlike others with trailing // information as being its maximum size rather than just a single trailing element. // Length = (sizeof(EXCEPTION_RECORD) - ((EXCEPTION_MAXIMUM_PARAMETERS - Params) * sizeof(ExceptionRecord->ExceptionInformation[0])));
// // The structure is currently less that 64k so we don't really need this probe. ProbeForRead(ExceptionRecord, Length, sizeof(ULONG));
// Copy the exception and context record to local storage so an // access violation cannot occur during exception dispatching. RtlCopyMemory(&ContextRecord2, ContextRecord, sizeof(CONTEXT)); RtlCopyMemory(&ExceptionRecord2, ExceptionRecord, Length); ContextRecord = &ContextRecord2; ExceptionRecord = &ExceptionRecord2;
// The number of parameters might have changed after we validated but before we // copied the structure. Fix this up as lower levels might not like this. ExceptionRecord->NumberParameters = Params; }
// If an exception occurs during the probe of the exception or context // record, then always handle the exception and return the exception code as the status value. } except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
// Move information from the context record to the exception and trap frames. KeContextToKframes(TrapFrame, ExceptionFrame, ContextRecord, ContextRecord->ContextFlags, PreviousMode);
// Make sure the reserved bit is clear in the exception code and // perform exception dispatching. // // N.B. The reserved bit is used to differentiate internally gerarated // codes from codes generated by application programs. ExceptionRecord->ExceptionCode &= 0xefffffff; KiDispatchException(ExceptionRecord, ExceptionFrame, TrapFrame, PreviousMode, FirstChance);
NTKERNELAPI VOID NTAPI ProbeForRead( IN CONST VOID *Address, IN SIZE_T Length, IN ULONG Alignment ) /*++ Routine Description: This function probes a structure for read accessibility and ensures correct alignment of the structure. If the structure is not accessible or has incorrect alignment, then an exception is raised. Arguments: Address - Supplies a pointer to the structure to be probed. Length - Supplies the length of the structure. Alignment - Supplies the required alignment of the structure expressed as the number of bytes in the primitive datatype (e.g., 1 for char, 2 for short, 4 for long, and 8 for quad). Return Value: None. --*/ { PAGED_CODE();
VOID ProbeForWrite( IN PVOID Address, IN SIZE_T Length, IN ULONG Alignment ) /*++ Routine Description: This function probes a structure for write accessibility and ensures correct alignment of the structure. If the structure is not accessible or has incorrect alignment, then an exception is raised. Arguments: Address - Supplies a pointer to the structure to be probed. Length - Supplies the length of the structure. Alignment - Supplies the required alignment of the structure expressed as the number of bytes in the primitive datatype (e.g., 1 for char, 2 for short, 4 for long, and 8 for quad). Return Value: None. --*/ { ULONG_PTR EndAddress; ULONG_PTR StartAddress; #if defined(_WIN64) ULONG_PTR PageSize; #else #define PageSize PAGE_SIZE #endif
// If the structure has zero length, then do not probe the structure for write accessibility or alignment. if (Length != 0) {
// If the structure is not properly aligned, then raise a data misalignment exception. ASSERT((Alignment == 1) || (Alignment == 2) || (Alignment == 4) || (Alignment == 8) || (Alignment == 16));
// Compute the ending address of the structure and probe for // write accessibility. EndAddress = StartAddress + Length - 1; if ((StartAddress <= EndAddress) && (EndAddress < MM_USER_PROBE_ADDRESS)) {
// N.B. Only the contents of the buffer may be probed. // Therefore the starting byte is probed for the // first page, and then the first byte in the page // for each succeeding page. #if defined(_WIN64) // // If this is a Wow64 process, then the native page is 4K, which // could be smaller than the native page size/ if (PsGetCurrentProcess()->Wow64Process != NULL) { PageSize = PAGE_SIZE_X86NT; } else { PageSize = PAGE_SIZE; } #endif
VOID __stdcall KiDispatchException( IN PEXCEPTION_RECORD ExceptionRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN FirstChance ) /*++ Routine Description: This function is called to dispatch an exception to the proper mode and to cause the exception dispatcher to be called. If the previous mode is kernel, then the exception dispatcher is called directly to process the exception. Otherwise the exception record, exception frame, and trap frame contents are copied to the user mode stack. The contents of the exception frame and trap are then modified such that when control is returned, execution will commense in user mode in a routine which will call the exception dispatcher. Arguments: ExceptionRecord - Supplies a pointer to an exception record. ExceptionFrame - Supplies a pointer to an exception frame. For NT386, this should be NULL. TrapFrame - Supplies a pointer to a trap frame. PreviousMode - Supplies the previous processor mode. FirstChance - Supplies a boolean value that specifies whether this is the first (TRUE) or second (FALSE) chance for the exception. Return Value: None. --*/ { CONTEXT ContextFrame; EXCEPTION_RECORD ExceptionRecord1, ExceptionRecord2; LONG Length; ULONG UserStack1; ULONG UserStack2;
// Move machine state from trap and exception frames to a context frame, // and increment the number of exceptions dispatched. KeGetCurrentPrcb()->KeExceptionDispatchCount += 1; ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; if (PreviousMode == UserMode) {
// For usermode exceptions always try to dispatch the floating // point state. This allows expection handlers & debuggers to // examine/edit the npx context if required. Plus it allows // exception handlers to use fp instructions without detroying // the npx state at the time of the exception. // // Note: If there's no 80387, ContextTo/FromKFrames will use the // emulator's current state. If the emulator can not give the // current state, then the context_floating_point bit will be // turned off by ContextFromKFrames. ContextFrame.ContextFlags |= CONTEXT_FLOATING_POINT; if (KeI386XMMIPresent) { ContextFrame.ContextFlags |= CONTEXT_EXTENDED_REGISTERS; } }
// if it is BREAK_POINT exception, we subtract 1 from EIP and report // the updated EIP to user. This is because Cruiser requires EIP // points to the int 3 instruction (not the instruction following int 3). // In this case, BreakPoint exception is fatal. Otherwise we will step // on the int 3 over and over again, if user does not handle it // // if the BREAK_POINT occured in V86 mode, the debugger running in the // VDM will expect CS:EIP to point after the exception (the way the // processor left it. this is also true for protected mode dos // app debuggers. We will need a way to detect this.
switch (ExceptionRecord->ExceptionCode) { case STATUS_BREAKPOINT: ContextFrame.Eip--; break; }
// Select the method of handling the exception based on the previous mode. ASSERT (( !((PreviousMode == KernelMode) && (ContextFrame.EFlags & EFLAGS_V86_MASK)) ));
if (PreviousMode == KernelMode) {
// Previous mode was kernel. // // If the kernel debugger is active, then give the kernel debugger the // first chance to handle the exception. If the kernel debugger handles // the exception, then continue execution. Else attempt to dispatch the // exception to a frame based handler. If a frame based handler handles // the exception, then continue execution. // // If a frame based handler does not handle the exception, // give the kernel debugger a second chance, if it's present. // // If the exception is still unhandled, call KeBugCheck().
// This is the second chance to handle the exception. if ((KiDebugRoutine != NULL) && (((KiDebugRoutine) (TrapFrame, ExceptionFrame, ExceptionRecord, &ContextFrame, PreviousMode, TRUE)) != FALSE)) {
// Previous mode was user. // // If this is the first chance and the current process has a debugger // port, then send a message to the debugger port and wait for a reply. // If the debugger handles the exception, then continue execution. Else // transfer the exception information to the user stack, transition to // user mode, and attempt to dispatch the exception to a frame based // handler. If a frame based handler handles the exception, then continue // execution with the continue system service. Else execute the // NtRaiseException system service with FirstChance == FALSE, which // will call this routine a second time to process the exception. // // If this is the second chance and the current process has a debugger // port, then send a message to the debugger port and wait for a reply. // If the debugger handles the exception, then continue execution. Else // if the current process has a subsystem port, then send a message to // the subsystem port and wait for a reply. If the subsystem handles the // exception, then continue execution. Else terminate the thread.
if (FirstChance == TRUE) {
// This is the first chance to handle the exception. if ((KiDebugRoutine != NULL) && ((PsGetCurrentProcess()->DebugPort == NULL) || (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; }
// Transfer exception information to the user stack, transition // to user mode, and attempt to dispatch the exception to a frame based handler. repeat: try {
// If the SS segment is not 32 bit flat, there is no point // to dispatch exception to frame based exception handler. if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) || TrapFrame->EFlags & EFLAGS_V86_MASK ) { ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION; ExceptionRecord2.ExceptionFlags = 0; ExceptionRecord2.NumberParameters = 0; ExRaiseException(&ExceptionRecord2); }
// Compute length of context record and new aligned user stack pointer. Length = (sizeof(CONTEXT) + CONTEXT_ROUND) & ~CONTEXT_ROUND; UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - Length;
// Probe user stack area for writability and then transfer the // context record to the user stack. ProbeForWrite((PCHAR)UserStack1, Length, CONTEXT_ALIGN); RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));
// Compute length of exception record and new aligned stack address. Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS - ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) & (~3); UserStack2 = UserStack1 - Length;
// Probe user stack area for writeability and then transfer the // context record to the user stack area. // N.B. The probing length is Length+8 because there are two arguments //need to be pushed to user stack later. ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG)); RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length);
// Push address of exception record, context record to the // user stack. They are the two parameters required by _KiUserExceptionDispatch. *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1; *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;
// Set new stack pointer to the trap frame. KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA); KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2));
// Set the address of the exception routine that will call the // exception dispatcher and then return to the trap handler. // The trap handler will restore the exception and trap frame // context and continue execution in the routine that will call the exception dispatcher. TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher; return;
// If the exception is a stack overflow, then attempt // to raise the stack overflow exception. Otherwise, // the user's stack is not accessible, or is misaligned, and second chance processing is performed. if (ExceptionRecord1.ExceptionCode == STATUS_STACK_OVERFLOW) { ExceptionRecord1.ExceptionAddress = ExceptionRecord->ExceptionAddress; RtlCopyMemory((PVOID)ExceptionRecord, &ExceptionRecord1, sizeof(EXCEPTION_RECORD)); goto repeat; } } }
// This is the second chance to handle the exception. if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) { goto Handled2; } elseif (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) { goto Handled2; } else { ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode); KeBugCheckEx( KERNEL_MODE_EXCEPTION_NOT_HANDLED, ExceptionRecord->ExceptionCode, (ULONG)ExceptionRecord->ExceptionAddress, (ULONG)TrapFrame, 0); } }
// Move machine state from context frame to trap and exception frames and // then return to continue execution with the restored state. Handled1:
// Exception was handled by the debugger or the associated subsystem // and state was modified, if necessary, using the get state and set // state capabilities. Therefore the context frame does not need to // be transfered to the trap and exception frames. Handled2: return; }