操作系统服务的Session 0隔离,通过Session 0隔离,Windows 7实现了各个Session之间的独立和更加安全的互访,使得操作系统的安全性有了较大的提高。从操作系统服务的Session 0隔离尝到了甜头后,雷德蒙的程序员们仿佛爱上了隔离这一招式。现在他们又将隔离引入了同一个Session之中的各个进程之间,带来全新的用户界面特权隔离。

  用户界面特权隔离

  在早期的Windows操作系统中,在同一用户下运行的所有进程有着相同的安全等级,拥有相同的权限。例如,一个进程可以自由地发送一个Windows消息到另外一个进程的窗口。从Windows Vista开始,当然也包括Windows 7,对于某些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)查看各个进程的特权等级。 (http://www.microsoft.com/technet/sysinternals)

  

  图1 进程浏览器

  下图展示了以不同特权等级运行的同一个应用程序,进程浏览器显示了它们拥有不同的特权等级:

  图2  不同特权等级的同一应用程序

  所以,当你发现你的进程之间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上运行时,因为UIPI机制,这种消息传递被阻断了,应用程序就会遇到兼容性问题。为了解决这个问题,Windows Vista引入了一个新的API函数ChangeWindowMessageFilter。利用这个函数,我们可以添加或者删除能够通过特权等级隔离的Windows消息。这就像拥有较高特权等级的进程,设置了一个过滤器,允许通过的Windows消息都被添加到这个过滤器的白名单,只有在这个白名单上的消息才允许传递进来。

  如果我们想容许一个消息可以发送给较高特权等级的进程,我们可以在较高特权等级的进程中调用ChangeWindowMessageFilter函数,以MSGFLT_ADD作为参数将消息添加进消息过滤器的白名单。同样的,我们也可以以MSGFLT_REMOVE作为参数将这个消息从白名单中删除。例如:

// 需要的头文件#include <windows.h>
#include <tchar.h>
#include "resource.h"// 全局对象
HINSTANCE g_hInstance;
HWND g_hFirstWnd;// 消息处理函数INT_PTR CALLBACK PingPongDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);// 主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nCmdShow) {g_hInstance = hInstance;// 获得窗口的句柄g_hFirstWnd = FindWindow(NULL, _T("用户界面特权等级隔离"));TCHAR text[256];LoadString(g_hInstance, g_hFirstWnd ? IDS_PING : IDS_PONG, text, 256);return DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_PINGPONG), NULL, PingPongDlgProc, (LPARAM)text);
}// 处理对话框消息
INT_PTR CALLBACK PingPongDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) {static LPCTSTR pszPingPong;static HWND hWndSend;static UINT uMsgBall;TCHAR sMessageBall[256];switch (message) {case WM_INITDIALOG:pszPingPong = (LPCTSTR)lParam;if(!LoadString(g_hInstance, IDS_MESSAGE_BALL, sMessageBall, 256))return FALSE;// 注册新的全局自定义消息,这里的全局,针对此进程而言uMsgBall = RegisterWindowMessage(sMessageBall);if(!uMsgBall) return FALSE;// 调用ChangeWindowMessageFilter函数,允许此消息可以传递进来ChangeWindowMessageFilter(uMsgBall, MSGFLT_ADD);// 开始发送消息给其他进程,也就是此应用程序的另一个实例if (g_hFirstWnd)PostMessage(g_hFirstWnd, uMsgBall, (WPARAM)hwndDlg, 0);return TRUE;case WM_TIMER:KillTimer(hwndDlg, 1);SetDlgItemText(hwndDlg, IDC_TEXT, _T(""));PostMessage(hWndSend, uMsgBall, (WPARAM)hwndDlg, 0); //Send the ball.return TRUE;default:if(message == uMsgBall) {        // 如果是我们自定义的消息SetDlgItemText(hwndDlg, IDC_TEXT, pszPingPong);hWndSend = (HWND)wParam;SetTimer(hwndDlg, 1, 500, 0);    return TRUE;}}return FALSE;
}

内容导航

  现在,我们可以调用ShellExecute,以不同的特权等级运行这个应用程序,他们虽然处于不同的特权等级,但是由于我们使用ChangeWindowMessageFilter将自定义的消息添加进了白名单,他们都可以处理这个自定义的消息了。

   // 应用程序名称LPCTSTR exeName = _T("UIPIDemo.exe");// 以不同的特权等级运行同一个应用程序// 更高权限HINSTANCE h1 = ShellExecute(NULL, _T("runas"), exeName, NULL, NULL, SW_SHOWDEFAULT);// 中等权限HINSTANCE h2 = ShellExecute(NULL, _T("open"), exeName, NULL, NULL, SW_SHOWDEFAULT);

  在Windows 7中,为了更加灵活地控制消息的传入,它引入了一个新的函数ChangeWindowMessageFilterEx,这个新的扩展函数可以为某个特定的窗口制定消息白名单,而不是像ChangeWindowMessageFilter一样为整个进程制定白名单。

// Windows 7新引入的函数
BOOL ChangeWindowMessageFilterEx(HWND hWnd, UINT message, DWORD action,PCHANGEFILTERSTRUCT pChangeFilterStruct
);

  在这个函数中,参数action表示这个函数的动作,它可以是MSGFLT_ALLOW (类似于 MSGFLT_ADD),MSGFLT_DISALLOW (类似于 MSGFLT_REMOVE), 和 MSGFLT_RESET,表示将窗口设置为它的默认过滤器。

  托管代码中绕过UIPI

  以上的例子演示了在非托管代码中调用ChangeWindowMessageFilter实现消息过滤白名单,允许消息通过用户界面特权隔离的过程。在托管代码中,我们还是使用这个API函数。为了便于使用,我们对这个API函数做一些包装。在托管代码中,我们用一个类来封装所有我们所需要的API函数:

// 用类封装对API函数的调用
internal static class NativeWrappers {[DllImport("user32")]public static extern uint RegisterWindowMessage(string msg);[DllImport("user32")]public static extern bool PostMessage(IntPtr hWnd,uint msg, IntPtr wParam, IntPtr lParam);public enum ChangeWindowMessageFilterFlags : uint {Add = 1, Remove = 2};[DllImport("user32")]public static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags);}

  完成API的封装后,我们就可以在主程序中直接使用这个类,完成进程消息过滤器白名单的设置。

public PingPongForm() {InitializeComponent();// 注册消息_message = NativeWrappers.RegisterWindowMessage("BALL");if(_message == 0)Close();else {// 添加可以通过的消息NativeWrappers.ChangeWindowMessageFilter(_message,NativeWrappers.ChangeWindowMessageFilterFlags.Add);// 发送消息NativeWrappers.PostMessage(Program.hOtherForm,_message, Handle, IntPtr.Zero);}}

  用户特权等级隔离,就像进程窗口的门神,把不受欢迎的Windows消息隔离在外,把列在客人名单上的Windows消息请进来。门神守候,家宅无忧!

VS与Win7 共舞:用户界面特权隔离相关推荐

  1. 与调试器共舞 - LLDB 的华尔兹

    转自:http://www.cocoachina.com/ios/20141219/10709.html 你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? 1 NSLog(@"%@& ...

  2. 与ObjectDataSource共舞

    4,ORM组件XCode(与ObjectDataSource共舞) XCode为了能更方便的解决大部分问题,不得不"屈身"于ObjectDataSource. 先上一个经典例子(O ...

  3. 谷歌参展攻略!AI皮影戏、3D作画、与AI共舞...嗨翻魔都(附视频)

    来源:大数据文摘 作者:睡不着的Iris.魏子敏 本文约3200字,建议阅读8分钟. 本文介绍了谷歌在上海举办艺术展,带领大家看看如何将人工智能和绘画.音乐和皮影戏全面结合起来. 谷歌来上海啦! 虽然 ...

  4. “云计算”三部曲之二:与“云”共舞——再谈云计算

    引言:去年,我曾在一篇名为<未来计算在"云-端">的文章中指出,纯"云计算"并不是启动计算未来的"万能钥匙","云+端 ...

  5. mongodb与java结合_MongoDB初探系列之四:MongoDB与Java共舞

    MongoDB初探系列之四:MongoDB与Java共舞 来源:互联网 作者:佚名 时间:2015-08-05 08:20 对各位注意到这个帖子的朋友说一声对不起,我不是故意的测试服务器一直没关,一忙 ...

  6. 与毒”共舞30年!清华美女研究生为何放弃高薪,选择特招入伍?背后的原因令人泪崩......

    全世界只有3.14 % 的人关注了 爆炸吧知识 从武汉新冠疫情爆发到如今,陈薇没有一天休息.短短半年间,54岁的她头发从黑到白,也哭了好几次. 刚去武汉-现在 陈薇的母亲也在电视上看到了女儿的变化:& ...

  7. 雨林木风与微软数年博弈:蚂蚁和大象共舞

    http://www.cnbeta.com/articles/105232.htm 虎年春节刚过,雨林木风公司位于松山湖的新大楼内,装修工人们又忙碌了起来. 雨林木风, 曾被业界称为"三大X ...

  8. 让R与Python共舞

    转载自:http://ices01.sinaapp.com/?p=129        R(又称R语言)是一款开源的跨平台的数值统计和数值图形化展现 工具.通俗点说,R是用来做统计和画图的.R拥有自己 ...

  9. 深度解密阿里达摩院:如何让科技与商业共舞

    恐怕再没有一个科研机构被赋予这么诗意的名字,达摩院. 它来源于武侠小说,作为武学最高研究机构,达摩院代表了修为的最高境界.同样,科研也需要精进.执着和专注的精神. 或许是源于对武学的痴迷,2017年1 ...

最新文章

  1. Loader的load方法和loadBytes方法LoaderContext参数
  2. Hinton神经网络公开课10 Combining multiple neural networks to improve generalization
  3. 如何设置Server-U的IP限制
  4. java中的fd是什么意思_java中关键字和保留字分别是什么意思
  5. 国科微电子:芯片将是国内企业下一个发力点
  6. 剑指offer——面试题55:字符流中第一个不重复的字符
  7. [BZOJ4913][SDOI2017]遗忘的集合
  8. PHM算法与智能分析技术
  9. zzuli:1047对数表
  10. 滤波器基础02——滤波器的传递函数与性能参数
  11. 远程 交换机 日志服务器,交换机怎么配置 日志发送日志服务器
  12. 人工智能期末考试复习
  13. vue设置浏览器顶部小图标
  14. 《创造成功本能》 博客思听 2011年2月
  15. linux使用man守护进程,man手册的使用
  16. 获取html中光标位置
  17. 华为 1+X《网络系统建设与运维(中级)》认 证实验模拟上机试题
  18. 双目立体匹配步骤详解
  19. Early Stopping
  20. Yolov5部署TensorRT加速

热门文章

  1. Istio调用链埋点原理剖析—是否真的“零修改”?
  2. 2018年自主车企销量目标完成情况 这几家企业估计要凉
  3. js获取微信验证实现微信自动跳转 检验code操作
  4. Python全栈 进阶(进阶内容都在这了)
  5. 又一年度热销爆款,OPPO R11上市一个月勇夺线下销量第一
  6. IE6/IE7中JavaScript json最后一个键值后不能增加逗号
  7. libtool的工作原理
  8. oracle spatial(一)开端
  9. CCF201803-1 跳一跳(序列处理)
  10. ASP.NET Core 入门教程 2、使用ASP.NET Core MVC框架构建Web应用