本章主要介绍了内核编程的一些注意事项,这里就本人关注的点以及新学到的知识进行记录分享
3.2 调试构建与发布构建
KdPrint
宏是 DbgPrint
的封装,在生成 Release 版本的时候无效果,在生成 Debug 版本有效果
3.3 内核 API
内核 API 前缀及其解释:
Nt 开头的函数和 Zw 开头的函数的区别是,用户模式下一般调用的函数最后都会走到 Nt 开头的函数那里,然后进入内核,会有一些安全性和缓冲区检查,Zw 开头的函数在内核模式下调用则绕过了这些用户层到内核层的安全性和缓冲区检查
一般来说,在驱动程序中最好调用 Zw 开头的函数而不是 Nt 开头的函数
3.6 动态内存分配
内核栈相当小,任何大块内存都必须进行分配
内核为驱动程序提供了两种通用内存池:
- 分页池:在需要时能够将页面换出的内存池
- 非分页池:永远不会换出页面,保证驻留在 RAM(物理内存)里的内存池
POOL_TYPE
枚举类型里,驱动程序可以使用的内存类型有三种: PagedPool,NonPagedPool,NonPagedPoolNx(无执行权限的非分页内存)
申请内存指定的 4 字节标记可以用来发现内存泄露问题
可使用 WDK 的 PoolMon 工具查看内存池分配的内存块信息
3.8 驱动程序对象
驱动对象 DRIVER_OBJECT 的 MajorFunction 字段是一个函数指针数组,指明了驱动程序支持哪些操作
3.9 设备对象
驱动对象不能直接跟用户程序进行通信,需要通过设备对象进行,所以驱动程序需要创建一个设备对象并命名,用户程序才能进行通信
CreateFile 函数的第一个参数接收的是符号链接,符号链接是一个内核对象,指向另一个内核对象,所有能被该函数调用的符号链接都位于对象管理器中名为 ??
的目录下,使用 WinObj 可以看到:比如这个 C:
盘路径就是一个符号链接指向 \Device\HarddiskVolume3
这个对象
??
多数指向 \Device
目录下的内部设备名称,用户模式不能直接用该名称,内核驱动可以通过 API:IoGetDeviceObjectPointer
进行访问
用户模式使用符号链接访问设备对象时,需要加上前缀 \\.\
以防设备管理器给当成文件名去处理了
内核模式使用 IoCreateDevice
创建设备对象,设备对象实例保存在驱动对象结构的 DeviceObject
中多个设备对象则会通过单向链表进行关联