根据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;
icacls
标记为不同的完整性级别,或具有UAC提升清单。此外,如果恶意应用程序可以将自己简单地放入“运行”键并自动获得高完整性访问权限而无需用户的许可,那么您不认为这将是一种安全漏洞吗? - Remy LebeauChangeWindowMessageFilter/Ex()
。为了澄清一下,"如果你的应用程序的第一个实例没有比第二个实例拥有更高的权限,则根本不需要调用ChangeWindowMessageFilterEx。" - Remy Lebeau