2024年4月30日发(作者:)

内核注入,技术古老但很实用。现在部分RK趋向无进程,玩的是SYS+DLL,有的无文件,全部存在于内

存中。可能有部分人会说:“都进内核了.什么不能干?”。是啊,要是内核中可以做包括R3上所有能做

的事,软件开发商们也没必要做应用程序了。有时,我们确实需要R3程序去干驱动做起来很困难或者没

必要驱动中去做的事,进程 / DLL是不错的选择,但进程目标太大,所以更多的同学趋向于注DLL。

若要开发安全软件、小型工具,可借鉴其思路,Anti Rootkits时,在某些极端情况下,可使用同样的

技术发现、清除RK,保证用户电脑的正常使用。在此,我将探讨几种内核注入DLL的思路及实现原理。

(1) APC技术

给一个Alertbale的用户态线程插APC,让其执行其中的ShellCode,来执行我们的代码。这个方法简单

易行,但是不够稳定,兼容性不好。测试中发现经常出现等插崩溃的情况,而且有杀软在的情

况下,插入有时会被拦截,起不到应有的效果。(可参考我以前逆过的一个驱动:逆向--编译通过--

源码)

(2) 内核Patch [url=file://KnownDLLs/][/url] CreateThread

[url=file://KnownDLLs/]KnownDLLs[/url]是系统加载时对象管理器加载最新磁盘DLL到内存的,当其

他进程想调用某个DLL时,就不用重复从磁盘加载了,而会从这里映射一份到自己的进程空间中去。这样

给我们做全局Patch提供了一个很好的机会:

ZwOpenSection打开 [url=file://KnownDlls/][/url],调用ZwMapVi

ewOfSection映射一份到自己进程空间,然后寻找在内存中代码节的空隙,选择这里作为我们f

ake函数的存储Buffer。修改CreateThread函数的开头5字节跳转到这个间隙,当系统任何一个线程创建时,

会走到CreateThread函数,然后执行空隙中的ShellCode,其负责调用LoadLibrary加载我们的DLL。DLL

一经加载,会发IOCTL通知本驱动,让驱动卸载HOOK。这样就完成了内核注DLL的过程。测试时发现

进程调用CreateThread函数很频繁,所以触发也会很快,基本1秒不到就能将DLL加载进去,

而我们的HOOK也卸掉了。所以稳定性提高不少。示意图如下:

(3) 内核 HOOK ZwMapViewOfSection

有部分模块加载时会调用ZwMapViewOfSection,比如进程创建时映射N份DLL到自己的虚拟空间中

去.我们替换SSDT中的这个函数,过滤出是加载的情况,从参数中取得其基址,Inline Hook其E

AT中的CreateThread函数,跳转到在这个进程虚拟地址空间中申请的Buffer,在其中完成DLL的加载过程.

关键API:

ZwAllocateVirtualMemory ---- 在此进程空间中分配内存,存放Shellcode

ZwProtectVirtualMemory ---- 使当前内存块具有可读可写属性

IoAllocateMdl ---- 创建MDL

关键Code如下:

同方法2相比,原理类似。但修改时机不同,效果差不多,只是注入DLL的时间会慢一些。至于She

llcode的编写,就大同小异了.萝卜白菜各有所爱,主要看个人发挥。要是闲写shellcode麻烦,请到看雪学院

去查资料,模板很多,在这里就不YY了。

(4) 内核 HOOK

NtCreateThread

线程(给空水壶注水 – 凉水),在这个暂停的线程里面折腾了一阵后完事了也厌倦了,于是系统跳了出来,

回到进程空间中,调用去通知,对它说:“这里有一个新进程出生了,你在你的表

里标记一下”。然后就开始加载DLL啦,把系统KnownDLLs中的自己需要的DLL都Map一份到这个大水

壶中。接着KiThreadStartup加热水壶中的凉水,于是水就开始沸腾了,此时主线程开始工作。。。

拦截NtCreateThread,取得当前线程上下文,保存它要返回的地址(会回到空水壶中去),劫持为我们

自己分配的地址,在其中填充ShellCode来加载目的DLL。至于选择Buffer,思路很多。这里可简单的Attach

到当前进程,在充足的虚拟2GB进程地址空间中分配属于你自己的一块小内存,够放ShellCode足矣。示

意图如下:

(5) 内核感染常用模块,让感染模块帮我们Load DLL

这个方法就有点绕远了,开始了最本质最原始的感染,可增加新节,可插空隙,总之,让别人的模块

Load进内存时顺路的帮我们加载下DLL,DLL一旦加载就可以恢复感染,清除痕迹。至于感染代码,网

上一堆。只要不是驱动感染驱动(多了个校验和),其他性质都一样,看自己发挥啦。

(6) 拦截NtCreateUserProcess、NtCreateSymbolicLinkObject

前者在Vista下才有. 拦截后通过PsLookupProcessThreadByCid得到ETHREAD / EPROCESS,判断是否

是引起的,若是则在此进程空间内分配一块内存,调用NtGetContextThread得到当前的线程

上下文,调用ZwWriteVirtualMemory填充Shellcode区域,取得LdrUnloadDll、LdrGetDllHandle等函数

地址,通过他们加载DLL。然后调用NtSetContextThread恢复原始的Context。关于这种方法,可参考

DriverDevelop上某人发的BIN。

[”内核实现DLL注入.可以完美绕过KAV瑞星等杀毒软件”]

(7) 内核拦截NtResumeThread

(8)NtUserSetWindowsHookEx 注入

顺便提下R3上DLL的注入:

RemoteThread (or NtCreateThreadEx (Used in Vista))

eadContext (change the EIP)

eAPCThread

ateUserThread

5. SetWindowHookEx

小结:

纵观进程启动的全过程,可patch的地方很多,只要保证进程、线程上下文不被破坏,注入的手法可多

种多样。只要保证我们的DLL注入时间足够短,稳定性足够高即可。当我们迫不得已要从内核注入DLL

到用户进程去时,系统已经中毒很深,此时运用类似上面提到的技术来加载DLL,让DLL做我们驱动无法

完成的任务,是可以接受的。