最近有点忙,一直没有时间来总结一下,好不容易逮着个有时间的星期天,静下心来,好好总结一下,完成未完成的系列教程,好了,废话少说,开题了。
现在进行Windows驱动开发,主要有两种框架,一种是NT框架的驱动,一种是WDM框架的驱动程序,微软后来为了区分WDM驱动的,又推出了KMDF和UMDF两种框架,KMDF是针对内核态的驱动程序开发的框架,而UMDF是用户态的驱动程序的开发框架,这两个框架对底层的API的封装程度又加重了一点,对于底层的安全软件的开发降低了控制的程度,就像微过滤驱动一样,不少安全厂商并不买账。
NT驱动主要工作在Windows 2000下,不支持即插即用和电源管理,大概包括三部分:入口函数(DriverEntry)、卸载历程(DriverUnload)、分发例程(Dispatch Function)。下面分别讲述这三个部分。
入口函数(DriverEntry)
驱动的入口函数主要是对驱动程序进行初始化工作,它是由系统进程所调用。在驱动程序初始化的时候,入口函数被加载进内存,进行初始化,完成之后,就要退出内存。一般入口函数的前边都要有这么一个标记
#pragma code_seg("INIT")标记驱动入口函数在内存中的位置。NT驱动的入口函数一般完成的工作也很简单,就是注册分发例程,下面是一个简单的代码示例:
- #pragma code_seg("INIT")
- extern"C"
- NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
- IN PUNICODE_STRING pRegistryPath
- )
- {
- NTSTATUS status;
- KdPrint(("Hello!welcome to the driver entry!/n"));
- pDriverObject->DriverUnload = DriverUnload;
- pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
- pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
- pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchReadWrite;
- pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchReadWrite;
-
- status = CreateDevice(pDriverObject);
- return status;
- }
卸载例程(DriverUnload)
卸载例程,顾名思义,就是负责驱动程序的卸载的。完成清扫的工作,删除符号链接,删除设备对象等等,下面是示例代码:
- #pragma CodeSeg("PAGED")
- VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
- {
- PDEVICE_OBJECT pNextObj;
- KdPrint(("Enter DriverUnload/n"));
- pNextObj = pDriverObject->DeviceObject;
- while (pNextObj != NULL)
- {
- PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
- pNextObj->DeviceExtension;
-
-
- UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
- IoDeleteSymbolicLink(&pLinkName);
- pNextObj = pNextObj->NextDevice;
- IoDeleteDevice( pDevExt->pDevice );
- }
- }
分发例程Dispatch
分发例程是驱动中真正进行功能实现的部分,系统产生的IRP都是由该部分进行处理,并且在执行的时候,分发函数运行在不同的进程中,存在于分页内存中,最最简单的处理函数可以参考上一篇文章中的示例代码,只是实现了转交的作用,具体想实现的功能,就要看用户的不同需求了,这里就不再赘述。
创建设备对象的函数代码:
- #pragma code_seg("INIT")
- NTSTATUS CreateDevice (
- IN PDRIVER_OBJECT pDriverObject)
- {
- NTSTATUS status;
- PDEVICE_OBJECT pDevObj;
- PDEVICE_EXTENSION pDevExt;
-
-
- UNICODE_STRING devName;
- RtlInitUnicodeString(&devName,L"//Device//MyDDKDevice");
-
-
- status = IoCreateDevice( pDriverObject,
- sizeof(DEVICE_EXTENSION),
- &(UNICODE_STRING)devName,
- FILE_DEVICE_UNKNOWN,
- 0, TRUE,
- &pDevObj );
- if (!NT_SUCCESS(status))
- return status;
-
- pDevObj->Flags |= DO_BUFFERED_IO;
- pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
- pDevExt->pDevice = pDevObj;
- pDevExt->ustrDeviceName = devName;
-
- UNICODE_STRING symLinkName;
- RtlInitUnicodeString(&symLinkName,L"//??//HelloDDK");
- pDevExt->ustrSymLinkName = symLinkName;
- status = IoCreateSymbolicLink( &symLinkName,&devName );
- if (!NT_SUCCESS(status))
- {
- IoDeleteDevice( pDevObj );
- return status;
- }
- return STATUS_SUCCESS;
- }
在Windows 2000出现之后,微软又加入了新的驱动模型--WDM驱动模型,主要添加了对即插即用和电源管理的支持。驱动的大概结构和NT驱动很类似,新增了添加设备例程,类似于上面的CreateDevice函数,因为在NT驱动中,不少开发者直接把CreateDevice的实现放在了DriverEntry中了,而我只是把它单拿出来了。
入口函数(DriverEntry)
WDM驱动程序的入口函数和上面的差不多,通用的代码中加入下边一句话就行了:
- pDriverObject->DriverExtension->AddDevice = MyDeviceAdd;
添加设备例程(AddDevice)
AddDevice类似于NT驱动中DriverEntry创建设备对象函数,但是略有不同,参数的个数不同,DriverObject是I/O管理器创建的驱动对象,PhysicalDeviceObject是底层总线驱动创建的PDO对象。大概的步骤是这样的:
首先通过IoCreateDevice创建设备对象,然后保存设备对象的地址,接下来将创建的设备对象FDO附加在底层PDO上面,最后设置FDO的flags子域。示例代码:
- #pragma CodeSeg("PAGED")
- NTSTATUS AddDevice(IN PDRIVER_OBJECT DriverObject,
- IN PDEVICE_OBJECT PhysicalDeviceObject)
- {
- PAGED_CODE();
- KdPrint(("Enter AddDevice/n"));
-
- NTSTATUS status;
- PDEVICE_OBJECT fdo;
- UNICODE_STRING devName;
- RtlInitUnicodeString(&devName,L"//Device//MyWDMDevice");
- status = IoCreateDevice(
- DriverObject,
- sizeof(DEVICE_EXTENSION),
- &(UNICODE_STRING)devName,
- FILE_DEVICE_UNKNOWN,
- 0,
- FALSE,
- &fdo);
- if( !NT_SUCCESS(status))
- return status;
- PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
- pdx->fdo = fdo;
- pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
- UNICODE_STRING symLinkName;
- RtlInitUnicodeString(&symLinkName,L"//DosDevices//MyWDM");
-
- pdx->ustrDeviceName = devName;
- pdx->ustrSymLinkName = symLinkName;
- status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);
-
- if( !NT_SUCCESS(status))
- {
- IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
- status = IoCreateSymbolicLink(&symLinkName,&devName);
- if( !NT_SUCCESS(status))
- {
- return status;
- }
- }
-
- fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
- fdo->Flags &= ~DO_DEVICE_INITIALIZING;
-
- KdPrint(("Leave AddDevice/n"));
- return STATUS_SUCCESS;
- }
分发例程(Dispatch Function)
通用的分发例程就不在介绍了,跟上面NT驱动的一样,没什么区别,下面介绍一下PNP。
即插即用例程Pnp
即插即用IRP,即IRP_MJ_PNP,一般是由即插即用管理器发送给WDM驱动程序的。关于即插即用,用户可以查看更加详细的资料,这里主要讲述一个IRP的处理,对IRP_MN_REMOVE_DEVICE IRP进行处理,这个例程主要实现了原来DriverUnload所实现的功能。原理很简单,看一下示例代码:
- NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
- {
- PAGED_CODE();
- KdPrint(("Enter HandleRemoveDevice/n"));
-
- Irp->IoStatus.Status = STATUS_SUCCESS;
- NTSTATUS status = DefaultPnpHandler(pdx, Irp);
- IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);
-
-
- if (pdx->NextStackDevice)
- IoDetachDevice(pdx->NextStackDevice);
-
-
- IoDeleteDevice(pdx->fdo);
- KdPrint(("Leave HandleRemoveDevice/n"));
- return status;
- }
卸载历程(DriverUnload)
卸载例程现在显得可有可无了,给一个简单的示例吧,其实什么都没实现
- void DriverUnload(IN PDRIVER_OBJECT DriverObject)
- {
- PAGED_CODE();
- KdPrint(("Enter DriverUnload/n"));
- KdPrint(("Leave DriverUnload/n"));
- }
这一点就说到这吧,本来还想说说驱动栈的结构,因为需要用图表比较清晰,现在无法上传图片,就不讲了。
此文章来自于【http://blog.csdn.net/caperingrabbit/article/details/5376297】