本文介绍了一种思路,利用安全软件驱动和WINDOWS本身处理全局钩子安装过程的不同,以绕过安全软件对全局钩子安装的拦截。这种方法并不保证能通用于任何安全软件。

安全软件,例如HIPS,AV等,通常会安装 NtUserSetWindowsHookEx / NtUserSetWinEventHook的钩子

拦截全局钩子注入,防止键盘、消息拦截或者DLL注入。

在这两个函数的参数中,都存在一个pstrlib参数,指向了要注册全局钩子的DLL路径名,这个路径是DOS路径名,通常安全软件会分配BUFFER,将pstrlib这个UNICODE_STRING中的字符串读取出来,再加上NT路径头 (\??\),然后进行相关处理后传递给RING3的处理程序,比较常见的例如使用RtlCopyUnicodeString , 或者直接RtlCopyMemory(LocalName , pstrlib->Buffer , pstrlib->Length)

而WINDOWS函数自身,例如NtUserSetWindowsHookEx, NtUserSetWinEvnetHook,他们是怎么处理这个参数的呢?

实际上,这些函数最终会调用内部函数GetHmodTableIndex将DLL名加入一个ATOM Table中,并返回一个INDEX,记为为ihmod,等到CallNextHook2,或者分发winevent hook 时,通过 ihmod再取回这个index,得到DLL名,并通过User mode callback调用LoadLibrary加载这个DLL

GetHmodTableIndex则会调用一个UserAddAtom函数来将DLL名加入UserAtomTableHandle这个atom中。UserAddAtom这个函数接收的DLL名参数,是一个LPCWSTR类型的字符串,来自pstrlib->Buffer.UserAddAtom并不关心pstrlib->Length的长度。等到向RING3程序注入这个DLL时,调用xxxLoadHmodIndex中才在UserGetAtomName获取这个字符串后,通过调用RtlInitUnicodeString重新计算这个字符串的长度。

那么,很简单的方法就可以绕过安全软件的全局钩子拦截了:

(1).length 传入0,直接绕过检测

(2).buffer为c:\windows\system32\msctf.dllx

length传递为该字符串字节长度-sizeof(wchar)

也就是让安全软件得到错误的系统DLL名,实际注册的是其他的文件