调用postmessage返回“访问被拒绝”

4

使用 Delphi 2007... 我有一个应用程序,它使用互斥锁来强制只运行一个实例。在 dpr 单元中,如果互斥锁已经存在,则可以使用 FindWindow 获取正在运行的实例的句柄,到目前为止没有问题。第二个实例通常是由虚拟打印机驱动程序启动,并引用命令行上的文件名。如果有命令行文件引用,则希望简单地向正在运行的实例发送消息并停止新实例。

我正在使用以下代码...

PostMessage(hwnd,WM_STARTUP_MESSAGE,0,0); //hwnd as returned by FindWindow

WM_STARTUP_MESSAGE被定义为WM_APP + 6057

有一个用户遇到了WM_STARTUP_MESSAGE在主线程中未能被处理的问题。从在dpr单元中记录启动信息中可看到,PostMessage返回false,SysErrorMessage(GetLastError)为:

Zugriff verweigert (his windows german translation is Access Denied).

我有很多用户使用这个应用程序,但只收到了2个关于这个问题的报告,并且无法在这里重现。我在Windows 10上使用,至少有一个用户也是如此,另一个用户我不确定。

我在主窗体的OnCreate中使用ChangeWindowMessageFilterEx来允许WM_COPYDATA。我曾经尝试将WM_STARTUP_MESSAGE也包含在其中,但会导致崩溃,因为该函数不喜欢该消息索引值,所以我推测它可能是保留给特定范围的消息值。

有人之前遇到过这个问题并能提供一些指导吗?

1个回答

11

根据PostMessage()的文档:

当消息被UIPI阻止时,使用GetLastError检索到的最后一个错误代码将设置为5(访问被拒绝)。

什么是Vista上的用户界面特权隔离(UIPI)

这也被称为UI特权级别隔离(UIPI)。

作为Vista安全启动的一部分,具有UI的应用程序将在三个不同的特权级别下运行。应用程序窗口可以与相同或较低级别的其他窗口交互,但不能与高级别/权限的应用程序交互

较低特权模式只能在高特权应用程序显式允许的情况下通过调用ChangeWindowMessageFilter()发送消息给高特权应用程序。较低特权的应用程序也只能读取由高特权应用程序拥有的HWND。

Internet Explorer是以最低特权级别运行的示例进程。

Windows完整性机制设计

UIPI实现了在Windows子系统中的限制,可以防止低权限应用程序发送窗口消息或在高权限进程中安装钩子。高权限应用程序允许向低权限进程发送窗口消息。这些限制是在SendMessage和相关窗口消息函数中实现的。并非所有从低权限进程发送到高权限进程的窗口消息都会被阻止。一般来说,“读取”类型的消息,例如WM_GETTEXT,可以从低权限窗口发送到高权限窗口。但是,写入类型的消息,例如WM_SETTEXT,将被阻止。
UIPI不会干扰或更改相同权限(或完整性)级别的应用程序之间的窗口消息传递行为。UIPI通过阻止以下行为来防止低权限进程访问高权限进程。低权限进程无法:
- 对运行具有较高权限的进程进行窗口句柄验证。 - 使用SendMessage或PostMessage发送到以较高权限运行的应用程序窗口。这些API返回成功,但会默默地丢弃窗口消息。 - 使用线程钩子附加到以较高权限运行的进程。 - 使用日志钩子监视以较高权限运行的进程。 - 对以较高权限运行的进程执行动态链接库(DLL)注入。
当错误发生时,您所发布的HWND属于一个以比发布消息的进程更高的完整性/权限级别运行的进程。您可以使用SysInternals' Process Explorer来检查。
您说你的应用程序的第二个实例是由虚拟打印机驱动程序运行的,因此该驱动程序可能在低于由用户启动的应用程序实例的完整性级别上运行。 ChangeWidowMessageFilter/Ex()对非系统消息ID没有任何限制(某些系统消息不能被过滤)。它肯定不会在用户定义的消息ID上崩溃。如果您没有权限更改给定HWND/MsgID的消息过滤器,则该函数将简单地返回FALSE,并且GetLastError()将告诉您原因。
如果您遇到崩溃问题,则与您代码中的其他内容有关。
此外,表单的OnCreate事件不是调用ChangeWindowMessageFilterEx()的最佳位置。在程序的生命周期中,表单可能需要动态地重新创建其HWND,甚至可能多次。每当创建新的HWND时,都必须再次调用ChangeWindowMessageFilterEx()。解决这个问题的最佳方法是覆盖表单的虚拟CreateWnd()方法,例如:
type
  TMyForm = class(TForm)
  protected
    procedure CreateWnd; override;
  end;

procedure TMyForm.CreateWnd;
begin
  inherited;
  ChangeWindowMessageFilterEx(Handle, WM_STARTUP_MESSAGE, MSGFLT_ALLOW, nil);
end;

如果一个应用程序在Windows启动时运行(通过注册表RUN项),它是否以更高的完整性级别运行? - John Taylor
它在用户的普通(中等)完整性级别下运行,除非可执行文件已使用icacls标记为不同的完整性级别,或具有UAC提升清单。此外,如果恶意应用程序可以将自己简单地放入“运行”键并自动获得高完整性访问权限而无需用户的许可,那么您不认为这将是一种安全漏洞吗? - Remy Lebeau
1
@John - 如果你的应用程序的第一个实例没有以更高的权限运行,那么你根本不需要调用ChangeWindowMessageFilterEx。 - Sertac Akyuz
@SertacAkyuz 低完整性进程无法发送到中等完整性进程,也无法发送到高完整性进程。而且还有一些用于系统的完整性级别。每个实例的运行方式都很重要。如果它们全部由用户运行,它们将处于相同的完整性级别,但它们并非全部都由用户运行,因此调用者的完整性会影响事情。 - Remy Lebeau
1
@SertacAkyuz,我理解你的评论是说中间完整性进程根本不需要使用ChangeWindowMessageFilter/Ex()。为了澄清一下,"如果你的应用程序的第一个实例没有比第二个实例拥有更高的权限,则根本不需要调用ChangeWindowMessageFilterEx。" - Remy Lebeau
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接