64-битное обнаружение Windows VMware
Я пытаюсь разработать приложение, которое определяет, работает ли программа внутри виртуальной машины.
Для 32-битной Windows уже есть методы, описанные в следующей ссылке: http://www.codeproject.com/Articles/9823/Detect-if-your-program-is-running-inside-a-Virtual
Я пытаюсь адаптировать код, касающийся обнаружения виртуальных ПК и VMware, в 64-битной операционной системе Windows. Для VMware код может успешно обнаруживаться в 64-разрядной ОС Windows XP. Но программа вылетает, когда я запускаю ее в собственной системе (64-битная ОС Windows 7).
Я помещаю код в файл.asm и определяю пользовательский шаг сборки с помощью файла ml64.exe. ASM-код для 64-битной Windows:
IsInsideVM proc
push rdx
push rcx
push rbx
mov rax, 'VMXh'
mov rbx, 0 ; any value but not the MAGIC VALUE
mov rcx, 10 ; get VMWare version
mov rdx, 'VX' ; port number
in rax, dx ; read port
; on return EAX returns the VERSION
cmp rbx, 'VMXh'; is it a reply from VMWare?
setz al ; set return value
movzx rax,al
pop rbx
pop rcx
pop rdx
ret
IsInsideVM endp
Я называю эту часть в файле cpp как:
__try
{
returnValue = IsInsideVM();
}
__except(1)
{
returnValue = false;
}
Заранее спасибо.
2 ответа
Старая красная таблетка от Джоанны может работать: случайная резервная страница блога invisiblethings.org:
Глотание Красной Таблетки более или менее эквивалентно следующему коду (возвращает ненулевое значение в Матрице):
int swallow_redpill () { unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3"; *((unsigned*)&rpill[3]) = (unsigned)m; ((void(*)())&rpill)(); return (m[5]>0xd0) ? 1 : 0; }
Сердцем этого кода на самом деле является инструкция SIDT (закодированная как 0F010D[addr]), которая хранит содержимое регистра таблицы дескрипторов прерываний (IDTR) в операнде-адресате, который фактически является ячейкой памяти. Что особенного и интересного в инструкции SIDT, так это то, что она может быть выполнена в непривилегированном режиме (ring3), но она возвращает содержимое чувствительного регистра, используемого внутри операционной системой.
Поскольку существует только один регистр IDTR, но одновременно работают как минимум две ОС (т.е. хост и гостевая ОС), VMM необходимо переместить IDTR гостя в безопасное место, чтобы он не конфликтовал с хостом. К сожалению, VMM не может знать, выполняет ли (и когда) процесс, выполняющийся в гостевой ОС, команду SIDT, поскольку он не является привилегированным (и не генерирует исключение). Таким образом, процесс получает перемещенный адрес таблицы IDT. Было замечено, что в VMWare, перемещенный адрес IDT находится по адресу 0xffXXXXXX, тогда как на Virtual PC это 0xe8XXXXXX. Это было протестировано на VMWare Workstation 4 и Virtual PC 2004, которые работают на хост-системе Windows XP.
Примечание: я не проверял это сам, но похоже, что он использует непривилегированный подход. Если сначала он не работает для x64, может помочь некоторая настройка.
Кроме того, только что обнаружил вопрос с контентом, который может вам помочь: Обнаружение VMM в Linux
Я предполагаю, что ваша функция портит регистры.
Запуск на реальном оборудовании (не в виртуальной машине), вероятно, должен вызывать исключение в "в rax, dx". Если это происходит, то управление передается вашему обработчику исключений, который устанавливает результат, но не восстанавливает регистры. Такое поведение будет полностью неожиданным для звонящего. Например, он может что-то сохранить в регистр EBX/RBX, а затем вызвать ваш ассемблерный код, ваш ассемблерный код "mov RBX, 0", он выполняет, перехватывает исключение, устанавливает результат, возвращает - и затем вызывающий абонент вдруг понимает, что его сохраненные данные больше не в EBX/RBX! Если в EBX/RBX был сохранен какой-то указатель - вы сильно упадете. Все может случиться.
Конечно, ваш ассемблерный код сохраняет / восстанавливает регистры, но это происходит только тогда, когда не возникает исключение. Т.е. если ваш код работает на ВМ. Затем ваш код выполняет свой обычный путь выполнения, исключений не возникает, регистры будут восстановлены в обычном режиме. Но если есть исключение - ваши POP будут пропущены, потому что выполнение будет передано обработчику исключений.
Правильный код, вероятно, должен выполнять PUSH/POP вне блока try/ кроме, а не внутри.