[windows内核]段寄存器属性探测

上篇说到
段寄存器一共有96位,可见部分只有16位,也就是我们在od看到的段选择子,还有80位是不可见的,我们可以通过mov指令进行读写[LDTR和TR除外]
段寄存器具有以下结构

struct SegMent
{
	WORD Selector;		// 段选择子 16位 可见
	WORD Attributes;	// 段属性  16位 不可见 只有少数几个属性位是有效的
	DWORD Base			// 段起始地址 32位 不可见  仅对于FS和GS有效
	DWORD Limit			// 段大小 32位 不可见   仅对于FS和GS有效
}

怎么证明这些属性是存在的呢,先抛开上回说的GDT(Global Descriptor Table 全局描述符表)概念,我们来写程序做个实验来探测一下这些属性。
下面先看一张表
在这里插入图片描述
相同的属性我不再写出来,至于这些属性怎么来的,先不用管,以后会提到。

探测Attributes

#include <iostream>
#include <windows.h>

int var = 0;
int main()
{
	__asm
	{
		xor eax, eax;
		mov ax, ss;
		mov ds, ax;
		mov dword ptr ds : [var], eax;
	}
	
	printf("%x\n", var);
	system("pause");
	return 0;
}

我们来执行一下
在这里插入图片描述
成功执行而且并没有报错,这是因为ds和ss都是可读可写的基址和长度都是一致的,所以并不会报错,这时候我们换成cs试一下

#include <iostream>
#include <windows.h>

int var = 0;
int main()
{
	__asm
	{
		xor eax, eax;
		mov ax, cs;
		mov ds, ax;
		mov dword ptr ds : [var], eax;
	}
	
	printf("%x\n", var);
	system("pause");
	return 0;
}

执行

在这里插入图片描述
可以看到这里直接产生了一个内存读写异常的崩溃,cs修饰的内存只有读和执行属性,并没有可写的属性,所以在写入的时候产生异常并崩溃,这也印证了段的Attributes是确实是存在的,并且修饰了段的属性。

探测Base
从我们上面那张表看,只有fs寄存器存在Base,其他的段寄存器的Base全是0,所以我们用fs测试

#include <iostream>
#include <windows.h>

int var = 0;

int main()
{
	__asm
	{
		xor eax, eax;
		mov ax, fs;
		mov ds, ax;
		mov eax, ds:[0];
		mov dword ptr ds : [var], eax;
	}
	
	printf("%x\n", var);
	system("pause");
	return 0;
}

这样写其实是会崩溃的,而且有两处错误
1.我们前面说过 MOV DWORD PTR DS:[0x132B2C4],EAX这句指令真正写入的地址是ds.base+0x132B2C4而现在可以通过下面的图看到ds已经被我们改变了,所以这时候引用ds修饰符其实是引用的fs的修饰符而fs.base+0x132B2C4这个地址是不正确的,是无效的,因为我们ds的base是0,根本不会寻址到我们的参数var进行赋值,所以我们可以将代码改为mov dword ptr ss : [var], eax;因为ss和ds段修饰符是一致的。

2.虽然我们临时将ds改成了ss ,但是还是会造成崩溃,我们从下面的图可以看到,在跳出我们的汇编代码后,后续的代码也使用了ds,所以我们要将ds赋值回来才可以成功运行。
在这里插入图片描述

#include <iostream>
#include <windows.h>

int var = 0;

int main()
{
	__asm
	{
		xor eax, eax;
		mov ax, fs;
		mov ds, ax;
		mov eax, ds:[0];
		mov dword ptr ss : [var], eax;
		xor eax, eax;
		mov ax, ss;
		mov ds, ax;
	}
	
	printf("%x\n", var);
	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述
代码成功执行,我们都知道,如果段中的base修饰如果不存在的话,直接读取0地址是肯定会产生异常,而我们代码中mov eax, ds:[0];其实是mov eax,7FFDF000+0,并不是真正的0地址,所以这里也验证了段寄存器中Base属性的存在。

探测Limit
还是使用上面的代码来进行测试

#include <iostream>
#include <windows.h>

int var = 0;

int main()
{
	__asm
	{
		xor eax, eax;
		mov ax, fs;
		mov ds, ax;
		mov eax, ds:[0x1000];
		mov dword ptr ss : [var], eax;
		xor eax, eax;
		mov ax, ss;
		mov ds, ax;
	}
	
	printf("%x\n", var);
	system("pause");
	return 0;
}

我们把mov eax, ds:[0];改成mov eax, ds:[0x1000];
编译执行,产生了访问异常

在这里插入图片描述
而我们通过观察上面的表我们得知fs的段大小为0xfff,范围在0x7FFDF000~0x7FFDF000+0xFFF,而我们读取的值在0x7FFDF000+0x1000,已经超出了范围,我们反过来验证一下

#include <iostream>
#include <windows.h>

int var = 0;
int main()
{
	__asm
	{
		xor eax, eax;
		mov ax, fs;
		mov ds, ax;
		mov eax, ss:[0x7FFDF000 + 0x1000];
		mov eax, ds:[0x1000];
		mov dword ptr ss : [var], eax;
		xor eax, eax;
		mov ax, ss;
		mov ds, ax;
	}
	
	printf("%x\n", var);
	system("pause");
	return 0;
}

在这里插入图片描述
我们可以看到mov eax, ss:[0x7FFDF000 + 0x1000];已经成功执行eax被成功赋值,如果再执行下一句mov eax, ds:[0x1000];就会产生读写异常,为什么同样的地址通过不同的段寄存器去读取会产生这种差异,原因就是fs的Limit 为0xfff,如果读取fs.base+0x1000就会产生越界读写,产生访问异常。

通过这几个简单的小实验,我们可以得知,段寄存器中不可见的80位是真实存在且有效的,只是我们看不见而已。

下篇来解决以前提过的问题,写入段寄存器的时候我们只好像给了16位,剩下的80位从哪里来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值