定位Windows分页结构内存区域
定位原理
由于处理器指令只能使用虚拟地址,而没有办法通过直接指定物理地址来更新一个内存位置的内容。因此访问PML4、PDPT等的唯一方法是将它们也映射到相应的虚拟地址上。虚拟内存管理才能够修改处理器的分页结构。
PML4 Auto-Entry
解决这个问题的方案是以一种非常特殊的方式使用一项PML4e:即Windows无页表随机化以前使用0x1ed处的项存储PML4自身的PFN,我们将这项称为PML4 Auto-Entry. 接下来我们将看到他的作用。
我们知道一个PML4e映射了512GB的虚拟地址空间,那么因为这个特殊项的存在,那么一定有那么一个范围大小的空间为这个特殊目的而保留。
接下来我们就确定一下这个PML4e所对应的地址空间范围。
我们知道PML4的索引被存储到VA的第39-47位,那么索引为0x1ed时,对应的虚拟地址就可以相应的推出来。
0x1ed = 1 1110 1101b
bit # 6 4 4 4 4 3
3...8 7 4 0 9
1...1 1111 0110 1xxx x...x
虚拟地址的39-47反推为0x1ed,因为47位为1,所以48-63也必须为1. 而0-38位可以从全0到全1. 因此地址范围就可以很快知道
1 2 3 4 5 6 | bit # 6 4 4 4 4 3 3
3...8 7 4 0 9 6
1...1 1111 0110 1xxx x...x
|___| |___||___||___|
| | | |
FFFF F 6 8-F
|
最终推出这个虚拟地址范围为
0xFFFFF68000000000 -- 0xFFFFF6FFFFFFFFFF
如果以范围后的首字节位结束地址那么范围为
0xFFFFF68000000000 -- 0xFFFFF70000000000
这个地址范围的虚拟地址的索引全是0x1ed,PML4e即为PML4 Auto-Entry。本来应该利用这个项的PFN定位PDPT,但是他却指向了PML4自身。在这种情况下,虚拟地址的30-38位索引得到的就是PDPTE,从21-29位索引得到的就是PDE。接着从20-12位索引得到的就是PTE。
PTE的虚拟地址就从0xFFFFF680`00000000开始,也就是我们常说的PTE_BASE。
PTEs是8字节大小,因此下一个PTE的虚拟地址就是0xFFFFF680`000000081
给定一个虚拟地址,那么他的PTE一定在512GB区域的某一个地方。在哪里呢?
求解方法:
给定一个虚拟地址,我们进行如下转化:
- VA中的所有索引位向右移9位
- 最左边未使用的位全部设置为1
- 将PML4 index替换成 PML4 Auto-Entry的 index
- 将0-2位置0
就会得到一个新的虚拟地址,即为所求。
得到这个PTE的虚拟地址后,进一步转化:
- 左移9位
- 根据移位完成后的第47位设置48-63位。
又得到一个新的虚拟地址,这个值是PTE映射的虚拟页的首地址。
将PML4 index 和 PDPT index域设置成auto-entry index,这样实际上PD index索引的就是PDPT,PT index索引的就是PD,即可看到PDEs。
PDE的范围因此也可以相应的计算出来
1 2 3 4 5 6 7 8 | 1 1110 1101
bit
3. .. 8 7 4 0 9 6 1 09
1. .. 1 1111 0110 1111 1011 01. .
|___| |___||___||__| |__| |___|
| | | | | |
FFFF F 6 F B 4 - 7
|
PDE开始地址 : 0xFFFFF6FB'40000000 即PDE_BASE
PDE结束地址:0xFFFFF6FB'80000000
可以发现这个地址范围在之前计算出来的PTE里。
也就是说他把实际的PTE范围分成了两个区域,中间的范围是拿给PDE映射的,而不是PTE。
给定一个VA也可以计算出其PDE的虚拟地址。一个PDE映射了512个PTE。
PDPTEs的虚拟地址就是左边填3个auto-entry index项。
PDPTE的起始地址就是 0xFFFFF6FB'7DA00000,即PPE_BASE
PDPTE的结束地址就是 0xFFFFF6FB'7DC00000
给定一个VA也可以计算出其PDPTE的虚拟地址。(移出三个index空位,分别将auto-entry index填入PML4,PDPT,PD)
如果四个索引位都设置成auto-entry index的话,那么会得到一个4kb的范围.
PML4E 起始地址 0xFFFFF6FB'7DBED000,即PXE_BASE
PML4E 结束地址 0xFFFFF6FB'7DBEE000
PML4 auto-entry的index是0x1e8。他的offset=0x1e8 x 8 = 0xf68。那么他的VA就是0xFFFFF6FB'7DBED000 + 0xF68 = 0XFFFFF6FB7DBEDF68。
由此可做如下内存区域区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | FFFFF700`00000000 |------------| --------------------------------------
| | |
| PTE High | |
| | |
FFFFF6FB`80000000 |------------| ----------------------------- |
| | | |
| PDE High | | |
| | | |
FFFFF6FB`7D000000 |------------|------------------ | |
| | | | |
| PDPTE High | | | |
FFFFF6FB`7DBEE000 |------------|----- | | |
| PML4E | | 4KB | 2MB | 1GB | 512GB
FFFFF6FB`7DBED000 |------------|----- PXE_BASE | | |
| | | | |
| PDPTE Low | | | |
FFFFF6FB`7DA00000 |------------|------------------ PPE_BASE | |
| | | |
| PDE Low | | |
| | | |
FFFFF6FB`40000000 |------------| ---------------------------- PDE_BASE|
| | |
| PTE Low | |
| | |
FFFFF680`00000000 |------------| -------------------------------------- PTE_BASE
Paging Structures Region
|
ed nt!Kd_SXS_Mask 0
ed nt!Kd_FUSION_Mask 0
接下来可以编写代码完成相关功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ULONG_PTR pte_base, pde_base, ppe_base, pxe_base;
pte_base = pde_base = ppe_base = pxe_base = 0;
PHYSICAL_ADDRESS pml4;
pml4.QuadPart = __readcr3();
PMMPTE_HARDWARE page_directory_va
= (PMMPTE_HARDWARE)MmGetVirtualForPhysical(pml4);
KdPrint(( "pml4: %p\n" , page_directory_va));
pxe_base = ( ULONG_PTR )page_directory_va;
ULONG_PTR pxe_end = pxe_base + (1ull << 12);
ULONG_PTR auto_entry_index = (pxe_base >> 12) & 0x1ff;
KdPrint(( "auto entry index: 0x%x\n" , auto_entry_index));
ULONG_PTR va = 0xFFFF0000'00000000;
pte_base = va | (auto_entry_index << 39);
ULONG_PTR pte_end = pte_base + (1ull << 39);
pde_base = pte_base | (auto_entry_index << 30);
ULONG_PTR pde_end = pde_base + (1ull << 30);
ppe_base = pde_base | (auto_entry_index << 21);
ULONG_PTR ppe_end = ppe_base + (1ull << 21);
KdPrint(( "pte_base: %p,end: %p\n" , pte_base, pte_end));
KdPrint(( "pde_base: %p,end: %p\n" , pde_base, pde_end));
KdPrint(( "ppe_base: %p,end: %p\n" , ppe_base, ppe_end));
KdPrint(( "pxe_base: %p,end: %p\n" , pxe_base, pxe_end));
|
测试结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 0 : kd> uf nt!MiGetPteAddress
nt!MiGetPteAddress:
fffff806` 09e64bf4 48c1e909 shr rcx, 9
fffff806` 09e64bf8 48b8f8ffffff7f000000 mov rax, 7FFFFFFFF8h
fffff806` 09e64c02 4823c8 and rcx,rax
fffff806` 09e64c05 48b8000000008081ffff mov rax, 0FFFF818000000000h
fffff806` 09e64c0f 4803c1 add rax,rcx
fffff806` 09e64c12 c3 ret
0 : kd> !pte 0
VA 0000000000000000
PXE at FFFF81C0E0703000 PPE at FFFF81C0E0600000 PDE at FFFF81C0C0000000 PTE at FFFF818000000000
contains 8A00000101B3E867 contains 0A00000101B3F867 contains 0000000000000000
pfn 101b3e - - - DA - - UW - V pfn 101b3f - - - DA - - UWEV contains 0000000000000000
not valid
0 : kd> g
pml4: FFFF81C0E0703000
auto entry index: 0x103
pte_base: FFFF818000000000,end: FFFF820000000000
pde_base: FFFF81C0C0000000,end: FFFF81C100000000
ppe_base: FFFF81C0E0600000,end: FFFF81C0E0800000
pxe_base: FFFF81C0E0703000,end: FFFF81C0E0704000
|
完整代码github:
https://github.com/BeneficialCode/PagingStructuresRegion
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2023-10-19 22:38
被VirtualCC编辑
,原因: 修正代码显示问题