Example #1
0
        public int ProcDetectScan(PTType Modes, int DetectOnly = 0)
        {
            scan.ScanMode = Modes;

            var rv = scan.Analyze(DetectOnly);

            foreach (var p in scan.DetectedProcesses.Values)
                Processes.Add(p);

            return rv;
        }
Example #2
0
        public int ProcDetectScan(PTType Modes, int DetectOnly = 0)
        {
            if (Phase >= 1 && OverRidePhase)
            {
                return(Processes.Count());
            }

            scan.ScanMode = Modes;

            var rv = scan.Analyze(DetectOnly);

            foreach (var p in scan.DetectedProcesses.Values)
            {
                Processes.Add(p);
            }

            Phase = 2;

            return(rv);
        }
Example #3
0
        /// <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;
        }
Example #4
0
        /// <summary>
        /// Group address spaces into related buckets
        /// 
        /// We will assign an address space ID to each detected proc so we know what process belongs with who
        /// After AS grouping we will know what EPTP belongs to which AS since one of the DP's will have it's CR3 in the VMCS 
        /// </summary>
        /// <param name="pTypes">Types to scan for, this is of the already detected processs list so it's already filtered really</param>
        public void GroupAS(PTType pTypes = PTType.UNCONFIGURED)
        {
            var PT2Scan = pTypes == PTType.UNCONFIGURED ? PTType.ALL : pTypes;

            if (Phase >=3 && OverRidePhase)
                return;

            // To join an AS group we want to see > 50% corelation which is a lot considering were only interperating roughly 10-20 values (more like 12)
            var p = from proc in Processes
                    where (((proc.PageTableType & PT2Scan) == proc.PageTableType))
                    orderby proc.CR3Value ascending
                    select proc;


            ASGroups = new Dictionary<int, List<DetectedProc>>();

            // we trim out the known recursive/self entries since they will naturally not be equivalent
            var AlikelyKernelSet = from ptes in p.First().TopPageTablePage
                                   where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key)
                                   select ptes.Value;

            int totUngrouped = Processes.Count();
            int CurrASID = 1;

            var grouped = new List<DetectedProc>();

            ASGroups[CurrASID] = new List<DetectedProc>();
            while (true)
            {
                ForegroundColor = ConsoleColor.Yellow;
                WriteLine("Scanning for group correlations");
                ForegroundColor = ConsoleColor.Cyan;
                foreach (var proc in p)
                {
                    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();


                    if (corralated > 0.50 && !ASGroups[CurrASID].Contains(proc))
                    {
                        WriteLine($"MemberProces: Group {CurrASID} Type [{proc.PageTableType}] GroupCorrelation [{corralated:P3}] PID [{proc.CR3Value:X}]");

                        proc.AddressSpaceID = CurrASID;
                        ASGroups[CurrASID].Add(proc);
                        // global list to quickly scan
                        grouped.Add(proc);
                    }
                }
                ForegroundColor = ConsoleColor.Yellow;

                var totGrouped = (from g in ASGroups.Values
                                  select g).Sum(x => x.Count());

                Console.WriteLine($"Finished Group {CurrASID} collected size {ASGroups[CurrASID].Count()} next group");
                // if there is more work todo, setup an entry for testing
                if (totGrouped < totUngrouped)
                {
                    CurrASID++;
                    ASGroups[CurrASID] = new List<DetectedProc>();
                }
                else
                    break; // we grouped them all!

                /// Isolate next un-grouped PageTable
                var UnGroupedProc = from nextProc in Processes
                                   where !grouped.Contains(nextProc)
                                   select nextProc;

                AlikelyKernelSet = from ptes in UnGroupedProc.First().TopPageTablePage
                                   where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key)
                                   select ptes.Value;
            }

            Console.WriteLine($"Done All process groups.");

            // after grouping link VMCS back to the group who 'discovered' the VMCS in the first place!
            var eptpz = VMCSs.GroupBy(eptz => eptz.EPTP).Select(ept => ept.First()).ToArray();

            // find groups dominated by each vmcs
            var VMCSGroup = from aspace in ASGroups.Values.AsEnumerable()
                            from ept in eptpz
                            where aspace.Any(adpSpace => adpSpace == ept.dp)
                            select new { AS = aspace, EPTctx = ept };

            // link the proc back into the eptp
            foreach (var ctx in VMCSGroup)
                foreach (var dp in ctx.AS)
                    dp.vmcs = ctx.EPTctx;

            Phase = 4;
            // were good, all Processes should have a VMCS if applicable and be identifiable by AS ID
        }
Example #5
0
        public int ProcDetectScan(PTType Modes, int DetectOnly = 0)
        {
            if (Phase >= 1 && OverRidePhase)
                return Processes.Count();

            scan.ScanMode = Modes;

            var rv = scan.Analyze(DetectOnly);

            foreach (var p in scan.DetectedProcesses.Values)
                Processes.Add(p);

            Phase = 2;

            return rv;
        }
Example #6
0
        public Vtero Scanit(ScanOptions op)
        {
            bool SkipVMCS = false;

#if TESTING
            foreach (var sx in op.DisabledScans)
            {
                var spec = sx.ToLower();

                if (spec.Contains("vmcs"))
                {
                    SkipVMCS = true;
                }
                if (spec.Contains("obsd"))
                {
                    Version = Version & ~PTType.OpenBSD;
                }
                if (spec.Contains("nbsd"))
                {
                    Version = Version & ~PTType.NetBSD;
                }
                if (spec.Contains("fbsd"))
                {
                    Version = Version & ~PTType.FreeBSD;
                }
                if (spec.Contains("lin"))
                {
                    Version = Version & ~PTType.LinuxS;
                }
                if (spec.Contains("hv"))
                {
                    Version = Version & ~PTType.HyperV;
                }
                if (spec.Contains("gen"))
                {
                    Version = Version & ~PTType.GENERIC;
                }
                if (spec.Contains("win"))
                {
                    Version = Version & ~PTType.Windows;
                }
            }
            if ((Version & PTType.VALUE) == PTType.VALUE)
            {
                bool Parsed = false;
                do
                {
                    if (args.Length < 2)
                    {
                        WriteLine($"Specify value");
                        return;
                    }

                    Parsed = uint.TryParse(args[2], NumberStyles.HexNumber, CultureInfo.CurrentCulture, out valuI);
                    if (!Parsed)
                    {
                        Parsed = ulong.TryParse(args[2], NumberStyles.HexNumber, CultureInfo.CurrentCulture, out valuL);
                        if (Parsed)
                        {
                            Is64Scan = true;
                        }
                        else
                        {
                            WriteLine($"Unable to parse input {args[2]}");
                            return;
                        }
                    }
                    else
                    {
                        valuL = (ulong)valuI;
                    }
                } while (!Parsed);
            }
#endif
            string Filename = null;
            Vtero  vtero    = null;
            PTType Version  = PTType.Windows;

            // this instance is temporally used for loading state
            // i.e. don't set properties or fields here

            var saveStateFile = $"{Filename}.inVtero.net";

            if (File.Exists(saveStateFile))
            {
                if (todo.Key != ConsoleKey.D)
                {
                    vtero = vtero.CheckpointRestoreState(saveStateFile);
                    vtero.OverRidePhase = true;
                }
                else
                {
                    File.Delete(saveStateFile);
                }
            }

            if (vtero.Phase < 2)
            {
                vtero = new Vtero(Filename);
            }

            //Mem.InitMem(parsed.FileName, null, vtero.DetectedDesc);
            ProgressBarz.Bar.Message = "First pass, looking for processes";

            ForegroundColor = ConsoleColor.Cyan;

#if TESTING
            Timer = Stopwatch.StartNew();

            if ((Version & PTType.VALUE) == PTType.VALUE)
            {
                var off = vtero.ScanValue(Is64Scan, valuL, 0);

                WriteLine(FormatRate(vtero.FileSize, Timer.Elapsed));
                using (var dstream = File.OpenRead(vtero.MemFile))
                {
                    using (var dbin = new BinaryReader(dstream))
                    {
                        foreach (var xoff in off)
                        {
                            WriteLine($"Checking Memory Descriptor @{(xoff + 28):X}");
                            if (xoff > vtero.FileSize)
                            {
                                WriteLine($"offset {xoff:X} > FileSize {vtero.FileSize:X}");
                                continue;
                            }

                            dstream.Position = xoff + 28;
                            var MemRunDescriptor = new MemoryDescriptor();
                            MemRunDescriptor.NumberOfRuns  = dbin.ReadInt64();
                            MemRunDescriptor.NumberOfPages = dbin.ReadInt64();

                            Console.WriteLine($"Runs: {MemRunDescriptor.NumberOfRuns}, Pages: {MemRunDescriptor.NumberOfPages} ");

                            if (MemRunDescriptor.NumberOfRuns < 0 || MemRunDescriptor.NumberOfRuns > 32)
                            {
                                continue;
                            }
                            for (int i = 0; i < MemRunDescriptor.NumberOfRuns; i++)
                            {
                                var basePage  = dbin.ReadInt64();
                                var pageCount = dbin.ReadInt64();

                                MemRunDescriptor.Run.Add(new MemoryRun()
                                {
                                    BasePage = basePage, PageCount = pageCount
                                });
                            }
                            WriteLine($"MemoryDescriptor {MemRunDescriptor}");
                        }
                    }
                }
                WriteLine("Finished VALUE scan.");
                return;
            }
            if ((Version & PTType.VALUE) == PTType.VALUE)
            {
                return;
            }
#endif
            // basic perf checking
            QuickOptions.Timer = Stopwatch.StartNew();
            var procCount = vtero.ProcDetectScan(Version);

            WriteColor(ConsoleColor.Blue, ConsoleColor.Yellow, $"{procCount} candidate process page tables. Time so far: {QuickOptions.Timer.Elapsed}, second pass starting. {QuickOptions.FormatRate(vtero.FileSize, QuickOptions.Timer.Elapsed)}");

            //BackgroundColor = ConsoleColor.Black;
            //ForegroundColor = ConsoleColor.Cyan;

            if (procCount < 3)
            {
                WriteColor(ConsoleColor.Red, "Seems like a fail. Try generic scanning or implement a state scan like LinuxS");
                return(null);
            }
            // second pass
            // with the page tables we acquired, locate candidate VMCS pages in the format
            // [31-bit revision id][abort indicator]
            // the page must also have at least 1 64bit value which is all set (-1)
            // Root-HOST CR3 will have uniform diff
            // unless an extent based dump image is input, some .DMP variations
            // TODO: Add support for extent based inputs
            // Guest VMCS will contain host CR3 & guest CR3 (hCR3 & gCR3)
            // sometimes CR3 will be found in multiple page tables, e.g. system process or SMP
            // if I have more than 1 CR3 from different file_offset, just trim them out for now
            // future may have a reason to isolate based on original locationAG

            if (SkipVMCS)
            {
                return(vtero);
            }

            ProgressBarz.Bar.Message = "Second pass, correlating for VMCS pages";

            var VMCSCount = vtero.VMCSScan();
            //Timer.Stop();

            WriteColor(ConsoleColor.Blue, ConsoleColor.Yellow, $"{VMCSCount} candidate VMCS pages. Time to process: {QuickOptions.Timer.Elapsed}, Data scanned: {vtero.FileSize:N}");

            // second time
            WriteColor(ConsoleColor.Blue, ConsoleColor.Yellow, $"Second pass done. {QuickOptions.FormatRate(vtero.FileSize * 2, QuickOptions.Timer.Elapsed)}");

            // each of these depends on a VMCS scan/pass having been done at the moment
            WriteColor(ConsoleColor.Cyan, ConsoleColor.Black, "grouping and joining all memory");

            // After this point were fairly functional
            vtero.GroupAS();

            // sync-save state so restarting is faster
            if (!File.Exists(saveStateFile))
            {
                Write($"Saving checkpoint... ");
                saveStateFile = vtero.CheckpointSaveState();
                WriteColor(ConsoleColor.White, saveStateFile);
            }
            return(vtero);
        }
Example #7
0
        /// <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.");


                    });
                    //}
                }
            //}
            });
        }
Example #8
0
        /// <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
        /// 
        /// TODO: Remake this.  Instead of just pre-buffering everything.  Ensure the GroupAS detections are appropriate 
        /// and if not, reassign the VMCS/EPTP page to bare metal or a different HVLayer item.
        /// </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 bitmask to interpret</param>
        public Dictionary<int, List<DetectedProc>> ExtrtactAddressSpaces(IOrderedEnumerable<VMCS> MemSpace = null, ConcurrentBag<DetectedProc> Procs = null, PTType pTypes = PTType.UNCONFIGURED)
        {
            Dictionary<int, List<DetectedProc>> rvList = new Dictionary<int, 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.Values.GroupBy(x => x.EPTP).Select(xg => xg.First()) : MemSpace;
            var ms = from memx in memSpace
                     orderby memx.Offset ascending
                     select memx;

            int gcnt = ASGroups.Count();
            int vmcnt = memSpace.Count();
            var tot = gcnt * vmcnt;
            var curr = 0;
            bool CollectKernelAS = true;

            var CurrColor = ForegroundColor;

            WriteColor(ConsoleColor.White, ConsoleColor.Black, $"assessing {tot} address space combinations");
            ProgressBarz.RenderConsoleProgress(0);

            var VMCSTriage = new Dictionary<VMCS, int>();

            //Parallel.ForEach(memSpace, (space) =>
            //foreach (var space in ms)
            //{
            // 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 memAxs = MemAccess;
            {

                var sx = 0;

                // assign address space by group
                foreach (var grp in ASGroups)
                {

                    // if the group is configured fully, then we know we were successful
                    // since we null out the list if we fail, so skip to next one
                    //if ((rvList.ContainsKey(grp.Key) && rvList[grp.Key] != null) || grp.Value == null)
                    //    continue;

                    rvList[grp.Key] = new List<DetectedProc>();
                    var orderedGroup = from px in grp.Value
                                        where ((px.PageTableType & PT2Scan) == px.PageTableType) && px.AddressSpaceID == grp.Key
                                        orderby px.CR3Value ascending
                                        select procList;

                    //foreach (var proc in from proc in grp.Value
                    //                     where (((proc.PageTableType & PT2Scan) == proc.PageTableType)) && (proc.AddressSpaceID == grp.Key)
                    //                    orderby proc.CR3Value ascending
                    //                   select proc)
                    //foreach(var proc in orderedGroup.SelectMany(x => x))
                    //Parallel.ForEach(p, (proc) =>
                    if (orderedGroup.Count() < 1)
                        continue;

                    var proc = orderedGroup.First().First();

                    {
                        int i = 0;
                        List<long> tableCounts = new List<long>();
                        if (proc.CandidateList == null || proc.CandidateList.Count < 1)
                        {
                            if (proc.vmcs != null)
                                proc.CandidateList = new List<VMCS>() { proc.vmcs }; // just set the one
                            else
                                proc.CandidateList = new List<VMCS>() { new VMCS() { EPTP = 0 } };

                        }

                        // find the best space for this proc 
                        foreach (var space in proc.CandidateList)
                        {
                            try
                            {
                                // this is killing memory, probably not needed
                                //var proc = px.Clone<DetectedProc>();
                                proc.vmcs = space;
                                if (VerboseOutput)
                                    WriteLine($"Scanning PT from Type [{proc.PageTableType}] PID [{proc.vmcs.EPTP:X}:{proc.CR3Value:X}] ID{proc.AddressSpaceID} Key{grp.Key}");

                                var pt = PageTable.AddProcess(proc, memAxs, CollectKernelAS);
                                CollectKernelAS = false;
                                if (pt != null)
                                {
                                    // If we used group detection correlation a valid EPTP should work for every process    
                                    // so if it's bad we skip the entire evaluation
                                    if (proc.vmcs != null && proc.PT.Root.Count > proc.TopPageTablePage.Count())
                                    {
                                        tableCounts[i++] = proc.PT.Root.Count;
                                        WriteLine($"TableCount for VMCS candidate is {proc.PT.Root.Count}");
                                        if (VerboseOutput)
                                            WriteLine($"{rvList[grp.Key].Count()} of {grp.Value.Count} Virtualized Process PT Entries [{proc.PT.Root.Count}] Type [{proc.PageTableType}] PID [{proc.vmcs.EPTP:X}:{proc.CR3Value:X}]");

                                        // collect process into now joined EPTP<->Proc
                                        rvList[grp.Key].Add(proc);

                                        if (rvList[grp.Key].Count() == grp.Value.Count && VerboseOutput)
                                        {
                                            ForegroundColor = ConsoleColor.Green;
                                            WriteLine($"Validated 100% {grp.Value.Count} of detected group {proc.AddressSpaceID}, continuing with next group.");
                                            ForegroundColor = CurrColor;
                                            break;
                                        }
                                    }
                                    else {
                                        // let's just cancel if we haven't done any decodes
                                        if (rvList[grp.Key].Count() < 1)
                                        {
                                            WriteColor(ConsoleColor.Yellow, $"Canceling evaluation of bad EPTP for this group/Address Space ({grp.Key}) a likely bare metal group");
                                            foreach (var p in Processes)
                                                if (p.vmcs != null && p.vmcs.EPTP == space.EPTP && p.AddressSpaceID == proc.AddressSpaceID)
                                                    p.vmcs = null;

                                            rvList[grp.Key] = 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:X12}");

                                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 successfully.");
                            //});
                        }
                    }
                }
            }
            //}
            //});

            CollectKernelAS = true;
            // a backup to test a non-VMCS 
            //using (var memAxs = new Mem(MemFile, null, DetectedDesc))
            //var memAxs = Mem.Instance;
            {
                var nonVMCSprocs = from proc in Processes
                                   where (((proc.PageTableType & PT2Scan) == proc.PageTableType))
                                   where proc.vmcs == null
                                   orderby proc.CR3Value ascending
                                   select proc;

                foreach (var pmetal in nonVMCSprocs)
                {
                    // unassigned, give them a unique entry for now, we should rerun the grouping method
                    if(pmetal.AddressSpaceID == 0)
                    {
                        var ASID = ASGroups.Count + 1;
                        pmetal.AddressSpaceID = ASID;
                        ASGroups.TryAdd(ASID, new ConcurrentBag<DetectedProc> { pmetal });
                    }

                    // this is a process on the bare metal
                    var pt = PageTable.AddProcess(pmetal, memAxs, CollectKernelAS);
                    CollectKernelAS = false;
                    WriteLine($"Process {pmetal.CR3Value:X12} Physical walk w/o SLAT yielded {pmetal.PT.Root.Count} entries, bare metal group is {pmetal.AddressSpaceID}");

                    if (rvList.ContainsKey(pmetal.AddressSpaceID) && rvList[pmetal.AddressSpaceID] == null)
                        rvList[pmetal.AddressSpaceID] = new List<DetectedProc>();

                    if (rvList.ContainsKey(pmetal.AddressSpaceID))
                        rvList[pmetal.AddressSpaceID].Add(pmetal);
                    else
                        rvList.Add(pmetal.AddressSpaceID, new List<DetectedProc>());
                }
            }
             return rvList;
        }
Example #9
0
        // TODO: Move above into DetectedProc class methods

    /// <summary>
    /// Group address spaces into related buckets
    /// 
    /// We will assign an address space ID to each detected proc so we know what process belongs with who
    /// After AS grouping we will know what EPTP belongs to which AS since one of the DP's will have it's CR3 in the VMCS 
    /// 
    /// Yes it's a bit complicated.  
    /// 
    /// The overall procedure however is straight forward in that; 
    /// 
    /// * For every detected process
    ///       Bucket into groups which are the "Address spaces" that initially are 
    ///       
    ///       (a) based on kernel address space similarities 
    ///       and then 
    ///       (b) based on what VMCS value was found pointing to that group
    ///              
    /// This ensures that if we have several hypervisors with a possibly identical kernel grouping (i.e. the PFN's
    /// were used by each kernel were identical), they are disambiguated by the VMCS.  (Which can be validated later)
    /// 
    /// The benefit here is that brute forcing at this stage is fairly expensive and can lead to significant overhead, there does
    /// tend to be some outliers for large systems that need to be looked at more to determine who they belong too.  Nevertheless, it's 
    /// inconsequential if they are grouped with the appropriate AS since even if they are isolated into their own 'AS' this is an artificial 
    /// construct for our book keeping.  The net result is that even if some process is grouped by itself due to some aggressive variation in
    /// kernel PFN' use (lots of dual mapped memory/MDL's or something), it's still able to be dumped and analyzed.
    /// 
    /// 
    /// </summary>
    /// <param name="pTypes">Types to scan for, this is of the already detected processes list so it's already filtered really</param>
    public void GroupAS(PTType pTypes = PTType.UNCONFIGURED)
        {
            var PT2Scan = pTypes == PTType.UNCONFIGURED ? PTType.ALL : pTypes;

            //if (Phase >=3 && OverRidePhase)
            //    return;

            // To join an AS group we want to see > 50% correlation which is a lot considering were only interoperating roughly 10-20 values (more like 12)
            var p = from proc in Processes
                    where (((proc.PageTableType & PT2Scan) == proc.PageTableType))
                    orderby proc.CR3Value ascending
                    select proc;

            ASGroups = new ConcurrentDictionary<int, ConcurrentBag<DetectedProc>>();

            // we trim out the known recursive/self entries since they will naturally not be equivalent
            var AlikelyKernelSet = from ptes in p.First().TopPageTablePage
                                   where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key)
                                   select ptes.Value;

            int totUngrouped = Processes.Count();
            int CurrASID = 1;
            int LastGroupTotal = 0;
            var grouped = new ConcurrentBag<DetectedProc>();

            WriteLine($"Scanning for group correlations total processes {totUngrouped}");
            ASGroups[CurrASID] = new ConcurrentBag<DetectedProc>();

            while (true)
            {
                ForegroundColor = ConsoleColor.Yellow;
                Parallel.ForEach(p, (proc) =>
                {
                    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 correlated = interSection.Count() * 1.00 / AlikelyKernelSet.Count();

                    // add this detected CR3/process address space to an address space grouping when
                    // the kernel range is above the acceptable threshold, the group does not contain this proc
                    // and this proc is not already joined into another group
                    if (correlated > GroupThreshold && !ASGroups[CurrASID].Contains(proc) && proc.AddressSpaceID == 0)
                    {
                        WriteColor(ConsoleColor.Cyan, $"MemberProces: Group {CurrASID} Type [{proc.PageTableType}] GroupCorrelation [{correlated:P3}] PID [{proc.CR3Value:X}]");

                        proc.AddressSpaceID = CurrASID;
                        ASGroups[CurrASID].Add(proc);
                        // global list to quickly scan
                        grouped.Add(proc);
                    }
                });

                ForegroundColor = ConsoleColor.Yellow;

                var totGrouped = (from g in ASGroups.Values
                                  select g).Sum(x => x.Count());

                WriteLine($"Finished Group {CurrASID} collected size {ASGroups[CurrASID].Count()}, continuing to group");
                // if there is more work todo, setup an entry for testing
                if (totGrouped < totUngrouped)
                {
                    // if we wind up here 
                    // there has been no forward progress in isolating further groups
                    if(LastGroupTotal == totGrouped)
                    {
                        ForegroundColor = ConsoleColor.Red;
                        WriteLine($"Terminating with non-grouped process candidates.  GroupThreshold may be too high. {GroupThreshold}");
                        var pz = from px in Processes
                                where px.AddressSpaceID == 0
                                select px;
                        
                        // just add the ungrouped processes as a single each bare metal
                        // unless it has an existing VMCS pointer
                        foreach (var px in pz)
                        {
                            WriteLine(px);
                            CurrASID++;
                            px.AddressSpaceID = CurrASID;
                            ASGroups[CurrASID] = new ConcurrentBag<DetectedProc>() { px };

                            var isCandidate = from pvmcs in scan.HVLayer
                                              where pvmcs.gCR3 == px.CR3Value
                                              select pvmcs;

                            if (isCandidate.Count() > 0)
                            {
                                px.CandidateList = new List<VMCS>(isCandidate);
                                px.vmcs = px.CandidateList.First();
                                WriteColor( ConsoleColor.White, $"Detected ungrouped {px.CR3Value} as a candidate under {px.CandidateList.Count()} values (first){px.vmcs.EPTP}");
                            }
                        }

                        ForegroundColor = ConsoleColor.Yellow;
                        break;
                    }


                    CurrASID++;
                    ASGroups[CurrASID] = new ConcurrentBag<DetectedProc>();
                    WriteLine($"grouped count ({totGrouped}) is less than total process count ({totUngrouped}, rescanning...)");
                    LastGroupTotal = totGrouped;
                }
                else
                    break; // we grouped them all!

                /// Isolate next un-grouped PageTable
                var UnGroupedProc = from nextProc in Processes
                                   where !grouped.Contains(nextProc)
                                   select nextProc;

                AlikelyKernelSet = from ptes in UnGroupedProc.First().TopPageTablePage
                                   where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key)
                                   select ptes.Value;
            }

            Console.WriteLine($"Done All process groups.");

            // after grouping link VMCS back to the group who 'discovered' the VMCS in the first place!
            var eptpz = VMCSs.Values.GroupBy(eptz => eptz.EPTP).OrderBy(eptx => eptx.Key).Select(ept => ept.First()).ToArray();

            // find groups dominated by each vmcs
            var VMCSGroup = from aspace in ASGroups.AsEnumerable()
                            from ept in eptpz
                            where aspace.Value.Any(adpSpace => adpSpace == ept.dp)
                            select new { AS = aspace, EPTctx = ept };

            // link the proc back into the eptp 
            foreach (var ctx in VMCSGroup)
                foreach (var dp in ctx.AS.Value)
                {
                    if(dp.CandidateList == null)
                        dp.CandidateList = new List<VMCS>();

                    dp.vmcs = ctx.EPTctx;
                    dp.CandidateList.Add(ctx.EPTctx);
                }

            // resort by CR3
            foreach (var ctx in ASGroups.Values)
            {
                var dpz = from d in ctx
                          orderby d.CR3Value descending
                          select d;

                if (dpz.Count() >= 1)
                {
                    var aspace = dpz.First().AddressSpaceID;
                    ASGroups[aspace] = new ConcurrentBag<DetectedProc>(dpz);
                }
            }

            Phase = 4;
            // were good, all Processes should have a VMCS if applicable and be identifiable by AS ID
        }
Example #10
0
        /// <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);
        }
Example #11
0
        /// <summary>
        /// Group address spaces into related buckets
        ///
        /// We will assign an address space ID to each detected proc so we know what process belongs with who
        /// After AS grouping we will know what EPTP belongs to which AS since one of the DP's will have it's CR3 in the VMCS
        /// </summary>
        /// <param name="pTypes">Types to scan for, this is of the already detected processs list so it's already filtered really</param>
        public void GroupAS(PTType pTypes = PTType.UNCONFIGURED)
        {
            var PT2Scan = pTypes == PTType.UNCONFIGURED ? PTType.ALL : pTypes;

            if (Phase >= 3 && OverRidePhase)
            {
                return;
            }

            // To join an AS group we want to see > 50% corelation which is a lot considering were only interperating roughly 10-20 values (more like 12)
            var p = from proc in Processes
                    where (((proc.PageTableType & PT2Scan) == proc.PageTableType))
                    orderby proc.CR3Value ascending
                    select proc;


            ASGroups = new Dictionary <int, List <DetectedProc> >();

            // we trim out the known recursive/self entries since they will naturally not be equivalent
            var AlikelyKernelSet = from ptes in p.First().TopPageTablePage
                                   where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key)
                                   select ptes.Value;

            int totUngrouped = Processes.Count();
            int CurrASID     = 1;

            var grouped = new List <DetectedProc>();

            ASGroups[CurrASID] = new List <DetectedProc>();
            while (true)
            {
                ForegroundColor = ConsoleColor.Yellow;
                WriteLine("Scanning for group correlations");
                ForegroundColor = ConsoleColor.Cyan;
                foreach (var proc in p)
                {
                    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();


                    if (corralated > 0.50 && !ASGroups[CurrASID].Contains(proc))
                    {
                        WriteLine($"MemberProces: Group {CurrASID} Type [{proc.PageTableType}] GroupCorrelation [{corralated:P3}] PID [{proc.CR3Value:X}]");

                        proc.AddressSpaceID = CurrASID;
                        ASGroups[CurrASID].Add(proc);
                        // global list to quickly scan
                        grouped.Add(proc);
                    }
                }
                ForegroundColor = ConsoleColor.Yellow;

                var totGrouped = (from g in ASGroups.Values
                                  select g).Sum(x => x.Count());

                Console.WriteLine($"Finished Group {CurrASID} collected size {ASGroups[CurrASID].Count()} next group");
                // if there is more work todo, setup an entry for testing
                if (totGrouped < totUngrouped)
                {
                    CurrASID++;
                    ASGroups[CurrASID] = new List <DetectedProc>();
                }
                else
                {
                    break; // we grouped them all!
                }
                /// Isolate next un-grouped PageTable
                var UnGroupedProc = from nextProc in Processes
                                    where !grouped.Contains(nextProc)
                                    select nextProc;

                AlikelyKernelSet = from ptes in UnGroupedProc.First().TopPageTablePage
                                   where ptes.Key > 255 && MagicNumbers.Each.All(ppx => ppx != ptes.Key)
                                   select ptes.Value;
            }

            Console.WriteLine($"Done All process groups.");

            // after grouping link VMCS back to the group who 'discovered' the VMCS in the first place!
            var eptpz = VMCSs.GroupBy(eptz => eptz.EPTP).Select(ept => ept.First()).ToArray();

            // find groups dominated by each vmcs
            var VMCSGroup = from aspace in ASGroups.Values.AsEnumerable()
                            from ept in eptpz
                            where aspace.Any(adpSpace => adpSpace == ept.dp)
                            select new { AS = aspace, EPTctx = ept };

            // link the proc back into the eptp
            foreach (var ctx in VMCSGroup)
            {
                foreach (var dp in ctx.AS)
                {
                    dp.vmcs = ctx.EPTctx;
                }
            }

            Phase = 4;
            // were good, all Processes should have a VMCS if applicable and be identifiable by AS ID
        }