从内核调用应用层方式
2023-11-02
32
0
从内核调用应用层有三种方式:
1.APC - ntdll!KiUserApcDispatcher
2.异常,其实和APC一样,只是回到应用层的入口函数不一样 ntdll!KiUserExceptionDispatcher
3.内核回调
内核回调
APC、异常相对于内核回调,其回调到应用层的入口比较单一,而内核回调是比较丰富的,即可以根据需要选择不同的函数。
消息机制从内核到应用层,使用的方法是内核回调。(Kernel32.dll中一堆函数)
DispatchMeesage在调用内核的函数之后,会回调到Windows消息循环函数处理相应的消息。所以这里汲到到内核回调。
内核对应的DispatchMessage的内核函数为NtUserDispatchMessage
NtUserDispatchMessage
{
IntDispatchMessage(msg)
{
PWND Window = UserGetWindowObject(pMsg->hwnd);
...
retval = co_IntCallWindowProc( Window->lpfnWndProc,
!Window->Unicode,
pMsg->hwnd,
pMsg->message,
pMsg->wParam,
pMsg->lParam,
-1);
{
Arguments->Proc = Proc;
Arguments->IsAnsiProc = IsAnsiProc;
Arguments->Wnd = Wnd;
Arguments->Msg = Message;
Arguments->wParam = wParam;
Arguments->lParam = lParam;
Arguments->lParamBufferSize = lParamBufferSize;
ResultPointer = NULL;
ResultLength = ArgumentLength;
IntSetTebWndCallback (&Wnd, &pWnd, &pActCtx);
UserLeaveCo();
Status = KeUserModeCallback(USER32_CALLBACK_WINDOWPROC,
Arguments,
ArgumentLength,
&ResultPointer,
&ResultLength);
}
}
}
KeUserModeCallback函数的第一个参数是回调到入口应用层函数User32.dll的函数索引。这个索引很多,所以很丰富。
回调函数地址表位于User32.dll中。
#define USER32_CALLBACK_WINDOWPROC (0)
#define USER32_CALLBACK_SENDASYNCPROC (1)
#define USER32_CALLBACK_LOADSYSMENUTEMPLATE (2)
#define USER32_CALLBACK_LOADDEFAULTCURSORS (3)
#define USER32_CALLBACK_HOOKPROC (4)
#define USER32_CALLBACK_EVENTPROC (5)
#define USER32_CALLBACK_LOADMENU (6)
#define USER32_CALLBACK_CLIENTTHREADSTARTUP (7)
#define USER32_CALLBACK_CLIENTLOADLIBRARY (8)
#define USER32_CALLBACK_GETCHARSETINFO (9)
#define USER32_CALLBACK_COPYIMAGE (10)
#define USER32_CALLBACK_SETWNDICONS (11)
#define USER32_CALLBACK_DELIVERUSERAPC (12)
#define USER32_CALLBACK_DDEPOST (13)
#define USER32_CALLBACK_DDEGET (14)
#define USER32_CALLBACK_SETOBM (15)
#define USER32_CALLBACK_LPK (16)
#define USER32_CALLBACK_UMPD (17)
#define USER32_CALLBACK_IMMPROCESSKEY (18)
#define USER32_CALLBACK_IMMLOADLAYOUT (19)
#define USER32_CALLBACK_MAXIMUM USER32_CALLBACK_IMMLOADLAYOUT
所以从内核到应用层的关键函数是KeUserModeCallback
NTSTATUS NTAPI KeUserModeCallback ( IN ULONG RoutineIndex,
IN PVOID Argument,
IN ULONG ArgumentLength,
OUT PVOID * Result,
OUT PULONG ResultLength
)
{
ULONG_PTR OldStack;
PUCHAR UserArguments;
PUCALLOUT_FRAME CalloutFrame;
PULONG_PTR UserStackPointer;
NTSTATUS CallbackStatus;
#ifdef _M_IX86
PEXCEPTION_REGISTRATION_RECORD ExceptionList;
#endif // _M_IX86
PTEB Teb;
ULONG GdiBatchCount = 0;
ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
ASSERT(KeGetPreviousMode() == UserMode);
/* Get the current user-mode stack */
UserStackPointer = KiGetUserModeStackAddress();
OldStack = *UserStackPointer;
/* Enter a SEH Block */
_SEH2_TRY
{
/* Calculate and align the stack. This is unaligned by 8 bytes, since the following
UCALLOUT_FRAME compensates for that and on entry we already have a full stack
frame with home space for the next call, i.e. we are already inside the function
body and the stack needs to be 16 byte aligned. */
UserArguments = (PUCHAR)ALIGN_DOWN_POINTER_BY(OldStack - ArgumentLength, 16) - 8;
/* The callout frame is below the arguments */
CalloutFrame = ((PUCALLOUT_FRAME)UserArguments) - 1;
/* Make sure it's all writable */
ProbeForWrite(CalloutFrame,
sizeof(PUCALLOUT_FRAME) + ArgumentLength,
sizeof(PVOID));
/* Copy the buffer into the stack */
RtlCopyMemory(UserArguments, Argument, ArgumentLength);
/* Write the arguments */
KiSetupUserCalloutFrame(CalloutFrame,
KeGetCurrentThread()->TrapFrame,
RoutineIndex,
UserArguments,
ArgumentLength);
/* Save the exception list */
Teb = KeGetCurrentThread()->Teb;
#ifdef _M_IX86
ExceptionList = Teb->NtTib.ExceptionList;
#endif // _M_IX86
/* Jump to user mode */
*UserStackPointer = (ULONG_PTR)CalloutFrame;
CallbackStatus = KiCallUserMode(Result, ResultLength);
if (CallbackStatus != STATUS_CALLBACK_POP_STACK)
{
#ifdef _M_IX86
/* Only restore the exception list if we didn't crash in ring 3 */
Teb->NtTib.ExceptionList = ExceptionList;
#endif // _M_IX86
}
else
{
/* Otherwise, pop the stack */
OldStack = *UserStackPointer;
}
/* Read the GDI Batch count */
GdiBatchCount = Teb->GdiBatchCount;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Get the SEH exception */
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
/* Check if we have GDI Batch operations */
if (GdiBatchCount)
{
*UserStackPointer -= 256;
KeGdiFlushUserBatch();
}
/* Restore stack and return */
*UserStackPointer = OldStack;
#ifdef _M_AMD64 // could probably move the update to TrapFrame->Rsp from the C handler to the asm code
__writegsqword(FIELD_OFFSET(KIPCR, UserRsp), OldStack);
#endif
return CallbackStatus;
}