/// <summary> /// The VMCS scan is based on the LINK pointer, abort code and CR3 register /// We later isolate the EPTP based on constraints for that pointer /// </summary> /// <param name="xoffset"></param> /// <returns>true if the page being scanned is a candidate</returns> public bool VMCS(long xoffset) { var RevID = (REVISION_ID)(block[0] & 0xffffffff); var Acode = (VMCS_ABORT)((block[0] >> 32) & 0x7fffffff); var KnownAbortCode = false; var KnownRevision = false; var Candidate = false; var LinkCount = 0; var Neg1 = -1; if (ScanForVMCSset == null) throw new NullReferenceException("Entered VMCS callback w/o having found any VMCS, this is a second pass Func"); // this might be a bit micro-opt-pointless ;) KnownRevision = typeof(REVISION_ID).GetEnumValues().Cast<REVISION_ID>().Any(x => x == RevID); KnownAbortCode = typeof(VMCS_ABORT).GetEnumValues().Cast<VMCS_ABORT>().Any(x => x == Acode); // TODO: Relax link pointer check. Possible when VMCS is shadow, then the link pointer is configured, retest this detection/nesting etc.. // Find a 64bit value for link ptr for (int l = 0; l < block.Length; l++) { if (block[l] == Neg1) LinkCount++; // too many if (LinkCount > 32) return false; } // Currently, we expect to have 1 Link pointer at least if (LinkCount == 0 || !KnownAbortCode) return false; // curr width of line to screen Candidate = false; Parallel.For(0, ScanForVMCSset.Length, (v) => { var ScanFor = ScanForVMCSset[v]; for (int check = 1; check < block.Length; check++) { if (block[check] == ScanFor.CR3Value && Candidate == false) { var OutputList = new List<long>(); StringBuilder sb = null, sbRED = null; byte[] shorted = null; var curr_width = 0; if (Vtero.VerboseOutput) { sb = new StringBuilder(); // reverse endianness for easy reading in hex dumps/editors shorted = BitConverter.GetBytes(block[check]); Array.Reverse(shorted, 0, 8); var Converted = BitConverter.ToUInt64(shorted, 0); sbRED = new StringBuilder(); sbRED.Append($"Hypervisor: VMCS revision field: {RevID} [{((uint)RevID):X8}] abort indicator: {Acode} [{((int)Acode):X8}]{Environment.NewLine}"); sbRED.Append($"Hypervisor: {ScanFor.PageTableType} CR3 found [{ScanFor.CR3Value:X16})] byte-swapped: [{Converted:X16}] @ PAGE/File Offset = [{xoffset:X16}]"); } for (int i = 0; i < block.Length; i++) { var value = block[i]; var eptp = new EPTP(value); // any good minimum size? 64kb? if (block[i] > 0 && block[i] < FileSize && eptp.IsFullyValidated() // && EPTP.IsValid(eptp.aEPTP) && EPTP.IsValid2(eptp.aEPTP) && EPTP.IsValidEntry(eptp.aEPTP) && !OutputList.Contains(block[i])) { Candidate = true; OutputList.Add(block[i]); if (Vtero.VerboseOutput) { var linefrag = $"[{i}][{block[i]:X16}] "; if (curr_width + linefrag.Length > MAX_LINE_WIDTH) { sb.Append(Environment.NewLine); curr_width = 0; } sb.Append(linefrag); curr_width += linefrag.Length; } } } if (Candidate && Vtero.VerboseOutput) { WColor(ConsoleColor.Red, ConsoleColor.Black, sbRED.ToString().PadRight(WindowWidth)); WColor(ConsoleColor.DarkGreen, ConsoleColor.Black, sb.ToString().PadRight(WindowWidth)); } // most VMWare I've scanned comes are using this layout // we know VMWare well so ignore any other potential candidates // TODO: Constantly Verify assumption's if (RevID == REVISION_ID.VMWARE_NESTED && OutputList.Contains(block[14])) { var vmcsFound = new VMCS { dp = ScanFor, EPTP = block[14], gCR3 = ScanFor.CR3Value, Offset = xoffset }; HVLayer.Add(vmcsFound); } else foreach (var entry in OutputList) HVLayer.Add(new VMCS { dp = ScanFor, EPTP = entry, gCR3 = ScanFor.CR3Value, Offset = xoffset }); } } }); return Candidate; }
/// <summary> /// The VMCS scan is based on the LINK pointer, abort code and CR3 register /// We later isolate the EPTP based on constraints for that pointer /// </summary> /// <param name="xoffset"></param> /// <returns>true if the page being scanned is a candidate</returns> public bool VMCS(long xoffset) { var RevID = (REVISION_ID)(block[0] & 0xffffffff); var Acode = (VMCS_ABORT)((block[0] >> 32) & 0x7fffffff); var KnownAbortCode = false; var KnownRevision = false; var Candidate = false; var LinkCount = 0; var Neg1 = -1; if(VMCSScanSet == null) throw new NullReferenceException("Entered VMCS callback w/o having found any VMCS, this is a second pass Func") ; // this might be a bit micro-opt-pointless ;) //Parallel.Invoke(() => //{ KnownRevision = typeof(REVISION_ID).GetEnumValues().Cast<REVISION_ID>().Any(x => x == RevID); //}, () => //{ KnownAbortCode = typeof(VMCS_ABORT).GetEnumValues().Cast<VMCS_ABORT>().Any(x => x == Acode); //}); // TODO: Link pointer may not always be needed, evaluate removing this constraint // Find a 64bit value for link ptr for (int l = 0; l < block.Length; l++) { if (block[l] == Neg1) LinkCount++; // too many if (LinkCount > 32) return false; } // Currently, we expect to have 1 Link pointer at least if (LinkCount == 0 || !KnownAbortCode) return false; // curr width of line to screen Candidate = false; Parallel.For(0, VMCSScanSet.Length, (v) => { var vmcs_entry = VMCSScanSet[v]; for (int check = 1; check < block.Length; check++) { if (block[check] == vmcs_entry.CR3Value && Candidate == false) { var OutputList = new List<long>(); StringBuilder sb = null, sbRED = null; byte[] shorted = null; var curr_width = 0; if (Vtero.VerboseOutput) { sb = new StringBuilder(); // reverse endianess for easy reading in hex dumps/editors shorted = BitConverter.GetBytes(block[check]); Array.Reverse(shorted, 0, 8); var Converted = BitConverter.ToUInt64(shorted, 0); sbRED = new StringBuilder(); sbRED.Append($"Hypervisor: VMCS revision field: {RevID} [{((uint)RevID):X8}] abort indicator: {Acode} [{((int)Acode):X8}]{Environment.NewLine}"); sbRED.Append($"Hypervisor: {vmcs_entry.PageTableType} CR3 found [{vmcs_entry.CR3Value:X16})] byte-swapped: [{Converted:X16}] @ PAGE/File Offset = [{xoffset:X16}]"); } for (int i = 0; i < block.Length; i++) { var eptp = new EPTP(block[i]); // any good minimum size? 64kb? if (block[i] > 0 && block[i] < FileSize && eptp.IsFullyValidated() && !OutputList.Contains(block[i])) { Candidate = true; OutputList.Add(block[i]); if (Vtero.VerboseOutput) { var linefrag = $"[{i}][{block[i]:X16}] "; if (curr_width + linefrag.Length > MAX_LINE_WIDTH) { sb.Append(Environment.NewLine); curr_width = 0; } sb.Append(linefrag); curr_width += linefrag.Length; } } } if (Candidate && Vtero.VerboseOutput) { ForegroundColor = ConsoleColor.Red; WriteLine(sbRED.ToString()); ForegroundColor = ConsoleColor.DarkGreen; WriteLine(sb.ToString()); } VMCS vmcsFound = null; // most VMWare I've scanned comes are using this layout if (RevID == REVISION_ID.VMWARE_NESTED && OutputList.Contains(block[14])) vmcsFound = new VMCS { dp = vmcs_entry, EPTP = block[14], gCR3 = vmcs_entry.CR3Value }; else if (OutputList.Count() == 1) vmcsFound = new net.VMCS { dp = vmcs_entry, EPTP = OutputList[0], gCR3 = vmcs_entry.CR3Value }; if (vmcsFound != null) HVLayer.Add(vmcsFound); } } }); return Candidate; }
/// <summary> /// The VMCS scan is based on the LINK pointer, abort code and CR3 register /// We later isolate the EPTP based on constraints for that pointer /// </summary> /// <param name="xoffset"></param> /// <returns>true if the page being scanned is a candidate</returns> public bool VMCS(long xoffset) { var RevID = (REVISION_ID)(block[0] & 0xffffffff); var Acode = (VMCS_ABORT)((block[0] >> 32) & 0x7fffffff); var KnownAbortCode = false; var KnownRevision = false; var Candidate = false; var LinkCount = 0; var Neg1 = -1; if (VMCSScanSet == null) { throw new NullReferenceException("Entered VMCS callback w/o having found any VMCS, this is a second pass Func"); } // this might be a bit micro-opt-pointless ;) //Parallel.Invoke(() => //{ KnownRevision = typeof(REVISION_ID).GetEnumValues().Cast <REVISION_ID>().Any(x => x == RevID); //}, () => //{ KnownAbortCode = typeof(VMCS_ABORT).GetEnumValues().Cast <VMCS_ABORT>().Any(x => x == Acode); //}); // TODO: Link pointer may not always be needed, evaluate removing this constraint // Find a 64bit value for link ptr for (int l = 0; l < block.Length; l++) { if (block[l] == Neg1) { LinkCount++; } // too many if (LinkCount > 32) { return(false); } } // Currently, we expect to have 1 Link pointer at least if (LinkCount == 0 || !KnownAbortCode) { return(false); } // curr width of line to screen Candidate = false; Parallel.For(0, VMCSScanSet.Length, (v) => { var vmcs_entry = VMCSScanSet[v]; for (int check = 1; check < block.Length; check++) { if (block[check] == vmcs_entry.CR3Value && Candidate == false) { var OutputList = new List <long>(); StringBuilder sb = null, sbRED = null; byte[] shorted = null; var curr_width = 0; if (Vtero.VerboseOutput) { sb = new StringBuilder(); // reverse endianess for easy reading in hex dumps/editors shorted = BitConverter.GetBytes(block[check]); Array.Reverse(shorted, 0, 8); var Converted = BitConverter.ToUInt64(shorted, 0); sbRED = new StringBuilder(); sbRED.Append($"Hypervisor: VMCS revision field: {RevID} [{((uint)RevID):X8}] abort indicator: {Acode} [{((int)Acode):X8}]{Environment.NewLine}"); sbRED.Append($"Hypervisor: {vmcs_entry.PageTableType} CR3 found [{vmcs_entry.CR3Value:X16})] byte-swapped: [{Converted:X16}] @ PAGE/File Offset = [{xoffset:X16}]"); } for (int i = 0; i < block.Length; i++) { var eptp = new EPTP(block[i]); // any good minimum size? 64kb? if (block[i] > 0 && block[i] < FileSize && eptp.IsFullyValidated() && !OutputList.Contains(block[i])) { Candidate = true; OutputList.Add(block[i]); if (Vtero.VerboseOutput) { var linefrag = $"[{i}][{block[i]:X16}] "; if (curr_width + linefrag.Length > MAX_LINE_WIDTH) { sb.Append(Environment.NewLine); curr_width = 0; } sb.Append(linefrag); curr_width += linefrag.Length; } } } if (Candidate && Vtero.VerboseOutput) { ForegroundColor = ConsoleColor.Red; WriteLine(sbRED.ToString()); ForegroundColor = ConsoleColor.DarkGreen; WriteLine(sb.ToString()); } VMCS vmcsFound = null; // most VMWare I've scanned comes are using this layout if (RevID == REVISION_ID.VMWARE_NESTED && OutputList.Contains(block[14])) { vmcsFound = new VMCS { dp = vmcs_entry, EPTP = block[14], gCR3 = vmcs_entry.CR3Value } } ; else if (OutputList.Count() == 1) { vmcsFound = new net.VMCS { dp = vmcs_entry, EPTP = OutputList[0], gCR3 = vmcs_entry.CR3Value } } ; if (vmcsFound != null) { HVLayer.Add(vmcsFound); } } } }); return(Candidate); }
// Translates virtual address to physical address by way of CR3->EPTP double dereferencing (up to 24 loads) public HARDWARE_ADDRESS_ENTRY VirtualToPhysical(HARDWARE_ADDRESS_ENTRY eptp, HARDWARE_ADDRESS_ENTRY aCR3, long Addr) { var rv = HARDWARE_ADDRESS_ENTRY.MinAddr; var va = new VIRTUAL_ADDRESS(Addr); var gVa = new VIRTUAL_ADDRESS(aCR3.PTE); var Attempted = HARDWARE_ADDRESS_ENTRY.MinAddr; var ConvertedV2hP = new List <HARDWARE_ADDRESS_ENTRY>(); try { Attempted = gVa.Address; //convert Guest CR3 gPA into Host CR3 pPA var gpaCR3 = VirtualToPhysical(eptp, gVa.Address); //Console.WriteLine($"In V2P2P, using CR3 {aCR3.PTE:X16}, found guest phys CR3 {gpaCR3.PTE:X16}, attempting load of PML4E from {(gpaCR3 | va.PML4):X16}"); // gPML4E - as we go were getting gPA's which need to pPA Attempted = gpaCR3.NextTableAddress | va.PML4; var gPML4E = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPML4E); //Console.WriteLine($"guest PML4E = {gPML4E}"); // take CR3 and extract gPhys for VA we want to query var hPML4E = VirtualToPhysical(eptp, gPML4E.NextTableAddress); if (EPTP.IsValid(hPML4E.PTE) && EPTP.IsValid2(hPML4E.PTE) && HARDWARE_ADDRESS_ENTRY.IsBadEntry(hPML4E)) { Attempted = hPML4E.NextTableAddress | (va.DirectoryPointerOffset << 3); var gPDPTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPDPTE); var hPDPTE = VirtualToPhysical(eptp, gPDPTE.NextTableAddress); if (EPTP.IsValid(hPDPTE.PTE)) { if (!EPTP.IsLargePDPTE(hPDPTE.PTE)) { if (EPTP.IsValid2(hPDPTE.PTE)) { Attempted = hPDPTE.NextTableAddress | (va.DirectoryOffset << 3); var gPDE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPDE); var hPDE = VirtualToPhysical(eptp, gPDE.NextTableAddress); if (EPTP.IsValid(hPDE.PTE)) { if (!EPTP.IsLargePDE(hPDE.PTE)) { if (EPTP.IsValid2(hPDE.PTE)) { Attempted = hPDE.NextTableAddress | (va.TableOffset << 3); var gPTE = (HARDWARE_ADDRESS_ENTRY)GetValueAtPhysicalAddr(Attempted); ConvertedV2hP.Add(gPTE); var hPTE = VirtualToPhysical(eptp, gPTE.NextTableAddress); if (EPTP.IsValidEntry(hPTE.PTE)) { rv = hPTE.NextTableAddress | va.Offset; } } } else { rv = (hPDE.PTE & 0xFFFFFFE00000) | va.TableOffset; //(Addr & 0x1FFFFF); } } } } else { rv = (hPDPTE.PTE & 0xFFFFC0000000) | va.DirectoryOffset; //(Addr & 0x3FFFFFFF); } } } } catch (PageNotFoundException ex) { throw new ExtendedPageNotFoundException( $"V2gP2hP conversion error. EPTP:{eptp}, CR3:{aCR3}, Requesting:{Attempted} Step:{ConvertedV2hP.Count()}. Step of 0 may indicate invalid EPTP.{Environment.NewLine}" , eptp, aCR3, Attempted, ConvertedV2hP, ex); } return(rv); }
/// <summary> /// The VMCS scan is based on the LINK pointer, abort code and CR3 register /// We later isolate the EPTP based on constraints for that pointer /// </summary> /// <param name="xoffset"></param> /// <returns>true if the page being scanned is a candidate</returns> public bool VMCS(int bo, long xoffset) { var RevID = (REVISION_ID)(block[bo + 0] & 0xffffffff); var Acode = (VMCS_ABORT)((block[bo + 0] >> 32) & 0x7fffffff); var KnownAbortCode = false; var KnownRevision = false; var Candidate = false; var LinkCount = 0; var Neg1 = -1; if (ScanForVMCSset == null) { throw new NullReferenceException("Entered VMCS callback w/o having found any VMCS, this is a second pass Func"); } // this might be a bit micro-opt-pointless ;) KnownRevision = typeof(REVISION_ID).GetEnumValues().Cast <REVISION_ID>().Any(x => x == RevID); KnownAbortCode = typeof(VMCS_ABORT).GetEnumValues().Cast <VMCS_ABORT>().Any(x => x == Acode); // TODO: Relax link pointer check. Possible when VMCS is shadow, then the link pointer is configured, retest this detection/nesting etc.. // Find a 64bit value for link ptr for (int l = 0; l < block.Length; l++) { if (block[bo + l] == Neg1) { LinkCount++; } // too many if (LinkCount > 32) { return(false); } } // Currently, we expect to have 1 Link pointer at least if (LinkCount == 0 || !KnownAbortCode) { return(false); } // curr width of line to screen Candidate = false; Parallel.For(0, ScanForVMCSset.Length, (v) => { var ScanFor = ScanForVMCSset[v]; for (int check = 1; check < block.Length; check++) { if (block[bo + check] == ScanFor.CR3Value && Candidate == false) { var OutputList = new List <long>(); StringBuilder sb = null, sbRED = null; byte[] shorted = null; var curr_width = 0; if (Vtero.VerboseOutput) { sb = new StringBuilder(); // reverse endianness for easy reading in hex dumps/editors shorted = BitConverter.GetBytes(block[bo + check]); Array.Reverse(shorted, 0, 8); var Converted = BitConverter.ToUInt64(shorted, 0); sbRED = new StringBuilder(); sbRED.Append($"Hypervisor: VMCS revision field: {RevID} [{((uint)RevID):X8}] abort indicator: {Acode} [{((int)Acode):X8}]{Environment.NewLine}"); sbRED.Append($"Hypervisor: {ScanFor.PageTableType} CR3 found [{ScanFor.CR3Value:X16})] byte-swapped: [{Converted:X16}] @ PAGE/File Offset = [{xoffset:X16}]"); } for (int i = 0; i < block.Length; i++) { var value = block[bo + i]; var eptp = new EPTP(value); // any good minimum size? 64kb? if (block[bo + i] > 0 && block[bo + i] < FileSize && eptp.IsFullyValidated() // && EPTP.IsValid(eptp.aEPTP) && EPTP.IsValid2(eptp.aEPTP) && EPTP.IsValidEntry(eptp.aEPTP) && !OutputList.Contains(block[bo + i])) { Candidate = true; OutputList.Add(block[bo + i]); if (Vtero.VerboseOutput) { var linefrag = $"[{i}][{block[bo + i]:X16}] "; if (curr_width + linefrag.Length > MAX_LINE_WIDTH) { sb.Append(Environment.NewLine); curr_width = 0; } sb.Append(linefrag); curr_width += linefrag.Length; } } } if (Candidate && Vtero.VerboseOutput) { WColor(ConsoleColor.Red, ConsoleColor.Black, sbRED.ToString().PadRight(WindowWidth)); WColor(ConsoleColor.DarkGreen, ConsoleColor.Black, sb.ToString().PadRight(WindowWidth)); } // most VMWare I've scanned comes are using this layout // we know VMWare well so ignore any other potential candidates // TODO: Constantly Verify assumption's if (RevID == REVISION_ID.VMWARE_NESTED && OutputList.Contains(block[bo + 14])) { var vmcsFound = new VMCS { dp = ScanFor, EPTP = block[bo + 14], gCR3 = ScanFor.CR3Value, Offset = xoffset }; HVLayer.Add(vmcsFound); } else { foreach (var entry in OutputList) { HVLayer.Add(new VMCS { dp = ScanFor, EPTP = entry, gCR3 = ScanFor.CR3Value, Offset = xoffset }); } } } } }); return(Candidate); }