WDF驱动开发-I/O请求相关(六)

UMDF 版本 2 开始,User-Mode驱动程序框架 (UMDF) 驱动程序如何访问受保护的资源。

处理 UMDF 驱动程序中的客户端模拟

UMDF 驱动程序通常在 LocalService 帐户下运行,无法访问需要用户凭据的文件或资源,例如受保护的文件或其他受保护的资源。 UMDF 驱动程序通常对在客户端应用程序和设备之间流动的命令和数据进行操作。 因此,大多数 UMDF 驱动程序不会访问受保护的资源。

但是,某些驱动程序可能需要访问受保护的资源。 例如,UMDF 驱动程序可能会从客户端应用程序提供的文件将固件加载到设备中。 该文件可能有一个访问控制列表 (ACL) ,可防止未经授权的用户修改文件并控制设备。 遗憾的是,此 ACL 还会阻止 UMDF 驱动程序访问该文件。

框架提供模拟功能,使驱动程序能够模拟驱动程序的客户端,并获取客户端对受保护资源的访问权限。

启用模拟

UMDF 驱动程序的安装包和客户端应用程序都必须启用框架的模拟功能,如下所示:

  • UMDF 驱动程序安装包的 INF 文件必须包含 UmdfImpersonationLevel 指令,并设置允许的最大模拟级别。 仅当 INF 文件包含 UmdfImpersonationLevel 指令时,才会启用模拟; 
  • 客户端应用程序必须为每个文件句柄设置允许的模拟级别。 应用程序使用 Microsoft Win32 CreateFile 函数中的服务质量 (QoS) 设置来设置允许的模拟级别; 
处理 I/O 请求的模拟

UMDF 驱动程序和框架按以下顺序处理 I/O 请求的模拟:

  • 驱动程序调用 WdfRequestImpersonate 方法以指定所需的模拟级别和 EvtRequestImpersonate 回调函数;
  • 框架检查请求的模拟级别。 如果请求的级别大于 UMDF 驱动程序的安装包和客户端应用程序允许的级别,则模拟请求将失败。 否则,框架将模拟客户端并立即调用 EvtRequestImpersonate 回调函数;

EvtRequestImpersonate 回调函数必须仅执行需要所请求模拟级别的操作,例如打开受保护的文件。

框架不允许驱动程序的 EvtRequestImpersonate 回调函数调用框架的任何对象方法。 这可确保驱动程序不会向其他驱动程序回调函数或其他驱动程序公开模拟级别。

最佳做法是,在为该请求调用 WdfRequestImpersonate 之前,驱动程序不应启用 I/O 请求的取消。

WdfRequestImpersonate 方法仅授予驱动程序请求的模拟级别。

将凭据向下传递驱动程序堆栈

当驱动程序收到 WdfRequestTypeCreate-typed I/O 请求时,驱动程序可能会将 I/O 请求向下的驱动程序堆栈转发到内核模式驱动程序。 内核模式驱动程序没有 WdfRequestImpersonate 提供给 UMDF 驱动程序的模拟功能。

因此,如果希望内核模式驱动程序接收客户端的用户凭据 (而不是驱动程序主机进程) 的凭据,驱动程序必须在调用 WdfRequestSend 时设置WDF_REQUEST_SEND_OPTION_IMPERSONATE_CLIENT标志,以便将创建请求发送到 I/O 目标。 如果模拟尝试失败, Send 方法将返回错误代码,除非驱动程序还设置了 WDF_REQUEST_SEND_OPTION_IMPERSONATION_IGNORE_FAILURE 标志。

以下示例演示 UMDF 驱动程序如何使用 WDF_REQUEST_SEND_OPTION_IMPERSONATE_CLIENT 标志将文件创建请求发送到 I/O 目标。 驱动程序的 INF 文件还必须包括 如上所述的 UmdfImpersonationLevel 指令。

WDFIOTARGET iotarget;
WDF_REQUEST_SEND_OPTIONS options;
NTSTATUS status;
WDF_REQUEST_PARAMETERS params;
ULONG sendFlags;  
 
WDF_REQUEST_PARAMETERS_INIT(&params);
WdfRequestGetParameters(Request, &params);
   
sendFlags = WDF_REQUEST_SEND_OPTION_SYNCHRONOUS;
if (params.Type == WdfRequestTypeCreate) {
    sendFlags |= WDF_REQUEST_SEND_OPTION_IMPERSONATE_CLIENT;
}
   
WDF_REQUEST_SEND_OPTIONS_INIT(&options, sendFlags);
if (WdfRequestSend(Request,
                   iotarget,
                   &options
                   ) == FALSE) {
    status = WdfRequestGetStatus(Request);
}

驱动程序不必在将请求发送到 I/O 目标之前调用 WdfRequestImpersonate 。

如果较低级别的驱动程序也转发请求,则客户端的模拟级别会向下传输驱动程序堆栈。

减少安全威胁

若要降低“特权提升”攻击的可能性,应:

  • 尽量避免使用模拟:例如,为了避免使用模拟打开驱动程序必须使用的文件,客户端应用程序可以打开该文件并使用 I/O 操作将文件内容发送到驱动程序;
  • 使用驱动程序所需的最低模拟级别:将驱动程序 INF 文件中的模拟级别设置为尽可能低。 如果驱动程序不需要任何模拟,请不要在 INF 文件中包含 UmdfImpersonationLevel 指令;
  • 最大程度地减少攻击者利用你的驱动程序的机会:EvtRequestImpersonate 回调函数应包含一小部分代码,这些代码仅执行需要模拟的操作。 例如,如果驱动程序访问受保护的文件,则仅在打开文件句柄时需要模拟。 它不需要模拟来读取或写入文件;
UMDF 中的主机进程超时

当反射器向驱动程序主机进程发送关键请求时,主机将启动内部计时器。 默认超时间隔为 60 秒。 关键请求包括即插即用、电源和 I/O 取消。

只要 User-Mode Driver Framework (UMDF) 驱动程序定期执行完成请求的操作,反射器将延长超时期限。 例如,对于删除请求,驱动程序需要定期从删除回调返回。

如果超时期限过期,反射器将生成 WER 错误报告,终止主机进程,并尝试重启设备。 

超时过期是反射器终止主机进程的最常见原因。

可以使用 WDF 验证程序控制应用程序延长超时期限。

支持 UMDF 驱动程序中的内核模式客户端

UMDF 版本 2 开始,User-Mode驱动程序框架 (UMDF) 驱动程序如何支持 内核模式客户端。

内核模式客户端是内核模式驱动程序,用于将 I/O 请求发送到 UMDF 驱动程序。 内核模式驱动程序可能位于 UMDF 驱动程序之上、位于同一设备堆栈中,或者可能位于不同的设备堆栈中。

内核模式驱动程序可以转发从用户模式应用程序收到的 I/O 请求,也可以创建新的 I/O 请求并将其发送到用户模式驱动程序。

如何在 UMDF 驱动程序中支持内核模式客户端

若要启用 UMDF 驱动程序对内核模式客户端的支持,UMDF 驱动程序的 INF 文件必须在其 INF DDInstall 中包含 UmdfKernelModeClientPolicy 指令。WDF 部分。

框架提供了两种对支持内核模式客户端的驱动程序有用的方法。 驱动程序可以调用 WdfRequestGetRequestorMode 方法,以确定 I/O 请求来自内核模式还是用户模式。 如果 I/O 请求来自用户模式,驱动程序可以调用 WdfRequestIsFromUserModeDriver 来确定请求来自应用程序还是来自其他用户模式驱动程序。

内核模式驱动程序的限制

仅当内核模式驱动程序满足以下要求时,UMDF 驱动程序才能处理来自内核模式驱动程序的 I/O 请求:

  • 当内核模式驱动程序发送 I/O 请求时,必须在 IRQL = PASSIVE_LEVEL 运行;
  • 除非驱动程序已将 UmdfFileObjectPolicy INF 指令设置为 AllowNullAndUnknownFileObjects,否则内核模式驱动程序发送到用户模式驱动程序的每个 I/O 请求都必须具有关联的文件对象。 框架之前必须已收到 I/O 管理器创建了文件对象的通知。 此类通知会导致框架调用用户模式驱动程序的 EvtDeviceFileCreate 回调函数,但该回调函数是可选的;
  • I/O 请求不能包含 IRP_MJ_INTERNAL_DEVICE_CONTROL 函数代码;
  • I/O 请求的缓冲区不得包含指向其他信息的指针,因为用户模式驱动程序无法取消引用指针;
  • 如果 I/O 请求包含指定“两者”缓冲区访问方法的 I/O 控制代码 ,则内核模式驱动程序必须在创建 I/O 请求的应用程序的进程上下文中发送 I/O 请求;
  • UMDF 驱动程序可能会在用户模式下修改 I/O 请求的输出数据。 因此,内核模式驱动程序必须验证它从用户模式驱动程序接收的任何输出数据;
  • 内核模式客户端通常应验证 UMDF 驱动程序传递给 WdfRequestCompleteWithInformation的信息值。 如果客户端是 KMDF 驱动程序,则可以调用 WdfRequestGetCompletionParams 以在IO_STATUS_BLOCK结构中获取此信息;

通常,框架不会验证 UMDF 驱动程序传递给 WdfRequestCompleteWithInformation 的信息值。 此参数通常指定传输的字节数。框架仅验证输出缓冲区和 缓冲 I/O 数据访问方法的信息值。 例如,如果访问方法是缓冲 I/O。

使用活动标识符

在框架版本 1.11 及更高版本中,UMDF 驱动程序可以设置和检索ID 的活动标识符。 活动 ID 允许关联多个 I/O 请求,以便可以使用 Windows 事件跟踪 (ETW) 跟踪来跟踪它们。 下面介绍驱动程序可能使用活动 ID 的一些可能方案。

将新请求与现有请求关联

在驱动程序的 I/O 调度回调函数中,可以创建多个框架 I/O 请求作为传入请求的结果。 驱动程序从原始请求获取活动 ID,并通过调用 WdfRequestRetrieveActivityId 和 WdfRequestSetActivityId 在新请求中设置该 ID。

将新请求与现有线程关联

驱动程序可能会在非 I/O 调度线程的线程或工作项中创建新的 I/O 请求。 可以从任何相应的请求或使用与 I/O 调度线程关联的活动 ID 来设置此类请求的活动 ID。 驱动程序可以通过调用 EventActivityIdControl ,然后调用 WdfRequestSetActivityId 来为每个新的 I/O 请求设置标识符来检索与当前线程关联的活动 ID。

如果驱动程序调用 Win32 API 来发送 I/O 请求,它可以从原始请求中检索活动 ID 并将其传播到线程。 然后,I/O 管理器会将与线程关联的活动 ID 应用于任何 I/O 请求数据包 (I/O 请求数据包), 它为响应请求而生成的 I/O 请求数据包。

创建 WDF HID 微型驱动程序

使用 Windows 驱动程序框架 (WDF) 创建人机接口设备 (HID) 微型驱动程序。

可以使用 KMDF 或 UMDF 编写 HID 微型驱动程序。 建议从 vhidmini2 微型驱动程序示例开始。 可以使用 KMDF 或 UMDF 2.x 编译此示例驱动程序。

要提供的内容
  • 将用UMDF的MsHidUmdf.sys 下编写一个较低的Filter驱动程序,或者用 KMDF编写 MsHidKmdf.sys ,这两者都作为操作系统的一部分包含在内。
  • 下载并查看 vhidmini2 示例。
  • 从驱动程序的 EvtDriverDeviceAdd 回调函数调用 WdfFdoInitSetFilter。
  • 创建 I/O 队列以接收 MsHidUmdf.sys 或 MsHidKmdf.sys 从类驱动程序传递到驱动程序的 I/O 请求。
  • 提供一个 EvtIoDeviceControl 回调函数,该函数分支到特定于 IOCTL 的方法处理程序。 查看 WDF HID 微型驱动程序 IOCTL 中所述的 IOCTL ,并确保驱动程序处理设备的相关 IOCTL。
  • 对于 UMDF,如果驱动程序由 ACPI 枚举,可以选择启用选择性挂起。 在设备的硬件密钥中,添加 EnableDefaultIdleNotificationHandler 子项并将其设置为 1。
  • 对于 UMDF,请在 INF 文件的 WDF 特定 DDInstall 节中设置以下 INF 指令:
[hidumdf.NT.Wdf]
UmdfKernelModeClientPolicy = AllowKernelModeClients
UmdfMethodNeitherAction=Copy
UmdfFileObjectPolicy=AllowNullAndUnknownFileObjects
UmdfFsContextUsePolicy = CanUseFsContext2

/*
1. UmdfKernelModeClientPolicy 到 AllowKernelModeClients ,以便可以在堆栈中加载内核模式直通驱动程序;
2. 要复制的 UmdfMethodNeitherAction 允许 UMDF 处理METHOD_NEITHER类型的 IOCTL;
3. UmdfFileObjectPolicy 到 AllowNullAndUnknownFileObjects;
4. UmdfFsContextUsePolicy to CanUseFsContext2;
*/

如果要编写适用于 Windows 7 的 UMDF HID 微型驱动程序, 请下载 Windows 驱动程序工具包 (WDK) 8.1 以获取 HidUmdf.sys的源代码。 然后编写 UMDF 1.11 驱动程序,并在驱动程序包中包含 HidUmdf.sys 和 UMDF 1.11。

体系结构

HID 类驱动程序 (HidClass.sys) 和框架提供冲突的 WDM 调度例程来处理某些 I/O 请求 ,例如)微型驱动程序的即插即用和电源管理请求。 因此,HID 微型驱动程序无法同时链接到类驱动程序和框架。 因此,Microsoft 提供 MsHidUmdf.sys 和 MsHidKmdf.sys,它们是驻留在类驱动程序和微型驱动程序之间的 WDM 驱动程序。

MsHidUmdf.sys 和 MsHidKmdf.sys 调用 HID 类驱动程序的 HidRegisterMinidriver 例程以注册为实际的 HID 微型驱动程序。 尽管这些驱动程序充当设备的函数驱动程序,但它们只是将 I/O 请求从类驱动程序传递到驱动程序 ,因此有时称为传递 驱动程序。 对于 KMDF 和 UMDF,你提供的唯一组件是 HID 微型驱动程序,它是位于直通驱动程序下的较低Filter驱动程序。

  • 0
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值