mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-09 17:19:26 +00:00
1814 lines
50 KiB
C
1814 lines
50 KiB
C
/**
|
|
* @file MemoryMapper.c
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief This file shows the functions to map memory to reserved system ranges
|
|
* @details also some of the functions derived from hvpp
|
|
* - https://github.com/wbenny/hvpp
|
|
*
|
|
* @version 0.1
|
|
* @date 2020-05-3
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
/**
|
|
* @brief Get Index of VA on PMLx
|
|
*
|
|
* @param Level PMLx
|
|
* @param Va Virtual Address
|
|
* @return UINT64
|
|
*/
|
|
_Use_decl_annotations_
|
|
UINT64
|
|
MemoryMapperGetIndex(PAGING_LEVEL Level, UINT64 Va)
|
|
{
|
|
UINT64 Result = Va;
|
|
Result >>= 12 + Level * 9;
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief Get page offset
|
|
*
|
|
* @param Level PMLx
|
|
* @param Va Virtual Address
|
|
* @return UINT32
|
|
*/
|
|
_Use_decl_annotations_
|
|
UINT32
|
|
MemoryMapperGetOffset(PAGING_LEVEL Level, UINT64 Va)
|
|
{
|
|
UINT32 Result = (UINT32)MemoryMapperGetIndex(Level, Va);
|
|
Result &= (1 << 9) - 1; // 0x1ff
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets virtual address and returns its PTE of the virtual address
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param Level PMLx
|
|
* @return PVOID virtual address of PTE
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperGetPteVa(PVOID Va, PAGING_LEVEL Level)
|
|
{
|
|
CR3_TYPE Cr3;
|
|
|
|
//
|
|
// Read the current cr3
|
|
//
|
|
Cr3.Flags = CpuReadCr3();
|
|
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
return MemoryMapperGetPteVaWithoutSwitchingByCr3(Va, Level, Cr3);
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets virtual address and returns its PTE of the virtual address
|
|
* based on the specific cr3
|
|
* @details the TargetCr3 should be kernel cr3 as we will use it to translate kernel
|
|
* addresses so the kernel functions to translate addresses should be mapped; thus,
|
|
* don't pass a KPTI meltdown user cr3 to this function
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param Level PMLx
|
|
* @param TargetCr3 kernel cr3 of target process
|
|
* @return PVOID virtual address of PTE based on cr3
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperGetPteVaByCr3(PVOID Va, PAGING_LEVEL Level, CR3_TYPE TargetCr3)
|
|
{
|
|
PPAGE_ENTRY PageEntry = NULL;
|
|
CR3_TYPE CurrentProcessCr3 = {0};
|
|
|
|
//
|
|
// Switch to new process's memory layout
|
|
// It is because, we're not trying to change the cr3 multiple times
|
|
// so instead of using PhysicalAddressToVirtualAddressByCr3 we use
|
|
// PhysicalAddressToVirtualAddress, but keep in mind that cr3 should
|
|
// be a kernel cr3 (not KPTI user cr3) as the functions to translate
|
|
// physical address to virtual address is not mapped on the user cr3
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(TargetCr3);
|
|
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
PageEntry = MemoryMapperGetPteVaWithoutSwitchingByCr3(Va, Level, TargetCr3);
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
|
|
return PageEntry;
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets virtual address and returns its PTE of the virtual address
|
|
* based on the target virtual address
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param Level PMLx
|
|
*
|
|
* @return PVOID virtual address of PTE based on cr3
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperGetPteVaOnTargetProcess(PVOID Va, PAGING_LEVEL Level)
|
|
{
|
|
PPAGE_ENTRY PageEntry = NULL;
|
|
CR3_TYPE GuestCr3;
|
|
CR3_TYPE CurrentProcessCr3 = {0};
|
|
|
|
//
|
|
// Move to guest process as we're currently in system cr3
|
|
//
|
|
|
|
//
|
|
// Find the current process cr3
|
|
//
|
|
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
|
|
|
|
//
|
|
// Switch to new process's memory layout
|
|
// It is because, we're not trying to change the cr3 multiple times
|
|
// so instead of using PhysicalAddressToVirtualAddressByCr3 we use
|
|
// PhysicalAddressToVirtualAddress, but keep in mind that cr3 should
|
|
// be a kernel cr3 (not KPTI user cr3) as the functions to translate
|
|
// physical address to virtual address is not mapped on the user cr3
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(GuestCr3);
|
|
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
PageEntry = MemoryMapperGetPteVaWithoutSwitchingByCr3(Va, Level, GuestCr3);
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
|
|
return PageEntry;
|
|
}
|
|
|
|
/**
|
|
* @brief This function checks whether the virtual address is present in the RAM or not
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param Level PMLx
|
|
*
|
|
* @return BOOLEAN Is present or not
|
|
*/
|
|
BOOLEAN
|
|
MemoryMapperCheckPteIsPresentOnTargetProcess(PVOID Va, PAGING_LEVEL Level)
|
|
{
|
|
PPAGE_ENTRY PageEntry = NULL;
|
|
CR3_TYPE GuestCr3;
|
|
CR3_TYPE CurrentProcessCr3 = {0};
|
|
BOOLEAN Result = FALSE;
|
|
|
|
//
|
|
// Move to guest process as we're currently in system cr3
|
|
//
|
|
|
|
//
|
|
// Find the current process cr3
|
|
//
|
|
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
|
|
|
|
//
|
|
// Switch to new process's memory layout
|
|
// It is because, we're not trying to change the cr3 multiple times
|
|
// so instead of using PhysicalAddressToVirtualAddressByCr3 we use
|
|
// PhysicalAddressToVirtualAddress, but keep in mind that cr3 should
|
|
// be a kernel cr3 (not KPTI user cr3) as the functions to translate
|
|
// physical address to virtual address is not mapped on the user cr3
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(GuestCr3);
|
|
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
PageEntry = MemoryMapperGetPteVaWithoutSwitchingByCr3(Va, Level, GuestCr3);
|
|
|
|
if (PageEntry == NULL)
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if page is present or not
|
|
//
|
|
if (PageEntry->Fields.Present == TRUE)
|
|
{
|
|
//
|
|
// It's present
|
|
//
|
|
Result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's not present
|
|
//
|
|
Result = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets virtual address and returns its PTE of the virtual address
|
|
* based on the target virtual address
|
|
* @details the TargetCr3 should be kernel cr3 as we will use it to translate kernel
|
|
* addresses so the kernel functions to translate addresses should be mapped; thus,
|
|
* don't pass a KPTI meltdown user cr3 to this function
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param Set
|
|
*
|
|
* @return PVOID virtual address of PTE based on cr3
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperSetExecuteDisableToPteOnTargetProcess(PVOID Va, BOOLEAN Set)
|
|
{
|
|
PPAGE_ENTRY PageEntry = NULL;
|
|
CR3_TYPE GuestCr3;
|
|
CR3_TYPE CurrentProcessCr3 = {0};
|
|
|
|
//
|
|
// Move to guest process as we're currently in system cr3
|
|
//
|
|
|
|
//
|
|
// Find the current process cr3
|
|
//
|
|
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
|
|
|
|
//
|
|
// Switch to new process's memory layout
|
|
// It is because, we're not trying to change the cr3 multiple times
|
|
// so instead of using PhysicalAddressToVirtualAddressByCr3 we use
|
|
// PhysicalAddressToVirtualAddress, but keep in mind that cr3 should
|
|
// be a kernel cr3 (not KPTI user cr3) as the functions to translate
|
|
// physical address to virtual address is not mapped on the user cr3
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(GuestCr3);
|
|
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
PageEntry = MemoryMapperGetPteVaWithoutSwitchingByCr3(Va, PagingLevelPageTable, GuestCr3);
|
|
|
|
//
|
|
// Set execute disable bit
|
|
//
|
|
PageEntry->Fields.ExecuteDisable = Set;
|
|
|
|
//
|
|
// Invalidate the TLB
|
|
//
|
|
CpuInvlpg(Va);
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
|
|
return PageEntry;
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets virtual address and returns its PTE of the virtual address
|
|
* based on the specific cr3 but without switching to the target address
|
|
* @details the TargetCr3 should be kernel cr3 as we will use it to translate kernel
|
|
* addresses so the kernel functions to translate addresses should be mapped; thus,
|
|
* don't pass a KPTI meltdown user cr3 to this function
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param Level PMLx
|
|
* @param TargetCr3 kernel cr3 of target process
|
|
* @return PVOID virtual address of PTE based on cr3
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperGetPteVaWithoutSwitchingByCr3(PVOID Va, PAGING_LEVEL Level, CR3_TYPE TargetCr3)
|
|
{
|
|
CR3_TYPE Cr3;
|
|
UINT64 TempCr3;
|
|
PUINT64 Cr3Va;
|
|
PUINT64 PdptVa;
|
|
PUINT64 PdVa;
|
|
PUINT64 PtVa;
|
|
UINT32 Offset;
|
|
|
|
Cr3.Flags = TargetCr3.Flags;
|
|
|
|
//
|
|
// Cr3 should be shifted 12 to the left because it's PFN
|
|
//
|
|
TempCr3 = Cr3.Fields.PageFrameNumber << 12;
|
|
|
|
//
|
|
// we need VA of Cr3, not PA
|
|
//
|
|
Cr3Va = (UINT64 *)PhysicalAddressToVirtualAddress(TempCr3);
|
|
|
|
//
|
|
// Check for invalid address
|
|
//
|
|
if (Cr3Va == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Offset = MemoryMapperGetOffset(PagingLevelPageMapLevel4, (UINT64)Va);
|
|
|
|
PPAGE_ENTRY Pml4e = (PAGE_ENTRY *)&Cr3Va[Offset];
|
|
|
|
if (!Pml4e->Fields.Present || Level == PagingLevelPageMapLevel4)
|
|
{
|
|
return Pml4e;
|
|
}
|
|
|
|
PdptVa = (UINT64 *)PhysicalAddressToVirtualAddress(Pml4e->Fields.PageFrameNumber << 12);
|
|
|
|
//
|
|
// Check for invalid address
|
|
//
|
|
if (PdptVa == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Offset = MemoryMapperGetOffset(PagingLevelPageDirectoryPointerTable, (UINT64)Va);
|
|
|
|
PPAGE_ENTRY Pdpte = (PAGE_ENTRY *)&PdptVa[Offset];
|
|
|
|
if (!Pdpte->Fields.Present || Pdpte->Fields.LargePage || Level == PagingLevelPageDirectoryPointerTable)
|
|
{
|
|
return Pdpte;
|
|
}
|
|
|
|
PdVa = (UINT64 *)PhysicalAddressToVirtualAddress(Pdpte->Fields.PageFrameNumber << 12);
|
|
|
|
//
|
|
// Check for invalid address
|
|
//
|
|
if (PdVa == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Offset = MemoryMapperGetOffset(PagingLevelPageDirectory, (UINT64)Va);
|
|
|
|
PPAGE_ENTRY Pde = (PAGE_ENTRY *)&PdVa[Offset];
|
|
|
|
if (!Pde->Fields.Present || Pde->Fields.LargePage || Level == PagingLevelPageDirectory)
|
|
{
|
|
return Pde;
|
|
}
|
|
|
|
PtVa = (UINT64 *)PhysicalAddressToVirtualAddress(Pde->Fields.PageFrameNumber << 12);
|
|
|
|
//
|
|
// Check for invalid address
|
|
//
|
|
if (PtVa == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Offset = MemoryMapperGetOffset(PagingLevelPageTable, (UINT64)Va);
|
|
|
|
PPAGE_ENTRY Pt = (PAGE_ENTRY *)&PtVa[Offset];
|
|
|
|
return Pt;
|
|
}
|
|
|
|
/**
|
|
* @brief This function checks if the page is mapped or not
|
|
* @details this function checks for PRESENT Bit of the page table
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param TargetCr3 kernel cr3 of target process
|
|
* @return PPAGE_ENTRY virtual address of PTE based on cr3
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperCheckIfPageIsPresentByCr3(PVOID Va, CR3_TYPE TargetCr3)
|
|
{
|
|
PPAGE_ENTRY PageEntry;
|
|
|
|
//
|
|
// Find the page table entry
|
|
//
|
|
PageEntry = MemoryMapperGetPteVaByCr3(Va, PagingLevelPageTable, TargetCr3);
|
|
|
|
if (PageEntry != NULL && PageEntry->Fields.Present)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function checks if the page has NX bit or not
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param TargetCr3 kernel cr3 of target process
|
|
* @return PPAGE_ENTRY virtual address of PTE based on cr3
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperCheckIfPageIsNxBitSetByCr3(PVOID Va, CR3_TYPE TargetCr3)
|
|
{
|
|
PPAGE_ENTRY PageEntry;
|
|
|
|
//
|
|
// Find the page table entry
|
|
//
|
|
PageEntry = MemoryMapperGetPteVaByCr3(Va, PagingLevelPageTable, TargetCr3);
|
|
|
|
if (PageEntry != NULL && !PageEntry->Fields.ExecuteDisable)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief This function checks target process to see
|
|
* if the page has NX bit or not
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param TargetCr3 kernel cr3 of target process
|
|
* @return BOOLEAN
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperCheckIfPageIsNxBitSetOnTargetProcess(PVOID Va)
|
|
{
|
|
BOOLEAN Result;
|
|
CR3_TYPE GuestCr3;
|
|
PPAGE_ENTRY PageEntry;
|
|
CR3_TYPE CurrentProcessCr3 = {0};
|
|
|
|
//
|
|
// Move to guest process as we're currently in system cr3
|
|
//
|
|
|
|
//
|
|
// Find the current process cr3
|
|
//
|
|
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
|
|
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(GuestCr3);
|
|
|
|
//
|
|
// Find the page table entry
|
|
//
|
|
PageEntry = MemoryMapperGetPteVa(Va, PagingLevelPageTable);
|
|
|
|
if (PageEntry != NULL && !PageEntry->Fields.ExecuteDisable)
|
|
{
|
|
Result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief This function checks target process to see
|
|
* if the PDE is a large page or not
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param TargetCr3 kernel cr3 of target process
|
|
* @return BOOLEAN
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperCheckIfPdeIsLargePageOnTargetProcess(PVOID Va)
|
|
{
|
|
BOOLEAN Result;
|
|
CR3_TYPE GuestCr3;
|
|
PPAGE_ENTRY PageEntry;
|
|
CR3_TYPE CurrentProcessCr3 = {0};
|
|
|
|
//
|
|
// Move to guest process as we're currently in system cr3
|
|
//
|
|
|
|
//
|
|
// Find the current process cr3
|
|
//
|
|
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
|
|
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(GuestCr3);
|
|
|
|
//
|
|
// Find the page table entry (PDE)
|
|
//
|
|
PageEntry = MemoryMapperGetPteVa(Va, PagingLevelPageDirectory);
|
|
|
|
if (PageEntry != NULL && PageEntry->Fields.LargePage)
|
|
{
|
|
Result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Result = FALSE;
|
|
}
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief This function reserve memory from system range (without physically allocating them)
|
|
*
|
|
* @param Size Size of reserving buffers
|
|
* @return PVOID Return the VA of the page
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperMapReservedPageRange(SIZE_T Size)
|
|
{
|
|
//
|
|
// The MmAllocateMappingAddress routine reserves a range of
|
|
// system virtual address space of the specified size.
|
|
//
|
|
return MmAllocateMappingAddress(Size, POOLTAG);
|
|
}
|
|
|
|
/**
|
|
* @brief This function frees the memory that was previously allocated
|
|
* from system range (without physically allocating them)
|
|
*
|
|
* @param VirtualAddress Virtual Address
|
|
* @return VOID
|
|
*/
|
|
_Use_decl_annotations_
|
|
VOID
|
|
MemoryMapperUnmapReservedPageRange(PVOID VirtualAddress)
|
|
{
|
|
MmFreeMappingAddress(VirtualAddress, POOLTAG);
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets virtual address and returns its PTE (Pml4e) virtual address
|
|
*
|
|
* @param VirtualAddress Virtual Address
|
|
* @return virtual address of PTE (Pml4e)
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperGetPte(PVOID VirtualAddress)
|
|
{
|
|
return MemoryMapperGetPteVa(VirtualAddress, PagingLevelPageTable);
|
|
}
|
|
|
|
/**
|
|
* @brief This function gets virtual address and returns its PTE (Pml4e) virtual address
|
|
* based on a specific Cr3
|
|
*
|
|
* @param VirtualAddress Virtual Address
|
|
* @param TargetCr3 Target process cr3
|
|
* @return virtual address of PTE (Pml4e)
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperGetPteByCr3(PVOID VirtualAddress, CR3_TYPE TargetCr3)
|
|
{
|
|
return MemoryMapperGetPteVaByCr3(VirtualAddress, PagingLevelPageTable, TargetCr3);
|
|
}
|
|
|
|
/**
|
|
* @brief This function MAPs one resreved page (4096) and returns
|
|
* its virtual adrresss and also PTE virtual address in PteAddress
|
|
*
|
|
* @param PteAddress Address of Page Table Entry
|
|
* @return virtual address of mapped (not physically) address
|
|
*/
|
|
_Use_decl_annotations_
|
|
PVOID
|
|
MemoryMapperMapPageAndGetPte(PUINT64 PteAddress)
|
|
{
|
|
PVOID Va;
|
|
UINT64 Pte;
|
|
|
|
//
|
|
// Reserve the page from system va space
|
|
//
|
|
Va = MemoryMapperMapReservedPageRange(PAGE_SIZE);
|
|
|
|
//
|
|
// Get the page's Page Table Entry
|
|
//
|
|
Pte = (UINT64)MemoryMapperGetPte(Va);
|
|
|
|
*PteAddress = Pte;
|
|
|
|
return Va;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the Memory Mapper
|
|
* @details This function should be called in vmx non-root
|
|
* in a IRQL <= APC_LEVEL
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
MemoryMapperInitialize()
|
|
{
|
|
UINT64 TempPte;
|
|
ULONG ProcessorsCount;
|
|
|
|
ProcessorsCount = KeQueryActiveProcessorCount(0);
|
|
|
|
//
|
|
// *** Reserve the address for all cores (read pte and va) ***
|
|
//
|
|
|
|
if (g_MemoryMapper != NULL)
|
|
{
|
|
//
|
|
// It's already initialized
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate the memory buffer structure
|
|
//
|
|
g_MemoryMapper = PlatformMemAllocateZeroedNonPagedPool(sizeof(MEMORY_MAPPER_ADDRESSES) * ProcessorsCount);
|
|
|
|
//
|
|
// Set the core's id and initialize memory mapper
|
|
//
|
|
for (SIZE_T i = 0; i < ProcessorsCount; i++)
|
|
{
|
|
//
|
|
// *** Initialize memory mapper for each core ***
|
|
//
|
|
|
|
//
|
|
// Initial and reserve for read operations
|
|
//
|
|
g_MemoryMapper[i].VirualAddressForRead = (UINT64)MemoryMapperMapPageAndGetPte(&TempPte);
|
|
g_MemoryMapper[i].PteVirtualAddressForRead = TempPte;
|
|
|
|
//
|
|
// Initial and reserve for write operations
|
|
//
|
|
g_MemoryMapper[i].VirualAddressForWrite = (UINT64)MemoryMapperMapPageAndGetPte(&TempPte);
|
|
g_MemoryMapper[i].PteVirtualAddressForWrite = TempPte;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief uninitialize the Memory Mapper
|
|
* @details This function should be called in vmx non-root
|
|
* in a IRQL <= APC_LEVEL
|
|
*
|
|
* @return VOID
|
|
*/
|
|
VOID
|
|
MemoryMapperUninitialize()
|
|
{
|
|
ULONG ProcessorsCount = KeQueryActiveProcessorCount(0);
|
|
|
|
for (SIZE_T i = 0; i < ProcessorsCount; i++)
|
|
{
|
|
//
|
|
// Unmap and free the reserved buffer
|
|
//
|
|
if (g_MemoryMapper[i].VirualAddressForRead != NULL64_ZERO)
|
|
{
|
|
MemoryMapperUnmapReservedPageRange((PVOID)g_MemoryMapper[i].VirualAddressForRead);
|
|
}
|
|
|
|
if (g_MemoryMapper[i].VirualAddressForWrite != NULL64_ZERO)
|
|
{
|
|
MemoryMapperUnmapReservedPageRange((PVOID)g_MemoryMapper[i].VirualAddressForWrite);
|
|
}
|
|
|
|
g_MemoryMapper[i].VirualAddressForRead = NULL64_ZERO;
|
|
g_MemoryMapper[i].PteVirtualAddressForRead = NULL64_ZERO;
|
|
|
|
g_MemoryMapper[i].VirualAddressForWrite = NULL64_ZERO;
|
|
g_MemoryMapper[i].PteVirtualAddressForWrite = NULL64_ZERO;
|
|
}
|
|
|
|
//
|
|
// Set the g_MemoryMapper to null
|
|
//
|
|
g_MemoryMapper = NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Read memory safely by mapping the buffer using PTE
|
|
* @param PaAddressToRead Physical address to read
|
|
* @param BufferToSaveMemory buffer to save the memory
|
|
* @param SizeToRead Size
|
|
* @param PteVaAddress Virtual Address of PTE
|
|
* @param MappingVa Mapping virtual address
|
|
* @param InvalidateVpids whether invalidate based on VPIDs or not
|
|
*
|
|
* @return BOOLEAN returns TRUE if it was successful and FALSE if there was error
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperReadMemorySafeByPte(PHYSICAL_ADDRESS PaAddressToRead,
|
|
PVOID BufferToSaveMemory,
|
|
SIZE_T SizeToRead,
|
|
UINT64 PteVaAddress,
|
|
UINT64 MappingVa,
|
|
BOOLEAN InvalidateVpids)
|
|
{
|
|
PVOID NewAddress;
|
|
PAGE_ENTRY PageEntry;
|
|
PPAGE_ENTRY Pte = (PAGE_ENTRY *)PteVaAddress;
|
|
PVOID Va = (PVOID)MappingVa;
|
|
|
|
//
|
|
// Copy the previous entry into the new entry
|
|
//
|
|
PageEntry.Flags = Pte->Flags;
|
|
|
|
PageEntry.Fields.Present = 1;
|
|
|
|
//
|
|
// Generally we want each page to be writable
|
|
//
|
|
PageEntry.Fields.Write = 1;
|
|
|
|
//
|
|
// Do not flush this page from the TLB on CR3 switch, by setting the
|
|
// global bit in the PTE.
|
|
//
|
|
PageEntry.Fields.Global = 1;
|
|
|
|
//
|
|
// Set the PFN of this PTE to that of the provided physical address,
|
|
//
|
|
PageEntry.Fields.PageFrameNumber = PaAddressToRead.QuadPart >> 12;
|
|
|
|
//
|
|
// Apply the page entry in a single instruction
|
|
//
|
|
Pte->Flags = PageEntry.Flags;
|
|
|
|
//
|
|
// Finally, invalidate the caches for the virtual address
|
|
// It's not mandatory to invalidate the address in the VM nested-virtualization
|
|
// because it will be automatically invalidated by the top hypervisor, however,
|
|
// we should use invlpg in physical computers as it won't invalidate it automatically
|
|
//
|
|
CpuInvlpg(Va);
|
|
|
|
//
|
|
// Also invalidate it from vpids if we're in vmx root
|
|
//
|
|
if (InvalidateVpids)
|
|
{
|
|
// __invvpid_addr(VPID_TAG, Va);
|
|
}
|
|
|
|
//
|
|
// Compute the address
|
|
//
|
|
NewAddress = (PVOID)((UINT64)Va + (PAGE_4KB_OFFSET & (PaAddressToRead.QuadPart)));
|
|
|
|
//
|
|
// Move the address into the buffer in a safe manner
|
|
//
|
|
memcpy(BufferToSaveMemory, NewAddress, SizeToRead);
|
|
|
|
//
|
|
// Unmap Address
|
|
//
|
|
Pte->Flags = NULL64_ZERO;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Write memory safely by mapping the buffer using PTE
|
|
*
|
|
* @param SourceVA Source virtual address
|
|
* @param PaAddressToWrite Destination physical address
|
|
* @param SizeToWrite Size
|
|
* @param PteVaAddress PTE of target virtual address
|
|
* @param MappingVa Mapping Virtual Address
|
|
* @param InvalidateVpids Invalidate VPIDs or not
|
|
* @return BOOLEAN returns TRUE if it was successful and FALSE if there was error
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperWriteMemorySafeByPte(PVOID SourceVA,
|
|
PHYSICAL_ADDRESS PaAddressToWrite,
|
|
SIZE_T SizeToWrite,
|
|
UINT64 PteVaAddress,
|
|
UINT64 MappingVa,
|
|
BOOLEAN InvalidateVpids)
|
|
{
|
|
PVOID NewAddress;
|
|
PAGE_ENTRY PageEntry;
|
|
PPAGE_ENTRY Pte = (PAGE_ENTRY *)PteVaAddress;
|
|
PVOID Va = (PVOID)MappingVa;
|
|
|
|
//
|
|
// Copy the previous entry into the new entry
|
|
//
|
|
PageEntry.Flags = Pte->Flags;
|
|
|
|
PageEntry.Fields.Present = 1;
|
|
|
|
//
|
|
// Generally we want each page to be writable
|
|
//
|
|
PageEntry.Fields.Write = 1;
|
|
|
|
//
|
|
// Do not flush this page from the TLB on CR3 switch, by setting the
|
|
// global bit in the PTE.
|
|
//
|
|
PageEntry.Fields.Global = 1;
|
|
|
|
//
|
|
// Set the PFN of this PTE to that of the provided physical address.
|
|
//
|
|
PageEntry.Fields.PageFrameNumber = PaAddressToWrite.QuadPart >> 12;
|
|
|
|
//
|
|
// Apply the page entry in a single instruction
|
|
//
|
|
Pte->Flags = PageEntry.Flags;
|
|
|
|
//
|
|
// Finally, invalidate the caches for the virtual address.
|
|
//
|
|
CpuInvlpg(Va);
|
|
|
|
//
|
|
// Also invalidate it from vpids if we're in vmx root
|
|
//
|
|
if (InvalidateVpids)
|
|
{
|
|
// __invvpid_addr(VPID_TAG, Va);
|
|
}
|
|
|
|
//
|
|
// Compute the address
|
|
//
|
|
NewAddress = (PVOID)((UINT64)Va + (PAGE_4KB_OFFSET & (PaAddressToWrite.QuadPart)));
|
|
|
|
//
|
|
// Move the address into the buffer in a safe manner
|
|
//
|
|
memcpy(NewAddress, SourceVA, SizeToWrite);
|
|
|
|
//
|
|
// Unmap Address
|
|
//
|
|
Pte->Flags = NULL64_ZERO;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Wrapper to read the memory safely by mapping the
|
|
* buffer by physical address (It's a wrapper)
|
|
*
|
|
* @param TypeOfRead Type of read
|
|
* @param AddressToRead Physical Address to read
|
|
* @param TargetProcessId Target Process Id
|
|
*
|
|
* @return UINT64 returns the target physical address and NULL if it fails
|
|
*/
|
|
_Use_decl_annotations_
|
|
UINT64
|
|
MemoryMapperReadMemorySafeByPhysicalAddressWrapperAddressMaker(
|
|
MEMORY_MAPPER_WRAPPER_FOR_MEMORY_READ TypeOfRead,
|
|
UINT64 AddressToRead,
|
|
UINT32 TargetProcessId)
|
|
{
|
|
PHYSICAL_ADDRESS PhysicalAddress = {0};
|
|
|
|
switch (TypeOfRead)
|
|
{
|
|
case MEMORY_MAPPER_WRAPPER_READ_PHYSICAL_MEMORY:
|
|
|
|
PhysicalAddress.QuadPart = AddressToRead;
|
|
|
|
break;
|
|
|
|
case MEMORY_MAPPER_WRAPPER_READ_VIRTUAL_MEMORY:
|
|
|
|
PhysicalAddress.QuadPart = VirtualAddressToPhysicalAddress((PVOID)AddressToRead);
|
|
|
|
break;
|
|
|
|
case MEMORY_MAPPER_WRAPPER_READ_VIRTUAL_MEMORY_UNSAFE:
|
|
|
|
if (TargetProcessId == NULL_ZERO)
|
|
{
|
|
PhysicalAddress.QuadPart = VirtualAddressToPhysicalAddress((PVOID)AddressToRead);
|
|
}
|
|
else
|
|
{
|
|
PhysicalAddress.QuadPart = VirtualAddressToPhysicalAddressByProcessId((PVOID)AddressToRead, TargetProcessId);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NULL64_ZERO;
|
|
break;
|
|
}
|
|
|
|
return PhysicalAddress.QuadPart;
|
|
}
|
|
|
|
/**
|
|
* @brief Wrapper to read the memory safely by mapping the
|
|
* buffer by physical address (It's a wrapper)
|
|
*
|
|
* @param TypeOfRead Type of read
|
|
* @param AddressToRead Address to read
|
|
* @param BufferToSaveMemory Destination to save
|
|
* @param SizeToRead Size
|
|
* @param TargetProcessId The process pid
|
|
*
|
|
* @return BOOLEAN if it was successful the returns TRUE and if it was
|
|
* unsuccessful then it returns FALSE
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperReadMemorySafeWrapper(
|
|
MEMORY_MAPPER_WRAPPER_FOR_MEMORY_READ TypeOfRead,
|
|
UINT64 AddressToRead,
|
|
UINT64 BufferToSaveMemory,
|
|
SIZE_T SizeToRead,
|
|
UINT32 TargetProcessId)
|
|
{
|
|
ULONG CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
|
|
UINT64 AddressToCheck;
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
|
|
//
|
|
// Check to see if PTE and Reserved VA already initialized
|
|
//
|
|
if (g_MemoryMapper[CurrentCore].VirualAddressForRead == NULL64_ZERO ||
|
|
g_MemoryMapper[CurrentCore].PteVirtualAddressForRead == NULL64_ZERO)
|
|
{
|
|
//
|
|
// Not initialized
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check whether we should apply multiple accesses or not
|
|
//
|
|
AddressToCheck = (CHAR *)AddressToRead + SizeToRead - ((CHAR *)PAGE_ALIGN(AddressToRead));
|
|
|
|
if (AddressToCheck > PAGE_SIZE)
|
|
{
|
|
//
|
|
// Address should be accessed in more than one page
|
|
//
|
|
UINT64 ReadSize = AddressToCheck;
|
|
|
|
while (SizeToRead != 0)
|
|
{
|
|
ReadSize = (UINT64)PAGE_ALIGN(AddressToRead + PAGE_SIZE) - AddressToRead;
|
|
|
|
if (ReadSize == PAGE_SIZE && SizeToRead < PAGE_SIZE)
|
|
{
|
|
ReadSize = SizeToRead;
|
|
}
|
|
|
|
/*
|
|
LogInfo("Addr From : %llx to Addr To : %llx | ReadSize : %llx\n",
|
|
AddressToRead,
|
|
AddressToRead + ReadSize,
|
|
ReadSize);
|
|
*/
|
|
|
|
//
|
|
// One access is enough (page+size won't pass from the PAGE_ALIGN boundary)
|
|
//
|
|
PhysicalAddress.QuadPart = MemoryMapperReadMemorySafeByPhysicalAddressWrapperAddressMaker(TypeOfRead,
|
|
AddressToRead,
|
|
TargetProcessId);
|
|
|
|
if (!MemoryMapperReadMemorySafeByPte(
|
|
PhysicalAddress,
|
|
(PVOID)BufferToSaveMemory,
|
|
ReadSize,
|
|
g_MemoryMapper[CurrentCore].PteVirtualAddressForRead,
|
|
g_MemoryMapper[CurrentCore].VirualAddressForRead,
|
|
FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Apply the changes to the next addresses (if any)
|
|
//
|
|
SizeToRead = SizeToRead - ReadSize;
|
|
AddressToRead = AddressToRead + ReadSize;
|
|
BufferToSaveMemory = BufferToSaveMemory + ReadSize;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// One access is enough (page+size won't pass from the PAGE_ALIGN boundary)
|
|
//
|
|
PhysicalAddress.QuadPart = MemoryMapperReadMemorySafeByPhysicalAddressWrapperAddressMaker(TypeOfRead,
|
|
AddressToRead,
|
|
TargetProcessId);
|
|
|
|
return MemoryMapperReadMemorySafeByPte(
|
|
PhysicalAddress,
|
|
(PVOID)BufferToSaveMemory,
|
|
SizeToRead,
|
|
g_MemoryMapper[CurrentCore].PteVirtualAddressForRead,
|
|
g_MemoryMapper[CurrentCore].VirualAddressForRead,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Read memory safely by mapping the buffer by physical address (It's a wrapper)
|
|
*
|
|
* @param PaAddressToRead Physical Address to read
|
|
* @param BufferToSaveMemory Destination to save
|
|
* @param SizeToRead Size
|
|
* @return BOOLEAN if it was successful the returns TRUE and if it was
|
|
* unsuccessful then it returns FALSE
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperReadMemorySafeByPhysicalAddress(UINT64 PaAddressToRead,
|
|
UINT64 BufferToSaveMemory,
|
|
SIZE_T SizeToRead)
|
|
{
|
|
//
|
|
// Call the wrapper
|
|
//
|
|
return MemoryMapperReadMemorySafeWrapper(MEMORY_MAPPER_WRAPPER_READ_PHYSICAL_MEMORY,
|
|
PaAddressToRead,
|
|
BufferToSaveMemory,
|
|
SizeToRead,
|
|
NULL_ZERO);
|
|
}
|
|
|
|
/**
|
|
* @brief Read memory safely by mapping the buffer (It's a wrapper)
|
|
*
|
|
* @param VaAddressToRead Virtual Address to read
|
|
* @param BufferToSaveMemory Destination to save
|
|
* @param SizeToRead Size
|
|
* @return BOOLEAN if it was successful the returns TRUE and if it was
|
|
* unsuccessful then it returns FALSE
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperReadMemorySafe(UINT64 VaAddressToRead, PVOID BufferToSaveMemory, SIZE_T SizeToRead)
|
|
{
|
|
return MemoryMapperReadMemorySafeWrapper(MEMORY_MAPPER_WRAPPER_READ_VIRTUAL_MEMORY,
|
|
VaAddressToRead,
|
|
(UINT64)BufferToSaveMemory,
|
|
SizeToRead,
|
|
NULL_ZERO);
|
|
}
|
|
|
|
/**
|
|
* @brief Read memory unsafely by mapping the buffer (It's a wrapper)
|
|
*
|
|
* @param VaAddressToRead Virtual Address to read
|
|
* @param BufferToSaveMemory Destination to save
|
|
* @param SizeToRead Size
|
|
* @param TargetProcessId The process pid
|
|
*
|
|
* @return BOOLEAN if it was successful the returns TRUE and if it was
|
|
* unsuccessful then it returns FALSE
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperReadMemoryUnsafe(UINT64 VaAddressToRead, PVOID BufferToSaveMemory, SIZE_T SizeToRead, UINT32 TargetProcessId)
|
|
{
|
|
return MemoryMapperReadMemorySafeWrapper(MEMORY_MAPPER_WRAPPER_READ_VIRTUAL_MEMORY_UNSAFE,
|
|
VaAddressToRead,
|
|
(UINT64)BufferToSaveMemory,
|
|
SizeToRead,
|
|
TargetProcessId);
|
|
}
|
|
|
|
/**
|
|
* @brief Read memory safely by mapping the buffer on the target process memory (It's a wrapper)
|
|
*
|
|
* @param VaAddressToRead Virtual Address to read
|
|
* @param BufferToSaveMemory Destination to save
|
|
* @param SizeToRead Size
|
|
* @return BOOLEAN if it was successful the returns TRUE and if it was
|
|
* unsuccessful then it returns FALSE
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperReadMemorySafeOnTargetProcess(UINT64 VaAddressToRead, PVOID BufferToSaveMemory, SIZE_T SizeToRead)
|
|
{
|
|
CR3_TYPE GuestCr3;
|
|
CR3_TYPE OriginalCr3;
|
|
BOOLEAN Result;
|
|
|
|
//
|
|
// Move to guest process as we're currently in system cr3
|
|
//
|
|
|
|
//
|
|
// Find the current process cr3
|
|
//
|
|
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
|
|
|
|
//
|
|
// Move to new cr3
|
|
//
|
|
OriginalCr3.Flags = CpuReadCr3();
|
|
CpuWriteCr3(GuestCr3.Flags);
|
|
|
|
//
|
|
// Read target memory
|
|
//
|
|
Result = MemoryMapperReadMemorySafe(VaAddressToRead, BufferToSaveMemory, SizeToRead);
|
|
|
|
//
|
|
// Move back to original cr3
|
|
//
|
|
CpuWriteCr3(OriginalCr3.Flags);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief Write memory safely by mapping the buffer on the target process memory (It's a wrapper)
|
|
*
|
|
* @param Destination Virtual Address to write
|
|
* @param Source value to write
|
|
* @param Size Size
|
|
* @return BOOLEAN if it was successful the returns TRUE and if it was
|
|
* unsuccessful then it returns FALSE
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperWriteMemorySafeOnTargetProcess(UINT64 Destination, PVOID Source, SIZE_T Size)
|
|
{
|
|
CR3_TYPE GuestCr3;
|
|
CR3_TYPE OriginalCr3;
|
|
BOOLEAN Result;
|
|
|
|
//
|
|
// *** Move to guest process ***
|
|
//
|
|
|
|
//
|
|
// Find the current process cr3
|
|
//
|
|
GuestCr3.Flags = LayoutGetCurrentProcessCr3().Flags;
|
|
|
|
//
|
|
// Move to new cr3
|
|
//
|
|
OriginalCr3.Flags = CpuReadCr3();
|
|
CpuWriteCr3(GuestCr3.Flags);
|
|
|
|
//
|
|
// Write target memory
|
|
//
|
|
Result = MemoryMapperWriteMemorySafe(Destination, Source, Size, GuestCr3);
|
|
|
|
//
|
|
// Move back to original cr3
|
|
//
|
|
CpuWriteCr3(OriginalCr3.Flags);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief Decides about making the address and converting the address
|
|
* to physical address based on the passed parameters
|
|
*
|
|
* @param TypeOfWrite Type of memory write
|
|
* @param DestinationAddr Destination Address
|
|
* @param TargetProcessCr3 The process CR3 (might be null)
|
|
* @param TargetProcessId The process PID (might be null)
|
|
*
|
|
* @return UINT64 returns the target physical address and NULL if it fails
|
|
*/
|
|
_Use_decl_annotations_
|
|
UINT64
|
|
MemoryMapperWriteMemorySafeWrapperAddressMaker(MEMORY_MAPPER_WRAPPER_FOR_MEMORY_WRITE TypeOfWrite,
|
|
UINT64 DestinationAddr,
|
|
PCR3_TYPE TargetProcessCr3,
|
|
UINT32 TargetProcessId)
|
|
{
|
|
PHYSICAL_ADDRESS PhysicalAddress = {0};
|
|
|
|
switch (TypeOfWrite)
|
|
{
|
|
case MEMORY_MAPPER_WRAPPER_WRITE_PHYSICAL_MEMORY:
|
|
|
|
PhysicalAddress.QuadPart = DestinationAddr;
|
|
|
|
break;
|
|
|
|
case MEMORY_MAPPER_WRAPPER_WRITE_VIRTUAL_MEMORY_UNSAFE:
|
|
|
|
if (TargetProcessId == NULL_ZERO)
|
|
{
|
|
PhysicalAddress.QuadPart = VirtualAddressToPhysicalAddress((PVOID)DestinationAddr);
|
|
}
|
|
else
|
|
{
|
|
PhysicalAddress.QuadPart = VirtualAddressToPhysicalAddressByProcessId((PVOID)DestinationAddr, TargetProcessId);
|
|
}
|
|
|
|
break;
|
|
|
|
case MEMORY_MAPPER_WRAPPER_WRITE_VIRTUAL_MEMORY_SAFE:
|
|
|
|
if (TargetProcessCr3 == NULL || TargetProcessCr3->Flags == NULL64_ZERO)
|
|
{
|
|
PhysicalAddress.QuadPart = VirtualAddressToPhysicalAddress((PVOID)DestinationAddr);
|
|
}
|
|
else
|
|
{
|
|
PhysicalAddress.QuadPart = VirtualAddressToPhysicalAddressByProcessCr3((PVOID)DestinationAddr, *TargetProcessCr3);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return NULL64_ZERO;
|
|
|
|
break;
|
|
}
|
|
|
|
return PhysicalAddress.QuadPart;
|
|
}
|
|
|
|
/**
|
|
* @brief Write memory safely by mapping the buffer (It's a wrapper)
|
|
*
|
|
* @param TypeOfWrite Type of memory write
|
|
* @param DestinationAddr Destination Address
|
|
* @param Source Source Address
|
|
* @param SizeToWrite Size
|
|
* @param TargetProcessCr3 The process CR3 (might be null)
|
|
* @param TargetProcessId The process PID (might be null)
|
|
*
|
|
* @return BOOLEAN returns TRUE if it was successful and FALSE if there was error
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperWriteMemorySafeWrapper(MEMORY_MAPPER_WRAPPER_FOR_MEMORY_WRITE TypeOfWrite,
|
|
UINT64 DestinationAddr,
|
|
UINT64 Source,
|
|
SIZE_T SizeToWrite,
|
|
PCR3_TYPE TargetProcessCr3,
|
|
UINT32 TargetProcessId)
|
|
{
|
|
ULONG CurrentCore = KeGetCurrentProcessorNumberEx(NULL);
|
|
UINT64 AddressToCheck;
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
|
|
//
|
|
// Check to see if PTE and Reserved VA already initialized
|
|
//
|
|
if (g_MemoryMapper[CurrentCore].VirualAddressForWrite == NULL64_ZERO ||
|
|
g_MemoryMapper[CurrentCore].PteVirtualAddressForWrite == NULL64_ZERO)
|
|
{
|
|
//
|
|
// Not initialized
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check whether it needs multiple accesses to different pages or no
|
|
//
|
|
AddressToCheck = (CHAR *)DestinationAddr + SizeToWrite - ((CHAR *)PAGE_ALIGN(DestinationAddr));
|
|
|
|
if (AddressToCheck > PAGE_SIZE)
|
|
{
|
|
//
|
|
// It need multiple accesses to different pages to access the memory
|
|
//
|
|
|
|
UINT64 WriteSize = AddressToCheck;
|
|
|
|
while (SizeToWrite != 0)
|
|
{
|
|
WriteSize = (UINT64)PAGE_ALIGN(DestinationAddr + PAGE_SIZE) - DestinationAddr;
|
|
|
|
if (WriteSize == PAGE_SIZE && SizeToWrite < PAGE_SIZE)
|
|
{
|
|
WriteSize = SizeToWrite;
|
|
}
|
|
|
|
/*
|
|
LogInfo("Addr From : %llx to Addr To : %llx | WriteSize : %llx\n",
|
|
DestinationAddr,
|
|
DestinationAddr + WriteSize,
|
|
WriteSize);
|
|
*/
|
|
|
|
PhysicalAddress.QuadPart = MemoryMapperWriteMemorySafeWrapperAddressMaker(TypeOfWrite,
|
|
DestinationAddr,
|
|
TargetProcessCr3,
|
|
TargetProcessId);
|
|
|
|
if (!MemoryMapperWriteMemorySafeByPte(
|
|
(PVOID)Source,
|
|
PhysicalAddress,
|
|
WriteSize,
|
|
g_MemoryMapper[CurrentCore].PteVirtualAddressForWrite,
|
|
g_MemoryMapper[CurrentCore].VirualAddressForWrite,
|
|
FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SizeToWrite = SizeToWrite - WriteSize;
|
|
DestinationAddr = DestinationAddr + WriteSize;
|
|
Source = Source + WriteSize;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// One access is enough to write
|
|
//
|
|
PhysicalAddress.QuadPart = MemoryMapperWriteMemorySafeWrapperAddressMaker(TypeOfWrite,
|
|
DestinationAddr,
|
|
TargetProcessCr3,
|
|
TargetProcessId);
|
|
return MemoryMapperWriteMemorySafeByPte(
|
|
(PVOID)Source,
|
|
PhysicalAddress,
|
|
SizeToWrite,
|
|
g_MemoryMapper[CurrentCore].PteVirtualAddressForWrite,
|
|
g_MemoryMapper[CurrentCore].VirualAddressForWrite,
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Write memory by mapping the buffer (It's a wrapper)
|
|
*
|
|
* @details this function CAN be called from vmx-root mode
|
|
*
|
|
* @param Destination Destination Virtual Address
|
|
* @param Source Source Virtual Address
|
|
* @param SizeToWrite Size
|
|
* @param TargetProcessCr3 CR3 of target process
|
|
*
|
|
* @return BOOLEAN returns TRUE if it was successful and FALSE if there was error
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperWriteMemorySafe(UINT64 Destination,
|
|
PVOID Source,
|
|
SIZE_T SizeToWrite,
|
|
CR3_TYPE TargetProcessCr3)
|
|
{
|
|
return MemoryMapperWriteMemorySafeWrapper(MEMORY_MAPPER_WRAPPER_WRITE_VIRTUAL_MEMORY_SAFE,
|
|
Destination,
|
|
(UINT64)Source,
|
|
SizeToWrite,
|
|
&TargetProcessCr3,
|
|
NULL_ZERO);
|
|
}
|
|
|
|
/**
|
|
* @brief Write memory safely by mapping the buffer (It's a wrapper)
|
|
*
|
|
* @details this function should not be called from vmx-root mode
|
|
*
|
|
* @param Destination Destination Virtual Address
|
|
* @param Source Source Virtual Address
|
|
* @param SizeToWrite Size
|
|
* @param TargetProcessId Target Process Id
|
|
*
|
|
* @return BOOLEAN returns TRUE if it was successful and FALSE if there was error
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperWriteMemoryUnsafe(UINT64 Destination, PVOID Source, SIZE_T SizeToWrite, UINT32 TargetProcessId)
|
|
{
|
|
return MemoryMapperWriteMemorySafeWrapper(MEMORY_MAPPER_WRAPPER_WRITE_VIRTUAL_MEMORY_UNSAFE,
|
|
Destination,
|
|
(UINT64)Source,
|
|
SizeToWrite,
|
|
NULL,
|
|
TargetProcessId);
|
|
}
|
|
|
|
/**
|
|
* @brief Write memory safely by mapping the buffer
|
|
*
|
|
* @param DestinationPa Destination Physical Address
|
|
* @param Source Source Address
|
|
* @param SizeToWrite Size
|
|
*
|
|
* @return BOOLEAN returns TRUE if it was successful and FALSE if there was error
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperWriteMemorySafeByPhysicalAddress(UINT64 DestinationPa,
|
|
UINT64 Source,
|
|
SIZE_T SizeToWrite)
|
|
{
|
|
//
|
|
// Call the wrapper for safe memory read
|
|
//
|
|
return MemoryMapperWriteMemorySafeWrapper(MEMORY_MAPPER_WRAPPER_WRITE_PHYSICAL_MEMORY,
|
|
DestinationPa,
|
|
Source,
|
|
SizeToWrite,
|
|
NULL,
|
|
NULL_ZERO);
|
|
}
|
|
|
|
/**
|
|
* @brief Reserve user mode address (not allocated) in the target user mode application
|
|
* @details this function should be called from vmx non-root mode
|
|
*
|
|
* @param ProcessId Target Process Id
|
|
* @param Allocate Whether allocate or just reserve
|
|
* @return Reserved address in the target user mode application
|
|
*/
|
|
_Use_decl_annotations_
|
|
UINT64
|
|
MemoryMapperReserveUsermodeAddressOnTargetProcess(UINT32 ProcessId, BOOLEAN Allocate)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID AllocPtr = NULL;
|
|
SIZE_T AllocSize = PAGE_SIZE;
|
|
PEPROCESS SourceProcess;
|
|
KAPC_STATE State = {0};
|
|
|
|
if (PsGetCurrentProcessId() != (HANDLE)ProcessId)
|
|
{
|
|
//
|
|
// User needs another process memory
|
|
//
|
|
|
|
if (PsLookupProcessByProcessId((HANDLE)ProcessId, &SourceProcess) != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// if the process not found
|
|
//
|
|
return NULL64_ZERO;
|
|
}
|
|
__try
|
|
{
|
|
KeStackAttachProcess(SourceProcess, &State);
|
|
|
|
//
|
|
// Allocate (not allocate, just reserve or reserve and allocate) in memory in target process
|
|
//
|
|
Status = ZwAllocateVirtualMemory(
|
|
NtCurrentProcess(),
|
|
&AllocPtr,
|
|
(ULONG_PTR)NULL,
|
|
&AllocSize,
|
|
Allocate ? MEM_COMMIT : MEM_RESERVE,
|
|
PAGE_EXECUTE_READWRITE);
|
|
|
|
KeUnstackDetachProcess(&State);
|
|
|
|
ObDereferenceObject(SourceProcess);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
KeUnstackDetachProcess(&State);
|
|
|
|
ObDereferenceObject(SourceProcess);
|
|
return NULL64_ZERO;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate in memory in target process
|
|
//
|
|
Status = ZwAllocateVirtualMemory(
|
|
NtCurrentProcess(),
|
|
&AllocPtr,
|
|
(ULONG_PTR)NULL,
|
|
&AllocSize,
|
|
Allocate ? MEM_COMMIT : MEM_RESERVE,
|
|
PAGE_EXECUTE_READWRITE);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return NULL64_ZERO;
|
|
}
|
|
|
|
return (UINT64)AllocPtr;
|
|
}
|
|
|
|
/**
|
|
* @brief Deallocates a previously reserved user mode address in the target user mode application
|
|
* @details this function should be called from vmx non-root mode
|
|
*
|
|
* @param ProcessId Target Process Id
|
|
* @param BaseAddress Previously allocated base address
|
|
* @return BOOLEAN whether the operation was successful or not
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperFreeMemoryOnTargetProcess(UINT32 ProcessId,
|
|
PVOID BaseAddress)
|
|
{
|
|
NTSTATUS Status;
|
|
SIZE_T AllocSize = PAGE_SIZE;
|
|
PEPROCESS SourceProcess;
|
|
KAPC_STATE State = {0};
|
|
|
|
if (PsGetCurrentProcessId() != (HANDLE)ProcessId)
|
|
{
|
|
//
|
|
// User needs another process memory
|
|
//
|
|
|
|
if (PsLookupProcessByProcessId((HANDLE)ProcessId, &SourceProcess) != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// if the process not found
|
|
//
|
|
return FALSE;
|
|
}
|
|
__try
|
|
{
|
|
KeStackAttachProcess(SourceProcess, &State);
|
|
|
|
//
|
|
// Free memory in target process
|
|
//
|
|
Status = ZwFreeVirtualMemory(NtCurrentProcess(),
|
|
&BaseAddress,
|
|
&AllocSize,
|
|
MEM_RELEASE);
|
|
|
|
KeUnstackDetachProcess(&State);
|
|
|
|
ObDereferenceObject(SourceProcess);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
KeUnstackDetachProcess(&State);
|
|
|
|
ObDereferenceObject(SourceProcess);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Deallocate memory in target process
|
|
//
|
|
Status = ZwFreeVirtualMemory(NtCurrentProcess(),
|
|
&BaseAddress,
|
|
&AllocSize,
|
|
MEM_RELEASE);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Operation was successful
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Maps a physical address to a PTE
|
|
* @details Find the PTE from MemoryMapperGetPteVaByCr3
|
|
*
|
|
* @param PhysicalAddress Physical Address to be mapped
|
|
* @param TargetProcessVirtualAddress Virtual Address of target process
|
|
* @param TargetProcessKernelCr3 Target process cr3
|
|
*
|
|
* @return VOID
|
|
*/
|
|
_Use_decl_annotations_
|
|
VOID
|
|
MemoryMapperMapPhysicalAddressToPte(PHYSICAL_ADDRESS PhysicalAddress,
|
|
PVOID TargetProcessVirtualAddress,
|
|
CR3_TYPE TargetProcessKernelCr3)
|
|
{
|
|
PPAGE_ENTRY PreviousPteEntry;
|
|
PAGE_ENTRY PageEntry;
|
|
CR3_TYPE CurrentProcessCr3;
|
|
|
|
//
|
|
// Find the page table entry of the reserved page in the target
|
|
// process memory layout
|
|
//
|
|
PreviousPteEntry = MemoryMapperGetPteVaByCr3(TargetProcessVirtualAddress, PagingLevelPageTable, TargetProcessKernelCr3);
|
|
|
|
//
|
|
// Switch to new process's memory layout
|
|
//
|
|
CurrentProcessCr3 = SwitchToProcessMemoryLayoutByCr3(TargetProcessKernelCr3);
|
|
|
|
//
|
|
// Read the previous entry in order to modify it
|
|
//
|
|
PageEntry.Flags = PreviousPteEntry->Flags;
|
|
|
|
//
|
|
// Make sure that the target PTE is readable, writable, executable
|
|
// present, global, etc.
|
|
//
|
|
PageEntry.Fields.Present = 1;
|
|
|
|
//
|
|
// It's not a supervisor page
|
|
//
|
|
PageEntry.Fields.Supervisor = 1;
|
|
|
|
//
|
|
// Generally we want each page to be writable
|
|
//
|
|
PageEntry.Fields.Write = 1;
|
|
|
|
//
|
|
// Do not flush this page from the TLB on CR3 switch, by setting the
|
|
// global bit in the PTE.
|
|
//
|
|
PageEntry.Fields.Global = 1;
|
|
|
|
//
|
|
// Set the PFN of this PTE to that of the provided physical address.
|
|
//
|
|
PageEntry.Fields.PageFrameNumber = PhysicalAddress.QuadPart >> 12;
|
|
|
|
//
|
|
// Apply the page entry in a single instruction
|
|
//
|
|
PreviousPteEntry->Flags = PageEntry.Flags;
|
|
|
|
//
|
|
// Finally, invalidate the caches for the virtual address
|
|
// It's not mandatory to invalidate the address in the VM nested-virtualization
|
|
// because it will be automatically invalidated by the top hypervisor, however,
|
|
// we should use invlpg in physical computers as it won't invalidate it automatically
|
|
//
|
|
CpuInvlpg(TargetProcessVirtualAddress);
|
|
|
|
//
|
|
// Restore the original process
|
|
//
|
|
SwitchToPreviousProcess(CurrentProcessCr3);
|
|
}
|
|
|
|
/**
|
|
* @brief This function the Supervisor bit of the target PTE based on the specific cr3
|
|
*
|
|
* @param Va Virtual Address
|
|
* @param Set Set it to 1 or 0
|
|
* @param Level PMLx
|
|
* @param TargetCr3 kernel cr3 of target process
|
|
* @return BOOLEAN whether was successful or not
|
|
*/
|
|
_Use_decl_annotations_
|
|
BOOLEAN
|
|
MemoryMapperSetSupervisorBitWithoutSwitchingByCr3(PVOID Va, BOOLEAN Set, PAGING_LEVEL Level, CR3_TYPE TargetCr3)
|
|
{
|
|
PPAGE_ENTRY Pml = NULL;
|
|
|
|
Pml = MemoryMapperGetPteVaWithoutSwitchingByCr3(Va, Level, TargetCr3);
|
|
|
|
if (!Pml)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Change the supervisor bit
|
|
//
|
|
if (Set)
|
|
{
|
|
Pml->Fields.Supervisor = 1;
|
|
}
|
|
else
|
|
{
|
|
Pml->Fields.Supervisor = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Read physical memory safely from vmx non-root mode
|
|
*
|
|
* @param PaAddressToRead Physical Address to read
|
|
* @param BufferToSaveMemory Destination to save
|
|
* @param SizeToRead Size
|
|
*
|
|
* @return BOOLEAN whether was successful or not
|
|
*/
|
|
BOOLEAN
|
|
MemoryMapperReadMemorySafeFromVmxNonRootByPhysicalAddress(UINT64 PaAddressToRead,
|
|
PVOID BufferToSaveMemory,
|
|
SIZE_T SizeToRead)
|
|
{
|
|
if (AsmVmxVmcall(VMCALL_READ_PHYSICAL_MEMORY, (UINT64)PaAddressToRead, (UINT64)BufferToSaveMemory, (UINT64)SizeToRead) == STATUS_SUCCESS)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Write physical memory safely from vmx non-root mode
|
|
*
|
|
* @param DestinationVa Destination Virtual Address
|
|
* @param Source Source Address
|
|
* @param SizeToWrite Size
|
|
*
|
|
* @return BOOLEAN whether was successful or not
|
|
*/
|
|
BOOLEAN
|
|
MemoryMapperWriteMemorySafeFromVmxNonRootyPhysicalAddress(UINT64 DestinationPa,
|
|
PVOID Source,
|
|
SIZE_T SizeToWrite)
|
|
{
|
|
if (AsmVmxVmcall(VMCALL_WRITE_PHYSICAL_MEMORY, (UINT64)DestinationPa, (UINT64)Source, (UINT64)SizeToWrite) == STATUS_SUCCESS)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|