/// <summary> /// /// Pretty much not used any more, but I guess I can leave it in for a bit. Trying to decide /// if I should focus on the core and release a bunch of .csx scripts seems everybody likes scripts these days /// /// Or maybe write a UI.... hmmmm /// /// /// 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> /// // TODO: RE-RE-Write this into an on-demand evaluated set of delegates to ease memory load // some testing on Windows 10: Virtualized Process PT Entries [1625181] Type [Windows] PID [97C0301E:1AB000] // that's over _1.6 Million_ page table entries, wow!!! long InlineExtract(PageTableRoot Root, int Depth = 4) { VIRTUAL_ADDRESS VAddr; var entries = 0L; var SLAT = Root.SLAT; var CR3 = Root.CR3; var top = Root.Entries; // pull level 4 entries attach level 3 foreach (var top_sub in top.SubTables) { //WriteLine($"4: Scanning {top_sub.Value.PTE:X16}"); // 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; // 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); 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; //WriteLine($"3: Scanning VA {s3va.Address:X16}"); // save 'PFN' entry into sub-table I should really revisit all these names VAddr = new VIRTUAL_ADDRESS(s3va.Address); var l3PFN = new PFN() { PTE = l3PTE, VA = VAddr }; 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; } 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); // 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; ///WriteLine($"2: Scanning VA {s2va.Address:X16}"); VAddr = new VIRTUAL_ADDRESS(s2va.Address); var l2PFN = new PFN() { PTE = l2PTE, VA = VAddr }; l3PFN.SubTables.Add(s2va, l2PFN); entries++; if (!l2PTE.LargePage && !KernelSpace) { 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; } 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); 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; //WriteLine($"1: Scanning VA {s1va.Address:X16}"); // copy this since were in a loop scanning and it (VA) changes every time VAddr = new VIRTUAL_ADDRESS(s1va.Address); var l1PFN = new PFN() { PTE = l1PTE, VA = VAddr }; l2PFN.SubTables.Add(s1va, l1PFN); entries++; } } } } } } //top.PFNCount += entries; return(entries); }
/// <summary> /// /// Pretty much not used any more, but I guess I can leave it in for a bit. Trying to decide /// if I should focus on the core and release a bunch of .csx scripts seems everybody likes scripts these days /// /// Or maybe write a UI.... hmmmm /// /// /// 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> /// // TODO: RE-RE-Write this into an on-demand evaluated set of delegates to ease memory load // some testing on Windows 10: Virtualized Process PT Entries [1625181] Type [Windows] PID [97C0301E:1AB000] // that's over _1.6 Million_ page table entries, wow!!! long InlineExtract(PageTableRoot Root, int Depth = 4) { VIRTUAL_ADDRESS VAddr; var entries = 0L; var SLAT = Root.SLAT; var CR3 = Root.CR3; var top = Root.Entries; // pull level 4 entries attach level 3 foreach (var top_sub in top.SubTables) { //WriteLine($"4: Scanning {top_sub.Value.PTE:X16}"); // 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; // 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); 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; //WriteLine($"3: Scanning VA {s3va.Address:X16}"); // save 'PFN' entry into sub-table I should really revisit all these names VAddr = new VIRTUAL_ADDRESS(s3va.Address); var l3PFN = new PFN() { PTE = l3PTE, VA = VAddr }; 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; 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); // 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; ///WriteLine($"2: Scanning VA {s2va.Address:X16}"); VAddr = new VIRTUAL_ADDRESS(s2va.Address); var l2PFN = new PFN() { PTE = l2PTE, VA = VAddr }; l3PFN.SubTables.Add(s2va, l2PFN); entries++; if (!l2PTE.LargePage && !KernelSpace) { 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; 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); 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; //WriteLine($"1: Scanning VA {s1va.Address:X16}"); // copy this since were in a loop scanning and it (VA) changes every time VAddr = new VIRTUAL_ADDRESS(s1va.Address); var l1PFN = new PFN() { PTE = l1PTE, VA = VAddr }; l2PFN.SubTables.Add(s1va, l1PFN); entries++; } } } } } } //top.PFNCount += entries; return entries; }