[windows内核]TLB

地址解析

当我们通过一个线性地址访问一个物理页(比如:MOV EAX,[0x12345678])时,实际上CPU未必只读了4个字节。

10-10-12分页

CPU先通过线性地址找到对应的PDE:4个字节
CPU再通过PDE和线性地址找到PTE:4个字节
最后再通过PTE找到对应物理页:4个字节
一共访问了12个字节,如果跨页可能更多。

2-9-9-12分页

找到PDPTE:8个字节
找到PDE:8个字节
找到PTE:8个字节
最后找到物理页:4个字节
一共访问了20个字节,如果跨页可能更多。

  • 为了提高访问效率,只能对线性地址与其对应的物理地址做记录。
  • CPU内部做了一张表,用来记录这些东西。它的效率和寄存器一样快,名字叫做TLB(Translation Lookaside Buffer)。
  • 由于TLB的效率很快,因此它的大小不能太大,少则十几条,多则也只有上百条,具体和CPU型号有关。

在手册第三卷里的4.10.2 Translation Lookaside Buffers (TLBs)能查看详细解释

引入TLB的目的是什么?

上面的地址解析已经详细说明了,就是为了减少到内存查页表的次数!
在这里插入图片描述

在一个进程的4GB空间中,有无数个线性地址,但是一个TLB最多只能记录上百条记录,那么这张表真的有意义吗?

有,一个小页大小为4k,一个大页的大小为2M/4M,这已经是非常能提高CPU寻址的效率了

TLB

结构
在这里插入图片描述
ATTR:属性
在10-10-12分页模式下:ATTR = PDE属性 & PTE属性
在2-9-9-12分页模式下:ATTR = PDPTE属性 & PDE属性 & PTE属性

LRU:统计信息
由于TLB的大小有限,因此当TLB被写满、又有新的地址即将写入时,TLB就会根据统计信息来判断哪些地址是不常用的,从而将不常用的记录从TLB中移除。

注意:

  1. 不同的CPU,TLB大小不同
  2. 只要Cr3发生变化,会导致处理器自动刷新非全局页的TLB表项,一核一套TLB
  3. 由于操作系统的高2G映射基本不变,因此如果Cr3改了,TLB刷新的话,重建高2G以上很浪费。所以PDE和PTE中有个G标志位(当PDE为大页时,G标志位才起作用),如果G位为1,刷新TLB时将不会刷新PDE/PTE
  4. G位为1的页,当TLB写满时,CPU根据统计信息将不常用的地址废弃,保留最常用的地址
TLB种类

TLB在X86体系的CPU中的实际应用最早是从Intel的486CPU开始的,在X86体系的CPU中,一般都设有如下4组TLB:

第一组:缓存一般页表(4K字节页面)的指令页表缓存(Instruction-TLB);
第二组:缓存一般页表(4K字节页面)的数据页表缓存(Data-TLB);
第三组:缓存大尺寸页表(2M/4M字节页面)的指令页表缓存(Instruction-TLB);
第四组:缓存大尺寸页表(2M/4M字节页面)的数据页表缓存(Data-TLB);

TLB衍生的攻防技术

Shadow-Walker
这个技术已经成为了历史,而且问题多多,具体可看

https://www.blackhat.com/docs/us-14/materials/us-14-Torrey-MoRE-Shadow-Walker-The-Progression-Of-TLB-Splitting-On-x86.pdf

https://bbs.pediy.com/thread-194933.htm

https://blog.can.ac/2018/04/26/splitting-data-from-code-forgotten-x86-feature-segmentation-n/

实验

下面做几个小实验来验证TLB

一.验证TLB的存在

找到两个有效的线性地址

然后构建调用门,修改GDT表项

上面的步骤之前有说过,这里不再重复

最后编译如下程序

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


DWORD g_tmpValue=0;

void __declspec(naked) proc()//0x00401000
{
	__asm 
	{
		//给0地址挂物理页
		mov dword ptr ds : [0xc0000000], 0x0d3cd867;//867是页属性 注意
		//给0地址挂上的物理页赋值
		mov dword ptr ds : [0], 0x12345678;

		//将0地址的物理页改成其他的物理页
		mov dword ptr ds : [0xc0000000], 0x0270a867;//867是页属性 注意

		//再次读取线性地址
		mov eax, dword ptr ds : [0];
		mov g_tmpValue, eax;

		retf;
	}



}


int main(int argc, char* argv[])
{


	//调用门
	char buff[6] = { 0 };
	*(DWORD*)&buff[0] = 0x12345678;
	*(WORD*)&buff[4] = 0x48;
	__asm
	{
		call fword ptr[buff]
	}
	printf("g_tmpValue=%x\n", g_tmpValue);
	getchar();
	return 0;
}

成功读出之前挂上的物理页的内容,证明了TLB的存在
在这里插入图片描述

二.修改cr3后是否会导致处理器自动刷新非全局页的TLB表项

编译如下代码

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


DWORD g_tmpValue=0;

void __declspec(naked) proc()//0x00401000
{
	__asm 
	{
		//给0地址挂物理页
		mov dword ptr ds : [0xc0000000], 0x0d3cd867;//867是页属性 注意
		//给0地址挂上的物理页赋值
		mov dword ptr ds : [0], 0x12345678;

		//将0地址的物理页改成其他的物理页
		mov dword ptr ds : [0xc0000000], 0x0270a867;//867是页属性 注意

		//修改cr3
		mov eax, cr3;//如果属性为967 修改cr3也不会刷新 因为G位为全局位
		mov cr3, eax;

		//再次读取线性地址
		mov eax, dword ptr ds : [0];
		mov g_tmpValue, eax;

		retf;
	}



}


int main(int argc, char* argv[])
{


	//调用门
	char buff[6] = { 0 };
	*(DWORD*)&buff[0] = 0x12345678;
	*(WORD*)&buff[4] = 0x48;
	__asm
	{
		call fword ptr[buff]
	}
	printf("g_tmpValue=%x\n", g_tmpValue);
	getchar();
	proc();
	return 0;
}

执行
在这里插入图片描述
并没有读出我们之前赋值的0x12345678 说明切换了cr3会导致刷新非全局页的TLB表项
我们再把代码稍作更改,把我们给0地址挂上的第一个物理页0x0d3cd867,改为0x0d3cd967,修改了G位
再次编译运行

在这里插入图片描述
成功读出之前挂上的物理页的内容,证明了切换cr3只会导致非全局页的TLB表项刷新

三.INVLPG指令的意义

上面我们已经证明了全局页在TLB表项是不会被自动刷新的,那么我们想要删除这个全局页在TLB中的表项该怎么办呢?
编译如下程序

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


DWORD g_tmpValue=0;

void __declspec(naked) proc()//0x00401000
{
	__asm 
	{
		//给0地址挂物理页
		mov dword ptr ds : [0xc0000000], 0x0d3cd967;//867是页属性 注意
		//给0地址挂上的物理页赋值
		mov dword ptr ds : [0], 0x12345678;

		//将0地址的物理页改成其他的物理页
		mov dword ptr ds : [0xc0000000], 0x0270a867;//867是页属性 注意

		//删除全局页
		INVLPG dword ptr ds : [0];

		//修改cr3
		mov eax, cr3;//如果属性为967 修改cr3也不会刷新 因为G位为全局位
		mov cr3, eax;

		//再次读取线性地址
		mov eax, dword ptr ds : [0];
		mov g_tmpValue, eax;

		retf;
	}



}


int main(int argc, char* argv[])
{


	//调用门
	char buff[6] = { 0 };
	*(DWORD*)&buff[0] = 0x12345678;
	*(WORD*)&buff[4] = 0x48;
	__asm
	{
		call fword ptr[buff]
	}
	printf("g_tmpValue=%x\n", g_tmpValue);
	getchar();
	return 0;
}

运行
在这里插入图片描述
可以看到,并没有成功读出我们之前赋值的内容,我们已经成功删除了全局页
手册中对INVLPG 指令的解释如下,在第二卷中
在这里插入图片描述
感兴趣的可以详细的测试一下

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值