Показать сообщение отдельно
Старый 01.07.2012, 18:56   #1
Specialist
 
Регистрация: 13.06.2012
Сообщений: 25
Репутация: 20
По умолчанию Под колпаком: обнаружение виртуальных машин / гипервизоров в *NIX

Часто бывает полезно определить, где мы находимся - в гостевой ОС или нет.
Способов это узнать больше десятка, однако все они делятся на 2 типа:
- поиск различий между поведением эмулятора и реального железа;
- чтение данных эмулируемых железок (чтение MAC, адреса шины,
названия устройства);

По поводу 2 способа распространяться особо не буду в силу его ненадёжности.
Ведь почти на всех VM можно изменить название/данные оборудования.
Но иногда бывает достаточно и простейших вещей. В Linux есть dmidecode,
которая легко выведет на чистую воду тот же VirtualBox в дефолтной инсталляции.
Из dmesg также можно выудить много полезного.
К данному способу следует отнести и сканирование BIOS на предмет заданных
строк, выдающих виртуалку с потрохами.

Что же касается неточностей в работе эмуляторов, то здесь существует непаханое
поле для детекта. Например, есть способ с таблицей IDT, которую VMWare
вынуждена копировать для нормального сосуществования с хост-системой.
Разница адресов как бы говорит нам "Я виртуалка! А ты муха в стакане!".
Тоже относится и к GDT. Способ довольно ненадёжен, к тому же он устарел. Пример можно посмотреть в RedPill.

Идём дальше. В некоторых виртуалках есть бекдоры. Например, в Parallels и
VMWare есть волшебные порты, с помощью которых можно вызывать API
виртуалки. Например:
PHP код:
   asm volatile (
    
"movw $0x5658, %%dx;"
    "in %%dx, %%eax;"
    
"=b"(result), "=a"(mem_size)
    : 
"a"(VM_MAGIC), "b"(0), "c"(0x14)
    ); 
Здесь VM_MAGIC это магическое число, которое кладётся в eax (VMXh),
а 0x14 - номер процедуры VM (узнать количество оперативки, выделенное VM).
Во многих источниках Варюшу детектят через функцию, возвращающую номер версии,
однако её можно отключить.

Рассмотрим ещё один способ. На этот раз на операционном столе у нас окажется
VirtualPC. Путём дизассемблирования хакеры выяснили, что она проглатывает
плохой опкод. Алгоритм детекта прост как 2 байта - скармливаем ей
опкод $0f,$3f,$07,$0b и ловим исключение SIG_ILL. Если его нет - значит
мы под колпаком, всё пропало и больше некуда бежать
Пример реализации:
PHP код:
    asm volatile (
    
".byte 0x0f, 0x3f, 0x07, 0x0b;"
    "test %%ebx, %%ebx;"
    "setz %0"
    
"=a"(result)
    : 
"a"(1), "b"(0)
    ); 
Обработка исключений производится путём установки обработчика сигнала
SIG_ILL и убойного комбо setjmp/longjmp, позволяющего откатиться назад
во времени (setjmp сохранит стек и регистры, longjmp восстановит).
Эх, вот бы найти способ использовать эти функции в реале. И возможно мне
бы не пришлось целый день прятаться в лесу, когда я в юные годы из озорства
затопил пионерский лагерь г*вном через забившиеся толчки (SIGSHIT) o_O

PHP код:
void segv_handler(int status
 {
    
// вернём всё назад
    
siglongjmp(env1);
 }

...

 
// установим обработчики
 
signal(SIGSEGVsegv_handler);  
 
signal(SIGILLsegv_handler);  

...

 if(
sigsetjmp(env1) == 0) {        
    
// злобокод
      // хихи, ой как стыдно то, за нами подглядывают :)
 
}

 
// поймали исключение - всё ок 
А вот ещё один забавный способ ухватить девчонку Bochs за ляжку:

PHP код:
  asm volatile (
    
".byte 0x66;"        // some hack to enforce gcc
    
"bswap %%eax"    // to accept 16 bit bswap operand
    
"=a"(result)
    : 
"a"(VM_MAGIC)
    );
    
    if (
result << 16) {    // %ax should be null on real CPUs
    
return TRUE;
    } 
Фишка в том, что результат работы bswap (изменить порядок байт) с 16-разрядным
операндом не определён (в соответствии с доками Intel). В реальности же
Intel/AMD просто обнулят незадачливый операнд. Однако Bochs всё делает по
своему, поэтому для детекта нам достаточно проверить результат на 0.

Что же касается обнаружения гипервизора, то наркософт документировала такую
возможность, однако их далеко не все слушаются (но Microsoft HV то уж точно).
В данном случае всё просто - команда cpuid должна записать в регистр значение
с установленным битом 31.

PHP код:
// check if 31 bit is set
        
asm volatile (
        
"cpuid;"
        "andl %%ebx, %%ecx;"
        
"=c"(result)
        : 
"a"(1), "b"(HYPERVISOR_BIT)
    ); 
Проверка является НЕНАДЁЖНОЙ. Для снижения вероятности ложной тревоги, стоит
ещё проверить адреса функций (leaf range):

PHP код:
       asm volatile (
        
"cpuid;"
            
"=a"(result)
        : 
"a"(HYPERVISOR_BIT
        ); 
А на закуску хочу описать собственный способ детектирования виртуальной
машины / гипервизора, который ко мне пришёл во время медитации с кодом ядра
Linux. Стоит отметить, что он тоже неуниверсален и работает довольно выборочно.
А идея состоит в том, что некоторые виртуалки (VirtualBox) и даже гипервизоры
не устанавливают регистры MTRR (Memory Type Range Registers - регистры,
позволяющие устанавливать диапазоны памяти с различными типами кэширования в
целях оптимизации). Есть FIXED MTRRs (диапазоны зашиты), а есть и те,
которые задаются динамически. В своём коде я работаю со вторыми. И конечно же,
для этого нужен модуль ядра, т.к. работа с этими регистрами возможна только
в реальном режиме. Пример того, как можно пробежаться по всем переменным MTRR
регистрам с помощью команды rdmsr:

PHP код:
for (0msr_cap.vcnti++) {
        
        
asm volatile (
        
"rdmsr;"
        
"=a"(low), "=d"(high)
        : 
"c"(VAR_MTRR_START 2)
        );
        
        
valid = (low RANGE_VALID) >> 11;
        if(
valid) {
            
has_valid_range TRUE;
        } 

Пару слов по поводу программы - достаточно просто распаковать и написать make,
всё остальное произойдёт автоматически. Код модуля работает только на 32-битных
системах (полагаю, на 64х ничего страшного не произойдёт, но лучше проверить),
поддержка 64-bit SMP появится позже (как появится железо).
Если вы знаете интересные способы обнаружения виртуалок, я постараюсь их
добавить в свою программу. Небольшая просьба: если будете тестировать,
напишите, где детект работает / не работает (версия VM) [не запускайте модуль где попало].

http://rghost.ru/38974794

p.s. не обращайте внимание на ошибку EPERM, так и должно быть
Specialist вне форума   Ответить с цитированием