public static PageTable AddProcess(DetectedProc dp, Mem mem, bool RedundantKernelEntries = true, int DepthToGo = 4) { if(Vtero.DiagOutput) WriteLine($"PT analysis of {dp}"); var rv = new PageTable { Root = new PageTableRoot() { SLAT = dp.vmcs != null ? dp.vmcs.EPTP : 0, CR3 = dp.CR3Value, Entries = new PFN() }, Failed = new List<HARDWARE_ADDRESS_ENTRY>(), DP = dp, mem = mem }; if (dp.MemAccess == null || mem != null) dp.MemAccess = mem; /* Commenting out this block so we defer the table enumeration // any output is error/warning output var cnt = rv.FillTable(RedundantKernelEntries, DepthToGo); rv.EntriesParsed += cnt; if (cnt == 0) { if (dp.vmcs != null) WriteLine($"BAD EPTP/DirectoryTable Base {dp.vmcs.EPTP:X12}, try a different candidate or this dump may lack a hypervisor. Recommend attempt PT walk W/O SLAT"); else WriteLine($"Decoding failed for {dp.CR3Value:X12}"); //WriteLine($"Physical walk w/o SLAT yielded {cnt} entries"); } */ dp.PT = rv; return rv; }
public static PageTable AddProcess(DetectedProc dp, Mem mem) { long Address = 0; int AddressIndex = 0; // dump Page table high to low var va = new VIRTUAL_ADDRESS(long.MaxValue - 0xfff); var rv = new PageTable { Failed = new List<HARDWARE_ADDRESS_ENTRY>(), DP = dp, mem = mem }; // TODO: encode VA's for self/recursive physical addr's if (dp.PageTableType == PTType.Windows) { Address = MagicNumbers.Windows_SelfAsVA; AddressIndex = MagicNumbers.Windows_SelfPtr; } // any output is error/warning output var cnt = rv.FillTable(new VIRTUAL_ADDRESS(Address), AddressIndex, dp.CR3Value); Debug.WriteLine($"extracted {cnt} PTE from process {dp.vmcs.EPTP:X16}:{dp.CR3Value:X16}"); dp.PT = rv; return rv; }
public VirtualScanner(DetectedProc Ctx, Mem backingBlocks) : this() { DPContext = Ctx; BackingBlocks = backingBlocks; BackingStream = new PhysicalMemoryStream(backingBlocks, Ctx); //mn ,n m,MemoryBank = new ConcurrentDictionary<int, Mem>(); //for(int i=0; i< Environment.ProcessorCount; i++) //MemoryBank[i] = new Mem(BackingBlocks); }
public static PageTable AddProcess(DetectedProc dp, Mem mem, bool OnlyUserSpace = false) { long Address = 0; int AddressIndex = 0; // dump Page table high to low var va = new VIRTUAL_ADDRESS(long.MaxValue - 0xfff); var rv = new PageTable { Failed = new List<HARDWARE_ADDRESS_ENTRY>(), DP = dp, mem = mem }; // TODO: encode VA's for self/recursive physical addr's if (dp.PageTableType == PTType.Windows) { Address = MagicNumbers.Windows_SelfAsVA; AddressIndex = MagicNumbers.Windows_SelfPtr; } // any output is error/warning output var cnt = rv.FillTable(new VIRTUAL_ADDRESS(Address), AddressIndex, dp.CR3Value, OnlyUserSpace); if (cnt == 0) { if (dp.vmcs != null) WriteLine($"BAD EPTP/DirectoryTable Base {dp.vmcs.EPTP:X16}, try a different candidate or this dump may lack a hypervisor. Attempting PT walk W/O SLAT"); else WriteLine($"Decoding failed for {dp.CR3Value:X16}"); /*cnt = rv.FillTable(new VIRTUAL_ADDRESS(Address), AddressIndex, dp.CR3Value, OnlyUserSpace); WriteLine($"Physical walk w/o SLAT yielded {cnt} entries");*/ } dp.PT = rv; return rv; }
public void DumpASToFile(List<DetectedProc> ToDump) { using (var memAxs = new Mem(MemFile, null, DetectedDesc)) { // in all likelyhood this is the kernel var tdp = (from p in ToDump where p.AddressSpaceID == 1 orderby p.CR3Value ascending select p).Take(1).First(); // as a test let's find the process with the most to dump /*var tdp = (from p in ToDump where p.AddressSpaceID == 1 orderby p.PT.RootPageTable.PFNCount descending select p).Take(1).First(); */ var pml4 = tdp.PT.RootPageTable; WriteLine($"Test dumping {tdp}, {pml4.PFNCount} entries scanned."); var MemRanges = pml4.SubTables.SelectMany(x => x.Value.SubTables).SelectMany(y => y.Value.SubTables).SelectMany(z => z.Value.SubTables); // WriteLine($"MemRanges = {MemRanges.Count()} available."); var saveLoc = Path.Combine(Path.GetDirectoryName(MemFile), Path.GetFileName(MemFile) + "."); long[] block = new long[0x200]; // 0x200 * 8 = 4k byte[] bpage = new byte[0x1000]; unsafe { fixed (void * lp = block, bp = bpage) { foreach (var pte in MemRanges) { if (VerboseOutput) WriteLine($"VA: {pte.Key:X16} \t PFN: {pte.Value.PTE}"); if (pte.Value.PTE.LargePage) { using (var lsavefile = File.OpenWrite(saveLoc + pte.Key.Address.ToString("X") + ".bin")) // 0x200 * 4kb = 2MB for (int i = 0; i < 0x200; i++) { try { memAxs.GetPageForPhysAddr(pte.Value.PTE, ref block); } catch (Exception ex) { } pte.Value.PTE.PTE += (i * 0x1000); if (block == null) continue; Buffer.MemoryCopy(lp, bp, 4096, 4096); lsavefile.Write(bpage, 0, 4096); } } else { try { memAxs.GetPageForPhysAddr(pte.Value.PTE, ref block); } catch (Exception ex) { } if (block == null) continue; using (var savefile = File.OpenWrite(saveLoc + pte.Key.Address.ToString("X") + ".bin")) { Buffer.MemoryCopy(lp, bp, 4096, 4096); savefile.Write(bpage, 0, 4096); } } } } } } }
/// <summary> /// This routine is fairly expensive, maybe unnecessary as well but it demo's walking the page table + EPT. /// You can connect an address space dumper really easily /// </summary> /// <param name="MemSpace">The list of VMCS/EPTP configurations which will alter the page table use</param> /// <param name="Procs">Detected procs to query</param> /// <param name="pTypes">Type bitmas to interpreate</param> public List<DetectedProc> ExtrtactAddressSpaces(IOrderedEnumerable<VMCS> MemSpace = null, ConcurrentBag<DetectedProc> Procs = null, PTType pTypes = PTType.UNCONFIGURED) { List<DetectedProc> rvList = new List<DetectedProc>(); var PT2Scan = pTypes == PTType.UNCONFIGURED ? (PTType.Windows | PTType.HyperV | PTType.GENERIC) : pTypes; var procList = Procs == null ? Processes : Procs; //var memSpace = MemSpace == null ? VMCSs.First() : MemSpace.First(); var memSpace = MemSpace == null ? VMCSs.GroupBy(x => x.EPTP).Select(xg => xg.First()) : MemSpace; var p = from proc in Processes where (((proc.PageTableType & PT2Scan) == proc.PageTableType)) orderby proc.CR3Value ascending select proc; int pcnt = Processes.Count(); int vmcnt = memSpace.Count(); var tot = pcnt * vmcnt; var curr = 0; var AlikelyKernelSet = from ptes in p.First().TopPageTablePage where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key) select ptes.Value; Console.ForegroundColor = ConsoleColor.Yellow; WriteLine($"assessing {tot} address spaces"); ProgressBarz.Progress = 0; var VMCSTriage = new Dictionary<VMCS, int>(); //Parallel.ForEach(memSpace, (space) => foreach (var space in memSpace) { // we do it this way so that parallelized tasks do not interfere with each other // overall it may blow the cache hit ratio but will tune a single task to see the larger/better cache // versus multicore, my suspicion is that multi-core is better using (var memAxs = new Mem(MemFile, null, DetectedDesc)) { var sx = 0; foreach (var px in p) //Parallel.ForEach(p, (proc) => { try { var proc = px.Clone<DetectedProc>(); proc.vmcs = space; var pt = PageTable.AddProcess(proc, memAxs, false); if (pt != null && VerboseOutput) { // If we used group detection correlation a valid EPTP should work for every process if (proc.vmcs != null && proc.PT.RootPageTable.PFNCount > proc.TopPageTablePage.Count()) { WriteLine($"Virtualized Process PT Entries [{proc.PT.RootPageTable.PFNCount}] Type [{proc.PageTableType}] PID [{proc.vmcs.EPTP:X}:{proc.CR3Value:X}]"); rvList.Add(proc); } else { WriteLine($"canceling evaluation of bad EPTP for this group"); foreach (var pxc in Processes) pxc.vmcs = null; break; } sx++; curr++; var progress = Convert.ToInt32((Convert.ToDouble(curr) / Convert.ToDouble(tot) * 100.0) + 0.5); ProgressBarz.RenderConsoleProgress(progress); } } catch (ExtendedPageNotFoundException eptpX) { WriteLine($"Bad EPTP selection;{Environment.NewLine}\tEPTP:{eptpX.RequestedEPTP}{Environment.NewLine}\t CR3:{eptpX.RequestedCR3}{Environment.NewLine} Attempting to skip to next proc."); memAxs.DumpPFNIndex(); } catch (MemoryRunMismatchException mrun) { WriteLine($"Error in accessing memory for PFN {mrun.PageRunNumber:X16}"); memAxs.DumpPFNIndex(); } catch (PageNotFoundException pnf) { WriteLine($"Error in selecting page, see {pnf}"); memAxs.DumpPFNIndex(); } catch (Exception ex) { WriteLine($"Error in memspace extraction: {ex.ToString()}"); memAxs.DumpPFNIndex(); } WriteLine($"{sx} VMCS dominated process address spaces and were decoded succsessfully."); //}); } } } //}); using (var memAxs = new Mem(MemFile, null, DetectedDesc)) { var nonVMCSprocs = from px in Processes where px.vmcs == null select px; foreach (var px in nonVMCSprocs) { var pmetal = px.Clone<DetectedProc>(); // this is a process on the bare metal var pt = PageTable.AddProcess(pmetal, memAxs, false); WriteLine($"Process {pmetal.CR3Value:X16} Physical walk w/o SLAT yielded {pmetal.PT.RootPageTable.PFNCount} entries"); rvList.Add(pmetal); } } return rvList; }
/// <summary> /// This routine is fairly expensive, maybe unnessisary as well but it demo's walking the page table + EPT. /// You can connect an address space dumper really easially /// </summary> /// <param name="MemSpace">The list of VMCS/EPTP configurations which will alter the page table use</param> /// <param name="Procs">Detected procs to query</param> /// <param name="pTypes">Type bitmas to interpreate</param> public void ExtrtactAddressSpaces(IOrderedEnumerable<VMCS> MemSpace = null, ConcurrentBag<DetectedProc> Procs = null, PTType pTypes = PTType.UNCONFIGURED) { var PT2Scan = pTypes == PTType.UNCONFIGURED ? (PTType.Windows | PTType.HyperV | PTType.GENERIC) : pTypes; var procList = Procs == null ? Processes : Procs; //var memSpace = MemSpace == null ? VMCSs.First() : MemSpace.First(); var memSpace = MemSpace == null ? VMCSs.GroupBy(x => x.EPTP).Select(xg => xg.First()) : MemSpace; //.Where(xw => xw.All(xz => xz.EPTP == 0x1138601E)) var p = from proc in Processes where (((proc.PageTableType & PT2Scan) == proc.PageTableType)) orderby proc.CR3Value ascending select proc; int pcnt = Processes.Count(); int vmcnt = memSpace.Count(); int tot = pcnt * vmcnt; int curr = 0; var AlikelyKernelSet = from ptes in p.First().TopPageTablePage where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key) select ptes.Value; Console.ForegroundColor = ConsoleColor.Yellow; WriteLine($"assessing {tot} address spaces"); ProgressBarz.Progress = 0; Parallel.ForEach(memSpace, (space) => //foreach (var space in memSpace) { using (var memAxs = new Mem(MemFile)) { var sx = 0; //foreach (var proc in p) Parallel.ForEach(p, (proc) => { try { proc.vmcs = space; var pt = PageTable.AddProcess(proc, memAxs); if (pt != null && VerboseOutput) { var currKern = from ptes in proc.TopPageTablePage where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key) select ptes.Value; var interSection = currKern.Intersect(AlikelyKernelSet); var corralated = interSection.Count() * 1.00 / AlikelyKernelSet.Count(); WriteLine($"PT Entries [{proc.PT.RootPageTable.PFNCount}] Type [{proc.PageTableType}] GroupCorrelation [{corralated:P3}] PID [{proc.vmcs.EPTP:X}:{proc.CR3Value:X}]"); sx++; curr++; var progress = Convert.ToInt32((Convert.ToDouble(curr) / Convert.ToDouble(tot) * 100.0) + 0.5); ProgressBarz.RenderConsoleProgress(progress); } } catch (ExtendedPageNotFoundException eptpX) { WriteLine($"Bad EPTP selection;{Environment.NewLine}\tEPTP:{eptpX.RequestedEPTP}{Environment.NewLine}\t CR3:{eptpX.RequestedCR3}{Environment.NewLine} Attempting to skip to next proc."); } catch (MemoryRunMismatchException mrun) { WriteLine($"Error in accessing memory for PFN {mrun.PageRunNumber:X16}"); } catch (PageNotFoundException pnf) { WriteLine($"Error in selecting page, see {pnf}"); } catch(Exception ex) { WriteLine($"Error in memspace extraction: {ex.ToString()}"); } WriteLine($"{sx} VMCS dominated process address spaces and were decoded succsessfully."); }); //} } //} }); }
// TODO: Figure out if MemoryCopy or BlockCopy ... public string WriteRange(VIRTUAL_ADDRESS KEY, PFN VALUE, string BaseFileName, Mem PhysMemReader = null, bool SinglePFNStore = false, bool DumpNULL = false) { if (SinglePFNStore && SISmap == null) SISmap = new WAHBitArray(); if(SinglePFNStore) { if (SISmap.Get((int)VALUE.PTE.PFN)) return string.Empty; SISmap.Set((int)VALUE.PTE.PFN, true); } bool canAppend = false; var saveLoc = BaseFileName + KEY.Address.ToString("X") + ".bin"; var lastLoc = BaseFileName + (KEY.Address - ContigSize).ToString("X") + ".bin"; if (File.Exists(lastLoc)) { canAppend = true; ContigSize += 0x1000; saveLoc = lastLoc; } else ContigSize = 0x1000; //unsafe //{ var block = new long[0x200]; // 0x200 * 8 = 4k var bpage = new byte[0x1000]; //fixed (void* lp = block, bp = bpage) //{ if (DiagOutput) WriteColor(VALUE.PTE.Valid ? ConsoleColor.Cyan : ConsoleColor.Red, $"VA: {KEY:X12} \t PFN: {VALUE.PTE}"); // if we have invalid (software managed) page table entries // the data may be present, or a prototype or actually in swap. // for the moment were only going to dump hardware managed data // or feel free to patch this up ;) if (!VALUE.PTE.Valid) return string.Empty; if (VALUE.PTE.LargePage) { bool GoodRead = false; using (var lsavefile = File.OpenWrite(saveLoc)) { // 0x200 * 4kb = 2MB // TODO: Large pages properly? // TODO: PageCache is still broken in some cases... disable for now here for (int i = 0; i < 0x200; i++) { PhysMemReader.GetPageForPhysAddr(VALUE.PTE, ref block, ref GoodRead); VALUE.PTE.PTE += 0x1000; if(!GoodRead) block = new long[0x200]; Buffer.BlockCopy(block, 0, bpage, 0, 4096); //Buffer.MemoryCopy(lp, bp, 4096, 4096); lsavefile.Write(bpage, 0, 4096); //lsavefile.Write(bpage, 0, 4096); } return lastLoc; } } else { try { PhysMemReader.GetPageForPhysAddr(VALUE.PTE, ref block); } catch (Exception ex) { } if (block != null) { if (DumpNULL || !UnsafeHelp.IsZero(block)) { Buffer.BlockCopy(block, 0, bpage, 0, 4096); //Buffer.MemoryCopy(lp, bp, 4096, 4096); using (var savefile = (canAppend ? File.Open(lastLoc, FileMode.Append, FileAccess.Write, FileShare.ReadWrite) : File.OpenWrite(saveLoc))) savefile.Write(bpage, 0, 4096); //savefile.Write(bpage, 0, 4096); return lastLoc; } } } ContigSize = 0; return string.Empty; //} //} }
public void AddProcessPageTable(DetectedProc tdp, Mem memAxs) { PageTable.AddProcess(tdp, memAxs, true); }
/// <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; long[] block; var rv = new List <Extract>(); // large page read if (entry != null && entry.PTE.LargePage) { block = new long[0x40000]; 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 long[0x200]; 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) != 0x1000) { 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 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); } Curr = i; i += 0x1000; 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); }
public PhysicalMemoryStream(Mem blockStorage, DetectedProc proc) { MemBlockStorage = blockStorage; Proc = proc; }
public PhysicalMemoryStream(Mem blockStorage, DetectedProc proc) { MemBlockStorage = blockStorage; Proc = proc; }
public static Mem InitMem(String mFile, AMemoryRunDetector Detector, uint[] BitmapArray = null) //: this() { var thiz = new Mem(); thiz.StartOfMemory = Detector != null ? Detector.StartOfMem : 0; if (Detector != null) { thiz.StartOfMemory = Detector.StartOfMem; thiz.MD = Detector; } #if USE_BITMAP // maybe there's a bit map we can use from a DMP file if (BitmapArray != null) pfnTableIdx = new WAHBitArray(WAHBitArray.TYPE.Bitarray, BitmapArray); else pfnTableIdx = new WAHBitArray(); // 32bit's of pages should be plenty? pfnTableIdx.Length = (int) (MapViewSize / 0x1000); #endif if (File.Exists(mFile)) { thiz.MemoryDump = mFile; thiz.FileSize = new FileInfo(mFile).Length; if (Detector != null) thiz.MD = Detector; } thiz.SetupStreams(); return thiz; }
public Mem(Mem parent) : this() { MD = parent.MD; StartOfMemory = parent.StartOfMemory; MemoryDump = parent.MemoryDump; FileSize = parent.FileSize; SetupStreams(); }