How to Find Out if a Binary Runs Inside QEMU

Published by Philipp Schuster on

In the previous blog post we talked about how to use QEMUs debugcon feature. To do this responsibly, we should only do this if the binary runs in fact inside QEMU. To find this out, one can use the cpuid instruction. It gives us a lot of information about our environment including information about the CPU brand name or if we run inside a hypervisor. This instruction is specific for the x86-platform.

It is not complicated but also not totally trivial. First, the binary can only run in QEMU if cpuid returns information about the presence of a hypervisor, because QEMU is a hypervisor (it creates virtual machines) and no real hardware.

But here is small pitfall. If we use QEMU in the normal way, i.e. without accelerator (so in fact as an emulator), the hypervisor ID will be a special magic value unique for QEMU. The value is hard-coded into QEMUs source code. If this value is present, we can be sure that we are getting executed by QEMU. If we use QEMU with kvm as accelerator, then the hypervisor ID will be the one of kvm. In this case, we need to use the cpuid instruction to query “extended information”. These will include an “extended brand string” that will include the name of QEMU. To be exactly, the string will look like QEMU Virtual CPU version 2.5+. And that’s it. The control flow is as shown below (all questions are related to the results from cpuid):

1) Are we in a hypervisor environment?
– No => return false
2) Is the hypervisor ID the one of QEMU?
– Yes => return true
3) Does the extended brand string include QEMU?
– Yes => return true
– No => return false

So in the end, it’s not that hard and just needs some cpuid magic. Luckily, there is an awesome Rust library called raw-cpuid that encapsulates this. I created and published a small Rust library called runs_inside_qemu, which is a wrapper around the important functions from the *raw-cpuid*-crate. Thanks to the author of this crate and happy hacking!

Example Code

use runs_inside_qemu::runs_inside_qemu;

fn main() {
    // If we are in QEMU, we use the nice "debugcon"-feature which maps
    // the x86 I/O-port `0xe9` to stdout or a file.
    if runs_inside_qemu() {
        unsafe {
            x86::io::outb(0xe9, b'H');
            x86::io::outb(0xe9, b'e');
            x86::io::outb(0xe9, b'l');
            x86::io::outb(0xe9, b'l');
            x86::io::outb(0xe9, b'o');
            x86::io::outb(0xe9, b'\n');
        }
    }
}

Philipp Schuster

Hi, I'm Philipp and interested in Computer Science. I especially like low level development, making ugly things nice, and de-mystify "low level magic".

0 Comments

Leave a Reply

Your email address will not be published.