Hi,
Candy wrote:How do you detect your OS is running inside bochs?
Unfortunately, Brynet-Inc is right - there should be a standard way of detecting virtual machines but there isn't. I personally blame security "experts", who want virtual machines to be entirely undetectable so they feel good about using them for honey pots (which is stupid if your code must be running at CPL=0 to detect the virtual machine).
This means you need to rely on flaws in the virtual machine, but you need to be a little tricky - for open source emulators like Bochs you want to rely on flaws that can't be easily changed. For an example, when Bochs was emulating an 80486 the CPUID instruction would say the CPU is an 80486DX but the 80486DX didn't actually support the CPUID instruction, so I used this as part of my Bochs detection code. Later, I rewrote "cpu/cpuid.cc" and fixed this problem (and others), which broke my own Bochs detection code.
For Bochs, first I try a little pre-check to see if it was compiled with the 0xE9 hack:
Code: Select all
in al,0xE9 ;Get return value from Bochs debug port
cmp al,0xE9 ;Is Bochs running (and compiled with "./configure --enable-port-e9-hack")?
je .isBochs ; yes, running in Bochs
This isn't a reliable test (it's easy to compile Bochs without the 0xE9 hack), but it can save time.
Next, RDTSC counts instructions (not cycles) and Bochs doesn't emulate caches. I flush caches (WBINVD) and then use instructions like "MUL dword [0x00000FFE]" (which is deliberately misaligned and should cause 2 cache misses). RDTSC will count instructions, so one WBINVD and five MULs will be 6 "cycles".
This doesn't work if RDTSC isn't supported (if Bochs is emulating an 80486). In this case I do 2 loops, where both loops run from one PIT IRQ to the next (a fixed amount of time). For the first loop I repeatedly flush caches (WBINVD) and do several misaligned reads (e.g. "cmp dword [0x00006FFF],0"), and for the second loop I repeatedly do NOP and several misaligned reads. Then I compare how many times each loop executed. For Bochs the difference is small and sometimes the first loop does more iterations (flushing the caches is faster than NOP). For this I'd recommend making sure paging isn't enabled, as Bochs does emulate TLBs and flushes the TLBs when you do WBINVD (which is wrong - WBINVD should only flush instruction and data caches, not the TLBs). With paging enabled the first loop would be slower due to TLB flushing, which makes it hard to detect Bochs using this method.
The problem is that it's hard to write code that doesn't also detect other emulators. With my method, you can't tell the difference between Bochs and VirtualBox (they have the same flaws).
I've also got code for detecting Qemu, VMware and VirtualPC. Qemu doesn't emulate caches, RDTSC is always supported and RDTSC measures cycles and not instructions. VMware has a special I/O port that will change some registers if you touch the IO port when EAX is set to a special magic number, which can be used to find out which version of VMware is running. VirtualPC has a special instruction (which should be an invalid opcode) - you just execute this instruction and see if you get an exception or not.
Anyway, the latest version of my code isn't on my web site yet, so I attached it. It's still a work in progress though - I'm still looking for more information on detecting other virtual machines.
Cheers,
Brendan