WH_JOURNALRECORD 的 SetWindowsHookEx 在 Vista/Windows 7 下失败

Posted

技术标签:

【中文标题】WH_JOURNALRECORD 的 SetWindowsHookEx 在 Vista/Windows 7 下失败【英文标题】:SetWindowsHookEx for WH_JOURNALRECORD fails under Vista/Windows 7 【发布时间】:2012-02-28 06:08:24 【问题描述】:

我正在准备一个Delphi模块,它在线程中设置一个钩子来记录一个宏:

FHandleRec  := SetWindowsHookEx(WH_JOURNALRECORD, FRecordProc, HInstance, 0);
FHandlePlay := SetWindowsHookEx(WH_JOURNALPLAYBACK, FPlayProc, HInstance, 0);

这在 WinXP 上运行良好,但在 Vista/Windows 7 上失败并显示ERROR_ACCESS_DENIED。 我在 Google (this) 中找到了引用 (that)。引用:

较低权限的进程不能: ... 使用日志挂钩来监视 更高权限的进程。

试过了,没有成功:

    以管理员身份运行应用程序。可能线程已启动 权限低于主线程(虽然我不是 100% 当然) 使用管理员安全上下文模拟线程 也无济于事。

代码示例:

if LogonUser(PWideChar(sAdminUser), PWideChar(sDomain), PWideChar(sPwd),
             LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken) then 
begin  
  if not ImpersonateLoggedOnUser(hToken) then
    raise Exception.Create('Error impersonating the user');
end;
FHandleRec := SetWindowsHookEx(WH_JOURNALRECORD, FRecordProc, HInstance, 0);

LogonUserImpersonateLoggedOnUser 执行时不会出错。

其他尝试的可能性:

    永久关闭 UAC。这有帮助,但我不能强制模块 用户这样做。 模块客户签署应用程序并将其放入受信任的 地点。没有尝试过,但这使模块从根本上复杂化 供用户使用。 将模块放入某个签名的应用程序并分发EXE。那 会破坏一些核心功能。

能否请您显示在 Visa / Windows 7 下设置挂钩的代码或建议可行的解决方案?

【问题讨论】:

您需要嵌入清单以向用户请求提升权限。我怀疑这属于“极其复杂”的类别。 【参考方案1】:

再次仔细阅读that article 的“用户界面权限隔离”部分。它指的是完整性级别,而不是用户权限。这就是为什么冒充另一个用户并不能解决问题的原因。 完整性级别是在进程首次启动时建立的,不能在代码中动态更改。

用户界面特权隔离 (UIPI) 是其中一种机制 这有助于将以完全管理员身份运行的进程与 以低于管理员权限的帐户运行的进程 相同的交互式桌面。 UIPI 特定于窗口和 图形子系统,称为 USER,支持窗口和用户 界面控件。 UIPI 阻止较低权限的应用程序 使用 Windows 消息将输入从一个进程发送到更高 特权过程。将输入从一个进程发送到另一个进程允许 在没有用户的情况下将输入注入另一个进程的进程 提供键盘或鼠标操作。

Windows Vista 通过定义一组用户界面来实现 UIPI 以分层方式的特权级别。级别的性质 是这样的,更高的权限级别可以发送窗口消息到 在较低级别运行的应用程序。但是,较低的级别不能 将窗口消息发送到在更高级别运行的应用程序窗口。

用户界面权限级别为进程级别。当一个 进程初始化,用户子系统调用安全 子系统来确定在 进程的安全访问令牌。桌面完整性级别由 进程创建时的安全子系统 改变。因此,用户界面权限级别也由 用户子系统在进程创建且未更改时。

标准用户运行的所有应用程序都具有相同的用户界面 特权级别。 UIPI 不会干扰或改变 相同权限级别的应用程序之间的窗口消息传递。 UIPI 对属于 管理员组,并且可能将应用程序作为标准运行 用户(有时称为具有过滤访问令牌的进程) 以及使用完整管理员访问令牌运行的进程 同一个桌面。 UIPI 阻止较低权限的进程 通过阻止列出的行为来访问更高权限的进程 下面。

使用日志挂钩来监控更高权限的进程。

根据this article,您的应用需要一个同时指定requestedExecutionLevel=requireAdministratoruiAccess=True 的UAC 清单。 UIAccess 权限很重要:

通过在 requestedPrivileges 属性中指定 UIAccess=”true”, 应用程序要求绕过 UIPI 限制 ... 使用 UIAccess 权限启动的进程:

可以设置日志挂钩。

【讨论】:

雷米,非常感谢您的回答。假设我有进程外 COM 服务器 - 使用 uiAccess=True 签名和显示的 exe。服务器设置挂钩程序并通过 COM 回调与用户应用程序交换数据。因此,所有处理仍由用户应用程序执行。问题:当一个标准的 UI 应用程序(未签名并在没有权限提升的情况下运行)使用这样的 COM 服务器时,服务器是否仍然能够设置日志挂钩? @AlexeyDaryin:顾名思义,进程外 COM 服务器在自己的进程中运行。标准app进程的权限不会影响COM服务器进程的权限。不过,您可能需要使用 CoInitializeSecurity() 来允许低权限进程连接和使用您的 COM 服务器。 @AlexeyDaryin,如果您不想提升具有 UAC/UIAccess 权限的主应用程序,那么您必须提升另一个进程,无论是 COM 服务器还是服务或任何其他进程你,任何适合你的需要。

以上是关于WH_JOURNALRECORD 的 SetWindowsHookEx 在 Vista/Windows 7 下失败的主要内容,如果未能解决你的问题,请参考以下文章

使用 WH_JOURNALRECORD 和取消似乎确实返回了 WM_CANCELJOURNAL

SetWindowsHookEx 与 Qt4 中的 WH_JOURNALRECORD 挂钩

启动流程 TopMost

QT 缩放到多边形

HOOK(钩子)函数

QApplication::setWindowIcon 适用于 Windows XP,但不适用于 Windows 7