Windows保护模式(六)10-10-12分页

本文深入探讨了分页和段管理在内存管理中的作用,详细阐述了10-10-12分页结构,包括CR3寄存器、页目录表、页表和物理页的关系。通过实例展示了线性地址到物理地址的转换过程,并解释了物理页的各种属性。此外,文章还介绍了如何利用这些知识进行内存操作,如修改物理页属性以实现权限变更和访问高2GB内存。最后,给出了利用调用门提权执行shellcode的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

页管理与段管理的关系

分页是用于从虚拟地址到物理地址转换的结构,与段式内存管理的关系如下图:
在这里插入图片描述

10-10-12分页结构

在这里插入图片描述

需要注意以下几点:

  • CR3 寄存器存储了一个页目录表的物理地址,处理器每个核有一个 CR3 寄存器,而每个进程对应一个页目录表,维护一个 4GB 的线性空间,改变 CR3 寄存器的值也就切换了进程空间。
  • 页目录表(PDE),页表(PTT)和物理页的大小均为 4KB 。其中 PDT 中的 PDE 和 PTT 中的 PTE 大小均为 4B,因此 PDT 和 PTT 中的元素个数均为 1024 个。
  • PDE 中的第 0x301 项 PDE 指向所在的 PDT,又因为 PDE 和 PTE 结构一致,因此操作系统就可以通过线性地址访问 PDT 和 PTT 中的任意位置。
  • 为避免套娃,所有线性地址向物理地址转换时所查询 PDE 和 PTE 所用的地址均为物理地址。
  • PDE 和 PTE 不一定都存放地址,因为对应线性地址可能没有映射到物理页,多个 PTE可能指向同一个物理页(共享内存),多个 PDE 也可能指向同一个 PTT(所有进程高 2G 内存对应的 PTT 相同)。

线性地址到物理地址转换过程

转换过程如下图所示:在这里插入图片描述
在这里插入图片描述

由于 PDT 和 PTT 中元素个数均为 2 10 2^{10} 210 ,而物理页大小为 2 12 2^{12} 212 字节,因此可以将线性地址划分为 10bit ,10bit ,12bit 三部分,前两部分按照 4 字节寻址,后一部分按照 1 字节寻址,这样便可以通过 10-10-12 分页将线性地址转换为物理地址。
首先 CPU 通过 CR3 寄存器 + 线性地址 Directory * 4 得到一个 PDE。判断 PDE 中的 PS 位是否为 1 ,如果 PS 为 1 则 PDE 直接指向一个 4MB 大小的物理页的基址,直接将线性地址的低 22 位加上该物理页的基址即可得到物理地址。如果 PS 为 0 则根据 PDE 中存储的 PTT 基址 +线性地址 Table * 4 得到一个物理页基址,将该物理页基址加上线性地址 Offset 即可得到线性地址对应的物理地址。

根据这一寻址方式,我们可以推断出两类特殊的线性地址:

  • 当前 CR3 指向的 PTE 的线性地址:0xC0300000
    • 因此第 N 个 PDE 的线性地址为:0xc030000 + N * 4
    • 其中第 0x301 个 PDE 存放着 CR3,对应线性地址为:0xC0300c00
  • 第一个 PDE 指向的 PTT(如果有)基址对应的线性地址:0xC0000000
    • 因此第 N 个 PDE 指向的 PTT 的第 M 个 PTE的线性地址为:0xc0000000 + N * 4096 + M * 4

物理页属性

物理页的属性 = PDE 属性 & PTE 属性
在这里插入图片描述
在这里插入图片描述

  • P位:有效位
    注意:当PDE或PTE中有一个的属性P=0时,物理页就是无效的
  • R/W位:读写位
    • R/W=0:只读
    • R/W=1:可读可写
  • U/S位:权限位
    • U/S=0:只有特权用户才能访问(0到2环)
    • U/S=1:普通用户也可以访问(3环)
  • PS位:PDE特有,PS即PageSize
    • PS=1:PDE直接指向物理页,低22位=页内偏移,偏移最大值为4MB,俗称"大页"
    • PS=0:PDE指向PTE
  • A位:访问位
    • A=1:该PDE/PTE被访问过
    • A=0:该PDE/PTE未被访问过
  • D位:脏位
    • D=1:该PDE/PTE被写过
    • D=0:该PDE/PTE未被写过

实验

根据线性地址找物理地址

通过 CE 确定记事本内容所在逻辑地址 0xAEF80。由于 Windows 的数据段寄存器基址为 0,因此该地址可以看做是线性地址。
在这里插入图片描述
WinDbg 获取 CR3

kd> !process 0 0
.
.
.
Failed to get VadRoot
PROCESS 8a01dda0  SessionId: 0  Cid: 0498    Peb: 7ffde000  ParentCid: 05f8
    DirBase: a9024000  ObjectTable: e1bf5dc8  HandleCount:  52.
    Image: notepad.exe

将 0xAEF80 地址按 10-10-12 分页进行拆分:0xAEF80 = 0x0 << 22 | 0xAE << 12 | 0x80
在 WinDbg 查找到对应物理地址:

kd> !dd a9024000 + 0
#a9024000 a8471067 a959f067 a997d067 00000000
#a9024010 a90b0067 00000000 00000000 00000000
#a9024020 00000000 00000000 00000000 00000000
#a9024030 00000000 00000000 00000000 00000000
#a9024040 00000000 00000000 00000000 00000000
#a9024050 00000000 00000000 00000000 00000000
#a9024060 00000000 00000000 00000000 00000000
#a9024070 00000000 00000000 00000000 00000000
kd> !dd a8471000 + ae * 4
#a84712b8 a9236067 a8cf9067 a89ba067 a8f3b067
#a84712c8 a8e7c067 a99bd067 a9afe067 ae80d067
#a84712d8 00000000 00000000 00000000 00000000
#a84712e8 00000000 00000000 00000000 00000000
#a84712f8 00000000 00000000 00000000 00000000
#a8471308 00000000 00000000 00000000 00000000
#a8471318 00000000 00000000 00000000 00000000
#a8471328 00000000 00000000 00000000 00000000
kd> !dd a9236000 + f80
#a9236f80 006b0073 00310079 00330032 00000000
#a9236f90 00000000 00000000 00000000 00000000
#a9236fa0 00000000 00000000 00000000 00000000
#a9236fb0 00000000 00a4000c 00080002 000a0100
#a9236fc0 98755206 000a0000 00020002 0008010e
#a9236fd0 00000000 00310079 00020094 000c010c
#a9236fe0 7468759c 00000001 bebacc94 46625cd3
#a9236ff0 31f3e0a1 69364999 58c99d96 42ce2f9b
kd> !db a9236f80
#a9236f80 73 00 6b 00 79 00 31 00-32 00 33 00 00 00 00 00 s.k.y.1.2.3.....
#a9236f90 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#a9236fa0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#a9236fb0 00 00 00 00 0c 00 a4 00-02 00 08 00 00 01 0a 00 ................
#a9236fc0 06 52 75 98 00 00 0a 00-02 00 02 00 0e 01 08 00 .Ru.............
#a9236fd0 00 00 00 00 79 00 31 00-94 00 02 00 0c 01 0c 00 ....y.1.........
#a9236fe0 9c 75 68 74 01 00 00 00-94 cc ba be d3 5c 62 46 .uht.........\bF
#a9236ff0 a1 e0 f3 31 99 49 36 69-96 9d c9 58 9b 2f ce 42 ...1.I6i...X./.B

获取 CR3

构造调用门提权后读取 0xC0300C00 处的地址。

#include<stdio.h>
#include<stdlib.h>

unsigned int _cr3;

__declspec(naked) void test(){
	__asm{
		int 3
		mov eax,dword ptr ds:[0xc0300c00]
		mov _cr3,eax
		retf
	}
}

int main(){
	unsigned char buf[6]={0,0,0,0,0x48,0};
	printf("test: %X\n",test);
	system("pause");
	__asm{
		push fs
		pushad
		pushfd
		call fword ptr ds:[buf]
		popfd
		popad
		pop fs
	}
	_cr3 = (_cr3 >> 12) << 12;
	printf("CR3: %X\n",_cr3);
	return 0;
}

在这里插入图片描述

通过修改物理页属性使常量可修改

首先测试一下 0 环权限是否可以修改常量

#include <stdio.h>
#include <stdlib.h>

const int val = 0;

__declspec(naked) void callGate() {
    __asm {
        mov val, 0x12345678
        retf
    }
}

int main() {
    unsigned char buf[] = {0, 0, 0, 0, 0x48, 0};
    printf("callGate: %X\nval: %X", callGate, &val);
    system("pause");
    __asm {
        call fword ptr ds:[buf]
    }
    return 0;
}

触发 c0000005 错误(好在没有蓝屏)。
判断读写属性不能只看权限。调试器修改内存数据并不是简单的提权,实际上也修改了 CR3 寄存器的写保护标志。
在这里插入图片描述
利用调用门提权后将页表的 R/W 位置 1 。可以修改常量。

#include <stdio.h>
#include <stdlib.h>

const int val = 0;

__declspec(naked) void callGate() {
    __asm {
        push 0x30
        pop fs
        pushad
        pushfd
        lea eax, val
        shr eax, 20
        and eax, 0xFFC
        or eax, 0xC0300000
        mov ebx,[eax]
        or ebx, 2
        mov [eax],ebx
        lea eax, val
        shr eax, 10
        and eax, 0x3FFFFC
        or eax, 0xC0000000
        mov ebx,[eax]
        or ebx, 2
        mov [eax], ebx
        popfd
        popad
        retf
    }
}

int main() {
    unsigned char gate[] = {0, 0, 0, 0, 0x48, 0};
    printf("Callgate: %X\nval addr: %X\n", callGate, &val);
    system("pause");
    __asm {
        push fs
        pushad
        pushfd
        call fword ptr ds:[gate]
        popfd
        popad
        pop fs
        mov val,0x12345678
    }
    printf("val: %X\n", val);
    return 0;
}

在这里插入图片描述

通过修改物理页属性读高2G内存

复用上一实验代码

#include <stdio.h>
#include <stdlib.h>

unsigned int *val = (unsigned int *) 0x8003F00C;

__declspec(naked) void callGate() {
    __asm {
        push 0x30
        pop fs
        pushad
        pushfd
        mov eax, val
        shr eax, 20
        and eax, 0xFFC
        or eax, 0xC0300000
        mov ebx,[eax]
        or ebx, 4
        mov [eax],ebx
        mov eax, val
        shr eax, 10
        and eax, 0x3FFFFC
        or eax, 0xC0000000
        mov ebx,[eax]
        or ebx, 4
        mov [eax], ebx
        popfd
        popad
        retf
    }
}

int main() {
    unsigned char gate[] = {0, 0, 0, 0, 0x48, 0};
    unsigned tval;
    printf("Callgate: %X\n", callGate);
    system("pause");
    __asm {
        push fs
        pushad
        pushfd
        call fword ptr ds:[gate]
        popfd
        popad
        pop fs
    }
    tval = *val;
    printf("val: %X\n", tval);
    return 0;
}

在这里插入图片描述
不稳定,下过 int3 断点后成功率会高一些,原因未知。

挂物理页执行shellcode

代码如下,调用门提权后判断 0 地址对应 PDE 和 PTE 是否有效,若无效则将 buf 对应结构的内容写入。

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

//                     push 0;     push 0;     push 0;     push 0;     call MessageBox;              retn;
unsigned char buf[] = {0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xc3};

__declspec(naked) void callGate() {
    __asm {
		push 0x30
		pop fs
		pushad
		pushfd
		lea eax, buf
		mov ebx, dword ptr ds:[0xC0300000]
		test ebx, ebx
		je __getPDE
		shr eax, 10
		and eax, 0x3FFFFc
		or eax, 0xC0000000
		mov eax, [eax]
		mov dword ptr ds:[0xC0000000], eax
		jmp __return
__getPDE:
		shr eax, 20
		and eax, 0xFFC
		or eax, 0xC0300000
		mov eax, [eax]
		mov dword ptr ds:[0xC0300000], eax
__return:
		popfd
		popad
		retf
    }
}

int main() {
    unsigned int funcAddress = (unsigned int) MessageBox;
    unsigned int _offset = ((unsigned int) buf) & 0xFFF;
    unsigned char gate[] = {0, 0, 0, 0, 0x48, 0};
    *(unsigned int *) (&buf[9]) = funcAddress - (13 + _offset);
    printf("callGate: %X\n", callGate);
    system("pause");
    __asm {
		push fs
		pushad
		pushfd
		call fword ptr ds:[gate]
		popfd
		popad
		pop fs
		mov eax, _offset
		call eax
    }
    return 0;
}

运行结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_sky123_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值