Windows内核Dll注入
近期在云桌面中遇到许多情况都需要对应用层程序的函数进行HOOK,并需要对函数参数进行各种修改,以便能够在虚拟桌面里面支持一些特性(尤其是一些3D的场景)。对API的HOOK是比较简单的,微软提供了开源的代码就能够实现;因此这里最主要的就是需要对HOOK的进程进行注入,来实现HOOK函数的功能。
在早期开发中,就实现过DLL注入的功能,不过都是基于用户层的注入来实现的,例如有远线程注入,消息钩子注入,用户层OPE注入。本篇中分享的是基于内核的注入技术,其中内核注入主要有:
- HOOK函数注入。
- 远程线程注入(使用
ZwCreateThreadEx
)。 - OEP注入。
- APC注入。
下面的文章主要是针对HOOK函数的注入,这个函数是NtTestAlert
,接下来分析整个实现过程。
1. NtTestAlert
HOOK注入的第一步是需要找到一个函数,这个函数可以确保所有的进程启动的时候都会被调用,这个函数就是NtTestAlert
,如下:
NTSYSAPI
NTSTATUS
NTAPI
NtTestAlert(
);
该函数的作用是什么呢?我们知道,Windows在从内核切换到用户层的时候,就会判断线程的APC状态标记Thread->ApcState.UserApcPending
,如下:
cmp [ebx+_KTHREAD.___u12.ApcState.UserApcPending], 0
如果Thread->ApcState.UserApcPending
为TRUE,那么就会对用户层的APC进行处理,那么什么情况下Thread->ApcState.UserApcPending
才会被设置为TRUE呢?两个情况都满足:
- 调用
Alertable
的函数。 - 当前线程有用户态APC(一般放入线程APC链表中)。
Alertable
的函数有如下:
DWORD SleepEx(
DWORD dwMilliseconds,
BOOL bAlertable
);
DWORD WaitForSingleObjectEx(
HANDLE hHandle,
DWORD dwMilliseconds,
BOOL bAlertable
);
这也是很多地方说的,只有在当前线程处于Alertable
状态的时候,用户层APC才能被分发。
那么有没有一种办法,不用让线程调用Alertable
的函数也能实现APC的分发呢?答案是有的;这个函数就是NtTestAlert
,该函数的作用大致可以理解如下:
if ((AlertMode == UserMode) &&
(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) != TRUE))
{
Thread->ApcState.UserApcPending = TRUE;
}
主要就是设置Thread->ApcState.UserApcPending = TRUE
,让APC可以被调度分发。
在每个线程启动的时候,都会调用NtTestAlert
来分发用户层的APC例程,如下:
0:000> k
# Child-SP RetAddr Call Site
00 0000005b`aedef448 00007fff`81703f88 ntdll!NtTestAlert
01 0000005b`aedef450 00007fff`81703ea3 ntdll!_LdrpInitialize+0xac
02 0000005b`aedef4d0 00007fff`81703dce ntdll!LdrpInitializeInternal+0x6b
03 0000005b`aedef750 00000000`00000000 ntdll!LdrInitializeThunk+0xe
因此我们找到了一个进程(线程)启动一定会调用的函数了。注入的原理就是HOOK该函数,当NtTestAlert
被执行的时候,就会跳转到我们HOOK的函数,然后该函数中通过LoadLibrary
加载需要注入的Dll。
2. 技术分析
首先我们需要对进程的事件进行监控,记录进程的一些信息,例如:
- 判断该进程是