310 likes | 566 Views
第五章 设备管理. 设备管理 --- 概述. Windows I/O 系统概述 I/O 系统结构 Windows 驱动程序 I/O 系统内核对象 I/O 请求和处理. Windows I/O 系统概述. I/O 系统: 负责管理输入输出设备。 向用户隐藏硬件细节。. I/O 系统结构. I/O 系统组成 I/O 管理器 设备驱动程序 PnP 管理器 电源管理器 WMI 支持例程 注册表 硬件抽象层( HAL ). Windows 驱动程序. 驱动程序的功能 发送控制命令,进行错误处理
E N D
设备管理---概述 • Windows I/O系统概述 • I/O系统结构 • Windows驱动程序 • I/O系统内核对象 • I/O请求和处理
Windows I/O系统概述 • I/O系统: 负责管理输入输出设备。 向用户隐藏硬件细节。
I/O系统结构 • I/O系统组成 • I/O管理器 • 设备驱动程序 • PnP管理器 • 电源管理器 • WMI支持例程 • 注册表 • 硬件抽象层(HAL)
Windows驱动程序 • 驱动程序的功能 • 发送控制命令,进行错误处理 • 对各种可能的有关设备排队、挂起、唤醒等操作进行处理 • 执行缓冲区策略 • 执行一些特殊处理,比如代码转换。 • 驱动程序的分类 • 用户态驱动程序:虚拟设备驱动程序和Windows子系统打印机驱动程序 • 核心态驱动程序:文件系统驱动程序 、即插即用驱动程序 、非即插即用驱动程序 • 其他内核态驱动程序:WDM驱动程序、分层的驱动程序
Windows驱动程序--续 驱动程序分类图:
Windows驱动程序--续 • 驱动程序的基本结构 • 初始化例程 • “添加-设备”例程 • 分发例程 • 启动I/O例程 • 中断服务例程 • DPC例程
I/O系统内核对象 • 文件对象 文件对象代表一个文件、设备或目录的打开实例。Windows系统将所有的设备都当成文件,所以文件对象就代表这个设备的设备对象。 • 驱动程序对象 • 驱动程序代表 系统中的一个 独立的驱动程序 • 驱动程序对象结构
I/O系统内核对象--续 • 设备对象和设备扩展 • 设备对象 代表一个具体的物理设备。 • 设备扩展 包含与特定设备相关的数据。 • 设备对象中包含一个指向对应驱动程序对象的指针,这样I/O管理器就能在接收到一个I/O请求时应该调用哪个驱动程序来处理该I/O请求。 • “下一个设备对象”指针指向属于同一个驱动程序的下一个设备对象,该域把多个设备对象连接起来。
I/O系统内核对象--续 • 对象之间的关系 • 文件对象指向一个打开的设备实例,每当一个线程打开一个文件或设备时,都用一个文件对象指向对应的设备对象 • 驱动程序对象有多个与他相关的设备对象,这样就能方便的实现对硬件设备的控制。 • 设备对象还有一个指针指向该驱动程序对象,I/O管理器在接收到一个I/O请求时就知道该调用哪个驱动程序。
I/O请求和处理 • I/O请求包 I/O系统使用一个I/O请求包(IRP,I/O Request Packet)表示每个I/O请求。当线程调用I/O服务时,I/O管理器就为该请求创建一个IRP数据结构,同时为该IRP传递一个指向相应驱动程序的指针。当驱动程序接收到一个IRP包时,执行IRP指定的操作,操作完成后将该IRP传递给I/O管理器。
I/O请求和处理--续 • I/O请求处理 • 请求处理过程: • I/O管理器接收用户发来的请求,并为该请求分配一个IRP数据结构。 • 检查I/O请求的合法性,并将IRP传递给合适的驱动程序。 • 驱动程序根据IRP的参数设置对设备进行操作。 • 操作完成时,驱动程序将IRP传递给I/O管理器。 • I/O管理器检查IRP的状态域,查看用户的I/O请求是否完成。
I/O请求和处理--续 • I/O请求处理过程图
设备管理---实验 WDM驱动程序 驱动程序实例 驱动程序加载
WDM驱动程序 • WDM是一个分层的驱动程序模型。在该模型中,驱动程序的层和堆栈一起工作处理I/O请求。
WDM驱动程序结构 可以把一个完整的驱动程序看作一个容器,它包含许多例程,当操作系统遇到一个IRP时,它就调用这个容器中的例程执行该IRP的各种操作。 驱动程序一般都有几个支持不同类型IRP的派遣函数,因此WDM驱动程序开发者的一个任务就是为这个容器选择所需要的例程。 WDM驱动程序--续
WDM驱动程序--续 • DriverEntry例程 DriverEntry是内核模式驱动程序主入口点函数,大部分的设备初始化工作都是在这个例程中完成的。函数原型如下: NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); 函数返回值是一个长整型的NTSTATUS值
WDM驱动程序--续 DriverEntry函数的主要工作有一下几个方面: • 首先是为驱动程序指定派遣函数。 • 每个WDM驱动程序必须能处理PNP、POWER、SYSTEM_CONTROL这三种请求;应该在这里为这些请求指定派遣函数。 • 在省略号处,你可以插入设置其它MajorFunction指针的其他代码,比如IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_WRITE等。 • 如果驱动程序需要访问设备的服务键,可以在这里备份RegistryPath串。 • 返回STATUS_SUCCESS说明函数成功。如果失败,应该返回NTSTATUS.H中的一个错误代码,或者返回用户定义的错误代码。STATUS_SUCCESS的值为0。
WDM驱动程序--续 • AddDevice例程 DriverEntry例程只能在驱动程序第一次被装入时执行一次,但是一个驱动程序可以被多个实际的设备利用,所以WDM驱动程序有一个特殊的AddDevice函数,PnP管理器为每个设备实例调用该函数。 函数原型为: NTSTATUS AddDevice ( PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo )
WDM驱动程序--续 对于功能驱动程序,其AddDevice函数的基本职责是创建一个设备对象并把它连接到以pdo为底的设备堆栈中。相关步骤如下: • 调用IoCreateDevice创建设备对象,并建立一个私有的设备扩展对象。 • 寄存一个或多个设备接口,以便应用程序能知道设备的存在。另外,还可以给出设备名并创建符号连接。 • 初始化设备扩展和设备对象的Flag成员。 • 调用IoAttachDeviceToDeviceStack函数把新设备对象放到堆栈上。
WDM驱动程序--续 • 分发例程 分发例程是一个设备驱动程序所提供的主要函数。可以实现设备的打开、关闭、读、写、电源管理、系统控制等功能。 分发例程主要包括以下几个: • 启动I/O例程 • 中断服务程序(ISR) • 中断服务的DPC例程 • 一个或者多个I/O完成例程 • 取消I/O例程 • 系统停机通知例程 • “错误-记录”例程
WDM驱动程序--续 所有的分发函数都有一个统一的函数原型: NTSTATUS dispatch_function( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { //添加函数处理代码 return STATUS_SUCCESS; }
WDM驱动程序--续 • Unload例程 DriverUnload例程的作用是释放DriverEntry例程在全局初始化过程中申请的任何资源,但它几乎没什么可做。如果你在DriverEntry中备份了RegistryPath串,应该在这里释放备份所占用的内存: VOID DriverUnload (PDRIVER_OBJECT DriverObject) { RtlFreeUnicodeString(&servkey); } 如果DriverEntry例程返回一个失败状态代码,系统将不再调用DriverUnload例程。因此不能让DriverEntry例程出错后产生任何副作用,必须在它返回错误代码前消除副作用。
驱动程序实例 • 源代码组成 HelloWorld驱动程序的源代码由四个文件组成,分别是HelloWorld.c,HelloWorld.h,makefile和source,这四个文件存放在HelloWorld文件夹里,下面分别介绍这四个文件的内容。 • HelloWorld.h HelloWorld.h文件主要工作是定义一些宏,和进行函数原型声明。
驱动程序实例--续 • HelloWorld.c 该文件包含三个函数,分别DriverEntry(),HelloWorldDispatch(),HelloWorldUnload()。 • DriverEntry()是驱动程序的入口函数 • HelloWorldDispatch()是分发函数,负责实现驱动程序的读写、打开、关闭等操作; • HelloWorldUnload()是驱动程序的卸载函数 • MAKEFILE文件和SOURCE文件 • 对于所有的驱动程序,MAKEFILE文件都是一样的 . • SOURCE文件包含一些宏,用以指导BUILD工具如何生成驱动程序可执行文件,从哪里获得输入以及在哪里进行输出。
驱动程序加载 • 驱动安装过程分析 加载和运行一个服务需要执行的典型操作步骤有: • 调用OpenSCManager()打开服务控制管理器,获取管理器句柄。 • 调用CreateService()创建一个服务,服务类型为内核驱动。 • 调用OpenService()取得服务句柄。 • 调用StartService()启动服务。 • 调用CloseServiceHandle()关闭服务句柄。
驱动程序的安装函数 执行流程图 当调用CreateService创建服务时,系统调用NtLoadDriver函数执行驱动程序的加载操作,但是NtLoadDriver只做一些判断,具体的加载工作由IopLoadUnloadDriver函数完成,所以上图中有一个从CreateService到NtloadDriver的调用,还有一个从NtLoadDriver到IopLoadUnloadDriver的调用路线,下面会介绍IopLoadUnloadDriver所做的具体工作。当调用StartService启动服务时,系统会调用驱动程序的入口点函数,进行一些初始化操作。 驱动程序加载--续
驱动程序加载--续 • 关键代码分析 NtLoadDriver( )函数在 \wrk-v1.2\base\ntos\io\iomgr\loadunld.c中定义,函数原型如下: NTSTATUS NtLoadDriver ( __in PUNICODE_STRING DriverServiceName ); • 参数:DriverServiceName是要加载的驱动程序在注册表中的名称。这是一个UNICODE_STRING类型的参数,指定要加载的驱动程序。 • 返回值:成功操作结束后返回操作的状态码,失败则返回NULL。
驱动程序加载--续 NtLoadDriver() 函数执行流程图
驱动程序加载--续 • 实验目的 验证驱动程序的动态加载过程。 • 实验原理 验证用户态应用程序在动态加载驱动程序时对NtLoadDriver ( )函数的调用过程。 • 实验环境 调试工具:Windbg 编程工具:Visual Studio 2005 操作系统:VMware 5.0+Windows Server 2003 • 实验内容 利用Visual Studio编写一个loadsys函数实现驱动程序的动态加载,然后编写一个程序test001,对驱动程序的功能表现进行测试。
驱动程序加载--续 • 实验过程 • 步骤1:修改源代码 根据关键代码分析部分的结果, 对\wrk-v1.2\base\ntos\io\iomgr\loadunld.c中的NtLoadDriver 函数添加注释信息 • 步骤2:重新编译WRK内核 • 步骤3:加载驱动程序 • 步骤4:卸载驱动程序