public static void DumpDetected(Vtero vtero, DetectedProc p, long VAStart = 0, long VAEnd = 0xffffffff0000) { var mods = vtero.ModuleScan(p, VAStart, VAEnd); // BUGBUG: TODO: Refactor the threadlocal stuff seems were re-entrant unsafe :( //Parallel.ForEach(mods, (detected) => //{ foreach (var detected in mods) { var cv_data = vtero.ExtractCVDebug(p, detected.Value, detected.Key); if (cv_data != null) { var sympath = Environment.GetEnvironmentVariable("_NT_SYMBOL_PATH"); if (string.IsNullOrWhiteSpace(sympath)) { sympath = "SRV*http://msdl.microsoft.com/download/symbols"; } if (vtero.TryLoadSymbols(p, detected.Value, cv_data, detected.Key, sympath)) { vtero.GetKernelDebuggerData(p, detected.Value, cv_data, sympath); } } } //}); }
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 VirtualScanner(DetectedProc Ctx, Mem backingBlocks, bool DoHeaderScan = false) : this() { DPContext = Ctx; BackingBlocks = backingBlocks; BackingStream = new PhysicalMemoryStream(backingBlocks, Ctx); HeaderScan = DoHeaderScan; }
public static void DumpDetected(Vtero vtero, DetectedProc p, long VAStart = 0, long VAEnd = 0xffffffff0000) { var mods = vtero.ModuleScan(p, 3, VAStart, VAEnd); //Parallel.ForEach(mods, (detected) => //{ foreach (var detected in p.Sections) { var cv_data = vtero.ExtractCVDebug(p, detected.Value); if (cv_data != null) { var sympath = Environment.GetEnvironmentVariable("_NT_SYMBOL_PATH"); if (string.IsNullOrWhiteSpace(sympath)) { sympath = "SRV*http://msdl.microsoft.com/download/symbols"; } // TODO: fix this or not? //if(Vtero.TryLoadSymbols(p.ID.GetHashCode(), cv_data, detected.Key)) // vtero.KernelProc = p; } } //}); }
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 int CompareTo(object obj) { int vi = 0; if (obj is DetectedProc) { DetectedProc dp = obj as DetectedProc; if (vmcs != null || dp.vmcs != null) { if (vmcs == null && dp.vmcs == null) { return(FileOffset.CompareTo(dp.FileOffset)); } else if (vmcs != null && dp.vmcs == null) { return(1); } else if (vmcs == null && dp.vmcs != null) { return(-1); } else { vi = vmcs.Offset.CompareTo(dp.vmcs.Offset); } } return(vi + FileOffset.CompareTo(dp.FileOffset)); } return(int.MinValue); }
public Dumper(Vtero vtero, string outDir, DetectedProc dp, MemRangeArgs args) { Vtero = vtero; DP = dp; OutDir = outDir; SelectedRegions = args.Regions; }
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); }
/// <summary> /// Naturally the Generic checker is fairly chatty but at least you can use it /// to find unknowns, we could use some more tunable values here to help select the /// best match, I currently use the value with the lowest diff, which can be correct /// /// This will find a self pointer in the first memory run for a non-sparse memory dump. /// /// The calling code is expected to adjust offset around RUN gaps. /// /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool Generic(int x, long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; //long bestShift = long.MaxValue, bestDiff = long.MaxValue; //var bestOffset = long.MaxValue; var i = 0x1ff; if (((block[x] & 0xff) == 0x63) || (block[x] & 0xfdf) == 0x847) { do { if (((block[x + i] & 0xff) == 0x63 || (block[x + i] & 0xff) == 0x67)) { // we disqualify entries that have these bits configured // 111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 // if ((block[x + i] & 0x7FFF000000000480) == 0) { var shifted = (block[x + i] & 0xFFFFFFFFF000); if (shifted == offset) { var diff = offset - shifted; // BUGBUG: Need to K-Means this or something cluster values to help detection of processes in sparse format // this could be better var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.GENERIC, TrueOffset = TrueOffset }; for (int p = 0; p < 0x200; p++) { if (block[x + p] != 0) { dp.TopPageTablePage.Add(p, block[x + p]); } } DetectedProcesses.TryAdd(offset, dp); if (Vtero.DiagOutput) { WriteColor(ConsoleColor.Cyan, ConsoleColor.Black, dp.ToString()); } Candidate = true; } } } i--; } while (i > 0xFF); } // maybe some kernels keep more than 1/2 system memory // wouldn't that be a bit greedy though!? return(Candidate); }
public void Dispose(bool disposing) { if (!disposedValue && disposing) { if (mem != null) { ((IDisposable)mem).Dispose(); } if (DP != null) { ((IDisposable)DP).Dispose(); } } mem = null; DP = null; disposedValue = true; }
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 static PageTable AddProcess(DetectedProc dp, Mem mem, bool RedundantKernelEntries = true) { 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); }
/// <summary> /// In some deployments Hyper-V was found to use a configuration as such /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool HV(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[0x1fe] & 0xFFFFFFFFF000); var diff = offset - shifted; // detect mode 2, 2 seems good for most purposes and is more portable // maybe 0x3 is sufficient if (shifted != 0 && ((block[0] & 0xfff) == 0x063) && ((block[0x1fe] & 0xff) == 0x63 || (block[0x1fe] & 0xff) == 0x67) && block[0x1ff] == 0) { // we disqualify entries that have these bits configured // 111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 // if (((ulong)block[0x1fe] & 0xFFFF000000000480) == 0) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.HyperV }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) { dp.TopPageTablePage.Add(p, block[p]); } } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) { WriteLine(dp.ToString()); } Candidate = true; } } } return(Candidate); }
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 static void DumpDetected(Vtero vtero, DetectedProc p, long VAStart = 0, long VAEnd = 0xffffffff0000) { var mods = vtero.ModuleScan(p, VAStart, VAEnd); //Parallel.ForEach(mods, (detected) => //{ foreach (var detected in p.Sections) { var cv_data = vtero.ExtractCVDebug(p, detected); if (cv_data != null) { var sympath = Environment.GetEnvironmentVariable("_NT_SYMBOL_PATH"); if (string.IsNullOrWhiteSpace(sympath)) sympath = "SRV*http://msdl.microsoft.com/download/symbols"; if(vtero.TryLoadSymbols(cv_data, detected.VA.Address, sympath)) vtero.KernelProc = p; } } //}); }
/// <summary> /// TODO: NetBSD needs some analysis /// Will add more later, this check is a bit noisy, consider it alpha /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool NetBSD(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[255] & 0xFFFFFFFFF000); var diff = offset - shifted; if (((block[511] & 0xf3) == 0x63) && ((0x63 == (block[320] & 0xf3)) || (0x63 == (block[256] & 0xf3)))) { if (((block[255] & 0xf3) == 0x63) && (0 == (block[255] & 0x7FFF000000000000))) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.NetBSD }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) { dp.TopPageTablePage.Add(p, block[p]); } } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) { WriteLine(dp.ToString()); } Candidate = true; } } } return(Candidate); }
/// <summary> /// The FreeBSD check for process detection is good /// Consider it release quality ;) /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool FreeBSD(int bo, long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[bo + 0x100] & 0xFFFFFFFFF000); var diff = offset - shifted; if (((block[bo] & 0xff) == 0x67) && (0x67 == (block[bo + 0xff] & 0xff))) { if (((block[bo + 0x100] & 0xff) == 0x63) && (0 == (block[bo + 0x100] & 0x7FFF000000000000))) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.FreeBSD, TrueOffset = TrueOffset }; for (int p = 0; p < 0x200; p++) { if (block[bo + p] != 0) { dp.TopPageTablePage.Add(p, block[bo + p]); } } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) { WriteColor(ConsoleColor.Cyan, ConsoleColor.Black, dp.ToString()); } Candidate = true; } } } return(Candidate); }
/// <summary> /// The FreeBSD check for process detection is good /// Consider it release quality ;) /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool FreeBSD(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[0x100] & 0xFFFFFFFFF000); var diff = offset - shifted; if (((block[0] & 0xff) == 0x67) && (0x67 == (block[0xff] & 0xff))) { if (((block[0x100] & 0xff) == 0x63) && (0 == (block[0x100] & 0x7FFF000000000000))) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.FreeBSD, TrueOffset = TrueOffset }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) dp.TopPageTablePage.Add(p, block[p]); } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) WriteColor(ConsoleColor.Cyan, ConsoleColor.Black, dp.ToString()); Candidate = true; } } } return Candidate; }
/// <summary> /// The LinuxS check is a single pass state preserving scanner /// /// This was created using kernel 3.19 as a baseline. More to follow. /// /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool LinuxS(long offset) { var Candidate = false; var group = -1; // The main observation on kern319 is the given set below of must-have offsets that are identical and 0x7f8 which is unique per process // Next is the behavior that uses entries in 2 directions from top down and bottom up // i.e. 0x7f0 0x0 are the next expected values. // All others would be unset in the top level / base page // // Kernel should have only the magnificent entries // memcmp 0 ranges 8-7f0, 800-880, 888-c88, c98-e88, e90-ea0, ea8-ff0 // after first (likely kernel) page table found, use it's lower 1/2 to validate other detected page tables // Linux was found (so far) to have a consistent kernel view. var kern319 = new Dictionary<int, bool> { [0x7f8] = false, [0x880] = true, [0xc90] = true, [0xe88] = true, [0xea0] = true, [0xff0] = true, [0xff8] = true }; var Profiles = new List<Dictionary<int, bool>>(); if (((block[0xFF] & 0xfff) == 0x067) && ((block[0x110] & 0xfff) == 0x067) && ((block[0x192] & 0xfff) == 0x067) && ((block[0x1d1] & 0xfff) == 0x067) && ((block[0x1d4] & 0xfff) == 0x067) && ((block[0x1fe] & 0xfff) == 0x067) && ((block[0x1ff] & 0xfff) == 0x067) // this is the largest block of 0's // just do this one to qualify //IsZero(block, 8, 0xe0) ) if ( /*IsZero(block, 8, 0xE0) && IsZero(block, 0x100, 0x10) &&*/ IsZero(block, 0x111, 0x80) && IsZero(block, 0x193, 0x3e) && IsZero(block, 0x1D2, 0x02) && IsZero(block, 0x1D5, 0x29)) { // before we catalog this entry, check to see if we can put it in a group for (int i = 0; i < LinuxSFirstPages.Count(); i++) if (EqualBytesLongUnrolled(block, LinuxSFirstPages[i], 0x100)) group = i; // if we haven't found anything yet, setup first page if (LinuxSFirstPage == null) { LinuxSFirstPage = block; LinuxSFirstPages.Add(block); group = 0; } // load DP var dp = new DetectedProc { CR3Value = offset, FileOffset = offset, Diff = 0, Mode = 2, Group = group, PageTableType = PTType.LinuxS, TrueOffset = TrueOffset }; for (int p = 0; p < 0x200; p++) if (block[p] != 0) dp.TopPageTablePage.Add(p, block[p]); if (Vtero.VerboseOutput) WriteColor(ConsoleColor.Cyan, dp.ToString()); DetectedProcesses.TryAdd(offset, dp); Candidate = true; } return Candidate; }
public PhysicalMemoryStream(Mem blockStorage, DetectedProc proc) { MemBlockStorage = blockStorage; Proc = proc; }
// TODO: MOVE below to DetectedProc class public CODEVIEW_HEADER ExtractCVDebug(DetectedProc dp, MemSection sec) { uint SizeData=0, RawData=0, PointerToRawData=0; Extract Ext = sec.Module; long VA = sec.VA.Address; if (dp.MemAccess == null) dp.MemAccess = new Mem(MemAccess); var _va = VA + Ext.DebugDirPos; var block = dp.VGetBlock(_va); var TimeDate2 = BitConverter.ToUInt32(block, ((int) Ext.DebugDirPos & 0xfff) + 4); if(TimeDate2 != Ext.TimeStamp & Vtero.VerboseOutput) { WriteColor(ConsoleColor.Yellow, "Unable to lock on to CV data."); return null; } if(Vtero.VerboseOutput) WriteColor(ConsoleColor.Green, $"Locked on to CV Debug info. Time2 = {TimeDate2:X} Time1 = {Ext.TimeStamp:X}"); var max_offset = (int)(_va & 0xfff) + 28; if (max_offset > 0x1000) { if (Vtero.VerboseOutput) WriteColor(ConsoleColor.Yellow, "CV block seems too straddle into next block (too large)"); return null; } SizeData = BitConverter.ToUInt32(block, (int)(_va & 0xfff) + 16); RawData = BitConverter.ToUInt32(block, (int)(_va & 0xfff) + 20); PointerToRawData = BitConverter.ToUInt32(block, (int)(_va & 0xfff) + 24); _va = VA + RawData; var bytes = new byte[16]; block = dp.VGetBlock(_va); // first 4 bytes var sig = block[((int)_va & 0xfff)]; Array.ConstrainedCopy(block, (((int) _va & 0xfff) + 4), bytes, 0, 16); var gid = new Guid(bytes); // after GUID var age = block[((int)_va & 0xfff) + 20]; // char* at end var str = Encoding.Default.GetString(block, (((int)_va & 0xfff) + 24), 32).Trim(); if (Vtero.VerboseOutput) { WriteLine($"Size = {SizeData} \t Raw = {RawData} \t Pointer {PointerToRawData}"); WriteLine($"Str {str} : GUID : {gid} : AGE {age}"); } var cv = new CODEVIEW_HEADER { VSize = (int) Ext.SizeOfImage, TimeDateStamp = TimeDate2, byteGuid = bytes, Age = age, aGuid = gid, Sig = sig, PdbName = str }; sec.DebugDetails = cv; return cv; }
/// <summary> /// The LinuxS check is a single pass state preserving scanner /// /// This was created using kernel 3.19 as a baseline. More to follow. /// /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool LinuxS(long offset) { var Candidate = false; var group = -1; // The main observation on kern319 is the given set below of must-have offsets that are identical and 0x7f8 which is unique per process // Next is the behavior that uses entries in 2 directions from top down and bottom up // i.e. 0x7f0 0x0 are the next expected values. // All others would be unset in the top level / base page // // Kernel should have only the magnificent entries // memcmp 0 ranges 8-7f0, 800-880, 888-c88, c98-e88, e90-ea0, ea8-ff0 // after first (likely kernel) page table found, use it's lower 1/2 to validate other detected page tables // Linux was found (so far) to have a consistent kernel view. var kern319 = new Dictionary <int, bool> { [0x7f8] = false, [0x880] = true, [0xc90] = true, [0xe88] = true, [0xea0] = true, [0xff0] = true, [0xff8] = true }; var Profiles = new List <Dictionary <int, bool> >(); if (((block[0xFF] & 0xfff) == 0x067) && ((block[0x110] & 0xfff) == 0x067) && ((block[0x192] & 0xfff) == 0x067) && ((block[0x1d1] & 0xfff) == 0x067) && ((block[0x1d4] & 0xfff) == 0x067) && ((block[0x1fe] & 0xfff) == 0x067) && ((block[0x1ff] & 0xfff) == 0x067) && // this is the largest block of 0's // just do this one to qualify IsZero(block, 8, 0xe0) ) /* * if (IsZero(block, 8, 0xE0) && * IsZero(block, 0x100, 0x10) && * IsZero(block, 0x111, 0x80) && * IsZero(block, 0x193, 0x3e) && * IsZero(block, 0x1D2, 0x02) && * IsZero(block, 0x1D5, 0x29)) */ { // before we catalog this entry, check to see if we can put it in a group for (int i = 0; i < LinuxSFirstPages.Count(); i++) { if (EqualBytesLongUnrolled(block, LinuxSFirstPages[i], 0x100)) { group = i; } } // if we haven't found anything yet, setup first page if (LinuxSFirstPage == null) { LinuxSFirstPage = block; LinuxSFirstPages.Add(block); group = 0; } // load DP var dp = new DetectedProc { CR3Value = offset, FileOffset = offset, Diff = 0, Mode = 2, Group = group, PageTableType = PTType.LinuxS }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) { dp.TopPageTablePage.Add(p, block[p]); } } if (Vtero.VerboseOutput) { WriteLine(dp.ToString()); } DetectedProcesses.TryAdd(offset, dp); Candidate = true; } return(Candidate); }
/// <summary> /// This is the same check as the earlier process detection code from CSW and DefCon /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool Windows(long offset) { var Candidate = false; // pre randomized kernel 10.16 anniversario edition const int SELF_PTR = 0x1ed; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[SELF_PTR] & 0xFFFFFFFFF000); var diff = offset - shifted; // detect mode 2, 2 seems good for most purposes and is more portable // maybe 0x3 is sufficient if (((block[0] & 0xfdf) == 0x847) && ((block[SELF_PTR] & 0xff) == 0x63 || (block[SELF_PTR] & 0xff) == 0x67)) { // we disqualify entries that have these bits configured //111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 if ((block[0x1ed] & 0x7FFF000000000480) == 0) { #if MODE_1 if (!SetDiff) { FirstDiff = diff; SetDiff = true; } #endif if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.Windows, TrueOffset = TrueOffset }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) dp.TopPageTablePage.Add(p, block[p]); } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) WriteColor(ConsoleColor.Cyan, ConsoleColor.Black, dp.ToString()); Candidate = true; } } } // mode 1 is implemented to hit on very few supported bits // developing a version close to this that will work for Linux #region MODE 1 IS PRETTY LOOSE #if MODE_1 else /// detect MODE 1, we can probably get away with even just testing & 1, the valid bit //if (((block[0] & 3) == 3) && (block[0x1ed] & 3) == 3) if ((block[0] & 1) == 1 && (block[0xf68 / 8] & 1) == 1) { // a possible kernel first PFN? should look somewhat valid... if (!SetDiff) { // I guess we could be attacked here too, the system kernel could be modified/hooked/bootkit enough // we'll see if we need to analyze this in the long run // the idea of mode 1 is a very low bit-scan, but we also do not want to mess up FirstDiff // these root entries are valid for all win64's for PTE/hyper/session space etc. if ((block[0xf78 / 8] & 1) == 1 && (block[0xf80 / 8] & 1) == 1 && (block[0xff8 / 8] & 1) == 1 && (block[0xff0 / 8] == 0)) { // W/O this we may see some false positives // however can remove if you feel aggressive if (diff < FileSize && (offset > shifted ? (diff + shifted == offset) : (diff + offset == shifted))) { FirstDiff = diff; SetDiff = true; } } } if (SetDiff && !(FirstDiff != diff) && (shifted < (FileSize + diff) //|| shifted != 0 )) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 1, PageTableType = PTType.Windows }; DetectedProcesses.TryAdd(offset, dp); WriteColor(dp); Candidate = true; } } } #endif #endregion return Candidate; }
public long GetSymAddress(DetectedProc dp, string SymName) { var AddrName = SymName + "Address"; if (dp.SymbolStore.ContainsKey(AddrName)) return dp.SymbolStore[AddrName]; DebugHelp.SYMBOL_INFO symInfo = new DebugHelp.SYMBOL_INFO(); symInfo.SizeOfStruct = 0x58; symInfo.MaxNameLen = 1024; var rv = DebugHelp.SymFromName(hCurrentProcess, SymName, ref symInfo); if (!rv) { WriteColor(ConsoleColor.Red, $"GetSymValue: {new Win32Exception(Marshal.GetLastWin32Error()).Message }."); return MagicNumbers.BAD_VALUE_READ; } dp.SymbolStore.Add(AddrName, symInfo.Address); return symInfo.Address; }
/// <summary> /// Only scanning for core kernel modules /// </summary> /// <param name="dp"></param> /// <param name="StartingVA"></param> /// <param name="EndingVA"></param> /// <returns>Array of modules</returns> public ConcurrentDictionary<long, Extract> ModuleScan(DetectedProc dp, long StartingVA = 0, long EndingVA = 0xFFFFFFFFF000) { if (dp.PT == null) PageTable.AddProcess(dp, new Mem(MemAccess)); var cnt = dp.PT.FillPageQueue(true); KVS = new VirtualScanner(dp, new Mem(MemAccess)); KVS.ScanMode = VAScanType.PE_FAST; if (VerboseLevel > 0) { WriteColor(ConsoleColor.Cyan, "Scanning VA for Kernel... "); ForegroundColor = ConsoleColor.Green; } Parallel.For(0, cnt, i => { PFN range; var curr = cnt - dp.PT.PageQueue.Count(); CursorLeft = 0; var done = (int)(Convert.ToDouble(curr) / Convert.ToDouble(cnt) * 100.0) + 0.5; if (VerboseLevel > 0) Write($"{done} % done"); if (dp.PT.PageQueue.TryDequeue(out range)) if (range.PTE.Valid) KVS.Run(range.VA.Address, range.VA.Address + (range.PTE.LargePage ? (1024 * 1024 * 2) : 0x1000)); }); if (VerboseLevel > 0) WriteLine("Done comprehensive VA scan"); foreach (var detected in KVS.Artifacts) dp.Sections.Add(new MemSection() { IsExec = true, Module = detected.Value, VA = new VIRTUAL_ADDRESS(detected.Key) }); return KVS.Artifacts; }
public void AddProcessPageTable(DetectedProc tdp, Mem memAxs) { PageTable.AddProcess(tdp, memAxs, true); }
public VirtualScanner(DetectedProc Ctx, Mem backingBlocks) : this() { DPContext = Ctx; BackingBlocks = backingBlocks; BackingStream = new PhysicalMemoryStream(backingBlocks, Ctx); }
public void ReScanNextLevel(DetectedProc tdp, bool DisplayOutput = false) { bool validInput, ignoreSlat; int levels; do { WriteColor(ConsoleColor.White, "How many levels to process? (1-4)"); var selection = ReadLine(); validInput = int.TryParse(selection, out levels); if (!validInput) WriteLine("invalid response."); } while (!validInput); do { WriteColor(ConsoleColor.White, "Ignore SLAT? (True|False)"); var selection = ReadLine(); validInput = bool.TryParse(selection, out ignoreSlat); if (!validInput) WriteLine("invalid response."); } while (!validInput); if (ignoreSlat) { tdp.vmcs = null; tdp.PT.Root.SLAT = 0; } tdp.PT.FillTable(true, levels); if(DisplayOutput) { var MemRanges = tdp.PT.Root.Entries.SubTables.SelectMany(x => x.Value.SubTables).SelectMany(y => y.Value.SubTables).SelectMany(z => z.Value.SubTables).ToList(); foreach (var mr in MemRanges) WriteColor(ConsoleColor.Cyan, $" {mr.Key} {mr.Value.PTE}"); } }
/// <summary> /// Naturally the Generic checker is fairly chatty but at least you can use it /// to find unknowns, we could use some more tunable values here to help select the /// best match, I currently use the value with the lowest diff, which can be correct /// /// This will find a self pointer in the first memory run for a non-sparse memory dump. /// /// The calling code is expected to adjust offset around RUN gaps. /// /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool Generic(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; //long bestShift = long.MaxValue, bestDiff = long.MaxValue; //var bestOffset = long.MaxValue; var i = 0x1ff; if (((block[0] & 0xff) == 0x63) || (block[0] & 0xfdf) == 0x847) { do { if (((block[i] & 0xff) == 0x63 || (block[i] & 0xff) == 0x67)) { // we disqualify entries that have these bits configured // 111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 // if ((block[i] & 0x7FFF000000000480) == 0) { var shifted = (block[i] & 0xFFFFFFFFF000); if (shifted == offset) { var diff = offset - shifted; // BUGBUG: Need to K-Means this or something cluster values to help detection of processes in sparse format // this could be better var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.GENERIC, TrueOffset = TrueOffset }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) dp.TopPageTablePage.Add(p, block[p]); } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) WriteColor(ConsoleColor.Cyan, ConsoleColor.Black, dp.ToString()); Candidate = true; } } } i--; } while (i > 0xFF); } // maybe some kernels keep more than 1/2 system memory // wouldn't that be a bit greedy though!? return Candidate; }
/// <summary> /// This is the same check as the earlier process detection code from CSW and DefCon /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool Windows(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[0x1ed] & 0xFFFFFFFFF000); var diff = offset - shifted; // detect mode 2, 2 seems good for most purposes and is more portable // maybe 0x3 is sufficient if (((block[0] & 0xfdf) == 0x847) && ((block[0x1ed] & 0xff) == 0x63 || (block[0x1ed] & 0xff) == 0x67)) { // we disqualify entries that have these bits configured //111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 if ((block[0x1ed] & 0x7FFF000000000480) == 0) { #if MODE_1 if (!SetDiff) { FirstDiff = diff; SetDiff = true; } #endif if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.Windows }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) { dp.TopPageTablePage.Add(p, block[p]); } } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) { WriteLine(dp.ToString()); } Candidate = true; } } } // mode 1 is implemented to hit on very few supported bits // developing a version close to this that will work for Linux #region MODE 1 IS PRETTY LOOSE #if MODE_1 else /// detect MODE 1, we can probably get away with even just testing & 1, the valid bit //if (((block[0] & 3) == 3) && (block[0x1ed] & 3) == 3) if ((block[0] & 1) == 1 && (block[0xf68 / 8] & 1) == 1) { // a possible kernel first PFN? should look somewhat valid... if (!SetDiff) { // I guess we could be attacked here too, the system kernel could be modified/hooked/bootkit enough // we'll see if we need to analyze this in the long run // the idea of mode 1 is a very low bit-scan, but we also do not want to mess up FirstDiff // these root entries are valid for all win64's for PTE/hyper/session space etc. if ((block[0xf78 / 8] & 1) == 1 && (block[0xf80 / 8] & 1) == 1 && (block[0xff8 / 8] & 1) == 1 && (block[0xff0 / 8] == 0)) { // W/O this we may see some false positives // however can remove if you feel aggressive if (diff < FileSize && (offset > shifted ? (diff + shifted == offset) : (diff + offset == shifted))) { FirstDiff = diff; SetDiff = true; } } } if (SetDiff && !(FirstDiff != diff) && (shifted < (FileSize + diff) //|| shifted != 0 )) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 1, PageTableType = PTType.Windows }; DetectedProcesses.TryAdd(offset, dp); WriteLine(dp); Candidate = true; } } } #endif #endregion return(Candidate); }
/// <summary> /// In some deployments Hyper-V was found to use a configuration as such /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool HV(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[0x1fe] & 0xFFFFFFFFF000); var diff = offset - shifted; // detect mode 2, 2 seems good for most purposes and is more portable // maybe 0x3 is sufficient if (shifted != 0 && ((block[0] & 0xfff) == 0x063) && ((block[0x1fe] & 0xff) == 0x63 || (block[0x1fe] & 0xff) == 0x67) && block[0x1ff] == 0) { // we disqualify entries that have these bits configured // 111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 // if (((ulong)block[0x1fe] & 0xFFFF000000000480) == 0) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.HyperV, TrueOffset = TrueOffset }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) dp.TopPageTablePage.Add(p, block[p]); } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) WriteColor(ConsoleColor.Cyan, ConsoleColor.Black, dp.ToString()); Candidate = true; } } } return Candidate; }
/// <summary> /// Naturally the Generic checker is fairly chatty but at least you can use it /// to find unknowns, we could use some more tuneables here to help select the /// best match, I currently use the value with the lowest diff, which can be correct /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool Generic(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; long bestShift = long.MaxValue, bestDiff = long.MaxValue; var bestOffset = long.MaxValue; var i = 0x1ff; do { if (((block[0] & 0xff) == 0x63) && block[0x1ff] == 0) { if (((block[i] & 0xff) == 0x63 || (block[i] & 0xff) == 0x67)) { // we disqualify entries that have these bits configured // 111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 // if ((block[i] & 0x7FFF000000000480) == 0) { var shifted = (block[i] & 0xFFFFFFFFF000); if (shifted != 0 && shifted < FileSize) { var diff = offset - shifted; if (diff < bestDiff) { bestShift = shifted; bestDiff = diff; bestOffset = offset; } if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.GENERIC }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) { dp.TopPageTablePage.Add(p, block[p]); } } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) { WriteLine(dp.ToString()); } Candidate = true; } } } } } i--; // maybe some kernels keep more than 1/2 system memory // wouldn't that be a bit greedy though!? } while (i > 0xFF); return(Candidate); }
/// <summary> /// TODO: Make better for all types /// </summary> /// <param name="dp"></param> /// <param name="SymName"></param> /// <returns>Currently a single byte for the address resolved from the Name</returns> public long GetSymValueLong(DetectedProc dp, string SymName) { long value = 0; if (dp.SymbolStore.ContainsKey(SymName)) return dp.SymbolStore[SymName]; value = dp.GetLongValue(GetSymAddress(dp, SymName)); dp.SymbolStore.Add(SymName, value); return value; }
/* OpenBSD /src/sys/arch/amd64/include/pmap.h #define L4_SLOT_PTE 255 #define L4_SLOT_KERN 256 #define L4_SLOT_KERNBASE 511 #define L4_SLOT_DIRECT 510 */ /// <summary> /// Slightly better check then NetBSD so I guess consider it beta! /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool OpenBSD(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; var shifted = (block[255] & 0xFFFFFFFFF000); var diff = offset - shifted; if (((block[510] & 0xf3) == 0x63) && (0x63 == (block[256] & 0xf3)) && (0x63 == (block[254] & 0xf3))) { if (((block[255] & 0xf3) == 0x63) && (0 == (block[255] & 0x7FFF000000000000))) { if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.OpenBSD }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) dp.TopPageTablePage.Add(p, block[p]); } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) WriteLine(dp.ToString()); Candidate = true; } } } return Candidate; }
public dynamic[] WalkProcList(DetectedProc dp) { bool GotData = false; // TODO: Build out symbol system / auto registration into DLR for DIA2 // expected kernel hardcoded var pdbFile = (from kern in dp.Sections where kern.DebugDetails != null && !string.IsNullOrWhiteSpace(kern.DebugDetails.PDBFullPath) && kern.DebugDetails.PDBFullPath.ToLower().Contains("ntkrnlmp") select kern.DebugDetails.PDBFullPath).FirstOrDefault(); if (string.IsNullOrWhiteSpace(pdbFile)) return null; // this is for DIA API SDK... // TODO: Perf analysis of switching over to xStruct... however it's expando objects // are a lot slower than using the dictionary SymForKernel = Sym.Initalize(pdbFile); long[] memRead = null; var PsHeadAddr = GetSymValueLong(dp, "PsActiveProcessHead"); // TODO: update this to be used instead of legacy .Dictionary kludge ;) //var rv = SymForKernel.xStructInfo(pdbFile, "_EPROCESS"); // figure out OFFSET_OF the process LIST_ENTRY // MemberInfo returned is Byte Position, Length var aplinks = SymForKernel.StructMemberInfo(pdbFile, "_EPROCESS", "ActiveProcessLinks.Flink"); var offset_of = aplinks.Item1; var sym_dtb = SymForKernel.StructMemberInfo(pdbFile, "_EPROCESS", "Pcb.DirectoryTableBase"); var sym_ImagePathPtr = SymForKernel.StructMemberInfo(pdbFile, "_EPROCESS", "SeAuditProcessCreationInfo.ImageFileName"); var sym_procID = SymForKernel.StructMemberInfo(pdbFile, "_EPROCESS", "UniqueProcessId"); var sym_vadRoot = SymForKernel.StructMemberInfo(pdbFile, "_EPROCESS", "VadRoot"); // adjust the first link through //var flink = dp.GetLongValue(PsHeadAddr); var typeDefs = from typeDef in SymForKernel.StructInfo where typeDef.Key.StartsWith("_EPROCESS") select typeDef; var flink = PsHeadAddr; do { // walk the offset back to the head of the _EPROCESS // this needs to adjsut since we get the entire block here based to the page not offset memRead = dp.GetVirtualLong((flink - offset_of), ref GotData); if (!GotData) break; // memRead is a long array so we have to divide the length by 8 var EprocCR3 = memRead[sym_dtb.Item1 / 8]; var ProcessID = memRead[sym_procID.Item1 / 8]; var VadRootPtr = memRead[sym_vadRoot.Item1 / 8]; // ImagePath here is a pointer to a struct, get ptr // +0x10 in this unicode string object var ImagePathPtr = memRead[sym_ImagePathPtr.Item1 / 8]; var ImagePathArr = dp.GetVirtualByte(ImagePathPtr+0x10); var ImagePath = Encoding.Unicode.GetString(ImagePathArr); var pathTrim = ImagePath.Split('\x0'); ImagePath = pathTrim[0]; dynamic lproc = new ExpandoObject(); var dproc = (IDictionary<string, object>)lproc; var staticDict = new Dictionary<string, object>(); lproc.Dictionary = staticDict; foreach (var def in typeDefs) { // custom types are not fitted this way // we just recuse into basic types if (def.Value.Item2 > 8) continue; // TODO: expand on this dynamic object stuff var defName = def.Key.Substring("_EPROCESS".Length + 1); //.Replace('.', '_'); switch (def.Value.Item2) { case 4: var ival = (int)(memRead[def.Value.Item1 / 8] & 0xffffffffff); dproc.Add(defName, ival); staticDict.Add(defName, ival); break; case 2: var sval = (short)(memRead[def.Value.Item1 / 8] & 0xffffff); dproc.Add(defName, sval); staticDict.Add(defName, sval); break; case 1: var bval = (byte)(memRead[def.Value.Item1 / 8] & 0xff); dproc.Add(defName, bval); staticDict.Add(defName, bval); break; default: var lval = memRead[def.Value.Item1 / 8]; dproc.Add(defName, lval); staticDict.Add(defName, lval); break; } } lproc.ImagePath = ImagePath; dp.LogicalProcessList.Add(lproc); // move flink to next list entry flink = memRead[offset_of / 8]; } while (PsHeadAddr != flink); return dp.LogicalProcessList.ToArray(); }
/// <summary> /// Naturally the Generic checker is fairly chatty but at least you can use it /// to find unknowns, we could use some more tuneables here to help select the /// best match, I currently use the value with the lowest diff, which can be correct /// </summary> /// <param name="offset"></param> /// <returns></returns> public bool Generic(long offset) { var Candidate = false; //var offset = CurrWindowBase + CurrMapBase; long bestShift = long.MaxValue, bestDiff = long.MaxValue; var bestOffset = long.MaxValue; var i = 0x1ff; do { if (((block[0] & 0xff) == 0x63) && block[0x1ff] == 0) { if (((block[i] & 0xff) == 0x63 || (block[i] & 0xff) == 0x67)) { // we disqualify entries that have these bits configured // 111 1111 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 1000 0000 // if ((block[i] & 0x7FFF000000000480) == 0) { var shifted = (block[i] & 0xFFFFFFFFF000); if (shifted != 0 && shifted < FileSize) { var diff = offset - shifted; if (diff < bestDiff) { bestShift = shifted; bestDiff = diff; bestOffset = offset; } if (!DetectedProcesses.ContainsKey(offset)) { var dp = new DetectedProc { CR3Value = shifted, FileOffset = offset, Diff = diff, Mode = 2, PageTableType = PTType.GENERIC }; for (int p = 0; p < 0x200; p++) { if (block[p] != 0) dp.TopPageTablePage.Add(p, block[p]); } DetectedProcesses.TryAdd(offset, dp); if (Vtero.VerboseOutput) WriteLine(dp.ToString()); Candidate = true; } } } } } i--; // maybe some kernels keep more than 1/2 system memory // wouldn't that be a bit greedy though!? } while (i > 0xFF); return Candidate; }
public static void DumpIt(Vtero vtero, ConfigOptions co, DumpOptions dmpo) { var Version = vtero.Version; Mem.InitMem(co.FileName, vtero.MRD); // Extract Address Spaces verifies the linkages between // process<->CR3<->EPTP(if there is one) // and that they are functional var vetted = vtero.ExtrtactAddressSpaces(null, null, Version); // leaving this in as an example maybe? ;) //WriteLine("enter a group ID: "); //input = ReadLine(); //int Grp = int.Parse(input); //WriteLine("enter a process ID: "); //input = ReadLine(); //long procID = long.Parse(input, NumberStyles.HexNumber); //var proc = (from procz in vtero.ASGroups[Grp] // where procz.CR3Value == procID // select procz).First(); //int i = 1; //DetectedProc dp = proc; //while(dp == null) // dp = vtero.GetKernelRangeFromGroup(i++); // Scan for kernel // NT kernel may be in 0xFFFFF80000000 to 0xFFFFF8800000 range long KernVAStart = 0xF80000000000; long KernVAEnd = KernVAStart + (0x8000000000 - 0x1000); string input = string.Empty; var Detections = new Dictionary <long, Extract>(); DetectedProc LikelyKernel = null; bool Decoded = false; // were doing this in nested loops to brute force our way past any errors // but only need the first set of detections per group foreach (var grpz in vtero.ASGroups) { foreach (var vm in vtero.VMCSs.Values) { WriteColor(ConsoleColor.White, $"Group ID: {grpz.Key}"); foreach (var p in grpz.Value) { WriteLine($"Proc: {p.CR3Value:X}"); Detections = Detections.Concat( vtero.ModuleScan(p, 3, KernVAStart, KernVAEnd).Where(x => !Detections.ContainsKey(x.Key))) .ToDictionary(x => x.Key, x => x.Value); if (Detections.Count() > 0) { LikelyKernel = p; if (vm.EPTP == 0) { p.vmcs = null; } else { p.vmcs = vm; } // scan for kernel foreach (var detected in Detections) { WriteColor(ConsoleColor.Green, $"Attempting to parse detected PE module loaded @ {detected.Key:X}"); WriteColor(ConsoleColor.Cyan, detected.Value.ToString()); if (detected.Value.ToString().Contains("POOLCODE")) { WriteColor(ConsoleColor.White, "Likely Kernel analyzing for CV data"); /* * var cv_data = vtero.ExtractCVDebug(LikelyKernel, detected.Value, detected.Key); * * if (cv_data != null) * { * var sympath = Environment.GetEnvironmentVariable("_NT_SYMBOL_PATH"); * if (string.IsNullOrWhiteSpace(sympath)) * sympath = "SRV*http://msdl.microsoft.com/download/symbols"; * * if (Vtero.TryLoadSymbols(cv_data, detected.Key, sympath)) * Decoded = vtero.GetKernelDebuggerData(LikelyKernel, detected.Value, cv_data, sympath); * } */ } } } if (Decoded) { break; } } if (Decoded) { break; } } if (Decoded) { break; } } ForegroundColor = ConsoleColor.Green; WriteLine($"{Environment.NewLine}Final analysis completed, address spaces extracted. {QuickOptions.Timer.Elapsed} {QuickOptions.FormatRate(vtero.FileSize * 3, QuickOptions.Timer.Elapsed)}"); // do a test dump // extract & dump could be done at the same time if (!dmpo.ListOnly) { vtero.DumpASToFile(); } //if (Vtero.VerboseOutput) //vtero.DumpFailList(); return; }
public long DumpProc(string Folder, DetectedProc Proc, bool IncludeData = false, bool KernelSpace = true) { //var entries = PageTable.Flatten(Proc.PT.Root.Entries.SubTables, 4); VIRTUAL_ADDRESS VA; VA.Address = 0; var PageTables = new Dictionary<VIRTUAL_ADDRESS, PFN>(); int level = 3; long entries = 0; if (Proc.PT == null) PageTable.AddProcess(Proc, MemAccess); foreach (var kvp in Proc.TopPageTablePage) { // were at the top level (4th) VA.PML4 = kvp.Key; var pfn = new PFN { PTE = kvp.Value, VA = new VIRTUAL_ADDRESS(VA.PML4 << 39) }; // Top level for page table PageTables.Add(VA, pfn); foreach (var DirectoryPointerOffset in Proc.PT.ExtractNextLevel(pfn, true, level)) { if (DirectoryPointerOffset == null) continue; foreach (var DirectoryOffset in Proc.PT.ExtractNextLevel(DirectoryPointerOffset, KernelSpace, level - 1)) { if (DirectoryOffset == null) continue; foreach (var TableOffset in Proc.PT.ExtractNextLevel(DirectoryOffset, KernelSpace, level - 2)) { if (TableOffset == null) continue; entries++; if (IncludeData == TableOffset.PTE.NoExecute) WriteRange(TableOffset.VA, TableOffset, Folder, MemAccess); if (!TableOffset.PTE.NoExecute) WriteRange(TableOffset.VA, TableOffset, Folder, MemAccess); } entries++; if (IncludeData == DirectoryOffset.PTE.NoExecute) WriteRange(DirectoryOffset.VA, DirectoryOffset, Folder, MemAccess); if (!DirectoryOffset.PTE.NoExecute) WriteRange(DirectoryOffset.VA, DirectoryOffset, Folder, MemAccess); } entries++; } } return entries; }
public VirtualScanner(DetectedProc Ctx, Mem backingBlocks, bool DoHeaderScan = false) : this() { DPContext = Ctx; BackingBlocks = backingBlocks; HeaderScan = DoHeaderScan; }
/// <summary> /// Prefer symbol loading. /// </summary> /// <param name="dp"></param> /// <param name="ext"></param> /// <param name="cv_data"></param> /// <param name="SymbolCache"></param> /// <returns></returns> public bool GetKernelDebuggerData(DetectedProc dp, Extract ext, CODEVIEW_HEADER cv_data, string SymbolCache) { DebugHelp.SYMBOL_INFO symInfo = new DebugHelp.SYMBOL_INFO(); bool rv = false; bool GotData = false; // Locate and extract some data points symInfo.SizeOfStruct = 0x58; symInfo.MaxNameLen = 1024; rv = DebugHelp.SymFromName(hCurrentProcess, "KdpDataBlockEncoded", ref symInfo); if(!rv) { WriteLine($"Symbol Find : {new Win32Exception(Marshal.GetLastWin32Error()).Message }."); return rv; } KernelProc = dp; // at this point we should return true if it's encoded or not rv = true; return rv; #if FALSE I'm leaving this in for now just to show the use of DecodePointer if needed since it could be uswed in a scenerio where symbols fail var KdpDataBlockEncoded = dp.GetByteValue(symInfo.Address); // Convention is to use *Address for addresses or the simple name is the value it is assumed to be a pointer dp.SymbolStore["KdDebuggerDataBlockAddress"] = GetSymAddress(dp, "KdDebuggerDataBlock"); if (KdpDataBlockEncoded == 0) WriteColor(ConsoleColor.Green, $"Kernel KdDebuggerDataBlock @ {dp.SymbolStore["KdDebuggerDataBlockAddress"]:X16} not encoded."); else { #if FALSE_NOT_NEEDED_IF_WE_USE_SYMBOLS var KdDebuggerDataBlock = dp.VGetBlockLong(dp.KdDebuggerDataBlockAddress, ref GotData); if (!GotData) WriteColor(ConsoleColor.Red, "Unable to read debuggerdatablock array"); // Windbg tells us the diff for loaded modules is 0x48 and active proc is 0x50 var EncLoadedModuleList = KdDebuggerDataBlock[9]; var EncActiveProcessList = KdDebuggerDataBlock[0xA]; var PsLoadedModuleList = (long) DecodePointer((ulong) dp.KdDebuggerDataBlockAddress, (ulong)dp.KiWaitAlways, (ulong)dp.KiWaitNever,(ulong) EncLoadedModuleList); var PsActiveProcessHead = (long) DecodePointer((ulong) dp.KdDebuggerDataBlockAddress, (ulong)dp.KiWaitAlways, (ulong)dp.KiWaitNever, (ulong) EncActiveProcessList); WriteColor(ConsoleColor.Cyan, $"Decoded LoadedModuleList {PsLoadedModuleList}, ActiveProcessList {PsActiveProcessHead}"); #endif } return rv; #endif }