段类型检查
加载段选择符进入段寄存器时候
- CS只能存放可执行的选择符
- 不可读可执行不能被加载到数据段寄存器
- 只有可写的数据段才能加载到SS
段权限检查
当给段寄存器赋值,实际是从GDT中获取相应的段描述符加载到段寄存器的不可见部分。这个时候有个权限检查,有三个概念:
- CPL:当前代码执行权限
- DPL:存在段描述符中,描述访问本段内存需要的权限
- RPL:存在于段寄存器加载时的段选择子中,描述了使用什么样的权限对目标进行访问
从数值上MAX(CPL,RPL)<DPL.
段内跳转不会产生权限检查(JMP,CALL,RET),段间会。
当S为0时:
- 调用门:Type=12。
- 中断门:Type=14。
- 陷阱门:Type=15。
- 任务门:Type=5。
调用门
调用门描述符中存储了一个代码段的段选择子。
指令格式:CALL CS:EIP(EIP废弃的)
- 根据CS的值查GDT表,找到对应段描述符,之歌描述符是一个调用门。
- 在调用门描述符中存储着另一个代码段的段选择子。
- 选择子指向的段,段Base+偏移地址,就是真正要执行的地址。
通过调用门可以原图,不过Windows并没有用调用门。
段寄存器一共有 96 位,其中16可见部分来源于段选择子的索引部分。剩下80位来源于 GDT表。++那GDT表64位是怎么表示80位的段描述符呢?++ 是有一部分是G位为零代表粒度是1字节,在段限长前面补12位000,如果G为1在段限长前面补FFF,FFF刚好4KB。(段限长就是FFFFF),所以当G=0,即粒度是1B时候,范围就是
2^00000000^到2^000FFFFF^即1B到4MB,当G=1,即粒度是1B时候,范围就是2^FFF00000^到2^FFFFFFFF^,即4KB到4GB。
中断门和陷阱门
除了GDT外,还有一个地方也存着门描述符,被称为IDT(中断描述符表)。
IDT中存着3种门描述符:
- 中断门描述符
- 陷阱门描述符
- 任务门描述符
当S为0,type为1110是个中断门
中断门描述符存储着一个断码段选择子。
当S为0,type为1111是陷阱门
- 陷阱门用于存放异常处理函数
- 中断门用于存放中断处理函数地址
- 中断门执行时候IF位会清零,屏蔽可屏蔽中断,陷阱门不会。
调用门提权实验
先确定一个事情,怎么提权?是要构造一个调用门描述符,然后CALL这个段的一个地址所以这个段的DPL得是3环的,所以,在构造的调用门描述符里,DPL位是3即11b,即3环就能访问本段的程序。然后这个调用门里的段选择子(就是13位可见的那部分个)是要执行的段,所以他的段选择子的RPL要是高权限,所以他的RPL应该是00,Ti也0,查GDT第二个就行所以索引是1,所以合起来是1000b,所以段选择子是8,所以要构造的调用门描述符里8,的作用是让其RPL是00为0,环以0环权限去访问,Ti为是0找GDT表,其他位置弄成如下
XXXXEC000008XXXX
下面的代码相关信息如下
- 前后XXXX为偏移,比如你要执行的代码的偏移,即下文函数的入口,构造下面代码
- 0x80b95500为任意一个内核空间(高地址空间),为了验证能不能提权,得到内核的信息。
- retf为不仅像ret一样pop ip,还pop cs
- 0x00,0x00,0x00,0x00,0x63,0x00 为CS:IP,因为内存以小端寸断存储。所以是这样,IP可以任意
- fword为远跳,6字节的,用于段间跳转。
- CS为0x63,即下面代码要CALL的段选择是0x63,是因为我们找了一个GDT里的空位为第12个位置,所以是1100,然后找GDT,权限是3环,所以段选择子是1100011b,所以这个是63
- 注意vs要关闭随机基址,虚拟机要单核,比较好做这个实验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include "stdafx.h" #include <Windows.h> int g_num = 0; _declspec(naked) void fun() { //这里如果用了 int 3 那么回到3环的时候,会造成程序崩溃,因为回去的时候,FS会被置0; _asm { push eax; mov eax, DWORD ptr ds : [0x80b95500]; mov g_num, eax; pop eax; retf; } } int main() { char buf[6] = { 0x00,0x00,0x00,0x00,0x63,0x00 }; _asm { call fword ptr ds : [buf]; } printf("%x", g_num); system("pause"); return 0; } |
参考资料:
[1]:赵炯,《Linux内核完全剖析》,机械工业出版社. 4.3.4节
[2]:https://blog.csdn.net/q1007729991/article/details/52538080
[3]:李忠,《x86汇编语言:从实模式到保护模式》,电子工业出版社