Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@tandasat
Last active November 28, 2023 19:09
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tandasat/9c17ca9aae9503360b28263b9cad1d6e to your computer and use it in GitHub Desktop.
Save tandasat/9c17ca9aae9503360b28263b9cad1d6e to your computer and use it in GitHub Desktop.
"use strict";
// This script implements the !dump_vmcs command that displays values of the all
// fields in the current VMCS. The processor must be in VMX-root operation with
// an active VMCS.
//
// As a reference, there are some other implementations of the same concept. The
// author is now aware of those two at least. Check them out as it may fit your
// need better.
// - https://github.com/ergot86/crap/blob/main/hyperv_stuff.js (Windbg JavaScript)
// - https://github.com/gerhart01/Hyper-V-scripts/blob/master/display-vmcs.py (IDA Python)
function initializeScript() {
return [
new host.apiVersionSupport(1, 7),
new host.functionAlias(dump_vmcs, "dump_vmcs"),
];
}
const print = e => host.diagnostics.debugLog(e);
const println = e => print(e + "\n");
const hex = e => "0x" + e.toString(16);
// Dumps the current VMCS.
function dump_vmcs() {
const User = host.currentThread.Registers.User;
const Control = host.namespace.Debugger.Utility.Control;
// Capture the current state.
let original_rax = User.rax.toString(16);
let original_rip = User.rip.toString(16);
let original_rflags = User.efl.toString(16);
// Find the virtual address of (the first) "VMREAD RAX, RAX" in the HV image
// range.
let dest = find_first_vmread_rax_rax();
// Loop over the VMCS encodings.
for (let i = 0; i < VmcsEncodings.length; i += 2) {
let name = VmcsEncodings[i];
let encoding = VmcsEncodings[i + 1];
// Jump (back) to "VMREAD RAX, RAX", update RAX with encoding to read,
// and execute the instruction.
Control.ExecuteCommand("r rip=" + hex(dest));
Control.ExecuteCommand("r rax=" + hex(encoding));
if (Control.ExecuteCommand("t").Last().includes("second chance")) {
throw new Error("CPU exception occurred with the VMREAD instruction." +
" Reboot the system with the .reboot command. This can happen" +
" when the system in an early boot stage where the processor is" +
" not in VMX operation.");
}
// Check whether the VMREAD instruction succeeded.
// eg: efl=00000342
let flags = host.parseInt64(
Control.ExecuteCommand("r efl").Last().substring(4), 16);
if ((flags & 0x41) == 0) { // if CF==0 && ZF==0
// Succeeded. RAX should contain the field value.
// eg: rax=0000000000000000
let rax = Control.ExecuteCommand("r rax").Last().substring(4);
println(rax + " " + name);
} else {
// VMREAD failed.
println("**** FAILED ****" + " " + name);
}
}
// All done. Restore the original state.
Control.ExecuteCommand("r rax=" + original_rax);
Control.ExecuteCommand("r rip=" + original_rip);
Control.ExecuteCommand("r efl=" + original_rflags);
}
// Returns the first virtual address with "VMREAD RAX, RAX" in the HV address range.
function find_first_vmread_rax_rax() {
const is_vmread_rax_rax = (memory, i) => (
memory[i] == 0x0f &&
memory[i + 1] == 0x78 &&
memory[i + 2] == 0xc0);
// Search each page in the HV image range. Returns the first hit.
let hv = find_hv_range();
for (let i = hv.Start; i.compareTo(hv.End) == -1; i = i.add(0x1000)) {
let found = search_memory(i, 0x1000, is_vmread_rax_rax);
if (found.length != 0) {
return found[0];
}
}
throw new Error("No VMREAD RAX, RAX (0f 78 c0) found.");
}
// Returns the range of the virtual address where the HV image is mapped.
function find_hv_range() {
const Control = host.namespace.Debugger.Utility.Control;
// Get the range with "lm".
// eg: fffff876`b89e0000 fffff876`b8de2000 hv (no symbols)
let chunks = Control.ExecuteCommand("lm m hv").Last().split(" ");
let start = chunks[0].replace("`", "");
let end = chunks[1].replace("`", "");
return {
"Start": host.parseInt64(start, 16),
"End": host.parseInt64(end, 16),
};
}
// Finds an array of virtual addresses that matches the specified condition.
function search_memory(address, bytes, predicate) {
// Memory read can fail if the address is not mapped.
try {
var memory = host.memory.readMemoryValues(address, bytes);
} catch (error) {
return [];
}
let index = [];
for (let i = 0; i < bytes; i++) {
if (predicate(memory, i)) {
index.push(address.add(i));
}
}
return index;
}
// The list of VMCS encodings as of the revision 79, March 2023.
const VmcsEncodings = [
"Virtual-processor identifier (VPID)", 0x00000000,
"Posted-interrupt notification vector", 0x00000002,
"EPTP index", 0x00000004,
"HLAT prefix size", 0x00000006,
"Last PID-pointer", 0x00000008,
"Guest ES selector", 0x00000800,
"Guest CS selector", 0x00000802,
"Guest SS selector", 0x00000804,
"Guest DS selector", 0x00000806,
"Guest FS selector", 0x00000808,
"Guest GS selector", 0x0000080A,
"Guest LDTR selector", 0x0000080C,
"Guest TR selector", 0x0000080E,
"Guest interrupt status", 0x00000810,
"PML index", 0x00000812,
"Guest UINV", 0x00000814,
"Host ES selector", 0x00000C00,
"Host CS selector", 0x00000C02,
"Host SS selector", 0x00000C04,
"Host DS selector", 0x00000C06,
"Host FS selector", 0x00000C08,
"Host GS selector", 0x00000C0A,
"Host TR selector", 0x00000C0C,
"Address of I/O bitmap A", 0x00002000,
"Address of I/O bitmap B", 0x00002002,
"Address of MSR bitmaps", 0x00002004,
"VM-exit MSR-store address", 0x00002006,
"VM-exit MSR-load address", 0x00002008,
"VM-entry MSR-load address", 0x0000200A,
"Executive-VMCS pointer", 0x0000200C,
"PML address", 0x0000200E,
"TSC offset", 0x00002010,
"Virtual-APIC address", 0x00002012,
"APIC-access address", 0x00002014,
"Posted-interrupt descriptor address", 0x00002016,
"VM-function controls", 0x00002018,
"EPT pointer", 0x0000201A,
"EOI-exit bitmap 0", 0x0000201C,
"EOI-exit bitmap 1", 0x0000201E,
"EOI-exit bitmap 2", 0x00002020,
"EOI-exit bitmap 3", 0x00002022,
"EPTP-list address", 0x00002024,
"VMREAD-bitmap address", 0x00002026,
"VMWRITE-bitmap address", 0x00002028,
"Virtualization-exception information address", 0x0000202A,
"XSS-exiting bitmap", 0x0000202C,
"ENCLS-exiting bitmap", 0x0000202E,
"Sub-page-permission-table pointer", 0x00002030,
"TSC multiplier", 0x00002032,
"Tertiary processor-based VM-execution controls", 0x00002034,
"ENCLV-exiting bitmap", 0x00002036,
"Low PASID directory address", 0x00002038,
"High PASID directory address", 0x0000203A,
"Shared EPT pointer", 0x0000203C,
"PCONFIG-exiting bitmap", 0x0000203E,
"Hypervisor-managed linear-address translation pointer", 0x00002040,
"PID-pointer table address", 0x00002042,
"Secondary VM-exit controls", 0x00002044,
"Guest-physical address", 0x00002400,
"VMCS link pointer", 0x00002800,
"Guest IA32_DEBUGCTL", 0x00002802,
"Guest IA32_PAT", 0x00002804,
"Guest IA32_EFER", 0x00002806,
"Guest IA32_PERF_GLOBAL_CTRL", 0x00002808,
"Guest PDPTE0", 0x0000280A,
"Guest PDPTE1", 0x0000280C,
"Guest PDPTE2", 0x0000280E,
"Guest PDPTE3", 0x00002810,
"Guest IA32_BNDCFGS", 0x00002812,
"Guest IA32_RTIT_CTL", 0x00002814,
"Guest IA32_LBR_CTL", 0x00002816,
"Guest IA32_PKRS", 0x00002818,
"Host IA32_PAT", 0x00002C00,
"Host IA32_EFER", 0x00002C02,
"Host IA32_PERF_GLOBAL_CTRL", 0x00002C04,
"Host IA32_PKRS", 0x00002C06,
"Pin-based VM-execution controls", 0x00004000,
"Primary processor-based VM-execution controls", 0x00004002,
"Exception bitmap", 0x00004004,
"Page-fault error-code mask", 0x00004006,
"Page-fault error-code match", 0x00004008,
"CR3-target count", 0x0000400A,
"Primary VM-exit controls", 0x0000400C,
"VM-exit MSR-store count", 0x0000400E,
"VM-exit MSR-load count", 0x00004010,
"VM-entry controls", 0x00004012,
"VM-entry MSR-load count", 0x00004014,
"VM-entry interruption-information field", 0x00004016,
"VM-entry exception error code", 0x00004018,
"VM-entry instruction length", 0x0000401A,
"TPR threshold", 0x0000401C,
"Secondary processor-based VM-execution controls", 0x0000401E,
"PLE_Gap", 0x00004020,
"PLE_Window", 0x00004022,
"Instruction-timeout control", 0x00004024,
"VM-instruction error", 0x00004400,
"Exit reason", 0x00004402,
"VM-exit interruption information", 0x00004404,
"VM-exit interruption error code", 0x00004406,
"IDT-vectoring information field", 0x00004408,
"IDT-vectoring error code", 0x0000440A,
"VM-exit instruction length", 0x0000440C,
"VM-exit instruction information", 0x0000440E,
"Guest ES limit", 0x00004800,
"Guest CS limit", 0x00004802,
"Guest SS limit", 0x00004804,
"Guest DS limit", 0x00004806,
"Guest FS limit", 0x00004808,
"Guest GS limit", 0x0000480A,
"Guest LDTR limit", 0x0000480C,
"Guest TR limit", 0x0000480E,
"Guest GDTR limit", 0x00004810,
"Guest IDTR limit", 0x00004812,
"Guest ES access rights", 0x00004814,
"Guest CS access rights", 0x00004816,
"Guest SS access rights", 0x00004818,
"Guest DS access rights", 0x0000481A,
"Guest FS access rights", 0x0000481C,
"Guest GS access rights", 0x0000481E,
"Guest LDTR access rights", 0x00004820,
"Guest TR access rights", 0x00004822,
"Guest interruptibility state", 0x00004824,
"Guest activity state", 0x00004826,
"Guest SMBASE", 0x00004828,
"Guest IA32_SYSENTER_CS", 0x0000482A,
"VMX-preemption timer value", 0x0000482E,
"Host IA32_SYSENTER_CS", 0x00004C00,
"CR0 guest/host mask", 0x00006000,
"CR4 guest/host mask", 0x00006002,
"CR0 read shadow", 0x00006004,
"CR4 read shadow", 0x00006006,
"CR3-target value 0", 0x00006008,
"CR3-target value 1", 0x0000600A,
"CR3-target value 2", 0x0000600C,
"CR3-target value 3", 0x0000600E,
"Exit qualification", 0x00006400,
"I/O RCX", 0x00006402,
"I/O RSI", 0x00006404,
"I/O RDI", 0x00006406,
"I/O RIP", 0x00006408,
"Guest-linear address", 0x0000640A,
"Guest CR0", 0x00006800,
"Guest CR3", 0x00006802,
"Guest CR4", 0x00006804,
"Guest ES base", 0x00006806,
"Guest CS base", 0x00006808,
"Guest SS base", 0x0000680A,
"Guest DS base", 0x0000680C,
"Guest FS base", 0x0000680E,
"Guest GS base", 0x00006810,
"Guest LDTR base", 0x00006812,
"Guest TR base", 0x00006814,
"Guest GDTR base", 0x00006816,
"Guest IDTR base", 0x00006818,
"Guest DR7", 0x0000681A,
"Guest RSP", 0x0000681C,
"Guest RIP", 0x0000681E,
"Guest RFLAGS", 0x00006820,
"Guest pending debug exceptions", 0x00006822,
"Guest IA32_SYSENTER_ESP", 0x00006824,
"Guest IA32_SYSENTER_EIP", 0x00006826,
"Guest IA32_S_CET", 0x00006828,
"Guest SSP", 0x0000682A,
"Guest IA32_INTERRUPT_SSP_TABLE_ADDR", 0x0000682C,
"Host CR0", 0x00006C00,
"Host CR3", 0x00006C02,
"Host CR4", 0x00006C04,
"Host FS base", 0x00006C06,
"Host GS base", 0x00006C08,
"Host TR base", 0x00006C0A,
"Host GDTR base", 0x00006C0C,
"Host IDTR base", 0x00006C0E,
"Host IA32_SYSENTER_ESP", 0x00006C10,
"Host IA32_SYSENTER_EIP", 0x00006C12,
"Host RSP", 0x00006C14,
"Host RIP", 0x00006C16,
"Host IA32_S_CET", 0x00006C18,
"Host SSP", 0x00006C1A,
"Host IA32_INTERRUPT_SSP_TABLE_ADDR", 0x00006C1C,
]
// Resources:
// https://doar-e.github.io/blog/2017/12/01/debugger-data-model/
// https://www.intel.com/sdm/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment