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

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

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

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

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

启用模拟

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

  • UMDF 驱动程序安装包的 INF 文件必须包含 UmdfImpersonationLevel 指令,并设置允许的最大模拟级别。 仅当 INF 文件包含 UmdfImpersonationLevel 指令时,才会启用模拟。 有关设置模拟级别的详细信息,请参阅 在 INF 文件中指定 WDF 指令

  • 客户端应用程序必须为每个文件句柄设置允许的模拟级别。 应用程序使用 Microsoft Win32 CreateFile 函数中的服务质量 (QoS) 设置来设置允许的模拟级别。 有关这些设置的详细信息,请参阅 Windows SDK 文档中 CreateFiledwFlagsAndAttributes 参数。

处理 I/O 请求的模拟

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

  1. 驱动程序调用 WdfRequestImpersonate 方法以指定所需的模拟级别和 EvtRequestImpersonate 回调函数。

  2. 框架检查请求的模拟级别。 如果请求的级别大于 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 回调函数应包含一小部分代码,这些代码仅执行需要模拟的操作。 例如,如果驱动程序访问受保护的文件,则仅在打开文件句柄时需要模拟。 它不需要模拟来读取或写入文件。