Thumbnail: logo

Detecting VMware presence on a 64-bit system [redpill respawned]

by on under blog

Hot on the heels of the latest post, I have decided to port to linux another lab example from Intermediate x86 class.
This time I will talk about the RedPill paper by Joanna Rutkowska.

The main purpose of this test code is to call the SIDT instruction and save its return value inside an array: this value will tell us whether we are running it inside a vm or not. What the SIDT instruction does is simply save the Interrupt Descriptor Table Register (IDTR) value inside an operand (typically another register). The program will then check if the 6th byte of the returned value equals to 0x00 and, if it does, it means it is running inside a virtual machine. The return value size is 6 bytes on 32-bit systems (4 bytes for the address + 2 byte for the limit ) and 10 bytes on 64-bit systems instead (8 + 2 bytes).

#include <stdio.h>

int main(){
	unsigned char warehouse[10];//10 bytes of storage to store the IDTR into
	memset(warehouse, 0, 6);//Just to make sure it's clean

	fflush(stdout);
	__asm__ __volatile__("sidt %0" : "g="(warehouse));

  printf("after SIDT instruction, storge contains:\n");

  for (int i = 0; i <= 9; i++)
	{
    printf("%02X", warehouse[i]);
	}

  printf("\n");

	if(warehouse[5] == 0x00){
		printf("This code being run in VMWare\n");
	}
	else{
		printf("This code being run on a normal system\n");
	}

	return 0xF0551112ED;
}

We can compile it without canaries, nx or pie so the resulting assembly code will be as clean as possible by running gcc.

gcc linux_verbosepill.c -o linux_verbosepill -g -fno-stack-protector -z execstack -no-pie

The SIDT instruction provides us with valuable information from a ring0 structure, the Interrupt Descriptor Table address, via a non privileged point of view (ring3). Since the IDT is a unique entity per system, in order to avoid resource conflicts, a VM must relocate the address to a different memory region.

We can then verify that under a VM we get this value.

root@kali_vm:~# ./linux_verbose_pill
after SIDT instruction, warehouse contains:
FF0F000000001CD7FFFF
This code being run in VMWare

On the other hand, a regular ‘bare metal’ installation, is returning a different address format.

root@kali_bare_metal:~# ./linux_verbose_pill
after SIDT instruction, warehouse contains:
FF0F00C057FFFFFFFFFF
This code being run on a normal system

We can say with a fair degree of certainty that a code running on a vm has the sixth value set to zero, while it’s 0xFF on a bare metal installation. I have limited my testing to Debian and I did not manage to test this scenario extensively so fire me an email/tweet if you spot any discrepancy with any other distro.

KVM NOTE
A friend of mine tested it on KVM, guest and host, and it resulted in the following value being shown on each of them.

FF0F008052FFFFFFFFF

My guess is that KVM is either somehow sharing the IDT between host and guest or it manages to hide the rebasing on the guest system, but if you have more accurate insights, please share them :)

assembly, vm, security, ia32, infosec


© 2018 Matteo Malvica. Illustrations by Sergio Kalisiak.