用户界面特权隔离

写这篇文章源于本人在开发过程中遇到向某个进程发送消息失败而起。

一、用户界面特权隔离

在早期的Windows操作系统中,在同一用户下运行的所有进程有着相同的安全等级,拥有相同的权限。例如,一个进程可以自由地发送一个Windows消息到另外一个进程的窗口。从Windows Vista开始,当然也包括Windows 7、Windows 10,对于某些Windows消息,这一方式再也行不通了。进程(或者其他的对象)开始拥有一个新的属性——特权等级(Privilege Level)。一个特权等级较低的进程不再可以向一个特权等级较高的进程发送消息,虽然他们在相同的用户权限下运行。这就是所谓的用户界面特权隔离(User Interface Privilege Isolation ,UIPI)。

UIPI的引入,最大的目的是防止恶意代码发送消息给那些拥有较高权限的窗口以对其进行攻击,从而获取较高的权限等等,在计算机系统中,这却是一种维护系统安全的合适方式。

二、UIPI的运行机制

在Windows 7中,当UAC(User Account Control)启用的时候,UIPI的运行可以得到最明显的体现。在UAC中,当一个管理员用户登录系统后,操作系统会创建两个令牌对象(Token Object):第一个是管理员令牌,拥有大多数特权(类似于Windows Vista之前的System中的用户),而第二个是一个经过过滤后的简化版本,只拥有普通用户的权限。

默认情况下,以普通用户权限启动的进程拥有普通特权等级(UIPI的等级划分为低等级(low),普通(normal),高等级(high),系统(system))。相同的,以管理员权限运行的进程,例如,用户右键单击选择“以管理员身份运行”或者是通过添加“runas”参数调用ShellExecute运行的进程,这样的进程就相应地拥有一个较高(high)的特权等级。

这将导致系统会运行两种不同类型,不同特权等级的进程(当然,从技术上讲这两个进程都是在同一用户下)。我们可以使用Windows Sysinternals工具集中的进程浏览器(Process Explorer)查看各个进程的特权等级。
用户界面特权隔离_第1张图片
所以,当你发现你的进程之间Windows消息通信发生问题时,不妨使用进程浏览器查看一下两个进程之间是否有合适的特权等级。

三、UIPI所带来的限制

正如我们前文所说,等级的划分,是为了防止以下犯上。所以,有了用户界面特权隔离,一个运行在较低特权等级的应用程序的行为就受到了诸多限制,它不可以:

  • 验证由较高特权等级进程创建的窗口句柄
  • 通过调用SendMessage和PostMessage向由较高特权等级进程创建的窗口发送Windows消息
  • 使用线程钩子处理较高特权等级进程
  • 使用普通钩子(SetWindowsHookEx)监视较高特权等级进程
  • 向一个较高特权等级进程执行DLL注入

但是,一些特殊Windows消息是容许的。因为这些消息对进程的安全性没有太大影响。这些Windows消息包括:

  0x000 - WM_NULL
  0x003 - WM_MOVE
  0x005 - WM_SIZE
  0x00D - WM_GETTEXT
  0x00E - WM_GETTEXTLENGTH
  0x033 - WM_GETHOTKEY
  0x07F - WM_GETICON
  0x305 - WM_RENDERFORMAT
  0x308 - WM_DRAWCLIPBOARD
  0x30D - WM_CHANGECBCHAIN
  0x31A - WM_THEMECHANGED
  0x313, 0x31B (WM_???)

四、修复UIPI问题

基于Windows Vista之前的操作系统行为所设计的应用程序,可能希望Windows消息能够在进程之间自由的传递,以完成一些特殊的工作。当这些应用程序在Windows 7/10上运行时,因为UIPI机制,这种消息传递被阻断了,应用程序就会遇到兼容性问题。为了解决这个问题,Windows Vista引入了一个新的API函数ChangeWindowMessageFilterChangeWindowMessageFilterEx。利用这2个函数,我们可以添加或者删除能够通过特权等级隔离的Windows消息。这就像拥有较高特权等级的进程,设置了一个过滤器,允许通过的Windows消息都被添加到这个过滤器的白名单,只有在这个白名单上的消息才允许传递进来。

已经将添加/移除白名单功能封装到UIPIMsgFilter函数,可以针对特定的窗体添加消息白名单:

PPX_API BOOL UIPIMsgFilter(HWND hWnd, UINT uMessageID, BOOL bAllow) {
            OSVERSIONINFO VersionTmp;
            VersionTmp.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
            GetVersionEx(&VersionTmp);
            BOOL res = FALSE;

            if (VersionTmp.dwMajorVersion >= 6) { // vista above.
                BOOL(WINAPI * pfnChangeMessageFilterEx)(HWND, UINT, DWORD, PCHANGEFILTERSTRUCT);
                BOOL(WINAPI * pfnChangeMessageFilter)(UINT, DWORD);

                CHANGEFILTERSTRUCT filterStatus;
                filterStatus.cbSize = sizeof(CHANGEFILTERSTRUCT);

                HINSTANCE hlib = LoadLibrary(_T("user32.dll"));

                if (hlib != NULL) {
                    (FARPROC &)pfnChangeMessageFilterEx = GetProcAddress(hlib, "ChangeWindowMessageFilterEx");

                    if (pfnChangeMessageFilterEx != NULL && hWnd != NULL) {
                        res = pfnChangeMessageFilterEx(hWnd, uMessageID, (bAllow ? MSGFLT_ADD : MSGFLT_REMOVE), &filterStatus);
                    }

                    // If failed, try again.
                    if (!res) {
                        (FARPROC &)pfnChangeMessageFilter = GetProcAddress(hlib, "ChangeWindowMessageFilter");

                        if (pfnChangeMessageFilter != NULL) {
                            res = pfnChangeMessageFilter(uMessageID, (bAllow ? MSGFLT_ADD : MSGFLT_REMOVE));
                        }
                    }
                }

                if (hlib != NULL) {
                    FreeLibrary(hlib);
                }
            }
            else {
                res = TRUE;
            }

            return res;
        }

你可能感兴趣的:(☆,Windows,Via,C/C++)