驾驶员安全清单

本文为驱动程序开发人员提供了驱动程序安全清单,以帮助降低驱动程序遭到入侵的风险。

驱动程序安全概述

安全漏洞是任何缺陷,使攻击者能够以导致系统崩溃或不可用的方式导致驱动程序发生故障。 此外,驱动程序代码中的漏洞可能使攻击者能够访问内核,从而有可能破坏整个 OS。 当大多数开发人员都在处理其驱动程序时,他们的重点是让驱动程序正常工作,而不是恶意攻击者是否会尝试利用其代码中的漏洞。

但是,在释放驱动程序后,攻击者可以尝试探测和识别安全漏洞。 开发人员必须在设计和实施阶段考虑这些问题,以尽量减少此类漏洞的可能性。 目标是在驱动程序发布之前消除所有已知的安全漏洞。

创建更安全的驱动程序需要系统架构师(有意识地考虑对驱动程序的潜在威胁)、实现代码的开发人员(在防御性编码常见操作(可能是攻击的来源)和测试团队(主动尝试查找弱点和漏洞)的合作。 通过正确协调所有这些活动,驾驶员的安全性得到了显著增强。

除了避免与受到攻击的驱动程序相关的问题之外,所述的许多步骤(例如更精确的内核内存使用)将提高驱动程序的可靠性。 这将降低支持成本,提高对产品的满意度。 完成以下清单中的任务将有助于实现所有这些目标。

安全清单:完成上述每个主题中所述的安全任务。

未标记的复选框,表示安全清单中的一个项目。确认内核驱动程序是必需的

表示安全清单中某个项目的未标记复选框。 使用驱动程序框架

表示安全清单中某个项目的未标记复选框。 控制对纯软件驱动程序的访问

表示安全清单中某个项目的未标记复选框。 请勿生产签名测试驱动程序代码

未标记的复选框,表示安全清单中的一项。执行威胁分析

表示安全清单中的项的未标记复选框。遵循驱动程序安全编码准则

表示安全清单中的项的未标记复选框。实现 HVCI 兼容代码

表示安全清单中某个项目的未标记复选框。 遵循技术特定的代码最佳做法

表示安全清单中某个项目的未标记复选框。 执行对等代码评审

表示安全清单中某项的未标记复选框。管理驱动访问控制

表示安全清单中的项的未标记复选框。增强设备安装安全性

表示安全清单中某个项目的未标记复选框。 执行正确的发布驱动程序签名

表示安全清单中某个项目的未标记复选框。 使用 CodeQL 检查驱动程序代码

表示安全清单中某个项目的未标记复选框。 将 SAL 注释添加到驱动程序代码

未勾选的复选框,表示安全检查表中的一项。使用驱动程序验证器检查漏洞

表示安全清单中某个项目的未标记复选框。 使用 BinSkim 二进制分析工具检查代码

表示安全清单中某个项目的未标记复选框。 通过硬件兼容性程序测试检查代码

表示安全清单中某项的未标记复选框。了解如何通过使用Microsoft易受攻击和恶意驱动程序报告中心来报告驱动程序

未标记复选框,表示安全清单中的一项。请查看安全编码资源

表示安全清单中某个项目的未标记复选框。 查看关键要点摘要

确认需要内核驱动程序

安全清单项 #1:确认需要内核驱动程序,并且风险较低的方法(如 Windows 服务或应用)不是更好的选择。

驱动程序位于 Windows 内核中,在内核中执行时出现问题会公开整个操作系统。 如果有任何其他选项可用,则其成本可能更低,并且与创建新内核驱动程序相比,其关联风险可能更低。 有关使用内置 Windows 驱动程序的详细信息,请参阅 是否需要编写驱动程序?

有关使用后台任务的相关信息,请参阅使用后台任务支持应用

有关使用 Windows 服务的信息,请参阅 服务

使用驱动程序框架

安全清单项 #2:使用驱动程序框架来减小代码的大小并提高其可靠性和安全性。

使用 Windows 驱动程序框架 来减小代码的大小并提高其可靠性和安全性。 若要快速入门,请查看使用 WDF 开发驱动程序。 有关使用风险较低的用户模式框架驱动程序(UMDF)的信息,请参阅 选择驱动程序模型

编写旧式 Windows 驱动程序模型(WDM) 驱动程序更耗时、成本高昂,而且几乎总是涉及重新创建驱动程序框架中可用的代码。

Windows 驱动程序框架源代码是开放源代码,可在 GitHub 上使用。 这是生成 Windows 10 中随附的 WDF 运行时库的相同源代码。 当可以跟踪驱动程序与 WDF 之间的交互时,可以更有效地调试驱动程序。 从 https://github.com/Microsoft/Windows-Driver-Frameworks 下载它。

控制对纯软件驱动程序的访问

安全清单项 #3:如果要创建仅限软件的驱动程序,则必须实现其他访问控制。

纯软件内核驱动程序不使用即插即用 (PnP) 与特定硬件 ID 相关联,可以在任何电脑上运行。 这种驱动程序可用于除最初预期之外的其他目的,从而创建攻击途径。

由于仅软件内核驱动程序包含额外的风险,因此它们必须仅限于在特定硬件上运行(例如,通过使用唯一的 PnP ID 来启用 PnP 驱动程序的创建,或通过检查 SMBIOS 表来检查是否存在特定硬件)。

例如,假设 OEM Fabrikam 想要分发一个驱动程序,以便为其系统启用超频工具。 如果此软件驱动程序在来自不同 OEM 的系统上执行,可能会导致系统不稳定或损坏。 Fabrikam 的系统应包含唯一的 PnP ID,以便创建也可以通过 Windows 更新更新的 PnP 驱动程序。 如果无法实现这一点,而 Fabrikam 构建了一个旧版驱动程序,该驱动程序应该找到另一种方法来验证它是否在 Fabrikam 系统上运行(例如,在启用任何功能之前检查 SMBIOS 表)。

不要生成签名测试代码

安全清单项 #4:不要生成代码对开发、测试和制造内核驱动程序代码进行签名。

用于开发、测试或制造的内核驱动程序代码可能包括构成安全风险的危险功能。 此危险代码不应使用 Windows 信任的证书进行签名。 执行危险驱动程序代码的正确机制是禁用 UEFI 安全启动、启用 BCD“TESTSIGNING”,并使用不受信任的证书对开发、测试和制造代码进行签名(例如,由 makecert.exe生成的证书)。

由受信任的软件发布者证书(SPC)或 Windows 硬件质量实验室(WHQL)签名签名的代码不得有助于绕过 Windows 代码完整性和安全技术。 在代码由受信任的 SPC 或 WHQL 签名进行签名之前,请先确保它符合创建可靠的内核模式驱动程序的指导。 此外,代码不得包含任何危险行为,如下所述。 有关驱动程序签名的详细信息,请参阅本文后面的发布驱动程序签名

危险行为的示例包括:

  • 提供将任意内核、物理或设备内存映射到用户模式的功能。
  • 提供读取或写入任意内核、物理或设备内存的功能,包括端口输入/输出(I/O)。
  • 提供对绕过 Windows 访问控制的存储的访问权限。
  • 提供修改硬件或固件的功能,而这些硬件或固件并非驱动程序所设计管理的。

执行威胁分析

安全清单项 #5:修改现有驱动程序威胁模型或为驱动程序创建自定义威胁模型。

在考虑安全性时,一种常见方法是创建特定的威胁模型,试图描述可能的攻击类型。 在设计驱动程序时,此方法非常有用,因为它会强制开发人员提前考虑针对驱动程序的潜在攻击途径。 识别潜在威胁后,驱动程序开发人员可以考虑防御这些威胁的方法,以提高驱动程序组件的整体安全性。

本文提供有关创建轻型威胁模型的驱动程序特定指南:驱动程序的威胁建模。 本文提供了一个示例驱动程序威胁模型图,该图可用作您开发驱动程序威胁模型的起始参考点。

演示假设内核模式驱动程序的示例数据流图。

IHV 和 OEM 可以使用安全开发生命周期(SDL)最佳做法和相关工具来提高其产品的安全性。 有关详细信息,请参阅为 OEM 推荐的 SDL

遵循驱动程序安全编码准则

安全清单项 #6:查看代码并删除任何已知的代码漏洞。

创建安全驱动程序的核心活动是识别代码中需要更改的区域,以避免已知的软件漏洞。 其中许多已知的软件漏洞都涉及监控内存的使用,以防止其他程序覆盖或干扰由驱动程序使用的内存位置。

代码扫描工具(如 CodeQL 和驱动程序特定测试)可用于帮助查找这些漏洞的一些但并非全部漏洞。 本主题稍后将介绍这些工具和测试。

内存缓冲区

使用适当的方法通过 IOCTL 访问数据缓冲区

Windows 驱动程序的主要职责之一是在用户模式应用程序和系统设备之间传输数据。 下表显示了用于访问数据缓冲区的三种方法。

IOCTL 缓冲区类型 总结 更多信息
METHOD_BUFFERED 推荐用于大多数方案 使用缓冲 I/O
METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT 在一些高速硬件输入/输出中使用 使用直接 I/O
METHOD_NEITHER 如果可能,请避免 既不使用缓冲输入输出,也不使用直接输入输出

通常,建议使用缓冲 I/O,因为它提供最安全的缓冲方法。 但即使使用缓冲 I/O,也存在风险,例如必须降低嵌入指针的风险。

有关在 IOCTL 中使用缓冲区的详细信息,请参阅 访问数据缓冲区的方法

使用 IOCTL 缓冲 I/O 时出错

  • 检查 IOCTL 相关缓冲区的大小。 有关详细信息,请参阅 无法检查缓冲区的大小

  • 正确初始化输出缓冲区。 有关详细信息,请参阅 输出缓冲区初始化失败

  • 正确验证可变长度缓冲区。 有关详细信息,请参阅无法验证可变长度缓冲区

  • 使用缓冲 I/O 时,请确保并在 IO_STATUS_BLOCK 结构信息字段中返回 OutputBuffer 的正确长度。 不要仅仅直接从 READ 请求中返回长度。 例如,考虑这样一种情况:从用户空间返回的数据表明存在 4K 缓冲区。 如果驱动程序实际上只应返回 200 个字节,但却在信息字段中返回了 4K,则发生了信息泄露漏洞。 出现此问题的原因是,在早期版本的 Windows 中,I/O 管理器用于缓冲 I/O 的缓冲区不会为零。 因此,用户应用将获得原始的 200 字节数据加上缓冲区中 4K-200 字节的数据(非分页池内容)。 这种情况可能发生在所有使用缓冲系统 I/O 的场合,而不仅仅是使用 IOCTL 的场合。

IOCTL 直接 I/O 出错

正确处理长度为零的缓冲区。 有关详细信息,请参阅直接 I/O 出错

引用用户空间地址时出现错误

特定于 MSR 模型的寄存器读取和写入

编译器内部函数(如 __readmsr__writemsr)可用于访问特定于模型的寄存器。 如果需要此访问权限,驱动程序必须始终检查要读取或写入到的寄存器是否受预期索引或范围的约束。

有关详细信息和代码示例,请参阅面向 Windows 驱动程序开发人员的开发安全最佳做法中的提供读/写 MSR 的能力

TOCTOU 漏洞

使用直接 I/O(用于 IOCTL 或读/写)时,可能存在潜在检查时间到使用时间 (TOCTOU) 漏洞。 请注意,驱动程序正在访问用户数据缓冲区,用户可以同时访问该缓冲区。

若要管理此风险,请将需要从用户数据缓冲区验证的任何参数复制到只能从内核模式(如堆栈或池)访问的内存。 然后,一旦用户应用程序无法访问数据,请验证并操作传入的数据。

驱动程序代码必须正确使用内存

  • 所有驱动程序池分配都必须位于非可执行文件 (NX) 池中。 使用 NX 内存池本质上比使用可执行的非分页(NP)池更安全,并提供更好的防止溢出攻击的保护。

  • 设备驱动程序必须正确处理各种用户模式,以及内核到内核 I/O 的请求。

若要允许驱动程序支持 HVCI 虚拟化,还需要满足额外的内存要求。 有关详细信息,请参阅本文后面的 实现 HVCI 兼容代码

句柄

设备对象

IRP

WDF 和 IRP

使用 WDF 的优点之一是 WDF 驱动程序通常不直接访问 IRP。 例如,框架将表示读取、写入和设备 I/O 控制操作的 WDM IRP 转换为 KMDF/UMDF 在 I/O 队列中接收的框架请求对象。

如果要编写 WDM 驱动程序,请查看以下指南。

正确管理 IRP I/O 缓冲区

以下文章提供有关验证 IRP 输入值的信息:

使用缓冲 I/O 执行 DispatchReadWrite

缓冲 I/O 出错

使用直接 I/O 执行 DispatchReadWrite

直接 I/O 出错

I/O 控制代码的安全问题

请考虑验证与 IRP 关联的值,例如缓冲区地址和长度。

如果您选择使用非 I/O,请注意,这与读取和写入不同,也与缓冲 I/O 和直接 I/O 不同。当使用非 I/O IOCTL 时,缓冲区指针和长度不会由 I/O 管理器进行验证。

正确处理 IRP 完成操作

除非驱动程序实际支持并处理 IRP,否则它永远不能用状态值 STATUS_SUCCESS 完成 IRP。 有关处理 IRP 完成操作的正确方法的信息,请参阅完成 IRP

管理驱动程序 IRP 挂起状态

驱动程序在保存 IRP 之前应将 IRP 标记为挂起,并应考虑将对 IoMarkIrpPending 的调用和分配都包含在互锁序列中。 有关详细信息,请参阅无法检查驱动程序的状态设备暂停时保持传入的 IRP

正确处理 IRP 取消操作

取消操作可能很难正确编码,因为它们通常以异步方式执行。 处理取消操作的代码中的问题可能会长时间被忽略,因为此代码通常不会在正在运行的系统中频繁执行。 请务必阅读并理解取消 IRP 下提供的所有信息。 请特别注意同步 IRP 取消取消 IRP 时要考虑的要点

最小化与取消操作相关的同步问题的一种推荐方法是实现可安全取消的 IRP 队列

正确处理 IRP 清理和关闭操作

请确保了解 IRP_MJ_CLEANUPIRP_MJ_CLOSE 请求之间的差异。 清理请求在应用程序关闭文件对象的所有句柄后到达,但有时在完成所有 I/O 请求之前到达。 关闭请求是在文件对象的所有输入/输出请求完成或取消后到达的。 有关详细信息,请参阅以下文章:

DispatchCreate、DispatchClose 和 DispatchCreateClose 例程

DispatchCleanup 例程

处理清理和关闭操作时出错

有关正确处理 IRP 的详细信息,请参阅 处理 IRP中的其他错误。

其他安全问题

  • 使用锁或互锁序列来防止争用条件。 有关详细信息,请参阅多处理器环境出错

  • 确保设备驱动程序正确处理各种用户模式以及内核到内核 I/O 请求。

  • 确保在安装或使用过程中驱动程序或关联的软件包未安装 TDI 筛选器或 LSP。

使用安全功能

其他代码漏洞

除了此处介绍的可能漏洞外,本文还提供了有关增强内核模式驱动程序代码安全性的其他信息:创建可靠 Kernel-Mode 驱动程序

有关 C 和C++安全编码的其他信息,请参阅本文末尾 安全编码资源。

管理驱动程序访问控制

安全清单项 #7:查看驱动程序以确保正确控制访问权限。

管理驱动程序访问控制 - WDF

驱动程序必须正常工作,以防止用户不恰当地访问计算机的设备和文件。 若要防止对设备和文件进行未经授权的访问,必须:

  • 仅在必要时命名设备对象。 命名设备对象通常是出于旧式原因所必需的,例如,如果应用程序希望使用特定名称打开设备,或者使用的是非 PNP 设备/控制设备。 请注意,WDF 驱动程序不需要为其 PnP 设备 FDO 命名,才能使用 WdfDeviceCreateSymbolicLink创建符号链接。

  • 保护对设备对象和接口的访问。

若要允许应用程序或其他 WDF 驱动程序访问 PnP 设备 PDO,应使用设备接口。 有关详细信息,请参阅使用设备接口。 设备接口充当设备堆栈 PDO 的符号链接。

控制对 PDO 的访问的更好方法是在 INF 中指定 SDDL 字符串。 如果 SDDL 字符串不在 INF 文件中,Windows 将应用默认的安全描述符。 有关详细信息,请参阅保护设备对象设备对象的 SDDL

有关控制访问权限的详细信息,请参阅以下文章:

在 KMDF 驱动程序中控制设备访问权限

名称、安全描述符和设备类 - 使设备对象易于访问... and SAFE 摘自 2017 年 1 月至 2 月由 OSR 出版的《NT 预览体验计划新闻稿》

管理驱动程序访问控制 - WDM

如果使用 WDM 驱动程序并使用命名设备对象,则可以使用 IoCreateDeviceSecure 并指定 SDDL 来保护它。 实现 IoCreateDeviceSecure 时,请始终为 DeviceClassGuid 指定自定义类 GUID。 不应在此处指定现有的类 GUID。 这样做可能会破坏属于该类的其他设备的安全设置或兼容性。 有关详细信息,请参阅 WdmlibIoCreateDeviceSecure

有关详细信息,请参阅以下文章:

控制设备访问权限

控制设备命名空间访问权限

驱动程序开发人员的 Windows 安全模型

安全标识符(SID)风险层次结构

以下部分介绍驱动程序代码中使用的常见 SID 的风险层次结构。 有关 SDDL 的一般信息,请参阅 设备对象的 SDDLSID 字符串,以及 SDDL 字符串语法

请务必了解,如果允许较低权限调用方访问内核,则代码风险会增大。 在此摘要图中,当您允许低权限 SID 访问您的驱动程序功能时,风险会增加。

SY (System)
\/
BA (Built-in Administrators)
\/
LS (Local Service)
\/
BU (Built-in User)
\/
AC (Application Container)

遵循常规最低特权安全原则,仅配置驱动程序正常运行所需的最低访问级别。

WDM 精细 IOCTL 安全控制

为了进一步管理用户模式调用方发送 IOCTL 时的安全性,驱动程序代码可以包含 IoValidateDeviceIoControlAccess 函数。 此函数允许驱动程序检查访问权限。 收到 IOCTL 后,驱动程序可以调用 IoValidateDeviceIoControlAccess,指定FILE_READ_ACCESS、FILE_WRITE_ACCESS 或两者。

实施精细 IOCTL 安全控制并不能取代使用上述技术管理驱动程序访问的需要。

有关详细信息,请参阅以下文章:

定义 I/O 控制代码

实现 HVCI 兼容代码

安全清单项 #8:验证驱动程序是否使用内存,使其兼容 HVCI。

内存使用情况和 HVCI 兼容性

HVCI 使用硬件技术和虚拟化将代码完整性 (CI) 决策功能与操作系统的其余部分隔离开来。 使用基于虚拟化的安全性隔离 CI 时,内核内存成为可执行文件的唯一方法是通过 CI 验证。 这意味着内核内存页永远不能同时是可写的和可执行的(W+X),并且可执行代码不能直接被修改。

若要实现 HVCI 兼容代码,请确保驱动程序代码执行以下操作:

  • 默认情况下选择加入 NX
  • 使用 NX API/标志进行内存分配 (NonPagedPoolNx)
  • 不要使用既可写又可执行的节
  • 不尝试直接修改可执行的系统内存
  • 在内核中不使用动态代码
  • 不将数据文件加载为可执行文件
  • 节对齐是 0x1000 (PAGE_SIZE) 的倍数。 例如,DRIVER_ALIGNMENT=0x1000

有关使用该工具和不兼容内存调用列表的详细信息,请参阅 实现 HVCI 兼容代码

有关相关系统基础知识安全测试的详细信息,请参阅 HyperVisor 代码完整性准备情况测试Hypervisor-Protected 代码完整性(HVCI)

遵循特定技术的代码最佳实践

安全清单项 #9:请查看以下针对驱动程序的特定技术指导。

文件系统

有关文件系统驱动程序安全性的详细信息,请参阅以下文章:

文件系统安全简介

文件系统安全问题

文件系统的安全功能

与其他文件系统筛选器驱动程序共存

NDIS - 网络

有关 NDIS 驱动程序安全性的信息,请参阅 网络驱动程序的安全问题。

显示

有关显示驱动程序安全性的信息,请参阅<内容挂起>。

打印机

有关打印机驱动程序安全性的信息,请参阅 V4 打印机驱动程序安全注意事项

Windows 映像获取(WIA)驱动程序的安全问题

有关 WIA 安全性的信息,请参阅 Windows 映像获取(WIA)驱动程序的安全问题。

增强设备安装安全性

安全清单项 #10:查看驱动程序创建和安装指南,确保遵循最佳做法。

创建安装驱动程序的代码时,必须确保始终以安全的方式安装设备。 安全设备安装是执行以下所有操作的过程:

  • 限制对设备及其设备接口类的访问
  • 限制对为设备创建的驱动程序服务的访问
  • 保护驱动程序文件免受修改或删除
  • 限制对设备的注册表项的访问
  • 限制对设备的 WMI 类的访问
  • 正确使用 SetupAPI 函数

有关详细信息,请参阅以下文章:

创建安全的设备安装

使用 SetupAPI 的指南

使用设备安装函数

设备和驱动程序安装高级主题

执行对等代码评审

安全清单项 #11:执行对等代码评审,查找其他工具和进程未显示的问题

寻找知识渊博的代码审阅者来查找你可能错过的问题。 第二组眼睛通常会看到你可能忽略的问题。

如果没有合适的员工在内部评审你的代码,请考虑借助外部帮助来实现这一目的。

执行正确的发布驱动程序签名

安全清单项 #12:使用 Windows 合作伙伴门户正确签署驱动程序以进行分发。

在向公众发布驱动程序包之前,建议提交程序包进行认证。 有关详细信息,请参阅测试性能和兼容性硬件计划入门硬件仪表板服务为公开发布的内核驱动程序进行签名的证明

使用 CodeQL 检查驱动程序代码

安全清单项 #13:使用 CodeQL 检查驱动程序代码中的漏洞。

CodeQL(由 GitHub 提供)是一个语义代码分析引擎,广泛的安全查询套件与可靠的平台相结合,使其成为保护驱动程序代码的宝贵工具。 有关详细信息,请参阅 CodeQL 和静态工具徽标测试

将 SAL 注释添加到驱动程序代码

安全清单项 #14:在驱动程序代码中添加 SAL 注释。

源代码注释语言(SAL)提供了一组批注,可用于描述函数如何使用其参数、它对其做出的假设以及它在完成时做出的保证。 批注在头文件 sal.h中定义。 用于C++的 Visual Studio 代码分析使用 SAL 注释来修改其函数分析。 有关用于 Windows 驱动程序开发的 SAL 2.0 的详细信息,请参阅 适用于 Windows 驱动程序的 SAL 2.0 注释使用 SAL 注释减少 C/C++ 代码缺陷

有关 SAL 的一般信息,请参阅 OSR 提供的本文。 https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/

使用驱动验证器检查漏洞

安全清单项 #15:使用驱动程序验证程序检查驱动程序代码中的漏洞。

驱动程序验证程序使用一组接口规则和操作系统模型来确定驱动程序是否与 Windows 操作系统正确交互。 DV 发现驱动程序代码中可能存在的漏洞,这些漏洞可能指向驱动程序中的潜在错误。

驱动程序验证程序允许对驱动程序进行实时测试。 驱动程序验证程序监视 Windows 内核模式驱动程序和图形驱动程序,以检测可能损坏系统的非法函数调用或操作。 驱动程序验证程序可以让 Windows 驱动程序受到各种压力和测试,以查找不当行为。 有关详细信息,请参阅 驱动程序验证程序

请注意,DV 仅支持某些类型的驱动程序。 有关 DV 可以验证的驱动程序的详细信息,请参阅 支持的驱动程序。 有关可用于你正在使用的驱动程序类型的 DV 测试的信息,请参阅以下页面。

若要熟悉 DV,可以使用其中一个示例驱动程序(例如特色烤箱示例:https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured)。

使用 BinSkim 二进制分析器检查代码

安全清单项 #16:按照以下步骤使用 BinSkim 仔细检查是否已配置编译和生成选项以最大程度地减少已知安全问题。

使用 BinSkim 检查二进制文件,以确定可能使二进制文件易受攻击的编码和构建做法。

BinSkim 检查以下项:

  • 使用过时的编译器工具集 - 应尽可能针对最新的编译器工具集编译二进制文件,以最大程度地使用当前编译器级别和 OS 提供的安全缓解措施。
  • 不安全的编译设置 - 二进制文件应使用最安全的设置进行编译,以启用 OS 提供的安全缓解措施,最大程度地提高编译器错误和可操作的警告报告,等等。
  • 签名问题 - 已签名的二进制文件应使用加密强算法进行签名。

BinSkim 是一种开源工具,它生成使用静态分析结果交换格式(SARIF) 格式的输出文件。 BinSkim 取代了以前的 BinScope 工具。

有关 BinSkim 的详细信息,请参阅 使用 BinSkim 检查二进制文件BinSkim 用户指南

使用硬件兼容性程序测试检查代码

安全清单项 #17:使用安全相关的硬件兼容性程序测试来检查安全问题。

硬件兼容性计划包括安全相关测试,可用于查找代码漏洞。 Windows 硬件兼容性计划利用 Windows 硬件实验室工具包(HLK)中的测试。 HLK 设备基础知识测试可在命令行上使用,以练习驱动程序代码和探测弱点。 有关设备基础测试和硬件兼容性计划的一般信息,请参阅 Windows 硬件实验室工具包

以下测试是可用于检查驱动程序代码是否存在与代码漏洞相关的某些行为的测试示例:

DF - 模糊随机 IOCTL 测试(可靠性)

DF - 模糊 sub-open 测试(可靠性)

DF - 模糊零长度缓冲区 FSCTL 测试(可靠性)

DF - 模糊随机 FSCTL 测试(可靠性)

DF - 模糊杂项 API 测试(可靠性)

还可以使用驱动程序验证程序随附的内核同步延迟模糊处理

CHAOS(并发硬件和操作系统)测试同时运行各种 PnP 驱动程序测试、设备驱动程序模糊测试和电源系统测试。 有关详细信息,请参阅 CHAOS 测试(设备基础)

设备基础渗透测试执行各种形式的输入攻击,这是安全测试的关键组成部分。 攻击和渗透测试可以帮助识别软件接口中的漏洞。 有关详细信息,请参阅 渗透测试(设备基础知识)

使用 Device Guard - 合规性测试以及本文中所述的其他工具确认驱动程序是否兼容 HVCI。

自定义和领域特定的测试工具

考虑开发自定义域特定的安全测试。 若要开发其他测试,请从软件的原始设计者处收集输入,也可以从与软件设计无关但熟悉特定类型驱动程序的开发资源获取信息,并咨询一名或多名熟悉安全入侵分析和防护的人员。

了解如何使用Microsoft易受攻击和恶意驱动程序报告中心报告驱动程序

安全清单项 #18: 了解如何使用Microsoft易受攻击和恶意驱动程序报告中心报告驱动程序

任何人都可以使用Microsoft易受攻击和恶意驱动程序报告中心提交可疑驱动程序。 有关如何提交驱动程序进行分析的信息,请参阅此博客文章 - 使用新的Microsoft易受攻击和恶意驱动程序报告中心 改进内核安全性

Reporting Center 可以扫描和分析为 x86 和 x64 体系结构生成的 Windows 驱动程序。 被检测为易受攻击和恶意的驱动程序将由Microsoft的易受攻击驱动程序团队标记出来,以便进行分析和调查。 确认易受攻击的驱动程序后,将发生相应的通知,它们会添加到易受攻击的驱动程序阻止列表中。 有关详细信息,请参阅 Microsoft 推荐的驱动程序阻止规则。 这些规则默认应用于启用了虚拟机监控程序保护的代码完整性(HVCI)设备,以及运行 S 模式的 Windows 10。

查看安全编码资源

安全清单项 #19:查看这些资源,以扩展你对适用于驱动程序开发人员的安全编码最佳做法的理解。

安全内核模式驱动程序编码指南

创建可靠的内核模式驱动程序

安全编码组织

卡内基梅隆大学 SEI CERT

Carnegie Mellon University SEI CERT C 编码标准:开发安全、可靠和安全系统(2016 年版)的规则。

MITRE - CERT C 安全编码标准解决的弱点

在成熟度模型中构建安全性 (BSIMM) - https://www.bsimm.com/

SAFECode - https://safecode.org/

CISA 资源

OSR

OSR 提供驱动程序开发培训和咨询服务。 OSR 新闻稿中的这些文章突出显示了驱动程序安全问题。

名称、安全描述符和设备类 - 使设备对象易于访问... and SAFE

你必须使用保护措施 - 内部驱动程序和设备安全性

锁定驱动程序 - 技术调查

Meltdown和Spectre:驱动程序呢?

个案研究

从警报到驱动程序漏洞:Microsoft Defender ATP 调查发现了特权升级漏洞

书籍

软件安全的 24 个致命罪:编程缺陷以及如何修复它们 迈克尔·霍华德、大卫·勒布兰克和约翰·维加

软件安全评估的艺术:识别和预防软件漏洞作者:Mark Dowd、John McDonald 和 Justin Schuh

编写安全软件第二版、迈克尔·霍华德和大卫·勒布兰克

软件安全评估的艺术:识别和预防软件漏洞作者:Mark Dowd 和 John McDonald

C 和 C++ 中的安全编码 (SEI 软件工程系列) 第二版,罗伯特·C·西科德

编程 Microsoft Windows 驱动程序模型(第二版),沃尔特·奥尼

使用 Windows Driver Foundation 开发驱动程序(开发人员参考)作者:Penny Orwick 和 Guy Smith

训练

Windows 驱动程序课堂培训可从以下供应商处获得:

可从各种来源获取安全编码在线培训。 例如,本课程可从 coursera 上获取:

识别 C/C++ 编程中的安全漏洞

SAFECode 还提供免费培训:

SAFECode.org/training

专业认证

CERT 提供 安全编码专业认证

关键要点摘要

驱动程序安全性是一项复杂的任务,包含许多要素,但以下是需要考虑的几个关键点:

  • 驱动程序位于 Windows 内核中,在内核中执行时出现问题会公开整个操作系统。 因此,请密切关注驱动程序安全,并在设计时考虑到安全性。

  • 应用最低特权原则:

    a. 使用严格的 SDDL 字符串来限制对驱动程序的访问

    b. 进一步限制单个 IOCTL

  • 创建威胁模型来识别攻击途径,并考虑是否可以进一步限制任何内容。

  • 请注意从用户模式传入的嵌入指针。 它们需要在 try except 内进行探测和访问,而且它们容易出现检查使用时间 (ToCToU) 问题,除非捕获并比较缓冲区的值。

  • 如果不确定,请使用 METHOD_BUFFERED 作为 IOCTL 缓冲方法。

  • 使用代码扫描实用工具查找已知代码漏洞并修正任何已识别的问题。

  • 寻找知识渊博的代码审阅者来查找你可能错过的问题。

  • 使用驱动程序验证程序并使用多个输入测试驱动程序,包括极端情况。