Windows内核基础知识-段选择子与段描述符

CPU的3个模式:

1、实模式:

DOS系统下的CPU用的就是实模式,里面用的地址就是真实的物理地址。现在的系统已经没有使用实模式的了,只是在启动的瞬间是实模式,然后进入保护模式。

2、保护模式:

保护模式中的地址是假的,如果要从保护模式到实模式的地址,无论是低2G还是高2G都要经过转换。除了CR3寄存器存储的是物理地址,其他的寄存器全是线性地址。

3、虚拟8086模式:

如果在保护模式下,想运行实模式的代码,也是可以运行的,就是虚拟8086模式。虚拟8086模式仍然是保护模式,而不是实模式。

保护模式

作用

我们学习保护模式,肯定要知道保护模式有啥用,它保护的是资源,也就是寄存器,它与段和页都有很大的联系,我们接下来的学习任务都是学习段和页。

段寄存器

ES 堆栈段 CS 代码段 SS 堆栈段 DS 堆栈段

FS 标志段寄存器 GS 全局段寄存器 LDTR TR

段是具有属性的

CS 代码段,可读可执行,不可以写

DS 数据段,可读也可写,不可执行

#include<Windows.h>
int val = 0x10;
int main(){
	_asm{
		mov ax, cs;
		mov ds, ax;
		mov ebx, 0x11;
		mov dword ptr[val], ebx;

	}
	return 0;
}

由于ds数据段是存放数据的,但是我们现在给ds寄存器赋值了cs寄存器的属性,那么就会导致ds现在不可以写入,也就是会赋值失败,下图就是报了访问异常的错误

探究段的基址base和长度limit

#include<Windows.h>
int val = 0x10;
int main(){
	_asm{
		mov eax, 0x1;
		mov dword ptr ds:[0], eax;

	}
	return 0;
}

当我们尝试写入ds:[0]会触发异常,但是我们改成下面的代码

#include<Windows.h>
int val = 0x10;
int main(){
	_asm{
		mov eax, 0x1;
		mov dword ptr fs:[0], eax;

	}
	return 0;
}

这串代码和前面的区别在于由ds改为了fs,说明我们写入fs的0位置是没有问题的,这个就是与段的基址base有关

我们可以发现除了fs,其他的段base都是从0开始的,而fs之所以可以正常访问,是因为fs指向TEB表,我们写入数据,相当于改了TEB表的便宜为0的地址上的数据。另外如果我们想得到fs的base,不能直接 lea eax,dword ptr fs:[0] ,这个会使eax=0,因为lea实际上取的是[]之间的东西

通过Windbg可以查看一下TEB结构体

在偏移0x18的地方存放着指向自己的首地址,直接mov eax,dword ptr fs:[0x18]

#include<Windows.h>
int val = 0x10;
int main(){
	_asm{
		xor eax, eax;
		mov eax, dword ptr fs : [0x18];
		mov val, eax;

	}
	printf("%X", val);
	system("pause");
	return 0;
}

保护模式寻址方式

段.base+offset(逻辑地址)=线性地址,取值最大长度limit

段选择子

保护模式下的段寄存器其实由16位的段选择子和64位的段描述符寄存器构成,我们OD看到的是段选择子

段选择子:存储段描述符的索引,也就是我们前面在OD里面看到的类似0x1B,0x23这些

段描述符寄存器: 存储段描述符,里面包含一些段的属性,段的base,段的limit等

在16位汇编中,段寄存器中放段基址,IP中放偏移。CS:IP

在32位汇编中,段寄存器中放段选择子,我们需要根据下图对段选择子进行拆分

举例

CS:1B 二进制为: 0001 1011 ,按图片拆分得到:00011 0 11

Index:3

Ti:0 //在windows中LDT表没有用,所以Ti一直为0

RPL:3 也就是说明我们现在是在R3层

如何查GDT表

GDT表的地址存放在gdtr寄存器中,GDT表的大小存放在gdtl中

我们用 dq 80b95000 (dq表示四字,即64位 )

从0开始,索引为3,所以我们找到:

CS段描述符为:00cffb00`0000ffff

段描述符

我们已经通过索引找到了段描述符,现在我们开始分析和拆解段描述符

分为高32位和低32位,根据上表拆解CS的段描述符:

我们把CS的段描述符转换成二进制

0000 0000 1100 1111 1111 1011 0000 0000 //高32位
0000 0000 0000 0000 1111 1111 1111 1111 //低32位

Base:0000 0000 0000 0000 0000 0000 0000 0000(十六进制00000000)
Limit:1111 1111 1111 1111 1111(十六进制fffff)
TYPE:1011(十六进制B)
S:1
DPL:11
P:1
AVL:0
D/B:1
G:1

同理也可以拆解其他的段描述符,这里可以用C语言去实现

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

struct low_word {
	unsigned int limit_0_15 : 16;
	unsigned int base_0_15 : 16;
};
struct high_word {
	unsigned int base_16_23 : 8;
	unsigned int type : 4;
	unsigned int s : 1;
	unsigned int DPL : 2;
	unsigned int p : 1;
	unsigned int limit_16_19 : 4;
	unsigned int avl : 1;
	unsigned int l : 1;
	unsigned int d_b : 1;
	unsigned int g : 1;
	unsigned int base_24_31 : 8;
};

void seg_des(struct high_word* ph, struct low_word* pl) {
	unsigned int seg_base;
	seg_base = (ph->base_24_31 << 24) | (ph->base_16_23 << 16) | (pl->base_0_15);//段基址 
	printf("seg_base=0x%08x\n", seg_base);
	unsigned int seg_limit;
	seg_limit = (ph->limit_16_19 << 16) | (pl->limit_0_15);
	printf("seg_limit=0x%x\n", seg_limit);
	printf("Type=0x%x\n", ph->type);
	printf("S=%x\n", ph->s);
	printf("DPL=%x\n", ph->DPL);
	printf("P=%x\n", ph->p);
	printf("AVL=%x\n", ph->avl);
	printf("DEF=%x\n", ph->l);
	printf("D/B=%x\n", ph->d_b);
	printf("G=%x\n", ph->g);
}

int main() {
	printf("please input the segment descriptor\n");
	struct high_word* high;
	struct low_word* low;
	
	unsigned int l_word = 0;
	unsigned int h_word = 0;

	//请求用户输入描述符,先是低32位,再是高32位
	printf("low:\n");
	scanf("%x", &l_word);
	printf("high:\n");
	scanf("%x", &h_word);
	printf("-----------------------\n");
	high = (struct high_word*)&h_word;
	low = (struct low_word*)&l_word;
	seg_des(high, low);
	printf("------------------------\n");
	return 0;

}

Type位

有四位,通过值在表中进行索引

G位

颗粒度

G = 1 Limit单位为页

G = 0 Limit单位为字节

页分为两种

大页:4MB

小页:4KB=4096B=0x1000Byte(我们使用)

所以Limit=Limit*0x1000,既ffffff*0x1000=ffffff000

因为我们从第0页开始,所以ffffff000+0x1000

又因为地址从0开始,所以还得ffffff000+0x1000-1=ffffffff

所以最后base为00000000,limit为ffffffff


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 767778848@qq.com