众所周知windows 10 14316开始实现了页表随机化,之前WRK里给出的#define PTE_BASE 0xFFFFF68000000000h 不再有效
那么各路神仙是怎么定位新的PTE_BASE的呢?
著名国际友人tandasat是通过特征码定位的:
// Get PTE_BASE from MmGetVirtualForPhysical
const auto p_MmGetVirtualForPhysical =
UtilGetSystemProcAddress(L"MmGetVirtualForPhysical");
if (!p_MmGetVirtualForPhysical) {
return STATUS_PROCEDURE_NOT_FOUND;
}
static const UCHAR kPatternWin10x64[] = {
0x48, 0x8b, 0x04, 0xd0, // mov rax, [rax+rdx*8]
0x48, 0xc1, 0xe0, 0x19, // shl rax, 19h
0x48, 0xba, // mov rdx, ????????`???????? ; PTE_BASE
};
auto found = reinterpret_cast<ULONG_PTR>(
UtilMemMem(p_MmGetVirtualForPhysical, 0x30, kPatternWin10x64,
sizeof(kPatternWin10x64)));
if (!found) {
return STATUS_PROCEDURE_NOT_FOUND;
}
found += sizeof(kPatternWin10x64);
HYPERPLATFORM_LOG_DEBUG("Found a hard coded PTE_BASE at %016Ix", found);
const auto pte_base = *reinterpret_cast<ULONG_PTR *>(found);
const auto index = (pte_base >> kUtilpPxiShift) & kUtilpPxiMask;
const auto pde_base = pte_base | (index << kUtilpPpiShift);
const auto ppe_base = pde_base | (index << kUtilpPdiShift);
const auto pxe_base = ppe_base | (index << kUtilpPtiShift);
g_utilp_pxe_base = static_cast<ULONG_PTR>(pxe_base);
g_utilp_ppe_base = static_cast<ULONG_PTR>(ppe_base);
g_utilp_pde_base = static_cast<ULONG_PTR>(pde_base);
g_utilp_pte_base = static_cast<ULONG_PTR>(pte_base);
g_utilp_pxi_shift = kUtilpPxiShift;
g_utilp_ppi_shift = kUtilpPpiShift;
g_utilp_pdi_shift = kUtilpPdiShift;
g_utilp_pti_shift = kUtilpPtiShift;
g_utilp_pxi_mask = kUtilpPxiMask;
g_utilp_ppi_mask = kUtilpPpiMask;
g_utilp_pdi_mask = kUtilpPdiMask;
g_utilp_pti_mask = kUtilpPtiMask;
return status;
那么鹅厂是如何定位的呢?
国际惯例直接上伪代码
char __fastcall InitializePteBase(char a1)
{
char v1; // bl
PHYSICAL_ADDRESS pml4t; // rdi
__int64 *pml4t_va; // r11
int slot; // edx
__int64 index; // rcx
__int64 v6; // r8
v1 = 0;
if ( a1 )
{
pml4t.QuadPart = __readcr3();
pml4t_va = (__int64 *)MmMapIoSpace(pml4t, 0x1000ui64, MmCached);
if ( pml4t_va )
{
slot = 0;
index = 0i64;
while ( (pml4t_va[index] & 0xFFFFFFFFF000i64) != pml4t.QuadPart )
{
++index;
++slot;
if ( index >= 512 )
goto LABEL_8;
}
v1 = 1;
v6 = (slot + 0x1FFFE00i64) << 39;
g_pte_base = (slot + 0x1FFFE00i64) << 39;
g_pxe_selfmapping_index = slot;
g_pde_base = v6 + ((__int64)slot << 30);
g_ppe_base = v6 + ((__int64)slot << 30) + ((__int64)slot << 21);
g_pxe_base = (void *)(g_ppe_base + ((__int64)slot << 12));
g_pxe_end = (__int64)g_pxe_base + 4096;
g_pte_end = v6 + 0x8000000000i64;
LABEL_8:
MmUnmapIoSpace(pml4t_va, 0x1000ui64);
}
}
else
{
g_pxe_selfmapping_index = 493i64;
v1 = 1;
g_pte_base = 0xFFFFF68000000000i64;
g_pde_base = 0xFFFFF6FB40000000i64;
g_ppe_base = 0xFFFFF6FB7DA00000i64;
g_pxe_base = (void *)0xFFFFF6FB7DBED000i64;
g_pxe_end = 0xFFFFF6FB7DBEE000i64;
g_pte_end = 0xFFFFF70000000000i64;
}
return v1;
}
先以当前cr3为物理地址映射成虚拟地址,相当于访问cr3所指向的pml4t表
然后在这个[512]的表(数组)中找到一项指向当前cr3的
也就是所谓的页表自映射,这一项PML4E所指向的物理页page_frame_number代表它自己这个表
满足这样的映射关系的PML4E所代表的虚拟地址空间也就相当于页表本身,一共0x8000000000/8 (512/8=64GB)个PTE
我们先从无随机化的情况论证:
大家都知道x64的线性地址是分成9① 9② 9③ 9④ 12⑤ 五部分进行地址翻译的:①代表pml4e索引,②代表pdpte索引,③代表pde索引,④代表pte索引,⑤代表页内偏移
无随机化的情况下第[493]项pml4e指向cr3,第[493]项pml4e是111101101(9位),左移39位让其变为第39~47位代表pml4e索引并补齐48~63位FFFF000000000000(保留位)
就是这样
刚好就是PTE_BASE
那么只要找到
指向当前cr3的pml4e index,将其左移39位并补上FFFF000000000000就得到了PTE_BASE
也就是代码中的 g_pte_base = (slot + 0x1FFFE00i64) << 39;(注意)
0x1FFFE00i64左移39位就是
FFFF000000000000
计算PDE_BASE也只需要将这个能够自映射的index左移30位让其变为30~38位代表pdpte索引,再加上PTE_BASE就等于PDE_BASE了
也就是
g_pde_base = g_pte_base + ((__int64)slot << 30);
PPE_BASE和PXE_BASE同理
注意有一点:win10 1803之后MmMapIoSpace不再允许映射页表相关的物理地址,因此只能用MmGetVirtualForPhysical代替,或者自己实现映射物理地址(方法很多)
// Get PTE_BASE from MmGetVirtualForPhysical
const auto p_MmGetVirtualForPhysical =
UtilGetSystemProcAddress(L"MmGetVirtualForPhysical");
if (!p_MmGetVirtualForPhysical) {
return STATUS_PROCEDURE_NOT_FOUND;
}
static const UCHAR kPatternWin10x64[] = {
0x48, 0x8b, 0x04, 0xd0, // mov rax, [rax+rdx*8]
0x48, 0xc1, 0xe0, 0x19, // shl rax, 19h
0x48, 0xba, // mov rdx, ????????`???????? ; PTE_BASE
};
auto found = reinterpret_cast<ULONG_PTR>(
UtilMemMem(p_MmGetVirtualForPhysical, 0x30, kPatternWin10x64,
sizeof(kPatternWin10x64)));
if (!found) {
return STATUS_PROCEDURE_NOT_FOUND;
}
found += sizeof(kPatternWin10x64);
HYPERPLATFORM_LOG_DEBUG("Found a hard coded PTE_BASE at %016Ix", found);
const auto pte_base = *reinterpret_cast<ULONG_PTR *>(found);
const auto index = (pte_base >> kUtilpPxiShift) & kUtilpPxiMask;
const auto pde_base = pte_base | (index << kUtilpPpiShift);
const auto ppe_base = pde_base | (index << kUtilpPdiShift);
const auto pxe_base = ppe_base | (index << kUtilpPtiShift);
g_utilp_pxe_base = static_cast<ULONG_PTR>(pxe_base);
g_utilp_ppe_base = static_cast<ULONG_PTR>(ppe_base);
g_utilp_pde_base = static_cast<ULONG_PTR>(pde_base);
g_utilp_pte_base = static_cast<ULONG_PTR>(pte_base);
g_utilp_pxi_shift = kUtilpPxiShift;
g_utilp_ppi_shift = kUtilpPpiShift;
g_utilp_pdi_shift = kUtilpPdiShift;
g_utilp_pti_shift = kUtilpPtiShift;
g_utilp_pxi_mask = kUtilpPxiMask;
g_utilp_ppi_mask = kUtilpPpiMask;
g_utilp_pdi_mask = kUtilpPdiMask;
g_utilp_pti_mask = kUtilpPtiMask;
return status;
那么鹅厂是如何定位的呢?
国际惯例直接上伪代码
char __fastcall InitializePteBase(char a1)
{
char v1; // bl
PHYSICAL_ADDRESS pml4t; // rdi
__int64 *pml4t_va; // r11
int slot; // edx
__int64 index; // rcx
__int64 v6; // r8
v1 = 0;
if ( a1 )
{
pml4t.QuadPart = __readcr3();
pml4t_va = (__int64 *)MmMapIoSpace(pml4t, 0x1000ui64, MmCached);
if ( pml4t_va )
{
slot = 0;
index = 0i64;
while ( (pml4t_va[index] & 0xFFFFFFFFF000i64) != pml4t.QuadPart )
{
++index;
++slot;
if ( index >= 512 )
goto LABEL_8;
}
v1 = 1;
v6 = (slot + 0x1FFFE00i64) << 39;
g_pte_base = (slot + 0x1FFFE00i64) << 39;
g_pxe_selfmapping_index = slot;
g_pde_base = v6 + ((__int64)slot << 30);
g_ppe_base = v6 + ((__int64)slot << 30) + ((__int64)slot << 21);
g_pxe_base = (void *)(g_ppe_base + ((__int64)slot << 12));
g_pxe_end = (__int64)g_pxe_base + 4096;
g_pte_end = v6 + 0x8000000000i64;
LABEL_8:
MmUnmapIoSpace(pml4t_va, 0x1000ui64);
}
}
else
{
g_pxe_selfmapping_index = 493i64;
v1 = 1;
g_pte_base = 0xFFFFF68000000000i64;
g_pde_base = 0xFFFFF6FB40000000i64;
g_ppe_base = 0xFFFFF6FB7DA00000i64;
g_pxe_base = (void *)0xFFFFF6FB7DBED000i64;
g_pxe_end = 0xFFFFF6FB7DBEE000i64;
g_pte_end = 0xFFFFF70000000000i64;
}
return v1;
}
先以当前cr3为物理地址映射成虚拟地址,相当于访问cr3所指向的pml4t表
然后在这个[512]的表(数组)中找到一项指向当前cr3的
也就是所谓的页表自映射,这一项PML4E所指向的物理页page_frame_number代表它自己这个表
满足这样的映射关系的PML4E所代表的虚拟地址空间也就相当于页表本身,一共0x8000000000/8 (512/8=64GB)个PTE
我们先从无随机化的情况论证:
大家都知道x64的线性地址是分成9① 9② 9③ 9④ 12⑤ 五部分进行地址翻译的:①代表pml4e索引,②代表pdpte索引,③代表pde索引,④代表pte索引,⑤代表页内偏移
无随机化的情况下第[493]项pml4e指向cr3,第[493]项pml4e是111101101(9位),左移39位让其变为第39~47位代表pml4e索引并补齐48~63位FFFF000000000000(保留位)
就是这样
刚好就是PTE_BASE
那么只要找到
指向当前cr3的pml4e index,将其左移39位并补上FFFF000000000000就得到了PTE_BASE
也就是代码中的 g_pte_base = (slot + 0x1FFFE00i64) << 39;(注意)
0x1FFFE00i64左移39位就是
FFFF000000000000
计算PDE_BASE也只需要将这个能够自映射的index左移30位让其变为30~38位代表pdpte索引,再加上PTE_BASE就等于PDE_BASE了
也就是
g_pde_base = g_pte_base + ((__int64)slot << 30);
PPE_BASE和PXE_BASE同理
注意有一点:win10 1803之后MmMapIoSpace不再允许映射页表相关的物理地址,因此只能用MmGetVirtualForPhysical代替,或者自己实现映射物理地址(方法很多)
char __fastcall InitializePteBase(char a1)
{
char v1; // bl
PHYSICAL_ADDRESS pml4t; // rdi
__int64 *pml4t_va; // r11
int slot; // edx
__int64 index; // rcx
__int64 v6; // r8
v1 = 0;
if ( a1 )
{
pml4t.QuadPart = __readcr3();
pml4t_va = (__int64 *)MmMapIoSpace(pml4t, 0x1000ui64, MmCached);
if ( pml4t_va )
{
slot = 0;
index = 0i64;
while ( (pml4t_va[index] & 0xFFFFFFFFF000i64) != pml4t.QuadPart )
{
++index;
++slot;
if ( index >= 512 )
goto LABEL_8;
}
v1 = 1;
v6 = (slot + 0x1FFFE00i64) << 39;
g_pte_base = (slot + 0x1FFFE00i64) << 39;
g_pxe_selfmapping_index = slot;
g_pde_base = v6 + ((__int64)slot << 30);
g_ppe_base = v6 + ((__int64)slot << 30) + ((__int64)slot << 21);
g_pxe_base = (void *)(g_ppe_base + ((__int64)slot << 12));
g_pxe_end = (__int64)g_pxe_base + 4096;
g_pte_end = v6 + 0x8000000000i64;
LABEL_8:
MmUnmapIoSpace(pml4t_va, 0x1000ui64);
}
}
else
{
g_pxe_selfmapping_index = 493i64;
v1 = 1;
g_pte_base = 0xFFFFF68000000000i64;
g_pde_base = 0xFFFFF6FB40000000i64;
g_ppe_base = 0xFFFFF6FB7DA00000i64;
g_pxe_base = (void *)0xFFFFF6FB7DBED000i64;
g_pxe_end = 0xFFFFF6FB7DBEE000i64;
g_pte_end = 0xFFFFF70000000000i64;
}
return v1;
}
先以当前cr3为物理地址映射成虚拟地址,相当于访问cr3所指向的pml4t表
然后在这个[512]的表(数组)中找到一项指向当前cr3的
也就是所谓的页表自映射,这一项PML4E所指向的物理页page_frame_number代表它自己这个表
满足这样的映射关系的PML4E所代表的虚拟地址空间也就相当于页表本身,一共0x8000000000/8 (512/8=64GB)个PTE
我们先从无随机化的情况论证:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2019-9-4 12:02
被hzqst编辑
,原因: