HyperDbg Debugger
Loading...
Searching...
No Matches
Ept.c File Reference

The implementation of functions relating to the Extended Page Table (a.k.a. EPT). More...

#include "pch.h"

Functions

BOOLEAN EptCheckFeatures (VOID)
 Check whether EPT features are present or not.
UINT8 EptGetMemoryType (SIZE_T PageFrameNumber, BOOLEAN IsLargePage)
 Check whether EPT features are present or not.
BOOLEAN EptBuildMtrrMap (VOID)
 Build MTRR Map of current physical addresses.
PEPT_PML1_ENTRY EptGetPml1Entry (PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress)
 Get the PML1 entry for this physical address if the page is split.
PVOID EptGetPml1OrPml2Entry (PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress, BOOLEAN *IsLargePage)
 Get the PML1 entry for this physical address if the large page is available then large page of Pml2 is returned.
PEPT_PML2_ENTRY EptGetPml2Entry (PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress)
 Get the PML2 entry for this physical address.
BOOLEAN EptSplitLargePage (PVMM_EPT_PAGE_TABLE EptPageTable, BOOLEAN UsePreAllocatedBuffer, SIZE_T PhysicalAddress)
 Convert large pages to 4KB pages.
BOOLEAN EptIsValidForLargePage (SIZE_T PageFrameNumber)
 Check if potential large page doesn't land on two or more different cache memory types.
BOOLEAN EptSetupPML2Entry (PVMM_EPT_PAGE_TABLE EptPageTable, PEPT_PML2_ENTRY NewEntry, SIZE_T PageFrameNumber)
 Set up PML2 Entries.
PVMM_EPT_PAGE_TABLE EptAllocateAndCreateIdentityPageTable (VOID)
 Allocates page maps and create identity page table.
BOOLEAN EptLogicalProcessorInitialize (VOID)
 Initialize EPT for an individual logical processor.
_Use_decl_annotations_ BOOLEAN EptHandlePageHookExit (VIRTUAL_MACHINE_STATE *VCpu, VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, UINT64 GuestPhysicalAddr)
 Check if this exit is due to a violation caused by a currently hooked page.
BOOLEAN EptHandleEptViolation (VIRTUAL_MACHINE_STATE *VCpu)
 Handle VM exits for EPT violations.
VOID EptHandleMisconfiguration (VOID)
 Handle vm-exits for EPT Misconfiguration.
_Use_decl_annotations_ VOID EptSetPML1AndInvalidateTLB (VIRTUAL_MACHINE_STATE *VCpu, PEPT_PML1_ENTRY EntryAddress, EPT_PML1_ENTRY EntryValue, INVEPT_TYPE InvalidationType)
 This function set the specific PML1 entry in a spinlock protected area then invalidate the TLB.
BOOLEAN EptCheckAndHandleEptHookBreakpoints (VIRTUAL_MACHINE_STATE *VCpu, UINT64 GuestRip)
 Perform checking and handling if the breakpoint vm-exit relates to EPT hook or not.
BOOLEAN EptCheckAndHandleBreakpoint (VIRTUAL_MACHINE_STATE *VCpu)
 Check if the breakpoint vm-exit relates to EPT hook or not.

Detailed Description

The implementation of functions relating to the Extended Page Table (a.k.a. EPT).

Author
Sina Karvandi (sina@.nosp@m.hype.nosp@m.rdbg..nosp@m.org)
Gbps
Matthijs Lavrijsen (matti.nosp@m.watt.nosp@m.i@gma.nosp@m.il.c.nosp@m.om)

Some of the codes are re-used from Gbps/gbhv (https://github.com/Gbps/gbhv)

Version
0.1
Date
2020-04-10

Function Documentation

◆ EptAllocateAndCreateIdentityPageTable()

PVMM_EPT_PAGE_TABLE EptAllocateAndCreateIdentityPageTable ( VOID )

Allocates page maps and create identity page table.

Returns
PVMM_EPT_PAGE_TABLE identity map page-table
688{
689 PVMM_EPT_PAGE_TABLE PageTable;
690 EPT_PML3_POINTER PML3Template;
691 EPT_PML3_ENTRY PML3TemplateLarge;
692 EPT_PML2_ENTRY PML2EntryTemplate;
693 SIZE_T EntryGroupIndex;
694 SIZE_T EntryIndex;
695
696 //
697 // Allocate all paging structures as 4KB aligned pages
698 //
699
700 //
701 // Allocate address anywhere in the OS's memory space and
702 // zero out all entries to ensure all unused entries are marked Not Present
703 //
705
706 if (PageTable == NULL)
707 {
708 LogError("Err, failed to allocate memory for PageTable");
709 return NULL;
710 }
711
712 //
713 // Keep track of dynamic 2MB->4KB split metadata for this EPT table.
714 //
715 InitializeListHead(&PageTable->DynamicSplitList);
716
717 //
718 // Create the template for the first entry in the PML4
719 //
720 PageTable->PML4[0].ReadAccess = 1;
721 PageTable->PML4[0].WriteAccess = 1;
722 PageTable->PML4[0].ExecuteAccess = 1;
723
724 //
725 // Copy the template into each of the 512 PML4 entry slots
726 //
727 CpuStosQ((SIZE_T *)&PageTable->PML4[1], PageTable->PML4[0].AsUInt, VMM_EPT_PML4E_COUNT - 1);
728
729 for (int i = 0; i < VMM_EPT_PML4E_COUNT; i++)
730 {
731 if (i == 0)
732 {
733 //
734 // Mark the first 512GB PML4 entry as present, which allows us to manage up
735 // to 512GB of discrete paging structures and also set other reserved bits
736 //
737 PageTable->PML4[0].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML3[0]) / PAGE_SIZE;
738 }
739 else
740 {
741 PageTable->PML4[i].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML3_RSVD[i - 1][0]) / PAGE_SIZE;
742 }
743 }
744
745 //
746 // Now mark each 1GB PML3 entry as RWX and map each to their PML2 entry
747 //
748
749 //
750 // Ensure stack memory is cleared
751 //
752 PML3Template.AsUInt = 0;
753 PML3TemplateLarge.AsUInt = 0;
754
755 //
756 // Set up one 'template' RWX PML3 entry and copy it into each of the 512 PML3 entries
757 // Using the same method as SimpleVisor for copying each entry using intrinsics
758 //
759 PML3Template.ReadAccess = 1;
760 PML3Template.WriteAccess = 1;
761 PML3Template.ExecuteAccess = 1;
762
763 PML3TemplateLarge.LargePage = 1;
764 PML3TemplateLarge.ReadAccess = 1;
765 PML3TemplateLarge.WriteAccess = 1;
766 PML3TemplateLarge.ExecuteAccess = 1;
767 PML3TemplateLarge.MemoryType = MEMORY_TYPE_UNCACHEABLE;
768
769 //
770 // Copy the template into each of the 512 PML3 entry slots for the original entries
771 //
772 CpuStosQ((SIZE_T *)&PageTable->PML3[0], PML3Template.AsUInt, VMM_EPT_PML3E_COUNT);
773
774 //
775 // Copt the template into each of the 512 PML3 entry slots for the reserved entries
776 //
777 for (SIZE_T i = 0; i < VMM_EPT_PML4E_COUNT - 1; i++)
778 {
779 CpuStosQ((SIZE_T *)&PageTable->PML3_RSVD[i][0], PML3TemplateLarge.AsUInt, VMM_EPT_PML3E_COUNT);
780 }
781
782 //
783 // For each of the 512 PML3 entries
784 //
785 for (EntryIndex = 0; EntryIndex < VMM_EPT_PML3E_COUNT; EntryIndex++)
786 {
787 //
788 // Map the 1GB PML3 entry to 512 PML2 (2MB) entries to describe each large page
789 // NOTE: We do *not* manage any PML1 (4096 byte) entries and do not allocate them
790 //
791 PageTable->PML3[EntryIndex].PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML2[EntryIndex][0]) / PAGE_SIZE;
792 }
793
794 //
795 // For each of the 512 PML3 reserved entries for reserved PML3 entries
796 //
797 for (SIZE_T i = 0; i < VMM_EPT_PML4E_COUNT - 1; i++)
798 {
799 for (SIZE_T j = 0; j < VMM_EPT_PML3E_COUNT; j++)
800 {
801 //
802 // Map the 1GB PML3 reserved entry to 512 PML3 (1GB) entries to describe each large page
803 // NOTE: We do *not* manage them since they are reserved for out of 512 GB MMIO ranges
804 // The first 512GB is used for the main system memory and the rest is reserved for MMIO
805 //
806 PageTable->PML3_RSVD[i][j].PageFrameNumber = (SIZE_512_GB + // First 512GB is used for system memory
807 (i * SIZE_512_GB) + (j * SIZE_1_GB)) >> // MMIO ranges
808 30; // Convert to page frame number
809 }
810 }
811
812 //
813 // Now we will set up the PML2 entries, which are 2MB large pages
814 //
815 PML2EntryTemplate.AsUInt = 0;
816
817 //
818 // All PML2 entries will be RWX and 'present'
819 //
820 PML2EntryTemplate.WriteAccess = 1;
821 PML2EntryTemplate.ReadAccess = 1;
822 PML2EntryTemplate.ExecuteAccess = 1;
823
824 //
825 // We are using 2MB large pages, so we must mark this 1 here
826 //
827 PML2EntryTemplate.LargePage = 1;
828
829 //
830 // For each collection of 512 PML2 entries (512 collections * 512 entries per collection),
831 // mark it RWX using the same template above.
832 // This marks the entries as "Present" regardless of if the actual system has memory at
833 // this region or not. We will cause a fault in our EPT handler if the guest access a page
834 // outside a usable range, despite the EPT frame being present here
835 //
836 CpuStosQ((SIZE_T *)&PageTable->PML2[0], PML2EntryTemplate.AsUInt, VMM_EPT_PML3E_COUNT * VMM_EPT_PML2E_COUNT);
837
838 //
839 // For each of the 512 collections of 512 2MB PML2 entries
840 //
841 for (EntryGroupIndex = 0; EntryGroupIndex < VMM_EPT_PML3E_COUNT; EntryGroupIndex++)
842 {
843 //
844 // For each 2MB PML2 entry in the collection
845 //
846 for (EntryIndex = 0; EntryIndex < VMM_EPT_PML2E_COUNT; EntryIndex++)
847 {
848 //
849 // Setup the memory type and frame number of the PML2 entry
850 //
851 EptSetupPML2Entry(PageTable, &PageTable->PML2[EntryGroupIndex][EntryIndex], (EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex);
852 }
853 }
854
855 return PageTable;
856}
BOOLEAN EptSetupPML2Entry(PVMM_EPT_PAGE_TABLE EptPageTable, PEPT_PML2_ENTRY NewEntry, SIZE_T PageFrameNumber)
Set up PML2 Entries.
Definition Ept.c:655
#define SIZE_512_GB
Integer 512GB.
Definition Ept.h:43
#define SIZE_1_GB
Integer 1GB.
Definition Ept.h:37
VOID CpuStosQ(UINT64 *Destination, UINT64 Value, SIZE_T Count)
Store UINT64 value to memory Count times.
Definition PlatformIntrinsics.c:463
PVOID PlatformMemAllocateContiguousZeroedMemory(SIZE_T NumberOfBytes)
... Backward Compatibility / Specific APIs ...
Definition PlatformMem.c:184
#define LogError(format,...)
Log in the case of error.
Definition HyperDbgHyperLogIntrinsics.h:113
IMPORT_EXPORT_VMM UINT64 VirtualAddressToPhysicalAddress(_In_ PVOID VirtualAddress)
Converts Virtual Address to Physical Address.
Definition Conversion.c:154
struct _VMM_EPT_PAGE_TABLE VMM_EPT_PAGE_TABLE
Structure for saving EPT Table.
#define VMM_EPT_PML4E_COUNT
The number of 512GB PML4 entries in the page table.
Definition State.h:79
#define VMM_EPT_PML3E_COUNT
The number of 1GB PDPT entries in the page table per 512GB PML4 entry.
Definition State.h:85
#define VMM_EPT_PML2E_COUNT
Then number of 2MB Page Directory entries in the page table per 1GB PML3 entry.
Definition State.h:92
struct _VMM_EPT_PAGE_TABLE * PVMM_EPT_PAGE_TABLE
EPT_PDPTE EPT_PML3_POINTER
Definition State.h:19
EPT_PDPTE_1GB EPT_PML3_ENTRY
Definition State.h:20
EPT_PDE_2MB EPT_PML2_ENTRY
Definition State.h:21
#define PAGE_SIZE
Size of each page (4096 bytes).
Definition common.h:80
NULL()
Definition test-case-generator.py:530
EPT_PML4_POINTER PML4[VMM_EPT_PML4E_COUNT]
28.2.2 Describes 512 contiguous 512GB memory regions each with 512 1GB regions.
Definition State.h:111
EPT_PML3_POINTER PML3[VMM_EPT_PML3E_COUNT]
Describes exactly 512 contiguous 1GB memory regions within a our singular 512GB PML4 region.
Definition State.h:125
LIST_ENTRY DynamicSplitList
Tracks dynamic 2MB->4KB splits for this EPT table. NOTE: Each item stores a direct VA to the split PM...
Definition State.h:140
EPT_PML2_ENTRY PML2[VMM_EPT_PML3E_COUNT][VMM_EPT_PML2E_COUNT]
For each 1GB PML3 entry, create 512 2MB entries to map identity. NOTE: We are using 2MB pages as the ...
Definition State.h:133
EPT_PML3_ENTRY PML3_RSVD[VMM_EPT_PML4E_COUNT - 1][VMM_EPT_PML3E_COUNT]
Describes exactly 512 contiguous 1GB memory regions within a our singular 512GB PML4 region (This ent...
Definition State.h:119

◆ EptBuildMtrrMap()

BOOLEAN EptBuildMtrrMap ( VOID )

Build MTRR Map of current physical addresses.

Build MTRR Map.

Returns
BOOLEAN

Build MTRR Map of current physical addresses.

Returns
BOOLEAN
168{
169 IA32_MTRR_CAPABILITIES_REGISTER MTRRCap;
170 IA32_MTRR_PHYSBASE_REGISTER CurrentPhysBase;
171 IA32_MTRR_PHYSMASK_REGISTER CurrentPhysMask;
172 IA32_MTRR_DEF_TYPE_REGISTER MTRRDefType;
173 PMTRR_RANGE_DESCRIPTOR Descriptor;
174 UINT32 CurrentRegister;
175 UINT32 NumberOfBitsInMask;
176
177 MTRRCap.AsUInt = CpuReadMsr(IA32_MTRR_CAPABILITIES);
178 MTRRDefType.AsUInt = CpuReadMsr(IA32_MTRR_DEF_TYPE);
179
180 //
181 // All MTRRs are disabled when clear, and the
182 // UC memory type is applied to all of physical memory.
183 //
184 if (!MTRRDefType.MtrrEnable)
185 {
186 g_EptState->DefaultMemoryType = MEMORY_TYPE_UNCACHEABLE;
187 return TRUE;
188 }
189
190 //
191 // The IA32_MTRR_DEF_TYPE MSR (named MTRRdefType MSR for the P6 family processors) sets the default
192 // properties of the regions of physical memory that are not encompassed by MTRRs
193 //
194 g_EptState->DefaultMemoryType = (UINT8)MTRRDefType.DefaultMemoryType;
195
196 //
197 // The fixed memory ranges are mapped with 11 fixed-range registers of 64 bits each. Each of these registers is
198 // divided into 8-bit fields that are used to specify the memory type for each of the sub-ranges the register controls:
199 // - Register IA32_MTRR_FIX64K_00000 - Maps the 512-KByte address range from 0H to 7FFFFH. This range
200 // is divided into eight 64-KByte sub-ranges.
201 //
202 // - Registers IA32_MTRR_FIX16K_80000 and IA32_MTRR_FIX16K_A0000 - Maps the two 128-KByte
203 // address ranges from 80000H to BFFFFH. This range is divided into sixteen 16-KByte sub-ranges, 8 ranges per
204 // register.
205 //
206 // - Registers IA32_MTRR_FIX4K_C0000 through IA32_MTRR_FIX4K_F8000 - Maps eight 32-KByte
207 // address ranges from C0000H to FFFFFH. This range is divided into sixty-four 4-KByte sub-ranges, 8 ranges per
208 // register.
209 //
210 if (MTRRCap.FixedRangeSupported && MTRRDefType.FixedRangeMtrrEnable)
211 {
212 const UINT32 K64Base = 0x0;
213 const UINT32 K64Size = 0x10000;
214 IA32_MTRR_FIXED_RANGE_TYPE K64Types = {CpuReadMsr(IA32_MTRR_FIX64K_00000)};
215 for (UINT32 i = 0; i < 8; i++)
216 {
217 Descriptor = &g_EptState->MemoryRanges[g_EptState->NumberOfEnabledMemoryRanges++];
218 Descriptor->MemoryType = K64Types.s.Types[i];
219 Descriptor->PhysicalBaseAddress = K64Base + (K64Size * i);
220 Descriptor->PhysicalEndAddress = K64Base + (K64Size * i) + (K64Size - 1);
221 Descriptor->FixedRange = TRUE;
222 }
223
224 const UINT32 K16Base = 0x80000;
225 const UINT32 K16Size = 0x4000;
226 for (UINT32 i = 0; i < 2; i++)
227 {
228 IA32_MTRR_FIXED_RANGE_TYPE K16Types = {CpuReadMsr(IA32_MTRR_FIX16K_80000 + i)};
229 for (UINT32 j = 0; j < 8; j++)
230 {
231 Descriptor = &g_EptState->MemoryRanges[g_EptState->NumberOfEnabledMemoryRanges++];
232 Descriptor->MemoryType = K16Types.s.Types[j];
233 Descriptor->PhysicalBaseAddress = (K16Base + (i * K16Size * 8)) + (K16Size * j);
234 Descriptor->PhysicalEndAddress = (K16Base + (i * K16Size * 8)) + (K16Size * j) + (K16Size - 1);
235 Descriptor->FixedRange = TRUE;
236 }
237 }
238
239 const UINT32 K4Base = 0xC0000;
240 const UINT32 K4Size = 0x1000;
241 for (UINT32 i = 0; i < 8; i++)
242 {
243 IA32_MTRR_FIXED_RANGE_TYPE K4Types = {CpuReadMsr(IA32_MTRR_FIX4K_C0000 + i)};
244
245 for (UINT32 j = 0; j < 8; j++)
246 {
247 Descriptor = &g_EptState->MemoryRanges[g_EptState->NumberOfEnabledMemoryRanges++];
248 Descriptor->MemoryType = K4Types.s.Types[j];
249 Descriptor->PhysicalBaseAddress = (K4Base + (i * K4Size * 8)) + (K4Size * j);
250 Descriptor->PhysicalEndAddress = (K4Base + (i * K4Size * 8)) + (K4Size * j) + (K4Size - 1);
251 Descriptor->FixedRange = TRUE;
252 }
253 }
254 }
255
256 for (CurrentRegister = 0; CurrentRegister < MTRRCap.VariableRangeCount; CurrentRegister++)
257 {
258 //
259 // For each dynamic register pair
260 //
261 CurrentPhysBase.AsUInt = CpuReadMsr(IA32_MTRR_PHYSBASE0 + (CurrentRegister * 2));
262 CurrentPhysMask.AsUInt = CpuReadMsr(IA32_MTRR_PHYSMASK0 + (CurrentRegister * 2));
263
264 //
265 // Is the range enabled?
266 //
267 if (CurrentPhysMask.Valid)
268 {
269 //
270 // We only need to read these once because the ISA dictates that MTRRs are
271 // to be synchronized between all processors during BIOS initialization.
272 //
273 Descriptor = &g_EptState->MemoryRanges[g_EptState->NumberOfEnabledMemoryRanges++];
274
275 //
276 // Calculate the base address in bytes
277 //
278 Descriptor->PhysicalBaseAddress = CurrentPhysBase.PageFrameNumber * PAGE_SIZE;
279
280 //
281 // Calculate the total size of the range
282 // The lowest bit of the mask that is set to 1 specifies the size of the range
283 //
284 CpuBitScanForward64((ULONG *)&NumberOfBitsInMask, CurrentPhysMask.PageFrameNumber * PAGE_SIZE);
285
286 //
287 // Size of the range in bytes + Base Address
288 //
289 Descriptor->PhysicalEndAddress = Descriptor->PhysicalBaseAddress + ((1ULL << NumberOfBitsInMask) - 1ULL);
290
291 //
292 // Memory Type (cacheability attributes)
293 //
294 Descriptor->MemoryType = (UCHAR)CurrentPhysBase.Type;
295
296 Descriptor->FixedRange = FALSE;
297
298 LogDebugInfo("MTRR Range: Base=0x%llx End=0x%llx Type=0x%x", Descriptor->PhysicalBaseAddress, Descriptor->PhysicalEndAddress, Descriptor->MemoryType);
299 }
300 }
301
302 LogDebugInfo("Total MTRR ranges committed: 0x%x", g_EptState->NumberOfEnabledMemoryRanges);
303
304 return TRUE;
305}
struct _MTRR_RANGE_DESCRIPTOR * PMTRR_RANGE_DESCRIPTOR
union _IA32_MTRR_FIXED_RANGE_TYPE IA32_MTRR_FIXED_RANGE_TYPE
Fixed range MTRR.
UCHAR CpuBitScanForward64(ULONG *Index, UINT64 Mask)
Bit scan forward (64-bit).
Definition PlatformIntrinsics.c:486
UINT64 CpuReadMsr(ULONG MsrAddress)
Read an MSR.
Definition PlatformIntrinsics.c:213
unsigned char UCHAR
Definition BasicTypes.h:34
#define TRUE
Definition BasicTypes.h:114
#define FALSE
Definition BasicTypes.h:113
unsigned char UINT8
Definition BasicTypes.h:52
unsigned int UINT32
Definition BasicTypes.h:54
unsigned long ULONG
Definition BasicTypes.h:31
#define LogDebugInfo(format,...)
Log, initialize boot information and debug information.
Definition HyperDbgHyperLogIntrinsics.h:155
EPT_STATE * g_EptState
Save the state and variables related to EPT.
Definition GlobalVariables.h:50
UCHAR MemoryType
Definition Ept.h:87
BOOLEAN FixedRange
Definition Ept.h:88
SIZE_T PhysicalBaseAddress
Definition Ept.h:85
SIZE_T PhysicalEndAddress
Definition Ept.h:86
UINT8 Types[8]
Definition Ept.h:100
struct _IA32_MTRR_FIXED_RANGE_TYPE::@170207037044173377154145111113263372336161202124 s

◆ EptCheckAndHandleBreakpoint()

BOOLEAN EptCheckAndHandleBreakpoint ( VIRTUAL_MACHINE_STATE * VCpu)

Check if the breakpoint vm-exit relates to EPT hook or not.

Parameters
VCpuThe virtual processor's state
Returns
BOOLEAN
1318{
1319 UINT64 GuestRip = 0;
1320 BOOLEAN IsHandledByEptHook;
1321
1322 //
1323 // Reading guest's RIP
1324 //
1325 VmxVmread64P(VMCS_GUEST_RIP, &GuestRip);
1326
1327 //
1328 // Don't increment rip by default
1329 //
1331
1332 //
1333 // Check if it relates to !epthook or not
1334 //
1335 IsHandledByEptHook = EptCheckAndHandleEptHookBreakpoints(VCpu, GuestRip);
1336
1337 return IsHandledByEptHook;
1338}
BOOLEAN EptCheckAndHandleEptHookBreakpoints(VIRTUAL_MACHINE_STATE *VCpu, UINT64 GuestRip)
Perform checking and handling if the breakpoint vm-exit relates to EPT hook or not.
Definition Ept.c:1217
VOID HvSuppressRipIncrement(VIRTUAL_MACHINE_STATE *VCpu)
Suppress the incrementation of RIP.
Definition Hv.c:320
UCHAR VmxVmread64P(size_t Field, UINT64 *FieldValue)
VMX VMREAD instruction (64-bit, pointer variant).
Definition PlatformIntrinsicsVmx.c:207
UCHAR BOOLEAN
Definition BasicTypes.h:35

◆ EptCheckAndHandleEptHookBreakpoints()

BOOLEAN EptCheckAndHandleEptHookBreakpoints ( VIRTUAL_MACHINE_STATE * VCpu,
UINT64 GuestRip )

Perform checking and handling if the breakpoint vm-exit relates to EPT hook or not.

Parameters
VCpuThe virtual processor's state
GuestRip
Returns
BOOLEAN
1218{
1219 PVOID TargetPage;
1220 PLIST_ENTRY TempList;
1221 BOOLEAN IsHandledByEptHook = FALSE;
1222
1223 //
1224 // ***** Check breakpoint for !epthook *****
1225 //
1226
1227 //
1228 // Check whether the breakpoint was due to a !epthook command or not
1229 //
1230 TempList = &g_EptState->HookedPagesList;
1231
1232 while (&g_EptState->HookedPagesList != TempList->Flink)
1233 {
1234 TempList = TempList->Flink;
1235 PEPT_HOOKED_PAGE_DETAIL HookedEntry = CONTAINING_RECORD(TempList, EPT_HOOKED_PAGE_DETAIL, PageHookList);
1236
1237 if (HookedEntry->IsExecutionHook)
1238 {
1239 for (SIZE_T i = 0; i < HookedEntry->CountOfBreakpoints; i++)
1240 {
1241 if (HookedEntry->BreakpointAddresses[i] == GuestRip)
1242 {
1243 //
1244 // We found an address that matches the details, let's trigger the event
1245 //
1246
1247 //
1248 // As the context to event trigger, we send the rip
1249 // of where triggered this event
1250 //
1251 DispatchEventHiddenHookExecCc(VCpu, (PVOID)GuestRip);
1252
1253 //
1254 // Pointer to the page entry in the page table
1255 //
1256 TargetPage = EptGetPml1Entry(VCpu->EptPageTable, HookedEntry->PhysicalBaseAddress);
1257
1258 //
1259 // Restore to its original entry for one instruction
1260 //
1262 TargetPage,
1263 HookedEntry->OriginalEntry,
1264 InveptSingleContext);
1265
1266 //
1267 // Next we have to save the current hooked entry to restore on the next instruction's vm-exit
1268 //
1269 VCpu->MtfEptHookRestorePoint = HookedEntry;
1270
1271 //
1272 // The following codes are added because we realized if the execution takes long then
1273 // the execution might be switched to another routines, thus, MTF might conclude on
1274 // another routine and we might (and will) trigger the same instruction soon
1275 //
1276 // The following code is not necessary on local debugging (VMI Mode), however, I don't
1277 // know why? just things are not reasonable here for me
1278 // another weird thing that I observed is the fact if you don't touch the routine related
1279 // to the I/O in and out instructions in VMWare then it works perfectly, just touching I/O
1280 // for serial is problematic, it might be a VMWare nested-virtualization bug, however, the
1281 // below approached proved to be work on both Debug Mode and WMI Mode
1282 // If you remove the below codes then when epthook is triggered then the execution stucks
1283 // on the same instruction on where the hooks is triggered, so 'p' and 't' commands for
1284 // steppings won't work
1285 //
1286
1287 //
1288 // We have to set Monitor trap flag and give it the HookedEntry to work with
1289 //
1291
1292 //
1293 // Indicate that we handled the ept violation
1294 //
1295 IsHandledByEptHook = TRUE;
1296
1297 //
1298 // Get out of the loop
1299 //
1300 break;
1301 }
1302 }
1303 }
1304 }
1305
1306 return IsHandledByEptHook;
1307}
VOID DispatchEventHiddenHookExecCc(VIRTUAL_MACHINE_STATE *VCpu, PVOID Context)
Handling debugger functions related to hidden hook exec CC events.
Definition Dispatch.c:1031
PEPT_PML1_ENTRY EptGetPml1Entry(PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress)
Get the PML1 entry for this physical address if the page is split.
Definition Ept.c:337
_Use_decl_annotations_ VOID EptSetPML1AndInvalidateTLB(VIRTUAL_MACHINE_STATE *VCpu, PEPT_PML1_ENTRY EntryAddress, EPT_PML1_ENTRY EntryValue, INVEPT_TYPE InvalidationType)
This function set the specific PML1 entry in a spinlock protected area then invalidate the TLB.
Definition Ept.c:1181
VOID HvEnableMtfAndChangeExternalInterruptState(VIRTUAL_MACHINE_STATE *VCpu)
Enables MTF and adjust external interrupt state.
Definition Hv.c:1438
void * PVOID
Definition BasicTypes.h:56
struct _EPT_HOOKED_PAGE_DETAIL EPT_HOOKED_PAGE_DETAIL
Structure to save the state of each hooked pages.
struct _EPT_HOOKED_PAGE_DETAIL * PEPT_HOOKED_PAGE_DETAIL
#define CONTAINING_RECORD(address, type, field)
Definition nt-list.h:36
BOOLEAN IsExecutionHook
This field shows whether the hook contains a hidden hook for execution or not.
Definition State.h:245
UINT64 BreakpointAddresses[MaximumHiddenBreakpointsOnPage]
Address of hooked pages (multiple breakpoints on a single page) this is only used in hidden breakpoin...
Definition State.h:280
EPT_PML1_ENTRY OriginalEntry
The original page entry. Will be copied back when the hook is removed from the page.
Definition State.h:230
SIZE_T PhysicalBaseAddress
The base address of the page. Used to find this structure in the list of page hooks when a hook is hi...
Definition State.h:203
UINT64 CountOfBreakpoints
Count of breakpoints (multiple breakpoints on a single page) this is only used in hidden breakpoints ...
Definition State.h:292
PVMM_EPT_PAGE_TABLE EptPageTable
Definition State.h:364
PEPT_HOOKED_PAGE_DETAIL MtfEptHookRestorePoint
Definition State.h:353

◆ EptCheckFeatures()

BOOLEAN EptCheckFeatures ( VOID )

Check whether EPT features are present or not.

Check for EPT Features.

Returns
BOOLEAN Shows whether EPT is supported in this machine or not

Check whether EPT features are present or not.

Returns
BOOLEAN
23{
24 IA32_VMX_EPT_VPID_CAP_REGISTER VpidRegister;
25 IA32_MTRR_DEF_TYPE_REGISTER MTRRDefType;
27 VpidRegister.AsUInt = CpuReadMsr(IA32_VMX_EPT_VPID_CAP);
28 MTRRDefType.AsUInt = CpuReadMsr(IA32_MTRR_DEF_TYPE);
29
30 if (!VpidRegister.PageWalkLength4 || !VpidRegister.MemoryTypeWriteBack || !VpidRegister.Pde2MbPages)
31 {
32 return FALSE;
33 }
34
35 if (!VpidRegister.AdvancedVmexitEptViolationsInformation)
36 {
37 LogDebugInfo("The processor doesn't report advanced VM-exit information for EPT violations");
38 }
39
40 if (VpidRegister.Invvpid &&
41 VpidRegister.InvvpidAllContexts &&
42 VpidRegister.InvvpidIndividualAddress &&
43 VpidRegister.InvvpidSingleContext &&
44 VpidRegister.InvvpidSingleContextRetainGlobals &&
46 {
48 LogDebugInfo("The processor supports VPID");
49 }
50
51 if (!VpidRegister.ExecuteOnlyPages)
52 {
53 g_CompatibilityCheck.ExecuteOnlySupport = FALSE;
54 LogDebugInfo("The processor doesn't support execute-only pages, execute hooks won't work as they're on this feature in our design");
55 }
56 else
57 {
58 g_CompatibilityCheck.ExecuteOnlySupport = TRUE;
59 }
60
61 if (!MTRRDefType.MtrrEnable)
62 {
63 LogError("Err, MTRR dynamic ranges are not supported");
64 return FALSE;
65 }
66
67 LogDebugInfo("All EPT features are present");
68
69 return TRUE;
70}
BOOLEAN g_IsVpidSupported
Whether VPID is supported or not.
Definition GlobalVariables.h:215
BOOLEAN g_IsTopLevelHypervisorHyperV
Whether the top level hypervisor is Hyper-V or not.
Definition GlobalVariables.h:220
COMPATIBILITY_CHECKS_STATUS g_CompatibilityCheck
Different attributes and compatibility checks of the current processor.
Definition GlobalVariables.h:26

◆ EptGetMemoryType()

UINT8 EptGetMemoryType ( SIZE_T PageFrameNumber,
BOOLEAN IsLargePage )

Check whether EPT features are present or not.

Parameters
PageFrameNumber
IsLargePage
Returns
UINT8 Return desired type of memory for particular small/large page
81{
82 UINT8 TargetMemoryType;
83 SIZE_T AddressOfPage;
84 SIZE_T CurrentMtrrRange;
85 MTRR_RANGE_DESCRIPTOR * CurrentMemoryRange;
86
87 AddressOfPage = IsLargePage ? PageFrameNumber * SIZE_2_MB : PageFrameNumber * PAGE_SIZE;
88
89 TargetMemoryType = (UINT8)-1;
90
91 //
92 // For each MTRR range
93 //
94 for (CurrentMtrrRange = 0; CurrentMtrrRange < g_EptState->NumberOfEnabledMemoryRanges; CurrentMtrrRange++)
95 {
96 CurrentMemoryRange = &g_EptState->MemoryRanges[CurrentMtrrRange];
97
98 //
99 // If the physical address is described by this MTRR
100 //
101 if (AddressOfPage >= CurrentMemoryRange->PhysicalBaseAddress &&
102 AddressOfPage < CurrentMemoryRange->PhysicalEndAddress)
103 {
104 // LogInfo("0x%X> Range=%llX -> %llX | Begin=%llX End=%llX", PageFrameNumber, AddressOfPage, AddressOfPage + SIZE_2_MB - 1, g_EptState->MemoryRanges[CurrentMtrrRange].PhysicalBaseAddress, g_EptState->MemoryRanges[CurrentMtrrRange].PhysicalEndAddress);
105
106 //
107 // 12.11.4.1 MTRR Precedences
108 //
109 if (CurrentMemoryRange->FixedRange)
110 {
111 //
112 // When the fixed-range MTRRs are enabled, they take priority over the variable-range
113 // MTRRs when overlaps in ranges occur.
114 //
115 TargetMemoryType = CurrentMemoryRange->MemoryType;
116 break;
117 }
118
119 if (TargetMemoryType == MEMORY_TYPE_UNCACHEABLE)
120 {
121 //
122 // If this is going to be marked uncacheable, then we stop the search as UC always
123 // takes precedence
124 //
125 TargetMemoryType = CurrentMemoryRange->MemoryType;
126 break;
127 }
128
129 if (TargetMemoryType == MEMORY_TYPE_WRITE_THROUGH || CurrentMemoryRange->MemoryType == MEMORY_TYPE_WRITE_THROUGH)
130 {
131 if (TargetMemoryType == MEMORY_TYPE_WRITE_BACK)
132 {
133 //
134 // If two or more MTRRs overlap and describe the same region, and at least one is WT and
135 // the other one(s) is/are WB, use WT. However, continue looking, as other MTRRs
136 // may still specify the address as UC, which always takes precedence
137 //
138 TargetMemoryType = MEMORY_TYPE_WRITE_THROUGH;
139 continue;
140 }
141 }
142
143 //
144 // Otherwise, just use the last MTRR that describes this address
145 //
146 TargetMemoryType = CurrentMemoryRange->MemoryType;
147 }
148 }
149
150 //
151 // If no MTRR was found, return the default memory type
152 //
153 if (TargetMemoryType == (UINT8)-1)
154 {
155 TargetMemoryType = g_EptState->DefaultMemoryType;
156 }
157
158 return TargetMemoryType;
159}
struct _MTRR_RANGE_DESCRIPTOR MTRR_RANGE_DESCRIPTOR
MTRR Descriptor.
#define SIZE_2_MB
Integer 2MB.
Definition Ept.h:31

◆ EptGetPml1Entry()

PEPT_PML1_ENTRY EptGetPml1Entry ( PVMM_EPT_PAGE_TABLE EptPageTable,
SIZE_T PhysicalAddress )

Get the PML1 entry for this physical address if the page is split.

Get the PML1 Entry of a special address.

Parameters
EptPageTableThe EPT Page Table
PhysicalAddressPhysical address that we want to get its PML1
Returns
PEPT_PML1_ENTRY Return NULL if the address is invalid or the page wasn't already split

Get the PML1 entry for this physical address if the page is split.

Parameters
EptPageTable
PhysicalAddress
Returns
PEPT_PML1_ENTRY
338{
339 SIZE_T Directory, DirectoryPointer, PML4Entry;
340 PEPT_PML2_ENTRY PML2;
341 PEPT_PML1_ENTRY PML1;
342
343 Directory = ADDRMASK_EPT_PML2_INDEX(PhysicalAddress);
344 DirectoryPointer = ADDRMASK_EPT_PML3_INDEX(PhysicalAddress);
345 PML4Entry = ADDRMASK_EPT_PML4_INDEX(PhysicalAddress);
346
347 //
348 // Addresses above 512GB are invalid because it is > physical address bus width
349 //
350 if (PML4Entry > 0)
351 {
352 return NULL;
353 }
354
355 PML2 = &EptPageTable->PML2[DirectoryPointer][Directory];
356
357 //
358 // Check to ensure the page is split
359 //
360 if (PML2->LargePage)
361 {
362 return NULL;
363 }
364
365 //
366 // Resolve the split PML1 VA that was recorded during EptSplitLargePage
367 //
368 PML1 = EptGetSplitPml1VaByPml2Entry(EptPageTable, PML2);
369
370 if (!PML1)
371 {
372 return NULL;
373 }
374
375 //
376 // Index into PML1 for that address
377 //
378 PML1 = &PML1[ADDRMASK_EPT_PML1_INDEX(PhysicalAddress)];
379
380 return PML1;
381}
#define ADDRMASK_EPT_PML2_INDEX(_VAR_)
Index of the 2nd paging structure (2MB).
Definition Ept.h:61
#define ADDRMASK_EPT_PML4_INDEX(_VAR_)
Index of the 4th paging structure (512GB).
Definition Ept.h:73
#define ADDRMASK_EPT_PML1_INDEX(_VAR_)
Index of the 1st paging structure (4096 byte).
Definition Ept.h:55
#define ADDRMASK_EPT_PML3_INDEX(_VAR_)
Index of the 3rd paging structure (1GB).
Definition Ept.h:67
EPT_PTE * PEPT_PML1_ENTRY
Definition State.h:23
EPT_PDE_2MB * PEPT_PML2_ENTRY
Definition State.h:21

◆ EptGetPml1OrPml2Entry()

PVOID EptGetPml1OrPml2Entry ( PVMM_EPT_PAGE_TABLE EptPageTable,
SIZE_T PhysicalAddress,
BOOLEAN * IsLargePage )

Get the PML1 entry for this physical address if the large page is available then large page of Pml2 is returned.

Parameters
EptPageTableThe EPT Page Table
PhysicalAddressPhysical address that we want to get its PML1
IsLargePageShows whether it's a large page or not
Returns
PVOID Return PEPT_PML1_ENTRY or PEPT_PML2_ENTRY
Parameters
EptPageTableThe EPT Page Table
PhysicalAddressPhysical address that we want to get its PML1
IsLargePageShows whether it's a large page or not
Returns
PEPT_PML1_ENTRY Return PEPT_PML1_ENTRY or PEPT_PML2_ENTRY
395{
396 SIZE_T Directory, DirectoryPointer, PML4Entry;
397 PEPT_PML2_ENTRY PML2;
398 PEPT_PML1_ENTRY PML1;
399
400 Directory = ADDRMASK_EPT_PML2_INDEX(PhysicalAddress);
401 DirectoryPointer = ADDRMASK_EPT_PML3_INDEX(PhysicalAddress);
402 PML4Entry = ADDRMASK_EPT_PML4_INDEX(PhysicalAddress);
403
404 //
405 // Addresses above 512GB are invalid because it is > physical address bus width
406 //
407 if (PML4Entry > 0)
408 {
409 return NULL;
410 }
411
412 PML2 = &EptPageTable->PML2[DirectoryPointer][Directory];
413
414 //
415 // Check to ensure the page is split
416 //
417 if (PML2->LargePage)
418 {
419 *IsLargePage = TRUE;
420 return PML2;
421 }
422
423 //
424 // Resolve the split PML1 VA that was recorded during EptSplitLargePage
425 //
426 PML1 = EptGetSplitPml1VaByPml2Entry(EptPageTable, PML2);
427
428 if (!PML1)
429 {
430 return NULL;
431 }
432
433 //
434 // Index into PML1 for that address
435 //
436 PML1 = &PML1[ADDRMASK_EPT_PML1_INDEX(PhysicalAddress)];
437
438 *IsLargePage = FALSE;
439 return PML1;
440}

◆ EptGetPml2Entry()

PEPT_PML2_ENTRY EptGetPml2Entry ( PVMM_EPT_PAGE_TABLE EptPageTable,
SIZE_T PhysicalAddress )

Get the PML2 entry for this physical address.

Split 2MB (LargePage) into 4kb pages.

Parameters
EptPageTableThe EPT Page Table
PhysicalAddressPhysical Address that we want to get its PML2
Returns
PEPT_PML2_ENTRY The PML2 Entry Structure

Get the PML2 entry for this physical address.

Parameters
EptPageTableThe EPT Page Table
PreAllocatedBufferThe address of pre-allocated buffer
PhysicalAddressPhysical address of where we want to split
Returns
BOOLEAN Returns true if it was successful or false if there was an error
451{
452 SIZE_T Directory, DirectoryPointer, PML4Entry;
453 PEPT_PML2_ENTRY PML2;
454
455 Directory = ADDRMASK_EPT_PML2_INDEX(PhysicalAddress);
456 DirectoryPointer = ADDRMASK_EPT_PML3_INDEX(PhysicalAddress);
457 PML4Entry = ADDRMASK_EPT_PML4_INDEX(PhysicalAddress);
458
459 //
460 // Addresses above 512GB are invalid because it is > physical address bus width
461 //
462 if (PML4Entry > 0)
463 {
464 return NULL;
465 }
466
467 PML2 = &EptPageTable->PML2[DirectoryPointer][Directory];
468 return PML2;
469}

◆ EptHandleEptViolation()

BOOLEAN EptHandleEptViolation ( VIRTUAL_MACHINE_STATE * VCpu)

Handle VM exits for EPT violations.

Handle EPT Violation.

Violations are thrown whenever an operation is performed on an EPT entry that does not provide permissions to access that page

Parameters
VCpuThe virtual processor's state
Returns
BOOLEAN Return true if the violation was handled by the page hook handler and false if it was not handled

Handle VM exits for EPT violations.

Parameters
VCpuThe virtual processor's state
Returns
BOOLEAN
1109{
1110 UINT64 GuestPhysicalAddr;
1111 VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification = {.AsUInt = VCpu->ExitQualification};
1112
1113 //
1114 // Reading guest physical address
1115 //
1116 VmxVmread64P(VMCS_GUEST_PHYSICAL_ADDRESS, &GuestPhysicalAddr);
1117
1118 if (EptHandlePageHookExit(VCpu, ViolationQualification, GuestPhysicalAddr))
1119 {
1120 //
1121 // Handled by page hook code
1122 //
1123 return TRUE;
1124 }
1125 else if (ExecTrapHandleEptViolationVmexit(VCpu, &ViolationQualification))
1126 {
1127 return TRUE;
1128 }
1129 else if (VmmCallbackUnhandledEptViolation(VCpu->CoreId, (UINT64)ViolationQualification.AsUInt, GuestPhysicalAddr))
1130 {
1131 //
1132 // Check whether this violation is meaningful for the application or not
1133 //
1134 return TRUE;
1135 }
1136
1137 LogError("Err, unexpected EPT violation at RIP: %llx", VCpu->LastVmexitRip);
1138 DbgBreakPoint();
1139 //
1140 // Redo the instruction that caused the exception
1141 //
1142 return FALSE;
1143}
BOOLEAN VmmCallbackUnhandledEptViolation(UINT32 CoreId, UINT64 ViolationQualification, UINT64 GuestPhysicalAddr)
routine callback to handle unhandled EPT violations
Definition Callback.c:167
_Use_decl_annotations_ BOOLEAN EptHandlePageHookExit(VIRTUAL_MACHINE_STATE *VCpu, VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, UINT64 GuestPhysicalAddr)
Check if this exit is due to a violation caused by a currently hooked page.
Definition Ept.c:950
BOOLEAN ExecTrapHandleEptViolationVmexit(VIRTUAL_MACHINE_STATE *VCpu, VMX_EXIT_QUALIFICATION_EPT_VIOLATION *ViolationQualification)
Handle EPT Violations related to the MBEC hooks.
Definition ExecTrap.c:655
UINT32 ExitQualification
Definition State.h:330
UINT32 CoreId
Definition State.h:328
UINT64 LastVmexitRip
Definition State.h:331

◆ EptHandleMisconfiguration()

VOID EptHandleMisconfiguration ( VOID )

Handle vm-exits for EPT Misconfiguration.

Handle Ept Misconfigurations.

Parameters
GuestAddress
Returns
VOID

Handle vm-exits for EPT Misconfiguration.

Returns
VOID
1153{
1154 UINT64 GuestPhysicalAddr = 0;
1155
1156 VmxVmread64P(VMCS_GUEST_PHYSICAL_ADDRESS, &GuestPhysicalAddr);
1157
1158 LogInfo("EPT Misconfiguration!");
1159
1160 LogError("Err, a field in the EPT paging structure was invalid, faulting guest address : 0x%llx",
1161 GuestPhysicalAddr);
1162
1163 //
1164 // We can't continue now.
1165 // EPT misconfiguration is a fatal exception that will probably crash the OS if we don't get out now
1166 //
1167}
#define LogInfo(format,...)
Define log variables.
Definition HyperDbgHyperLogIntrinsics.h:71

◆ EptHandlePageHookExit()

_Use_decl_annotations_ BOOLEAN EptHandlePageHookExit ( VIRTUAL_MACHINE_STATE * VCpu,
VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification,
UINT64 GuestPhysicalAddr )

Check if this exit is due to a violation caused by a currently hooked page.

If the memory access attempt was RW and the page was marked executable, the page is swapped with the original page.

If the memory access attempt was execute and the page was marked not executable, the page is swapped with the hooked page.

Parameters
VCpuThe virtual processor's state *
ViolationQualificationThe violation qualification in vm-exit
GuestPhysicalAddrThe GUEST_PHYSICAL_ADDRESS that caused this EPT violation
Returns
BOOLEAN Returns true if it was successful or false if the violation was not due to a page hook
953{
954 PVOID TargetPage;
955 UINT64 CurrentRip;
956 UINT32 CurrentInstructionLength;
957 BOOLEAN IsHandled = FALSE;
958 BOOLEAN ResultOfHandlingHook = FALSE;
959 BOOLEAN IgnoreReadOrWriteOrExec = FALSE;
960 BOOLEAN IsExecViolation = FALSE;
961
962 LIST_FOR_EACH_LINK(g_EptState->HookedPagesList, EPT_HOOKED_PAGE_DETAIL, PageHookList, HookedEntry)
963 {
964 if (HookedEntry->PhysicalBaseAddress == (SIZE_T)PAGE_ALIGN(GuestPhysicalAddr))
965 {
966 //
967 // *** We found an address that matches the details ***
968 //
969
970 //
971 // Returning true means that the caller should return to the ept state to
972 // the previous state when this instruction is executed
973 // by setting the Monitor Trap Flag. Return false means that nothing special
974 // for the caller to do
975 //
976
977 //
978 // Reaching here means that the hooks was actually caused VM-exit because of
979 // our configurations, but here we double whether the hook needs to trigger
980 // any event or not because the hooking address (physical) might not be in the
981 // target range. For example we might hook 0x123b000 to 0x123b300 but the hook
982 // happens on 0x123b4600, so we perform the necessary checks here
983 //
984
985 if (GuestPhysicalAddr >= HookedEntry->StartOfTargetPhysicalAddress && GuestPhysicalAddr <= HookedEntry->EndOfTargetPhysicalAddress)
986 {
987 ResultOfHandlingHook = EptHookHandleHookedPage(VCpu,
988 HookedEntry,
989 ViolationQualification,
990 GuestPhysicalAddr,
991 &HookedEntry->LastContextState,
992 &IgnoreReadOrWriteOrExec,
993 &IsExecViolation);
994 }
995 else
996 {
997 //
998 // Here we assume the hook is handled as the hook needs to be
999 // restored (just not within the range)
1000 //
1001 ResultOfHandlingHook = TRUE;
1002 }
1003
1004 if (ResultOfHandlingHook)
1005 {
1006 //
1007 // Here we check whether the event should be ignored or not,
1008 // if we don't apply the below restorations routines, the event
1009 // won't redo and the emulation of the memory access is passed
1010 //
1011 if (!IgnoreReadOrWriteOrExec)
1012 {
1013 //
1014 // Pointer to the page entry in the page table
1015 //
1016 TargetPage = EptGetPml1Entry(VCpu->EptPageTable, HookedEntry->PhysicalBaseAddress);
1017
1018 //
1019 // Restore to its original entry for one instruction
1020 //
1022 TargetPage,
1023 HookedEntry->OriginalEntry,
1024 InveptSingleContext);
1025
1026 //
1027 // Next we have to save the current hooked entry to restore on the next instruction's vm-exit
1028 //
1029 VCpu->MtfEptHookRestorePoint = HookedEntry;
1030
1031 //
1032 // The following codes are added because we realized if the execution takes long then
1033 // the execution might be switched to another routines, thus, MTF might conclude on
1034 // another routine and we might (and will) trigger the same instruction soon
1035 //
1036
1037 //
1038 // We have to set Monitor trap flag and give it the HookedEntry to work with
1039 //
1041 }
1042 }
1043
1044 //
1045 // Indicate that we handled the ept violation
1046 //
1047 IsHandled = TRUE;
1048
1049 //
1050 // Get out of the loop
1051 //
1052 break;
1053 }
1054 }
1055
1056 //
1057 // Check whether the event should be ignored or not
1058 //
1059 if (IgnoreReadOrWriteOrExec)
1060 {
1061 //
1062 // Do not redo the instruction (EPT hooks won't affect the VMCS_VMEXIT_INSTRUCTION_LENGTH),
1063 // thus, we use custom length diassembler engine to ignore the instruction at target address
1064 //
1065
1066 // HvPerformRipIncrement(VCpu); // invalid because EPT Violation won't affect VMCS_VMEXIT_INSTRUCTION_LENGTH
1067 HvSuppressRipIncrement(VCpu); // Just to make sure nothing is added to the address
1068
1069 //
1070 // If the target violation is for READ/WRITE, we ignore the current instruction and move to the
1071 // next instruction, but if the violation is for execute access, then we just won't increment the RIP
1072 //
1073 if (!IsExecViolation)
1074 {
1075 //
1076 // Get the RIP here as the RIP might be changed by the user and thus is not valid to be read
1077 // from the VCpu
1078 //
1079 CurrentRip = HvGetRip();
1081
1082 CurrentRip = CurrentRip + CurrentInstructionLength;
1083
1084 HvSetRip(CurrentRip);
1085 }
1086 }
1087 else
1088 {
1089 //
1090 // Redo the instruction (it's also not necessary as the EPT Violation won't affect VMCS_VMEXIT_INSTRUCTION_LENGTH)
1091 //
1093 }
1094
1095 return IsHandled;
1096}
BOOLEAN EptHookHandleHookedPage(VIRTUAL_MACHINE_STATE *VCpu, EPT_HOOKED_PAGE_DETAIL *HookedEntryDetails, VMX_EXIT_QUALIFICATION_EPT_VIOLATION ViolationQualification, SIZE_T PhysicalAddress, EPT_HOOKS_CONTEXT *LastContext, BOOLEAN *IgnoreReadOrWriteOrExec, BOOLEAN *IsExecViolation)
Handles page hooks (trigger events).
Definition EptHook.c:1706
VOID HvSetRip(UINT64 Rip)
Set guest's RIP.
Definition Hv.c:1240
UINT64 HvGetRip()
Read guest's RIP.
Definition Hv.c:1224
#define LIST_FOR_EACH_LINK(_head, _struct_type, _member, _var)
Definition MetaMacros.h:34
IMPORT_EXPORT_VMM UINT32 DisassemblerLengthDisassembleEngineInVmxRootOnTargetProcess(PVOID Address, BOOLEAN Is32Bit)
Disassembler length disassembler engine.
Definition Disassembler.c:297
BOOLEAN CommonIsGuestOnUsermode32Bit()
determines if the guest was in 32-bit user-mode or 64-bit (long mode)
Definition Common.c:97
#define PAGE_ALIGN(Va)
Aligning a page.
Definition common.h:86

◆ EptIsValidForLargePage()

BOOLEAN EptIsValidForLargePage ( SIZE_T PageFrameNumber)

Check if potential large page doesn't land on two or more different cache memory types.

Parameters
PageFrameNumberPFN (Physical Address)
Returns
BOOLEAN
624{
625 SIZE_T StartAddressOfPage = PageFrameNumber * SIZE_2_MB;
626 SIZE_T EndAddressOfPage = StartAddressOfPage + (SIZE_2_MB - 1);
627 MTRR_RANGE_DESCRIPTOR * CurrentMemoryRange;
628 SIZE_T CurrentMtrrRange;
629
630 for (CurrentMtrrRange = 0; CurrentMtrrRange < g_EptState->NumberOfEnabledMemoryRanges; CurrentMtrrRange++)
631 {
632 CurrentMemoryRange = &g_EptState->MemoryRanges[CurrentMtrrRange];
633
634 if ((StartAddressOfPage <= CurrentMemoryRange->PhysicalEndAddress &&
635 EndAddressOfPage > CurrentMemoryRange->PhysicalEndAddress) ||
636 (StartAddressOfPage < CurrentMemoryRange->PhysicalBaseAddress &&
637 EndAddressOfPage >= CurrentMemoryRange->PhysicalBaseAddress))
638 {
639 return FALSE;
640 }
641 }
642
643 return TRUE;
644}

◆ EptLogicalProcessorInitialize()

BOOLEAN EptLogicalProcessorInitialize ( VOID )

Initialize EPT for an individual logical processor.

Initialize EPT Table based on Processor Index.

Creates an identity mapped page table and sets up an EPTP to be applied to the VMCS later

Returns
BOOLEAN

Initialize EPT for an individual logical processor.

Returns
BOOLEAN
866{
867 ULONG ProcessorsCount;
868 PVMM_EPT_PAGE_TABLE PageTable;
869 EPT_POINTER EPTP = {0};
870
871 //
872 // Get number of processors
873 //
874 ProcessorsCount = KeQueryActiveProcessorCount(0);
875
876 for (SIZE_T i = 0; i < ProcessorsCount; i++)
877 {
878 //
879 // Allocate the identity mapped page table
880 //
882
883 if (!PageTable)
884 {
885 //
886 // Try to deallocate previous pools (if any)
887 //
888 for (SIZE_T j = 0; j < ProcessorsCount; j++)
889 {
890 if (g_GuestState[j].EptPageTable != NULL)
891 {
892 MmFreeContiguousMemory(g_GuestState[j].EptPageTable);
893 g_GuestState[j].EptPageTable = NULL;
894 }
895 }
896
897 LogError("Err, unable to allocate memory for EPT");
898 return FALSE;
899 }
900
901 //
902 // Virtual address to the page table to keep track of it for later freeing
903 //
904 g_GuestState[i].EptPageTable = PageTable;
905
906 //
907 // Use default memory type
908 //
909 EPTP.MemoryType = g_EptState->DefaultMemoryType;
910
911 //
912 // We might utilize the 'access' and 'dirty' flag features in the dirty logging mechanism
913 //
914 EPTP.EnableAccessAndDirtyFlags = TRUE;
915
916 //
917 // Bits 5:3 (1 less than the EPT page-walk length) must be 3, indicating an EPT page-walk length of 4;
918 // see Section 28.2.2
919 //
920 EPTP.PageWalkLength = 3;
921
922 //
923 // The physical page number of the page table we will be using
924 //
925 EPTP.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&PageTable->PML4) / PAGE_SIZE;
926
927 //
928 // We will write the EPTP to the VMCS later
929 //
930 g_GuestState[i].EptPointer = EPTP;
931 }
932
933 return TRUE;
934}
PVMM_EPT_PAGE_TABLE EptAllocateAndCreateIdentityPageTable(VOID)
Allocates page maps and create identity page table.
Definition Ept.c:687
VIRTUAL_MACHINE_STATE * g_GuestState
Save the state and variables related to virtualization on each to logical core.
Definition GlobalVariables.h:38

◆ EptSetPML1AndInvalidateTLB()

_Use_decl_annotations_ VOID EptSetPML1AndInvalidateTLB ( VIRTUAL_MACHINE_STATE * VCpu,
PEPT_PML1_ENTRY EntryAddress,
EPT_PML1_ENTRY EntryValue,
INVEPT_TYPE InvalidationType )

This function set the specific PML1 entry in a spinlock protected area then invalidate the TLB.

This function should be called from vmx root-mode

Parameters
VCpuThe virtual processor's state
EntryAddressPML1 entry information (the target address)
EntryValueThe value of pm1's entry (the value that should be replaced)
InvalidationTypetype of invalidation
Returns
VOID
1185{
1186 //
1187 // set the value
1188 //
1189 EntryAddress->AsUInt = EntryValue.AsUInt;
1190
1191 //
1192 // invalidate the cache
1193 //
1194 if (InvalidationType == InveptSingleContext)
1195 {
1196 EptInveptSingleContext(VCpu->EptPointer.AsUInt);
1197 }
1198 else if (InvalidationType == InveptAllContext)
1199 {
1201 }
1202 else
1203 {
1204 LogError("Err, invalid invalidation parameter");
1205 }
1206}
UCHAR EptInveptAllContexts()
Invalidates all contexts in EPT cache table.
Definition Invept.c:54
UCHAR EptInveptSingleContext(_In_ UINT64 EptPointer)
Invalidates a single context in ept cache table.
Definition Invept.c:40
EPT_POINTER EptPointer
Definition State.h:363

◆ EptSetupPML2Entry()

BOOLEAN EptSetupPML2Entry ( PVMM_EPT_PAGE_TABLE EptPageTable,
PEPT_PML2_ENTRY NewEntry,
SIZE_T PageFrameNumber )

Set up PML2 Entries.

Parameters
EptPageTable
NewEntryThe PML2 Entry
PageFrameNumberPFN (Physical Address)
Returns
VOID
656{
657 //
658 // Each of the 512 collections of 512 PML2 entries is setup here
659 // This will, in total, identity map every physical address from 0x0
660 // to physical address 0x8000000000 (512GB of memory)
661 // ((EntryGroupIndex * VMM_EPT_PML2E_COUNT) + EntryIndex) * 2MB is
662 // the actual physical address we're mapping
663 //
664 NewEntry->PageFrameNumber = PageFrameNumber;
665
666 if (EptIsValidForLargePage(PageFrameNumber))
667 {
668 NewEntry->MemoryType = EptGetMemoryType(PageFrameNumber, TRUE);
669
670 return TRUE;
671 }
672 else
673 {
674 //
675 // Here we won't need to use pre-allocated buffers
676 //
677 return EptSplitLargePage(EptPageTable, FALSE, PageFrameNumber * SIZE_2_MB);
678 }
679}
BOOLEAN EptIsValidForLargePage(SIZE_T PageFrameNumber)
Check if potential large page doesn't land on two or more different cache memory types.
Definition Ept.c:623
BOOLEAN EptSplitLargePage(PVMM_EPT_PAGE_TABLE EptPageTable, BOOLEAN UsePreAllocatedBuffer, SIZE_T PhysicalAddress)
Convert large pages to 4KB pages.
Definition Ept.c:481
UINT8 EptGetMemoryType(SIZE_T PageFrameNumber, BOOLEAN IsLargePage)
Check whether EPT features are present or not.
Definition Ept.c:80

◆ EptSplitLargePage()

BOOLEAN EptSplitLargePage ( PVMM_EPT_PAGE_TABLE EptPageTable,
BOOLEAN UsePreAllocatedBuffer,
SIZE_T PhysicalAddress )

Convert large pages to 4KB pages.

Parameters
EptPageTableThe EPT Page Table
UsePreAllocatedBufferWhether allocate a memory or use pre-allocated buffer
PhysicalAddressPhysical address of where we want to split
Returns
BOOLEAN Returns true if it was successful or false if there was an error
Parameters
EptPageTable
UsePreAllocatedBuffer
PhysicalAddress
Returns
BOOLEAN
484{
485 PVMM_EPT_DYNAMIC_SPLIT NewSplit;
486 EPT_PML1_ENTRY EntryTemplate;
487 SIZE_T EntryIndex;
488 PEPT_PML2_ENTRY TargetEntry;
489 EPT_PML2_POINTER NewPointer;
490
491 //
492 // Find the PML2 entry that's currently used
493 //
494 TargetEntry = EptGetPml2Entry(EptPageTable, PhysicalAddress);
495
496 if (!TargetEntry)
497 {
498 LogError("Err, an invalid physical address passed");
499 return FALSE;
500 }
501
502 //
503 // If this large page is not marked a large page, that means it's a pointer already.
504 // That page is therefore already split.
505 //
506 if (!TargetEntry->LargePage)
507 {
508 return TRUE;
509 }
510
511 //
512 // Allocate the PML1 entries
513 //
514 if (UsePreAllocatedBuffer)
515 {
517 }
518 else
519 {
521 }
522
523 if (!NewSplit)
524 {
525 LogError("Err, failed to allocate dynamic split memory");
526 return FALSE;
527 }
528 RtlZeroMemory(NewSplit, sizeof(VMM_EPT_DYNAMIC_SPLIT));
529
530 //
531 // Point back to the entry in the dynamic split for easy reference for which entry that
532 // dynamic split is for
533 //
534 NewSplit->Fields.Entry = TargetEntry;
535
536 //
537 // Make a template for RWX
538 //
539 EntryTemplate.AsUInt = 0;
540 EntryTemplate.ReadAccess = 1;
541 EntryTemplate.WriteAccess = 1;
542 EntryTemplate.ExecuteAccess = 1;
543
544 //
545 // Set the UserModeExecute bit based on the global state of MBEC
546 //
548 {
549 EntryTemplate.UserModeExecute = 1;
550 }
551 else
552 {
553 EntryTemplate.UserModeExecute = 0;
554 }
555
556 //
557 // copy other bits from target entry
558 //
559 EntryTemplate.MemoryType = TargetEntry->MemoryType;
560 EntryTemplate.IgnorePat = TargetEntry->IgnorePat;
561 EntryTemplate.SuppressVe = TargetEntry->SuppressVe;
562
563 //
564 // Copy the template into all the PML1 entries
565 //
566 CpuStosQ((SIZE_T *)&NewSplit->PML1[0], EntryTemplate.AsUInt, VMM_EPT_PML1E_COUNT);
567
568 //
569 // Set the page frame numbers for identity mapping
570 //
571 for (EntryIndex = 0; EntryIndex < VMM_EPT_PML1E_COUNT; EntryIndex++)
572 {
573 //
574 // Convert the 2MB page frame number to the 4096 page entry number plus the offset into the frame
575 //
576 NewSplit->PML1[EntryIndex].PageFrameNumber = ((TargetEntry->PageFrameNumber * SIZE_2_MB) / PAGE_SIZE) + EntryIndex;
577 NewSplit->PML1[EntryIndex].MemoryType = EptGetMemoryType(NewSplit->PML1[EntryIndex].PageFrameNumber, FALSE);
578 }
579
580 //
581 // Allocate a new pointer which will replace the 2MB entry with a pointer to 512 4096 byte entries
582 //
583 NewPointer.AsUInt = 0;
584 NewPointer.WriteAccess = 1;
585 NewPointer.ReadAccess = 1;
586 NewPointer.ExecuteAccess = 1;
587
588 //
589 // Set the UserModeExecute bit based on the global state of MBEC
590 //
592 {
593 NewPointer.UserModeExecute = 1;
594 }
595 else
596 {
597 NewPointer.UserModeExecute = 0;
598 }
599
600 NewPointer.PageFrameNumber = (SIZE_T)VirtualAddressToPhysicalAddress(&NewSplit->PML1[0]) / PAGE_SIZE;
601
602 //
603 // Now, replace the entry in the page table with our new split pointer
604 //
605 RtlCopyMemory(TargetEntry, &NewPointer, sizeof(NewPointer));
606
607 //
608 // Track the split so we can directly resolve PML1 VA from PML2 entry
609 // without any PA->VA reverse translation
610 //
611 InsertHeadList(&EptPageTable->DynamicSplitList, &(NewSplit->DynamicSplitList));
612
613 return TRUE;
614}
UINT64 PoolManagerCallbackRequestPool(POOL_ALLOCATION_INTENTION Intention, BOOLEAN RequestNewPool, UINT32 Size)
routine callback to request pool
Definition Callback.c:358
PEPT_PML2_ENTRY EptGetPml2Entry(PVMM_EPT_PAGE_TABLE EptPageTable, SIZE_T PhysicalAddress)
Get the PML2 entry for this physical address.
Definition Ept.c:450
struct _VMM_EPT_DYNAMIC_SPLIT * PVMM_EPT_DYNAMIC_SPLIT
struct _VMM_EPT_DYNAMIC_SPLIT VMM_EPT_DYNAMIC_SPLIT
Split 2MB granularity to 4 KB granularity.
PVOID PlatformMemAllocateNonPagedPool(SIZE_T NumberOfBytes)
Allocates non-paged pool memory.
Definition PlatformMem.c:208
@ SPLIT_2MB_PAGING_TO_4KB_PAGE
Definition DataTypes.h:76
EPT_PTE EPT_PML1_ENTRY
Definition State.h:23
EPT_PDE EPT_PML2_POINTER
Definition State.h:22
#define VMM_EPT_PML1E_COUNT
Then number of 4096 byte Page Table entries in the page table per 2MB PML2 entry when dynamically spl...
Definition State.h:99
BOOLEAN g_ModeBasedExecutionControlState
Enable interception of Cr3 for Mode-based Execution detection.
Definition GlobalVariables.h:106
LIST_ENTRY DynamicSplitList
Linked list entries for each dynamic split.
Definition Ept.h:163
EPT_PML1_ENTRY PML1[VMM_EPT_PML1E_COUNT]
The 4096 byte page table entries that correspond to the split 2MB table entry.
Definition Ept.h:147
PEPT_PML2_ENTRY Entry
Definition Ept.h:155
union _VMM_EPT_DYNAMIC_SPLIT::@275337017054050120003261225167376117025061264241 Fields
The pointer to the 2MB entry in the page table which this split is servicing.