码迷,mamicode.com
首页 > 其他好文 > 详细

KeUserModeCallback函数

时间:2021-04-14 12:36:12      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:library   ica   就是   执行   off   not   inpu   长度   安装   

内核调用用户

正常的系统调用过程为Ring3->Ring0->Ring3,而KeUserModeCallback提供了一种Ring0->Ring3->Ring0的方式,即从内核去用户层执行代码。

KeUserModeCallback (
    IN ULONG ApiNumber,					//对应函数在KernelCallback表中的索引
    IN PVOID InputBuffer,				//ApiNumber不同(即调用的函数不同),此参数对应不同的结构
    IN ULONG InputLength,
    OUT PVOID *OutputBuffer,			        //执行后输出的结果
    IN PULONG OutputLength
    )

KeUserModeCallback是ntoskrnl.exe导出的,系统中很多回调机制都是通过KeUserModeCallback实现的。
其参数ApiNumber是需要执行的回调函数在KernelCallback回调函数表中的索引,这张回调函数表的地址存放在PEB->KernelCallback中的每一个函数都有不同的用处,在windbg中好看如下。

技术图片

其对应的回调函数表为:

技术图片

第二个参数InputBuffer会根据ApiNumber索引回调函数的不同而指向不同的结构体,第四个参数OutputBuffer指向的缓冲区中是执行完回调函数后返回的信息。

KeUserModeCallback的执行流程

一般是含有GDI系统服务的win32k.sys调用KeUserModeCallback,所以一般要HOOKKeUserModeCallback时都会通过IAThook win32k.sys的导入表中对KeUserModeCallback的调用。接着会调用nt!KiCallUserMode--->KiServiceExit--->sysexit进入应用层中,然后在应用层执行的第一个函数就是ntdll!KiUserCallbackDispatcher。
技术图片
为什么调用堆栈中没有显示调用了nt!KiServiceExit呢,因为其是通过nt!KiCallUserMode执行jmp执行调用的。

技术图片

应用层执行的第一个函数就是ntdll!KiUserCallbackDispatcher,此函数会根据从内核中传入的ApiNumber也就是回调函数在KernelCallback回调函数表中的索引调用对应的回调函数,执行完对应的回调函数后ntdll!KiUserCallbackDispatcher通过调用User32!XyCallbackReturn,此函数会在内部执行int 0x2e,通过中断门返回内核。

技术图片

接着回到内核中后执行的第一个函数就是nt!KiCallbackReturn,然后会继续返回到nt!KeUserModeCallback中。
技术图片

总结一般其调用过程就是这样

win32k.sys --->nt!KeUserModeCallback --->nt!KiCallUserMode--->KiServiceExit--->sysexit     //Ring0--->Ring3

ntdll!KiUserCallbackDispatcher--->对应KernelCallback表中的回调函数                        //Ring3

ntdll!KiUserCallbackDispatcher--->User32!XyCallbackReturn--->int 0x2e                  //Ring3--->Ring0

nt!KiCallbackReturn--->nt!KeUserModeCallback                                            //Ring0

KeUserModeCallback的应用

全局钩子

我们通常利用SetWindwosHook设置全局消息钩子来实现dll注入,其实其就是利用nt!KeUserModeCallback函数来完成函数的调用的。
当我们在一个应用 程序中调用SetWindowsHook安装全局消息钩子并设置hook回调函数后,如果我们这时候打开另一个程序,如果此程序是一个GUI程序,其在加载过程中会产生一些消息,这样系统就会尝试调用我们刚刚注册的全局消息回调函数,如果发现此回调函数对应的dll还没有加载其就会尝试加载此dll,从而实现dll的注入。

技术图片

这其实就是内核通过调用nt!KeUserModeCallback 函数,然后进入到用户层后调用KernelCallback回调函数表中的位于USER32.dll模块中的USER32!_ClientLoadLibrary函数,此函数会进一步调用kernel32!LoadLibraryEx加载需要加载的dll文件。
对应的nt!KeUserModeCallback 的InputBuffer参数结构为,

typedef struct DLLHOOK
{
DWORD      dwBufferSize;        	//结构长度
DWORD      dwAdditionalData;    
DWORD      dwFixupsCount;        
LPVOID     pbFree                
DWORD      offCbkPtrs;           
DWORD      bFixed;                
UNICODE_STRING lpDLLPath;         	//加载的Dll名字
union
{
DWORD      lpfnNotify                   // 调用函数相对于模块的RVA
UNICODE_STRING lpInitFunctionName;      // 调用函数名
}
DWORD      offCbk[2];              
};

所以只有GDI程序,即加载user32.dll的程序才能使用全局消息钩子注入dll了。我们可以通过IAThook win32k.sys的KeUserModeCallback函数并判断dll的名字来达到防止全局消息钩子注入。

WH_JOURNALRECORD消息记录钩子

KeUserModeCallback函数

标签:library   ica   就是   执行   off   not   inpu   长度   安装   

原文地址:https://www.cnblogs.com/revercc/p/14656013.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!