阅读:2807回复:2
[原创]Windows驱动学习感悟
author: jonathan
/*-----------------------------------------------------------------------------------------------------*/ 好久没有来驱网了,看到在举办活动,也来说说自己学习Windows驱动的感受。此文对稍微有经验的初学者可能有些帮助,帮助您拨开迷雾看到真相,呵呵。仓促之作,有错误还请谅解! 0 前言 0.1 说在前面的话 机械设备的使用是为了提高工作效率,实现人无法手工完成的想法,即机械设备是一种工具。 计算机也是一个工具。学习操作系统和驱动,是为了在现有的系统上更好使用这个工具。 学习的过程是一个被动的过程。学习的目的是为了更好主动使用工具,使用这个工具完成更多的工作。 学习的内容:控制机械设备的原理,操作系统原理,某一具体操作系统的原理 0.2 分层结构设计 学习操作系统内容真的是很多,这里就以从操作系统的实现分层设计模式为例做一个简单的说明。 Windows/Linux操作系统都实现了分层设计模式,这是一个把复杂任务分解为多个单一功能组件来共同完成一项任务的比较成功的实现方式(他的引申设计模式是管道设计模式),类似生产车间的流水线(一切事物都是想通的,要学会用哲学的角度来分析问题、解决问题,呵呵). 0.2.1 分层模式条件 每层模块暴露出接口,供其他模块使用 各层次之间流转数据流 上层模块的接口对下层模块是不可见的,实现数据流从上向下流转。 上层模块可以调用下层模块接口,使用下层模块提供的服务 使用特定技术实现数据流从下向上流转 0.2.2 Windows/Linux两种分层模式的不同 从0.2.1可以知道,上层模块的接口对下层模块是不可见的。那么如何达到数据的双向流动呢?对,简单说使用回调技术达到数据流从下向上流转。 回调实现的方式很多种,看看Windows/linux不同点: Windows: Windows操作系统使用动态回调技术:其把回调函数注册在数据包IRP里面,从而实现动态回调。为什么是动态呢?因为该回调根据情况可以随时更改。 Linux:Linux操作系统通过模块注册接口技术:下层模块维护上层回调模块链表,上层模块初始化时注册相应的回调到下层模块。这样,在下层模块得到数据后,就可以调用相应的上层模块的回调函数上传数据。 两种方式各有优缺点,具体就不再描述(http://hi.baidu.com/jonathan2004/blog/item/bb219b0b6f8228296a60fb4b.html)。 通过学习,在系统设计时,就可以借鉴并且发展使用这两种方式。 0.2.3 操作系统是什么 http://hi.baidu.com/jonathan2004/blog/item/40c9b550a32bed030cf3e333.html Windows内核运行的环境一般分为三种:硬中断上下文、软中断上下文、进程上下文。 1 驱动Driver结构 Driver结构分为两大类:数据和例程 1.1 Driver关键例程 初始化例程(一般为DriverEntry)、添加设备例程(add-device)、一组分发例程、Start I/O例程、中断例程(ISR)、中断服务例程(DPC) 1.2 Driver辅助例程 完成例程、取消IO例程、Fast分发例程、卸载例程、系统关闭通知例程、错误日志例程 2 Driver与Device之间的关系 Device Object可以看作是物理或者虚拟设备的代表;而Driver Object则为Device Object实现具体的功能。 重点注意:对内核的操作,具体是对设备对象来操作(见操作树),而非Driver Object。 3 关于设备对象名字 应用层通过设备名字来操作设备。对于不同设备命名方式有些不同: 对于非PnP驱动或者文件系统驱动,一般使用很好记的名字来创建设备对象的名字。但这类名字不适合对真实的设备命名。 对于真实设备通过IoRegisterDeviceInterface来注册GUID来代表设备。 4 File Object 、Device Object 或者 Driver Object之间关系 File Object --> Device Object / File,即文件对象是一个内存对象,代表了设备或者文件的接口。通过这个接口来读写设备或者文件。 4.1 打开对象/文件 (1) 应用层通过名字来打开文件,如c:\1.txt. (2) 经过应用层到内核的名字转化(具体略),最后形成\\??\\c:\\1.txt. (3) 通过ObpLookupObjectName查找\\??\\c:名字的对象,找到真实设备。 (4) 根据真实设备通过VPB找到文件系统设备栈的顶层设备。 (5) 根据\\1.txt名字调用文件系统的IRP_MJ_CREATE创建打开文件,返回FileObject. 过程:fopen->CreateFile->NtCreateFile->sysentry/int 2e->KeServiceDescriptorTable/KeServieDescriptorTableShadow->NtCreateFile->IoCreateFile -> .... 具体见http://hi.baidu.com/jonathan2004/blog/item/e226b22976afeef7e6cd4092.html。 4.2 读写对象/文件 通过FileObject这个内存对象接口,调用IoGetRelatedDeviceObject得到设备栈顶层设备,执行IRP_MJ_READ/WRITE/CONTROL等分发例程。 4.3 关闭对象/文件 通过FileObject这个内存对象接口关闭对象。 具体见http://hi.baidu.com/jonathan2004/blog/item/6c21b0c8a4d8478bc917683a.html 5 Windows对象树建立 |->ACPI风扇 虚拟总线驱动Root->主总线遍历器(ACPI)->系统主总线(PCI)->.... |->... 注意:ACPI遍历总线依据是安装Windows系统过程记录在注册表里面的总线信息。 下面简单描述一下Windows启动过程,对象如何建立树的。 5.1 收集硬件信息 开机->bios->mbr->扇区引导代码->ntldr(实模式)->ntldr(os loader)->ntdectect.com(收集基本硬件信息)->os load 加载 ntoskrnl.exe, hal.dll, SYSTEM注册信息, 引导 -启动的驱动模块. 最后os load完成加载并形成LOADER_PARAMETER_BLOCK数据结构传递给ntoskrnl.exe,并把控制权ntoskrnl.exe 5.2 建立设备树 在LOADER_PARAMETER_BLOCK中包含了基本硬件信息,其中包括了bus等信息。 ntoskrnl.exe获得控制权后,进行内核初始化,并且在I/O系统初始化过程中设备列举。 根设备: \Driver\PnpManager实现一个无名设备对象Root。 注意:\Driver\PnpManager这个对象是通过IoCreateDriver创建而非IopLoadDriver,是一个无映像文件的驱动对象。 支持即插即用的设备列举: 在安装Windows系统过程中,收集了硬件信息,并记录在SYSTEM注册表中\Control\Class中的硬件设备信息。 计算机启动后,从ntldr传递的LOADER_PARAMETER_BLOCK中包含了硬件信息。从BUS开始,内核读取\Control\Class\{4D36E97D-E325-11CE-BFC1-08002BE10318}信息以及其下的 注册的设备,开始了一步步遍历。 Root->ACPI HAL->ACPI->PCI->... 不支持即插即用的设备列举: 都挂载Root下。 总结: 应用层通过句柄Handle操作设备/文件->Handle到FileObject映射->定位到FileObject代表的设备Device Object->获取DeviceObject设备栈顶层设备->通过IRP在各层次之间专递数据->直到FileObject代表的设备对象->设备对象相应的Driver Object来操作IRP->操作完成后回调反回->应用层 以上描述重点是进程上下文的内容。 6 中断处理机制 现在所有的操作系统中对中断处理分为两类:线性化中断(SOLARIS)和非线性化中断(Windows/Linux)。 6.1 线性化中断 这里简单描述,不做重点描述。 线性化中断几个特点: 中断处理内可睡眠;中断和普通线程一样参与调度;中断线程使用全局最高优先级。 6.2 非线性化中断 非线性化中断几个特点: 中断不可睡眠;硬件睡眠可中断软中断;中断不可调度 6.2.1 中断为何不可调度 调度器都是基于对象来保存上下文信息,以便再次调用返回现场。由于硬/软中断没有对应的对象保存上下文,所以无法参与调度器来调度。 6.2.2 中断分级划分 Windows/Linux对中断都划分为硬中断和软中断。硬中断要求快速处理,而软中断需要处理硬中断未处理的后续任务。 Windows和Linux关于硬中断和软中断处理方式是不同的。由于这里以Windows为重点描述,不过先简单描述一下Linux中断处理过程。 Linux:硬件中断产生->do_IRQ->request_irq注册的ISR->irq_exit->do_softirq开始软中断 schedule->do_softirq开始软中断 syscall->do_softirq开始软中断 Linux中断有很多内容,特别是关于spin_lock,spin_lock_bh,spin_lock_irq,spin_lock_irqsave等锁的使用更是重要,需要不同环境使用不同的锁。我曾经没有有效使用锁,不断造成所的睡眠情况,花了我3个小时来排查错误,很头痛的。这里就不再描述了。 6.2.3 Windows中断 Windows中断的硬中断通过ISR来完成,软中断通过DPC来完成任务。 Windows分为三个软中断等级:DISPATCH_LEVEL,APC_LEVEL,PASSVIE_LEVEL。这里重点描述DPC,其他两个就不再描述。 DPC_LEVEL会阻塞APC_LEVEL,PASSVIE_LEVEL运行的代码,不会被任何线程抢占。 KiDispatchInterrupt函数即是中断调度函数,又是线程调度函数,其运行在DPC_LEVEL。这说明软中断会在硬件中断处理后和线程调度前来查看并调度软中断。 IoRequestDpc注册DPC回调函数到DPC LIST->HalRequestSoftwareInterrupt设置当前IRQL为DPC_LEVEL->如果当前硬件级的 IRQL > DPC_LEVEL 则阻塞DPC。 硬件中断产生->HalEndSystemInterrupt:如果没有其他硬件中断,则把IRQL降低->如果当前有阻塞的DPC,则KiDispatchInterrupt来回调注册的DPC函数。 7 内核项目 和应用层编写复杂项目一样,也可以在内核编写复杂项目。在内核编写项目中应该考虑的一下事项: 7.1 编写内核动态库编写共享代码 7.2 使用不同镜像文件编写各内核模块协调工作 7.3 使用内核线程编写多线程模块 7.4 各模块之间可以通过Callback机制或者共享内存方式来传递信息 7.5 模块内部不同线程之间可通过队列或者共享内存方式来传递信息 7.6 通过事件通知来相互通知队列内部数据情况 7.7 。。。 7.8 用通用的设计思想来设计(可像应用层一样设计系统),用内核的规则来编写代码,即可,一切都是简单的。 8 应用方向 8.1 新的Windows杀毒方式 新的内核在MBR首先接管控制,然后查杀病毒,最后在切换到真正操作系统。 举例: MBR->linux裁剪的内核查杀Windows病毒->切换到Windows操作系统继续引导 http://hi.baidu.com/jonathan2004/blog/item/eeb8ec150e4e8019c93d6da5.html 8.2 Windows系统盘加密 如何加密Windows系统盘,仅仅是系统盘。这还是一个不错的研究方向。 8.3 网络加速 多研究现在rfc描述的协议有那些漏洞,还是很有意义的事情。 网络加速就是一个很重要的方向,特别是3G时代。对于网络协议存在的问题,进行有效的弥补。 8.4 智能化嵌入式 嵌入式的发展将是未来发展重要发展方向,相信会有井喷的时间来到。 有精力的朋友可以向这几方面研究研究,不要总是做些HOOK来HOOK去的事情,不要总是在内核做小动作,不要总是跟着MS等屁股后面找人家的漏洞帮助ms擦屁股,要做就做一些有意义的事情。 |
|
|
沙发#
发布于:2010-12-19 13:59
支持
|
|
|
板凳#
发布于:2010-12-28 12:39
up up
|
|
|