Windows驱动程序框架理解_经典入门
驱动基础知识.

驱动程序对象
• DeviceObject(PDEVICE_OBJECT) 指向一 个设备对象链表,每个设备对象代表一个设备。 I/O管理器把多个设备对象连接起来并维护这 个域。非WDM驱动程序的DriverUnload函数 利用这个域来遍历设备对象列表,以便删除其 中的设备对象。WDM驱动程序没有必要使用 这个域。 FastIoDispatch(PFAST_IO_DISPATCH)指 向一个函数指针表,这些函数是由文件系统和 网络驱动程序输出的。 DriverStartIo(PDRIVER_STARTIO)指向驱动 程序中处理串行I/O请求的函数,I/O管理器自 动为驱动程序串行化多个I/O请求。 DriverUnload (PDRIVER_UNLOAD)指向驱 动程序中的清除函数。 MajorFunction (array of PDRIVER_DISPATCH)是一个函数指针表, 指向存在于驱动程序中的二十多种IRP处理函 数。
Windows 驱动基础知识
Windows NT5.0 System Structure
应用程序 Window 32 API调用 Win32子系统
用户模式
内核模式 I/O管理器 转递IRP给驱动程序派遣函数 设备驱动程序 HAL调用 硬件抽象层 平台相关操作 硬件 系统服务接口
FindNextFile function operation process
应用程序调用 Kernel32.dll中 的FindNextFile Kernel32.dll中 的FindNextFile
Ntdll.dll中的 NtQueryDirectory SYSTEM 或 INT 2E 用户模式 内核模式
KiSystemService
Ntoskrnl.exe中的 NtQueryDirectoryFile
WinPcap所涉及的Windows驱动基础知识(三)

深度剖析WinPcap之(三)——所涉及的Windows驱动基础知识1.1 Windows驱动的基础知识本节主要描述在WinPcap的NPF中经常使用一些编写Windows驱动程序所需掌握的部分基础知识,以便于后面的理解。
1.1.1 驱动对象(DRIVER_OBJECT)每个驱动程序都有唯一的驱动对象与之对应,该驱动对象在驱动程序被加载时由内核的对象管理程序所创建。
驱动对象用DRIVER_OBJECT数据结构表示,它作为驱动程序的一个实例被内核加载,对一个驱动程序内核I/O管理器只加载一个实例。
驱动对象数据结构在wdm.h文件中的定义如下。
typedef struct _DRIVER_OBJECT {CSHORT Type;CSHORT Size;/**DeviceObject为每个驱动程序所创建的一个或多个设备对象链表,*Flags提供一个扩展的标识定位驱动对象*/PDEVICE_OBJECT DeviceObject;ULONG Flags;/*下列各成员字段描述驱动程序从哪儿被加载*/PVOID DriverStart;ULONG DriverSize;PVOID DriverSection;PDRIVER_EXTENSION DriverExtension;/**DriverName成员被错误日志线程用来*确定一个I/O请求越界的驱动名称*/UNICODE_STRING DriverName;/*指向注册表中硬件信息的路径*/PUNICODE_STRING HardwareDatabase;/**如果驱动支持“fast I/O”,*就指向一个“fast I/O”的派遣函数数组*/PFAST_IO_DISPATCH FastIoDispatch;/**描述该特定驱动的入口点。
*主函数(major function)派遣函数表必须是对象最后的成员,*因此它仍然是可扩展的*/PDRIVER_INITIALIZE DriverInit;PDRIVER_STARTIO DriverStartIo;PDRIVER_UNLOAD DriverUnload;PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];} DRIVER_OBJECT;typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;下面分别描述驱动对象中驱动程序可访问的成员。
Windows 驱动程序开发指导说明书

课程内容驱动基本概念介绍驱动核心代码分析WDM和WDF介绍Windows 10通用驱动平台驱动程序是一个软件模块,可以使操作系统和硬件设备进行交互驱动程序是操作系统的一个扩展驱动程序一般是由硬件的设计者或厂商进行编写Microsoft已经为符合公共设计规范的硬件设备提供了内置的驱动程序可执行文件,扩展名是.exe入口函数是Main()Main()函数完成大部分工作应用程序完成工作后返回,并释放内存空间在用户态运行可执行文件,扩展名是.sys入口函数是DriverEntry()DriverEntry()不会做很多工作,只是初始化驱动驱动其他部分会注册很多回调函数,会被系统的不同模块调用驱动不会返回,会一直存在直至被显示的释放在内核态或用户态运行Driver StacksPDO位于驱动栈的最底层,和总线驱动相关联当总线驱动被加载时,它会枚举所有挂载在总线上面的设备并请求设备所需要的资源每个设备都有自己对应的PDOPnP管理器会确定每个设备的驱动并在设备的PDO 之上构建适当的设备栈设备栈的核心部分,FDO和设备功能驱动相关联设备功能驱动完成Windows和设备交互的核心功能对上向应用程序和服务提供上层接口对下为设备或其他驱动提供数据交换的接口一个设备栈可以包含多个FiDO,可以在FDO之上或之下每个FiDO和一个过滤驱动相关联,FiDO是可选的通常的目的是修改一些在设备栈中传输的I/O请求,例如可以加密和解密读写请求当一个新设备被插入到系统后,系统总线驱动会向PnP管理器报告这个新设备PnP管理器通过总线驱动查询这个设备的更多信息,比如设备ID和设备所需要的资源PnP管理器利用这个信息去查找是否有有对应的驱动在本地或WU(Windows Update)上面一旦查找到设备对应的驱动,Windows便会安装并加载这个驱动加载驱动到地址空间解析驱动中引入的函数-调用其他模块调用驱动的入口函数(DriverEntry()),因此驱动可以注册回调函数调用AddDevice(),驱动此时可以创建一个“设备对象”,并将这个对象加入到设备栈中所有的事物在驱动框架中都是用对象呈现的(驱动,设备,请求等等)对象拥有属性,方法和事件WDF 对象方法属性事件操作对象的函数被WDF 框架调用用于通知某些事件设置或获取单个属性值的方法Driver (WDFDRIVER)Device (WDFDEVICE)Device (WDFDEVICE)Queue (WDFQUEUE)Queue (WDFQUEUE)……ObjectOperation方法:Status = Wdf Device Create ();属性:Cannot failWdfInterrupt Get Device();WdfInterrupt Set Policy();Can fail:Status = WdfRegistry Assign Value();Status = WdfRegistry Query Value();Status = WdfRequest Retrieve InputBuffer();回调事件:PFN_WDF_INTERRUPT_ENABLE EvtInterruptEnable初始化宏:WDF_XXX_CONFIG_INITWDF_XXX_EVENT_CALLBACKS_INIT当驱动被加载时,DriverEntry是第一个被操作系统调用的函数WdfDriverCreate( RawDriverObject, […] , attributes, &driver )NTSTATUS DriverEntry(_In_PDRIVER_OBJECT DriverObject ,_In_PUNICODE_STRING RegistryPath ) {[…]// Create WDF Driver ObjectWDF_OBJECT_ATTRIBUTES_INIT(&attributes);attributes.EvtDriverUnload = OnDriverUnload;WDF_DRIVER_CONFIG_INIT(&config, OnDeviceAdd);status = WdfDriverCreate(DriverObject ,RegistryPath ,&attributes,&config,&driver );}WDF EventWDF MethodWDF ObjectDeclare vars这是一个过滤驱动程序吗?驱动程序是电源管理策略的所有者吗?为设备对象创建I/O队列创建辅助对象,例如计时器,工作者对象,锁等NTSTATUS OnDeviceAdd( WDFDRIVER Driver,PWDFDEVICE_INIT DeviceInit) {WDFDEVICE device;IWDFIoQueue* pDefaultQueue= NULL;DeviceInit->SetPowerPolicyOwnership(TRUE );status= WdfDeviceCreate(&DeviceInit,&deviceAttributes, &device);context = GetContext(device);context->WdfDevice= device;status = pIWDFDevice->CreateIoQueue(NULL, TRUE, WdfIoQueueDispatchParallel,TRUE, FALSE, &pDefaultQueue);return status;}Static Configuration Device CreateSetting ContextQueue Create进入电源状态管理(D0Entry)使能中断(InterruptEnable)…获取硬件资源,进行一些静态配置,(PrepareHardware)进入电源状态管理(D0Entry)使能中断(InterruptEnable)…获取硬件资源,进行一些静态配置,(PrepareHardware)NTSTATUS OnPrepareHardware(WDFDEVICE Device ,WDFCMRESLIST ResourcesRaw ,WDFCMRESLIST ResourcesTranslated ) {int ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated );for (i=0; i < ResourceCount; i++) {descriptor =WdfCmResourceListGetDescriptor(ResourcesTranslated , i);switch (descriptor->Type) {case CmResourceTypePort : […]case CmResourceTypeMemory : […]case CmResourceTypeInterrupt : […]default : break ;}}return STATUS_SUCCESS ;}进入电源状态管理(D0Entry)使能中断(InterruptEnable)…获取硬件资源,进行一些静态配置,(PrepareHardware)NTSTATUS OnD0Entry(IN WDFDEVICE Device ,IN WDF_POWER_DEVICE_STATE RecentPowerState ){PADXL345AccDevice pAccDevice = nullptr pAccDevice = GetContext(Device);WdfWaitLockAcquire(pAccDevice->m_WaitLock);I2CSensorWriteRegister(pAccDevice->m_I2CIoTarget, MY_REGISTER,MY_VALUE, sizeof (MY_VALUE) );pAccDevice->m_PoweredOn = true ;WdfWaitLockRelease(pAccDevice->m_WaitLock);return STATUS_SUCCESS ;}进入电源状态管理(D0Entry)使能中断(InterruptEnable)…获取硬件资源,进行一些静态配置,(PrepareHardware)NTSTATUS OnInterruptEnable(IN WDFINTERRUPT Interrupt,IN WDFDEVICE Device){PDEVICE_EXTENSION devExt;ULONG regUlong;PULONG intCsr;devContext = GetDeviceContext(WdfInterruptGetDevice(Interrupt) );intRegId = &devContext->IntRegisterId regVal = READ_REGISTER_ULONG( intRegId );regVal = ENABLE_INTERRUPT_BYTE( regVal );WRITE_REGISTER_ULONG( intRegId, regVal );return STATUS_SUCCESS;}进入电源状态管理(D0Entry)使能中断(InterruptEnable)…获取硬件资源,进行一些静态配置,(PrepareHardware)EvtIoResume EvtDMAEnablerFillEvtDeviceSelfManagedIoInitEvtDeviceDisarmWakeFromSxEventChildListScanForChildren EvtDeviceRemoveAddedResourcesStart power-managed queuesEvtIoResume Disarm wake signal, if it was armed. (called onlyduring power up; not called during resource rebalance)EvtDeviceDisarmWakeFromSx EvtDeviceDisarmWakeFromS0Request information about child devicesEvtChildListScanForChildren Enable DMA, if driver supports it EvtDmaEnablerSelfManagedIoStartEvtDmaEnablerEnableEvtDmaEnablerFillConnect interruptsEvtDeviceD0EntryPostInterruptsEnabledEvtInterruptEnable Notify Driver of state change EvtDeviceD0EntryDevice OperationalRestart from here if device is in low power statePrepare hardware for power EvtDevicePrepareHardwareChange resources requirements EvtDeviceRemoveAddedResourcesEvtDeviceFilterAddResourceRequirementsEvtDeviceFilterRemoveResourcRequirementsRestart from here if rebalancing resourcesCreate Device object EvtDriverDeviceAddDevice arrivedEnable self-managed I/O, if driver supports it.EvtDeviceSelfManagedIoInit (implicit power up),EvtDeviceSelfManagedIoRestart (explicit power up)Stop power-managed queuesEvtIoStop Arm wake signal, if it was not armed. (calledonly during power up; not called duringresource rebalance)EvtDeviceArmWakeFromSx EvtDeviceArmWakeFromS0Disable DMA, if driver supports it EvtDmaEnablerSelfManagedIoStopEvtDmaEnablerDisableEvtDmaEnablerFlushDisconnect interrupts EvtDeviceD0EntryPostInterruptsDisabledEvtInterruptDisableNotify Driver of state changeEvtDeviceD0Exit Device OperationalStop here if transitioning to low power stateRelease hardware EvtDeviceReleaseHardwarePurge power-managed queuesEvtIoStop Stop here if rebalancing resourcesFlush I/O if driver supports self-managedI/OEvtDeviceSelfManagedIoFlush Device removedSuspend self-managed I/O, if driver supports it.EvtDeviceSelfManagedIoSuspend Cleanup I/O buffers if driver supports self-managed i/o EvtDeviceSelfManagedIoCleanupDelete device object s context area.EvtDeviceContextCleanupEvtDeviceContextDestroyWDM和操作系统深度耦合,WDM驱动程序直接调用系统服务例程,直接操作系统数据结构WDM驱动程序全部为内核态程序,操作系统对驱动输入只做有限的检查WDF框架处理与操作系统的交互,驱动本身专注于和设备交互WDF基于对象模型和事件驱动WDF支持内核态程序和用户态程序将操作系统底层的复杂逻辑抽象化使驱动代码有可能<20行对不同的硬件设备使用相同的编程模型例如GPIO,UART,I2C,NFC,传感器驱动框架内置的日志系统为数据分析定制的工具支持上千种不同的硬件设备最初UMDF V1基于C++ COMUMDF V2使用和内核态驱动开发相同的模型和语法支持USB周边设备,传感器,NFC,智能卡,HID(包括触控)等等驱动崩溃只会影响宿主进程,不会影响整个操作系统系统重启策略可以自动恢复崩溃的UMDF驱动Windows 10提供了一系列API和DDI,对于所有的Windows平台都是通用的,被称为Universal Windows Platform(UWP) Windows通用驱动是指一个内核态或用户态的驱动并能运行在所有基于UWP的系统上面Windows通用驱动只能调用属于UWP部分的DDI,这部分DDI会在MSDN文档中标记为Universal确定你的驱动是否支持UWP,把你的驱动标记为通用驱动然后重新编译在Visual Studio中打开驱动项目工程在配置选项中把操作系统选择为Windows 10在工程属性中把目标平台改为“通用”,其他选项还有“桌面”和“手机”重新编译驱动,这时可以会出现一些链接器错误尝试修复这些错误,对于出现错误的API,请参考文档是否有通用平台的API可以替代,如果没有,您可能需要重新设计你的驱动KMDF version Release method Included in this versionof Windows Drivers using it run on1.19Windows 10, version1607 WDK Windows 10, version1607Windows 10 version1607 and later,Windows Server 20161.17Windows 10, version1511 WDK Windows 10, version1511Windows 10 version1511 and later,Windows 10 Mobile,Windows 10 IoT Core,Windows Server 20161.15Windows 10 WDK Windows 10Windows 10 for desktop editions, Windows 10 Mobile, IoT Core, Windows Server 2016UMDF version Release method Included in this version ofWindows Drivers using it can run on2.19Windows 10, version 1607WDK Windows 10, version 1607Windows 10, version 1607 (all SKUs), Windows Server 20162.17Windows 10, version 1511WDK Windows 10, version 1511Windows 10 for desktop editions (Home, Pro, Enterprise, and Education), Windows 10 Mobile, Windows 10 IoT Core (IoT Core), Windows Server 20162.15Windows 10 WDK Windows 10Windows 10 for desktop editions, Windows 10 Mobile, IoT Core, Windows Server 2016驱动程序运行在哪个版本的操作系统上驱动程序支持的硬件类型驱动程序使用的驱动模型确定驱动程序是否使用了只有KMDF支持的功能,如果驱动程序没有使用KMDF的功能,并且驱动运行在Windows 8.1或以后的系统上,则可以迁移到UMDF 2https:///en-us/windows/hardware/drivers/wdf/wdf-porting-guide Which Drivers Can Be Ported and WhereDifferences Between WDM and WDFPreparing for PortingSteps in PortingSummary of KMDF and WDM Equivalents。
Windows应用程序的基本结构

Windows程序的特点
大致说来windows编程有两种方法: a.windows c方式(SDK),SDK编程就是直接调用windows 的API进行编程; b.c++方式:即对SDK函数进行包装,如VC的MFC,BCB的 OWL等。MFC把这些API封闭起来,共有一百多个类组成. API,全称application program interface,意思是应用程序编 程接口(说起API并不仅仅指windows而言, windows支持 的API叫winapi)。winapi就是应用程序和windows之间通讯 的一个编程界面。windows提供了上千个API函数,以方便程 序员来编写应用程序。
WinMain函数的功能
WinMain函数 功 能 注册窗口类,建立窗口及执行必要的初始化 进入消息循环,根据接受的消息调用相应的处理过程 当消息循环检索到WM_QUIT时终止程序运行
三个基本的组成部分:函数说明、初始化和消息循环 WinMain函数说明
注意!Win是多任务管理的 ,同一应用程序的多个窗口 可能会同时存,Win系统对每 个窗口的执行称为一个实例, WinMain函数的说明如下: 并用一个实例句柄来唯一标 识 int WINAPI WinMain
HDC 设备环境句柄 HCURSOR 光标句柄 HFONT 字体句柄 HPEN 画笔句柄 HBRUSH 画刷句柄
Windows程序的特点
窗口句柄: 系统通过窗口句柄来在整个系统中唯一标识一个窗口,发送一个消息 时必须指定一个窗口句柄表明该消息由那个窗口接收。而每个窗口都 会有自己的窗口过程,所以用户的输入就会被正确的处理。 所有的命名采用了匈牙利表示法。如消息的前缀使用msg.句柄使用h. 函数使用fn等。 Windows程序则至少两个主程序, 一个是WinMain(), int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state );
驱动基础知识

设备驱动程序种类
虚拟设备驱动程序,被用于仿真 16位MS-DOS应用程序 用户模式驱动程序 子系统打印机驱动程序,把与设备无关 的图形请求转译成与打印相关的命令
文件系统驱动程序,接受针对文件操作的请求
内核模式驱动程序
即插即用驱动程序,与硬件一起工作,结合了 电源和PNP管理器
非即插即用驱动程序,
文件系统驱动程序和磁盘驱动程序的分层结构
应用程序调用 Kernel32.dll中 的FindNextFile Kernel32.dll中 的FindNextFile
Ntdll.dll中的 NtQueryDirectory SYSTEM 或 INT 2E 用户模式 内核模式
KiSystemService
Ntoskrnl.exe中的 NtQueryDirectoryFile
环境子系统或DLL
1 NtWriteFile(file_handle, char_buffer)
系统服务
2 在一个文件中指定的偏移处 写入数据
文件系统驱动程序 I/O管理器 3 将相对于文件的字节偏移转 译成相对于卷的字节偏移, 并调用下一个驱动程序
磁盘驱动程序
4 调用驱动程序以便在相对于 卷的字节偏移处写数据 5 将相对于卷的字节偏移转译 成物理位置的磁盘偏移,并 传送数据
驱动程序对象
• DeviceObject(PDEVICE_OBJECT) 指向一 个设备对象链表,每个设备对象代表一个设备。 I/O管理器把多个设备对象连接起来并维护这 个域。非WDM驱动程序的DriverUnload函数 利用这个域来遍历设备对象列表,以便删除其 中的设备对象。WDM驱动程序没有必要使用 这个域。 FastIoDispatch(PFAST_IO_DISPATCH)指 向一个函数指针表,这些函数是由文件系统和 网络驱动程序输出的。 DriverStartIo(PDRIVER_STARTIO)指向驱动 程序中处理串行I/O请求的函数,I/O管理器自 动为驱动程序串行化多个I/O请求。 DriverUnload (PDRIVER_UNLOAD)指向驱 动程序中的清除函数。 MajorFunction (array of PDRIVER_DISPATCH)是一个函数指针表, 指向存在于驱动程序中的二十多种IRP处理函 数。
第17讲 Windows程序设计基础

1. 2.
主菜单 弹出式菜单
菜单类
在.NET类库中,定义了一个非常庞大的基础类, 以供用户在各种编程中使用。在这些类中,有几个专 为用户编写菜单程序所用的类,通过使用这几个类, 你可以设计出自己想要的任何菜单。下面主要介绍三 个类:
1. 2. 3.
MenuStrip ContextMenuStrip ToolStripMenuItem
Windows应用程序框架
Windows程序运行机制 Windows程序设计是一种事件驱动方式的程 序设计模式。在程序提供给用户的界面中有许 多可操作的可视对象。用户从所有可能的操作 中任意选择,被选择的操作会产生某些特定的 事件,这些事件发生后将会向程序中的某些对 象发出消息,然后这些对象调用相应的消息处 理函数来完成特定的操作。
1.
在“工具箱”中双击ToolTip为窗体添加一个ToolTip, 此时窗体上所有控件的属性里都多了一个“toolTip1 上的ToolTip”属性,它可使控件与toolTip1关联起来 以创建工具提示。
高级控件
本节主要讲述以下几个主要高级控件的应用: NumericUpDown控件 ProgressBar控件 ListView控件 TreeView控件 Splitter控件 TabControl控件
ListView(列表视图)
ListView用列表的形式显示一组数据,每条数据都 是一个ListItem类型的对象。通常使用ListView来显示 对数据库的查询结果,Windows系统中的文件浏览器就 是一个ListView控件。 【例】ListView控件应用 View属性表示显示数据的视图模式,它包含如下4个选项:
Windows程序运行机制 2、事件驱动程序运行 事件指的是独立的偶发的事情,在计算机中,可以 通过单击鼠标、单击按钮、操作键盘产生事件,也可以 通过程序的控制而产生,甚至可以由另一个窗口的操作 产生。 在Windows程序中,可以对相应的事件编写对应 的响应函数,当该事情发生时,响应函数就会被调用。
windows文件核心驱动结构简介、FileMon例程简介、开发注意要点

本帖子是《注册表实时监控拦截》的下篇,也是本系列的最后一篇。
主要讲述如何自己动手做一个在windows系统下的文件操作拦截的小驱动。
利用本驱动,可以实现对本机的所有文件操作请求进行实时监视、拦截,可以完全保护文件系统,在此基础上的进行深入开发后,可以做到文件加密、病毒防护等功能。
本帖子分三个部分:windows文件核心驱动结构简介、FileMon例程简介、开发注意要点。
一、windwos文件核心驱动结构简介在windows操作系统中(NT以上版本),规定了一套严格的文件操作请求处理流程,总体结构如下图:在图中上部分有一条虚线,这是“用户态”软件和“核心驱动”的分界线(用户态软件可以理解为普通的、可见的软件,如各类exe类型的软件)。
当用户态软件需要操作文件时,则发出文件操作请求,然后统一发送给系统的I/O管理器,I/O管理器把这个操作请求依次向下传递给“文件系统驱动”、“磁盘驱动”等,后者执行真正的文件操作。
上图是在本机上进行文件操作的流程,那么在访问网络上的文件,则处理流程如下:可以看到,在目标计算机(即文件所处的计算机)上,文件操作请求同样经过了“本地文件系统”和“磁盘驱动”,在这一点上,两者没有区别。
那么,如果我们要自己做一个文件的驱动,应该放在什么位置?下图给出了详细的“自定义驱动”的插入位置:图中以深色表示的部分,即留给用户可以插入“自定义驱动”的位置,可以看到,新插入的驱动和系统的驱动是“串接”在一起的。
所以,所有的文件操作均会通过“自定义驱动”(当然了,如果有人又开发了另外一个驱动,并插入到你所开发的驱动的下面,然后不通过正规的文件驱动而是直接发送消息到下层他的驱动,这样是拦截不到的)。
此时,你可以在你的驱动中很悠闲地处理这些请求,而且想啥时候丢弃、修改这些文件请求,那是全凭自己的心情了。
二、FileMon例程简介在了解了windows文件核心驱动的大致结构后,我们可以动手来试试了。
WindowsDriverFoundationWindows驱动程序基础

Windows Driver FoundationWDF简介WDFSTA TUS DioEvtDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit){// ...//// Initialize our idle policy//WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings,IdleCannotWakeFromS0);status = WdfDeviceUpdateS0IdleSettings(device, &idleSettings);if (!NT_SUCCESS(status)) {DbgPrint("WdfDeviceUpdateS0IdleSettings failed 0x%0x ", status);return status;}return(status);}WDF亮点WDF亮点主要是针对新的 PnP/Power 模型的进一步优化和调整、可放弃的驱动创建(例如在 Longhorn 中可以中断并放弃 IRP_MJ_CREA TE 操作)、新的存储架构、灵活的任务请求队列(支持串行、并行和定制任务分发)、以及一些其他细节方面的改进。
在驱动模型方面,WDF 使用一些新的类型驱动 WDM 的相应类型,并做了一定的扩展:WDF 类型 WDM 类型WDFDRIVER DRIVER_OBJECTWDFDEVICE DEVICE_OBJECTWDFREQUEST IRPWDFQUEUE DPC 队列WDFINTERRUPT ISR & DPCforISRDriverEntry 在 WDF 中只负责 WDFDRIVER 类型对象的初始化和构造工作,将设备的管理完全丢到 DioEvtDeviceAdd 函数中,由 WDF 框架在合适的时候调用。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
标题: 【原创】Windows驱动程序框架windows驱动程序入门比较坑爹一点,本文旨在降低入门的门槛。
注:下面的主要以NT式驱动为例,部分涉及到WDM驱动的差别会有特别说明。
首先,肯定是配置好对应的开发环境啦,不懂的就百度下吧,这里不再次描述了。
在Console控制台下,我们的有一个入口函数main;在Windows图形界面平台下,有另外一个入口函数Winmain。
我们只要在这入口函数里面调用其他相关的函数,程序就会按照我们的意愿跑起来了。
在我们用IDE开发的时候,也许你不会发现这些细微之处是如何配置出来的,一般来说我们也不用理会,因为在新建工程的时候,IDE已经帮我们把编译器(Compiler)以及连接器(Linker)的相关参数设置好,在正式编程的时候,我们只要按照规定的框架编程就行了。
同样,在驱动程序也有一个入口函数DriverEntry,这并不是一定的,但这是微软默认的、推荐使用的。
在我们配置开发环境的时候我们有机会指定入口函数,这是链接器的参数/entry:"DriverEntry"。
入口函数的声明代码:DriverEntry主要是对驱动程序进行初始化工作,它由系统进程(System)创建,系统启动的时候System系统进程就被创建了。
驱动加载的时候,系统进程将会创建新的线程,然后调用执行体组件中的对象管理器,创建一个驱动对象(DRIVER_OBJECT)。
另外,系统进程还得调用执行体组件中的配置管理程序,查询此驱动程序在注册表中对应项。
系统进程在调用驱动程序的Driv erEntry的时候就会将这两个值传到pDriverObject和pRegistryPath。
接下来,我们介绍下上面出现的几个数据结构:typedef LONG NTSTATUS在驱动开发中,我们应习惯于用NTSTATUS返回信息,NTSTATUS各个位有不同的含义,我们可以也应该用宏NT_SUCCESS来判断是否返回成功。
代码:NTSTAUS的编码意义:其中Ser是Serviity的缩写,代表严重程度。
00:成功01:信息10:警告11:错误C是Customer的缩写,代表自定义的位。
Facility:设备位Code:设备的状态代码。
根据这定义编码,还有补码的概念,那么只要是错误的时候,最高位就是1,NTSTATUS的值就是负数,所以可以大于零来判断,但无论如何都希望读者用NT_SUCCESS宏来判断是否成功,因为这可能在以后会有所改动,即使这么多年来都一直沿用着。
同样的,微软也为我们定义了其他几个判断宏:代码:有了之前的介绍,这三个相信不说大家也能领会了。
但最常用的还是NT_SUCCESS。
我们继续说其他的两个数据结构,先说PUNICODE_STRING吧,P代表这是一个指针类型,指向一个UNICODE_STRING结构。
宽字符串结构体(UNICODE_STRING)代码:其中,ØLength:Unicode字符串当前的字符长度。
注意不是字节数,每个Unicode字符占用两个字节。
ØMaximumLength:该Unicode字符串的最大容纳长度。
ØBuffer:Unicode字符串的缓冲地址。
UNICODE_STRING是Windows驱动开发里面经常用到的一个结构,用Length来标记字符串的长度而不再用\0来表示结束。
可以用RtlInitUnicodeString来对其初始化,但这里的pRegistryPath是直接由创建驱动程序的线程传进来的参数,如果在接下来仍需要用到该值,最好是使用RtlCopyUnicodeString函数将其值另外保存下来,因为这个字符串并不是长期存在的,DriverEn try函数返回的时候可能就会被销毁了。
PDRIVER_OBJECT,P代表这是一个指针类型,指向一个驱动对象(DRIVER_OBJECT),每个驱动程序都有一个驱动对象。
这是一个半透明的数据结构,微软没有公开它的完全定义,只是有提到几个成员,但我们依旧可以通过WinDbg看到它的定义,只是不同的系统可能会存在不同的结构。
不过我另外在WDK的头文件WDM.h里面发现了它的定义:驱动对象(DRIVER_OBJECT)代码:这里提下几个比较重要的字段,ØDeviceObject:指向由此驱动创建的设备对象。
每个驱动程序都会有一个或多个的设备对象。
其中,每个设备对象都会有一个指针指向下一个设备对象,这在我们介绍设备对象的时候再继续说。
ØDriverName:驱动的名字,该字符串一般为\Driver\[驱动程序名称]。
ØHardwareDatabase:记录设备的硬件数据库键名。
该字符串一般为"\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services \[服务名]"。
ØFastIoDispatch:指向快速I/O函数入口,是文件驱动中用到的排遣函数。
ØDriverStartIo:记录StartIo例程的函数地址,用于串行化操作。
ØDriverUnload:指定驱动卸载时所用的回调函数地址。
ØMajorFunction:这是一个函数指针数组,每个指针指向的是一个函数,该函数就是处理相应IRP的排遣函数,数组的索引值与IRP_MJ_XXX相对应。
我们已经了解了DriverEntry函数头的那个数据结构了,但这还不够,在DriverEntry里,我们主要是对驱动程序进行初始化,这就涉及到其他的一些数据结构了,下面我们继续逐一地介绍。
设备对象(DEVICE_OBJECT)代码:这里只对几个比较重要的字段进行说明:ØDriverObject:指向创建此设备对象的驱动程序对象。
同属于一个驱动程序的设备对象指向的是同一个驱动对象。
ØNextObject:指向同一个驱动程序创建的下一个设备对象。
同一个驱动对象可以创建若干个设备对象,每个设备对象根据N extDevice来连成一个链表,最后一个设备对象的NextDevice域为NULL。
ØAttachedDevice:指向附加到此设备对象之上的最近设备对象。
这里需要理解分层驱动程序的概念。
ØDeviceExtension:指向设备的扩展对象。
每个设备都会指定一个设备扩展对象,这个数据结构由驱动程序开发者自行定义,可以用来记录一些与设备相关的一些信息,同时应尽量避免使用全局变量,将数据存放在设备扩展里,具有很大的灵活性。
ØCurrentIrp:在使用StartIO例程的时候,该成员指向的是当前IRP结构。
ØFlags:指定了该设备对象的标记。
下面列出了常用的几个标记:ØDeviceType:指定设备的类型。
一般在开发虚拟设备时,选择FILE_DEVICE_UNKNOW。
其他的请自行参考WDK文档。
ØStackSize:在多层驱动情况下,驱动与驱动之间会形成类似堆栈的结构,称之为设备栈。
IRP会依次从最高层传递到最底层。
StackSize描述的就是该层数。
最底层的设备层数为1。
ØAlignmentRequirement:在进行大容量传输的时候,往往需要进行内存对齐,以保证传输速度。
请使用类似FILE_XXX_AL IGNMENT的方式进行赋值。
下面给大家展示一下DriverEntry的最基本框架:代码:这是用C++写的,所以必要的地方加上了extern“C”,否则会引起一些错误,这是因为C++与C在进行名称粉碎的时候处理得不一样,C++这个改进主要是为了实现一些高级功能,比如多态。
虽然加上extern “C”会有点麻烦,但可以用上C++那些功能,个人觉得也有所值。
如果用C,直接忽略上面的extern “C”。
NTDDK.h是NT式驱动需要加载的头文件,如果是WDM式驱动,那么加载的是WDM.h#define INITCODE code_seg("INIT")定义一个宏,#prama INITCODE还原后就是#pramacode_seg(“INIT”),表示接下来的代码加载到INIT内存区域中,成功加载后,可以退出内存。
对于DriverEntry这种一次性的函数而言,这是最适合的选择,可以节省内存。
函数结束后需要显式地切换回来,如:#prama LOCKEDCODE。
同样,PAGECODE表示分页内存,作用是将此部分代码放入分页内存中运行,在里面的代码切换进程上下文时可能会被换回分页文件。
LOCKEDCODE表示默认内存,也就是非分页内存,里面的代码常驻内存。
IRQL处于DISPATCH_LEVEL或者以上的等级,必须处于非分页内存里面。
同理,对于数据段也有同样的机制,于是有了PAGEDATA、LOCKEDDATA、INITDATA。
KdPrint是一个宏,在调试版本(Checked)里面(具备DBG宏定义),有代码:而在正式版本(Free)里面,KdPrint被定义为空。
所以可以用来作为调试输出。
但注意Kdprint后面是两层括号,用法与C语言运行库的printf差不多。
pDriverObject->DriverUnload = UnloadRoutine;将卸载例程函数告诉驱动对象,驱动对象在前面已经有定义,这里不做深入讨论。
pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchRoutine;注册排遣例程。
Windows是消息驱动,而驱动程序是IRP驱动的,I/O管理器将发送到驱动的“消息”封装在IRP里面,驱动程序也将结果告诉IRP。
类似windows的消息机制,对于不同的“消息”,驱动程序需要注册不同的处理例程来区别对待,当然也可以放在同一个例程里面,然后用sw itch语句来区别对待,但当处理过程比较长的时候,会比较凌乱。
IRP_MJ_CREATE是当RING3应用程序在使用CreateFile函数建立与驱动程序的通信通道时所激活的。
IRP_MJ_READ是ReadFile,IRP_MJ_WRITE是WriteFile,而IRP_MJ_CLOSE是CloseHandle关闭文件句柄的时候产生的。
小知识:对于WDM式驱动,仍需要注册AddDevice例程,pDriverObject->DriverExtension->AddDevice = WDMAddDeviceRou tine,设备对象的初始化将在AddDevice里面进行而不是DriverEntry。
另外还需要注册IRP_MJ_PNP排遣函数。