声明:原创文章,转载请注明出处(http://5e365.lofter.com/post/1d1aa3a8_660c2b6)
内核向用户态进程注入代码原理
通过KeInitializeApc、KeInsertQueueApc(未文档化函数)插入内核APC回调函数、ExQueueWorkItem插入WorkItem回调函数,附加用户进程,将注入代码复制到用户进程内存空间,最后通过一个用户APC指向注入代码,并异步得到执行。
详细流程如下:
1) PsSetLoadImageNotifyRoutine注册回调函数
2) 任意进程加载模块时,上步注册的回调函数得到执行
调用FsRtlIsNameInExpression,判断加载的是kernel32.dll,是继续执行,否则返回。
(TheFsRtlIsNameInExpression routine determines whether a Unicode string matches thespecified pattern.)
FsRtlAllocatePool申请0x34字节内核非分页内存,用于后续APC对象及回调参数。将kernel32.dll基址存入0x34字节的最后一个DWORD中(用于APC回调函数入参)
调用KeGetCurrentThread得到当前线程对象(用于APC初始化的入参)
调用KeInitializeApc、KeInsertQueueApc(未文档化函数)插入内核APC回调函数(执行后续流程)
3) 上步插入的内核APC函数异步执行
调用ExFreePool释放APC结构体内存
FsRtlAllocatePool申请0x40字节非分页内核内存(用于后续EVENT对象、WORKITEM对象及其参数)
调用KeGetCurrentThread、PsGetCurrentProcess、PsGetCurrentThreadProcessId得到进程对象,线程对象,PID(存入上面申请的内存中,作为参数传给ExQueueWorkItem插入的回调函数)
调用KeInitializeEvent,用于同步,等待WorkItem执行完毕
调用ExQueueWorkItem插入后续流程的回调函数
调用KeWaitForMutexObject等待上面的WorkItem回调函数执行完毕
调用ExFreePool释放上面申请的0x40字节内核内存
4) 上步ExQueueWorkItem插入的回调函数执行
调用PsLookupProcessByProcessId得到EPROCESS
调用PsGetProcessImageFileName通过EPROCESS得到进程文件名
计算进程文件名的hash值与想要注入代码的进程列表做对比(包含在内继续执行,否则返回)
调用KeStackAttachProcess将当前线程附加到目标进程用户态地址空间
配置入参数ClientId,ObjectAttributes后,调用ZwOpenProcess打开目标进程
调用ZwAllocateVirtualMemory申请目标进程用户态内存
mov指令将内核中的注入代码拷贝到上步申请的用户态进程空间
调用FsRtlAllocatePool申请0x30字节内核非分页内存用于APC对象
调用KeInitializeApc,KeInsertQueueApc插入用户APC(注入代码在用户态进程执行)
调用ZwClose,KeUnstackDetachProcess,ObDereferenceObject释放资源
末尾调用KeSetEvent设置事件,用于同步,通知WorkItem执行完毕
5) 上步插入的用户APC异步得到执行(注入的代码在目标进程执行)
至此,完成了内核态向目标用户进程注入代码并得到执行