Windows 10 emulation with QEMU
Debuggers influence how machines behave. Indeed, using a debugger such as WinDbg to debug the Windows kernel changes how the operating system behaves. Some examples are:
- PatchGuard is disabled
- DRM technology is disabled
Also, putting breakpoints at certain addresses can trigger bugchecks.
When trying to solve most of the questions around the Windows kernel, this is not a problem. However in some cases this can become an issue.
I recently had the case where I was studying the KPTI (kernel page table isolation) implementation in Windows. This technology prevents user mode code from directly reading kernel mode code, as can be done with the Meltdown vulnerability. KPTI thus remediates agains Meltdown.
But when debugging KPTI with WinDbg, register outputs were not exactly as what you would expect initially. When breaking in usermode code, a kernel mode interrupt handler is called, which swaps out the usermode CR3 and loads the kernelmode CR3. So when asking WinDbg the CR3 value, I was given the kernelmode CR3 value.
In this case, as in other cases, it might be useful to have a full system emulation in order to not disturb the Windows kernel with our debugging.
This is where QEMU came in. This little article will serve as some basic commands to get an environment up and running and play with it.
First, we create a disk:
qemu-img create -f qcow2 -o size=40G Windows.img
Next we boot the system from the installation CD:
qemu-system-x86_64 -boot d -cdrom Windows.iso -m 2048M -hda Windows.img
Just follow the classic installation steps. Once installed to the disk, you can boot Windows:
qemu-system-x86_64 -m 2048M -hda Windows10.img -monitor stdio
A qemu
prompt will appear, which is called the monitor, from where you can stop, continue, snapshot, inspect, … the machine. The info registers
command is particularly interesting as it shows all the CPU registers. A part of its output is shown here:
We see that at this moment we are in usermode code:
RIP
points to a usermode addressGS
contains a usermode address (points to theTEB
when running in usermode)
An example output of when it is in kernelmode:
You can also inspect memory. For example, to find the current _KTHREAD
:
Or print the IDT and disassemble the interrupt vector for INT 3:
Of course, this interface is very rudimentary and lacks for example putting breakpoints. We can also attach gdb
for more powerful debugging with the -s
flag:
qemu-system-x86_64 -m 2048M -hda Windows10.img -monitor stdio -s
Now we can attach gdb
:
Now you can use all of the power of gdb
commands to inspect the machine and put breakpoints. However, reading some stuff such as the IDTR
is not possible with gdb
. So combining the power of gdb
and qemu
allows for powerful system inspection!
Let’s say we want to break on the page fault handler. First we would determine the address of the page fault handler by dumping the IDT
with qemu
:
Next, in gdb
we put a breakpoint on the handler and continue execution until it hits our breakpoint:
By the way, you can execute QEMU commands directly from gdb
by prefixing them with monitor
.
As a final example to show a case where qemu
may serve instead of WinDbg, let’s consider putting a breakpoint on the INT3 handler nt!KiBreakpointTrapShadow
. If you do this in WinDbg and hit a breakpoint, you will BugCheck with code 0x7f.
However, with QEMU we can do this and step through the breakpoint handler, as we are completely outside of the system:
Nice!
Of course, there are some big drawbacks with the QEMU approach. The most important ones I can think of:
- No symbols. Go try to find
nt!PsLoadedModuleList
for example. Good luck. To tackle this problem, the framework PyREBox uses the Volatility framework to get some decent information out of this raw memory. But still, you do not have full symbol information. - Slower performance when emulating than virtualizing.
- Not all those handy WinDbg extensions and meta commands.
- I did not find a way to read the MSR registers, nor in with a
gdb
orQEMU
command!
So as a conclusion, debugging a Windows system with QEMU and gdb certainly has its advantages as the operating systems does not notice anything. However, we loose the comfortable WinDbg environment, where symbols and extensions are in abundance.