14.修改 PTE 实现挂物理页读写空指针

一、10-10-12二级映射

下图是101012二级映射结构:

在这里插入图片描述

CR3寄存器存放物理地址,指向的是页目录表(PDT)

页目录表(PDT)大小是4KB=4096字节,每个成员(PDE)占4字节,所以共有1024个

PDE指向页表(PTE),PTE指向物理页

PDE和PTE后六位都是属性

PTE特征:

1.可以不指向物理页

2.多个PTE可以指向同一个物理页

3.一个PTE只能指向一个物理页

 

二、读写NULL指针


正常编程中,不能读写NULL,原因是NULL指针没有对应的物理页,因此,只要我们让NULL指针最终映射到一块可读写的物理页,就可以用NULL去读写数据了。

代码短小精悍,也很好理解,关键步骤都在windbg里操作。下面先让程序跑起来,停在getchar()处。
 

// PDE_PTE.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
	int x = 1; // 让NULL指向x所在的物理页
	printf("x的地址:%x\n", &x);
	getchar(); // 在windbg里修改NULL 的PTE
	// NULL不能读写,原因是PTE为0,即没有分配物理页
	// 所以只要给NULL的PTE分配物理页,就能读写NULL了	
	printf("NULL地址数据:%x\n",*(int*)NULL);
	*(int*)NULL = 0x112233;	
	printf("NULL地址数据:%x\n",*(int*)NULL); // 0x112233
	return 0;
}



在这里插入图片描述

 

 

 

然后,在windbg里按照上一节课的做法,找到 x 的物理地址。
拆分x的地址 0x0012ff60
00000000‭00 0x0
0100101111 0x12f
111101100000‬ 0xf60

!process 0 0 查到CR3=1915d000,CR3的值是PDT表的基址。
!dd 1915d000+4*0 查到x对应的PDE也就是PDT[0]=07842067,属性清零后是 07842000,这个值是x的PTT的基址。
!dd 07842000+4*12f 查x的PTE也就是PTT[12f]=0ce53067,属性清零后就得到x的物理页基址 0ce53000
!dd 0ce53000+f60 以dword形式查看x的内存


在这里插入图片描述

 

 

NULL = 0
拆分得:
00000000‭00
00000000‭00
00000000‭0000
下标全是0。
CR3是一样的,一个进程只有一个,CR3=1915d000
找NULL的PDE,!dd 1915d000,因为下标都是0,所以NULL 的PDE 和 x的PDE是一样的,不用改,都是 07842067。
所以 07842000 就是NULL的PTT,然后NULL的第二个下标也是0,所以 !dd 07842000 就能查到PTT[0] 即 NULL的PTE,值是0.
在这里插入图片描述

 

用!ed指令改写成x的PTE 0ce53067
!ed 7842000 0ce53067

 

在这里插入图片描述

现在x和NULL的PTE值相同,NULL和x处于同一个物理页,物理页基址是 0ce53000,但是物理地址仍然是不同的,因为x在物理页内的偏移是 0xf60,而NULL的偏移是0。

但是没有关系,NULL已经指向了一块可用的物理页了,现在可以对NULL进行读写了

在这里插入图片描述

 

 

三、为变量x再映射一个线性地址,并通过这个新的地址读取x的值


方法是调用 VirtualAlloc 申请一个物理页,得到一个指针p,拆分地址,将p后12位改成和x的后12位相同,这样就构造好了一个新的线性地址。

解释一下为什么要VirtualAlloc,目的是确保有一块可用的物理页,并且和x的物理页不是同一个;而修改p的后12位,目的是让p的物理页偏移和x一样。

接下来,只需要修改p的PDE和PTE,使得p和x指向同一个物理页即可。
 

// PDE_PTE.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
	int x = 0x1;
	printf("x的地址:%x\n", &x);
	// 申请一个新的物理页,将p的PTE和物理页内偏移改成和x一样
	int p = (int)VirtualAlloc(NULL,0x1000,MEM_COMMIT,PAGE_READWRITE);
	int p_bak = p;
	memset((int*)p,0,0x1000);
	
	p &= 0xFFFFF000;
	p |= ((int)&x & 0x00000FFF);
	printf("新的线性地址:%x\n", p);
	getchar(); // 在windbg里修改 p 的 PTE

	// 用新的线性地址读x
	printf("*addr:%x\n",*(int*)p); // 0x1
	// 用新的线性地址写x
	*(int*)p = 0x112233;
	printf("x:%x\n",x); // 0x112233

	getchar();
	VirtualFree((int*)p_bak,0x1000,MEM_DECOMMIT);
	return 0;
}

 

在这里插入图片描述

 

 

然后将p的PTE改成和x的PTE一样,这个过程步骤繁琐:

拆分x:
0x0012ff60
00000000‭00 0x0
0100101111 0x12f
111101100000‬ 0xf60

拆分p:
0x003b0f60
00000000‭00 0x0
1110110000 0x3b0
111101100000‬ 0xf60

查CR3:
!process 0 0
CR3 = 0ef6f000

查PDEx
!dd 0ef6f000
PDEx = 09f43067

查PTEx
!dd 09f43000+4*12f
PTEx = 0f461047

查PTEp
!dd 09f43000+4*3b0


在这里插入图片描述

 

 

改PTEp
!ed 9f43ec0 0f461047

执行剩余的代码:

在这里插入图片描述

 

四、10-10-12分页模式物理内存能够识别的最多范围是多少

在这里插入图片描述

 

页目录表有1024项,页表有1024项,物理页有4KB

寻址范围:1024 * 1024 * 4096 = 4GB

 

五、如何判断2个线性地址是否在同一个物理页?

看线性地址前20位,只要相同就在同一个物理页。
因为前20位确定了PDE 和 PTE,PTE相同一定是同一个物理页。

 

 

<think>好的,用户遇到了面故障执行错误的问题,需要了解其原因和解决方法。首先,我需要回忆操作系统相关的知识,特别是内存管理和面错误的部分。 面故障(Page Fault)通常发生在进程访问的虚拟地址没有映射到物理内存时,这时候操作系统需要处理这个异常。但执行错误(Execute Error)可能涉及到权限问题,比如尝试执行不可执行的面。 可能的原因包括: 1. 访问了未映射的地址,导致缺错误。但如果是执行错误,可能面存在但权限不足,比如代码段被标记为不可执行。 2. 内存权限设置错误,例如数据段或堆栈段被错误地标记为可执行,而操作系统启用了NX位(不可执行位)保护。 3. 内存损坏,比如缓冲区溢出覆盖了表项,导致权限位被修改。 4. 驱动程序或内核模块错误地修改了内存映射,导致权限冲突。 5. 硬件故障,比如内存条损坏,但这种情况比较少见。 解决方法方面: 首先检查应用程序是否有内存访问越界或执行非法操作,使用调试工具如GDB分析崩溃点。如果是权限问题,需要检查表项中的权限位是否正确设置。对于启用了NX位的系统,确保代码段可执行而数据段不可执行。如果是驱动程序问题,更新或回滚驱动版本。硬件问题则需要运行内存检测工具。 另外,用户提供的引用中有提到延迟失效漏洞和集群节点固定命令,可能和分布式系统中的内存一致性或缓存失效有关,但不确定是否直接相关。需要确认面错误是否发生在集群环境下,可能涉及节点间内存同步问题,此时按照引用[2]的方法固定节点可能有助于稳定内存状态。 最后,生成相关问题时要覆盖原因、排查工具、系统保护机制、硬件问题以及集群环境下的处理。</think>### 面故障执行错误的原因及解决方法 #### 一、核心原因分析 1. **权限冲突** 进程尝试执行没有执行权限的内存区域(如数据段或堆栈段),触发硬件保护机制。现代处理器支持$NX$位(No-eXecute)技术,操作系统通过$$PAGE_EXECUTE_READWRITE$$等标志控制内存区域权限[^1]。 2. **无效地址访问** 访问未映射到物理内存的虚拟地址(如空指针解引用),或访问已被释放的内存区域,引发缺异常(Page Fault)。此时若错误类型为$0xC0000005$(ACCESS_VIOLATION),通常伴随STATUS_IN_PAGE_ERROR[^2]。 3. **内存映射异常** 驱动程序或内核模块错误修改表项(PTE)导致权限位异常,例如将代码误标记为不可执行。此类问题常见于$$mmap()$$系统调用参数配置错误。 4. **硬件故障** 内存条损坏或总线错误导致物理地址读写异常,但此类情况仅占故障总量的$<5\%$。 #### 二、排查与解决方法 1. **调试工具定位** 使用GDB/WinDbg分析崩溃堆栈: ```bash gdb -ex "bt full" --args ./your_program ``` 重点关注$$si/eip$$寄存器指向的故障地址权限属性。 2. **检查内存权限配置** 对于Linux系统,通过$$/proc/[pid]/maps$$查看进程内存映射: ```bash cat /proc/$(pidof your_program)/maps | grep -i [故障地址所在区间] ``` 验证目标地址区间是否具有$x$(可执行)权限。 3. **系统级保护设置** 在集群环境中,若出现跨节点内存同步问题,可参考引用[2]的节点固定方法: ```bash crsctl pin css -n <node1> <node2> ``` 4. **代码层面修复** - 使用AddressSanitizer检测内存越界访问 ```bash gcc -fsanitize=address -g your_code.c ``` - 对动态生成代码(如JIT编译)需显式设置可执行权限: ```c mprotect(code_ptr, size, PROT_READ | PROT_EXEC); ``` #### 三、预防措施 1. 开启DEP(Data Execution Prevention)保护机制 2. 定期使用memtest86+检测物理内存完整性 3. 在分布式系统中遵循引用[1]的延迟失效处理规范,确保缓存一致性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值