UAC+MIC+UIPI

UAC(User Account Control)应该没什么好讲的了。用过Vista的人都知道。典型的示例就是弹出对话框、进程权限的提升,以及文件系统和注册表的重定向(File and Registry Virtualization)。所以今天乘着兴致,继续研究与UAC息息相关的另外两项Vista的安全方面的创新技术MIC(完整性级别控制)和UIPI(用户界面特权隔离)。

MIC和UIPI其实是Vista中进程隔离所使用的一种技术。它通过为操作系统引入一项新的概念:标签SID,来标识进程的Access Token的完整性级别。说得通俗点,就是Access Token中加入了一个新的标志位,用以帮助操作系统判断该进程的安全级别。注意,这里只是针对用户态的进程,内核态的进程已经够霸道,所以根本就不需要了。该SID将进程分为了如下几个等级:

  1. Low Mandatory:最低级别,如IE等一些相对隐患较大的进程一般为这个完整性级别;
  2. Medium Mandatory:以普通用户启动的进程;
  3. High Mandatory:经过权限提升的进程;
  4. Sys Level:以Local Service和System账号运行的进程。

在传统的操作系统中,判断进程是否有权利访问某个资源都是通过查询资源对象的ACL(Account Control List)来进行的。如果进程的Access Token满足ACL中的要求,那么就可以对这个资源进行访问。通常我们所熟悉的操作就是Right Click一个文件夹,Share and Serurity菜单项中的Permission来设置ACL。然而Vista中的MIC在这个限制条件上又加了一个安全层,就是当进程的完整性级别>=资源的级别的时候,访问才能进行。好处是什么呢?就是即使你有Admin的权限,但是不满足这个条件,你仍然会无所适从。

UIPI,这个比较“萨根”!默认情况下,禁止低完整性级别的应用程序向高级别的发送Windows消息。可见Vista在安全性方面的把关是非常之严格的。诸如以前的“粉碎性攻击”,一律都在Vista上失效了。当然这种高安全级别的限制会对一些Legacy的程序造成一定的兼容性问题,但是我个人还是觉得值得。[这里要感谢某位MVP,他给出了一个方法来关闭UIPI,并且得到了Vista安全小组的确认]可以通过修改HKLM\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\POLICIES\SYSTEMENABLEUIPI来关闭UIPI。

一些小实验帮助加深理解:

  1. Vista中以Administrator方式打开一个Notepad;
  2. Windows+R输入cmd,通过Tasklist |find /i "Notepad"来获得这个Notepad的PID;
  3. TaskKill /pid "<PID>"来关闭这个Notepad;结果如下图:

MIC UIPI

Hope this can help…

CR3寄存器

Virtual Memory的实现是需要硬件支持的!
 
依稀记得n久在学校里学《操作系统原理》的时候,曾经介绍过OS中为了实现内存分页的各个表项啊什么XX的,用来将某逻辑地址,通过某种恶心的查询策略,翻译成CPU可寻址的物理地址。现在在工作中由于经常接触汇编级别的调试,迂回于Process的4G地址空间中,猛然回想起之前老师所说的,现在看起来是如此的清晰和真实|||
 
不知道还有多少人记得CPU的保护模式?呵呵,狞笑…
 
  • 实模式:特征是实地址模式,地址的转换就是很简单的:段+偏移=物理地址。那时老师总会强调一句,DOS就是在这个模式下运行的。汗~
  • 保护模式:(重点来了)就是它的提出,为多任务和Virtual Memory提供了硬件上的支持。所以有了现在的Process Space这个东东。此外加上了特权的概念。Intel CPU提供了Ring0到Ring3的4种模式,Windows使用了其中的两种Ring0和Ring1,分别对应我们耳熟能详的Kernel和User模式。
  • 系统管理模式:BIOS执行电源管理,安全检查等等特定任务。

Virtual Memory和Process Space在保护模式中实现。接下来比较重要的问题在于,他们是相对于一个进程的。不同的进程拥有自己的所属。所以当CPU在执行不同应用程序的时候,他们是要切换的!!那怎么切换呢?呵呵,狞笑…其实答案已经在Title中给出。CR3寄存器!!!

从一个Virtual Address转换成一个Physical Address,需要经历“段+偏移”产生的线性地址这么一个中间产物。一般格式如下(32位线性地址):

  1. 31~22位:页目录地址偏移;
  2. 21~12:页表偏移。它含有一个Flag,用以标识地址是在RAM中还是已经被交换到硬盘中了,如果在硬盘中,OK那么Page Fault,SWAP;
  3. 11~0:Offset,相对于该页的偏移;
  4. 页目录,也就是当前进程的内存页,它的基地址就保存在CR3寄存器中!

所以我们把线性地址翻译成物理地址,需要CR3,然后获得对应的页目录项、页表项,然后将他们累加就可以了。所以CR3很关键,它的值改变了,那就不能在当前Process Space中寻址了。所以如果CR3设置为另一个进程的页目录基地址,那么CPU就是在切换地址空间。

Hope this can help…

/GS

 
好的文章总是耐人寻味的。反复几遍读下来,结合自己平时在CER bucket的工作经历,感触颇深,因此不写点东西出就觉得难受|||Brandon Bray的这篇经典文章,我算是读得比较晚的了,所有的在buffer overrun方面的knowledge base都是自己平时东拼西凑(哦,记得Jeffrey以前的Tech Talk),总结出来的。这篇文章总算是系统地学习了一下Visual Studio.net所提供的/GS编译开关的用途了。
 
对于stack方面的corrupt,分为2种:1)Buffer Overrun:缓冲区溢出使得高地址的栈内容被破坏;2)Buffer Underflow:缓冲区溢出使得高地址的栈内容被破坏。通常前者最常见,也是破坏比较严重的。为什么?因为Return Address在这个高地址上,它一般是主要的攻击目标。然后,/GS就是用来防止通过Return Address来黑程序的一种手段。要注意的是,这不是一个万能措施,能够提供攻击的地点还有很多,诸如Exception Handler的地址,对象的虚表,函数指针,甚至是Out-of-Range的数组index,都能被利用,以通过代码注入的方式,让黑客得逞。/GS一般只有在分配char buffer[??]的地方插入代码。
 
在平时工作中,最近汇编看得比较多,诸如以下的符号几乎是司空见惯。(但是有谁会去主动关心它的作用呢?)
00411323  sub         esp,68h // 栈多分配了4字节,用来存放Cookie
00411326  mov         eax,dword ptr [___security_cookie (416004h)]
0041132B  xor         eax,ebp
0041132D  mov         dword ptr [ebp-4],eax
;————————————————–
00411350  mov         ecx,dword ptr [ebp-4]
00411353  xor         ecx,ebp
00411355  call        @ILT+15(@__security_check_cookie@4) (411014h)
___security_cookie是一个CRT的全局变量,它提供了一个Cookie值(这个Cookie被安插在return address和local object之间,这样有效的做到了对于Overrun的Check)。CRT会通过调用__security_init_cookie来对它进行初始化,一般它通过CUP的计数器来生成。由于dll或exe都有自己的一份___security_cookie,所以并不会冲突。注意:这里也正体验了一点——/GS关键在于cookie,而cookie依靠CRT,所以/GS在某种程度上是需要CRT的支持的。如果我们关闭了CRT的支持,那么这一切就需要我们自己手工去处理,也即显示调用__security_init_cookie。
 
此外,如下两个函数也是经常在生成的汇编代码中看到的:
1)__RTC_CheckEsp 2)_RTC_CheckStackVars
通过字面意思,应该也可以略知一二了。__RTC_CheckEsp用来check栈帧指针,_RTC_CheckStackVars用来检查栈上的本地变量。熟悉Visual Studio的人应该有注意过这样一个编译选项(C/C++->Code Generation)Basic Runtime Checks(RTC的缩写由来),选项如下:
  1. Stack Frames (/RTCs)
  2. Uninitialized Variables (/RTCu)
  3. Both (/RTC1, equiv.to /RTCsu)//这个是前面两个的合并
其实正是由于这个开关的影响,才会产生以上的两个函数。
 
最后想说明的是,由于这篇文章出于2002年,所以有部分地方已经Out-of-Date了。_set_security_error_handler has been removed. Remove any calls to that function; the default handler is a much safer way of dealing with security errors.[MSDN],所以我们无法添加handler来处理buffer overrun。其实这个也很好理解,微软提供的默认处理函数很重要,所以不应该被随意改写。就是这样!!~~
 
OK, Hope this can help书呆子