对编写基础的驱动有所了解后,我们来进一步了解一下内核,本篇会介绍两个概念,内核空间以及内核模块,先从内核空间说起。
内核空间
内核空间的概念,我们还是比较熟悉的。这里我们主要关注一点,就是不同进程在低2G内存空间对应的物理页往往是不同的,而在高2G内存空间对应的物理页往往是相同的。如图: 这一要素,主要运用于跨进程读取内存等手法,之前的文章也提到过,这里我们来验证一下这个理论:
- 第一步,写一个驱动,获取全局变量的地址,程序比较简单,就不贴代码了,直接上图
- 第二步运行驱动,在DebugView中可以看到该全局变量的线性地址:0xbac93000
- 然后我们随机打开一个应用,这里以记事本为例。打开后,在Windbg中,找到记事本的Cr3查看该进程在线性地址0xbac93000处的值为多少 可以发现,我们在记事本这个进程中查看0xbac93000这个线性地址对应的物理页时,它所存着的值恰好是我们在另一个驱动中定义的全局变量的值。这正说明了,不同进程在高2G中对应的物理页是相同的。(这里解释一下 .process 这个指令的作用,0xaaaabbbb对应着某个进程的进程结构体的地址,.process aaaabbbb这个指令就相当于切换到这个进程,之后所访问的地址,都是这个进程地址空间中的地址)
内核模块
基本概念
- 硬件种类繁多,不可能做出一个兼容所有硬件的内核,所以,微软提供规定的接口格式,让硬件驱动人员按照规定的格式编写“驱动程序”。当然,并不是每个驱动程序一定需要关联一个硬件,也可以仅仅是一个程序,就如同我们之前所写的。
- 这些驱动程序,每一个都是一个模块,称为“内核模块”,都可以加载到内核中,也都遵守PE结构。本质上,任意一个.sys文件与内核文件(例如ntoskrnl.exe)没有区别。
有了上述概念后,我们可以打个比喻,内核空间(高2G),相当于一个进程;而各个加载到内核中的内核模块,就相当于加载到进程中的DLL;内核模块提供0环的函数实现以及硬件的程序驱动,DLL为这个进程提供额外功能。这样就好理解了。
DRIVER_OBJECT
在之前编写的驱动程序中,入口函数总会传递一个参数,它的类型是PDRIVER_OBJECT,说明这是一个指向DRIVER_OBJECT的指针,而DRIVER_OBJECT正是驱动模块对应的结构体,来描述该驱动的相关信息。 由图,这里着重介绍四个比较关键的字段:
- DriverStart(+0x00C):驱动模块在内核中的地址
- DriverSize(+0x010):驱动模块在内核中的大小
- DriverName(+0x01C):驱动模块在内核中的名字
- DriverSection(+0x014):这个位置存的是一个指针,指向一个_LDR_DATA_TABLE_ENTRY结构体
LDR_DATA_TABLE_ENTRY
驱动在内核中也属于内核模块,该结构体描述了内核模块的相关信息,同时包含串着所有内核模块的双向链表,通过该链表,可以遍历所有内核模块。
- InLoadOrderLinks(+0x000):串着所有内核模块的双向链表
- DllBase(+0x018):当前内核模块的基址
- SizeOfImage(+0x020):当前内核模块的大小
- FullDllName(+0x024):当前内核模块的完整路径
- BaseDllName(+0x02C):当前内核模块的模块名
内核模块遍历
有了上面这些基础后呢,就可以自己实现内核模块遍历的功能了。这里先说一下思路:
- 首先我们写一个驱动,驱动函数的入口会传一个PDRIVER_OBJECT这个参数,我们就可以利用这个参数,获取到自身驱动的DRIVER_OBJECT
- 有了DRIVER_OBJECT结构体后,就可以通过其偏移0x014位置处的值,找到LDR_DATA_TABLE_ENTRY结构体,该结构体的第一个元素,就是串着所有内核模块的双向链表
- 写一个循环,遍历这个双向链表,打印出相应的信息
下面附上代码:
1 |
|
观察实验结果:
这里主要打印了模块名,基址以及大小。观察结构可以发现,内核模块中不仅包含驱动文件,还有一些系统的dll,exe。因为它们本质上都是PE结构,所以不论是驱动文件还是内核文件,在内核空间中,都是一种内核模块。
关系梳理
之前,在学习完KPCR后,对进程结构体,线程结构体,KPCR进行了简单的关系梳理,不过需要指出一点,在后面分析线程切换函数SwapContext时也指出了,线程寻找进程,主要用的是KTHREAD+0x44偏移处的Process,而不是用ETHREAD+0x220的偏移处的EPROCESS,不过呢,通常情况下,这两个地方都可以用。
现在又学习完了驱动、内核相关的结构体,来看看它们之间的关系如何(在已知驱动的情况下):
遍历内核模块:PDRIVER_OBJECT -> DRIVER_OBJECT -> DriverSection -> _LDR_DATA_TABLE_ENTRY -> InLoadOrderLinks
遍历驱动模块名:PDRIVER_OBJECT -> DRIVER_OBJECT -> DriverSection -> _LDR_DATA_TABLE_ENTRY -> InLoadOrderLinks -> DllBase(模块名.sys)
参考教程:https://www.bilibili.com/video/BV1NJ411M7aE?p=60
参考文章:https://blog.csdn.net/qq_41988448/article/details/103514007
参考笔记:张嘉杰的笔记
参考代码:葫芦娃救爷爷,Joney,张嘉杰