use super::{pointer, profile, Pointer};
use vmc::{Os, PhysicalAddress, VirtualAddress};
const VAD_MASK: u64 = 0xffffffff00000000;
const MMPTE_VALID_BIT: u64 = 1 << 0;
const MMPTE_SWIZZLE_BIT: u64 = 1 << 4;
const MMPTE_SOFTWARE_BIT: u64 = 1 << 10;
const MMPTE_TRANSITION_BIT: u64 = 1 << 11;
#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(transparent)]
pub(crate) struct MmPte(u64);
enum MmPteKind {
Software,
Transition,
Vad,
Zero,
Unknown,
}
impl MmPte {
#[inline]
fn is_valid(self) -> bool {
self.0 & MMPTE_VALID_BIT != 0
}
#[inline]
fn transition_page(self) -> PhysicalAddress {
PhysicalAddress(self.0 & vmc::mask_range(12, 40))
}
#[inline]
fn classify(self) -> MmPteKind {
let pte = self.0;
if pte & MMPTE_SOFTWARE_BIT != 0 {
if pte & VAD_MASK == VAD_MASK {
MmPteKind::Vad
} else {
MmPteKind::Software
}
} else if pte == 0 {
MmPteKind::Vad
} else if pte & MMPTE_TRANSITION_BIT != 0 {
MmPteKind::Transition
} else if pte >> 32 == 0 {
MmPteKind::Zero
} else {
MmPteKind::Unknown
}
}
}
impl<B: vmc::Backend> super::Windows<B> {
#[inline]
fn unswizzle(&self, pte: MmPte) -> MmPte {
if pte.0 & MMPTE_SWIZZLE_BIT == 0 {
MmPte(pte.0 & self.unswizzle_mask)
} else {
pte
}
}
fn read_vad_pte(
&self,
addr: VirtualAddress,
buf: &mut [u8],
proc: vmc::Process,
) -> vmc::TranslationResult<()> {
if addr.is_kernel() {
return Err(vmc::TranslationError::Invalid(0));
}
let result = (|| {
let vma = self
.process_find_vma_by_address(proc, addr)?
.ok_or("encountered unmapped page")?;
let vma_start = self.vma_start(vma)?;
let pte: Pointer<profile::Mmvad, _> = Pointer::new(vma.0, self, pointer::KernelSpace);
let mut pte = pte.read_pointer_field(|mmvad| mmvad.FirstPrototypePte)?;
pte.addr += ((addr - vma_start) as u64 / 0x1000) * 8;
pte.read()
})();
match result {
Ok(pte) => self.read_prototype_pte(addr, buf, pte),
Err(err) => {
log::warn!("Failed to read VAD PTE for address {addr:#x}: {err}");
Err(vmc::TranslationError::Invalid(0))
}
}
}
fn read_prototype_pte(
&self,
addr: VirtualAddress,
buf: &mut [u8],
pte: MmPte,
) -> vmc::TranslationResult<()> {
let offset = addr.0 & vmc::mask(12);
if pte.is_valid() {
let addr_base = PhysicalAddress(pte.0 & vmc::mask_range(12, 48));
self.backend.read_physical(addr_base + offset, buf)?;
return Ok(());
}
let pte = self.unswizzle(pte);
match pte.classify() {
MmPteKind::Transition => {
self.backend
.read_physical(pte.transition_page() + offset, buf)?;
Ok(())
}
MmPteKind::Zero => {
buf.fill(0);
Ok(())
}
_ => Err(vmc::TranslationError::Invalid(pte.0)),
}
}
pub(super) fn read_virtual_memory_raw(
&self,
mmu_addr: PhysicalAddress,
addr: VirtualAddress,
buf: &mut [u8],
proc: Option<vmc::Process>,
) -> vmc::TranslationResult<()> {
let entry = match self.backend.read_virtual_memory(mmu_addr, addr, buf) {
Ok(()) => return Ok(()),
Err(vmc::TranslationError::Invalid(entry)) => MmPte(entry),
Err(vmc::TranslationError::Memory(err)) => return Err(err.into()),
};
let offset = addr.0 & vmc::mask(12);
let pte = self.unswizzle(entry);
match pte.classify() {
MmPteKind::Software => {
let pte_addr = VirtualAddress(pte.0 >> 16);
let pte = self.backend.read_value_virtual(self.kpgd, pte_addr)?;
self.read_prototype_pte(addr, buf, pte)
}
MmPteKind::Transition => {
self.backend
.read_physical(pte.transition_page() + offset, buf)?;
Ok(())
}
MmPteKind::Vad => match proc {
Some(proc) => self.read_vad_pte(addr, buf, proc),
None => Err(vmc::TranslationError::Invalid(pte.0)),
},
MmPteKind::Zero => {
buf.fill(0);
Ok(())
}
MmPteKind::Unknown => Err(vmc::TranslationError::Invalid(pte.0)),
}
}
}