Windows消息队列
+ -

从内核调用应用层方式

2023-11-02 1 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;
}

0 篇笔记 写笔记

Windows内核回调函数
CmRegisterCallbackCmRegisterCallbackEx 注册表回调ExRegisterCallbackObRegisterCallbacksPsSetCreateProcessNotifyRoutinePsSetCreateProcessNotifyRoutineEx......
从内核调用应用层方式
从内核调用应用层有三种方式:1.APC - ntdll!KiUserApcDispatcher2.异常,其实和APC一样,只是回到应用层的入口函数不一样 ntdll!KiUserExceptionDispatcher3.内核回调内核回调APC、异常相对于内核回调,其回调到应用层的入口比较单一,而内......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!