C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍
因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚。因此想看一下驱动开发。查了很多资料,看到有人推荐Windows驱动开发技术详解这本书,因此本篇文章也是基于这本书进行学习的。有些图片也是按照书上自己画的。
Windows操作系统示意图
首先,需要下载相应的工具,将环境搭建起来,VS和WDK,由于我已经安装了VS2017,所以需要找对应版本的WDK(方法)。如果想要查OS的版本,可以WIN+R输入winver就可以看到OS的版本了,
老版本对应链接:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/other-wdk-downloads
安装好了后就需要写一下程序了,参考链接:https://blog.csdn.net/liny000/article/details/81260385
Windows架构简图
Win32子系统将API函数转化为Native API函数。在Native API接口中,已经没有了子系统的概念,它将这种调用转化为系统服务函数的调用。其中,Native API穿过了用户模式和内核模式的界面,达到了内核模式。系统服务函数通过I/O管理器将消息传递给驱动程序。
在内核模式下,执行体组件提供了大量的内核函数供驱动程序调用。内核主要负责进程、线程的调度情况。驱动程序通过硬件抽象层与具体硬件进行操作。
Windows API分为三类,分别是USER函数、GDI函数和KERNEL函数。
》USER函数:这类函数管理窗口、菜单、对话框和控件。
》GDI函数:这类函数在物理设备商执行绘图操作。
》KERNEL函数:这类函数管理非GUI资源,例如:进程、线程、文件和同步服务等。
可以发现Windows系统目录中有对应的三个系统文件,分别是USER32.dll、GDI32.dll和KERNEL32.dll。这三个文件提供了以上三类API的接口。当应用程序加载的时候,操作系统出了将应用程序加载到内存中,同时将以上三个DLL文件加载到内存中。
1、Native API
大部分Win32子系统的API,都通过Native API实现的。Native API的函数一般都是在Win32API上加上Nt两个字母。例如,CreateFile函数对应着NtCreateFile函数。所有Native API都是在Ntdll.dll中实现的。以上三个Win32子系统的核心dll文件都是依赖于Ntdll.dll的。
在Win32的底下设置一层Native API的调用,是基于版本兼容的考虑。Win32 API从Windows NT到Windows 2000,再到Windows XP,基本保持一致,变化的只是Native API。作为应用程序的开发者,只需了解Win32的API,而不用关心Native API的变化。这种机制,可以让WindowsNT上的程序直接在更高版本的Windows上运行,而不用重新编译。
2、I/O管理器
I/O管理器负责发起I/O请求,并且管理这些请求。它由一系列内核模式下的例程所组成,这些例程为用户模式下的进程提供了统一接口。I/O管理器的目标是使来自用户模式的I/O请求独立于设备。
无论是对端口的读写、对键盘的访问,还是对磁盘文件的操作都统一为IRP(I/O Request Packages)的请求形式。其中IRP包含了对设备操作的重要数据,例如是读操作还是写操作、读多少字节、写多少字节,是直接读到用户进程中,还是先读到系统缓冲中,在读到用户进程中等。
IRP被传递到具体设备的驱动程序中,驱动程序负责“完成”这些IRP,并将完成的状态按原路返回到用户模式下的应用程序中。实际上,I/O管理器担当着用户模式代码和设备驱动程序之间的接口。
3、配置管理程序
在Windows上,配置管理程序记录所有计算机软件、硬件的配置信息。它使用一个被称为注册表(Registry)的数据库保存这些数据。设备驱动程序根据注册表中的信息进行加载。
另外,驱动程序还会从注册表中提取相应的参数,这样可以提高驱动程序的灵活性。例如:设备操作的延时时间,可以作为参数写进注册表。驱动程序加载的时候读取该值,而不是将延时时间在编写程序的时候写成定值。
4、驱动程序
I/O管理器接收应用程序的请求后,创建相应的IRP,并传送至驱动程序进行处理,有如下几种处理的方法。
(1)根据IRP的请求,直接操作具体硬件,然后完成此IRP,并返回。
(2)将此IRP的请求,转发到更底层的驱动中去,并等待底层驱动的返回。
(3)接受到IRP请求,不是急于完成。而是分配新的IRP发到其他驱动程序中,并等待返回。
驱动程序处理IRP的过程往往不是单独操作,而是将以上几种操作结合在一起。
5、内核
内核被认为是Windows操作系统的心脏。Windows的内核从执行体组件分割出来。和执行体组件相比,内核是非常小的。内核为执行体组件提供最基本的支持,它负责提供进程和线程的调度,通过自旋锁(Spin Lock)提供对多CPU同步支持,提供中断处理等。
内核提供了以下功能:
》对内核对象的支持。
》对线程的调度。
》对多处理器同步的支持。
》中断处理函数的支持。
》对错误陷阱的支持。
》对其他硬件特殊功能的支持。
Windows内核执行在最高的特权之上,它被设计成可以并行地运行在多处理器商。内核在调度线程的时候不能被其他线程所打断,即不能允许线程的切换。但是内核可以被更高的中断请求级别(IRQL)所打断。
从应用程序到驱动程序
打开Windows的设备管理器,可以发现这里罗列着计算机里安装的所有设备,这些设备有的是真实的物理设备。例如:网卡设备、显卡设备等。有些设备则是虚拟设备。例如,自己编写的驱动,它没有对应着PC的任何设备,而完全是虚拟出来的“假”设备。虚拟光驱也是这样的虚拟设备。还有些设备介于真实物理设备和虚拟设备之间,比如磁盘的卷设备,磁盘对应磁盘设备,磁盘上的分区又会产生卷设备,这个完全是逻辑上的概念,对卷设备的所有操作,全部会转化成磁盘设备的操作。
设备分类 | 功能 |
文件设备 | 对存储文件的操作 |
目录设备 | 对目录的操作 |
逻辑磁盘设备 | 对逻辑磁盘的操作 |
物理磁盘设备 | 对物理磁盘的操作 |
串口设备 | 对诸如串口鼠标自、串口Modem设备的操作 |
并口设备 | 对诸如并口打印机的操作 |
PC上的设备千差万别,所实现的功能完全不同,如何用统一的接口操作不同的设备,是一个很麻烦的问题。Windows的设计者们为了简化对不同设备的操作,实现对不同设备统一接口,将所有设备以普通文件看待。也就是说在Windows中,无论何种设备,都用操作文件的办法去操作设备。
对所有设备的操作统一成和文件操作一样的操作,这一方法非常巧妙。文件操作和设备操作有很多类似的地方。例如,二者都有打开、关闭、读、写、取消等操作。下表列举了文件操作和设备操作所使用的的Win32 API函数。
Win32 API | 对文件操作 | 对设备操作 |
CreateFile | 打开或创建文件 | 打开或创建设备 |
CloseHandle | 关闭文件 | 关闭设备 |
ReadFile | 读文件 | 读设备 |
WriteFile | 写文件 | 写设备 |
CancelIO | 取消读写文件操作 | 取消读写设备操作 |
DeviceIoControl | (无) | 对设备进行特殊操作 |
下面更深入的介绍一下,Win32 API是如何一步步对设备驱动程序进行读写操作的。
下图是对Windows架构简图的简化,可以很清晰地看到应用程序到驱动程序是怎样操作设备的。
这里以CreateFile API为例,其他操作设备的API类似。首先应用程序调用CreateFile API,这个API是由Win32子系统的三大模块中的Kernel32.dll实现的。CreateFile函数会调用Ntdll.dll中的NtCreateFile函数,其中NtCreateFile是未文档化的函数,程序员最好不要直接使用这个未文档化的函数。
NtCreateFile的作用是穿越用户模式的边界,进入到内核模式,这个步骤是通过软中断实现的。进入到内核模式后,会调用系统的服务函数,这里会调用同名的系统服务NtCreateFile。(这里很容易搞混,虽然都叫NtCreateFile,但一个是位于用户模式的Native API,另一个是位于内核模式的系统服务调用)
NtCreateFile系统函数调用通过I/O管理器,创建IRP并传输到设备的驱动程序中。IRP(I/O Request Package)即输入输出请求包,是驱动程序开发中重要的数据结构。驱动程序的运行,完全是靠IRP驱动的。下面会有对IRP的专门介绍,这里可以将IRP理解为一个消息。这个消息告诉驱动程序,是需要读操作还是写操作。
驱动程序根据IRP,进行相应的操作。这些操作一般是对设备的直接操作,例如对端口的读操作。对端口的读操作根据不同的硬件平台,实现方法会有所不同,Windows根据不同的硬件平台,会有不同的硬件抽象层(HAL)。硬件抽象层提供一组宏,如READ_PORT_BUFFER_UCHAR。例如,对32位X86系列CPU中的Windows,READ_PORT_BUFFER_UCHAR被解释为汇编代码IN操作。
回想这个复杂的过程,经过了多个层次的交互,只是为了执行一个读写端口的操作。对于有过DOS变成经验的程序员来说,在DOS中操作硬件完全可以不使用驱动,直接使用IN汇编代码就可以实现。事实的确如此,但Windows这样做完全是为了安全的考虑。所有直接操作硬件的指令,如读写物理内存、读写端口都认为是危险的操作,必须经过驱动才能完成。
试想一下,如果应用程序能任意执行IN和OUT汇编指令,那么就可轻易地对磁盘进行控制,这会给操作系统带来安全隐患。又例如,如果能直接读写物理内存,黑客会很容易地对当前进程以外的其他进程进行内存读写,那么盗取账号密码将会变得非常简单。
因此,在应用程序中无法执行IN汇编指令,而必须通过驱动程序来执行。在一层层地调用中,每层又会严格地检查,保证参数的合法性。
C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍的更多相关文章
- C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载
基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...
- C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构
对于NT式驱动来说,主要的函数是DriverEntry例程.卸载例程及各个IRP的派遣例程. 一.驱动加载过程与驱动入口函数(DriverEntry) 和编写普通应用程序一样,驱动程序有个入口函数,也 ...
- C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构
数据结构是计算机程序的核心,I/O管理器定义了一些数据结构,这些数据结构是编写驱动程序时所必须掌握的.驱动程序经常要创建和维护这些数据结构的实例. 一.驱动对象(DRIVER_OBJECT) 每个驱动 ...
- Windows驱动开发(中间层)
Windows驱动开发 一.前言 依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发. 二.初步环境 1.下载安装WDK7.1.0(WinDDK\7600.16385 ...
- [Windows驱动开发](一)序言
笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓——内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...
- windows驱动开发推荐书籍
[作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...
- Windows 驱动开发 - 7
在<Windows 驱动开发 - 5>我们所说的读写操作在本篇实现. 在WDF中实现此功能主要为:EvtIoRead和EvtIoWrite. 首先,在EvtDeviceAdd设置以上两个回 ...
- Windows 驱动开发 - 5
上篇<Windows 驱动开发 - 4>我们已经完毕了硬件准备. 可是我们还没有详细的数据操作,比如接收读写操作. 在WDF中进行此类操作前须要进行设备的IO控制,已保持数据的完整性. 我 ...
- windows 驱动开发入门——驱动中的数据结构
最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...
随机推荐
- 二、Nginx 服务器升级
1,编译新版本的nginx 软件 [root@client lnmp_soft]# tar -xf nginx-1.12.2.tar.gz -C .. [root@client lnmp_soft ...
- 【NX二次开发】隐藏、显示对象UF_OBJ_set_blank_status
隐藏.显示对象用UF_OBJ_set_blank_status() 查看对象显示情况用UF_OBJ_ask_display_properties() 效果: 源码: #include "Te ...
- PTA题目集4-6总结
PTA题目集4-6总结 一:前言 在题集4-6中,所考查的主要知识点有正则表达式,类与类之间的调用,类的聚合,继承,封装,接口与多态,三种排序方法如选择排序,冒泡排序,插入排序,ArrayList,s ...
- MySQL的可重复读级别能解决幻读问题吗?
之前在深入了解数据库理论的时候,了解到事务的不同隔离级别可能存在的问题.为了更好的理解所以在MySQL数据库中测试复现这些问题.关于脏读和不可重复读在相应的隔离级别下都很容易的复现了. 但是对于幻读, ...
- 题解 P3232 [HNOI2013]游走
洛谷P3232[NOI2013]游走 题目描述 给定一个 n 个点 m 条边的无向连通图,顶点从 1 编号到 n,边从 1 编号到 m. 小 Z 在该图上进行随机游走,初始时小 Z 在 1 号顶点,每 ...
- Terraform模块Module管理,聚合资源的抽取与复用
我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 最近工作中用到了Terraform,权当学习记录一下,希望能帮助到其它人. Terraform系列文章如下: T ...
- Kubernetes之deployment
Kubernetes实现了零停机的升级过程.升级操作可以通过使用ReplicationController或者ReplicaSet实现,但是Kubernetes提供了另一种基于ReplicaSet的资 ...
- 05 找出占用CPU、内存过高的进程
#!/bin/bash export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin echo "----- ...
- Flask(1)- 简介
背景 为啥要学,很久之前就学过点,没写文章 最近因为要写机器人工具,其实就是简单的纯服务端工具 反正 flask 也挺简单,一天快速过完 概念会直接搬教程的,实操自己敲一遍再总结 参考教程 https ...
- docker下创建redis cluster集群
概述 在Redis中,集群的解决方案有三种 主从复制 哨兵机制 Cluster Redis Cluster是Redis的分布式解决方案,在 3.0 版本正式推出. 准备工作 1.确定本机IP地址 2. ...