驱动程序安全清单

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

驱动程序安全概述

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

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

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

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

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

表示安全清单中的项的“未标记”复选框。确认需要内核驱动程序

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

表示安全清单中的项的“未标记”复选框。控制对仅软件驱动程序的访问

表示安全清单中的项的“未标记”复选框。不要对测试驱动程序代码进行生产签名

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

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

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

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

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

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

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

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

表示安全清单中的项的“未标记”复选框。使用 Visual Studio 中的代码分析调查驱动程序安全性

表示安全清单中的项的“未标记”复选框。使用静态驱动程序验证程序检查漏洞

表示安全清单中的项的“未标记”复选框。使用 BinSkim 二进制分析器检查代码

表示安全清单中的项的“未标记”复选框。使用代码验证工具

表示安全清单中的项的“未标记”复选框。查看调试器技术和扩展

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

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

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

确认需要内核驱动程序

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

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

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

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

使用驱动程序框架

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

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

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

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,以便能够创建 PnP 驱动程序,该驱动程序也可通过Windows 更新进行更新。 如果这不可行,并且 Fabrikam 创建了旧版驱动程序,该驱动程序应找到另一种方法来验证它在 Fabrikam 系统上执行 (,例如,在启用任何功能) 之前检查 SMBIOS 表。

不要对测试代码进行生产签名

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

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

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

危险行为的示例包括:

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

执行威胁分析

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

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

本文提供有关创建轻型威胁模型的特定于驱动程序的指导: 驱动程序的威胁建模。 本文提供了一个示例驱动程序威胁模型图,可用作驱动程序的起点。

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

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

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

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

创建安全驱动程序的核心活动是识别代码中需要更改的区域,以避免已知的软件漏洞。 其中许多已知软件漏洞涉及严格跟踪内存使用情况,以避免其他漏洞覆盖或以其他方式构成驱动程序使用的内存位置的问题。

本文的代码 验证工具 部分介绍了可用于帮助查找已知软件漏洞的软件工具。

内存缓冲区

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

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

IOCTL 缓冲区类型 总结 更多信息
METHOD_BUFFERED 建议用于大多数情景 使用缓冲 I/O
METHOD_IN_DIRECT或METHOD_OUT_DIRECT 用于某些高速 HW I/O 使用直接 I/O
METHOD_NEITHER 如果可能,请避免 既不使用缓冲 I/O,也不使用直接 I/O

一般情况下,建议使用缓冲 I/O,因为它提供最安全的缓冲方法。 但是,即使使用缓冲 I/O 也存在风险,例如必须缓解的嵌入式指针。

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

使用 IOCTL 缓冲 I/O 时出错

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

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

  • 正确验证可变长度缓冲区。 有关详细信息,请参阅 验证 Variable-Length 缓冲区失败

  • 使用缓冲 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 漏洞

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

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

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

  • 所有驱动程序池分配都必须在 NX) 池 (不可执行。 与使用可执行的非分页 (NP) 池相比,使用 NX 内存池本质上更安全,并且可以更好地防范溢出攻击。

  • 设备驱动程序必须正确处理各种用户模式以及内核到内核 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 完成操作

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

管理驱动程序 IRP 挂起状态

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

正确处理 IRP 取消操作

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

尽量减少与取消操作关联的同步问题的一种建议方法是实现 取消安全 IRP 队列

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

请确保了解 IRP_MJ_CLEANUPIRP_MJ_CLOSE 请求之间的区别。 清理请求在应用程序关闭文件对象上的所有句柄之后,但有时在所有 I/O 请求完成之前到达。 关闭请求在文件对象的所有 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 驱动程序中控制设备访问权限

名称、安全描述符和设备类 - 使设备对象易于访问... 和 SAFEOSR 发布的 2017 年 1 月 2 月 NT Insider 新闻稿

管理驱动程序访问控制 - 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/flags 进行内存分配 (NonPagedPoolNx)
  • 不使用可写和可执行的节
  • 不尝试直接修改可执行的系统内存
  • 不在内核中使用动态代码
  • 不以可执行文件的形式加载数据文件
  • 节对齐方式是0x1000 (PAGE_SIZE) 的倍数。 例如,DRIVER_ALIGNMENT=0x1000

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

有关相关系统基础安全测试的详细信息,请参阅 HyperVisor 代码完整性就绪测试和受虚拟机监控程序保护的代码完整性 (HVCI)

遵循特定于技术的代码最佳做法

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

文件系统

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

文件系统安全性简介

文件系统安全问题

文件系统的安全功能

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

NDIS - 网络

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

显示

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

打印机

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

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

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

增强设备安装安全性

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

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

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

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

创建安全的设备安装

SetupAPI 使用指南

使用设备安装函数

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

执行对等代码评审

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

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

如果没有合适的人员在内部评审代码,请考虑为此聘请外部帮助。

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

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

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

使用 Visual Studio 中的代码分析调查驱动程序安全性

安全清单项 #13:按照以下步骤使用 Visual Studio 中的代码分析功能检查驱动程序代码中的漏洞。

使用 Visual Studio 中的代码分析功能检查代码中的安全漏洞。 Windows 驱动程序工具包 (WDK) 安装的规则集旨在检查本机驱动程序代码中的问题。

有关详细信息,请参阅 如何为驱动程序运行代码分析

有关详细信息,请参阅 驱动程序的代码分析概述。 有关代码分析的其他背景信息,请参阅深入Visual Studio 2013静态代码分析

若要熟悉代码分析,可以使用示例驱动程序之一,例如特色烤箱示例或 https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured ELAM 早期启动反恶意软件示例 https://github.com/Microsoft/Windows-driver-samples/tree/main/security/elam

  1. 在 Visual Studio 中打开驱动程序解决方案。

  2. 在 Visual Studio 中,对于解决方案中的每个项目,请更改项目属性以使用所需的规则集。 例如:“项目 >> 属性代码 >> 分析 >> 常规”,选择“ 推荐的驱动程序规则”。 除了使用重新提交的驱动程序规则之外,还使用 推荐的本机规则 规则集。

  3. 选择“在解决方案中生成 >> 运行代码分析”。

  4. 在 Visual Studio 中生成输出窗口的“ 错误列表 ”选项卡中查看警告。

选择每个警告的说明以查看代码中的问题区域。

选择链接的警告代码以查看其他信息。

确定是否需要更改代码,或者是否需要添加批注以允许代码分析引擎正确遵循代码的意图。 有关代码注释的详细信息,请参阅 使用 SAL 注释减少 C/C++ 代码缺陷适用于 Windows 驱动程序的 SAL 2.0 注释

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

使用静态驱动程序验证程序检查漏洞

安全清单项 #14:按照以下步骤使用 Visual Studio 中的静态驱动程序验证程序 (SDV) 来检查驱动程序代码中的漏洞。

静态驱动程序验证程序 (SDV) 使用一组接口规则和操作系统模型来确定驱动程序是否与 Windows 操作系统正确交互。 SDV 发现驱动程序代码中的缺陷,这些缺陷可能指向驱动程序中的潜在 bug。

有关详细信息,请参阅静态驱动程序验证器和静态驱动程序验证程序简介。

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

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

  1. 在 Visual Studio 中打开目标驱动程序解决方案。

  2. 在 Visual Studio 中,将生成类型更改为 “发布”。 静态驱动程序验证程序要求生成类型为 release,而不是 debug。

  3. 在 Visual Studio 中,选择“生成 >> 解决方案”。

  4. 在 Visual Studio 中,选择“驱动程序 >> 启动静态驱动程序验证程序”。

  5. 在 SDV 的“规则”选项卡上,选择“规则集”下的“默认值”。

    尽管默认规则发现许多常见问题,但请考虑运行更广泛的 “所有驱动程序规则 ”规则集。

  6. 在 SDV 的“ ”选项卡上,选择“ 开始”。

  7. SDV 完成后,查看输出中的任何警告。 “ ”选项卡显示发现的缺陷总数。

  8. 选择每个警告以加载 SDV 报告页,并检查与可能的代码漏洞关联的信息。 使用报告调查验证结果,并确定驱动程序中未通过 SDV 验证的路径。 有关详细信息,请参阅 静态驱动程序验证程序报告

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

安全清单项 #15:按照以下步骤使用 BinSkim 将编译和生成选项配置为最小化已知安全问题的双倍检查。

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

BinSkim 检查:

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

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

有关 BinSkim 的详细信息,请参阅 BinSkim 用户指南

请按照以下步骤验证安全编译选项是否已在要交付的代码中正确配置。

  1. 下载并安装跨平台 .NET Core SDK

  2. 确认已安装 Visual Studio。 有关下载和安装 Visual Studio 的信息,请参阅 安装 Visual Studio

  3. 有许多选项可用于下载 BinSkim,例如 NuGet 包。 在此示例中,我们将使用 git clone 选项从此处下载: https://github.com/microsoft/binskim 并将其安装在 64 位 Windows 电脑上。

  4. 打开 Visual Studio 开发人员命令提示窗口并创建目录,例如 C:\binskim-master

    C:\> Md \binskim-master
    
  5. 移动到刚刚创建的目录。

    C:\> Cd \binskim-master
    
  6. 使用 git clone 命令下载所有所需的文件。

    C:\binskim-master> git clone --recurse-submodules https://github.com/microsoft/binskim.git
    
  7. 移动到克隆命令创建的新 binskim 字典。

    C:\> Cd \binskim-master\binskim
    
  8. 运行 BuildAndTest.cmd 以确保发布生成成功,并且所有测试都通过。

    C:\binskim-master\binskim> BuildAndTest.cmd
    
    Welcome to .NET Core 3.1!
    ---------------------
    SDK Version: 3.1.101
    
    ...
    
    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64\BinSkim.Sdk.dll
    1 File(s) copied
    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\linux-x64\BinSkim.Sdk.dll
    1 File(s) copied
    
    ...
    
    
  9. 生成过程使用 BinSkim 可执行文件创建一组目录。 移动到 win-x64 生成输出目录。

    C:\binskim-master\binskim> Cd \binskim-master\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64>
    
  10. 显示分析选项的帮助。

C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim help analyze

BinSkim PE/MSIL Analysis Driver 1.6.0.0

--sympath                      Symbols path value, e.g., SRV*http://msdl.microsoft.com/download/symbols or Cache*d:\symbols;Srv*http://symweb. See
                              https://learn.microsoft.com/windows-hardware/drivers/debugger/advanced-symsrv-use for syntax information. Note that BinSkim will clear the
                              _NT_SYMBOL_PATH environment variable at runtime. Use this argument for symbol information instead.

--local-symbol-directories     A set of semicolon-delimited local directory paths that will be examined when attempting to locate PDBs.

-o, --output                   File path to which analysis output will be written.

--verbose                      Emit verbose output. The resulting comprehensive report is designed to provide appropriate evidence for compliance scenarios.

...

设置符号路径

如果要在运行 BinSkim 的同一台计算机上生成要分析的所有代码,通常不需要设置符号路径。 这是因为符号文件在编译的本地框中可用。 如果使用更复杂的生成系统,或者将符号重定向到不同位置, (不与编译的二进制) 一起,请使用 --local-symbol-directories 将这些位置添加到符号文件搜索中。 如果代码引用不属于代码的已编译二进制文件,则可以使用 Window 调试器同情来检索符号,以验证这些代码依赖项的安全性。 如果在这些依赖项中发现问题,可能无法修复它们。 但是,了解通过承担这些依赖项可能面临的任何安全风险可能很有用。

提示

添加引用联网符号服务器) 的符号路径 (时,请添加本地缓存位置以指定用于缓存符号的本地路径。 不这样做可能会极大地损害 BinSkim 的性能。 以下示例指定 d:\symbols 处的本地缓存。 --sympath Cache*d:\symbols;Srv*http://symweb 有关同情的详细信息,请参阅 Windows 调试器的符号路径

  1. 执行以下命令以分析已编译的驱动程序二进制文件。 更新目标路径以指向已编译的驱动程序 .sys 文件。

    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\echo.sys"
    
  2. 有关其他信息,请添加如下所示的详细选项。

    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys" --verbose
    

    注意

    --verbose 选项将为每个检查生成显式通过/失败结果。 如果未提供详细,则只会看到 BinSkim 检测到的缺陷。 通常不建议将 --verbose 选项用于实际自动化系统,因为日志文件的大小增加,而且在发生单个故障时更难选取这些故障,因为它们将嵌入在大量“通过”结果中。

  3. 查看命令输出以查找可能的问题。 此示例输出显示通过的三个测试。 有关规则的其他信息,如 BA2002,请参阅 BinSkim 用户指南

    Analyzing...
    Analyzing 'osrusbfx2.sys'...
    ...
    
    C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys\Debug\osrusbfx2.sys: pass BA2002: 'osrusbfx2.sys' does not incorporate any known vulnerable dependencies, as configured by current policy.
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: pass BA2005: 'osrusbfx2.sys' is not known to be an obsolete binary that is vulnerable to one or more security problems.
    C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys: pass BA2006: All linked modules of 'osrusbfx2.sys' generated by the Microsoft front-end satisfy configured policy (compiler minimum version 17.0.65501.17013).
    
  4. 此输出显示测试 BA3001 未运行,因为该工具指示驱动程序不是 ELF 二进制文件。

    ...
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: notapplicable BA3001: 'osrusbfx2.sys' was not evaluated for check 'EnablePositionIndependentExecutable' as the analysis is not relevant based on observed metadata: image is not an ELF binary.
    
  5. 此输出显示测试 BA2007 的错误。

    ...
    
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: error BA2007: 'osrusbfx2.sys' disables compiler warning(s) which are required by policy.
    A compiler warning is typically required if it has a high likelihood of flagging memory corruption, information disclosure, or double-free vulnerabilities.
    To resolve this issue, enable the indicated warning(s) by removing /Wxxxx switches (where xxxx is a warning id indicated here) from your command line, and resolve any warnings subsequently raised during compilation.
    

若要在 Visual Studio 中启用这些警告,请在项目的属性页中的 C/C++ 下,删除在 “禁用特定警告”中不希望排除的值。

用于在 Visual Studio 2019 中禁用特定警告的对话框的屏幕截图。

Visual Studio 中用于驱动程序项目的默认编译选项可以禁用如下所示的警告。 BinSkim 将报告这些警告。

C4603 - “name”:使用预编译标头后宏未定义或定义不同

C4627 - “description”:在查找预编译标头时跳过

C4986 - “声明”:异常规范与以前的声明不匹配

有关编译器警告的详细信息,请参阅编译器警告(按编译器版本)。

使用其他代码验证工具

安全清单项 #16:使用这些附加工具来帮助验证代码是否遵循安全建议,并探测开发过程中遗漏的差距。

除了上面讨论的Visual Studio Code分析静态驱动程序验证程序和Binskim 之外,还使用以下工具来探测开发过程中遗漏的差距。

驱动程序验证程序

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

硬件兼容性程序测试

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

以下测试是一些测试示例,这些测试对于检查与代码漏洞相关的某些行为可能很有用:

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

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

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

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

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

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

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

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

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

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

请考虑开发自定义域特定安全测试。 若要开发其他测试,请收集软件的原始设计人员的输入,以及熟悉所开发的特定驱动程序类型的不相关的开发资源,以及一个或多个熟悉安全入侵分析和防护的人员。

查看调试器技术和扩展

安全清单项 #17:查看这些调试器工具,并考虑它们在开发调试工作流中的使用。

!acl 扩展格式化并显示访问控制列表的内容 (ACL) 。 有关详细信息,请参阅 确定对象的 ACL!acl

!token 扩展显示安全令牌对象的格式化视图。 有关详细信息,请参阅 !token

!tokenfields 扩展显示访问令牌对象中字段的名称和偏移量, (TOKEN 结构) 。 有关详细信息,请参阅 !tokenfields

!sid 扩展在指定地址显示 SID) (安全标识符。 有关详细信息,请参阅 !sid

!sd 扩展在指定地址处显示安全描述符。 有关详细信息,请参阅 !sd

Microsoft 易受攻击和恶意驱动程序报告中心

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

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

查看安全编码资源

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

查看这些资源以了解有关驱动程序安全性的详细信息

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

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

保护编码组织

卡内基梅隆大学 SEI CERT

卡内基梅隆大学 SEI CERT C 编码标准:开发安全、可靠和安全系统的规则 (2016 版) 。

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

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

SAFECode - https://safecode.org/

CISA 资源

OSR

OSR 提供驱动程序开发培训和咨询服务。 OSR 新闻稿中的这些文章重点介绍了驱动程序安全问题。

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

你必须使用保护 -- 在驱动程序 & 设备安全性

锁定驱动程序 - 技术调查

崩溃和幽灵:司机呢?

案例研究

从警报到驱动程序漏洞:Microsoft Defender ATP 调查发现权限提升缺陷

书籍

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

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

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

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

C 和 C++ 中的安全编码 (软件工程) 第 2 版中的 SEI 系列,Robert C. Seacord

对 Microsoft Windows 驱动程序模型进行编程 (第 2 版) , Walter Oney

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

培训

供应商提供 Windows 驱动程序课堂培训,如下所示:

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

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

SAFECode 也提供免费培训:

SAFECode.org/training

专业认证

CERT 提供 安全编码专业认证

关键要点摘要

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

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

  • 应用最小特权原则:

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

    b. 进一步限制单个 IOCTL 的

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

  • 请注意从 usermode 传入的嵌入指针。 除非捕获并比较缓冲区的值,否则需要在尝试中对其进行探测、访问,并且它们容易出现检查使用时间 (ToCToU) 问题。

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

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

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

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