因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚。因此想看一下驱动开发。查了很多资料,看到有人推荐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驱动开发(一)内部构造介绍的更多相关文章

  1. C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载

    基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...

  2. C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构

    对于NT式驱动来说,主要的函数是DriverEntry例程.卸载例程及各个IRP的派遣例程. 一.驱动加载过程与驱动入口函数(DriverEntry) 和编写普通应用程序一样,驱动程序有个入口函数,也 ...

  3. C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构

    数据结构是计算机程序的核心,I/O管理器定义了一些数据结构,这些数据结构是编写驱动程序时所必须掌握的.驱动程序经常要创建和维护这些数据结构的实例. 一.驱动对象(DRIVER_OBJECT) 每个驱动 ...

  4. Windows驱动开发(中间层)

    Windows驱动开发 一.前言 依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发. 二.初步环境 1.下载安装WDK7.1.0(WinDDK\7600.16385 ...

  5. [Windows驱动开发](一)序言

    笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓——内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...

  6. windows驱动开发推荐书籍

    [作者] 猪头三 个人网站 :http://www.x86asm.com/ [序言] 很多人都对驱动开发有兴趣,但往往找不到正确的学习方式.当然这跟驱动开发的本土化资料少有关系.大多学的驱动开发资料都 ...

  7. Windows 驱动开发 - 7

    在<Windows 驱动开发 - 5>我们所说的读写操作在本篇实现. 在WDF中实现此功能主要为:EvtIoRead和EvtIoWrite. 首先,在EvtDeviceAdd设置以上两个回 ...

  8. Windows 驱动开发 - 5

    上篇<Windows 驱动开发 - 4>我们已经完毕了硬件准备. 可是我们还没有详细的数据操作,比如接收读写操作. 在WDF中进行此类操作前须要进行设备的IO控制,已保持数据的完整性. 我 ...

  9. windows 驱动开发入门——驱动中的数据结构

    最近在学习驱动编程方面的内容,在这将自己的一些心得分享出来,供大家参考,与大家共同进步,本人学习驱动主要是通过两本书--<独钓寒江 windows安全编程> 和 <windows驱动 ...

随机推荐

  1. 【VBS】获取文件夹大小

    文件截图: 运行结果: 第一步:编写脚本 GetFloderSize.vbs 1 '获得文件夹的大小 by 王牌飞行员(https://www.cnblogs.com/KMould/p/1233481 ...

  2. Django基础之cookie与session

    cookie与session 由来及简介 HTTP协议四大特性 1.基于请求响应 2.基于TCP.IP作用于应用层之上 3.无连接 4.无状态 基于HTTP协议的通信无法记录客户端状态 但是现在很多软 ...

  3. PAT甲级 1093 Count PAT‘s (25 分) 状态机解法

    题目 原题链接 The string APPAPT contains two PAT's as substrings. The first one is formed by the 2nd, the ...

  4. MySQL的启动选项和系统变量该如何配置?

    MySQL的配置信息可以通过两种方式实现,一种是命令行形式,在启动MySQL服务时后边带上相关配置参数,此种方式会在MySQL重启后失效.另外一种是通过写入配置文件,如my.cnf,启动或者重启MyS ...

  5. 『言善信』Fiddler工具 — 16、使用Fiddler抓取移动端App请求

    目录 1.抓取Android移动端App请求 2.抓取IOS移动端App请求 3.总结: 1.抓取Android移动端App请求 前提: 因为Fiddler抓包的原理就是通过代理,所以确保被测终端要和 ...

  6. Spring WebFlux 教程:如何构建反应式 Web 应用程序

    Spring WebFlux 教程:如何构建反应式 Web 应用程序 反应式系统提供了我们在高数据流世界中所需的无与伦比的响应能力和可扩展性.然而,反应式系统需要经过专门培训的工具和开发人员来实现这些 ...

  7. Python UI自动化

    Python3--Uiautomator2--Pytest--Alure使用 官方源码GitHub地址:https://github.com/openatx/uiautomator2 介绍 uiaut ...

  8. Vue 动态组件和异步组件

    基础案例 动态组件切换类比"bilibili-个人中心"的横向菜单切换不同的标签页的功能. 在Vue中可以使用 component 标签,并加一个特殊的属性(attribute) ...

  9. centos 安装jre

    r第一步:将安装的jre安装文件上传到Linux系统中(这里用的是finalshell工具) 第二步:  解压tar -zxvf server-jre-8u131-linux-x64.tar.gz 显 ...

  10. 3shell命令替换

    Shell 命令替换是指将命令的输出结果赋值给某个变量.比如,将使用ls命令查看到的某个目录中的内容保存到某个变量中,这就需要使用命令替换. Shell 中有两种方式可以完成命令替换,一种是反引号` ...