【原创】《windows驱动开发技术详解》第4章实验总结一
目录
1 实验要求
2 编写过程
2.1 确立整体架构
2.1.1 入口函数——DriverEntry
2.1.2 自定义创建设备函数——CreateDevice
2.1.3 卸载函数——DriverUnLoad
2.1.4 IRP派遣函数
2.1.5 DUMP函数
3. 收获
4. 完整代码
正文
1 实验要求
2 编写过程
2.1 确立整体架构
2.1.1 入口函数——DriverEntry
(1)作用
- 初始化Driver_Object驱动对象, 注册调用函数入口包括DriverUnload,MajorFunction的相关IRP派遣函数IRP_MJ_CREATE、IRP_MJ_CLOSE、IRP_MJ_READ、IRP_MJ_WRITE
- 创建设备对象
(2)初学者会遇到的问题
- 当源文件为CPP格式时,DriverEntry没有使用extern "C"声明,导致错误error MSB3030: 无法复制文件“D:\D_DRIVER\NT_Driver层次结构\x64\Win7Debug\NT_Driver层次结构.sys”,原因是找不到该文件。
- NTSTATUS的定义,主要是NT_SUCCESS
2.1.2 自定义创建设备函数——CreateDevice
(1)作用
- 创建指定设备名和符号链接名的设备对象
- IoCreateSymbolicLink将符号链接和设备名联系起来
- 设置设备对象的Flags值DO_BUFFERED_IO和设备拓展对象中的deviceName和symbolicName,用于卸载函数等其他地方
(2)初学者会遇到的问题
◆在初始化设备拓展结构体时,将其中的deviceName(类型为UNICODE_STRING)设置为devName(类型为UNICODE_STRING),用了RtlCopyUnicodeString复制函 数,结果复制失败。原因是DeviceExtension的deviceName为空,而它的类型UNICODE_STRING是一种结构体
1 typedef struct _UNICODE_STRING { 2 USHORT Length; 3 USHORT MaximumLength; 4 #ifdef MIDL_PASS 5 [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer; 6 #else // MIDL_PASS 7 _Field_size_bytes_part_(MaximumLength, Length) PWCH Buffer; 8 #endif // MIDL_PASS 9 } UNICODE_STRING;
RtlCopyUnicodeString函数不会修改MaximumLength和Buffer,也就是说当MaximumLength小于源字符串的长度,那只能截取源字符串MaximumLength长度的字符了。
对于解决方法,可以直接将devName赋值给结构体的deviceName,如下(不安全,因为如果devName内存释放了Buffer存储的地址就不合法了,而结构体的deviceName继续引用就会出问题);
2.1.3 卸载函数——DriverUnLoad
(1)作用
- 删除设备对象以及对应的符号链接
(2)初学者会遇到的问题
- 定义卸载函数时未按照系统规定的函数指针类型定义,VOID DriverUnLoad(PDRIVER_OBJECT pDriverObject),要注意到函数的返回值是VOID(大写),形参类型是PDRIVER_OBJECT。
2.1.4 IRP派遣函数
(1)作用
- 处理IRP
(2)初学者会遇到的问题
- 未认识到IRP派遣函数函数的原型是NTSTATUS DefaultDispatchRoutine(PDEVICE_OBJECT pDeviceObject, PIRP pIrp);函数名可以改变。
2.1.5 DUMP函数
(1)作用
- 显示调试信息
(2)初学者会遇到的问题
- KdPrint对驱动对象或设备对象地址的显示,其调用为KdPrint(("Driver Address:%#010x\n",pDriverObject));;用显示16进制的地址,8位加上0x总共10位,容易搞错
- UNICODE_STRING的输出,KdPrint(("Driver HardwareDatabase:%S\n", pDriverObject->HardwareDatabase->Buffer));容易忽略Buffer域
3. 收获
- 当你看着书,跟着书一步一步的走,你会觉得什么都掌握,但是当你开始打开IDE编码的时候会突然无从下手。重要的细节不经过实际的编码是掌握不牢的。
- 就我自己的编码方法,我会把程序的整个框架先写出来,写接口,再写步骤注释,最后再按照注释完整写出代码,整个过程会清晰很多。
- 编码的时候,不要一边盯着书,不会的要自己尽力回忆,再不会则写个注释标记一下,等所有东西写完之后再去看书检查,效果会更好,而且比较容易记录错误。
4. 完整代码
1 #include <NTDDK.h> 2 //设备拓展结构体 3 typedef struct { 4 PDEVICE_OBJECT pDeviceObject; //指向自己 5 UNICODE_STRING deviceName; //设备名 6 UNICODE_STRING symbolicName; //符号链接 7 PDEVICE_OBJECT attachDevice; //下层设备对象 8 }DeviceExtension, *PDeviceExtension; 9 //DUMP函数 10 void dump(PDRIVER_OBJECT pDriverObject) 11 { 12 KdPrint(("------------------------------------------------------\n")); 13 KdPrint(("Begin dump")); 14 //打印调试信息 15 KdPrint(("Driver Address:%#010x\n",pDriverObject)); 16 KdPrint(("Driver Name:%S\n", pDriverObject->DriverName.Buffer)); 17 KdPrint(("Driver HardwareDatabase:%S\n", pDriverObject->HardwareDatabase->Buffer)); 18 PDEVICE_OBJECT pDeviceObject = pDriverObject->DeviceObject; 19 KdPrint(("Driver first device:%#010x\n", pDeviceObject)); 20 for (int i = 1; NULL != pDeviceObject; pDeviceObject = pDeviceObject->NextDevice,++i) 21 { 22 KdPrint(("the %d device\n", i)); 23 KdPrint(("Device AttachedDevice:%#010x\n", pDeviceObject->AttachedDevice)); 24 KdPrint(("Device NextDevice:%#010x\n", pDeviceObject->NextDevice)); 25 KdPrint(("Device StackSize:%d\n", pDeviceObject->StackSize)); 26 KdPrint(("Device's DriverObject:%#010x\n", pDeviceObject->DriverObject)); 27 } 28 KdPrint(("dump over\n")); 29 KdPrint(("------------------------------------------------------\n")); 30 } 31 //创建设备对象 32 NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject, UNICODE_STRING &linkName, UNICODE_STRING &devName) 33 { 34 NTSTATUS status; 35 PDEVICE_OBJECT pDeviceObject; 36 PDeviceExtension pDevExt; 37 //创建设备对象 38 status = IoCreateDevice(pDriverObject, sizeof(DeviceExtension), &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); 39 if (!NT_SUCCESS(status)) 40 { 41 return status; 42 } 43 //创建符号链接 44 status = IoCreateSymbolicLink(&linkName, &devName); 45 if (!NT_SUCCESS(status)) 46 { 47 //删除设备对象 48 IoDeleteDevice(pDeviceObject); 49 return status; 50 } 51 //设置设备对象 52 pDeviceObject->Flags |= DO_BUFFERED_IO; 53 pDevExt = (PDeviceExtension)(pDeviceObject->DeviceExtension); 54 pDevExt->deviceName = devName; 55 pDevExt->symbolicName = linkName; 56 pDevExt->pDeviceObject = pDeviceObject; 57 return STATUS_SUCCESS; 58 } 59 //返回值不能为NTSTATUS,为VOID 60 VOID DriverUnLoad(PDRIVER_OBJECT pDriverObject) 61 { 62 KdPrint(("Leave\n")); 63 //删除设备对象 64 //删除符号链接 65 PDEVICE_OBJECT pcurDevice, pNextDevice; 66 pcurDevice = pDriverObject->DeviceObject; 67 while (NULL != pcurDevice) 68 { 69 PDeviceExtension pDevExt = (PDeviceExtension)(pcurDevice->DeviceExtension); 70 pNextDevice = pcurDevice->NextDevice; 71 IoDeleteSymbolicLink(&pDevExt->symbolicName); 72 IoDeleteDevice(pcurDevice); 73 pcurDevice = pNextDevice; 74 } 75 } 76 //IRP派遣函数 77 NTSTATUS DefaultDispatchRoutine(PDEVICE_OBJECT pDeviceObject, PIRP pIrp) 78 { 79 //显示IRP类型 80 KdPrint(("%d", pIrp->Type)); 81 pDeviceObject; 82 return STATUS_SUCCESS; 83 } 84 extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegisterPath) 85 { 86 NTSTATUS status = STATUS_SUCCESS; 87 UNICODE_STRING linkName,devName; 88 KdPrint(("Create\n")); 89 pRegisterPath; 90 //注册调用函数入口 91 pDriverObject->DriverUnload = DriverUnLoad; 92 pDriverObject->MajorFunction[IRP_MJ_CREATE] = 93 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = 94 pDriverObject->MajorFunction[IRP_MJ_READ] = 95 pDriverObject->MajorFunction[IRP_MJ_WRITE] = DefaultDispatchRoutine; 96 //创建设备对象 97 RtlInitUnicodeString(&devName, L"\\Device\\C_1"); 98 RtlInitUnicodeString(&linkName, L"\\??\\C_1"); 99 status = CreateDevice(pDriverObject,linkName,devName); 100 if (!NT_SUCCESS(status)) 101 { 102 return status; 103 } 104 105 RtlInitUnicodeString(&devName, L"\\Device\\C_2"); 106 RtlInitUnicodeString(&linkName, L"\\??\\C_2"); 107 status = CreateDevice(pDriverObject, linkName, devName); 108 if (!NT_SUCCESS(status)) 109 { 110 IoDeleteSymbolicLink(&linkName); 111 IoDeleteDevice(pDriverObject->DeviceObject); 112 return status; 113 } 114 dump(pDriverObject); 115 return status; 116 }