Windows 10 emulation with QEMU

Benoît Sevens
4 min readMar 18, 2019

--

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 address
  • GS contains a usermode address (points to the TEB 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 or QEMU 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.

--

--