public long GetValueAtPhysicalAddr(HARDWARE_ADDRESS_ENTRY PAddr) { long[] block = null; return(GetPageForPhysAddr(PAddr, ref block)); //return pageData[PAddr.AddressOffset >> 3]; }
public override long Seek(long offset, SeekOrigin origin) { HARDWARE_ADDRESS_ENTRY PhysAddr = HARDWARE_ADDRESS_ENTRY.MaxAddr; long DestAddr = long.MaxValue; switch (origin) { default: case SeekOrigin.Begin: DestAddr = offset; break; case SeekOrigin.End: DestAddr = Length - offset; break; case SeekOrigin.Current: DestAddr = position; DestAddr += offset; break; } PhysAddr = MemBlockStorage.VirtualToPhysical(Proc.vmcs.EPTP, Proc.CR3Value, DestAddr); if (PhysAddr == HARDWARE_ADDRESS_ENTRY.MinAddr || PhysAddr == HARDWARE_ADDRESS_ENTRY.MaxAddr || PhysAddr == HARDWARE_ADDRESS_ENTRY.MaxAddr - 1) { throw new PageNotFoundException($"unable to locate the physical page for the supplied virtual address {DestAddr}", PhysAddr, null, null); } position = DestAddr; CurrPage = PhysAddr; return(DestAddr); }
public long GetPageForPhysAddr <T>(HARDWARE_ADDRESS_ENTRY PAddr, ref T[] block, ref bool GotData) { long rv = 0; // convert PAddr to PFN var aPFN = PAddr.NextTable_PFN; GotData = false; // should return with - error_value // This happens quite a bit and is a good boost // I guess were constrained by int.MaxValue pages here. // so that is about 8TB // TODO: explore 5 level page tables and larger than 8TB inputs :) if (aPFN > int.MaxValue || aPFN < 0) { return(0); } // paranoid android setting var FileOffset = OffsetToMemIndex(aPFN); if (FileOffset >= 0) { rv = GetPageFromFileOffset(FileOffset + PAddr.AddressOffset, ref block, ref GotData); } if (!GotData) { rv = MagicNumbers.BAD_VALUE_READ; } return(rv); }
/// <summary> /// Get a long back for the address specified /// </summary> /// <param name="PAddr">physical address (byte address)</param> /// <returns>value</returns> public long GetValueAtPhysicalAddr(HARDWARE_ADDRESS_ENTRY PAddr) { bool Ignored = false; long[] block = new long[512]; return(GetPageForPhysAddr(PAddr, ref block, ref Ignored)); }
public ExtendedPageNotFoundException(string message, HARDWARE_ADDRESS_ENTRY eptpUsed, HARDWARE_ADDRESS_ENTRY cr3Used, HARDWARE_ADDRESS_ENTRY lastEPAttempted, List <HARDWARE_ADDRESS_ENTRY> ePFound, PageNotFoundException ex) : base(message, ex) { EPFound = ePFound; LastEPAttempted = lastEPAttempted; RequestedCR3 = cr3Used; RequestedEPTP = eptpUsed; }
/// <summary> /// Scan and return Extract objects which represent detected PE's /// </summary> /// <param name="Start"></param> /// <param name="Stop">We just truncate VA's at 48 bit's</param> /// <returns>count of new detections since last Run</returns> public Dictionary <long, Extract> Run(long Start = 0, long Stop = 0xFFFFffffFFFF, ParallelLoopState pState = null, Mem Instance = null) { var memAxss = Instance == null ? BackingBlocks : Instance; var rv = new Dictionary <long, Extract>(); // convert index to an address // then add start to it long i = Start; var block = new long[0x200]; // 0x200 * 8 = 4k while (i < Stop) { if (pState != null && pState.IsStopped) { return(rv); } HARDWARE_ADDRESS_ENTRY locPhys = HARDWARE_ADDRESS_ENTRY.MinAddr; if (DPContext.vmcs != null) { locPhys = memAxss.VirtualToPhysical(DPContext.vmcs.EPTP, DPContext.CR3Value, i); } else { locPhys = memAxss.VirtualToPhysical(DPContext.CR3Value, i); } var Curr = i; i += 0x1000; if (HARDWARE_ADDRESS_ENTRY.IsBadEntry(locPhys)) { continue; } bool GotData = false; memAxss.GetPageForPhysAddr(locPhys, ref block, ref GotData); if (!GotData) { continue; } foreach (var scanner in CheckMethods) { var scan_detect = scanner(Curr, block); if (scan_detect != VAScanType.UNDETERMINED) { rv.Add(Curr, Artifacts[Curr]); if (Vtero.VerboseOutput) { Console.WriteLine($"Detected PE @ VA {Curr:X}"); } } } } return(rv); }
public static bool IsBadEntry(HARDWARE_ADDRESS_ENTRY entry) { if (entry <= HARDWARE_ADDRESS_ENTRY.MinAddr + 2L || entry >= HARDWARE_ADDRESS_ENTRY.MaxAddr - 2L) { return(true); } return(false); }
public long GetPageForPhysAddr(HARDWARE_ADDRESS_ENTRY PAddr, ref long[] block, ref bool GotData, bool NoCache = true) { long rv = 0; // convert PAddr to PFN var aPFN = PAddr.NextTable_PFN; GotData = false; //NoCache = true; if (!NoCache && PageCache.ContainsKey(aPFN)) { do { PageCache.TryGetValue(aPFN, out block); }while (block == null); // shift down since were loading a long[] GotData = true; return(block[(PAddr >> 3) & 0x1ff]); } // should return with - error_value if (aPFN > int.MaxValue || aPFN < 0) { return(0); } #if USE_BITMAP if (pfnTableIdx != null) { pfnTableIdx.Set((int)PFN, true); } #endif // paranoid android setting var FileOffset = OffsetToMemIndex(aPFN); if (FileOffset < 0) { rv = MagicNumbers.BAD_RUN_CONFIG_READ; } else { // add back in the file offset for possible exact byte lookup rv = GetPageFromFileOffset(FileOffset + PAddr.AddressOffset, ref block, ref GotData); } if (!GotData) { rv = MagicNumbers.BAD_VALUE_READ; } if (!NoCache && GotData) { PageCache.TryAdd(aPFN, block); } return(rv); }
/// <summary> /// Get a long back for the address specified /// </summary> /// <param name="PAddr">physical address (byte address)</param> /// <returns>value</returns> public long GetValueAtPhysicalAddr(HARDWARE_ADDRESS_ENTRY PAddr) { bool Ignored = false; long[] block = new long[512]; return(GetPageForPhysAddr(PAddr, ref block, ref Ignored)); //return pageData[PAddr.AddressOffset >> 3]; }
// Extract a single page of data from a physical address in source dump // account for memory gaps/run layout // TODO: Add windowing currently uses naive single-page-at-a-time view public long GetPageForPhysAddr(HARDWARE_ADDRESS_ENTRY PAddr, ref long[] block) { // convert PAddr to PFN var PFN = PAddr.NextTable_PFN; if (PageCache.ContainsKey(PFN)) { if (PageCache.TryGetValue(PAddr, out block)) { return(block[PAddr & 0x1ff]); } } // record our access attempt to the pfnIndex if (PFN > int.MaxValue || PFN > MD.MaxAddressablePageNumber) { return(0); } pfnTableIdx.Set((int)PFN, true); // paranoid android setting var Fail = true; long IndexedPFN = 0; for (int i = 0; i < MD.NumberOfRuns; i++) { if (PFN >= MD.Run[i].BasePage && PFN < (MD.Run[i].BasePage + MD.Run[i].PageCount)) { var currBaseOffset = PFN - MD.Run[i].BasePage; IndexedPFN += currBaseOffset; Fail = false; break; } IndexedPFN += MD.Run[i].PageCount; } if (Fail) { throw new MemoryRunMismatchException(PAddr.PTE); } // Determine file offset based on indexed/gap adjusted PFN and page size var FileOffset = StartOfMemory + (IndexedPFN * PAGE_SIZE); // add back in the file offset for possible exact byte lookup var rv = GetPageFromFileOffset(FileOffset + PAddr.AddressOffset, ref block); if (block != null) { PageCache.TryAdd(PFN, block); } return(rv); }
/// <summary> /// /// </summary> /// <param name="Start"></param> /// <param name="Stop">We just truncate VA's at 48 bit's</param> /// <returns>count of new detections since last Run</returns> public long Run(long Start = 0, long Stop = 0xFFFFffffFFFF) { DetectedFragments = new ConcurrentDictionary <long, VAScanType>(); // convert index to an address // then add start to it long i = Start; var block = new long[0x200]; // 0x200 * 8 = 4k while (i < Stop) { foreach (var scanner in CheckMethods) { HARDWARE_ADDRESS_ENTRY locPhys = HARDWARE_ADDRESS_ENTRY.MinAddr; if (DPContext.vmcs != null) { //locPhys = MemoryBank[j].VirtualToPhysical(DPContext.vmcs.EPTP, DPContext.CR3Value, i); locPhys = BackingBlocks.VirtualToPhysical(DPContext.vmcs.EPTP, DPContext.CR3Value, i); } else { //locPhys = MemoryBank[j].VirtualToPhysical(DPContext.CR3Value, i); locPhys = BackingBlocks.VirtualToPhysical(DPContext.CR3Value, i); } var Curr = i; i += 0x1000; if (HARDWARE_ADDRESS_ENTRY.IsBadEntry(locPhys)) { continue; } bool GotData = false; BackingBlocks.GetPageForPhysAddr(locPhys, ref block, ref GotData, false); if (!GotData) { continue; } var scan_detect = scanner(Curr, block); if (scan_detect != VAScanType.UNDETERMINED) { DetectedFragments.TryAdd(Curr, scan_detect); if (Vtero.VerboseOutput) { Console.WriteLine($"Detected PE @ VA {Curr:X}"); } } } } return(DetectedFragments.Count()); }
/// <summary> /// These properties combine together to formulate our database/cross reference between physical to virtual addresses /// </summary> /// <param name="RawEntry">PTE</param> /// <param name="va">Virtual Address</param> /// <param name="pageTable">CR3 address</param> /// <param name="sLAT">EPTP</param> public PFN(long RawEntry, long va, long pageTable, long sLAT) { PTE = new HARDWARE_ADDRESS_ENTRY(RawEntry); // this is the key into bitmap, since were never going to get past 32bit PFN // figures to make it only uint VA = va; PageTable.PTE = pageTable; SLAT.PTE = sLAT; SubTables = new Dictionary<VIRTUAL_ADDRESS, PFN>(); }
/// <summary> /// These properties combine together to formulate our database/cross reference between physical to virtual addresses /// </summary> /// <param name="RawEntry">PTE</param> /// <param name="va">Virtual Address</param> /// <param name="pageTable">CR3 address</param> /// <param name="sLAT">EPTP</param> public PFN(long RawEntry, long va, long pageTable, long sLAT) { PTE = new HARDWARE_ADDRESS_ENTRY(RawEntry); // this is the key into bitmap, since were never going to get past 32bit PFN // figures to make it only uint VA = va; PageTable.PTE = pageTable; SLAT.PTE = sLAT; SubTables = new Dictionary <VIRTUAL_ADDRESS, PFN>(); }
public bool IsDumpedPFN(HARDWARE_ADDRESS_ENTRY PTE) { if (PTE.PFN == 0xffffffffff) { return(true); } if (PTE.LargePage) { return(false); } return(DumpedPFNBitmap.GetBit((ulong)PTE.PFN)); }
// Extract a single page of data from a physical address in source dump // accout for memory gaps/run layout // TODO: Add windowing currently uses naieve single-page-at-a-time view public long GetPageForPhysAddr(HARDWARE_ADDRESS_ENTRY PAddr, ref long[] block) { // convert PAddr to PFN var PFN = PAddr.NextTable_PFN; // paranoid android setting var Fail = true; long IndexedPFN = 0; for (int i = 0; i < MD.NumberOfRuns; i++) { if (PFN >= MD.Run[i].BasePage && PFN < (MD.Run[i].BasePage + MD.Run[i].PageCount)) { var currBaseOffset = PFN - MD.Run[i].BasePage; IndexedPFN += currBaseOffset; Fail = false; break; } IndexedPFN += MD.Run[i].PageCount; } if (Fail) throw new MemoryRunMismatchException(PAddr.PTE); // Determine file offset based on indexed/gap adjusted PFN and page size var FileOffset = StartOfMemory + (IndexedPFN * PAGE_SIZE); // add back in the file offset for possiable exact byte lookup return GetPageFromFileOffset(FileOffset + PAddr.AddressOffset, ref block); }
/// <summary> /// Extract a single page of data from a physical address in source dump /// accounts for memory gaps/run layout /// </summary> /// <param name="PAddr">byte address an address contained in the block</param> /// <param name="block">array to be filled</param> /// <returns>specific return value for long value at </returns> public long GetPageForPhysAddr(HARDWARE_ADDRESS_ENTRY PAddr, ref long[] block) { bool GoodRead = false; return(GetPageForPhysAddr(PAddr, ref block, ref GoodRead)); }
public long hostPTE; // if we have SLAT and had the chance to de-virtualize, place the translated entry here public PFN() { PTE = long.MaxValue; }
/// <summary> /// Scan and return Extract objects which represent detected PE's /// /// TODO:simplify/rewrite this /// </summary> /// <param name="Start"></param> /// <param name="Stop">We just truncate VA's at 48 bit's</param> /// <returns>count of new detections since last Run</returns> public List <Extract> Run(long Start = 0, long Stop = 0xFFFFffffFFFF, PFN entry = null, ParallelLoopState pState = null, Mem Instance = null) { bool GotData = false; var memAxss = Instance == null ? BackingBlocks : Instance; long i = Start, Curr = 0; byte[] block; var rv = new List <Extract>(); // large page read if (entry != null && entry.PTE.LargePage) { block = new byte[MagicNumbers.LARGE_PAGE_SIZE]; memAxss.GetPageForPhysAddr(entry.PTE, ref block, ref GotData); if (GotData) { rv = FastPE(Start, block); } return(rv); } else // use supplied page sized physical entry if (entry != null && Stop - Start == MagicNumbers.PAGE_SIZE) { block = new byte[MagicNumbers.PAGE_SIZE]; memAxss.GetPageForPhysAddr(entry.PTE, ref block, ref GotData); if (GotData) { rv = FastPE(Start, block); } // we only header scan when asked and if the page read is 1 from an alignment if (!HeaderScan) { return(rv); } if ((Start & 0xF000) != MagicNumbers.PAGE_SIZE) { return(rv); } // if were doing a header scan back up i so we do the previous page i -= 0x1000; // back up Stop also so we just scan this current page one time Stop -= 0x1000; } // just use the virtual addresses and attempt to locate phys from page walk // this is a really slow way to enumerate memory // convert index to an address // then add start to it block = new byte[MagicNumbers.PAGE_SIZE]; // 0x200 * 8 = 4k while (i < Stop) { if (pState != null && pState.IsStopped) { return(rv); } HARDWARE_ADDRESS_ENTRY locPhys = HARDWARE_ADDRESS_ENTRY.MinAddr; if (DPContext.vmcs != null) { locPhys = memAxss.VirtualToPhysical(DPContext.vmcs.EPTP, DPContext.CR3Value, i); } else { locPhys = memAxss.VirtualToPhysical(DPContext.CR3Value, i); } Curr = i; i += MagicNumbers.PAGE_SIZE; if (HARDWARE_ADDRESS_ENTRY.IsBadEntry(locPhys) || !locPhys.Valid) { continue; } memAxss.GetPageForPhysAddr(locPhys, ref block, ref GotData); if (!GotData) { continue; } var new_pe = FastPE(Curr, block); rv.AddRange(new_pe); if (Vtero.VerboseOutput && new_pe.Count > 0) { Console.WriteLine($"Detected PE @ VA {Curr:X}"); } } return(rv); }
// Translates virtual address to physical address (normal CR3 path) // Since Valid & Read access overlap each other for EPT and normal PT go through this path for both public HARDWARE_ADDRESS_ENTRY VirtualToPhysical(HARDWARE_ADDRESS_ENTRY aCR3, long Addr) { var rv = HARDWARE_ADDRESS_ENTRY.MaxAddr; var va = new VIRTUAL_ADDRESS(Addr); var ConvertedV2P = new List <HARDWARE_ADDRESS_ENTRY>(); var Attempted = HARDWARE_ADDRESS_ENTRY.MinAddr; //Console.WriteLine($"V2P CR3 = {aCR3.PTE:X16} VA = {va}"); // PML4E try { Attempted = (HARDWARE_ADDRESS_ENTRY)aCR3.NextTableAddress | (va.PML4 << 3); var PML4E = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); //Console.WriteLine($"PML4E = {PML4E.PTE:X16}"); ConvertedV2P.Add(PML4E); if (PML4E.Valid) { Attempted = PML4E.NextTableAddress | (va.DirectoryPointerOffset << 3); var PDPTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2P.Add(PDPTE); //Console.WriteLine($"PDPTE = {PDPTE.PTE:X16}"); if (PDPTE.Valid) { if (!PDPTE.LargePage) { Attempted = PDPTE.NextTableAddress | (va.DirectoryOffset << 3); var PDE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2P.Add(PDE); //Console.WriteLine($"PDE = {PDE.PTE:X16}"); if (PDE.Valid) { if (!PDE.LargePage) { Attempted = PDE.NextTableAddress | (va.TableOffset << 3); var PTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2P.Add(PTE); //Console.WriteLine($"PTE = {PTE.PTE:X16}"); //rv = PTE; // page is normal 4kb if (PTE.Valid) { rv = PTE.PTE | (PTE.NextTableAddress | va.Offset); } else { rv.Valid = false; } } else { // we have a 2MB page rv = PDE.PTE | ((PDE.PTE & 0xFFFFFFE00000) | va.TableOffset << 12); } } else { rv.Valid = false; } } else { // we have a 1GB page rv = PDPTE.PTE | ((PDPTE.PTE & 0xFFFFC0000000) | va.DirectoryOffset << 12 << 9); //rv = PDPTE; } } else { rv.Valid = false; } } else { rv.Valid = false; } } catch (Exception ex) { rv.Valid = false; } finally { //foreach(var paddr in ConvertedV2P) //{ //} } //Console.WriteLine($"return from V2P {rv:X16}"); // serialize the dictionary out return(rv); }
public long GetValueAtPhysicalAddr(HARDWARE_ADDRESS_ENTRY PAddr) { long[] block = null; return GetPageForPhysAddr(PAddr, ref block); //return pageData[PAddr.AddressOffset >> 3]; }
// Extract a single page of data from a physical address in source dump // account for memory gaps/run layout // TODO: Add windowing currently uses naive single-page-at-a-time view public long GetPageForPhysAddr(HARDWARE_ADDRESS_ENTRY PAddr, ref long[] block) { // convert PAddr to PFN var PFN = PAddr.NextTable_PFN; if (PageCache.ContainsKey(PFN)) { if (PageCache.TryGetValue(PAddr, out block)) return block[PAddr & 0x1ff]; } // record our access attempt to the pfnIndex if (PFN > int.MaxValue || PFN > MD.MaxAddressablePageNumber) return 0; pfnTableIdx.Set((int)PFN, true); // paranoid android setting var Fail = true; long IndexedPFN = 0; for (int i = 0; i < MD.NumberOfRuns; i++) { if (PFN >= MD.Run[i].BasePage && PFN < (MD.Run[i].BasePage + MD.Run[i].PageCount)) { var currBaseOffset = PFN - MD.Run[i].BasePage; IndexedPFN += currBaseOffset; Fail = false; break; } IndexedPFN += MD.Run[i].PageCount; } if (Fail) throw new MemoryRunMismatchException(PAddr.PTE); // Determine file offset based on indexed/gap adjusted PFN and page size var FileOffset = StartOfMemory + (IndexedPFN * PAGE_SIZE); // add back in the file offset for possible exact byte lookup var rv = GetPageFromFileOffset(FileOffset + PAddr.AddressOffset, ref block); if(block != null) PageCache.TryAdd(PFN, block); return rv; }
public override long Seek(long offset, SeekOrigin origin) { HARDWARE_ADDRESS_ENTRY PhysAddr = HARDWARE_ADDRESS_ENTRY.MaxAddr; long DestAddr = long.MaxValue; switch(origin) { default: case SeekOrigin.Begin: DestAddr = offset; break; case SeekOrigin.End: DestAddr = Length - offset; break; case SeekOrigin.Current: DestAddr = position; DestAddr += offset; break; } PhysAddr = MemBlockStorage.VirtualToPhysical(Proc.vmcs.EPTP, Proc.CR3Value, DestAddr); if(PhysAddr == HARDWARE_ADDRESS_ENTRY.MinAddr || PhysAddr == HARDWARE_ADDRESS_ENTRY.MaxAddr || PhysAddr == HARDWARE_ADDRESS_ENTRY.MaxAddr-1) throw new PageNotFoundException($"unable to locate the physical page for the supplied virtual address {DestAddr}", PhysAddr, null, null); position = DestAddr; CurrPage = PhysAddr; return DestAddr; }
public IEnumerable <PFN> ExtractNextLevel(PFN PageContext, int Level = 4, bool RedundantKernelSpaces = false) { if (PageContext == null) { yield break; } var SLAT = Root.SLAT; var CR3 = Root.CR3; var top = Root.Entries; VIRTUAL_ADDRESS SubVA = PageContext.VA; HARDWARE_ADDRESS_ENTRY PA = PageContext.PTE; // get the page that the current PFN describes var HW_Addr = PA.NextTableAddress; if (SLAT != 0) { var hHW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hHW_Addr = mem.VirtualToPhysical(SLAT, HW_Addr); } catch (Exception ex) { if (Vtero.DiagOutput) { WriteLine($"level{Level}: Unable to V2P {HW_Addr}"); } } HW_Addr = hHW_Addr; if (HW_Addr == long.MaxValue || HW_Addr == long.MaxValue - 1) { yield break; } } if (PageContext.PTE.LargePage && Level <= 1) { // cyclic if (MemorizeTables) { PageContext.SubTables.Add(PageContext.VA, PageContext); } yield break; } long[] page = new long[512]; bool ReadData = false; // copy VA since were going to be making changes var valueRead = mem.GetPageForPhysAddr(HW_Addr, ref page, ref ReadData); if (!ReadData || page == null) { yield break; } // if every entry is exactially the same bail out var check1 = page[0]; if (Array.TrueForAll <long>(page, (entry) => entry == check1)) { yield break; } var dupVA = new VIRTUAL_ADDRESS(SubVA.Address); for (int i = 0; i < 512; i++) { // kernel indexes are only relevant on the top level if (Level == 4 && (!RedundantKernelSpaces && i >= MagicNumbers.KERNEL_PT_INDEX_START_USUALLY)) { continue; } if (page[i] == 0) { continue; } switch (Level) { case 4: dupVA.PML4 = i; break; case 3: dupVA.DirectoryPointerOffset = i; break; case 2: dupVA.DirectoryOffset = i; break; case 1: dupVA.TableOffset = i; break; default: break; } var pfn = new PFN { VA = new VIRTUAL_ADDRESS(dupVA.Address), PTE = new HARDWARE_ADDRESS_ENTRY(page[i]) }; if (MemorizeTables) { PageContext.SubTables.Add( pfn.VA, pfn); } EntriesParsed++; yield return(pfn); } yield break; }
public PageNotFoundException(string message, HARDWARE_ADDRESS_ENTRY lastPageAttempted, List <HARDWARE_ADDRESS_ENTRY> pagesFound, Exception ex) : base(message, ex) { PagesFound = pagesFound; LastPageAttempted = lastPageAttempted; }
/// <summary> /// An Inline extraction for the page table hierarchy. /// Why not let the compiler do it? I have code clones here? /// /// I guess so, but we can see/deal with the subtle differences at each level here as we implement them. /// e.g. some levels have LargePage bits and we may also lay over other CPU modes here like 32 in 64 etc.. /// </summary> /// <param name="top"></param> /// <param name="Level"></param> /// <returns></returns> long InlineExtract(PFN top, int Level) { if (Level == 0) return 0; var entries = 0L; var VA = new VIRTUAL_ADDRESS(top.VA); //WriteLine($"4: Scanning {top.PageTable:X16}"); var hPA = HARDWARE_ADDRESS_ENTRY.MinAddr; var SLAT = top.SLAT; var CR3 = top.PageTable; // pull level 4 entries attach level 3 foreach (var top_sub in top.SubTables) { // scan each page for the level 4 entry var PTE = top_sub.Value.PTE; // we don't need to | in the PML4 AO (address offset) since were pulling down the whole page not just the one value // and were going to brute force our way through the entire table so this was just confusing things. var l3HW_Addr = PTE.NextTableAddress ;// | top_sub.Key.PML4; // if we have an EPTP use it and request resolution of the HW_Addr if (SLAT != 0) { var hl3HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl3HW_Addr = mem.VirtualToPhysical(SLAT, l3HW_Addr); } catch (Exception) { if (Vtero.DiagOutput) WriteLine($"level3: Failed lookup {l3HW_Addr:X16}"); } l3HW_Addr = hl3HW_Addr; } if (SLAT != 0 && (l3HW_Addr == long.MaxValue || l3HW_Addr == long.MaxValue-1)) continue; // copy VA since were going to be making changes var s3va = new VIRTUAL_ADDRESS(top_sub.Key.Address); //WriteLine($"3: Scanning {s3va.Address:X16}"); top_sub.Value.hostPTE = l3HW_Addr; // cache translated value var lvl3_page = new long[512]; // extract the l3 page for each PTEEntry we had in l4 try { mem.GetPageForPhysAddr(l3HW_Addr, ref lvl3_page); } catch (Exception) { if (Vtero.DiagOutput) WriteLine($"level3: Failed lookup {l3HW_Addr:X16}"); } if (lvl3_page == null) continue; for (uint i3 = 0; i3 < 512; i3++) { if (lvl3_page[i3] == 0) continue; var l3PTE = new HARDWARE_ADDRESS_ENTRY(lvl3_page[i3]); // adjust VA to match extracted page entries s3va.DirectoryPointerOffset = i3; // save 'PFN' entry into sub-table I should really revisit all these names var l3PFN = new PFN(l3PTE, s3va.Address, CR3, SLAT); top_sub.Value.SubTables.Add(s3va, l3PFN); entries++; /// TODO: Double check if this is a real bit... /// I added it to help weed out some failure cases if (!l3PTE.LargePage) { // get the page that the current l3PFN describes var l2HW_Addr = l3PTE.NextTableAddress; if (SLAT != 0) { var hl2HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl2HW_Addr = mem.VirtualToPhysical(SLAT, l2HW_Addr); } catch (Exception ex) { if (Vtero.DiagOutput) WriteLine($"level2: Unable to V2P {l3PTE}"); } l2HW_Addr = hl2HW_Addr; } // TODO: more error handling of exceptions & bad returns // TODO: support software PTE types if (l2HW_Addr == HARDWARE_ADDRESS_ENTRY.MaxAddr) continue; l3PFN.hostPTE = l2HW_Addr; var lvl2_page = new long[512]; try { mem.GetPageForPhysAddr(l2HW_Addr, ref lvl2_page); } catch (Exception ex) { if (Vtero.DiagOutput) WriteLine($"level2: Failed lookup {l2HW_Addr:X16}"); } if (lvl2_page == null) continue; // copy VA var s2va = new VIRTUAL_ADDRESS(s3va.Address); //WriteLine($"2: Scanning {s2va.Address:X16}"); // extract PTE's for each set entry for (uint i2 = 0; i2 < 512; i2++) { if (lvl2_page[i2] == 0) continue; var l2PTE = new HARDWARE_ADDRESS_ENTRY(lvl2_page[i2]); s2va.DirectoryOffset = i2; var l2PFN = new PFN(l2PTE, s2va.Address, CR3, SLAT) { Type = PFNType.Data }; l3PFN.SubTables.Add(s2va, l2PFN); entries++; if (!l2PTE.LargePage) { var l1HW_Addr = l2PTE.NextTableAddress; if (SLAT != 0) { var hl1HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl1HW_Addr = mem.VirtualToPhysical(SLAT, l1HW_Addr); } catch (Exception ex) { if (Vtero.DiagOutput) WriteLine($"level1: Unable to V2P {l2PTE}"); } l1HW_Addr = hl1HW_Addr; } if (l1HW_Addr == HARDWARE_ADDRESS_ENTRY.MaxAddr) continue; l2PFN.hostPTE = l1HW_Addr; var lvl1_page = new long[512]; try { mem.GetPageForPhysAddr(l1HW_Addr, ref lvl1_page); } catch (Exception ex) { if(Vtero.DiagOutput) WriteLine($"level1: Failed lookup {l1HW_Addr:X16}"); } if (lvl1_page == null) continue; var s1va = new VIRTUAL_ADDRESS(s2va.Address); //WriteLine($"1: Scanning {s1va.Address:X16}"); for (uint i1 = 0; i1 < 512; i1++) { if (lvl1_page[i1] == 0) continue; var l1PTE = new HARDWARE_ADDRESS_ENTRY(lvl1_page[i1]); s1va.TableOffset = i1; var l1PFN = new PFN(l1PTE, s1va.Address, CR3, SLAT) { Type = PFNType.Data }; l2PFN.SubTables.Add(s1va, l1PFN); entries++; } } } } } } //top.PFNCount += entries; return entries; }
public PFN() { PTE = long.MaxValue; }
/// <summary> /// Get a long back for the address specified /// </summary> /// <param name="PAddr">physical address (byte address)</param> /// <returns>value</returns> public long GetValueAtPhysicalAddr(HARDWARE_ADDRESS_ENTRY PAddr) { bool Ignored = false; long[] block = new long[512]; return GetPageForPhysAddr(PAddr, ref block, ref Ignored); //return pageData[PAddr.AddressOffset >> 3]; }
public static bool IsBadEntry(HARDWARE_ADDRESS_ENTRY entry) { if (entry <= HARDWARE_ADDRESS_ENTRY.MinAddr + 2L || entry >= HARDWARE_ADDRESS_ENTRY.MaxAddr - 2L) return true; return false; }
/// <summary> /// An Inline extraction for the page table hierarchy. /// Why not let the compiler do it? I have code clones here? /// /// I guess so, but we can see/deal with the subtle differences at each level here as we implement them. /// e.g. some levels have LargePage bits and we may also lay over other CPU modes here like 32 in 64 etc.. /// </summary> /// <param name="top"></param> /// <param name="Level"></param> /// <returns></returns> long InlineExtract(PFN top, int Level) { if (Level == 0) { return(0); } var entries = 0L; var VA = new VIRTUAL_ADDRESS(top.VA); //WriteLine($"4: Scanning {top.PageTable:X16}"); var hPA = HARDWARE_ADDRESS_ENTRY.MinAddr; var SLAT = top.SLAT; var CR3 = top.PageTable; // pull level 4 entries attach level 3 foreach (var top_sub in top.SubTables) { // scan each page for the level 4 entry var PTE = top_sub.Value.PTE; // we don't need to | in the PML4 AO (address offset) since were pulling down the whole page not just the one value // and were going to brute force our way through the entire table so this was just confusing things. var l3HW_Addr = PTE.NextTableAddress; // | top_sub.Key.PML4; // if we have an EPTP use it and request resolution of the HW_Addr if (SLAT != 0) { var hl3HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl3HW_Addr = mem.VirtualToPhysical(SLAT, l3HW_Addr); } catch (Exception) { if (Vtero.DiagOutput) { WriteLine($"level3: Failed lookup {l3HW_Addr:X16}"); } } l3HW_Addr = hl3HW_Addr; } if (SLAT != 0 && (l3HW_Addr == long.MaxValue || l3HW_Addr == long.MaxValue - 1)) { continue; } // copy VA since were going to be making changes var s3va = new VIRTUAL_ADDRESS(top_sub.Key.Address); //WriteLine($"3: Scanning {s3va.Address:X16}"); top_sub.Value.hostPTE = l3HW_Addr; // cache translated value var lvl3_page = new long[512]; // extract the l3 page for each PTEEntry we had in l4 try { mem.GetPageForPhysAddr(l3HW_Addr, ref lvl3_page); } catch (Exception) { if (Vtero.DiagOutput) { WriteLine($"level3: Failed lookup {l3HW_Addr:X16}"); } } if (lvl3_page == null) { continue; } for (uint i3 = 0; i3 < 512; i3++) { if (lvl3_page[i3] == 0) { continue; } var l3PTE = new HARDWARE_ADDRESS_ENTRY(lvl3_page[i3]); // adjust VA to match extracted page entries s3va.DirectoryPointerOffset = i3; // save 'PFN' entry into sub-table I should really revisit all these names var l3PFN = new PFN(l3PTE, s3va.Address, CR3, SLAT); top_sub.Value.SubTables.Add(s3va, l3PFN); entries++; /// TODO: Double check if this is a real bit... /// I added it to help weed out some failure cases if (!l3PTE.LargePage) { // get the page that the current l3PFN describes var l2HW_Addr = l3PTE.NextTableAddress; if (SLAT != 0) { var hl2HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl2HW_Addr = mem.VirtualToPhysical(SLAT, l2HW_Addr); } catch (Exception ex) { if (Vtero.DiagOutput) { WriteLine($"level2: Unable to V2P {l3PTE}"); } } l2HW_Addr = hl2HW_Addr; } // TODO: more error handling of exceptions & bad returns // TODO: support software PTE types if (l2HW_Addr == HARDWARE_ADDRESS_ENTRY.MaxAddr) { continue; } l3PFN.hostPTE = l2HW_Addr; var lvl2_page = new long[512]; try { mem.GetPageForPhysAddr(l2HW_Addr, ref lvl2_page); } catch (Exception ex) { if (Vtero.DiagOutput) { WriteLine($"level2: Failed lookup {l2HW_Addr:X16}"); } } if (lvl2_page == null) { continue; } // copy VA var s2va = new VIRTUAL_ADDRESS(s3va.Address); //WriteLine($"2: Scanning {s2va.Address:X16}"); // extract PTE's for each set entry for (uint i2 = 0; i2 < 512; i2++) { if (lvl2_page[i2] == 0) { continue; } var l2PTE = new HARDWARE_ADDRESS_ENTRY(lvl2_page[i2]); s2va.DirectoryOffset = i2; var l2PFN = new PFN(l2PTE, s2va.Address, CR3, SLAT) { Type = PFNType.Data }; l3PFN.SubTables.Add(s2va, l2PFN); entries++; if (!l2PTE.LargePage) { var l1HW_Addr = l2PTE.NextTableAddress; if (SLAT != 0) { var hl1HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl1HW_Addr = mem.VirtualToPhysical(SLAT, l1HW_Addr); } catch (Exception ex) { if (Vtero.DiagOutput) { WriteLine($"level1: Unable to V2P {l2PTE}"); } } l1HW_Addr = hl1HW_Addr; } if (l1HW_Addr == HARDWARE_ADDRESS_ENTRY.MaxAddr) { continue; } l2PFN.hostPTE = l1HW_Addr; var lvl1_page = new long[512]; try { mem.GetPageForPhysAddr(l1HW_Addr, ref lvl1_page); } catch (Exception ex) { if (Vtero.DiagOutput) { WriteLine($"level1: Failed lookup {l1HW_Addr:X16}"); } } if (lvl1_page == null) { continue; } var s1va = new VIRTUAL_ADDRESS(s2va.Address); //WriteLine($"1: Scanning {s1va.Address:X16}"); for (uint i1 = 0; i1 < 512; i1++) { if (lvl1_page[i1] == 0) { continue; } var l1PTE = new HARDWARE_ADDRESS_ENTRY(lvl1_page[i1]); s1va.TableOffset = i1; var l1PFN = new PFN(l1PTE, s1va.Address, CR3, SLAT) { Type = PFNType.Data }; l2PFN.SubTables.Add(s1va, l1PFN); entries++; } } } } } } //top.PFNCount += entries; return(entries); }
/// <summary> /// Extract a single page of data from a physical address in source dump /// accounts for memory gaps/run layout /// </summary> /// <param name="PAddr">byte address an address contained in the block</param> /// <param name="block">array to be filled</param> /// <returns>specific return value for long value at </returns> public long GetPageForPhysAddr(HARDWARE_ADDRESS_ENTRY PAddr, ref long[] block) { bool GoodRead = false; return GetPageForPhysAddr(PAddr, ref block, ref GoodRead); }
// Translates virtual address to physical address (normal CR3 path) // Since Valid & Read access overlap each other for EPT and normal PT go through this path for both public HARDWARE_ADDRESS_ENTRY VirtualToPhysical(HARDWARE_ADDRESS_ENTRY aCR3, long Addr) { var rv = HARDWARE_ADDRESS_ENTRY.MaxAddr; var va = new VIRTUAL_ADDRESS(Addr); var ConvertedV2P = new List<HARDWARE_ADDRESS_ENTRY>(); var Attempted = HARDWARE_ADDRESS_ENTRY.MinAddr; //Console.WriteLine($"V2P CR3 = {aCR3.PTE:X16} VA = {va}"); // PML4E try { Attempted = (HARDWARE_ADDRESS_ENTRY) aCR3.NextTableAddress | (va.PML4 << 3); var PML4E = (HARDWARE_ADDRESS_ENTRY) GetValueAtPhysicalAddr(Attempted); //Console.WriteLine($"PML4E = {PML4E.PTE:X16}"); ConvertedV2P.Add(PML4E); if (PML4E.Valid) { Attempted = PML4E.NextTableAddress | (va.DirectoryPointerOffset << 3); var PDPTE = (HARDWARE_ADDRESS_ENTRY) GetValueAtPhysicalAddr(Attempted); ConvertedV2P.Add(PDPTE); //Console.WriteLine($"PDPTE = {PDPTE.PTE:X16}"); if (PDPTE.Valid) { if (!PDPTE.LargePage) { Attempted = PDPTE.NextTableAddress | (va.DirectoryOffset << 3); var PDE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2P.Add(PDE); //Console.WriteLine($"PDE = {PDE.PTE:X16}"); if (PDE.Valid) { if (!PDE.LargePage) { Attempted = PDE.NextTableAddress | (va.TableOffset << 3); var PTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2P.Add(PTE); //Console.WriteLine($"PTE = {PTE.PTE:X16}"); // page is normal 4kb if (PTE.Valid) rv = PTE.NextTableAddress | va.Offset; else rv.Valid = false; } else { // we have a 2MB page rv = (PDE.PTE & 0xFFFFFFE00000) | (Addr & 0x1FFFFF); } } else rv.Valid = false; } else { // we have a 1GB page rv = (PDPTE.PTE & 0xFFFFC0000000) | (Addr & 0x3FFFFFFF); } } else rv.Valid = false; } else rv.Valid = false; } catch (Exception ex) { throw new PageNotFoundException("V2P conversion error page not found", Attempted, ConvertedV2P, ex); } finally { foreach(var paddr in ConvertedV2P) { } } //Console.WriteLine($"return from V2P {rv:X16}"); // serialize the dictionary out return rv; }
public void SetDumpedPFN(HARDWARE_ADDRESS_ENTRY PTE) { DumpedPFNBitmap.SetBit((ulong)PTE.PFN); }
// Translates virtual address to physical address by way of CR3->EPTP double dereferencing (up to 24 loads) public HARDWARE_ADDRESS_ENTRY VirtualToPhysical(HARDWARE_ADDRESS_ENTRY eptp, HARDWARE_ADDRESS_ENTRY aCR3, long Addr) { var rv = HARDWARE_ADDRESS_ENTRY.MinAddr; var va = new VIRTUAL_ADDRESS(Addr); var gVa = new VIRTUAL_ADDRESS(aCR3.PTE); var Attempted = HARDWARE_ADDRESS_ENTRY.MinAddr; var ConvertedV2hP = new List <HARDWARE_ADDRESS_ENTRY>(); try { Attempted = gVa.Address; //convert Guest CR3 gPA into Host CR3 pPA var gpaCR3 = VirtualToPhysical(eptp, gVa.Address); //Console.WriteLine($"In V2P2P, using CR3 {aCR3.PTE:X16}, found guest phys CR3 {gpaCR3.PTE:X16}, attempting load of PML4E from {(gpaCR3 | va.PML4):X16}"); // gPML4E - as we go were getting gPA's which need to pPA Attempted = gpaCR3.NextTableAddress | va.PML4; var gPML4E = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPML4E); //Console.WriteLine($"guest PML4E = {gPML4E}"); // take CR3 and extract gPhys for VA we want to query var hPML4E = VirtualToPhysical(eptp, gPML4E.NextTableAddress); if (EPTP.IsValid(hPML4E.PTE) && EPTP.IsValid2(hPML4E.PTE) && HARDWARE_ADDRESS_ENTRY.IsBadEntry(hPML4E)) { Attempted = hPML4E.NextTableAddress | (va.DirectoryPointerOffset << 3); var gPDPTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPDPTE); var hPDPTE = VirtualToPhysical(eptp, gPDPTE.NextTableAddress); if (EPTP.IsValid(hPDPTE.PTE)) { if (!EPTP.IsLargePDPTE(hPDPTE.PTE)) { if (EPTP.IsValid2(hPDPTE.PTE)) { Attempted = hPDPTE.NextTableAddress | (va.DirectoryOffset << 3); var gPDE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPDE); var hPDE = VirtualToPhysical(eptp, gPDE.NextTableAddress); if (EPTP.IsValid(hPDE.PTE)) { if (!EPTP.IsLargePDE(hPDE.PTE)) { if (EPTP.IsValid2(hPDE.PTE)) { Attempted = hPDE.NextTableAddress | (va.TableOffset << 3); var gPTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPTE); var hPTE = VirtualToPhysical(eptp, gPTE.NextTableAddress); if (EPTP.IsValidEntry(hPTE.PTE)) { rv = hPTE.NextTableAddress | va.Offset; } } } else { rv = (hPDE.PTE & 0xFFFFFFE00000) | va.TableOffset; //(Addr & 0x1FFFFF); } } } } else { rv = (hPDPTE.PTE & 0xFFFFC0000000) | va.DirectoryOffset; //(Addr & 0x3FFFFFFF); } } } } catch (PageNotFoundException ex) { throw new ExtendedPageNotFoundException( $"V2gP2hP conversion error. EPTP:{eptp}, CR3:{aCR3}, Requesting:{Attempted} Step:{ConvertedV2hP.Count()}. Step of 0 may indicate invalid EPTP.{Environment.NewLine}" , eptp, aCR3, Attempted, ConvertedV2hP, ex); } return(rv); }
// Translates virtual address to physical address by way of CR3->EPTP double dereferencing (up to 24 loads) public HARDWARE_ADDRESS_ENTRY VirtualToPhysical(HARDWARE_ADDRESS_ENTRY eptp, HARDWARE_ADDRESS_ENTRY aCR3, long Addr) { var rv = HARDWARE_ADDRESS_ENTRY.MinAddr; var va = new VIRTUAL_ADDRESS(Addr); var gVa = new VIRTUAL_ADDRESS(aCR3.PTE); var Attempted = HARDWARE_ADDRESS_ENTRY.MinAddr; var ConvertedV2hP = new List<HARDWARE_ADDRESS_ENTRY>(); try { Attempted = gVa.Address; //convert Guest CR3 gPA into Host CR3 pPA var gpaCR3 = VirtualToPhysical(eptp, gVa.Address); // Validate page table. Possibly adjust for run gaps //var GapSize = ValidateAndGetGap(gpaCR3, aCR3); // let's just ignore failures for now //if (GapSize == long.MaxValue) //Debug.Print("Table verification error. YMMV."); long GapSize = 0; //} //Console.WriteLine($"In V2P2P, using CR3 {aCR3.PTE:X16}, found guest phys CR3 {gpaCR3.PTE:X16}, attemptng load of PML4E from {(gpaCR3 | va.PML4):X16}"); // gPML4E - as we go were getting gPA's which need to pPA Attempted = (gpaCR3.NextTableAddress - GapSize) | (va.PML4 << 3); var gPML4E = (HARDWARE_ADDRESS_ENTRY) GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPML4E); //Console.WriteLine($"guest PML4E = {gPML4E}"); // take CR3 and extract gPhys for VA we want to query var hPML4E = VirtualToPhysical(eptp, gPML4E.NextTableAddress); if (EPTP.IsValid(hPML4E.PTE) && EPTP.IsValid2(hPML4E.PTE)) { Attempted = (hPML4E.NextTableAddress - GapSize) | (va.DirectoryPointerOffset << 3); var gPDPTE = (HARDWARE_ADDRESS_ENTRY) GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPDPTE); var hPDPTE = VirtualToPhysical(eptp, gPDPTE.NextTableAddress); if (EPTP.IsValid(hPDPTE.PTE)) { if(!EPTP.IsLargePDPTE(hPDPTE.PTE)) { if (EPTP.IsValid2(hPDPTE.PTE)) { Attempted = (hPDPTE.NextTableAddress - GapSize) | (va.DirectoryOffset << 3); var gPDE = (HARDWARE_ADDRESS_ENTRY) GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPDE); var hPDE = VirtualToPhysical(eptp, gPDE.NextTableAddress); if (EPTP.IsValid(hPDE.PTE)) { if (!EPTP.IsLargePDE(hPDE.PTE)) { if (EPTP.IsValid2(hPDE.PTE)) { Attempted = (hPDE.NextTableAddress - GapSize) | (va.TableOffset << 3); var gPTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPTE); var hPTE = VirtualToPhysical(eptp, gPTE.NextTableAddress); if (EPTP.IsValidEntry(hPTE.PTE)) rv = (hPTE.NextTableAddress - GapSize) | va.Offset; } } else rv = (hPDE.PTE & 0xFFFFFFE00000) | (Addr & 0x1FFFFF); } } } else rv = (hPDPTE.PTE & 0xFFFFC0000000) | (Addr & 0x3FFFFFFF); } } } catch(PageNotFoundException ex) { throw new ExtendedPageNotFoundException( $"V2gP2hP conversion error. EPTP:{eptp}, CR3:{aCR3}, Requesting:{Attempted} Step:{ConvertedV2hP.Count()}. Step of 0 may indicate invalid EPTP.{Environment.NewLine}" , eptp, aCR3, Attempted, ConvertedV2hP, ex); } return rv; }
public PageNotFoundException(string message, HARDWARE_ADDRESS_ENTRY lastPageAttempted, List<HARDWARE_ADDRESS_ENTRY> pagesFound, Exception ex) : base(message, ex) { PagesFound = pagesFound; LastPageAttempted = lastPageAttempted; }
public ExtendedPageNotFoundException(string message, HARDWARE_ADDRESS_ENTRY eptpUsed, HARDWARE_ADDRESS_ENTRY cr3Used, HARDWARE_ADDRESS_ENTRY lastEPAttempted, List<HARDWARE_ADDRESS_ENTRY> ePFound, PageNotFoundException ex) : base(message, ex) { EPFound = ePFound; LastEPAttempted = lastEPAttempted; RequestedCR3 = cr3Used; RequestedEPTP = eptpUsed; }
public SLAT_ENTRY(HARDWARE_ADDRESS_ENTRY slat) { SLATEntry = slat.PTE; }
long InlineExtract(PFN top, int Level) { if (Level == 0) return 0; var entries = 0L; var VA = new VIRTUAL_ADDRESS(top.VA); var hPA = HARDWARE_ADDRESS_ENTRY.MinAddr; var CR3 = top.PageTable; var SLAT = top.SLAT; // pull level 4 entries attach level 3 foreach (var top_sub in top.SubTables) { // scan each page for the level 4 entry var PTE = top_sub.Value.PTE; var l3HW_Addr = PTE.NextTableAddress | top_sub.Key.PML4; // if we have an EPTP use it and request resolution of the HW_Addr if (SLAT != 0) { var hl3HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; hl3HW_Addr = mem.VirtualToPhysical(SLAT, l3HW_Addr); l3HW_Addr = hl3HW_Addr; } if (l3HW_Addr == long.MaxValue) continue; // copy VA since were going to be making changes var s3va = new VIRTUAL_ADDRESS(top_sub.Key.Address); top_sub.Value.hostPTE = l3HW_Addr; // cache translated value var lvl3_page = new long[512]; // extract the l3 page for each PTEEntry we had in l4 try { mem.GetPageForPhysAddr(l3HW_Addr, ref lvl3_page); } catch (Exception) { WriteLine($"level3: Failed lookup {l3HW_Addr:X16}"); } if (lvl3_page == null) continue; for (uint i3 = 0; i3 < 512; i3++) { if (lvl3_page[i3] == 0) continue; var l3PTE = new HARDWARE_ADDRESS_ENTRY(lvl3_page[i3]); // adjust VA to match extracted page entries s3va.DirectoryPointerOffset = i3; // save 'PFN' entry into sub-table I should really revisit all these names var l3PFN = new PFN(l3PTE, s3va.Address, CR3, SLAT); top_sub.Value.SubTables.Add(s3va, l3PFN); entries++; // get the page that the current l3PFN describes var l2HW_Addr = l3PTE.NextTableAddress; if (SLAT != 0) { var hl2HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl2HW_Addr = mem.VirtualToPhysical(SLAT, l2HW_Addr); } catch(Exception ex) { WriteLine($"level2: Unable to V2P {l3PTE}"); } l2HW_Addr = hl2HW_Addr; } // TODO: more error handlng of exceptions & bad return's // TODO: support software PTE types if (l2HW_Addr == HARDWARE_ADDRESS_ENTRY.MaxAddr) continue; l3PFN.hostPTE = l2HW_Addr; var lvl2_page = new long[512]; try { mem.GetPageForPhysAddr(l2HW_Addr, ref lvl2_page); } catch (Exception ex) { WriteLine($"level2: Failed lookup {l2HW_Addr:X16}"); } if (lvl2_page == null) continue; // copy VA var s2va = new VIRTUAL_ADDRESS(s3va.Address); // extract PTE's for each set entry for (uint i2 = 0; i2 < 512; i2++) { if (lvl2_page[i2] == 0) continue; var l2PTE = new HARDWARE_ADDRESS_ENTRY(lvl2_page[i2]); s2va.DirectoryOffset = i2; var l2PFN = new PFN(l2PTE, s2va.Address, CR3, SLAT); l3PFN.SubTables.Add(s2va, l2PFN); entries++; if (!l2PTE.LargePage) { var l1HW_Addr = l2PTE.NextTableAddress; if (SLAT != 0) { var hl1HW_Addr = HARDWARE_ADDRESS_ENTRY.MaxAddr; try { hl1HW_Addr = mem.VirtualToPhysical(SLAT, l1HW_Addr); } catch (Exception ex) { WriteLine($"level1: Unable to V2P {l2PTE}"); } l1HW_Addr = hl1HW_Addr; } if (l1HW_Addr == HARDWARE_ADDRESS_ENTRY.MaxAddr) continue; l2PFN.hostPTE = l1HW_Addr; var lvl1_page = new long[512]; try { mem.GetPageForPhysAddr(l1HW_Addr, ref lvl1_page); } catch (Exception ex) { WriteLine($"level1: Failed lookup {l1HW_Addr:X16}"); } if (lvl1_page == null) continue; var s1va = new VIRTUAL_ADDRESS(s2va.Address); for (uint i1 = 0; i1 < 512; i1++) { if (lvl1_page[i1] == 0) continue; var l1PTE = new HARDWARE_ADDRESS_ENTRY(lvl1_page[i1]); s1va.TableOffset = i1; var l1PFN = new PFN(l1PTE, s1va.Address, CR3, SLAT); l2PFN.SubTables.Add(s1va, l1PFN); entries++; } } } } } top.PFNCount += entries; return entries; }
public long GetPageForPhysAddr(HARDWARE_ADDRESS_ENTRY PAddr, ref long[] block, ref bool GotData) { long rv = 0; // convert PAddr to PFN var aPFN = PAddr.NextTable_PFN; GotData = false; // should return with - error_value // This happens quite a bit and is a good boost // I guess were constrained by int.MaxValue pages here. // so that is about 8TB // TODO: explore 5 level page tables and larger than 8TB inputs :) if (aPFN > int.MaxValue || aPFN < 0) return 0; #if USE_BITMAP if(pfnTableIdx != null) pfnTableIdx.Set((int)PFN, true); #endif // paranoid android setting var FileOffset = OffsetToMemIndex(aPFN); if (FileOffset >= 0) rv = GetPageFromFileOffset(FileOffset + PAddr.AddressOffset, ref block, ref GotData); if (!GotData) rv = MagicNumbers.BAD_VALUE_READ; return rv; }