1.驱动程序在某些特定时候可以理解为内核模块,即运行在Ring0级的一段代码。
2.内核模块位于内核空间,而内核空间又被所有的进程共享。因此,内核模块实际上可以位于任何一个可能的进程空间中的。这些进程取决于请求的来源、处理的进程。
3.系统进程不代表内核空间的进程,而是特指进程“System”,它的PID始终为4,在内核代码中调用PsGetCurrentProcessId就可以发现内核模块中分发函数调用时,当前进程一半都不是System进程,但内核模块的DriverEntry函数确实是由系统进程来加载的,这并不说明内核代码始终运行在System进程里面。
4.绝大多数的内核API函数的返回值都是一个NTSTATUS,我们自己编写的函数也将要这么做。如果碰到了奇怪的NTSTATUS值,请到ntstatus.h中寻找,在帮助里找不到。
5.关于字符串:UNICODE_STRING内核态字符串,可以使用DbgPrint("%wZ",&str)来打印UNICODE_STRING str。
6.关于驱动对象:Windows内核采用面向对象的编程方式,但是使用的确是C语言,所谓的“Windows内核对象”,并不是一个C++类的对象,而是Windows的内核程序员使用C语言对面向对象编程方式的一中模拟。这些内核对象分别用结构体表似乎,都在内存中,我们在内核中写的代码,可以随意的访问它们。
7.一个驱动对象 代表了一个驱动程序,或者说一个内核模块。如果要在内核中加载一个驱动程序,或者说一个内核模块,必须要填写如下的DRIVER_OBJECT结构的驱动对象。驱动程序并没有像main()程序一样生成一个进程,它只是填写了一组符合Windows内核规定的回调函数,这些注册了的回调函数功能都由驱动程序来填写实现。
由此,我们可以想到编写一套内核模块程序,寻找一些关键的驱动对象结构,然后改写下面的分发函数为我们自己的函数,就肯能从内核中实现hook,这就是所谓的分发函数Hook技术。
8.设备对象:DEVICE_OBJECT,常被简称为DO,在内核世界里,大部分“消息”都是以请求(IRP)的方式传递。而设备对象是唯一可以接受请求的实体,任何一个“请求”(IRP)都是发送给某个设备对象的。
需要注意的是,一个DO,并不一定特指硬件设备。驱动程序为它的每一个设备创建设备对象,对于驱动程序来说,设备对象就代表了设备。
一个驱动创建的所有设备对象(DEVICE_OBJECT)链成一条链,通过驱动对象(DRIVER_OBJECT),可以找到由该驱动创建的所有设备对象(DEVICE_OBJECT)。一个设备对象(DEVICE_OBJECT)也可以找到创建它的驱动的驱动对象(DRIVER_OBJECT)。
关于设备对象的使用:设备对象,代表了一个“设备”,当Windows向一个设备发出请求的时候,就在内核中以IRP方式传递设备请求消息,拥有目的设备对象的驱动对象捕获该消息,并通过该驱动对象实现的分发函数分发处理,从而实现设备对象响应。
9.请求
一些让硬件设备进行读写等动作的消息在内核中被IO管理器翻译成请求(IRP或者与之等效的形式,如快速调用),并发往某个设备对象。
过程的通俗解释:
上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求,操作系统将相应的I/O请求转换成相应的IRP,不同的IRP会根据类型不同被分派到不同的分发函数中进行处理。
内核中大部分请求都是IRP形式。在内核中,IRP也是一个相当复杂的数据结构,因为它记载着无数种请求信息。我们没有必要去一一了解它的结构。
10.内核API函数
帮助中有很多的API函数,主要的API前缀是Io、Ex、Rtl、Ke、Zw、Nt和Ps开头,还有关于网络驱动程序开发的Ndis开头的,开发WDF驱动相关的Wdf开头。