Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
一、知识点讲解
1. 设备对象
我们在开发窗口程序的时候,消息被封装成一个结构体:MSG,在内核开发时,消息被封装成另外一个结构体:IRP(I/O Request Package I/O请求包)。
在窗口程序中,能够接收消息的只能是窗口对象(由窗口对象将消息分发给各个窗口过程)。
在内核中,能够接收IRP消息的只能时设备对象。
2. 创建设备对象
调用 IoCreateDevice API 来创建设备对象,其中需要传入设备名称(R3依据这个找到),需要初始化字符串。
1 //创建设备名称 2 UNICODE_STRING Devicename; 3 RtlInitUnicodeString(&Devicename,L"\\Device\\MyDevice"); 4 5 //创建设备 6 IoCreateDevice( 7 pDriver, //当前设备所属的驱动对象 8 0, 9 &Devicename, //设备对象的名称 10 FILE_DEVICE_UNKNOWN, 11 FILE_DEVICE_SECURE_OPEN, 12 FALSE, 13 &pDeviceObj //设备对象指针 14 );
3. 设置数据交互方式
pDeviceObj->Flags |= DO_BUFFERED_IO (注意: I= 表示按位或的含义)
其存在三种读写方式:
1)缓冲区读写方式(DO_BUFFERED_IO):操作系统将应用程序提供缓冲区的数据复制到内核模式下的地址中。
2) 直接方式读写(DO_DIRECT_IO):操作系统会将用户模式下的缓冲区锁住。然后操作系统将这段缓冲区在内核模式地址再次映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的时同一区域的物理内存。缺点就是要单独占用物理页面。
3)其他方式读写(不设置Flags):很危险,直接读写缓冲区地址,很容易出现蓝屏并且很可能数据丢失(读取过程中挂起页置换)。
4. 创建符号链接
//创建符号链接名称
RtlInitUnicodeString(&SymbolicLinkName,L"\\??\\MyTestDriver");
// 创建符号链接
IoCreateSymbolicLink(&SymbolicLinkName,&Devicename);
特别说明:
1)设备名称的作用就是给内核对象用的,如果要在R3访问,必须要有符号链接。其实就是一个别名,没有这个别名,在R3不可见。
2)在内核模式下,符号链接是以 '\??\'开头的,如果C盘就是 "\??\C:"
3) 在用户模式下,则是以 '\\.\' 开头的,如果是C盘就是 "\\.\C:"
5. IRP与派遣函数
如下图,在R3层面上,其由窗口对象负责将消息发送给对应的回调函数;而在内核层,将IRP发送给设备对象,由设备对象转发给对应的派遣函数。
6、IRP的类型
微软文档 :IRP structure
1) 当应用层通过CreateFile,ReadFile,WriteFile,CloseHandle等函数打开、从设备读取数据、向设备写入数据、关闭设备的时候,会时操作系统产生出IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_CLOSE等不同的IRP。
2)其他的IRP类型
IRP_MJ_DEVICE_CONTROL DeviceControl函数会产生此IRP
IRP_MJ_POWER 在操作系统处理电源消息时,产生次IRP
IRP_MJ_SHUTDOWN 关闭系统前会产生此IRP
7、派遣函数在哪里注册呢?
其在 _DRIVER_OBJECT 函数最后一个数组中。其派遣函数的种类及其有限(由IRP消息类型限制),可以看出一共有29种。
关于 _DRIVER_OBJECT的介绍可以查看之前这篇博客:内核空间与内核地址
8. 注册派遣函数
如下图,我们直接采取对数组赋值的形式来设置派遣函数。
9. 派遣函数格式
一定要设置其返回状态 NTSTATUS,三环程序判断API是否调用成功就是根据这个状态,如果不设置,则可能会出错。