《Windows驱动开发技术详解》之HelloDDK
编写如下代码:
运行会报错:
这里的原因是有没有引用到的形参,需要添加如下代码即可:
记得在系统启动时按F8禁用掉数字签名:
将编译好的驱动拖入虚拟机,以管理员身份运行DbgView捕捉内核态输出数据,以管理员身份运行InstDrv以加载驱动,我们就可以看到启动和停止时,DriverEntry和DriverUnload中输出的数据了:
下面,来对上面的实验进行简单的说明:
Windows驱动程序的入口函数并不是main函数,而是DriverEntry函数,这个函数由内核中的I/O管理器负责调用。参数pDriverObject是I/O管理器传递进来的驱动对象;参数pRegistryPath是驱动程序在注册表中的路径。
下面,我们来把上面的例子完善下:
1 #include"helloddk.h" 2 typedef struct _DEVICE_EXTENSION{ 3 PDEVICE_OBJECT pDevice; 4 UNICODE_STRING ustrDeviceName; 5 UNICODE_STRING ustrSymLinkName; 6 }DEVICE_EXTENSION,*PDEVICE_EXTENSION; 7 8 9 NTSTATUS DriverEntry( 10 IN PDRIVER_OBJECT pDriverObject, 11 IN PUNICODE_STRING pRegistryPath) 12 { 13 DbgPrint("Hello, world\n"); 14 UNREFERENCED_PARAMETER(pDriverObject); 15 UNREFERENCED_PARAMETER(pRegistryPath); 16 NTSTATUS status; 17 18 pDriverObject->DriverUnload = DriverUnload;//向IO管理器注册回调函数 19 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine; 20 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine; 21 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine; 22 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine; 23 status = CreateDevice(pDriverObject);//创建驱动设备对象 24 return STATUS_SUCCESS; 25 } 26 27 VOID DriverUnload(PDRIVER_OBJECT pDriverObject) 28 { 29 UNREFERENCED_PARAMETER(pDriverObject); 30 PDEVICE_OBJECT pNextObj; 31 pNextObj = pDriverObject->DeviceObject; 32 while (pNextObj != NULL){ 33 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;//获取设备扩展 34 UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName; 35 IoDeleteSymbolicLink(&pLinkName);//删除符号链接 36 pNextObj = pNextObj->NextDevice;//得到下一个设备对象地址 37 IoDeleteDevice(pDevExt->pDevice); 38 } 39 DbgPrint("Goodbye world!\n"); 40 } 41 //创建设备对象 42 NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject){ 43 DbgPrint("Create Device!\n"); 44 NTSTATUS status; 45 PDEVICE_OBJECT pDevObj; 46 PDEVICE_EXTENSION pDevExt; 47 48 UNICODE_STRING devName; 49 RtlInitUnicodeString(&devName, L"\\Device\\MyDDKDevice"); 50 51 status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj); 52 if (!NT_SUCCESS(status)) 53 return status; 54 pDevObj->Flags |= DO_BUFFERED_IO; 55 pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; 56 pDevExt->pDevice = pDevObj; 57 pDevExt->ustrDeviceName = devName; 58 UNICODE_STRING symLinkName; 59 RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK"); 60 pDevExt->ustrSymLinkName = symLinkName; 61 status = IoCreateSymbolicLink(&symLinkName, &devName);//创建符号链接 62 //驱动的设备名称只有在内核态可见,对于应用程序是不可见的, 63 //驱动需要暴露一个符号链接,该链接指向真正的设备名称 64 if (!NT_SUCCESS(status)){ 65 IoDeleteDevice(pDevObj); 66 return status; 67 } 68 return STATUS_SUCCESS; 69 } 70 71 NTSTATUS HelloDDKDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp){ 72 DbgPrint("HelloDDKDispatchRoutine!\n"); 73 UNREFERENCED_PARAMETER(pDevObj); 74 NTSTATUS status = STATUS_SUCCESS; 75 pIrp->IoStatus.Status = status; 76 pIrp->IoStatus.Information = 0; 77 IoCompleteRequest(pIrp, IO_NO_INCREMENT); 78 return status; 79 }
启动、停止:
I/O管理器的作用:
I/O管理器在内存中创建一个IRP来代表一个I/O操作,传递一个指向IRP的指针到正确的驱动程序,当此I/O操作完成时再处理该请求包。相反地,驱动程序接收一个IRP,执行该IRP指定的操作,然后将IRP传回给I/O管理器,这是因为:所请求的I/O操作已经完成,或者必须将其传给另一个驱动程序以作进一步处理。
I/O管理器也提供一些对于不同驱动程序都通用的代码,
例如,I/O管理器提供了这样一个功能:允许一个驱动程序调用其它驱动程序。它也为I/O请求管理缓冲区、为驱动程序提供超时支持、记录下哪些可安装的文件系统被加载到操作系统中。在I/O管理器中有将近100个不同的例程可供设备驱动程序调用。