Windows 操作系统架构介绍

  • 4 种基本类型的用户模式进程
    • 用户进程
    • 服务进程
    • 系统进程
    • 环境子系统服务进程
    • Windows 内核模式组件
  • 重要的系统组件
    • 环境子系统和子系统 DLL
      • 子系统的启动
      • Windows 子系统
      • Windows 10 和 Win32k.sys
    • 其他子系统
    • 执行体
    • 内核
      • 内核对象
      • 内核处理器控制区(KPCR)和控制块
      • 硬件支持
    • 硬件抽象层
    • 设备驱动程序
      • Windows 驱动程序模型
      • Windows 驱动程序基础
      • 通用 Windows 驱动程序
    • 系统进程
  • 附录
    • 常用 API 接口命名含义
    • 本文主要内容梳理

Windows 系统架构包括以下组成部分:

  1. 内核:Windows 内核是操作系统的核心部分,它负责管理系统资源、处理用户程序和驱动程序的请求、协调各种系统组件之间的通信等任务。Windows 内核分为用户模式和内核模式,其中内核模式是更高级别、更安全的模式,用户程序无法直接访问内核模式。

  2. 驱动程序:Windows 系统需要很多不同类型的驱动程序来管理硬件设备和提供系统功能,如网络驱动程序、声卡驱动程序、显卡驱动程序等。这些驱动程序运行在内核模式下,可以访问系统的底层资源和硬件设备。

  3. 用户模式:用户模式是 Windows 操作系统中运行应用程序的模式。在用户模式下,应用程序可以访问一些系统资源,如文件系统、网络、进程和线程等,但不能直接访问内核模式或底层硬件设备。

  4. Win32 子系统:Win32 子系统是 Windows 操作系统中的一种应用程序接口,它允许 32 位应用程序在 Windows 操作系统上运行。Win32 子系统提供了访问操作系统资源和功能的标准接口,使得应用程序可以在各种 Windows 系统上运行。

  5. Windows API:Windows API 是一组应用程序接口,用于访问 Windows 操作系统的各种资源和功能。Windows API 可以在任何编程语言中使用,包括 C/C++、Java、Python 和 C# 等。

  6. 用户界面:Windows 操作系统的用户界面包括桌面、任务栏、窗口、菜单和对话框等组件。这些组件通过 Windows API 和用户模式实现,提供了用户与操作系统进行交互和操作的方式。

总体来说,Windows 系统架构是一个包括多个组件和模式的复杂系统,通过这些组件和模式协同工作,提供了一个稳定、安全、易用的操作系统环境。

这里将简单介绍 Windows 的基本系统架构。通过前面章节的内容,我们已经知道了 Windows 分为用户模式和内核模式两部分。简单示意图如下:

这里最底层可以看到一个“虚拟机监控程序”,它也是运行在内核模式下(RING-0),但是因为i使用了特殊的 CPU 指令(VT-x、SVM),所以可以将自己与内核隔离的同时继续监视内核(或应用程序)。因此我们经常会听到 Ring-1 这个称呼。

4 种基本类型的用户模式进程

这里需要先简单介绍一下关于 Windows 操作系统下的四种基本的用户模式进程:

  • 用户进程
  • 服务进程
  • 系统进程
  • 环境子系统服务器进程

用户进程

用户进程可能是下面某种类型之一:

  • Windows 的 32 位或 64 位(Windows 8 和后续版本中,在 Windows 运行时的基础上运行的 Windows 应用也属于此类)进程。
  • Windows 3.1 的 16 位进程
  • MS-DOS 的 16 位进程
  • POSIX 的 32 位或 64 位进程

(注意:16 位进程只能运行在 32 位的 Windows 上,Windows 8 开始不再支持 POSIX 应用程序)

服务进程

服务进程承载了 Windows 服务,如 Task Scheduler 和 Print Spooler 服务。

通常来说,服务需要能在用户不登录的情况下运行,很多 Windows 服务器应用。如 Microsoft SQL Server 和 Microsoft Exchange Server 也包含了以服务方式运行的组件。

系统进程

系统进程是指静态或硬编码的进程,例如非 Windows 服务的登录进程和会话管理器。也就是说,这些进程并非由服务控制管理器启动。

环境子系统服务进程

环境子系统服务进程是指那些实现了操作系统环境的支持部分的进程。

所谓“环境”是指呈现给用户和程序员的、操作系统中可进行个性化的部分。Windows NT 首发时提供了三个环境子系统:Windows、POSIX、OS/2。但是对 OS/2 子系统的支持到 Windows 2000 之后便已经停止,对 POSIX 的支持在 Windows XP 之后停止。Windows 7 旗舰版和企业版客户端以及服务器版本的 Windows 2008 R2 提供了一种名为 Subsystem for UNIX-based Applications(SUA)的增强型 POSIX 子系统。SUA 现在已经停止支持,不再作为可选功能包含在(客户端或服务器版本)Windows 中。

Windows 内核模式组件

在 Windows 中,用户应用程序无法直接调用原生的 Windows 操作系统服务,而是需要通过一个或多个子系统动态链接库(DLL)调用。子系统 DLL 的作用在于将文档化的函数转换为相应的内部(通常未文档化)原生系统服务调用,这些调用通常是在 Ntdll.dll 中实现的。这种转换可能涉及,也可能不涉及将消息发送给用户进程提供服务的环境子系统进程。

Windows 内核模式组件如下:

  • 执行体。Windows 执行体包含操作系统的基础服务,例如内存管理、进程和线程管理、安全性、I/O、网络以及进程之间通信。
  • Windows 内核。Windows 内核包含底层操作系统函数,例如线程调度、中断和异常分发、多处理器同步。内核还提供了一系列的例程和基本对象,执行体的其他部分会使用它们实现更高层次的功能。
  • 设备驱动程序。设备驱动程序包括将用户 I/O 函数调用转换为特定硬件设备 I/O 请求的硬件设备驱动程序,以及诸如文件系统和网络驱动程序等非硬件设备驱动程序。
  • 硬件抽象层(HAL,hardware abstraction layer)。这层代码负责将内核、设备驱动程序以及 Windows 执行体的其他部分与和具体平台有关的差异进行隔离。
  • 窗口和图形系统。窗口和图形系统用于实现用户界面(GUI)功能(通常也称为 Windows USER 和 GDI 功能),例如处理窗口、用户界面控件以及进行绘制。
  • 虚拟机监控程序层。这一部分只包含虚拟机监控程序本身。这一部分不包含其他驱动程序或模块。但虚拟机监控程序本身就是由多种内部层和驱动程序组成的,如自己的内存管理器、虚拟处理器调度器、中断和计时器管理、同步例程、分区(虚拟机实例)管理、分区间通信(IPC)等。

重要的系统组件

前面已经提到了 Windows 的基础架构以及常见的进程类型,也捎带提到了关于 Windows 系统组件的内容。下图将这部分的系统组件结合起来可以直观看到 Windows 核心系统架构和组件。

从上图中可以看 Windows 操作系统环境中重要的几个系统组件分别是:

  1. 环境子系统和子系统 DLL
  2. 其他子系统
  3. 执行体
  4. 内核
  5. 硬件抽象层
  6. 设备驱动程序
  7. 系统进程

下面对上述内容分析进行详细的说明和介绍。

环境子系统和子系统 DLL

环境子系统的角色在于将基本 Windows 执行体系统服务的某些子集暴漏给应用程序。此类子系统可以访问 Windows 原生服务的不同子集。这意味着在一个子系统的基础上建立的应用程序所能执行的某个操作,可能无法被建立在另一个子系统的基础上的应用程序做到。例如,Windows 应用程序无法使用 SUA 的 fork 函数。

每个可执行映像(.exe)都会绑定到唯一的子系统。映像运行时,负责创建进程的代码会在映像头部检查子系统的类型代码,进而将新建进程告知给正确的子系统。该类型代码可以使用 Microsoft Visual Studio 的 linker 命令中的 /SUBSYSTEM linker 选项来制定(或使用工程属性中 Linker/System 属性页的 SubSystem 选项来指定)。

如前所述,用户应用程序不会直接调用 Windows 系统服务,而是要通过一个或多个子系统 DLL 来进行。这些库导出的接口都有相应的文档说明,链接到对应子系统的程序均可调用。例如,Windows 子系统 DLL (如 Kernel32.dll、Advapi32.dll、User32.dll 和 Gdi32.dll)实现了 Windows API 函数,SUA 子系统 DLL(Psxdll.dll)可实现 SUA API 函数。

这里,可以使用 Dependency Walker 工具(Dependency.exe)来查看映像文件的子系统类型。(关于这个工具可以查看这篇文章中有提到如何下载和使用,https://blog.csdn.net/qq_37596943/article/details/131515390?spm=1001.2014.3001.5501)

分别查看 Windows 操作系统自带的 Notepad.exe 和 CMD.exe。

从上面两张图可以看出,Notepad.exe 的依赖 DLL 和 CMD.exe 的依赖 DLL 完全不一样。并且 Notepad.exe 由于是 GUI 应用程序会依赖 GDI32.DLL,而 CMD.exe 就很简单只依赖最基础的 NTDLL.DLL 库。

这里说明一下当应用程序调用子系统 DLL 中的函数时会发生的情况:

  • 函数完全在子系统 DLL 内部以用户模式实现。换句话说,不会向环境子系统进程发送任何消息,也不会调用任何 Windows 执行体系统服务。函数将在用户模式下执行,并将执行结果返回给调用方,如 GetCurrentProcess 和 GetCurrentProcessId 函数。(运行中的 进程 ID 是不会改变的,因此该 ID 可以从某个缓存的位置获取,以避免对内核调用。)
  • 函数需要对 Windows 执行体进行一个或多个调用。例如,Windows 的 ReadFile 和 WriteFile 函数就分别需要调用底层内部(且针对用户模式的使用未提供文档)的 Windows I/O 系统服务 NtReadFile 和 NtWriteFile。
  • 函数要在环境子系统进程中执行一些工作。(运行于用户模式的环境子系统进程负责维持在其控制下运行的客户端应用程序的状态)这种情况下,需要通过 ALPC 以消息的形式向环境子系统发出客户端/服务器请求,借此让子系统执行所需操作。随后子系统 DLL 将等待响应,并将响应返回给调用方。

某些函数会结合上述第二种和第三种结合使用,例如 Windows 的 CreateProcess 以及 ExitWindowsEx 函数。

子系统的启动

子系统是由会话管理器(Smss.exe)进程启动的。子系统的启动信息存储在注册表 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems 键下。

图中的 Required 键值列出了系统引导时加载的子系统。该值包含两个字符串:Windows 和 Debug。

Windows 值包含了 Windows 子系统的文件规范,其中 Csrss.exe 代表了客户局端/服务器运行时子系统。

Debug 值为空(该值从 Windows XP 开始就不需要了,单位了维持兼容性依然保留),因此不会生效。

optional 值代表可选的子系统,在本例子中同样为空,因为 Windows 10 已经无法再使用 SUA。原本可用的使用,将通过 POSIX 的数值指向另一个值,进而指向 Psxss.exe(POSIX 子系统进程)。Optional 的值均为“按需加载”的,这意味着会在首次遇到 POSIX 映像的时候再开始加载。Kmode 注册表值包含 Windows 子系统内核模式部分的文件名(Win32K.sys)。

Windows 子系统

虽然 Windows 在设计的时候即可支持多种独立的环境子系统,但实际上让每个子系统实现所有的代码以处理窗口和显示 I/O 会产生大量重复的系统函数,最终对系统的体积和性能产生不利影响。由于 Windows 是最主要的子系统,Windows 的设计者决定将基本函数放在 Windows 子系统中,并让其他子系统调用 Windows 子系统以实现显示 I/O,因此 SUA 子系统会调用 Windows 子系统中的服务来实现显示 I/O。

在这种设计决策的影响下,Windows 子系统成了任何 Windows 系统的必须组件,甚至在不提供交互式用户登录的服务器系统上也是如此。因此这个进程也成为关键进程(如果该进程因为任何情况退出,那么系统将崩溃)。

Windows 子系统包含下列重要组件:

  1. 对于每个会话,环境子系统进程(Csrss.exe)的一个实例将加载 4 个 DLL(Basesrv.dll、Winsrv.dll、Sxssrv.dll、Csrsrv.dll),借此提供下列支持:
  • 与进程和线程的创建及删除有关的多种管理任务
  • Windows 应用程序的关闭(通过 ExitWindowsEx API 实现)
  • 包含有关注册表位置映射的 .ini 文件,借此维持向后兼容性
  • 以 Windows 消息的形式将某些内核通知消息(如来自即插即用管理器的消息)发送给 Windows 应用程序(WM_DEVICECHANGE)
  • 为 16 位 DOS 虚拟机(VDM)进程提供部分支持(仅限 32 位 Windows)
  • 支持 Side-by-Side(SxS)/Fusion 和清单缓存
  • 为多种自然语言支持函数提供缓存
  1. 一个内核模式的设备驱动程序(Win32k.sys),用于提供下列支持。
  • 窗口管理器。控制窗口的显示,管理屏幕输出,收集来自键盘、鼠标和其他设备的输入,并将用户消息传递给应用程序
  • 图形设备接口(GDI)。这是专门为图形输出设备设计的函数库,包含了有关线条、文本和图形的绘制函数,以及图形控制函数。
  • DirectX 功能的封装函数。这是通过另一个内核模式驱动程序(Dxgkrnl.sys)实现的函数。
  1. 控制台宿主进程(Conhost.exe),用于为控制台(字符界面)应用程序提供支持。
  2. 桌面窗口管理器(Dwm.exe),借此可以在一个表层通过 CDD 和 DirectX 合称可见的窗口渲染结果。
  3. 子系统 DLL(如 Kernel32.dll、Advapi32.dll、User32.dll、Gdi32.dll),将文档化的 Windows API 函数转译为 Ntoskrnl.exe 和 Win32k.sys 中相应的,并且未经文档化(对于用户模式)的内核模式系统服务调用。
  4. 图形设备驱动程序。用于提供与硬件相关的图形显示驱动程序、打印机驱动程序以及视频微端口驱动程序。

Windows 10 和 Win32k.sys

Windows 10 设备最基本的窗口管理要求高度依赖于具体的设备类型,运行 Windows 的完整桌面计算机需要具备所有的窗口管理能力,例如调整窗口大小、窗口所有者、子窗口等。而手机或小型平板电脑上运行的 Windows mobile 10 并不需要如此多的功能,因为只有一个前台窗口,无法最小化或调整大小。物联网设备也是如此,这类设备甚至可能根本无须显示画面。

因此 Win32K.sys 的功能被拆分到多个内核模块中,而某些系统可能并不需要所有这些模块。由于代码的复杂度降低,这样的做法大幅减少了窗口管理器的攻击面,并移除了很多遗留代码。

  • 在手机(Windows mobile 10)中,Win32k.sys 会加载 Win32kMin.sys 和 Win32kBase.sys
  • 在完整桌面计算机系统中,Win32k.sys 会加载 Win32kBase.sys 和 Win32kFull.sys
  • 在某些物联网系统中,Win32k.sys 可能只需要加载 Win32kBase.sys

应用程序需要调用标准的 USER 函数来创建用户界面上的控件,如窗口和按钮,并显示在屏幕上。窗口管理器会将这些请求传递给 GDI,GDI 会将其传递给图形设备驱动程序,并由图形设备驱动程序结合显示设备进行必要的调整。显示器驱动程序会与视图微端口驱动程序成对使用,借此为完整的视频显示提供支持。

GDI 提供了一组标准的二维函数,可供应用程序在不了解与图形设备有关的任何信息的前提下与其通信。GDI 函数位于应用程序和诸如显示器驱动程序与打印机驱动程序等图形设备之间,负责解释应用程序的图像输出请求,并将请求发送给图形显示器驱动程序。GDI 还为使用不同图形输出设备的应用程序提供了标准化的接口。借助这些接口,应用程序代码可以与硬件设备及其驱动程序保持独立。GDI 还会针对设备的能力对消息进行裁剪,通常会将请求拆分为多个可管理的部件。例如,一些设备可以理解绘制椭圆的命令,一些设备则可能需要 GDI 将命令解释为“放置在某一个坐标位置的一系列像素点”。

由于子系统的大部分内容运行在内核模式下,因此只有少数 Windows 函数需要向 Windows 子系统进程发送进程和线程创建与终止、DOS 设备盘符映射等消息。

其他子系统

如前所述,Windows 最初可支持 POSIX 和 OS/2 子系统。由于新的 Windows 已不包含这些子系统,这里就不详细说明了。

Pico 提供程序和适用于 Linux 的 Windows 子系统

过去十多年来,传统子系统模型以足够的扩展性和能力为 POSIX 和 OS/2 提供了支持,然而该模型的两个技术局限使其难以适用于更广泛的非 Windows 二进制文件,只能用于一些非常具体的场景中。

为了解决 Windows 下针对 POSIX 的兼容性留下的一系列问题。现在 Windows 采用了新的方式来构建子系统,可以无须对其他环境的系统调用以及传统 PE 映像的执行进行传统的用户模式包装。

该模型定义了 Pico 提供程序这一概念。这是一种自定义的内核模式驱动程序,可通过 PsRegisterPicoProvider API 接收对专用内核接口的访问。这种专用接口会带来很多好处。

原生映像

使用 Dependency Walker 打开 Smss.exe (会话管理器进程),可以看到它仅依赖 Ntdll.dll ,并且这里显示它的子系统是 Native。

执行体

Windows 执行体是 Ntoskrnl.exe 的上层。(下层为内核)执行体包含下列类型的函数:

  • 可在用户模式下到处和调用的函数。这些函数叫做系统服务,可通过 Ntdll.dll 到处。
  • 通过 DeviceIoControl 调用的设备驱动程序函数。这为从用户模式到内核模式调用设备驱动程序中的函数提供了通用接口,借此可调用与读/写操作无关的函数。
  • 只能从内核模式调用,并在 WDK 中导出并且文档化的函数。这些函数包含多种支持例程,如 I/O 管理器、常规执行体函数(Ex)等
  • 可在内核模式下导出并调用,但未在 WDK 中文档化的函数。这些函数包括引导视频驱动程序调用的函数,以 Inbv 开头。
  • 定义为全局符号但不可导出的函数。这些函数包括在 Ntoskrnl.exe 中内部调用的支持函数,例如以 Iop(内部 I/O 管理器支持函数)或 Mi(内部内存管理支持函数)开头的函数。
  • 位于模块内部但未定义为全局符号的函数。这些函数为执行体和内核专用。

对于执行体的主要组件,这里简单列举一下:

  1. 配置管理器
  2. 进程管理器
  3. 安全引用监视器
  4. I/O 管理器
  5. 即插即用(PnP,Plug and Play)管理器
  6. 电源管理器
  7. Windows 驱动程序模型(WDM)
  8. 内存管理器
  9. 缓存管理器
  10. 对象管理器
  11. 异步 LPC(ALPC) 设施
  12. 运行库函数
  13. 执行体支持例程。

此外,还包含多种其他基础架构例程:

  • 内核模式调试器库。
  • 用户模式调试框架
  • 虚拟机监控程序库和 VBS 库
  • 勘误表管理器
  • 驱动程序验证程序
  • Windows 时间跟踪(ETW)
  • Windows 诊断基础架构
  • Windows 硬件错误架构
  • 文件系统运行库
  • 内核填充码引擎

内核

内核由 Ntoskrnl.exe 中一系列用于提供基础机制的函数组成,例如被执行体使用的线程调度和同步服务,以及与底层硬件架构独立的支持。内核代码主要用 C 语言编写,不过对于需要使用特殊处理器指令和寄存器的任务,由于难以用 C 语言代码访问,因此继续使用了汇编代码。

很多内核函数均在 WDK 中提供了文档(搜索以 Ke 开头的函数即可),这是因为设备驱动程序的实现也需要它们。

内核对象

内核提供了一种明确定义,可预知的操作系统底层原语和机制,可供执行体中的高层组件执行自己所需的操作。

在内核之外,执行体会将线程和其他共享的资源看做对象。

有一组名为控制对象的内核对象建立了控制各种操作系统函数所需的语义。这其中包括异步过程调用(APC)对象,延迟过程调用(Deferred Procedure Call,DPC)对象,以及 I/O 管理器使用的多种对象,如中断对象。

另一组名为分发器对象的内核对象所包含的同步能力可以改变或影响线程的调度。分发器对象包括内核线程、互斥体、时间、内核事件对、信号量、定时器以及可等待的定时器。执行体会使用内核函数创建并维护内核对象实例,构建更复杂的对象并提供给用户模式。

内核处理器控制区(KPCR)和控制块

内核使用一种名为内核处理控制区(Kernel Processor Control Region,KPCR)的数据结构存储与处理器有关的数据。KPCR 包含一些基本信息,如处理器的中断分发表(interrupt Dispatch Table,IDT)、任务状态段(Task State Segment,TSS)以及全局描述符表(Global Descriptor Table,GDT)。

KPCR 还包含中断控制器状态,这是与其他模块共享的(ACPI、HAL)。

KPCR 还包含一种名为内核处理器控制块(Kernel Processor Control Block,KPRCB)的嵌入式数据结构。为了供第三方驱动程序和其他内部 Windows 内核组件使用,KPCR 是文档化的数据结构;而 KPCB 是一种专供 Ntoskrnl.exe 中内核代码使用的私有结构,它包含下列内容:

  • 调度信息。如处理器上正在调度的当前线程、下一个执行线程、以及空闲线程
  • 处理器的分发器数据库。其中包含每个优先级的就绪队列
  • DPC 队列
  • CPU 供应商和标识符信息,如型号、步进、速度、特征位
  • CPU 和 NUMA 拓扑。如节点信息、每个芯片的内核数、每个内核的逻辑处理器数等
  • 缓存大小
  • 时间计数信息,如 DPC 和中断时间。

可以通过 windbg 利用 !prcb 查看 PRCB 的地址和数据信息

硬件支持

内核的另一个重要工作是将执行体和设备驱动程序从 Windows 支持的不同硬件体系架构的差异中抽象或抽离出来,这项工作也包括处理各种功能(例如中断处理、异常分发和处理器同步)之间的差异。

硬件抽象层

硬件抽象层(HAL)是实现可移植性的关键。HAL 是一种可加载的内核模式模块(Hal.dll),为运行 Windows 的硬件平台提供了底层接口。它可以将诸如 I/O 接口、中断控制器、多处理器通信机制等与特定硬件有关的细节以及与特定体系架构和计算机有关的功能隐藏起来。

WDK 中介绍了很多 HAL 例程,具体大家可以参考 WDK 文档。关于具体的模块有以下分别的描述:

HAL 文件名支持的系统
Halacpi.dll高级配置和电源接口(ACPI)计算机,暗指只有一颗处理器且不支持 APIC (中断控制器),两个条件有任意不满足的情况则使用下面的 HAL
Halmacpi.dll支持 ACPI 的高级可编程中断控制器(APIC)计算机,使用 APIC 也意味着可支持 SMP

这里,可以使用 Dependency Walker 工具来查看 Ntoskrnl.exe 的依赖。


可以看到其中的几个 DLL 库。

  • PSHED.DLL。特定于平台的硬件错误驱动程序(Platform-Specific Hardware Error Driver,PSHED)为底层平台的硬件错误报告能力提供了抽象。这是通过对操作系统隐藏平台的错误处理机制,并为 Windows 操作系统暴露一致的接口实现的。
  • BOOTVID.DLL。X86 系统上的引导视频驱动程序(Boorvid),为系统启动过程中显示引导文字和引导徽标所需的 VGA 命令提供支持。
  • KDCOM.DLL。内核模式调试器协议(KD)通信库。
  • CI.DLL。完整性库
  • MSRPC.SYS。适用于内核模式的微弱远程过程调用(RPC)客户端驱动程序,可供内核(及其他驱动程序)通过 RPC 与用户模式服务通信,或用于管理 MES 编码的资源。

设备驱动程序

设备驱动程序是一种可加载的内核模式模块(其文件通常使用 .sys 扩展),它在 I/O 管理器和相关硬件之间建立了接口。设备驱动程序在内核模式下,使用下列三种上下文之一来运行。

  1. 在发起 I/O 功能的用户线程上下文中(例如读取操作)
  2. 在内核模式系统线程的上下文(例如即插即用管理器发起的请求)
  3. 作为中断的结果,不处于任何特定线程上下文中,而是处于当中断产生时的“当前”线程上下文中。

Windows 的设备驱动程序并不会直接操作硬件,而是会调用 HAL 中的函数与硬件进行交互。驱动程序通常使用 C 或者 C++ 编写,因此正确使用 HAL 例程可以在 Windows 支持的不同 CPU 体系架构之间进行源代码级别的移植,在同一体系架构族内则可以实现二进制移植。

设备驱动程序主要包含以下几种类型:

  • 硬件设备驱动程序。使用 HAL 操作硬件将输出写入物理设备或网络,或从中获取输入。硬件设备驱动程序包含很多类型,如总线驱动程序、人机接口驱动程序、大容量存储设备驱动程序等。
  • 文件系统驱动程序。是指接收面向文件 I/O 请求,并将其转换为面向特定设备 I/O 请求的 Windows 驱动程序。
  • 文件系统筛选器驱动程序。包括执行磁盘镜像和加密、扫码查找病毒、拦截 I/O 请求,以及在将 I/O 传递到下一层之前执行附加处理操作的驱动程序。
  • 网络重定向和服务器。这种文件系统驱动程序分别负责将文件系统 I/O 请求传递到网络上的其他计算机,或接收其他计算机通过网络传递来的请求。
  • 协议驱动程序。负责实现网络协议,例如 TCP/IP、NetBEUI 和 IPX/SPX。
  • 内核流式筛选器驱动程序。此类驱动程序会连接在一起对数据流进行信号处理,例如录制或播放音视频。
  • 软件驱动程序。这种内核模块将代表某些用户模式进程执行只能在内核模式下执行的操作。

Windows 驱动程序模型

最初的 Windows 驱动程序是在第一版 NT 系统(3.1)创建的,但是由于即插即用(PnP)的原因(NT 驱动不支持 PnP),一直持续到 Windows 2000 发布支持了 PnP 驱动,通过 WDM 的驱动模型(Windows driver model,WDM)支持了 PnP 和电源选项的支持。一直到现在,WDM 经过多次更新依然被保留下来,并成为了 Windows 2000 和后续版本系统编写硬件驱动程序的基本模型。

WDM 驱动模型可以分为三种类型:

  1. 总线驱动程序。服务于总线控制器、适配器、桥接器或任何具备子设备的设备。系统中的每一类总线都有一个总线驱动程序。第三方厂商可以编写总线驱动程序,为 VMEbus、Multibus、Futurebus 等新总线类型提供支持。
  2. 功能驱动程序。是最主要的设备驱动程序,为相应设备提供了可供操作的接口。除非设备以原始方式(RAW)运行(I/O 由总线驱动程序和总线筛选器一起完成这种),否则功能驱动程序是设备必须的驱动程序,通常也是唯一能访问与设备有关的寄存器的驱动程序。
  3. 筛选器驱动程序。筛选器驱动程序可用于为设备或现有驱动层序增加额外功能。或修改来自其他驱动程序的 I/O 请求或相应。通常用于修复无法正确提供硬件资源需求信息的硬件设备。筛选器驱动程序是可选地,并且能够以任意数量存在于功能驱动程序之上或者之下。筛选器驱动程序通常由系统原始设备制造商(original equipment manufacturer,OEM)或独立硬件供应商(independent hardware vendor,IHV)提供。

在 WDM 驱动程序环境中,无法由单一驱动程序控制某个设备的方方面面。总线驱动程序负责向 PnP 管理器报告总线上连接的设备,功能驱动程序则负责操作设备。

Windows 驱动程序基础

Windows 驱动程序基础(Windows driver foundation,WDF)提供了内核模式驱动程序框架(kernel-mode driver framework,KMDF)和用户模式驱动程序框架(user-mode driver framework,UMDF)。这两种框架简化了 Windows 驱动程序的开发工作。

KMDF 提供了一种更简化的 WDM 接口,并在不改动底层总线、总能、筛选器模型的前提下隐藏了驱动开发过程中的复杂性。KMDF 驱动程序能够响应自己可以注册的事情,并调用 KMDF 库来执行非自己所管理设备特定的各种工作,例如常规的电源管理和同步。某些情况下,单一的 KMDF 函数调用即可取代原本大量的 WDM 代码实现的操作。

UMDF 使得某些类型的设备(大部分为 USB 设备或其他高延迟协议总线,例如视频摄像头、MP3 播放器、手机、打印机等)能够实现用户模式的驱动。从本质上看,UMDF 会将每个用户模式的驱动程序作为用户模式的服务来运行,并使用 ALPC 与内核模式下运行的包装(wrapper)驱动程序通信,借此实现对硬件的实际访问。如果 UMDF 驱动程序崩溃,进程会“死掉”并且通常会重新启动。这样就不会导致系统变得不翁当,唯一的代价仅仅是在服务宿主重新启动驱动程序的过程中,这个设备暂时不可用。

通用 Windows 驱动程序

从 Windows 10 开始,可以借助通用 Windows 驱动程序(universal Windows driver,UWD),使用 Windows 10 通用内核提供的共享 API 和设备驱动程序接口(device driver interface,DDI)实现驱动程序的“一次编写,处处执行”。这类驱动程序可以面向特定 CPU 架构实现二进制兼容,可以用于包括物联网设备、手机、HoloLens、Xbox one、笔记本电脑登不同形态的设备。通用 Windows 驱动程序可以使用 KMDF、UMDF 2.x 或者 WDM 作为自己的驱动程序模型。

可以通过 Msinfo32.exe 查看本机已安装的设备驱动程序。


从图中可以看出,我们通过这里能够找到当前已经安装的设备驱动程序,并且还能显示出驱动程序对应的驱动文件的位置及当前服务的状态。

当我们手动启动该服务后,也能看到它现在处于运行状态了。

使用 process explorer 工具也可以通过 system 进程查看当前加载的设备驱动程序。

系统进程

每一个 Windows 10 系统都会包含一些特殊的系统进程。它们并没有运行用户模式的可执行文件,这类进程称为最小进程。(例如:Idle、system、secure system、memory compression)

  • Idle 进程。为每颗 CPU 包含一个线程,用于占用空闲的 CPU 时间
  • System 进程。包含大部分内核模式系统线程和句柄
  • secure system 进程。包含 VTL1 下安全内核的地址空间
  • memory compression 进程。包含用户模式进程压缩后的工作量
  • 会话管理器(Smss.exe)
  • Windows 子系统(Csrss.exe)
  • 会话 0 初始化(Wininit.exe)
  • Logon 进程(Winlogon.exe)
  • 服务控制管理器(services.exe)及其创建的子服务进程,例如系统提供的常规服务宿主进程(Svchost.exe)
  • 本地安全认证服务(Lsass.exe),以及启用 Credential Guard 后隔离的本地安全认证服务器(Lsaiso.exe)

可以通过打开 Process explorer 工具查看进程树来了解上述这些进程都是谁创建的,有助于了解每个进程的来源。

附录

常用 API 接口命名含义

Windows 执行体组件常用的大部分函数的名称前缀都是有一定的含义。例如前缀的第一个字母后跟 i(代表内部,internal),完整前缀后跟字母 p (代表私有,private)。常见如下:

前缀组件
Alpc高级本地过程调用,advanced local procedure call
Cc公用缓存,common cache
Cm配置管理器,configuration manager
Dbg内核调试支持,kernel debug support
Dbgk用户模式调试框架,debugging framework for user mode
Em勘误管理器,errata manager
EtwWindows 事件跟踪,event tracing for Windows
Ex执行体支持例程,executive support routine
FsRtl文件系统运行库,file system runtime library
Hv配置单元库,hive library
Hvl虚拟机监控程序库,hypervisor library
IoI/O 管理器,I/O manager
Kd内核模式调试器,kernel debugger
Ke内核,kernel
Kse内核填充码引擎,kernel shim engine
Lsa本地安全机构,local security authority
Mm内存管理器,memory manager
NtNT 系统服务,NT system service,可在用户模式下通过系统调用访问
Ob对象管理器,object manager
Pf预读取器,Prefetchr
Po电源管理器,Power manager
PoFx电源框架,power framework
PpPnP 管理器,PnP manager
Ppm处理器电源管理器,processor power manager
Ps进程支持,process support
Rtl运行时库,Run-time library
Se安全引用监视器,security reference monitor
Sm存储管理器,store manager
Tm事务管理器,transaction manager
Ttm终端超时管理器,terminal timeout manager
Vf驱动程序验证器,driver verifier
Vsl虚拟安全模式库,virtual secure mode library
WdiWindows 诊断基础架构,Windows diagnostic infrastructure
WfpWindows 指纹,Windows finger print
WheaWindows 硬件错误架构,Windows hardware error architecture
WmiWindows management instrumentation
Zw系统服务入口点镜像,可将之前访问模式设置为内核模式。

通常,Windows 例程的命名规范如下格式:

<Prefix><Operation><Object>

本文主要内容梳理