Using LLMs (in this case, with ChatGPT) to debug and reverse engineer Windows hypervisor technology is like going from “hunting with a stone spear to using a guided missile”.*
*paraphrased from the article, The Future of Crash Analysis: AI meets WinDbg.
I’ve written numerous articles on capturing Intel Processor Trace during Windows VM Launches and VM Exits. And I was asked recently whether it’s possible to capture register state at each instruction execution, at every N instructions (like every 32 instructions), or even after an allotted period of time. This would be awesome for exploring the traversal of given code paths, and seeing how key register contents change. Well, yes, it is possible; let’s take a walk-through.
Good pre-requisite reading for this article is: Using ChatGPT on Windows Secure Kernel with Intel Processor Trace.
Let’s take a very simple example: starting in the Windows kernel (ntkrnlmp) in Guest mode (of course), undergoing a transition to Host mode, and then back into ntkrnlmp as a Guest. This is one of a million interesting code paths to look at, but it’s particularly good because it invokes a VM Exit and then a VM Resume on the boostrap processor (BSP), as the Windows kernel traps into the hypervisor (Host mode) and then does a resume back into the Normal Kernel (Guest mode again). Let’s examine that code path and do register state capture with the use of the built-in SourcePoint Command language.
As we’ll be doing some automation here, I’ll be using using the SourcePoint powerful command language via either the Command window, or as a macro. This programming language is very ‘C’-like, and has a ton of built-in functions to perform just about anything that the GUI is capable of. For some background reading, go here: The Minnowboard Chronicles Episode 9: SourcePoint Command Language and Macros.
To dump register state, let’s zero in specifically on the General Purpose Registers and the VMCS registers. A simple macro, RegisterDump.mac, is used to dump all of these; it looks like this:
regs sregs cregs dregs printf("Guest State:\n") printf(“GUEST_BNDCFGS %16X\n”, GUEST_BNDCFGS) printf(“GUEST_CR0 %16X\n”, GUEST_CR0) printf(“GUEST_CR3 %16X\n”, GUEST_CR3) printf(“GUEST_CR4 %16X\n”, GUEST_CR4) printf(“GUEST_CS %16X\n”, GUEST_CS) printf(“GUEST_CSAR %16X\n”, GUEST_CSAR) printf(“GUEST_CSBAS %16X\n”, GUEST_CSBAS) printf(“GUEST_CSLIM %16X\n”, GUEST_CSLIM) printf(“GUEST_DEBUGCTL %16X\n”, GUEST_DEBUGCTL) printf(“GUEST_DR7 %16X\n”, GUEST_DR7) printf(“GUEST_DS %16X\n”, GUEST_DS) printf(“GUEST_DSAR %16X\n”, GUEST_DSAR) printf(“GUEST_DSBAS %16X\n”, GUEST_DSBAS) printf(“GUEST_DSLIM %16X\n”, GUEST_DSLIM) printf(“GUEST_EFER %16X\n”, GUEST_EFER) printf(“GUEST_ES %16X\n”, GUEST_ES) printf(“GUEST_ESAR %16X\n”, GUEST_ESAR) printf(“GUEST_ESBAS %16X\n”, GUEST_ESBAS) printf(“GUEST_ESLIM %16X\n”, GUEST_ESLIM) printf(“GUEST_FS %16X\n”, GUEST_FS) printf(“GUEST_FSAR %16X\n”, GUEST_FSAR) printf(“GUEST_FSBAS %16X\n”, GUEST_FSBAS) printf(“GUEST_FSLIM %16X\n”, GUEST_FSLIM) printf(“GUEST_GDTBAS %16X\n”, GUEST_GDTBAS) printf(“GUEST_GDTLIM %16X\n”, GUEST_GDTLIM) printf(“GUEST_GS %16X\n”, GUEST_GS) printf(“GUEST_GSAR %16X\n”, GUEST_GSAR) printf(“GUEST_GSBAS %16X\n”, GUEST_GSBAS) printf(“GUEST_GSLIM %16X\n”, GUEST_GSLIM) printf(“GUEST_IDTBAS %16X\n”, GUEST_IDTBAS) printf(“GUEST_IDTLIM %16X\n”, GUEST_IDTLIM) printf(“GUEST_INT_STATE %16X\n”, GUEST_INT_STATE) printf(“GUEST_INT_STS %16X\n”, GUEST_INT_STS) printf(“GUEST_INTERRUPT_SSP_TABLE_ADDR %16X\n”, GUEST_INTERRUPT_SSP_TABLE_ADDR) printf(“GUEST_LBR_CTL %16X\n”, GUEST_LBR_CTL) printf(“GUEST_LDTAR %16X\n”, GUEST_LDTAR) printf(“GUEST_LDTBAS %16X\n”, GUEST_LDTBAS) printf(“GUEST_LDTLIM %16X\n”, GUEST_LDTLIM) printf(“GUEST_LDTR %16X\n”, GUEST_LDTR) printf(“GUEST_LINEAR_ADDR %16X\n”, GUEST_LINEAR_ADDR) printf(“GUEST_PAT %16X\n”, GUEST_PAT) printf(“GUEST_PDE %16X\n”, GUEST_PDE) printf(“GUEST_PDPTE0 %16X\n”, GUEST_PDPTE0) printf(“GUEST_PDPTE1 %16X\n”, GUEST_PDPTE1) printf(“GUEST_PDPTE2 %16X\n”, GUEST_PDPTE2) printf(“GUEST_PDPTE3 %16X\n”, GUEST_PDPTE3) printf(“GUEST_PERF_GLOBAL_CTRL %16X\n”, GUEST_PERF_GLOBAL_CTRL) printf(“GUEST_PHYSICAL_ADDR %16X\n”, GUEST_PHYSICAL_ADDR) printf(“GUEST_PKRS %16X\n”, GUEST_PKRS) printf(“GUEST_PML_INDEX %16X\n”, GUEST_PML_INDEX) printf(“GUEST_RFLAGS %16X\n”, GUEST_RFLAGS) printf(“GUEST_RIP %16X\n”, GUEST_RIP) printf(“GUEST_RSP %16X\n”, GUEST_RSP) printf(“GUEST_RTIT_CTL %16X\n”, GUEST_RTIT_CTL) printf(“GUEST_S_CET %16X\n”, GUEST_S_CET) printf(“GUEST_SMBASE %16X\n”, GUEST_SMBASE) printf(“GUEST_SS %16X\n”, GUEST_SS) printf(“GUEST_SSAR %16X\n”, GUEST_SSAR) printf(“GUEST_SSBAS %16X\n”, GUEST_SSBAS) printf(“GUEST_SSLIM %16X\n”, GUEST_SSLIM) printf(“GUEST_SSP %16X\n”, GUEST_SSP) printf(“GUEST_STATE %16X\n”, GUEST_STATE) printf(“GUEST_SYSENTER_CS %16X\n”, GUEST_SYSENTER_CS) printf(“GUEST_SYSENTER_EIP %16X\n”, GUEST_SYSENTER_EIP) printf(“GUEST_SYSENTER_ESP %16X\n”, GUEST_SYSENTER_ESP) printf(“GUEST_TR %16X\n”, GUEST_TR) printf(“GUEST_TRAR %16X\n”, GUEST_TRAR) printf(“GUEST_TRBAS %16X\n”, GUEST_TRBAS) printf(“GUEST_TRLIM %16X\n”, GUEST_TRLIM) printf(“GUEST_UINV %16X\n”, GUEST_UINV) printf(“GUEST_VMCSP %16X\n”, GUEST_VMCSP) printf("Host State:\n") printf(“HOST_CR0 = %16X\n”, HOST_CR0) printf(“HOST_CR3 = %16X\n”, HOST_CR3) printf(“HOST_CR4 = %16X\n”, HOST_CR4) printf(“HOST_CS = %16X\n”, HOST_CS) printf(“HOST_DS = %16X\n”, HOST_DS) printf(“HOST_EFER = %16X\n”, HOST_EFER) printf(“HOST_ES = %16X\n”, HOST_ES) printf(“HOST_FS = %16X\n”, HOST_FS) printf(“HOST_FSBAS = %16X\n”, HOST_FSBAS) printf(“HOST_GDTBAS = %16X\n”, HOST_GDTBAS) printf(“HOST_GS = %16X\n”, HOST_GS) printf(“HOST_GSBAS = %16X\n”, HOST_GSBAS) printf(“HOST_IDTBAS = %16X\n”, HOST_IDTBAS) printf(“HOST_INTERRUPT_SSP_TABLE_ADDR = %16X\n”, HOST_INTERRUPT_SSP_TABLE_ADDR) printf(“HOST_PAT = %16X\n”, HOST_PAT) printf(“HOST_PERF_GLOBAL_CTRL = %16X\n”, HOST_PERF_GLOBAL_CTRL) printf(“HOST_PKRS = %16X\n”, HOST_PKRS) printf(“HOST_RIP = %16X\n”, HOST_RIP) printf(“HOST_RSP = %16X\n”, HOST_RSP) printf(“HOST_S_CET = %16X\n”, HOST_S_CET) printf(“HOST_SS = %16X\n”, HOST_SS) printf(“HOST_SSP = %16X\n”, HOST_SSP) printf(“HOST_SYSENTER_CS = %16X\n”, HOST_SYSENTER_CS) printf(“HOST_SYSENTER_EIP = %16X\n”, HOST_SYSENTER_EIP) printf(“HOST_SYSENTER_ESP = %16X\n”, HOST_SYSENTER_ESP) printf(“HOST_TR = %16X\n”, HOST_TR) printf(“HOST_TRBAS = %16X\n”, HOST_TRBAS) printf("VM-Entry Controls:\n") printf(“VMENTRY_CONTROL = %16X\n”, VMENTRY_CONTROL) printf(“VMENTRY_EXC_ERR = %16X\n”, VMENTRY_EXC_ERR) printf(“VMENTRY_INST_LEN = %16X\n”, VMENTRY_INST_LEN) printf(“VMENTRY_INT_INFO = %16X\n”, VMENTRY_INT_INFO) printf(“VMENTRY_MSR_LD_ADDR = %16X\n”, VMENTRY_MSR_LD_ADDR) printf(“VMENTRY_MSR_LD_COUNT = %16X\n”, VMENTRY_MSR_LD_COUNT) printf("VM-Exit Controls:\n") printf(“VMEXIT_CONTROL2 = %16X\n”, VMEXIT_CONTROL2) printf(“VMEXIT_INST_INFO = %16X\n”, VMEXIT_INST_INFO) printf(“VMEXIT_INST_LEN = %16X\n”, VMEXIT_INST_LEN) printf(“VMEXIT_INT_ERR = %16X\n”, VMEXIT_INT_ERR) printf(“VMEXIT_INT_INFO = %16X\n”, VMEXIT_INT_INFO) printf(“VMEXIT_MSR_LD_ADDR = %16X\n”, VMEXIT_MSR_LD_ADDR) printf(“VMEXIT_MSR_LD_COUNT = %16X\n”, VMEXIT_MSR_LD_COUNT) printf(“VMEXIT_MSR_ST_ADDR = %16X\n”, VMEXIT_MSR_ST_ADDR) printf(“VMEXIT_MSR_ST_COUNT = %16X\n”, VMEXIT_MSR_ST_COUNT) printf(“VMEXIT_PRIMARY = %16X\n”, VMEXIT_PRIMARY) printf(“VMEXIT_QUALIFICATION = %16X\n”, VMEXIT_QUALIFICATION) printf(“VMEXIT_REASON = %16X\n”, VMEXIT_REASON) printf(“VMEXIT_XSS_EXIT_BITMAP = %16X\n”, VMEXIT_XSS_EXIT_BITMAP) printf("VM-Execution Controls:\n") printf(“VMCS_APIC_ACC_ADDR = %16X\n”, VMCS_APIC_ACC_ADDR) printf(“VMCS_CR0_MASK = %16X\n”, VMCS_CR0_MASK) printf(“VMCS_CR0_SHADOW = %16X\n”, VMCS_CR0_SHADOW) printf(“VMCS_CR3_COUNT = %16X\n”, VMCS_CR3_COUNT) printf(“VMCS_CR3_VALUE0 = %16X\n”, VMCS_CR3_VALUE0) printf(“VMCS_CR3_VALUE1 = %16X\n”, VMCS_CR3_VALUE1) printf(“VMCS_CR3_VALUE2 = %16X\n”, VMCS_CR3_VALUE2) printf(“VMCS_CR3_VALUE3 = %16X\n”, VMCS_CR3_VALUE3) printf(“VMCS_CR4_MASK = %16X\n”, VMCS_CR4_MASK) printf(“VMCS_CR4_SHADOW = %16X\n”, VMCS_CR4_SHADOW) printf(“VMCS_ENCLS_EXIT_BITMAP = %16X\n”, VMCS_ENCLS_EXIT_BITMAP) printf(“VMCS_ENCLV_EXIT_BITMAP = %16X\n”, VMCS_ENCLV_EXIT_BITMAP) printf(“VMCS_EOI_EXIT_BITMAP0 = %16X\n”, VMCS_EOI_EXIT_BITMAP0) printf(“VMCS_EOI_EXIT_BITMAP1 = %16X\n”, VMCS_EOI_EXIT_BITMAP1) printf(“VMCS_EOI_EXIT_BITMAP2 = %16X\n”, VMCS_EOI_EXIT_BITMAP2) printf(“VMCS_EOI_EXIT_BITMAP3 = %16X\n”, VMCS_EOI_EXIT_BITMAP3) printf(“VMCS_EPTP = %16X\n”, VMCS_EPTP) printf(“VMCS_EPTP_INDEX = %16X\n”, VMCS_EPTP_INDEX) printf(“VMCS_EPTP_LIST_ADDR = %16X\n”, VMCS_EPTP_LIST_ADDR) printf(“VMCS_EXC_BITMAP = %16X\n”, VMCS_EXC_BITMAP) printf(“VMCS_HIGH_PASID_ADDR = %16X\n”, VMCS_HIGH_PASID_ADDR) printf(“VMCS_HLAT_PRESIZE = %16X\n”, VMCS_HLAT_PRESIZE) printf(“VMCS_HLATP = %16X\n”, VMCS_HLATP) printf(“VMCS_IDT_ERR = %16X\n”, VMCS_IDT_ERR) printf(“VMCS_IDT_INFO = %16X\n”, VMCS_IDT_INFO) printf(“VMCS_INST_TOC = %16X\n”, VMCS_INST_TOC) printf(“VMCS_IO_BITMAPA = %16X\n”, VMCS_IO_BITMAPA) printf(“VMCS_IO_BITMAPB = %16X\n”, VMCS_IO_BITMAPB) printf(“VMCS_IO_RCX = %16X\n”, VMCS_IO_RCX) printf(“VMCS_IO_RDI = %16X\n”, VMCS_IO_RDI) printf(“VMCS_IO_RIP = %16X\n”, VMCS_IO_RIP) printf(“VMCS_IO_RSI = %16X\n”, VMCS_IO_RSI) printf(“VMCS_LOW_PASID_ADDR = %16X\n”, VMCS_LOW_PASID_ADDR) printf(“VMCS_MSR_BITMAP = %16X\n”, VMCS_MSR_BITMAP) printf(“VMCS_PCONFIG_EXIT_BITMAP = %16X\n”, VMCS_PCONFIG_EXIT_BITMAP) printf(“VMCS_PF_MASK = %16X\n”, VMCS_PF_MASK) printf(“VMCS_PF_MATCH = %16X\n”, VMCS_PF_MATCH) printf(“VMCS_PID_ADDR = %16X\n”, VMCS_PID_ADDR) printf(“VMCS_PIDP_INDEX = %16X\n”, VMCS_PIDP_INDEX) printf(“VMCS_PIDP_TABLE_ADDR = %16X\n”, VMCS_PIDP_TABLE_ADDR) printf(“VMCS_PINV = %16X\n”, VMCS_PINV) printf(“VMCS_PLE_GAP = %16X\n”, VMCS_PLE_GAP) printf(“VMCS_PLE_WIN = %16X\n”, VMCS_PLE_WIN) printf(“VMCS_PML_ADDR = %16X\n”, VMCS_PML_ADDR) printf(“VMCS_SHARED_EPTP = %16X\n”, VMCS_SHARED_EPTP) printf(“VMCS_SPEC_CTRL_MASK = %16X\n”, VMCS_SPEC_CTRL_MASK) printf(“VMCS_SPEC_CTRL_SDW = %16X\n”, VMCS_SPEC_CTRL_SDW) printf(“VMCS_SPPTP = %16X\n”, VMCS_SPPTP) printf(“VMCS_TD_KEYID = %16X\n”, VMCS_TD_KEYID) printf(“VMCS_TPR_THRESHOLD = %16X\n”, VMCS_TPR_THRESHOLD) printf(“VMCS_TSC_MULT = %16X\n”, VMCS_TSC_MULT) printf(“VMCS_TSC_OFFSET = %16X\n”, VMCS_TSC_OFFSET) printf(“VMCS_VAPIC_ADDR = %16X\n”, VMCS_VAPIC_ADDR) printf(“VMCS_VEIA = %16X\n”, VMCS_VEIA) printf(“VMCS_VM_PIN = %16X\n”, VMCS_VM_PIN) printf(“VMCS_VM_PRIMARY = %16X\n”, VMCS_VM_PRIMARY) printf(“VMCS_VM_SECONDARY = %16X\n”, VMCS_VM_SECONDARY) printf(“VMCS_VM_TERTIARY = %16X\n”, VMCS_VM_TERTIARY) printf(“VMCS_VMCSP = %16X\n”, VMCS_VMCSP) printf(“VMCS_VMFC = %16X\n”, VMCS_VMFC) printf(“VMCS_VMINST_ERR = %16X\n”, VMCS_VMINST_ERR) printf(“VMCS_VMREAD_BITMAP = %16X\n”, VMCS_VMREAD_BITMAP) printf(“VMCS_VMWRITE_BITMAP = %16X\n”, VMCS_VMWRITE_BITMAP) printf(“VMCS_VPID = %16X\n”, VMCS_VPID)
Note that the regs, sregs, cregs and dregs respectively display the GPRs, segment registers, control registers, and debug registers. And, although there is not currently a single command that dumps the VMCS (we’ll add that in a future release), for illustration purposes I’ve just used a bunch of printf’s: note that it’s a simple matter of just typing in a register’s name into the Command window, or calling it out within a macro, to display it.
This is all very interesting, but how do I display this state at a pre-determined sequence of instructions, breakpoints, time, or otherwise? This is easy: the use of event macros, the command language, and breakpoints will do the trick. SourcePoint has powerful automation capabilities built-in.
Let’s take an example: below are some simple steps that perform the tasks for this investigation:
- Dump registers (capturing some of the current processor state)
- Do a target Go
- Wait for 0.001 seconds
- Stop the target
- Capture Intel Processor Trace
- Dump the registers again
Firstly, set an event macro up to run the RegisterDump.mac when a breakpoint is hit:
This is super simple to encode in a macro with a handful of lines. Here’s the pseudocode:
run RegisterDump.mac // Dump the registers go sleep(0.001) // Sleep for 1/1000th of a second stop print cycles // Dump the Intel PT data run RegisterDump.mac
Below is the some of the register dump, as well as some Intel PT:
RAX = 00000000FFFFFFB0: EAX=FFFFFFB0 AX=FFB0 AH=FF AL=B0 RBX = 0000000000000030: EBX=00000030 BX=0030 BH=00 BL=30 RCX = 00000000000002D0: ECX=000002D0 CX=02D0 CH=02 CL=D0 RDX = FFFFF80452551000: EDX=52551000 DX=1000 DH=10 DL=00 RBP = 00000000002142A0: EBP=002142A0 BP=42A0 BPL=A0 RSI = FFFFF804525512D0: ESI=525512D0 SI=12D0 SIL=D0 RDI = 00000000000002D0: EDI=000002D0 DI=02D0 DIL=D0 RSP = FFFFCF0A7D006B00: ESP=7D006B00 SP=6B00 SPL=00 R8 = 0000000000214000: R8D=00214000 R8W=4000 R8B=00 R9 = 0000000000020000: R9D=00020000 R9W=0000 R9B=00 R10 = 0000000000001000: R10D=00001000 R10W=1000 R10B=00 R11 = FFFFE18218448120: R11D=18448120 R11W=8120 R11B=20 R12 = 0000000000214000: R12D=00214000 R12W=4000 R12B=00 R13 = 0000000000020000: R13D=00020000 R13W=0000 R13B=00 R14 = FFFFE18218476000: R14D=18476000 R14W=6000 R14B=00 R15 = FFFFF80452551000: R15D=52551000 R15W=1000 R15B=00 CS = 0010 DS = 002B SS = 0018 ES = 002B FS = 0053 GS = 002B RIP = FFFFF80457BE567A: EIP=57BE567A IP=567A RFLAGS = 0000000000050282: EFLAGS=00050282 FLAGS=0282 ID=0 VIP=0 VIF=0 AC=1 VM=0 RF=1 NT=0 IOPL=0 OF=0 DF=0 INF=1 TF=0 SF=1 ZF=0 AF=0 PF=0 CF=0 CS = 0010 CSBAS = 0000000000000000: CSBAS_32=00000000 CSLIM = 00000000 CSAR = 209B: G=0 D=0 L=1 AVL=0 P=1 DPL=0 C=0 R=1 A=1 DS = 002B DSBAS = 0000000000000000: DSBAS_32=00000000 DSLIM = FFFFFFFF DSAR = C0F3: G=1 B=1 AVL=0 P=1 DPL=3 E=0 W=1 A=1 SS = 0018 SSBAS = 0000000000000000: SSBAS_32=00000000 SSLIM = 00000000 SSAR = 4093: G=0 B=1 AVL=0 P=1 DPL=0 E=0 W=1 A=1 ES = 002B ESBAS = 0000000000000000: ESBAS_32=00000000 ESLIM = FFFFFFFF ESAR = C0F3: G=1 B=1 AVL=0 P=1 DPL=3 E=0 W=1 A=1 FS = 0053 FSBASE = 0000000000000000 FSLIM = 00003C00 FSAR = 40F3: G=0 B=1 AVL=0 P=1 DPL=3 E=0 W=1 A=1 GS = 002B GSBASE = FFFFF804522C2000 GSLIM = FFFFFFFF GSAR = C0F3: G=1 B=1 AVL=0 P=1 DPL=3 E=0 W=1 A=1 GDTBAS = FFFFF8045386EFB0: GDTBAS_32=5386EFB0 GDTLIM = 0057 IDTBAS = FFFFF8045386C000: IDTBAS_32=5386C000 IDTLIM = 0FFF LDTR = 0000 LDTBAS = 0000000000000000: LDTBAS_32=00000000 LDTLIM = FFFF LDTAR = C000: G=1 AVL=0 P=0 DPL=0 B=0 TR = 0040 TSSBAS = FFFFF8045386D000: TSSBAS_32=5386D000 TSSLIM = 00000067 TSSAR = 008B: G=0 AVL=0 P=1 DPL=0 B=1 CR0 = 0000000080050033: CR0_32=80050033 PG=1 CD=0 NW=0 AM=1 WP=1 NE=1 ET=1 TS=0 EM=0 MP=1 PE=1 CR2 = FFFFE182184E5000: CR2_32=184E5000 CR3 = 0000000487282000: CR3_32=87282000 LAM_U48=0 LAM_U57=0 PDB=0000487282 PCD=0 PWT=0 CR4 = 0000000000B52EF8: CR4_32=00B52EF8 LAM_SUP=0 UINTR=0 PKS=0 CET=1 PKE=0 SMAP=1 SMEP=1 KL=0 OSXSAVE=1 PCIDE=0 FSGSBASE=1 SMXE=0 VMXE=1 LA57=0 UMIP=1 OSXMMEXCPT=1 OSFXSR=1 PCE=0 PGE=1 MCE=1 PSE=1 PAE=1 DE=1 TSD=0 PVI=0 VME=0 CR8 = 0000000000000000: TPR=0 SSP = 0000000000000000 DR0 = 0000000000000000 DR1 = 0000000000000000 DR2 = 0000000000000000 DR3 = 0000000000000000 DR6 = 00000000FFFF0FF0: RTM=1 BT=0 BS=0 BD=0 B3=0 B2=0 B1=0 B0=0 DR7 = 0000000000000400: LN3=0 RW3=0 LN2=0 RW2=0 LN1=0 RW1=0 LN0=0 RW0=0 GD=0 RTM=0 GE=0 LE=0 G3=0 L3=0 G2=0 L2=0 G1=0 L1=0 G0=0 L0=0 Guest State: GUEST_BNDCFGS DEADBEE0BADBEEF GUEST_CR0 80050033 GUEST_CR3 1AD000 GUEST_CR4 B52EF8 GUEST_CS 10 GUEST_CSAR 209B GUEST_CSBAS 0 GUEST_CSLIM 0 GUEST_DEBUGCTL 0 GUEST_DR7 400 GUEST_DS 2B GUEST_DSAR C0F3 GUEST_DSBAS 0 GUEST_DSLIM FFFFFFFF GUEST_EFER D00 GUEST_ES 2B GUEST_ESAR C0F3 GUEST_ESBAS 0 GUEST_ESLIM FFFFFFFF GUEST_FS 53 GUEST_FSAR 40F3 GUEST_FSBAS 0 GUEST_FSLIM 3C00 GUEST_GDTBAS FFFFF8045386EFB0 GUEST_GDTLIM 57 GUEST_GS 2B GUEST_GSAR C0F3 GUEST_GSBAS FFFFF804522C2000 GUEST_GSLIM FFFFFFFF GUEST_IDTBAS FFFFF8045386C000 GUEST_IDTLIM FFF GUEST_INT_STATE 0 GUEST_INT_STS 0 GUEST_INTERRUPT_SSP_TABLE_ADDR 0 GUEST_LBR_CTL DEADBEE0BADBEEF GUEST_LDTAR 1C000 GUEST_LDTBAS 0 GUEST_LDTLIM FFFFFFFF GUEST_LDTR 0 GUEST_LINEAR_ADDR 0 GUEST_PAT 7010600070106 GUEST_PDE 0 GUEST_PDPTE0 0 GUEST_PDPTE1 0 GUEST_PDPTE2 0 GUEST_PDPTE3 0 GUEST_PERF_GLOBAL_CTRL FF GUEST_PHYSICAL_ADDR 4000166318 GUEST_PKRS DEADBEE0BADBEEF GUEST_PML_INDEX 0 GUEST_RFLAGS 40206 GUEST_RIP FFFFF80458056573 GUEST_RSP FFFFCF0A7D006F00 GUEST_RTIT_CTL 2C0F GUEST_S_CET 0 GUEST_SMBASE 0 GUEST_SS 18 GUEST_SSAR 4093 GUEST_SSBAS 0 GUEST_SSLIM 0 GUEST_SSP 0 GUEST_STATE 0 GUEST_SYSENTER_CS 0 GUEST_SYSENTER_EIP 0 GUEST_SYSENTER_ESP 0 GUEST_TR 40 GUEST_TRAR 8B GUEST_TRBAS FFFFF8045386D000 GUEST_TRLIM 67 GUEST_UINV BEEF GUEST_VMCSP FFFFFFFFFFFFFFFF Host State: HOST_CR0 = 80010031 HOST_CR3 = 100103000 HOST_CR4 = 422E0 HOST_CS = 10 HOST_DS = 20 HOST_EFER = D01 HOST_ES = 20 HOST_FS = 20 HOST_FSBAS = 0 HOST_GDTBAS = FFFFF81165E01190 HOST_GS = 20 HOST_GSBAS = FFFFF81165FAD000 HOST_IDTBAS = FFFFF81165E00010 HOST_INTERRUPT_SSP_TABLE_ADDR = 0 HOST_PAT = 606050400070106 HOST_PERF_GLOBAL_CTRL = 0 HOST_PKRS = DEADBEE0BADBEEF HOST_RIP = FFFFF81165A3E30B HOST_RSP = 10000005FC0 HOST_S_CET = 0 HOST_SS = 20 HOST_SSP = 0 HOST_SYSENTER_CS = 0 HOST_SYSENTER_EIP = 0 HOST_SYSENTER_ESP = 0 HOST_TR = 30 HOST_TRBAS = FFFFF81165F7D000 VM-Entry Controls: VMENTRY_CONTROL = 1233FF VMENTRY_EXC_ERR = 0 VMENTRY_INST_LEN = 0 VMENTRY_INT_INFO = 0 VMENTRY_MSR_LD_ADDR = 0 VMENTRY_MSR_LD_COUNT = 0 VM-Exit Controls: VMEXIT_CONTROL2 = DEADBEE0BADBEEF VMEXIT_INST_INFO = 12614924 VMEXIT_INST_LEN = 5 VMEXIT_INT_ERR = 0 VMEXIT_INT_INFO = 0 VMEXIT_MSR_LD_ADDR = 0 VMEXIT_MSR_LD_COUNT = 0 VMEXIT_MSR_ST_ADDR = 0 VMEXIT_MSR_ST_COUNT = 0 VMEXIT_PRIMARY = 3FFFF VMEXIT_QUALIFICATION = 20 VMEXIT_REASON = 2E VMEXIT_XSS_EXIT_BITMAP = 0 VM-Execution Controls: VMCS_APIC_ACC_ADDR = 101400000 VMCS_CR0_MASK = FFFFFFFFFFFFFFE1 VMCS_CR0_SHADOW = 80050033 VMCS_CR3_COUNT = 0 VMCS_CR3_VALUE0 = 0 VMCS_CR3_VALUE1 = 0 VMCS_CR3_VALUE2 = 0 VMCS_CR3_VALUE3 = 0 VMCS_CR4_MASK = FFFFFFFFFFFFFE7F VMCS_CR4_SHADOW = B50EF8 VMCS_ENCLS_EXIT_BITMAP = DEADBEE0BADBEEF VMCS_ENCLV_EXIT_BITMAP = DEADBEE0BADBEEF VMCS_EOI_EXIT_BITMAP0 = 0 VMCS_EOI_EXIT_BITMAP1 = 0 VMCS_EOI_EXIT_BITMAP2 = 0 VMCS_EOI_EXIT_BITMAP3 = 0 VMCS_EPTP = 1109F501E VMCS_EPTP_INDEX = BEEF VMCS_EPTP_LIST_ADDR = 0 VMCS_EXC_BITMAP = 60000 VMCS_HIGH_PASID_ADDR = DEADBEE0BADBEEF VMCS_HLAT_PRESIZE = BEEF VMCS_HLATP = DEADBEE0BADBEEF VMCS_IDT_ERR = 0 VMCS_IDT_INFO = 0 VMCS_INST_TOC = BADBEEF VMCS_IO_BITMAPA = 101401000 VMCS_IO_BITMAPB = 101402000 VMCS_IO_RCX = 1 VMCS_IO_RDI = 1 VMCS_IO_RIP = 1 VMCS_IO_RSI = 1 VMCS_LOW_PASID_ADDR = DEADBEE0BADBEEF VMCS_MSR_BITMAP = 10E054000 VMCS_PCONFIG_EXIT_BITMAP = DEADBEE0BADBEEF VMCS_PF_MASK = 0 VMCS_PF_MATCH = 0 VMCS_PID_ADDR = 10E05B340 VMCS_PIDP_INDEX = BEEF VMCS_PIDP_TABLE_ADDR = DEADBEE0BADBEEF VMCS_PINV = 1D VMCS_PLE_GAP = 0 VMCS_PLE_WIN = 0 VMCS_PML_ADDR = 0 VMCS_SHARED_EPTP = DEADBEE0BADBEEF VMCS_SPEC_CTRL_MASK = DEADBEE0BADBEEF VMCS_SPEC_CTRL_SDW = DEADBEE0BADBEEF VMCS_SPPTP = DEADBEE0BADBEEF VMCS_TD_KEYID = BADBEEF VMCS_TPR_THRESHOLD = 0 VMCS_TSC_MULT = 1000000000000 VMCS_TSC_OFFSET = 0 VMCS_VAPIC_ADDR = 10E053000 VMCS_VEIA = 0 VMCS_VM_PIN = BF VMCS_VM_PRIMARY = B6A065FA VMCS_VM_SECONDARY = 5013AF VMCS_VM_TERTIARY = 0 VMCS_VMCSP = 0 VMCS_VMFC = 0 VMCS_VMINST_ERR = C VMCS_VMREAD_BITMAP = DEADBEE0BADBEEF VMCS_VMWRITE_BITMAP = DEADBEE0BADBEEF VMCS_VPID = 1 -130226 P0 *** VMCS pointer change: 10E052 *** -130192 P0 FFFFF804579D52A7 test al,40 -229.414 us P0 FFFFF804579D52A9 je ::ntkrnlmp.woutput_l+90f P0 FFFFF804579D52AB cmp [rdx+10],00000000 P0 FFFFF804579D52B0 jne ::ntkrnlmp.woutput_l+90f P0 FFFFF804579D52B7 call ::ntkrnlmp.fputwc_nolock P0 FFFFF804579D638C sub rsp,00000038 P0 FFFFF80457D6390 test rdx,rdx P0 FFFFF804579D6393 jne ::ntkrnlmp.fputwc_nolock+22 P0 FFFFF804579D63AE add [rdx+08],fffffffe P0 FFFFF804579D63B2 js ::ntkrnlmp.fputwc_nolock+37 -130185 P0 FFFFF804579D63B4 mov rax,[rdx] -229.406 us
Note that whenever a VMCS register cannot be read, because it is deprecated, out of context, or simply not available (i.e. HLAT is not active on this target), it shows up with “DEAD” or “BEEF” or some combination thereof.
And below is the Intel PT captured once the target is stopped, as can be seen in a SourcePoint screenshot:
It’s time to collect the beginning register dump, Intel PT, and ending register dump, and feed it to ChatGPT for analysis. Here’s an few excerpts of the results:
🔒 1. CET (Control-Flow Enforcement Technology) Enablement
CET uses shadow stacks and indirect branch tracking. Its activation requires:
- CR4.CET (Bit 23) — not explicitly visible, but we can infer CET enablement from:
- VMCS entry control VMENTRY_CONTROL = 0x1213FF
This includes:
- Bit 20 set: Load CET state → ✅
- Therefore, CET is enabled in the guest at VM-entry.
PT Behavior Correlation:
Intel PT shows frequent instruction-pointer changes within tight loops:
mov [r9], al
add rdi, 2
inc r9
sub r10, 1
jne CmpGetNameControlBlock+1b0
🧠 Shadow Stack Insight:
- These forward-only calls (call, retn) align with a CET-protected environment.
- No suspicious jmp to invalid regions — consistent with valid indirect control flow.
- No PT TNT (Taken/Not Taken) anomalies suggesting control-flow violations.
✅ Conclusion: CET is enforced, and execution flow never violates shadow stack integrity during trace.
🧭 2. VMCS + PT Correlation for Secure Nested Execution
VMCS Guest State:
- CR4.VMXE = 1 (VMX instructions allowed in guest)
- CR4.SMEE = 1 (SMX enabled)
- CR4.SMPE/SMAP = 1 (Supervisor protections)
- BNDCFGS present (MPX state loaded — often with CET)
Intel PT Events:
- Starts at HvpEnlistFreeCells, ends in CmpGetNameControlBlock
- Consistent with hypervisor-managed secure memory enumeration and registry lookup
- No EPT violations, no unexpected VMEXITs, no flow divergence
✅ Conclusion: The guest is likely running in a nested, secure enclave (e.g. VTL1) with CET and memory protections enforced. PT confirms smooth control flow without interruption, validating VMCS configuration.
🔁 Summary Table
Capability | VMCS Evidence | PT Confirmation |
CET | Load CET = 1, BNDCFGS present | Valid returns, no IB violations |
SMEP/SMAP | CR4.SMPE = 1, CR4.SMAP = 1 | No code execution from user pages |
VMX in Guest | CR4.VMXE = 1 | No violation of nested control |
Secure Context | SMXE = 1, BNDCFGS, FSGSBASE | Secure stack behavior, string copy integrity |
Displaying all VMCS fields with changed values:
Register | Before Intel PT | After Intel PT |
RAX | 00000000FFFFFFB0 | 0000000000000032 |
RBX | 0000000000000030 | 000000008D6BBC52 |
RCX | 00000000000002D0 | FFFFE182185E4240 |
RDX | FFFFF80452551000 | 0000000000000000 |
RBP | 00000000002142A0 | 00000000000000D9 |
RSI | FFFFF804525512D0 | FFFFE182185E4150 |
RDI | 00000000000002D0 | 00000000000000C0 |
RSP | FFFFCF0A7D006B00 | FFFFCF0A7D005B60 |
R8 | 0000000000214000 | 0000000000000003 |
R9 | 0000000000020000 | FFFFE182185E41CA |
R10 | 0000000000001000 | 0000000000000079 |
R11 | FFFFE18218448120 | 0000000000001001 |
R12 | 0000000000214000 | 00000000000006C4 |
R13 | 0000000000020000 | FFFFCF0A7D005F10 |
R14 | FFFFE18218476000 | 00000000000000F3 |
R15 | FFFFF80452551000 | 0000000000068501 |
RIP | FFFFF80457BE567A | FFFFF80457C0BE40 |
RFLAGS | 0000000000050282 | 0000000000050202 |
CR2 | FFFFE182184E5000 | FFFFE182188C3392 |
VMENTRY_CONTROL | 1233FF | 1213FF |
VMEXIT_INST_INFO | 12614924 | 0 |
VMEXIT_INST_LEN | 5 | 4 |
VMEXIT_INT_INFO | 0 | 800000FF |
VMEXIT_PRIMARY | 3FFFF | 3EFFF |
VMEXIT_QUALIFICATION | 20 | 0 |
VMEXIT_REASON | 2E | 1 |
VMCS_VM_PRIMARY | B6A065FA | B6A06DFA |
Now, it’s possible to create much more complex sequences using the power of SourcePoint’s command language. Here’s another example where we capture execution trace using Intel Processor Trace and register state during five sequential VM Exits; just with one simple chained command in the Command window:
P8>define ord1 i=0; while(i<5) {i++; printf(“VM Exit # = %u\n”,i); go; wait}
Or you can use the SourcePoint bstep (branch step) command to break with every code branch. Very cool!
In the interest of space, I won’t go through the results here; suffice to say that they’re fascinating!
This blog is getting a little long to cover all I would like to write about. There are a million things to review about the use of SourcePoint + AI. This is probably best served with an upcoming webinar and/or video.
And in an upcoming blog, I’ll write about the use of SourcePoint with MCP.