Обнаружение VMM в Linux
Я пытаюсь определить, работаю ли я в виртуальной среде (vmware, virtualbox и т. Д.)
В Windows я использую несколько ASM, но не могу использовать их в Linux, в основном потому, что код может быть скомпилирован и запущен на 32- или 64-битном Linux.
Следующий код работает в Windows 32 и 64 и был протестирован на VMWare, virtualbox и других виртуальных машинах:
#include <stdio.h>
int idtCheck ()
{
unsigned char m[2];
__asm sidt m;
printf("IDTR: %2.2x %2.2x\n", m[0], m[1]);
return (m[1]>0xd0) ? 1 : 0;
}
int gdtCheck()
{
unsigned char m[2];
__asm sgdt m;
printf("GDTR: %2.2x %2.2x\n", m[0], m[1]);
return (m[1]>0xd0) ? 1 : 0;
}
int ldtCheck()
{
unsigned char m[2];
__asm sldt m;
printf("LDTR: %2.2x %2.2x\n", m[0], m[1]);
return (m[0] != 0x00 && m[1] != 0x00) ? 1 : 0;
}
int main(int argc, char * argv[])
{
idtCheck();
gdtCheck();
if (ldtCheck())
printf("Virtual Machine detected.\n");
else
printf("Native machine detected.\n");
return 0;
}
Теперь GCC жалуется на __asm для всех функций. Я пробовал с asm (), asm и другими формами, которые я использовал в прошлом, но ни одна из них не работает. Есть идеи?
1 ответ
Ну, я не разбирал машинный код там, но вот версия с использованием встроенного ассемблера GCC:
int redpill()
{
unsigned char idt_addr[(sizeof(long)==8)?10:6];
__asm__("SIDT (%[ptr])"
: "=m" (idt_addr)
: [ptr] "r" (&idt_addr));
// examine high order byte
return idt_addr[(sizeof(long)==8)?9:5] > 0xd0;
}
Это должно "работать" даже для 64-битных систем, но я не проверял их там.
ТЕМ НЕ МЕНИЕ! Это вовсе не гарантирует, что вы получите желаемый результат. Во-первых, он не будет работать с аппаратной виртуализацией, так как вы не видите истинную IDT. Во-вторых, он опирается на детали реализации VMWare и Virtual PC, которые, вероятно, могут быть изменены довольно легко. Это может даже вызвать ложную тревогу, если ваша ОС решит поставить свою IDT на высокий адрес. Поэтому я не рекомендую этот подход вообще.
Для виртуальных машин, использующих аппаратную поддержку VMX, вероятно, лучше всего было бы сделать что-то, что должно быть быстрым в оборудовании, но требовать прерывания на виртуальной машине, и проверить время. Что-то вроде CPUID
было бы хорошей ставкой; сравните его в виртуальной машине и на реальном оборудовании (по сравнению с фиктивным циклом, который выполняет ADD или что-то, что имеет дело с разными тактовыми частотами), и посмотрите, какой профиль тестируемой машины более точно соответствует. Так как каждый CPUID
придется выйти из виртуальной машины, чтобы спросить ядро хоста о том, какие возможности он хочет предоставить; потребуется гораздо больше времени, чем на реальном оборудовании.
Для других типов виртуальных машин вы можете выполнить аналогичную проверку синхронизации, просто загрузив управляющий регистр или регистр отладки - виртуальная машина должна будет перехватить ее или заменить эмулированным кодом. Если вы работаете с чем-то похожим на VMware, он может заменить ловушку некоторым эмулированным кодом - в этом случае вы сможете идентифицировать его, изменив по времени код, содержащий управляющий регистр или операцию регистра отладки. Это заставит виртуальную машину сделать недействительным свой кэшированный код, что потребует дорогой ловушки для виртуальной машины, которая будет отображаться в вашем тесте.
Обратите внимание, что оба эти подхода потребуют помощи от ядра ОС - будет очень трудно определить, находитесь ли вы в ВМ, если у вас по крайней мере нет контроля над эмулируемым ядром. И если виртуальная машина действительно сложна, она может также подделать синхронизацию, и в этот момент все становится действительно сложно - но это приводит к снижению производительности и приводит к смещению часов (легко определить, если вы можете подключиться к Интернету и запросить время сервер где-то!), поэтому большинство коммерческих виртуальных машин этого не делают.