Skip to content

Instantly share code, notes, and snippets.

@xtrnc

xtrnc/README.md Secret

Forked from steffengy/README.md
Last active March 13, 2024 03:08
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save xtrnc/496da9243bab29629bc0461562bff458 to your computer and use it in GitHub Desktop.
Save xtrnc/496da9243bab29629bc0461562bff458 to your computer and use it in GitHub Desktop.
Enable WSL2 nested virtualization

DISCLAIMER: This can corrupt your WSL or whatever, so no guarantees this works and does what your want. Use at your own risk.

Prereqs:

  • Windows 10 pro/enterprise (in win11 it should work by default)
  • Install WinDbg Preview from the Microsoft store.
  1. You might have to build a custom WSL kernel with KVM support. This seems to be unnecessary if your kernel is >=v4.19.104. To check this, open your wsl2 distro and run uname -r, it should show the current kernel version, for example: 5.10.60.1-microsoft-standard-WSL2. If it's >=v4.19.104 skip to step 2. Building the kernel in WSL:
# before starting, check other guides too (https://boxofcables.dev/accelerated-kvm-guests-on-wsl-2/)
# dependencies
sudo apt install build-essential flex bison libssl-dev libelf-dev
# get the kernel
git clone https://github.com/microsoft/WSL2-Linux-Kernel.git
cd WSL2-Linux-Kernel
make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl
# now you need to enable: 
# Virtualization -> KVM & Intel KVM (or AMD, not both, depending on your CPU)
# Processor type Features -> /dev/cpu/* /msr support
# build it (it could take a while)
make -j8 KCONFIG_CONFIG=Microsoft/config-wsl
# copy the kernel to your user profile folder
cp vmlinux /mnt/c/Users/<YOUR_USER>/
# now 
  1. Copy enable-kvm.bat and script.js (below) to an empty folder. Run net stop LxssManager as an admin and check that it stopped successfully. You may need to restart your PC if it doesn't stop or find out why it doesn't before continuing.
  • Optional: If you're using a custom kernel, edit it in your wsl config or uncomment the LinuxKernelDirect line in script.js and set the correct path to your kernel. Thanks to @offlinehacker, who increased automatization of this part!
  1. Open the folder in a cmd/powershell as an admin and run enable-kvm.bat. As instructed in the bat file, you'll have to wait for the debugger to attach (as in the attached image) and verify at the end if it worked. image
  2. Detach and close WinDbg.
REM Ensure vmcompute.exe is running
wsl.exe -e true
REM Listen for and Intercept utility vm creation
start Windbgx.exe -pn vmcompute.exe -c "bp vmcompute!Marshal::JsonParser::JsonParser;g;.scriptrun %CD%\script.js;.scriptrun %CD%\script.js;.scriptrun %CD%\script.js;.detach;qq"
REM Ensure WSL Utility VM is not running
net stop LxssManager
REM Press a key if WinDbg started and it's showing 'bp vmcompute!Marshal::JsonParser::JsonParser;g....' in the Command window
net start LxssManager
Rem/||(
Pressing a key again will open wsl.exe where you can run 'sudo kvm-ok'
You should see the following if it worked:
INFO: /dev/kvm exists
KVM acceleration can be used
)
pause
start wsl.exe
"use strict";
function initializeScript()
{
return [new host.apiVersionSupport(1, 3)];
}
function continueExecution() {
var cmd = "g";
host.diagnostics.debugLog(cmd);
var lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
}
function invokeScript()
{
/* bp vmcompute!Marshal::JsonParser::JsonParser */
var cmd;
var lines;
// 1. Check if WSL
var magic = host.memory.readWideString(host.currentThread.Registers.User.rdx, 14);
if (magic == '{"Owner":"WSL"') {
host.diagnostics.debugLog("IS WSL\n");
} else {
host.diagnostics.debugLog("IS NOT WSL request\n");
return continueExecution();
}
// dump length and read machine spec json
var len = host.currentThread.Registers.User.r8;
host.diagnostics.debugLog("String length: ", len, " dumping memory: ", host.currentThread.Registers.User.rdx, "\n");
var jsonString = host.memory.readWideString(host.currentThread.Registers.User.rdx, len);
host.diagnostics.debugLog("Before: ", jsonString, "\n");
// parse and modify machine spec json
var machineSpec = JSON.parse(jsonString);
machineSpec.VirtualMachine.ComputeTopology.Processor.ExposeVirtualizationExtensions = true;
// machineSpec.VirtualMachine.Chipset.LinuxKernelDirect.KernelFilePath = "C:\\Users\\<YOUR_USER>\\vmlinux";
var machineSpecJson = JSON.stringify(machineSpec);
host.diagnostics.debugLog("After: ", JSON.stringify(machineSpec), "\n");
var newLen = "0x" + machineSpecJson.length.toString(16);
// allocate memory
cmd = ".dvalloc 4096"
lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
var addr = lines[0];
var addrParts = addr.split(" ");
var freeMem = addrParts[addrParts.length-1].replace("`", "");
host.diagnostics.debugLog("Allocated ", freeMem, "\n");
// write memory
host.diagnostics.debugLog("Writing memory ", freeMem, " length: ", newLen, "\n");
cmd = "eu " + freeMem + ' "' + machineSpecJson.split("\\").join("\\\\").split('"').join('\\"') + '"';
lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
// patch rdx with new memory address
var cmd = "r @rdx = " + freeMem;
host.diagnostics.debugLog(cmd);
var lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
// patch r8 with new memory size
var cmd = "r @r8 = " + newLen;
host.diagnostics.debugLog(cmd);
var lines = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmd)
for (var line of lines) host.diagnostics.debugLog(" ", line, "\n");
return continueExecution();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment