From bc16f431ab99526c3316ff631c98f24f5ccf7435 Mon Sep 17 00:00:00 2001 From: winterknife Date: Sun, 15 Jun 2025 18:03:13 -0400 Subject: [PATCH] added new extension command to PagingDbgExt --- PagingDbgExt/Inc/PageTableWalk.h | 131 +++++++++++- PagingDbgExt/Misc/AllocTest.exe | Bin 0 -> 4096 bytes PagingDbgExt/PagingDbgExt.vcxproj | 2 +- PagingDbgExt/README.md | 308 ++++++++++++++------------- PagingDbgExt/Src/DbgExt.cpp | 4 +- PagingDbgExt/Src/Help.cpp | 3 +- PagingDbgExt/Src/PageTableWalk.cpp | 326 ++++++++++++++++++++++++++++- 7 files changed, 625 insertions(+), 149 deletions(-) create mode 100644 PagingDbgExt/Misc/AllocTest.exe diff --git a/PagingDbgExt/Inc/PageTableWalk.h b/PagingDbgExt/Inc/PageTableWalk.h index d79bcc7..8a045a1 100644 --- a/PagingDbgExt/Inc/PageTableWalk.h +++ b/PagingDbgExt/Inc/PageTableWalk.h @@ -7,7 +7,7 @@ // // Modifications: // 2025-05-20 Created -// 2025-06-02 Updated +// 2025-06-08 Updated // ======================================================================== // ======================================================================== @@ -108,6 +108,120 @@ typedef union _PML4E { } PML4E, *PPML4E; #pragma warning(pop) +// PDPT Entry (PDPTE) +#pragma warning(push) +#pragma warning(disable: 4201) +typedef union _PDPTE { + struct { + QWORD P : 1; // Present + QWORD RW : 1; // Read/Write + QWORD US : 1; // User/Supervisor + QWORD PWT : 1; // Page-level Write Through + QWORD PCD : 1; // Page-level Cache Disable + QWORD A : 1; // Accessed + QWORD D : 1; // Dirty + QWORD PS : 1; // Page Size (must be set if entry maps a 1 GB page), reserved (must be 0) if processor doesn't support huge pages + QWORD G : 1; // Global (if CR4.PGE = 1, determines whether the translation is global, ignored otherwise) + QWORD Ignored1 : 2; // Ignored + QWORD R : 1; // Restart (HLAT paging) + QWORD PAT : 1; // Page Attribute Table (PAT) + QWORD Reserved1 : 17; // Reserved (must be 0) + QWORD PFN1 : 18; // Physical address of the 1-GByte page referenced by this entry + QWORD Reserved2 : 4; // Reserved (must be 0) + QWORD Ignored2 : 7; // Ignored + QWORD PK : 4; // Protection Key (if CR4.PKE = 1 or CR4.PKS = 1, this may control the page’s access rights, ignored otherwise) + QWORD XD : 1; // Execute Disable, reserved (must be 0) if IA32_EFER.NXE = 0 + }; + struct { + QWORD : 12; + QWORD PFN2 : 36; // Physical address of 4-KByte aligned page directory referenced by this entry + QWORD : 16; + }; + QWORD Value; +} PDPTE, *PPDPTE; +#pragma warning(pop) + +// PD Entry (PDE) +#pragma warning(push) +#pragma warning(disable: 4201) +typedef union _PDE { + struct { + QWORD P : 1; // Present + QWORD RW : 1; // Read/Write + QWORD US : 1; // User/Supervisor + QWORD PWT : 1; // Page-level Write Through + QWORD PCD : 1; // Page-level Cache Disable + QWORD A : 1; // Accessed + QWORD D : 1; // Dirty + QWORD PS : 1; // Page Size (must be set if entry maps a 2 MB page) + QWORD G : 1; // Global (if CR4.PGE = 1, determines whether the translation is global, ignored otherwise) + QWORD Ignored1 : 2; // Ignored + QWORD R : 1; // Restart (HLAT paging) + QWORD PAT : 1; // Page Attribute Table (PAT) + QWORD Reserved1 : 8; // Reserved (must be 0) + QWORD PFN1 : 27; // Physical address of the 2-MByte page referenced by this entry + QWORD Reserved2 : 4; // Reserved (must be 0) + QWORD Ignored2 : 7; // Ignored + QWORD PK : 4; // Protection Key (if CR4.PKE = 1 or CR4.PKS = 1, this may control the page’s access rights, ignored otherwise) + QWORD XD : 1; // Execute Disable, reserved (must be 0) if IA32_EFER.NXE = 0 + }; + struct { + QWORD : 12; + QWORD PFN2 : 36; // Physical address of 4-KByte aligned page table referenced by this entry + QWORD : 16; + }; + QWORD Value; +} PDE, *PPDE; +#pragma warning(pop) + +// PT Entry (PTE) +#pragma warning(push) +#pragma warning(disable: 4201) +typedef union _PTE { + struct { + QWORD P : 1; // Present + QWORD RW : 1; // Read/Write + QWORD US : 1; // User/Supervisor + QWORD PWT : 1; // Page-level Write Through + QWORD PCD : 1; // Page-level Cache Disable + QWORD A : 1; // Accessed + QWORD D : 1; // Dirty + QWORD PAT : 1; // Page Attribute Table (PAT) + QWORD G : 1; // Global (if CR4.PGE = 1, determines whether the translation is global, ignored otherwise) + QWORD Ignored1 : 2; // Ignored + QWORD R : 1; // Restart (HLAT paging) + QWORD PFN : 36; // Physical address of the 4-KByte page referenced by this entry + QWORD Reserved1 : 4; // Reserved (must be 0) + QWORD Ignored2 : 7; // Ignored + QWORD PK : 4; // Protection Key (if CR4.PKE = 1 or CR4.PKS = 1, this may control the page’s access rights, ignored otherwise) + QWORD XD : 1; // Execute Disable, reserved (must be 0) if IA32_EFER.NXE = 0 + }; + QWORD Value; +} PTE, *PPTE; +#pragma warning(pop) + +// Windows x64 hardware PxE (MMU's interpretation) +typedef struct _MMPTE_HARDWARE { + QWORD Valid : 1; // Present + QWORD Dirty1 : 1; // Read/Write (cleared together with Dirty bit) + QWORD Owner : 1; // User/Supervisor + QWORD WriteThrough : 1; // Page-level Write Through + QWORD CacheDisable : 1; // Page-level Cache Disable + QWORD Accessed : 1; // Accessed + QWORD Dirty : 1; // Dirty + QWORD LargePage : 1; // Page Attribute Table (PAT) or Page Size (PS) + QWORD Global : 1; // Global + QWORD CopyOnWrite : 1; // Copy On Write (CoW) + QWORD Unused : 1; // Ignored + QWORD Write : 1; // Used by the Memory Manager to recognize the page as writable + QWORD PageFrameNumber : 36; // Page Frame Number (PFN) + QWORD ReservedForHardware : 4; // Reserved (must be 0) + QWORD ReservedForSoftware : 4; // Reserved (must be 0) + QWORD WsleAge : 4; // Working Set List Entry (WSLE) + QWORD WsleProtection : 3; // Working Set List Entry (WSLE) + QWORD NoExecute : 1; // Execute Disable (XD/NX) +} MMPTE_HARDWARE, *PMMPTE_HARDWARE; + #pragma endregion // ======================================================================== @@ -177,6 +291,21 @@ extern "C" __declspec(dllexport) VOID __stdcall get_pxe_address( _In_z_ PCSTR strArgs ); +/// @brief Built-in command to translate the specified linear address (VA) to its mapped physical address (PA) and determine the VA's access rights from the CR3 +/// @param hCurrentProcess Handle for the current process +/// @param hCurrentThread Handle for the current thread +/// @param qwCurrentPc Program counter +/// @param dwProcessor Current processor +/// @param strArgs Command line arguments (VA and CR3 value) +/// @return None +extern "C" __declspec(dllexport) VOID __stdcall get_physical_address( + _In_ HANDLE hCurrentProcess, + _In_ HANDLE hCurrentThread, + _In_ QWORD qwCurrentPc, + _In_ DWORD dwProcessor, + _In_z_ PCSTR strArgs +); + #pragma endregion // ======================================================================== diff --git a/PagingDbgExt/Misc/AllocTest.exe b/PagingDbgExt/Misc/AllocTest.exe new file mode 100644 index 0000000000000000000000000000000000000000..834e0e957a3e890a5770ddf55e15a9e75bf63144 GIT binary patch literal 4096 zcmeHJTWl0n82)>KKxK;tmJogLfUO$H*4{)?W89^?Fvym!y(<t=7>Pz7ygm^LX)!T~L{OWdF1ahA$VlP#Zcg@yaZnf*Qk#4K3-7HDf zIOy$=e1Td8&#YGxEoU9Wm6AB{+$F-gh7P+yPAoj_B(C6C2Vs>?fLKgw&eTY>s`TE` zuirzowG6%_B#5S#G!ivf+CdBUle`&Bh&Kf5C>?@msKJP-mP+(CFBkx=z*Ss4bPPX! z4H?@{a>p8Eu3EO<9Dexu`<25#=75Fso*e3i^UEe6l)QIZ3eRg01#gINE(GfaI9kaE z>xPuc8!cyNx%w~fGZ41{_%Q%iKQtK=aTSG?g7=fvTn4$$2;3`^7Yp9!0KIrND+=CY z&?diD@}72CcMi8x^BFW^eiRdj4T0MTj4huRyt|8WPq}f=%DCe)ZY#&NU;>>uKQ%Ml z4rTN#s)mthB3zv;LKlM77F;Fe`yr(wSP9G`r>q@sxy+FR1@C$E(q?=Gnq>UAtbCWO z{4uJ{&tfWg&%yr4+GRx9^;p&+>GUhGsrgsg=@=&9rzHLzaBywps@y}#15z-tz%0QK znBm8xETiaTSN9$wwbTqY!=Emj+rAr{0)T2F` z$!8^xJ(~tay(zY36G@Aedb5Tif!^+rZKgfJZ94|r#2(+E=uNA`NljR~P@~Ue6NV;K z5m(bO8?)jXf4QQXX~JbxOSg&oSPgZPMq$z^N~6>PJq6vMaf)D0&={^H(0#bH8s{US?}F2Bu#A|zSGM7I{!qoW;KN;k$M?z)&2c1}&Y zO}@{>Hr(vCo7K&?aKCudRf{*QRqWa(GHS6=u0QHaQ&*?7iec5%L|>pyG#;$alrVPv z7vx9wQQ5O%cF@Ihl8KL-3Fe%Viz(fkvU${-Jex(wQaq)ptG_(Yf6Ch=b}W@wnz{zc z=Pi9F-2YnqQQ_B9sl(;;$hG0!CnA1#C~PQf93r9soESjEiFa#K{XD8v^7GMGRLBZ z)euW2iB>P~OBhyGO>Q?djUKMtp;_%&!_d-J#6GlWo$Jw?=I5D?uuoVut&m~p2L9Pn zdr~(wMV@$chwB|?-&{rLV4|35SEy@uDBRL)S3XkFnbFctl^#AB3I9VJHgbELX&nV( z`?IDc&Bb@#qz5X(x;~oC6rnyfnbrC!5bO&?cDfbROv0BkeFqY0UsN|VpZ&J^l1;uQ d-xixhnVq`@{VN^d_je7^F@D)^jsIN-{sgpGyy^e| literal 0 HcmV?d00001 diff --git a/PagingDbgExt/PagingDbgExt.vcxproj b/PagingDbgExt/PagingDbgExt.vcxproj index 4ee58b8..6edce68 100644 --- a/PagingDbgExt/PagingDbgExt.vcxproj +++ b/PagingDbgExt/PagingDbgExt.vcxproj @@ -58,7 +58,7 @@ %(AdditionalOptions) /homeparams - Native + Windows true true DebugFull diff --git a/PagingDbgExt/README.md b/PagingDbgExt/README.md index 8d7c489..19f7117 100644 --- a/PagingDbgExt/README.md +++ b/PagingDbgExt/README.md @@ -1,7 +1,7 @@ # EVENSTAR - PagingDbgExt ## Version -- `v10.5.6` +- `v10.6.6` ## Brief - `ISA: x86` @@ -15,33 +15,34 @@ ## Usage - Set up kernel-mode debugging of the target ``` -0: kd> vertarget +1: kd> vertarget Windows 10 Kernel Version 22000 MP (2 procs) Free x64 Product: WinNt, suite: TerminalServer SingleUserTS Edition build lab: 22000.1.amd64fre.co_release.210604-1628 -Kernel base = 0xfffff804`6580f000 PsLoadedModuleList = 0xfffff804`66438b90 -Debug session time: Tue Jun 3 17:15:28.502 2025 (UTC - 4:00) -System Uptime: 0 days 0:12:56.092 +Kernel base = 0xfffff804`4fe00000 PsLoadedModuleList = 0xfffff804`50a29b90 +Debug session time: Sun Jun 15 14:17:31.297 2025 (UTC - 4:00) +System Uptime: 0 days 3:37:31.814 -0: kd> .load PagingDbgExt.dll +1: kd> .load PagingDbgExt.dll -0: kd> .chain +1: kd> .chain --snip-- Extension DLL chain: - PagingDbgExt: API 10.5.6, + PagingDbgExt: API 10.6.6, [path: C:\Users\winterknife\Desktop\Tools\WinDbgExtensions\PagingDbgExt.dll] --snip-- -0: kd> .extmatch /D /e PagingDbgExt * +1: kd> .extmatch /D /e PagingDbgExt * !PagingDbgExt.check_4_level_paging_mode !PagingDbgExt.get_kernel_dtb +!PagingDbgExt.get_physical_address !PagingDbgExt.get_pml4_autoentry_index !PagingDbgExt.get_process_dtb !PagingDbgExt.get_pxe_address !PagingDbgExt.get_pxe_base !PagingDbgExt.help -0: kd> !PagingDbgExt.help +1: kd> !PagingDbgExt.help Help for debugger extension DLL PagingDbgExt.dll help - Show help menu get_kernel_dtb - Find the kernel's DTB (DirectoryTableBase) which is the physical address of the base of the paging-structure hierarchy for KVAS contained in CR3 from the Low Stub @@ -50,172 +51,169 @@ Help for debugger extension DLL PagingDbgExt.dll get_pml4_autoentry_index - Find the self-reference entry/auto-entry index of the Page Map Level 4 (PML4) table from the CR3 get_pxe_base - Compute the starting KVA of the paging structure entry (PxE) range from the PML4 table auto-entry index get_pxe_address - Compute the KVA of the paging structure entry (PxE) that maps the specified VA from the PML4 table auto-entry index + get_physical_address - Translate the specified linear address (VA) to its mapped physical address (PA) and determine the VA's access rights from the CR3 -0: kd> !PagingDbgExt.get_kernel_dtb +1: kd> !PagingDbgExt.get_kernel_dtb [+] nt!_PROCESSOR_START_BLOCK structure found at PA=0x13000 -[+] HAL Heap base KVA=0xFFFFF7C440000000 +[+] HAL Heap base KVA=0xFFFFF7D980000000 [+] nt!_PROCESSOR_START_BLOCK.ProcessorState.SpecialRegisters.Cr3=0x1AE000 -0: kd> ? poi(nt!HalpLowStubPhysicalAddress) +1: kd> ? poi(nt!HalpLowStubPhysicalAddress) Evaluate expression: 77824 = 00000000`00013000 -0: kd> ? poi(nt!HalpOriginalHeapStart) -Evaluate expression: -9052717318144 = fffff7c4`40000000 +1: kd> ? poi(nt!HalpOriginalHeapStart) +Evaluate expression: -8961449263104 = fffff7d9`80000000 -0: kd> r @cr3 -cr3=00000000001ae000 - -0: kd> !process 4 0 -Searching for Process with Cid == 4 -PROCESS ffffc4866e2ab040 - SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 - DirBase: 001ae000 ObjectTable: ffffe7876f403dc0 HandleCount: 1970. - Image: System - -0: kd> dt nt!_KPROCESS ffffc4866e2ab040 DirectoryTableBase - +0x028 DirectoryTableBase : 0x1ae000 - -0: kd> dt /p nt!_KPROCESSOR_STATE 0x13090 SpecialRegisters.Cr3 +1: kd> dt /p nt!_KPROCESSOR_STATE 0x13090 SpecialRegisters.Cr3 +0x000 SpecialRegisters : +0x010 Cr3 : 0x1ae000 -0: kd> !db 0x13000 -# 13000 e9 4d 06 00 01 00 00 00-01 00 00 00 3f 00 18 30 .M..........?..0 -# 13010 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -# 13020 00 00 00 00 00 00 00 00-00 00 00 00 00 9b 20 00 .............. . -# 13030 00 00 00 00 00 00 00 00-ff ff 00 00 00 93 cf 00 ................ -# 13040 00 00 00 00 00 00 00 00-ff ff 00 00 00 9b cf 00 ................ -# 13050 00 00 00 00 00 00 00 00-00 90 4f 79 00 00 00 00 ..........Oy.... -# 13060 79 36 01 00 30 00 d7 36-01 00 10 00 00 00 00 00 y6..0..6........ -# 13070 70 18 c2 65 04 f8 ff ff-00 c0 00 40 c4 f7 ff ff p..e.......@.... +1: kd> r @cr3 +cr3=00000000001ae000 -0: kd> db poi(nt!HalpLowStub) -fffff7c4`4000c000 e9 4d 06 00 01 00 00 00-01 00 00 00 3f 00 18 30 .M..........?..0 -fffff7c4`4000c010 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -fffff7c4`4000c020 00 00 00 00 00 00 00 00-00 00 00 00 00 9b 20 00 .............. . -fffff7c4`4000c030 00 00 00 00 00 00 00 00-ff ff 00 00 00 93 cf 00 ................ -fffff7c4`4000c040 00 00 00 00 00 00 00 00-ff ff 00 00 00 9b cf 00 ................ -fffff7c4`4000c050 00 00 00 00 00 00 00 00-00 90 4f 79 00 00 00 00 ..........Oy.... -fffff7c4`4000c060 79 36 01 00 30 00 d7 36-01 00 10 00 00 00 00 00 y6..0..6........ -fffff7c4`4000c070 70 18 c2 65 04 f8 ff ff-00 c0 00 40 c4 f7 ff ff p..e.......@.... +1: kd> !process 4 0 +Searching for Process with Cid == 4 +PROCESS ffff8088a80af040 + SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 + DirBase: 001ae000 ObjectTable: ffffcf03a6203dc0 HandleCount: 2209. + Image: System -0: kd> dps poi(nt!HalpLowStub) L10 -fffff7c4`4000c000 00000001`00064de9 -fffff7c4`4000c008 3018003f`00000001 -fffff7c4`4000c010 00000000`00000001 -fffff7c4`4000c018 00000000`00000000 -fffff7c4`4000c020 00000000`00000000 -fffff7c4`4000c028 00209b00`00000000 -fffff7c4`4000c030 00000000`00000000 -fffff7c4`4000c038 00cf9300`0000ffff -fffff7c4`4000c040 00000000`00000000 -fffff7c4`4000c048 00cf9b00`0000ffff -fffff7c4`4000c050 00000000`00000000 -fffff7c4`4000c058 00000000`794f9000 -fffff7c4`4000c060 36d70030`00013679 -fffff7c4`4000c068 00000000`00100001 -fffff7c4`4000c070 fffff804`65c21870 nt!HalpLMStub -fffff7c4`4000c078 fffff7c4`4000c000 +1: kd> dps poi(nt!HalpLowStub) L10 +fffff7d9`8000c000 00000001`00064de9 +fffff7d9`8000c008 3018003f`00000001 +fffff7d9`8000c010 00000000`00000001 +fffff7d9`8000c018 00000000`00000000 +fffff7d9`8000c020 00000000`00000000 +fffff7d9`8000c028 00209b00`00000000 +fffff7d9`8000c030 00000000`00000000 +fffff7d9`8000c038 00cf9300`0000ffff +fffff7d9`8000c040 00000000`00000000 +fffff7d9`8000c048 00cf9b00`0000ffff +fffff7d9`8000c050 00000000`00000000 +fffff7d9`8000c058 00000000`bffff000 +fffff7d9`8000c060 36d70030`00013679 +fffff7d9`8000c068 00000000`00100001 +fffff7d9`8000c070 fffff804`50212870 nt!HalpLMStub +fffff7d9`8000c078 fffff7d9`8000c000 -0: kd> !pte poi(nt!HalpLowStub) - VA fffff7c44000c000 -PXE at FFFFF97CBE5F2F78 PPE at FFFFF97CBE5EF888 PDE at FFFFF97CBDF11000 PTE at FFFFF97BE2200060 -contains 0000000002182063 contains 8000000002185063 contains 8000000002186063 contains 8000000000013963 -pfn 2182 ---DA--KWEV pfn 2185 ---DA--KW-V pfn 2186 ---DA--KW-V pfn 13 -G-DA--KW-V - -0: kd> !vtop 0x1ae000 0xfffff7c44000c000 -Amd64VtoP: Virt fffff7c44000c000, pagedir 00000000001ae000 -Amd64VtoP: PML4E 00000000001aef78 -Amd64VtoP: PDPE 0000000002182888 -Amd64VtoP: PDE 0000000002185000 -Amd64VtoP: PTE 0000000002186060 -Amd64VtoP: Mapped phys 0000000000013000 -Virtual address fffff7c44000c000 translates to physical address 13000. - -0: kd> !process 0 0 lsass.exe -PROCESS ffffc4866ede80c0 - SessionId: 0 Cid: 02e8 Peb: 414677f000 ParentCid: 0208 - DirBase: 7dec0000 ObjectTable: ffffe78771c7b2c0 HandleCount: 926. +1: kd> !process 0 0 lsass.exe +PROCESS ffff8088a8b26080 + SessionId: 0 Cid: 02e4 Peb: 9c876d1000 ParentCid: 0210 + DirBase: 10f8f1000 ObjectTable: ffffcf03a8a81280 HandleCount: 999. Image: lsass.exe -0: kd> !PagingDbgExt.get_process_dtb 0xffffc4866ede80c0 -[*] nt!_EPROCESS KVA=0xFFFFC4866EDE80C0 -[+] nt!_EPROCESS.Pcb.DirectoryTableBase=0x7DEC0000 +1: kd> !PagingDbgExt.get_process_dtb 0xffff8088a8b26080 +[*] nt!_EPROCESS KVA=0xFFFF8088A8B26080 +[+] nt!_EPROCESS.Pcb.DirectoryTableBase=0x10F8F1000 -0: kd> !PagingDbgExt.check_4_level_paging_mode -[!] Current processor number = 0 +1: kd> !PagingDbgExt.check_4_level_paging_mode +[!] Current processor number = 1 [+] 4-level IA-32e paging mode is enabled! -0: kd> !PagingDbgExt.get_pml4_autoentry_index 0x1AE000 -[!] Current processor number = 0 +1: kd> !PagingDbgExt.get_pml4_autoentry_index 0x1AE000 +[!] Current processor number = 1 [+] 4-level IA-32e paging mode is enabled! [*] CR3=0x1AE000 [+] Page Map Level 4 (PML4) table base PA=0x1AE000 -[+] PML4 auto-entry found at index: 0x1F2 (0n498) +[+] PML4 auto-entry found at index: 0x151 (0n337) -0: kd> ? (poi(nt!MmPteBase) >> 0n39) & 0x1ff -Evaluate expression: 498 = 00000000`000001f2 +1: kd> ? (poi(nt!MmPteBase) >> 0n39) & 0x1ff +Evaluate expression: 337 = 00000000`00000151 -0: kd> !PagingDbgExt.get_pxe_base 0x1F2 -[!] Current processor number = 0 +1: kd> !PagingDbgExt.get_pxe_base 0x151 +[!] Current processor number = 1 [+] 4-level IA-32e paging mode is enabled! -[*] PML4 table auto-entry index: 0x1F2 (0n498) -[+] PTE_BASE=0xFFFFF90000000000 -[+] PDE_BASE=0xFFFFF97C80000000 -[+] PPE_BASE=0xFFFFF97CBE400000 -[+] PXE_BASE=0xFFFFF97CBE5F2000 -[+] PXE_SELFMAP=0xFFFFF97CBE5F2F90 -[+] PXE_TOP=0xFFFFF97CBE5F2FFF -[+] PPE_TOP=0xFFFFF97CBE5FFFFF -[+] PDE_TOP=0xFFFFF97CBFFFFFFF -[+] PTE_TOP=0xFFFFF97FFFFFFFFF +[*] PML4 table auto-entry index: 0x151 (0n337) +[+] PTE_BASE=0xFFFFA88000000000 +[+] PDE_BASE=0xFFFFA8D440000000 +[+] PPE_BASE=0xFFFFA8D46A200000 +[+] PXE_BASE=0xFFFFA8D46A351000 +[+] PXE_SELFMAP=0xFFFFA8D46A351A88 +[+] PXE_TOP=0xFFFFA8D46A351FFF +[+] PPE_TOP=0xFFFFA8D46A3FFFFF +[+] PDE_TOP=0xFFFFA8D47FFFFFFF +[+] PTE_TOP=0xFFFFA8FFFFFFFFFF -0: kd> ? poi(nt!MmPteBase) -Evaluate expression: -7696581394432 = fffff900`00000000 +1: kd> ? poi(nt!MmPteBase) +Evaluate expression: -96207267430400 = ffffa880`00000000 -0: kd> uf nt!MiGetPteAddress +1: kd> uf nt!MiGetPteAddress nt!MiGetPteAddress: -fffff804`65b20f90 48c1e909 shr rcx,9 -fffff804`65b20f94 48b8f8ffffff7f000000 mov rax,7FFFFFFFF8h -fffff804`65b20f9e 4823c8 and rcx,rax -fffff804`65b20fa1 48b80000000000f9ffff mov rax,0FFFFF90000000000h -fffff804`65b20fab 4803c1 add rax,rcx -fffff804`65b20fae c3 ret +fffff804`50111f90 48c1e909 shr rcx,9 +fffff804`50111f94 48b8f8ffffff7f000000 mov rax,7FFFFFFFF8h +fffff804`50111f9e 4823c8 and rcx,rax +fffff804`50111fa1 48b80000000080a8ffff mov rax,0FFFFA88000000000h +fffff804`50111fab 4803c1 add rax,rcx +fffff804`50111fae c3 ret -0: kd> uf nt!MiGetPdeAddress +1: kd> uf nt!MiGetPdeAddress nt!MiGetPdeAddress: -fffff804`65ae0420 48c1e912 shr rcx,12h -fffff804`65ae0424 81e1f8ffff3f and ecx,3FFFFFF8h -fffff804`65ae042a 48b8000000807cf9ffff mov rax,0FFFFF97C80000000h -fffff804`65ae0434 4803c1 add rax,rcx -fffff804`65ae0437 c3 ret +fffff804`500d1420 48c1e912 shr rcx,12h +fffff804`500d1424 81e1f8ffff3f and ecx,3FFFFFF8h +fffff804`500d142a 48b800000040d4a8ffff mov rax,0FFFFA8D440000000h +fffff804`500d1434 4803c1 add rax,rcx +fffff804`500d1437 c3 ret -0: kd> rdmsr 0xC0000082 -msr[c0000082] = fffff804`65c37bc0 - -0: kd> !PagingDbgExt.get_pxe_address 0xfffff80465c37bc0 0x1f2 -[!] Current processor number = 0 +1: kd> !PagingDbgExt.get_pxe_address 0xFFFFF78000000000 0x151 +[!] Current processor number = 1 [+] 4-level IA-32e paging mode is enabled! -[*] VA=0xFFFFF80465C37BC0 -[*] PML4 table auto-entry index: 0x1F2 (0n498) -[+] PTE KVA=0xFFFFF97C0232E1B8 -[+] PDE KVA=0xFFFFF97CBE011970 -[+] PPE KVA=0xFFFFF97CBE5F0088 -[+] PXE KVA=0xFFFFF97CBE5F2F80 +[*] VA=0xFFFFF78000000000 +[*] PML4 table auto-entry index: 0x151 (0n337) +[+] PTE KVA=0xFFFFA8FBC0000000 +[+] PDE KVA=0xFFFFA8D47DE00000 +[+] PPE KVA=0xFFFFA8D46A3EF000 +[+] PXE KVA=0xFFFFA8D46A351F78 -0: kd> !pte 0xfffff80465c37bc0 - VA fffff80465c37bc0 -PXE at FFFFF97CBE5F2F80 PPE at FFFFF97CBE5F0088 PDE at FFFFF97CBE011970 PTE at FFFFF97C0232E1B8 -contains 000000000218B063 contains 000000000218C063 contains 000000000219C063 contains 090000007F05C121 -pfn 218b ---DA--KWEV pfn 218c ---DA--KWEV pfn 219c ---DA--KWEV pfn 7f05c -G--A--KREV +1: kd> !pte 0xFFFFF78000000000 + VA fffff78000000000 +PXE at FFFFA8D46A351F78 PPE at FFFFA8D46A3EF000 PDE at FFFFA8D47DE00000 PTE at FFFFA8FBC0000000 +contains 000000000F202063 contains 000000000F203063 contains 000000000F204063 contains 800000000F22C963 +pfn f202 ---DA--KWEV pfn f203 ---DA--KWEV pfn f204 ---DA--KWEV pfn f22c -G-DA--KW-V -0: kd> !pte 0xFFFFF97CBE011970 - VA fffff97c0232e000 -PXE at FFFFF97CBE5F2F90 PPE at FFFFF97CBE5F2F80 PDE at FFFFF97CBE5F0088 PTE at FFFFF97CBE011970 -contains 80000000001AE063 contains 000000000218B063 contains 000000000218C063 contains 000000000219C063 -pfn 1ae ---DA--KW-V pfn 218b ---DA--KWEV pfn 218c ---DA--KWEV pfn 219c ---DA--KWEV +1: kd> rdmsr 0xC0000082 +msr[c0000082] = fffff804`50228bc0 -0: kd> .unload PagingDbgExt.dll +1: kd> !PagingDbgExt.get_physical_address 0xfffff80450228bc0 0x1ae000 +[!] Current processor number = 1 +[+] 4-level IA-32e paging mode is enabled! +[*] VA=0xFFFFF80450228BC0 +[*] CR3=0x1AE000 +[+] PML4 table base PA=0x1AE000 +[+] PML4 index: 0x1F0 (0n496) +[+] PML4E PA=0x1AEF80 +[+] PML4E contents=0xF20B063 +[+] PDPT base PA=0xF20B000 +[+] PDPT index: 0x11 (0n17) +[+] PDPTE PA=0xF20B088 +[+] PDPTE contents=0xF20C063 +[+] PD table base PA=0xF20C000 +[+] PD index: 0x81 (0n129) +[+] PDE PA=0xF20C408 +[+] PDE contents=0xA000001008001A1 +[+] 2 MB physical page base PA=0x100800000 +[+] 2 MB physical page offset: 0x28BC0 (0n166848) +[+] PA=0x100828BC0 +[+] Read-only address. +[+] Supervisor-mode address. +[+] Executable address. +[+] Dirty: 0 +[+] Global: 1 + +1: kd> !vtop 0x1ae000 0xfffff80450228bc0 +Amd64VtoP: Virt fffff80450228bc0, pagedir 00000000001ae000 +Amd64VtoP: PML4E 00000000001aef80 +Amd64VtoP: PDPE 000000000f20b088 +Amd64VtoP: PDE 000000000f20c408 +Amd64VtoP: Large page mapped phys 0000000100828bc0 +Virtual address fffff80450228bc0 translates to physical address 100828bc0. + +1: kd> !pte fffff804`50228bc0 + VA fffff80450228bc0 +PXE at FFFFA8D46A351F80 PPE at FFFFA8D46A3F0088 PDE at FFFFA8D47E011408 PTE at FFFFA8FC02281140 +contains 000000000F20B063 contains 000000000F20C063 contains 0A000001008001A1 contains 0000000000000000 +pfn f20b ---DA--KWEV pfn f20c ---DA--KWEV pfn 100800 -GL-A--KREV LARGE PAGE pfn 100828 + +1: kd> .unload PagingDbgExt.dll Unloading PagingDbgExt extension DLL ``` @@ -243,4 +241,28 @@ I don't plan on improving the heuristics at the moment but you reading this shou 9. [Getting Physical: Extreme abuse of Intel based Paging Systems - Part 3 - Windows HAL's Heap](https://www.coresecurity.com/core-labs/articles/getting-physical-extreme-abuse-of-intel-based-paging-systems) 10. [Windows 8 Kernel Memory Protections Bypass](https://labs.withsecure.com/publications/windows-8-kernel-memory-protections-bypass) 11. [Virtual Memory - Intro to Paging Tables](https://blog.back.engineering/23/08/2020/) -12. [What Makes It Page?: The Windows 7 (x64) Virtual Memory Manager](https://www.amazon.ca/What-Makes-Page-Windows-Virtual/dp/1479114294) \ No newline at end of file +12. [What Makes It Page?: The Windows 7 (x64) Virtual Memory Manager](https://www.amazon.ca/What-Makes-Page-Windows-Virtual/dp/1479114294) +13. [Circumventing Leak Restrictions and Breaking KASLR on Windows 11 24H2 using an HVCI-compatible Driver with Physical Memory Access](https://xacone.github.io/kaslr_leak_24h2.html) +14. [Windows Address Translation Deep Dive – Part 1](https://bsodtutorials.wordpress.com/2021/06/14/windows-address-translation-deep-dive-part-1/) +15. [Windows Address Translation Deep Dive – Part 2](https://bsodtutorials.wordpress.com/2024/04/05/windows-address-translation-deep-dive-part-2/) +16. [Windows Address Translation Deep Dive – Part 3](https://bsodtutorials.wordpress.com/2024/05/14/windows-address-translation-deep-dive-part-3/) +17. [Windows Memory Introspection with IceBox](https://blog.thalium.re/posts/windows-full-memory-introspection-with-icebox/) +18. [Windows Virtual Address Translation and the Pagefile.](https://web.archive.org/web/20200120124724/http://blog.rekall-forensic.com/2014/10/windows-virtual-address-translation-and.html) +19. [Windows 10 KVAS and Software SMEP](https://wumb0.in/windows-10-kvas-and-software-smep.html) +20. [MMPTE_HARDWARE](https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/mm/mi/mmpte/hardware.htm) +21. [Virtual Memory, Page Tables, and One Bit - CVE-2016-7255](https://ricklarabee.blogspot.com/2017/01/virtual-memory-page-tables-and-one-bit.html) +22. [Windows 10 HAL’s Heap – Extinction of the "HalpInterruptController" Table Exploitation Technique](https://labs.bluefrostsecurity.de/blog/2017/05/11/windows-10-hals-heap-extinction-of-the-halpinterruptcontroller-table-exploitation-technique/) +23. [Meltdown Reloaded: Breaking Windows KASLR by Leaking KVA Shadow Mappings](https://labs.bluefrostsecurity.de/blog/2020/06/30/meltdown-reloaded-breaking-windows-kaslr/) +24. [Turning the Pages: Introduction to Memory Paging on Windows 10 x64](https://connormcgarr.github.io/paging/) +25. [Exploit Development: Leveraging Page Table Entries for Windows Kernel Exploitation](https://connormcgarr.github.io/pte-overwrites/) +26. [Page Table: From Virtual to Physical](https://dx9.uk/posts/physical-memory/) +27. [Direct Page Manipulation (DPM)](https://github.com/SamuelTulach/DirectPageManipulation) +28. [anyvtop](https://github.com/kkent030315/anyvtop) +29. [PageTableInjection](https://github.com/kkent030315/PageTableInjection) +30. [PTView](https://github.com/VollRagm/PTView) +31. [PTM - Page Table Manipulation From Usermode](https://blog.back.engineering/01/12/2020/) +32. [Making something out of Zeros: Alternative primitive for Windows Kernel Exploitation](https://www.coresecurity.com/core-labs/articles/making-something-out-zeros-alternative-primitive-windows-kernel-exploitation) +33. [pagewalkr](https://github.com/Deputation/pagewalkr) +34. [Paging](https://shhoya.github.io/hv_paging.html) +35. [Demystifying Physical Memory Primitive Exploitation on Windows](https://0dr3f.github.io/Demystifying_Physical_Memory_Primitive_Exploitation_on_Windows) +36. [Intel® 64 and IA-32 Architectures Software Developer Manuals](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html) \ No newline at end of file diff --git a/PagingDbgExt/Src/DbgExt.cpp b/PagingDbgExt/Src/DbgExt.cpp index 34fb415..d04f5fc 100644 --- a/PagingDbgExt/Src/DbgExt.cpp +++ b/PagingDbgExt/Src/DbgExt.cpp @@ -8,7 +8,7 @@ // // Modifications: // 2024-10-08 Created -// 2025-06-03 Updated +// 2025-06-15 Updated // ======================================================================== // ======================================================================== @@ -23,7 +23,7 @@ #pragma region GLOBALS -EXT_API_VERSION ApiVersion = { 10, 5, EXT_API_VERSION_NUMBER64, 0 }; +EXT_API_VERSION ApiVersion = { 10, 6, EXT_API_VERSION_NUMBER64, 0 }; WINDBG_EXTENSION_APIS ExtensionApis; DWORD SavedMajorVersion; DWORD SavedMinorVersion; diff --git a/PagingDbgExt/Src/Help.cpp b/PagingDbgExt/Src/Help.cpp index d22b513..2e8efb8 100644 --- a/PagingDbgExt/Src/Help.cpp +++ b/PagingDbgExt/Src/Help.cpp @@ -8,7 +8,7 @@ // // Modifications: // 2025-05-14 Created -// 2025-06-03 Updated +// 2025-06-15 Updated // ======================================================================== // ======================================================================== @@ -47,6 +47,7 @@ VOID __stdcall help( " get_pml4_autoentry_index - Find the self-reference entry/auto-entry index of the Page Map Level 4 (PML4) table from the CR3\n" " get_pxe_base - Compute the starting KVA of the paging structure entry (PxE) range from the PML4 table auto-entry index\n" " get_pxe_address - Compute the KVA of the paging structure entry (PxE) that maps the specified VA from the PML4 table auto-entry index\n" + " get_physical_address - Translate the specified linear address (VA) to its mapped physical address (PA) and determine the VA's access rights from the CR3\n" ); } diff --git a/PagingDbgExt/Src/PageTableWalk.cpp b/PagingDbgExt/Src/PageTableWalk.cpp index 6e4e6b5..46f99f6 100644 --- a/PagingDbgExt/Src/PageTableWalk.cpp +++ b/PagingDbgExt/Src/PageTableWalk.cpp @@ -10,7 +10,7 @@ // // Modifications: // 2025-05-20 Created -// 2025-06-03 Updated +// 2025-06-14 Updated // ======================================================================== // ======================================================================== @@ -361,4 +361,328 @@ cleanup: return; } +_Use_decl_annotations_ +VOID __stdcall get_physical_address( + HANDLE hCurrentProcess, + HANDLE hCurrentThread, + QWORD qwCurrentPc, + DWORD dwProcessor, + PCSTR strArgs +) { + // Init local variables + BOOLEAN bTranslation = FALSE; + VIRTUAL_ADDRESS virtualAddress; ZERO_MEMORY(&virtualAddress, sizeof(VIRTUAL_ADDRESS)); + QWORD qwCR3 = 0; + QWORD qwPML4BasePa = 0; + QWORD qwPML4Index = 0; + QWORD qwPML4EPa = 0; + PML4E pml4e; ZERO_MEMORY(&pml4e, sizeof(PML4E)); + DWORD dwBytesRead = 0; + QWORD qwPDPTBasePa = 0; + QWORD qwPDPTIndex = 0; + QWORD qwPDPTEPa = 0; + PDPTE pdpte; ZERO_MEMORY(&pdpte, sizeof(PDPTE)); + QWORD qwPhysicalPageBasePa = 0; + QWORD qwPhysicalPageOffset = 0; + QWORD qwPhysicalAddress = 0; + BOOLEAN bReadableWritablePage = FALSE; + BOOLEAN bUserModePage = FALSE; + BOOLEAN bNonExecutablePage = FALSE; + BOOLEAN bDirtyPage = FALSE; + BOOLEAN bGlobalPage = FALSE; + QWORD qwPDBasePa = 0; + QWORD qwPDIndex = 0; + QWORD qwPDEPa = 0; + PDE pde; ZERO_MEMORY(&pde, sizeof(PDE)); + QWORD qwPTBasePa = 0; + QWORD qwPTIndex = 0; + QWORD qwPTEPa = 0; + PTE pte; ZERO_MEMORY(&pte, sizeof(PTE)); + + // Determine if the target uses 64-bit pointers + if (!IsPtr64()) { + dprintf("[-] IA-32 target is not supported by this extension!\n"); + goto cleanup; + } + + // Check if paging mode == 4-level IA-32e + // Add a check for HLAT paging (IA32_VMX_PROCBASED_CTLS3[1] - Enable HLAT) + if (!check_4_level_paging_mode(hCurrentProcess, hCurrentThread, qwCurrentPc, dwProcessor, strArgs)) { + goto cleanup; + } + + // Convert string representation of the VA to an integer + // In 4-level IA-32e paging mode, linear address width == 48 + if (!StrToInt64ExA(strArgs, STIF_SUPPORT_HEX, (LONGLONG*)&virtualAddress.Value)) { + dprintf("[-] Invalid args!\n"); + goto cleanup; + } + dprintf("[*] VA=0x%I64X\n", virtualAddress.Value); + + // Convert string representation of the CR3 value to an integer + if (!StrToInt64ExA(StrStrA(strArgs, " "), STIF_SUPPORT_HEX, (LONGLONG*)&qwCR3)) { + dprintf("[-] Invalid args!\n"); + goto cleanup; + } + dprintf("[*] CR3=0x%I64X\n", qwCR3); + + // Every paging structure is 4096 bytes in size and comprises a number of individual entries + // In 4-level IA-32e paging mode, sizeof(paging structure entry) == 64 bits (8 bytes) + // In 4-level IA-32e paging mode, number of paging structure entries in each table == 512 (4096 / 8) + // Every valid paging structure entry contains a PFN (PA >> PAGE_SHIFT) which can be used to either reference another paging structure entry or to map a page + + // CR3 contains the physical address of the first paging structure that the processor will use for linear address translation + // In 4-level IA-32e paging mode, the first paging structure is the Page Map Level 4 (PML4) table + // In 4-level IA-32e paging mode, physical address width == MAXPHYADDR (given by CPUID.80000008H:EAX[7:0], MAXPHYADDR is at most 52) + // In Windows x64, MAXPHYADDR == 48 + // CR3[MAXPHYADDR–1:12] == Physical address of the 4-KByte aligned PML4 table used for linear-address translation + qwPML4BasePa = PFN_TO_PAGE(extract_bits(qwCR3, 12, 36), PAGE_SIZE_4KB); + dprintf("[+] PML4 table base PA=0x%I64X\n", qwPML4BasePa); + + // Get the PML4 index from the VA + // A PML4E is identified using a PML4 index == VA[47:39] (9 bits) + qwPML4Index = virtualAddress.PML4Index; + dprintf("[+] PML4 index: 0x%X (0n%d)\n", qwPML4Index, qwPML4Index); + + // Compute PML4E PA for the given VA + qwPML4EPa = qwPML4BasePa + (qwPML4Index * sizeof(PML4E)); + dprintf("[+] PML4E PA=0x%I64X\n", qwPML4EPa); + + // Read the selected PML4E for the given VA + // Each PML4E controls access to a 512 GB region of the linear-address space + ReadPhysical(qwPML4EPa, &pml4e.Value, sizeof(PML4E), &dwBytesRead); + if (dwBytesRead != sizeof(PML4E)) { + dprintf("[-] Error reading physical memory!\n"); + goto cleanup; + } + dprintf("[+] PML4E contents=0x%I64X\n", pml4e.Value); + + // Check for invalid PML4E (an entry that is used neither to reference another paging structure nor to map a page) + // There is no translation for a linear address whose translation would use such a paging structure entry + // Any access to such a VA would cause a Page Fault exception (#PF) + // In Windows x64, such a PxE is then interpreted by the Memory Manager as a software PTE of which there are various types + if (pml4e.P == 0 || pml4e.PS == 1 || pml4e.Reserved1 != 0) { + dprintf("[-] Invalid PML4E!\n"); + goto cleanup; + } + + // In 4-level IA-32e paging mode, the second paging structure is the Page Directory Pointer Table (PDPT) + // PML4E[MAXPHYADDR–1:12] == Physical address of 4-KByte aligned PDPT referenced by this entry + qwPDPTBasePa = PFN_TO_PAGE(pml4e.PFN, PAGE_SIZE_4KB); + dprintf("[+] PDPT base PA=0x%I64X\n", qwPDPTBasePa); + + // Get the PDPT index from the VA + // A PDPTE is identified using a PDPT index == VA[38:30] (9 bits) + qwPDPTIndex = virtualAddress.PDPTIndex; + dprintf("[+] PDPT index: 0x%X (0n%d)\n", qwPDPTIndex, qwPDPTIndex); + + // Compute PDPTE PA for the given VA + qwPDPTEPa = qwPDPTBasePa + (qwPDPTIndex * sizeof(PDPTE)); + dprintf("[+] PDPTE PA=0x%I64X\n", qwPDPTEPa); + + // Read the selected PDPTE for the given VA + // Each PDPTE controls access to a 1 GB region of the linear-address space + ReadPhysical(qwPDPTEPa, &pdpte.Value, sizeof(PDPTE), &dwBytesRead); + if (dwBytesRead != sizeof(PDPTE)) { + dprintf("[-] Error reading physical memory!\n"); + goto cleanup; + } + dprintf("[+] PDPTE contents=0x%I64X\n", pdpte.Value); + + // Check for invalid PDPTE (an entry that is used neither to reference another paging structure nor to map a page) + if (pdpte.P == 0 || pdpte.Reserved2 != 0) { + dprintf("[-] Invalid PDPTE!\n"); + goto cleanup; + } + + // Check Page Size (PS) bit of PDPTE + if (pdpte.PS == 1 && pdpte.Reserved1 == 0) { + // PDPTE maps a 1 GB page + // PDPTE[MAXPHYADDR–1:30] == Physical address of the 1-GByte page referenced by this entry + qwPhysicalPageBasePa = PFN_TO_PAGE(pdpte.PFN1, PAGE_SIZE_1GB); + dprintf("[+] 1 GB physical page base PA=0x%I64X\n", qwPhysicalPageBasePa); + + // Get the physical page offset from the VA + // 1 GB physical page offset == VA[29:0] (30 bits) + qwPhysicalPageOffset = virtualAddress.Offset1GB; + dprintf("[+] 1 GB physical page offset: 0x%X (0n%d)\n", qwPhysicalPageOffset, qwPhysicalPageOffset); + + // Data may be written to any linear address (subject to CPL, WP, and SMAP) with a translation for which the R/W flag (bit 1) is 1 in every paging structure entry controlling the translation + bReadableWritablePage = ((pml4e.RW) && (pdpte.RW)); + + // If the U/S flag (bit 2) is 0 in at least one of the paging structure entries controlling the translation of the linear address, the address is a supervisor-mode address + // Otherwise, the address is a user-mode address + bUserModePage = ((pml4e.US) && (pdpte.US)); + + // Instructions may be fetched from any linear address (subject to CPL, NXE, and SMEP) with a translation for which the XD flag (bit 63) is 0 in every paging structure entry controlling the translation + bNonExecutablePage = ((pml4e.XD) || (pdpte.XD)); + + // Whenever there is a write to a linear address, the processor sets the Dirty flag (bit 6) if it is not already set in the paging structure entry that maps the page + bDirtyPage = pdpte.D; + + // If the Global flag (bit 8) is 1 in the paging structure entry that maps the page, any TLB entry cached for the linear address using that paging structure entry is considered to be global (subject to PGE) + bGlobalPage = pdpte.G; + + bTranslation = TRUE; + + goto cleanup; + } + + // In 4-level IA-32e paging mode, the third paging structure is the Page Directory (PD) table + // PDPTE[MAXPHYADDR–1:12] == Physical address of 4-KByte aligned PD table referenced by this entry + qwPDBasePa = PFN_TO_PAGE(pdpte.PFN2, PAGE_SIZE_4KB); + dprintf("[+] PD table base PA=0x%I64X\n", qwPDBasePa); + + // Get the PD index from the VA + // A PDE is identified using a PD index == VA[29:21] (9 bits) + qwPDIndex = virtualAddress.PDIndex; + dprintf("[+] PD index: 0x%X (0n%d)\n", qwPDIndex, qwPDIndex); + + // Compute PDE PA for the given VA + qwPDEPa = qwPDBasePa + (qwPDIndex * sizeof(PDE)); + dprintf("[+] PDE PA=0x%I64X\n", qwPDEPa); + + // Read the selected PDE for the given VA + // Each PDE controls access to a 2 MB region of the linear-address space + ReadPhysical(qwPDEPa, &pde.Value, sizeof(PDE), &dwBytesRead); + if (dwBytesRead != sizeof(PDE)) { + dprintf("[-] Error reading physical memory!\n"); + goto cleanup; + } + dprintf("[+] PDE contents=0x%I64X\n", pde.Value); + + // Check for invalid PDE (an entry that is used neither to reference another paging structure nor to map a page) + if (pde.P == 0 || pde.Reserved2 != 0) { + dprintf("[-] Invalid PDE!\n"); + goto cleanup; + } + + // Check Page Size (PS) bit of PDE + if (pde.PS == 1 && pde.Reserved1 == 0) { + // PDE maps a 2 MB page + // PDE[MAXPHYADDR–1:21] == Physical address of the 2-MByte page referenced by this entry + qwPhysicalPageBasePa = PFN_TO_PAGE(pde.PFN1, PAGE_SIZE_2MB); + dprintf("[+] 2 MB physical page base PA=0x%I64X\n", qwPhysicalPageBasePa); + + // Get the physical page offset from the VA + // 2 MB physical page offset == VA[20:0] (21 bits) + qwPhysicalPageOffset = virtualAddress.Offset2MB; + dprintf("[+] 2 MB physical page offset: 0x%X (0n%d)\n", qwPhysicalPageOffset, qwPhysicalPageOffset); + + // Data may be written to any linear address (subject to CPL, WP, and SMAP) with a translation for which the R/W flag (bit 1) is 1 in every paging structure entry controlling the translation + bReadableWritablePage = ((pml4e.RW) && (pdpte.RW) && (pde.RW)); + + // If the U/S flag (bit 2) is 0 in at least one of the paging structure entries controlling the translation of the linear address, the address is a supervisor-mode address + // Otherwise, the address is a user-mode address + bUserModePage = ((pml4e.US) && (pdpte.US) && (pde.US)); + + // Instructions may be fetched from any linear address (subject to CPL, NXE, and SMEP) with a translation for which the XD flag (bit 63) is 0 in every paging structure entry controlling the translation + bNonExecutablePage = ((pml4e.XD) || (pdpte.XD) || (pde.XD)); + + // Whenever there is a write to a linear address, the processor sets the Dirty flag (bit 6) if it is not already set in the paging structure entry that maps the page + bDirtyPage = pde.D; + + // If the Global flag (bit 8) is 1 in the paging structure entry that maps the page, any TLB entry cached for the linear address using that paging structure entry is considered to be global (subject to PGE) + bGlobalPage = pde.G; + + bTranslation = TRUE; + + goto cleanup; + } + + // In 4-level IA-32e paging mode, the fourth paging structure is the Page Table (PT) + // PDE[MAXPHYADDR–1:12] == Physical address of 4-KByte aligned PT referenced by this entry + qwPTBasePa = PFN_TO_PAGE(pde.PFN2, PAGE_SIZE_4KB); + dprintf("[+] PT base PA=0x%I64X\n", qwPTBasePa); + + // Get the PT index from the VA + // A PTE is identified using a PT index == VA[20:12] (9 bits) + qwPTIndex = virtualAddress.PTIndex; + dprintf("[+] PT index: 0x%X (0n%d)\n", qwPTIndex, qwPTIndex); + + // Compute PTE PA for the given VA + qwPTEPa = qwPTBasePa + (qwPTIndex * sizeof(PTE)); + dprintf("[+] PTE PA=0x%I64X\n", qwPTEPa); + + // Read the selected PTE for the given VA + // Each PTE controls access to a 4 KB region of the linear-address space + ReadPhysical(qwPTEPa, &pte.Value, sizeof(PTE), &dwBytesRead); + if (dwBytesRead != sizeof(PTE)) { + dprintf("[-] Error reading physical memory!\n"); + goto cleanup; + } + dprintf("[+] PTE contents=0x%I64X\n", pte.Value); + + // Check for invalid PTE (an entry that is used neither to reference another paging structure nor to map a page) + if (pte.P == 0 || pte.Reserved1 != 0) { + dprintf("[-] Invalid PTE!\n"); + goto cleanup; + } + + // PTE maps a 4 KB page + // PTE[MAXPHYADDR–1:12] == Physical address of the 4-KByte page referenced by this entry + qwPhysicalPageBasePa = PFN_TO_PAGE(pte.PFN, PAGE_SIZE_4KB); + dprintf("[+] 4 KB physical page base PA=0x%I64X\n", qwPhysicalPageBasePa); + + // Get the physical page offset from the VA + // 4 KB physical page offset == VA[11:0] (12 bits) + qwPhysicalPageOffset = virtualAddress.Offset4KB; + dprintf("[+] 4 KB physical page offset: 0x%X (0n%d)\n", qwPhysicalPageOffset, qwPhysicalPageOffset); + + // Data may be written to any linear address (subject to CPL, WP, and SMAP) with a translation for which the R/W flag (bit 1) is 1 in every paging structure entry controlling the translation + bReadableWritablePage = ((pml4e.RW) && (pdpte.RW) && (pde.RW) && (pte.RW)); + + // If the U/S flag (bit 2) is 0 in at least one of the paging structure entries controlling the translation of the linear address, the address is a supervisor-mode address + // Otherwise, the address is a user-mode address + bUserModePage = ((pml4e.US) && (pdpte.US) && (pde.US) && (pte.US)); + + // Instructions may be fetched from any linear address (subject to CPL, NXE, and SMEP) with a translation for which the XD flag (bit 63) is 0 in every paging structure entry controlling the translation + bNonExecutablePage = ((pml4e.XD) || (pdpte.XD) || (pde.XD) || (pte.XD)); + + // Whenever there is a write to a linear address, the processor sets the Dirty flag (bit 6) if it is not already set in the paging structure entry that maps the page + bDirtyPage = pte.D; + + // If the Global flag (bit 8) is 1 in the paging structure entry that maps the page, any TLB entry cached for the linear address using that paging structure entry is considered to be global (subject to PGE) + bGlobalPage = pte.G; + + bTranslation = TRUE; + + // Cleanup +cleanup: + if (bTranslation) { + // Get mapped physical address (PA) + qwPhysicalAddress = qwPhysicalPageBasePa + qwPhysicalPageOffset; + dprintf("[+] PA=0x%I64X\n", qwPhysicalAddress); + + // Display the access rights for the linear address + // Data reads, data writes, and instruction fetches to/from the linear address depend on the access rights specified by the paging structure entries controlling the translation + if (bReadableWritablePage) { + dprintf("[+] Writable address.\n"); + } + else { + dprintf("[+] Read-only address.\n"); + } + + if (bUserModePage) { + dprintf("[+] User-mode address.\n"); + } + else { + dprintf("[+] Supervisor-mode address.\n"); + } + + if (bNonExecutablePage) { + dprintf("[+] Non-executable address.\n"); + } + else { + dprintf("[+] Executable address.\n"); + } + + dprintf("[+] Dirty: %d\n", bDirtyPage); + + dprintf("[+] Global: %d\n", bGlobalPage); + } + + return; +} + #pragma endregion \ No newline at end of file