static void LowerBoundOutput() { const int BarHeight = 5; var MaxText = (WindowTop + WindowHeight - 1) - BarHeight; if (CursorTop >= MaxText) { BackgroundColor = ConsoleColor.Black; var newlines = CursorTop % MaxText; for (int i = 0; i < newlines; i++) { WriteLine(" ".PadRight(WindowWidth)); } CursorTop = MaxText - 1; } ProgressBarz.RenderConsoleProgress(ProgressBarz.Progress); }
/// <summary> /// A simple memory mapped scan over the input provided in the constructor /// </summary> /// <param name="ExitAfter">Optionally stop checking or exit early after this many candidates. 0 does not exit early.</param> /// <returns></returns> public int Analyze(int ExitAfter = 0) { CurrWindowBase = 0; mapSize = (64 * 1024 * 1024); if (File.Exists(Filename)) { using (var fs = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var mapName = Path.GetFileNameWithoutExtension(Filename) + DateTime.Now.ToBinary().ToString("X16"); using (var mmap = MemoryMappedFile.CreateFromFile(fs, mapName, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.Inheritable, false)) { if (FileSize == 0) { FileSize = new FileInfo(Filename).Length; } while (CurrWindowBase < FileSize) { using (var reader = mmap.CreateViewAccessor(CurrWindowBase, mapSize, MemoryMappedFileAccess.Read)) { CurrMapBase = 0; reader.ReadArray(CurrMapBase, buffers[filled], 0, 512); while (CurrMapBase < mapSize) { var offset = CurrWindowBase + CurrMapBase; // next page, may be faster with larger chunks but it's simple to view 1 page at a time CurrMapBase += 4096; block = buffers[filled]; filled ^= 1; #pragma warning disable HeapAnalyzerImplicitParamsRule // Array allocation for params parameter Parallel.Invoke(() => Parallel.ForEach <Func <long, bool> >(CheckMethods, (check) => { check(offset); }), () => { if (CurrMapBase < mapSize) { UnsafeHelp.ReadBytes(reader, CurrMapBase, ref buffers[filled]); } } ); if (ExitAfter > 0 && ExitAfter == DetectedProcesses.Count()) { return(DetectedProcesses.Count()); } var progress = Convert.ToInt32((Convert.ToDouble(CurrWindowBase) / Convert.ToDouble(FileSize) * 100.0) + 0.5); if (progress != ProgressBarz.Progress) { ProgressBarz.RenderConsoleProgress(progress); } } } // close current window CurrWindowBase += CurrMapBase; if (CurrWindowBase + mapSize > FileSize) { mapSize = FileSize - CurrWindowBase; } } } } // close map } // close stream return(DetectedProcesses.Count()); }
/// <summary> /// A simple memory mapped scan over the input provided in the constructor /// </summary> /// <param name="ExitAfter">Optionally stop checking or exit early after this many candidates. 0 does not exit early.</param> /// <returns></returns> public int Analyze(int ExitAfter = 0) { CurrWindowBase = 0; mapSize = init_map_size; long RunShift = 0; if (File.Exists(Filename)) { using (var fs = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var mapName = Path.GetFileNameWithoutExtension(Filename) + DateTime.Now.ToBinary().ToString("X16"); using (var mmap = MemoryMappedFile.CreateFromFile(fs, mapName, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.Inheritable, false)) { if (FileSize == 0) { FileSize = new FileInfo(Filename).Length; } // TODO: Clean up all the shifts while (CurrWindowBase < FileSize) { using (var reader = mmap.CreateViewAccessor(CurrWindowBase, mapSize, MemoryMappedFileAccess.Read)) { CurrMapBase = 0; //reader.ReadArray(CurrMapBase, buffers[filled], 0, block_count); UnsafeHelp.ReadBytes(reader, CurrMapBase, ref buffers[filled], block_count); while (CurrMapBase < mapSize) { // setup buffers for parallel load/read block = buffers[filled]; filled ^= 1; var CURR_BASES = CurrWindowBase + CurrMapBase; CurrMapBase += block_size; #pragma warning disable HeapAnalyzerImplicitParamsRule // Array allocation for params parameter Parallel.Invoke(() => Parallel.ForEach <Func <int, long, bool> >(CheckMethods, (check) => { for (int bo = 0; bo < block_count; bo += 512) { // Adjust for known memory run / extents mappings. // Adjust TrueOffset is actually possibly used by check fn (TODO: CLEAN UP THE GLOBALS) var offset = TrueOffset = CURR_BASES + (bo * 8); var offset_pfn = offset >> MagicNumbers.PAGE_SHIFT; // next page, may be faster with larger chunks but it's simple to view 1 page at a time long IndexedOffset_pfn = 0; do { IndexedOffset_pfn = vtero.MemAccess.OffsetToMemIndex(offset_pfn + RunShift); if (IndexedOffset_pfn == -1) { RunShift++; continue; } if (IndexedOffset_pfn == -2) { break; } } while (IndexedOffset_pfn < 0); // found shift, accumulate indexes offset_pfn += RunShift; IndexedOffset_pfn = IndexedOffset_pfn >> MagicNumbers.PAGE_SHIFT; // Calculate DIFF var diff_off_pfn = offset < IndexedOffset_pfn ? IndexedOffset_pfn - offset_pfn : offset_pfn - IndexedOffset_pfn; // Skew Offset offset += (diff_off_pfn << MagicNumbers.PAGE_SHIFT); ///// !!! DO CHECK !!! check(bo, offset); } }), () => { if (CurrMapBase < mapSize) { var total_count_remain = ((mapSize - CurrMapBase) / 8); var read_in_count = total_count_remain > block_count ? block_count : total_count_remain; UnsafeHelp.ReadBytes(reader, CurrMapBase, ref buffers[filled], (int)read_in_count); } } ); if (ExitAfter > 0 && (ExitAfter == DetectedProcesses.Count())) // || FoundValueOffsets.Count() >= ExitAfter)) { return(DetectedProcesses.Count()); } } } // close current window CurrWindowBase += CurrMapBase; if (CurrWindowBase + mapSize > FileSize) { mapSize = FileSize - CurrWindowBase; } var progress = Convert.ToInt32(Convert.ToDouble(CurrWindowBase) / Convert.ToDouble(FileSize) * 100.0); if (progress != ProgressBarz.Progress) { ProgressBarz.RenderConsoleProgress(progress); } } } } // close map } // close stream return(DetectedProcesses.Count()); }
/// <summary> /// Scan for a class configured variable "HexScanDword" /// This is a specialized thing we are trying to avoid over scanning /// Turns out the physical memory run data maintained by the OS is typically very deep physically /// So in start-up we may use this depending on input file /// </summary> /// <param name="ExitAfter"></param> /// <returns></returns> public static IEnumerable <long> BackwardsValueScan(String Filename, int ScanFor, int ExitAfter = 0) { List <long> FoundValueOffsets = new List <long>(); var FileSize = new FileInfo(Filename).Length; long ReadSize = 1024 * 1024 * 8; var ValueReadCount = (int)ReadSize / 4; var RevMapSize = ReadSize; var ShortFirstChunkSize = (int)(FileSize & (ReadSize - 1)); var ShortFirstChunkBase = FileSize - ShortFirstChunkSize; if (ShortFirstChunkSize != 0) { var found = MapScanFile(Filename, ShortFirstChunkBase, ScanFor, ShortFirstChunkSize / 4); foreach (var offset in found) { yield return(offset); } if (ShortFirstChunkBase == 0) { yield break; } } var RevCurrWindowBase = FileSize - ShortFirstChunkSize; RevCurrWindowBase -= RevMapSize; var ChunkCount = (FileSize / RevMapSize) + 1; bool StopRunning = false; long localOffset = ShortFirstChunkBase - ReadSize; for (long i = ChunkCount; i > 0; i--) { if (!StopRunning) { if (Vtero.VerboseLevel > 1) { WriteColor($"Scanning From {localOffset:X} To {(localOffset + ReadSize):X} bytes"); } var results = MapScanFile(Filename, localOffset, ScanFor, ValueReadCount); foreach (var offset in results) { yield return(offset); } CurrWindowBase += (1 * ReadSize); CurrWindowBase = CurrWindowBase > FileSize ? FileSize : CurrWindowBase; var progress = Convert.ToInt32(Convert.ToDouble(CurrWindowBase) / Convert.ToDouble(FileSize) * 100.0); if (progress != ProgressBarz.Progress) { ProgressBarz.RenderConsoleProgress(progress); } localOffset -= RevMapSize; if (localOffset < 0 && !StopRunning) { localOffset = 0; StopRunning = true; } } } yield break; }
/// <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); }