From 83a6c64817edd12780ee3724b42fbceee034a94b Mon Sep 17 00:00:00 2001 From: memN0ps <89628341+memN0ps@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:32:45 +1200 Subject: [PATCH 1/2] Added functions to do gva->gpa & gpa->hpa --- hypervisor/src/intel/addresses.rs | 96 +++++++-------- hypervisor/src/intel/ept.rs | 56 ++++----- hypervisor/src/intel/hooks/hook_manager.rs | 8 +- hypervisor/src/intel/paging.rs | 131 +++++++++++++-------- hypervisor/src/intel/vmexit/commands.rs | 9 +- hypervisor/src/intel/vmexit/mtf.rs | 2 +- hypervisor/src/intel/vmexit/vmcall.rs | 2 +- hypervisor/src/windows/nt/pe.rs | 9 +- hypervisor/src/windows/ssdt/ssdt_hook.rs | 2 +- 9 files changed, 169 insertions(+), 146 deletions(-) diff --git a/hypervisor/src/intel/addresses.rs b/hypervisor/src/intel/addresses.rs index 2965e8a..94ffb71 100644 --- a/hypervisor/src/intel/addresses.rs +++ b/hypervisor/src/intel/addresses.rs @@ -5,9 +5,15 @@ //! as well as methods for extracting page frame numbers (PFNs) and other address-related information. use { - crate::intel::paging::PageTables, - core::ops::{Deref, DerefMut}, - x86::bits64::paging::{PAddr, BASE_PAGE_SHIFT}, + crate::{ + error::HypervisorError, + intel::{ept::Ept, paging::PageTables, support::vmread}, + }, + log::trace, + x86::{ + bits64::paging::{PAddr, BASE_PAGE_SHIFT}, + vmx::vmcs, + }, }; /// A representation of physical addresses. @@ -23,16 +29,16 @@ impl PhysicalAddress { Self(PAddr::from(pa)) } + /// Constructs a `PhysicalAddress` from a given virtual address. + pub fn from_va(va: u64) -> Result { + Ok(Self(PAddr::from(Self::pa_from_va(va)?))) + } + /// Constructs a `PhysicalAddress` from a given page frame number (PFN). pub fn from_pfn(pfn: u64) -> Self { Self(PAddr::from(pfn << BASE_PAGE_SHIFT)) } - /// Constructs a `PhysicalAddress` from a given virtual address. - pub fn from_va(va: u64) -> Self { - Self(PAddr::from(Self::pa_from_va(va))) - } - /// Retrieves the page frame number (PFN) for the physical address. pub fn pfn(&self) -> u64 { self.0.as_u64() >> BASE_PAGE_SHIFT @@ -43,60 +49,44 @@ impl PhysicalAddress { self.0.as_u64() } - /// Converts a virtual address to its corresponding physical address. - pub fn pa_from_va(va: u64) -> u64 { - let guest_cr3 = PageTables::get_guest_cr3(); - PageTables::translate_guest_virtual_to_physical(guest_cr3 as usize, va as _).unwrap() as u64 - } - - /// Reads a value of a specified type from guest memory at the provided virtual address, ensuring safety by internal validation. + /// Converts a guest virtual address to its corresponding host physical address. /// - /// # Arguments + /// This function first translates the guest virtual address to a guest physical address + /// using the guest's CR3. It then translates the guest physical address to a host physical + /// address using the EPT (Extended Page Table). /// - /// * `guest_cr3` - The base address of the guest's page table hierarchy. - /// * `guest_va` - The guest virtual address from which to read. + /// # Arguments /// - /// # Returns + /// * `va` - The guest virtual address to translate. /// - /// * Returns an `Option` which is `Some(value)` if the read is successful and safe, or `None` if the address cannot be translated or if safety conditions are not met. + /// # Safety /// - /// # Type Parameters + /// This function is unsafe because it involves raw memory access and relies on the integrity + /// of the VMCS (Virtual Machine Control Structure). /// - /// * `T` - The type of the value to read. This can be any type that implements the `Copy` trait and has a size that can be read atomically. + /// # Returns /// - /// # Credits - /// Credits to Jessie (jessiep_) for the initial concept. - pub fn read_guest_memory(guest_cr3: usize, guest_va: usize) -> Option { - // Safety justification: - // The translation function ensures that the physical address is valid and maps to a real physical memory location. - // The dereference is only performed if the translation succeeds, and it's constrained to types that are Copy, implying they can be safely duplicated and do not manage resources that require manual cleanup. - // Still, the caller must ensure that reading from this specific address does not violate any safety contracts. - let pa = PageTables::translate_guest_virtual_to_physical(guest_cr3, guest_va)?; - unsafe { Some(*(pa as *const T)) } - } -} + /// A `Result` containing the host physical address on success, or an error if the translation fails. + pub fn pa_from_va(va: u64) -> Result { + let guest_cr3 = vmread(vmcs::guest::CR3); + trace!("Guest CR3: {:#x}", guest_cr3); -impl Deref for PhysicalAddress { - type Target = PAddr; + let guest_pa = unsafe { PageTables::translate_guest_virtual_to_guest_physical(guest_cr3, va)? }; + trace!("Guest VA: {:#x} -> Guest PA: {:#x}", va, guest_pa); - /// Dereferences the `PhysicalAddress` to retrieve the underlying `PAddr`. - fn deref(&self) -> &Self::Target { - &self.0 - } -} + // Translate guest physical address (GPA) to host physical address (HPA) using Extended Page Tables (EPT) + // In a 1:1 mapping, the guest physical address is the same as the host physical address. + // This translation is not required in a 1:1 mapping but is done for demonstration purposes + // and in case changes are made to the Paging/EPT. + let vmcs_eptp = vmread(vmcs::control::EPTP_FULL); + trace!("VMCS EPTP: {:#x}", vmcs_eptp); -impl DerefMut for PhysicalAddress { - /// Provides mutable access to the underlying `PAddr`. - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} + let (pml4_address, _, _) = Ept::decode_eptp(vmcs_eptp)?; + trace!("EPT PML4 Address: {:#x}", pml4_address); -/// Converts a virtual address to its corresponding physical address. -/// -/// # Arguments -/// -/// * `ptr` - The virtual address to convert. -pub fn physical_address(ptr: *const u64) -> PAddr { - PhysicalAddress::from_va(ptr as u64).0 + let host_pa = unsafe { Ept::translate_guest_pa_to_host_pa(pml4_address, guest_pa)? }; + trace!("Guest PA: {:#x} -> Host PA: {:#x}", guest_pa, host_pa); + + Ok(host_pa) + } } diff --git a/hypervisor/src/intel/ept.rs b/hypervisor/src/intel/ept.rs index e77f001..909a275 100644 --- a/hypervisor/src/intel/ept.rs +++ b/hypervisor/src/intel/ept.rs @@ -121,52 +121,39 @@ impl Ept { Ok(()) } - /// Reads a value from a given guest physical address. + /// Translates a guest physical address to a host physical address using the EPT. /// - /// This function translates the guest physical address (GPA) to the corresponding host physical address (HPA) - /// using the EPT, and then reads the value at the HPA. + /// This function traverses the EPT hierarchy (PML4, PDPT, PD, PT) to translate the given + /// guest physical address (GPA) to its corresponding host physical address (HPA). /// /// # Arguments /// - /// * `guest_pa` - The guest physical address to read from. + /// * `guest_pa` - The guest physical address to translate. /// /// # Returns /// - /// A `Result` containing the value read from the address on success. - pub fn read_guest_pa(&self, guest_pa: u64) -> Result { - // Translate the guest physical address to host physical address. - // In a 1:1 mapping, the guest physical address is the same as the host physical address. - // Assuming the environment allows direct memory access to those addresses. - let host_pa = self.translate_guest_pa_to_host_pa(guest_pa)?; - trace!("Reading from GPA {:#x} (HPA: {:#x})", guest_pa, host_pa); - - // Read the value at the host physical address. - // Assuming the host physical address can be directly dereferenced. - // You may need to adjust the pointer dereferencing method based on your environment. - let value = unsafe { *(host_pa as *const u64) }; - trace!("Read value: {:#x}", value); - - Ok(value) - } - + /// A `Result` containing the host physical address on success. /// Translates a guest physical address to a host physical address using the EPT. - /// /// This function traverses the EPT hierarchy (PML4, PDPT, PD, PT) to translate the given /// guest physical address (GPA) to its corresponding host physical address (HPA). /// /// # Arguments /// + /// * `ept_base` - The base address of the EPT structure. /// * `guest_pa` - The guest physical address to translate. /// /// # Returns /// /// A `Result` containing the host physical address on success. - pub fn translate_guest_pa_to_host_pa(&self, guest_pa: u64) -> Result { + pub unsafe fn translate_guest_pa_to_host_pa(ept_base: u64, guest_pa: u64) -> Result { let guest_pa = VAddr::from(guest_pa); + // Cast the EPT base to the PML4 table structure. + let pml4_table = ept_base as *const Pml4; + // Calculate the PML4 index and access the corresponding entry. - let pmld4_index = pml4_index(guest_pa); - let pml4_entry = &self.pml4.0.entries[pmld4_index]; + let pml4_index = pml4_index(guest_pa); + let pml4_entry = &(*pml4_table).0.entries[pml4_index]; // Check if the PML4 entry is present (readable). if !pml4_entry.readable() { @@ -174,9 +161,12 @@ impl Ept { return Err(HypervisorError::InvalidPml4Entry); } + // Cast the entry to the PDPT table structure. + let pdpt_table = (pml4_entry.pfn() << BASE_PAGE_SHIFT) as *const Pdpt; + // Calculate the PDPT index and access the corresponding entry. let pdpt_index = pdpt_index(guest_pa); - let pdpt_entry = &self.pdpt.0.entries[pdpt_index]; + let pdpt_entry = &(*pdpt_table).0.entries[pdpt_index]; // Check if the PDPT entry is present (readable). if !pdpt_entry.readable() { @@ -184,15 +174,18 @@ impl Ept { return Err(HypervisorError::InvalidPdptEntry); } - // Check if the PDPT entry is huge page (1 GB), if so, calculate the host physical address. + // Check if the PDPT entry is a huge page (1 GB), if so, calculate the host physical address. if pdpt_entry.large() { let host_pa = (pdpt_entry.pfn() << BASE_PAGE_SHIFT) + (guest_pa.as_u64() % HUGE_PAGE_SIZE as u64); return Ok(host_pa); } + // Cast the entry to the PD table structure. + let pd_table = (pdpt_entry.pfn() << BASE_PAGE_SHIFT) as *const Pd; + // Calculate the PD index and access the corresponding entry. let pd_index = pd_index(guest_pa); - let pd_entry = &self.pd[pdpt_index].0.entries[pd_index]; + let pd_entry = &(*pd_table).0.entries[pd_index]; // Check if the PD entry is present (readable). if !pd_entry.readable() { @@ -200,15 +193,18 @@ impl Ept { return Err(HypervisorError::InvalidPdEntry); } - // Check if the PD entry is large page (2 MB), if so, calculate the host physical address. + // Check if the PD entry is a large page (2 MB), if so, calculate the host physical address. if pd_entry.large() { let host_pa = (pd_entry.pfn() << BASE_PAGE_SHIFT) + (guest_pa.as_u64() % LARGE_PAGE_SIZE as u64); return Ok(host_pa); } + // Cast the entry to the PT table structure. + let pt_table = (pd_entry.pfn() << BASE_PAGE_SHIFT) as *const Pt; + // Calculate the PT index and access the corresponding entry. let pt_index = pt_index(guest_pa); - let pt_entry = &self.pt.0.entries[pt_index]; + let pt_entry = &(*pt_table).0.entries[pt_index]; // Check if the PT entry is present (readable). if !pt_entry.readable() { diff --git a/hypervisor/src/intel/hooks/hook_manager.rs b/hypervisor/src/intel/hooks/hook_manager.rs index 9e58a32..61e1558 100644 --- a/hypervisor/src/intel/hooks/hook_manager.rs +++ b/hypervisor/src/intel/hooks/hook_manager.rs @@ -142,10 +142,10 @@ impl HookManager { /// * `Ok(())` - The kernel base and size were set successfully. pub fn set_kernel_base_and_size(&mut self, guest_va: u64) -> Result<(), HypervisorError> { // Get the base address of ntoskrnl.exe. - self.ntoskrnl_base_va = unsafe { get_image_base_address(guest_va).ok_or(HypervisorError::FailedToGetImageBaseAddress)? }; + self.ntoskrnl_base_va = unsafe { get_image_base_address(guest_va)? }; // Get the physical address of ntoskrnl.exe using GUEST_CR3 and the virtual address. - self.ntoskrnl_base_pa = PhysicalAddress::pa_from_va(self.ntoskrnl_base_va); + self.ntoskrnl_base_pa = PhysicalAddress::pa_from_va(self.ntoskrnl_base_va)?; // Get the size of ntoskrnl.exe. self.ntoskrnl_size = unsafe { get_size_of_image(self.ntoskrnl_base_pa as _).ok_or(HypervisorError::FailedToGetKernelSize)? } as u64; @@ -323,7 +323,7 @@ impl HookManager { ) -> Result<(), HypervisorError> { debug!("Creating EPT hook for function at VA: {:#x}", guest_function_va); - let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va)); + let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va)?); debug!("Guest function PA: {:#x}", guest_function_pa.as_u64()); let guest_page_pa = guest_function_pa.align_down_to_base_page(); @@ -424,7 +424,7 @@ impl HookManager { pub fn ept_unhook_function(&mut self, vm: &mut Vm, guest_function_va: u64, _ept_hook_type: EptHookType) -> Result<(), HypervisorError> { debug!("Removing EPT hook for function at VA: {:#x}", guest_function_va); - let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va)); + let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(guest_function_va)?); debug!("Guest function PA: {:#x}", guest_function_pa.as_u64()); let guest_page_pa = guest_function_pa.align_down_to_base_page(); diff --git a/hypervisor/src/intel/paging.rs b/hypervisor/src/intel/paging.rs index 74bcd9b..f30d682 100644 --- a/hypervisor/src/intel/paging.rs +++ b/hypervisor/src/intel/paging.rs @@ -6,13 +6,11 @@ //! https://github.com/tandasat/Hello-VT-rp/blob/main/hypervisor/src/paging_structures.rs use { - crate::{error::HypervisorError, intel::support::vmread}, + crate::error::HypervisorError, bitfield::bitfield, core::ptr::addr_of, - x86::{ - current::paging::{BASE_PAGE_SHIFT, LARGE_PAGE_SIZE}, - vmx::vmcs, - }, + log::error, + x86::bits64::paging::{pd_index, pdpt_index, pml4_index, pt_index, VAddr, BASE_PAGE_SHIFT, BASE_PAGE_SIZE, HUGE_PAGE_SIZE, LARGE_PAGE_SIZE}, }; /// Represents the entire Page Tables structure for the hypervisor. @@ -88,14 +86,13 @@ impl PageTables { log::debug!("Identity map built successfully"); } - /// Translates a guest virtual address to a physical address using the guest's CR3. + /// Translates a guest virtual address to a guest physical address using the guest's CR3. /// This function traverses the guest's page tables, assuming an identity-mapped /// host address space for simplicity. /// /// # Arguments - /// * `guest_cr3` - The guest CR3 register value, which contains the base address of the - /// guest's page table hierarchy. - /// * `virtual_address` - The guest virtual address to translate. + /// * `guest_cr3` - The guest CR3 register value, which contains the base address of the guest's page table hierarchy. + /// * `guest_va` - The guest virtual address to translate. /// /// # Safety /// This function is unsafe because it involves raw memory access based on potentially @@ -103,58 +100,82 @@ impl PageTables { /// or the memory is not properly mapped. /// /// # Returns - /// Returns Some(usize) containing the translated physical address if successful, - /// or None if the translation fails at any level of the page table hierarchy. + /// Returns a `Result` containing the translated guest physical address if successful, + /// or an error if the translation fails at any level of the page table hierarchy. /// /// # Credits - /// Credits to Jessie (jessiep_) for the initial concept. - pub fn translate_guest_virtual_to_physical(guest_cr3: usize, virtual_address: usize) -> Option { - // Mask used to clear the lower 12 bits of an address, effectively aligning it to a page boundary. - const ADDRESS_MASK: usize = ((1 << x86::bits64::paging::MAXPHYADDR) - 1) & !0xFFF; - - // Start at the base of the guest's page table hierarchy. - let mut current_paging = guest_cr3 as *const usize; - - // Iterate through the page table levels, checking for large pages and - // extracting the physical address from the page table entries. - for (supports_large, index, offset_mask) in [ - (false, (virtual_address >> 39) & 0x1FF, 0), - (true, (virtual_address >> 30) & 0x1FF, 0x3FFFFFFF), - (true, (virtual_address >> 21) & 0x1FF, 0x1FFFFF), - ] { - let page_entry = unsafe { *current_paging.add(index) }; - - // If the page is not present, translation fails. - if page_entry & 1 == 0 { - return None; - } + /// Credits to Jessie (jessiep_) for the help. + pub unsafe fn translate_guest_virtual_to_guest_physical(guest_cr3: u64, guest_va: u64) -> Result { + let guest_va = VAddr::from(guest_va); - // If this is a large page, calculate the physical address and return it, taking into account the offset within the large page. - if supports_large && (page_entry & 0x80 != 0) { - return Some((page_entry & ADDRESS_MASK) | (virtual_address & offset_mask)); - } + // Cast guest CR3 to the PML4 table structure + let pml4_table = guest_cr3 as *const Pml4; - // go to the next page :) - current_paging = (page_entry & ADDRESS_MASK) as *const usize; + // Calculate the PML4 index and access the corresponding entry. + let pml4_index = pml4_index(guest_va); + let pml4_entry = &(*pml4_table).0.entries[pml4_index]; + + // Check if the PML4 entry is present (readable). + if !pml4_entry.present() { + error!("PML4 entry is not present: {:#x}", guest_va); + return Err(HypervisorError::InvalidPml4Entry); } - let page_entry = unsafe { *current_paging.add((virtual_address >> 12) & 0x1FF) }; + // Cast the entry to the PDPT table structure. + let pdpt_table = (pml4_entry.pfn() << BASE_PAGE_SHIFT) as *const Pdpt; + + // Calculate the PDPT index and access the corresponding entry. + let pdpt_index = pdpt_index(guest_va); + let pdpt_entry = &(*pdpt_table).0.entries[pdpt_index]; - // If the page is not present, translation fails. - if page_entry & 1 == 0 { - return None; + // Check if the PDPT entry is present (readable). + if !pdpt_entry.present() { + error!("PDPT entry is not present: {:#x}", guest_va); + return Err(HypervisorError::InvalidPdptEntry); } - Some((page_entry & ADDRESS_MASK) | (virtual_address & 0xFFF)) - } + // Check if the PDPT entry is a huge page (1 GB), if so, calculate the guest physical address. + if pdpt_entry.large() { + let guest_pa = (pdpt_entry.pfn() << BASE_PAGE_SHIFT) + (guest_va.as_u64() % HUGE_PAGE_SIZE as u64); + return Ok(guest_pa); + } - /// Gets the guest CR3 value from the VMCS. - /// - /// # Returns - /// - /// * The guest CR3 value from the VMCS. - pub fn get_guest_cr3() -> u64 { - vmread(vmcs::guest::CR3) + // Cast the entry to the PD table structure. + let pd_table = (pdpt_entry.pfn() << BASE_PAGE_SHIFT) as *const Pd; + + // Calculate the PD index and access the corresponding entry. + let pd_index = pd_index(guest_va); + let pd_entry = &(*pd_table).0.entries[pd_index]; + + // Check if the PD entry is present (readable). + if !pd_entry.present() { + error!("PD entry is not present: {:#x}", guest_va); + return Err(HypervisorError::InvalidPdEntry); + } + + // Check if the PD entry is a large page (2 MB), if so, calculate the guest physical address. + if pd_entry.large() { + let guest_pa = (pd_entry.pfn() << BASE_PAGE_SHIFT) + (guest_va.as_u64() % LARGE_PAGE_SIZE as u64); + return Ok(guest_pa); + } + + // Cast the entry to the PT table structure. + let pt_table = (pd_entry.pfn() << BASE_PAGE_SHIFT) as *const Pt; + + // Calculate the PT index and access the corresponding entry. + let pt_index = pt_index(guest_va); + let pt_entry = &(*pt_table).0.entries[pt_index]; + + // Check if the PT entry is present (readable). + if !pt_entry.present() { + error!("PT entry is not present: {:#x}", guest_va); + return Err(HypervisorError::InvalidPtEntry); + } + + // The PT entry is a 4 KB page, calculate the guest physical address. + let guest_pa = (pt_entry.pfn() << BASE_PAGE_SHIFT) + (guest_va.as_u64() % BASE_PAGE_SIZE as u64); + + Ok(guest_pa) } /// Gets the physical address of the PML4 table, ensuring it is 4KB aligned. @@ -207,6 +228,14 @@ pub struct Pdpt(Table); #[derive(Debug, Clone, Copy)] struct Pd(Table); +/// Represents an EPT Page-Table Entry (PTE) that maps a 4-KByte Page. +/// +/// PTEs are part of the fourth level in the standard x86-64 paging hierarchy. +/// +/// Reference: IntelĀ® 64 and IA-32 Architectures Software Developer's Manual: 4.5 Paging +#[derive(Debug, Clone, Copy)] +pub struct Pt(Table); + /* /// Represents a Page-Table Entry (PTE) that maps a 4-KByte Page. /// diff --git a/hypervisor/src/intel/vmexit/commands.rs b/hypervisor/src/intel/vmexit/commands.rs index 0ad5b8f..91eb256 100644 --- a/hypervisor/src/intel/vmexit/commands.rs +++ b/hypervisor/src/intel/vmexit/commands.rs @@ -27,7 +27,14 @@ pub fn handle_guest_commands(vm: &mut Vm) -> bool { debug!("Handling commands"); // Convert guest RCX register value to a pointer to ClientData - let client_data_ptr = PhysicalAddress::pa_from_va(vm.guest_registers.rcx); + let client_data_ptr = match PhysicalAddress::pa_from_va(vm.guest_registers.rcx) { + Ok(pa) => pa, + Err(e) => { + error!("Failed to convert guest RCX to pointer: {:?}", e); + return false; + } + }; + debug!("Client data pointer: {:#x}", client_data_ptr); // Convert the pointer to ClientData diff --git a/hypervisor/src/intel/vmexit/mtf.rs b/hypervisor/src/intel/vmexit/mtf.rs index 015c7a6..80b069b 100644 --- a/hypervisor/src/intel/vmexit/mtf.rs +++ b/hypervisor/src/intel/vmexit/mtf.rs @@ -38,7 +38,7 @@ pub fn handle_monitor_trap_flag(vm: &mut Vm) -> Result Result { trace!("Guest RAX - VMCALL command number: {:#x}", vmcall_number); trace!("Guest RIP: {:#x}", vm.guest_registers.rip); - let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(vm.guest_registers.rip)); + let guest_function_pa = PAddr::from(PhysicalAddress::pa_from_va(vm.guest_registers.rip)?); trace!("Guest PA: {:#x}", guest_function_pa.as_u64()); let guest_page_pa = guest_function_pa.align_down_to_base_page(); diff --git a/hypervisor/src/windows/nt/pe.rs b/hypervisor/src/windows/nt/pe.rs index c7c2df5..666ce76 100644 --- a/hypervisor/src/windows/nt/pe.rs +++ b/hypervisor/src/windows/nt/pe.rs @@ -3,6 +3,7 @@ use { crate::{ + error::HypervisorError, intel::addresses::PhysicalAddress, windows::nt::types::{ IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DOS_SIGNATURE, IMAGE_NT_SIGNATURE, PIMAGE_DOS_HEADER, PIMAGE_EXPORT_DIRECTORY, PIMAGE_NT_HEADERS64, @@ -173,14 +174,14 @@ pub fn djb2_hash(buffer: &[u8]) -> u32 { /// # Credits /// /// To Jessie (jessiep_) and Satoshi: https://gist.github.com/tandasat/bf0189952f113518f75c4f008c1e8d04#file-guestagent-c-L134-L161 -pub unsafe fn get_image_base_address(start_va: u64) -> Option { +pub unsafe fn get_image_base_address(start_va: u64) -> Result { // Align the start address down to the nearest page boundary. let mut guest_va = start_va & !0xFFF; loop { // Attempt to read the potential DOS signature at the current address. - match *(PhysicalAddress::pa_from_va(guest_va) as *const u16) { - IMAGE_DOS_SIGNATURE => return Some(guest_va), // Found the 'MZ' signature. + match *(PhysicalAddress::pa_from_va(guest_va)? as *const u16) { + IMAGE_DOS_SIGNATURE => return Ok(guest_va), // Found the 'MZ' signature. _ => { if guest_va == 0 { break; // Prevent underflow and ensure the loop eventually terminates. @@ -190,5 +191,5 @@ pub unsafe fn get_image_base_address(start_va: u64) -> Option { } } - None // The 'MZ' signature was not found in the scanned range. + Err(HypervisorError::FailedToGetImageBaseAddress) // The 'MZ' signature was not found in the scanned range. } diff --git a/hypervisor/src/windows/ssdt/ssdt_hook.rs b/hypervisor/src/windows/ssdt/ssdt_hook.rs index 6b23905..9071146 100644 --- a/hypervisor/src/windows/ssdt/ssdt_hook.rs +++ b/hypervisor/src/windows/ssdt/ssdt_hook.rs @@ -84,7 +84,7 @@ impl SsdtHook { // let offset = unsafe { ssdt.p_service_table.add(api_number as usize).read() as usize >> 4 }; // We can't do this because it's a guest VA. // let offset_ptr_va = unsafe { ssdt.p_service_table.add(api_number as usize) }; - let offset_ptr_pa = PhysicalAddress::pa_from_va(offset_ptr_va as u64) as *const i32; + let offset_ptr_pa = PhysicalAddress::pa_from_va(offset_ptr_va as u64)? as *const i32; let offset = unsafe { offset_ptr_pa.read() as usize >> 4 }; trace!("SSDT function offset: {:#x}", offset); From 9ebae169436a502cab7feb0a3ce43e9fbe1413d1 Mon Sep 17 00:00:00 2001 From: memN0ps <89628341+memN0ps@users.noreply.github.com> Date: Wed, 31 Jul 2024 23:36:20 +1200 Subject: [PATCH 2/2] This would cause a crash due to pre-alloc Pts for hooks - This translation is not required in a 1:1 mapping but is done for demonstration purposes and in case changes are made to the Paging/EPT. --- hypervisor/src/intel/addresses.rs | 11 ++++++++--- hypervisor/src/intel/ept.rs | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hypervisor/src/intel/addresses.rs b/hypervisor/src/intel/addresses.rs index 94ffb71..bd1732c 100644 --- a/hypervisor/src/intel/addresses.rs +++ b/hypervisor/src/intel/addresses.rs @@ -84,9 +84,14 @@ impl PhysicalAddress { let (pml4_address, _, _) = Ept::decode_eptp(vmcs_eptp)?; trace!("EPT PML4 Address: {:#x}", pml4_address); - let host_pa = unsafe { Ept::translate_guest_pa_to_host_pa(pml4_address, guest_pa)? }; - trace!("Guest PA: {:#x} -> Host PA: {:#x}", guest_pa, host_pa); + // Note: This may cause a crash at `!pt_entry.readable()` because the hypervisor has pre-allocated page tables + // in the hook_manager that are not passed to this function. We're attempting to translate a guest physical address to a host physical address using the EPT. + // The hypervisor maps everything as 2MB pages. The hooked pages are split and stored in the pre-allocated Pt, + // which are usually passed as a parameter, those are not stored in the EPT structure. + // This translation is not required in a 1:1 mapping but is done for demonstration purposes and in case changes are made to the Paging/EPT. + // let host_pa = unsafe { Ept::translate_guest_pa_to_host_pa(pml4_address, guest_pa)? }; + // trace!("Guest PA: {:#x} -> Host PA: {:#x}", guest_pa, host_pa); - Ok(host_pa) + Ok(guest_pa) } } diff --git a/hypervisor/src/intel/ept.rs b/hypervisor/src/intel/ept.rs index 909a275..71a1cff 100644 --- a/hypervisor/src/intel/ept.rs +++ b/hypervisor/src/intel/ept.rs @@ -526,6 +526,7 @@ impl Ept { pub fn create_eptp_with_wb_and_4lvl_walk(&self) -> Result { // Get the virtual address of the PML4 table for EPT. let addr = addr_of!(self.pml4) as u64; + trace!("EPT PML4 (self) address: {:#x}", addr); // Get the physical address of the PML4 table for EPT. let ept_pml4_base_addr = addr;