http://tech.it168.com/a2009/0924/737/000000737968.shtml

【IT168 专稿】在上文中,我们介绍了操作系统服务的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消息请进来。门神守候,家宅无忧!

UIPI VS与Win7 共舞:用户界面特权隔离的更多相关文章

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

    你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? NSLog(@"%@", whatIsInsideThisThing); 或者跳过一个函数调用来简化程序的行为? NSNu ...

  2. 与中国最顶尖sharepoint工程师共舞

    最近又跳了,来到某家外企.自以为善能称心如意,谁知乃井里之蛙. 给我的最大感触是,做sharepoint一定要做过非常大型的部署开发,没有经过这种淬炼,天天闷声研究,做一些页面功能,对技术提升毫无帮助 ...

  3. 让R与Python共舞

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

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

    你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? 1 NSLog(@"%@", whatIsInsideThisThing); 或者跳过一个函数调用来简化程序的行为? 1 ...

  5. “双十二”年终盛典,Guitar Pro邀您一起倾情共舞

    躲过了双十一,躲不过双十二,隐约昨天还是双十一,马上双十二又叕来了,弱弱的问一句“你们的手长粗了来了吗?”在这即将结束的年终盛典里,各商家又将如“双十一”般纷纷使出浑身解数,作为吉他最佳拍档的编曲软件 ...

  6. NGK公链:去中心化交易+挖矿生态体系共舞

    NGK生态公链是一个安全.透明.专业的去中心化商业应用平台.作为一条具有技术信任甚至是公众信任的公链,NGK以区块链技术为支撑,利用区块链透明.公正.公开.数据不可篡改.分布式存储.可追溯等技术优势, ...

  7. [C#] 與Android共舞–透過GET方式傳資料給Server(含解決中文編碼問題) (转帖)

    上一篇文章分享了透過POST 方式傳資料回Server,這一篇來談談有關於透過GET的方式傳遞 首先,如我預期的一樣,透過網址傳遞,會產生編碼問題,這邊我就順代解掉,希望有碰到的人 可以不用為此煩惱. ...

  8. [C#] 與Android共舞–手機post資料給Server (转帖)

    最近在搞安卓,跟Server溝通是一定要的,這範例很簡單,就是我在Android 上面,透過POST 的方式傳資料給 Server ,則Server 收到值後直接回傳, Server side 是用a ...

  9. JS函数式编程【译】2.2 与函数共舞

随机推荐

  1. Java for LeetCode 117 Populating Next Right Pointers in Each Node II

    Follow up for problem "Populating Next Right Pointers in Each Node". What if the given tre ...

  2. java多线程---基础

    一, java多线程----线程与进程 进程: 程序(任务)的执行过程,拥有资源(共享内存,共享资源)和线程(一个或者多个,至少一个).  例如:打开任务管理器,qq,chrome,都属于进程. 线程 ...

  3. PAT 甲级 1128. N Queens Puzzle (20) 【STL】

    题目链接 https://www.patest.cn/contests/pat-a-practise/1128 思路 可以 对每一个皇后 都判断一下 它的 行,列 ,左右对角线上 有没有皇后 深搜解决 ...

  4. Unity基本API总览

  5. <密码学入门>关于RSA算法的加密解密及代码实现

    RSA算法 是一种公钥加密算法,RSA算法相比别的算法思路非常清晰,但是想要破解的难度非常大.RSA算法基于一个非常简单的数论事实:两个素数相乘得到一个大数很容易,但是由一个大数分解为两个素数相乘却非 ...

  6. ActiveMQ持久化机制

    用户注册成功后发短信提醒 同步http 异步mq JMS中两种通讯模式: 发布订阅   一对多  topic   去过消费者集群的话 都会消费 消息队列   点对点 queue  去过消费者集群的话 ...

  7. python类初探

    class human: is_alive=True is_man=True def __init__(self,age): print('this is __init__() method, whi ...

  8. PHP闭包详解

    匿名函数 提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似PHP闭包实现主要就是靠它.声明一个匿名函数是这样: $func = function() { }; //带结束符 可以看 ...

  9. laravel基础课程---8、laravel响应和视图(响应是什么)

    laravel基础课程---8.laravel响应和视图(响应是什么) 一.总结 一句话总结: 就是向请求返回的响应数据(一般为html(视图),当然也可以是变量值):所有的路由及控制器必须返回某个类 ...

  10. FileHeader 详解文件头部注释-sublimeText

    FileHeader 前言: 直奔主题吧,sublimeText 的大部分插件都是非常好用,易用的,而且很容易找到资料.这次用 FileHeader 发现网上的资料很少,而且大部分说得都很模糊,或者直 ...