480 lines
16 KiB
NASM
480 lines
16 KiB
NASM
title "User Mode Dispatch Code"
|
|
;++
|
|
;
|
|
; Copyright (c) 2000 Microsoft Corporation
|
|
;
|
|
; Module Name:
|
|
;
|
|
; trampoln.asm
|
|
;
|
|
; Abstract:
|
|
;
|
|
; This module implements the trampoline code necessary to dispatch user
|
|
; mode APCs and exceptions.
|
|
;
|
|
; Author:
|
|
;
|
|
; David N. Cutler (davec) 4-Jul-2000
|
|
;
|
|
; Environment:
|
|
;
|
|
; User mode.
|
|
;
|
|
;--
|
|
|
|
include ksamd64.inc
|
|
|
|
extern RtlDispatchException:proc
|
|
extern RtlRaiseException:proc
|
|
extern RtlRaiseStatus:proc
|
|
extern RtlRestoreContext:proc
|
|
extern Wow64PrepareForException:qword
|
|
extern ZwCallbackReturn:proc
|
|
extern ZwContinue:proc
|
|
extern ZwRaiseException:proc
|
|
extern ZwTestAlert:proc
|
|
|
|
subttl "User APC Exception Handler"
|
|
;++
|
|
;
|
|
; EXCEPTION_DISPOSITION
|
|
; KiUserApcHandler (
|
|
; IN PEXCEPTION_RECORD ExceptionRecord,
|
|
; IN UINT_PTR EstablisherFrame,
|
|
; IN OUT PCONTEXT ContextRecord,
|
|
; IN OUT PDISPATCHER_CONTEXT DispatcherContext
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function is called when an exception occurs in an APC routine or one
|
|
; of its dynamic descendents, or when an unwind through the APC dispatcher
|
|
; is in progress. If an unwind is in progress, then test alert is called to
|
|
; ensure that all currently queued APCs are executed.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; ExceptionRecord (rcx) - Supplies a pointer to an exception record.
|
|
;
|
|
; EstablisherFrame (rdx) - Supplies the frame pointer of the establisher
|
|
; of this exception handler.
|
|
;
|
|
; ContextRecord (r8) - Supplies a pointer to a context record.
|
|
;
|
|
; DispatcherContext (r9) - Supplies a pointer to the dispatcher context
|
|
; record.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; Continue search is returned as the function value.
|
|
;
|
|
;--
|
|
|
|
UaFrame struct
|
|
Fill dq ? ; fill to 8 mod 16
|
|
UaFrame ends
|
|
|
|
NESTED_ENTRY KiUserApcHandler, _TEXT$00
|
|
|
|
alloc_stack (sizeof UaFrame) ; allocate stack frame
|
|
|
|
END_PROLOGUE
|
|
|
|
test dword ptr ErExceptionFlags[rcx], EXCEPTION_UNWIND ; test if unwinding
|
|
jz short @f ; if z, unwind not in progress
|
|
call ZwTestAlert ; test for alert pending
|
|
@@: mov rax, ExceptionContinueSearch ; set continue search disposition
|
|
sub rsp, sizeof UaFrame ; deallocate stack frame
|
|
ret ; return
|
|
|
|
NESTED_END KiUserApcHandler, _TEXT$00
|
|
|
|
subttl "User APC Dispatcher"
|
|
;++
|
|
;
|
|
; The following code is never executed. Its purpose is to support exception
|
|
; dispatching and unwinding through the call to the APC dispatcher.
|
|
;
|
|
;--
|
|
|
|
altentry KiUserApcDispatcher
|
|
|
|
NESTED_ENTRY KiUserApcDispatch, _TEXT$00, KiUserApcHandler
|
|
|
|
.pushframe ; push machine frame
|
|
.allocstack CONTEXT_FRAME_LENGTH ; allocate stack frame
|
|
.savereg rbx, CxRbx ; save nonvolatile integer registers
|
|
.savereg rbp, CxRbp ;
|
|
.savereg rsi, CxRsi ;
|
|
.savereg rdi, CxRdi ;
|
|
.savereg r12, CxR12 ;
|
|
.savereg r13, CxR13 ;
|
|
.savereg r14, CxR14 ;
|
|
.savereg r15, CxR15 ;
|
|
.savexmm128 xmm6, CxXmm6 ; save nonvolatile floating register
|
|
.savexmm128 xmm7, CxXmm7 ;
|
|
.savexmm128 xmm8, CxXmm8 ;
|
|
.savexmm128 xmm9, CxXmm9 ;
|
|
.savexmm128 xmm10, CxXmm10 ;
|
|
.savexmm128 xmm11, CxXmm11 ;
|
|
.savexmm128 xmm12, CxXmm12 ;
|
|
.savexmm128 xmm13, CxXmm13 ;
|
|
.savexmm128 xmm14, CxXmm14 ;
|
|
.savexmm128 xmm15, CxXmm15 ;
|
|
|
|
END_PROLOGUE
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KiUserApcDispatcher (
|
|
; IN PVOID NormalContext,
|
|
; IN PVOID SystemArgument1,
|
|
; IN PVOID SystemArgument2,
|
|
; IN PKNORMAL_ROUTINE NormalRoutine
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered on return from kernel mode to deliver an APC
|
|
; in user mode. The context frame for this routine was built when the
|
|
; APC interrupt was processed and contains the entire machine state of
|
|
; the current thread. The specified APC routine is called and then the
|
|
; machine state is restored and execution is continued.
|
|
;
|
|
; N.B. This function is not called in the typical way. Instead of a normal
|
|
; subroutine call to the nested entry point above, the alternate entry
|
|
; point below is stored into the RIP address of the trap frame. Thus
|
|
; when the kernel returns from the trap, the following code is executed
|
|
; directly.
|
|
;
|
|
; N.B. The home addresses for called routine register parameters are
|
|
; allocated in the context record.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; NormalContext (P1Home) - Supplies the normal context parameter that was
|
|
; specified when the APC was initialized.
|
|
;
|
|
; SystemArgument1 (P2Home) - Supplies the first argument that was provided by
|
|
; the system when the APC was queued.
|
|
;
|
|
; SystemArgument2 (P3Home) - Supplies the second argument that was provided by
|
|
; the system when the APC was queued.
|
|
;
|
|
; NormalRoutine (P4Home) - Supplies the address of the function that is to
|
|
; be called.
|
|
;
|
|
; N.B. Register RSP supplies a pointer to a context record.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
ALTERNATE_ENTRY KiUserApcDispatcher
|
|
|
|
mov rcx, CxP1Home[rsp] ; get APC parameters
|
|
mov rdx, CxP2Home[rsp] ;
|
|
mov r8, CxP3Home[rsp] ;
|
|
call qword ptr CxP4Home[rsp] ; call APC routine
|
|
|
|
;
|
|
; N.B. The system service to continue execution must be called since test
|
|
; alert must also be executed.
|
|
;
|
|
|
|
mov rcx, rsp ; set address of context record
|
|
mov dl, TRUE ; set test alert argument
|
|
call ZwContinue ; continue execution
|
|
|
|
;
|
|
; Unsuccessful completion after attempting to continue execution. Use the
|
|
; return status as the exception code and attempt to raise another exception.
|
|
;
|
|
|
|
mov esi, eax ; save return status
|
|
@@: mov ecx, esi ; set status value
|
|
call RtlRaiseStatus ; raise status
|
|
jmp short @b ; loop forever
|
|
|
|
NESTED_END KiUserApcDispatch, _TEXT$00
|
|
|
|
subttl "User Callback Dispatcher"
|
|
;++
|
|
;
|
|
; The following code is never executed. Its purpose is to support exception
|
|
; dispatching and unwinding through the call to the exception dispatcher.
|
|
;
|
|
;--
|
|
|
|
altentry KiUserCallbackDispatcher
|
|
|
|
NESTED_ENTRY KiUserCallbackDispatch, _TEXT$00
|
|
|
|
.pushframe ; push machine frame
|
|
.allocstack (CalloutFrameLength - MachineFrameLength) ; allocate stack frame
|
|
|
|
END_PROLOGUE
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KiUserCallbackDispatcher (
|
|
; IN ULONG ApiNumber,
|
|
; IN PVOID InputBuffer,
|
|
; IN ULONG INputLength
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered on a callout from kernel mode to execute a user
|
|
; mode callback function. All arguments for this function have been placed
|
|
; on the stack.
|
|
;
|
|
; N.B. This function is not called in the typical way. Instead of a normal
|
|
; subroutine call to the nested entry point above, the alternate entry
|
|
; point below is stored into the RIP address of the trap frame. Thus
|
|
; when the kernel returns from the trap, the following code is executed
|
|
; directly.
|
|
;
|
|
; N.B. The home addresses for called routine register parameters are
|
|
; allocated in the user callout frame.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; N.B. Register RSP supplies a pointer to a user callout record.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; This function returns to kernel mode.
|
|
;
|
|
;--
|
|
|
|
ALTERNATE_ENTRY KiUserCallbackDispatcher
|
|
|
|
mov rcx, CkBuffer[rsp] ; set input buffer address
|
|
mov edx, CkLength[rsp] ; set input buffer length
|
|
mov r8d, CkApiNumber[rsp] ; get API number
|
|
mov rax, gs:[TePeb] ; get process environment block address
|
|
mov r9, PeKernelCallbackTable[rax] ; get callback table address
|
|
call qword ptr [r9][r8 * 8] ; call specified function
|
|
|
|
;
|
|
; If a return from the callback function occurs, then the output buffer
|
|
; address and length are returned as NULL.
|
|
;
|
|
|
|
xor ecx, ecx ; clear output buffer address
|
|
xor edx, edx ; clear output buffer length
|
|
mov r8d, eax ; set completion status
|
|
call ZwCallbackReturn ; return to kernel mode
|
|
|
|
;
|
|
; Unsuccessful completion after attempting to return to kernel mode. Use the
|
|
; return status as the exception code and attempt to raise another exception.
|
|
;
|
|
|
|
mov esi, eax ; save status value
|
|
@@: mov ecx, esi ; set status value
|
|
call RtlRaiseStatus ; raise exception
|
|
jmp short @b ; loop forever
|
|
|
|
NESTED_END KiUserCallbackDispatch, _TEXT$00
|
|
|
|
subttl "User Exception Dispatcher"
|
|
;++
|
|
;
|
|
; The following code is never executed. Its purpose is to support exception
|
|
; dispatching and unwinding through the call to the exception dispatcher.
|
|
;
|
|
;--
|
|
|
|
ExceptionFramelength equ (ExceptionRecordLength + CONTEXT_FRAME_LENGTH)
|
|
|
|
altentry KiUserExceptionDispatcher
|
|
|
|
NESTED_ENTRY KiUserExceptionDispatch, _TEXT$00
|
|
|
|
.pushframe ;
|
|
.allocstack (ExceptionRecordLength + CONTEXT_FRAME_LENGTH) ; allocate stack frame
|
|
.savereg rbx, CxRbx ; save nonvolatile integer registers
|
|
.savereg rbp, CxRbp ;
|
|
.savereg rsi, CxRsi ;
|
|
.savereg rdi, CxRdi ;
|
|
.savereg r12, CxR12 ;
|
|
.savereg r13, CxR13 ;
|
|
.savereg r14, CxR14 ;
|
|
.savereg r15, CxR15 ;
|
|
.savexmm128 xmm6, CxXmm6 ; save nonvolatile floating register
|
|
.savexmm128 xmm7, CxXmm7 ;
|
|
.savexmm128 xmm8, CxXmm8 ;
|
|
.savexmm128 xmm9, CxXmm9 ;
|
|
.savexmm128 xmm10, CxXmm10 ;
|
|
.savexmm128 xmm11, CxXmm11 ;
|
|
.savexmm128 xmm12, CxXmm12 ;
|
|
.savexmm128 xmm13, CxXmm13 ;
|
|
.savexmm128 xmm14, CxXmm14 ;
|
|
.savexmm128 xmm15, CxXmm15 ;
|
|
|
|
END_PROLOGUE
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KiUserExceptionDispatcher (
|
|
; IN PEXCEPTION_RECORD ExceptionRecord,
|
|
; IN PCONTEXT ContextRecord
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered on return from kernel mode to dispatch a user
|
|
; mode exception. If a frame based handler handles the exception, then
|
|
; the execution is continued. Else last chance processing is performed.
|
|
;
|
|
; N.B. This function is not called in the typical way. Instead of a normal
|
|
; subroutine call to the nested entry point above, the alternate entry
|
|
; point below is stored into the RIP address of the trap frame. Thus
|
|
; when the kernel returns from the trap, the following code is executed
|
|
; directly.
|
|
;
|
|
; N.B. The home addresses for called routine register parameters are
|
|
; allocated in the context record.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; ExceptionRecord (rsi) - Supplies a pointer to an exception record.
|
|
;
|
|
; ContextRecord (rdi) - Supplies a pointer to a context record.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
ALTERNATE_ENTRY KiUserExceptionDispatcher
|
|
|
|
;
|
|
; If this is a wow64 process, then give wow64 a chance to clean up before
|
|
; dispatching the exception.
|
|
;
|
|
|
|
mov rax, Wow64PrepareForException ; get wow64 routine address
|
|
test rax, rax ; test if routine specified
|
|
jz short Ue05 ; if z, routine not specified
|
|
mov rcx, rsi ; set exception record address
|
|
mov rdx, rdi ; set context record address
|
|
call rax ; call wow64 clean up routine
|
|
Ue05: mov rcx, rsi ; set exception record address
|
|
mov rdx, rdi ; set context record address
|
|
call RtlDispatchException ; dispatch the exception
|
|
|
|
;
|
|
; If the return status is TRUE, then the exception was handled and execution
|
|
; should be continued with the continue service in case the context was
|
|
; changed. If the return status is FALSE, then the exception was not handled
|
|
; and raise exception is called to perform last chance exception processing.
|
|
;
|
|
|
|
test al, al ; test if the exception was handled
|
|
jz short Ue10 ; if z, exception was not handled
|
|
|
|
;
|
|
; Restore context and continue execution.
|
|
;
|
|
|
|
mov rcx, rdi ; set context record address
|
|
xor edx, edx ; set set exception record address
|
|
call RtlRestoreContext ; restore context
|
|
jmp short Ue20 ; finish in common code
|
|
|
|
;
|
|
; Last chance processing.
|
|
;
|
|
|
|
Ue10: mov rcx, rsi ; set exception record address
|
|
mov rdx, rdi ; set context record address
|
|
xor r8b, r8b ; set first change argument falue
|
|
call ZwRaiseException ; raise exception
|
|
|
|
;
|
|
; Common code for nonsuccessful completion of the continue or raise exception
|
|
; services. Use the return status as the exception code and attempt to raise
|
|
; another exception.
|
|
;
|
|
|
|
Ue20: mov esi, eax ; save status value
|
|
@@: mov ecx, esi ; set status value
|
|
call RtlRaiseStatus ; raise exception
|
|
jmp short @b ; loop forever
|
|
|
|
NESTED_END KiUserExceptionDispatch, _TEXT$00
|
|
|
|
subttl "Raise User Exception Dispatcher"
|
|
;++
|
|
;
|
|
; NTSTATUS
|
|
; KiRaiseUserExceptionDispatcher (
|
|
; VOID
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This routine is entered on return from kernel mode to raise a user
|
|
; mode exception.
|
|
;
|
|
; N.B. This function is not called in the normal manner. An exception is
|
|
; raised from within the system by placing the address of this
|
|
; function in the RIP of the trap frame. This replaces the normal
|
|
; return to the system service stub.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; None.
|
|
;
|
|
; Implicit Arguments:
|
|
;
|
|
; Status (eax) - Supplies the system service status.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; The exception code that was raised.
|
|
;
|
|
;--
|
|
|
|
RuFrame struct ;
|
|
P1Home dq ? ; parameter home address
|
|
Xrecord db ExceptionRecordLength dup (?) ; exception record
|
|
Status dd ? ; saved exception code
|
|
Fill1 dd ? ; fill to 0 mod 8
|
|
Fill2 dq ? ; fill to 8 mod 16
|
|
RuFrame ends
|
|
|
|
NESTED_ENTRY KiRaiseUserExceptionDispatcher, _TEXT$00
|
|
|
|
alloc_stack (sizeof RuFrame) ; allocate stack frame
|
|
|
|
END_PROLOGUE
|
|
|
|
mov RuFrame.Status[rsp], eax ; save the service status
|
|
mov eax, gs:[TeExceptionCode] ; get exception code
|
|
lea rcx, RuFrame.Xrecord[rsp] ; get exception record address
|
|
mov ErExceptionCode[rcx], eax ; set exception code
|
|
xor eax, eax ; generate zero value
|
|
mov ErExceptionFlags[rcx], eax ; zero exception flags
|
|
mov ErExceptionRecord[rcx], rax ; zero exception record
|
|
mov rdx, sizeof RuFrame[rsp] ; get the real return address
|
|
mov ErExceptionAddress[rcx], rdx ; set exception address
|
|
mov ErNumberParameters[rcx], eax ; zero number of parameters
|
|
call RtlRaiseException ; raise exception
|
|
mov eax, RuFrame.Status[rsp] ; get exception code to return
|
|
add rsp, sizeof RuFrame ; dealloate stack frame
|
|
ret ; return
|
|
|
|
NESTED_END KiRaiseUserExceptionDispatcher, _TEXT$00
|
|
|
|
end
|