Detectando Máquinas Virtuais
1. Introdução
As vezes precisamos que nosso programa se comporte de forma diferente quando estiver executando em uma máquina virtual. Conseguir a restrição de funcionalidades em ambiente virtualizado é, além de mais uma técnica anticracking – utilizada para evitar análise online de código –, um importante recurso para garantir compatibilidade, segurança, estabilidade e proteção de recursos.
Um desenvolvedor que necessita proteger seu código, precisa dificultar o processo de engenharia reversa do seu programa, antecipando as condições do ambiente em que o cracker trabalhará. Ou seja, deve de restringir algumas funcionalidades que torna o código permissível ao cracking.
Para o cracking, conhecer as técnicas de detecção de máquinas virtuais é fundamental para criação de software que utilizem VM Escape. Encontrar brechas em MV permite que acessemos como superusuários uma máquina virtual e, em alguns casos, até a máquina hospedeira.
2. A Pílula Vermelha
O programa que abaixo é uma simples aplicação da detecção de VM: impedir ações para dificultar o processo da análise online do código.
Com base no código de Joanna Rutkowska, “Red Pill” (em uma referencia ao filme Matrix), nosso programa restringe a execução de algumas de suas partes, permitindo que rode somente em ambientes não-virtuais.
A idéia é simples: o programa possui uma função inicializadora, que poderia ser protegida ou não por um keycheker. Esta função (programStarter) verifica se o programa está dentro de uma máquina virtual. Se estiver, ele emite uma mensagem dizendo que não continuará sua execução e termina.
Novamente, este exemplo é um resumo simplificado de como alguns sistemas (como o Windows Vista) fazem para bloquear seus recursos quando executado em máquinas virtuais. Segue abaixo o código.
/*
* main.cpp
*
* Created on: 23/07/2008
* Author: SAWP
*
*
* If on a VM, the programa dont execute. The program starter is called if
* running on hardware.
*
* www.sawp.com.br
*/
#include
using namespace std;
using std::cout;
using std::cin;
#include "SystemMon.h"
int main(void){
SystemMon *ptrSysMon = NULL;
ptrSysMon = new SystemMon();
exit(0);
}
/*
* SystemMon.h
*
* Created on: 23/07/2008
* Author: SAWP
*/
#ifndef SYSTEMMON_H_
#define SYSTEMMON_H_
class SystemMon {
public:
SystemMon();
virtual ~SystemMon();
protected:
const bool isVirtualMachine(void) ;
void programStarter(void);
void program(void);
};
#endif /* SYSTEMMON_H_ */
/*
* SystemMon.cpp
*
* Created on: 23/07/2008
* Author: SAWP
*/
#include "SystemMon.h"
#include
/** ### Public ### **/
SystemMon::SystemMon() {
programStarter();
}
SystemMon::~SystemMon() {
// TODO Auto-generated destructor stub
}
/** ### Private ### **/
/**
* Detect if is on virtual machine.
*
* Based on: http://invisiblethings.org/tools/redpill.c
*
* modified from joanna at invisiblethings.org
* should compile and run on any Intel based OS
*
* http://invisiblethings.org
*/
const bool SystemMon::isVirtualMachine() {
unsigned char m[ 6 ];
//unsigned char rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3";
unsigned char rpill[] = "\x0F\x01\x0D\x00\x00\x00\x00\xc3";
*((unsigned*) &rpill[3]) = (unsigned) m;
((void(*)()) &rpill)();
if(m[5] > 0xd0){
return true;
}
return false;
}
/**
* The program functionality
*/
void SystemMon::program(){
puts("Hello World, Im not in VM. Im the host!");
}
/**
* start the program if running without VM
*/
void SystemMon::programStarter(){
// your start-check code here...
if(!this->isVirtualMachine()){
this->program(); //start the program if isnt in VM
}else{
puts("Execute at host machine.");
exit(EXIT_SUCCESS); // Don't allow execute.
}
}
Conforme o autor do código, Rutkowska, tomando o pílula vermelha, é mais ou menos como o código da função isVirtualMachine()
, que retornará true se estiver executada em uma máquina virtual.
“Red Pill” é um código chama atenção porque consegue detectar uma MV com um código reduzido. Ela também é asm-free, ou seja, independe de compiladores e pode rodar em qualquer sistema operacional, sendo uma solução portável.
Veja o programa sendo executado em uma máquina virtual com MS Windows como guest:
Download: http://www.megaupload.com/pt/?d=I7T4TND0
Agora, o mesmo programa compilado e executado em um Host Unix.
Download: http://www.megaupload.com/pt/?d=RMVQ14YK
3. Funcionamento
Este código utiliza a instrução SIDT ( _asm sidt idt )
, codificada como 0F010D[endereço]
(“\x0f\x01\x0d”) ou a SIDTL
, codificada como 0F01F8[endereço]
(ou _asm sidtl 0xfffffff8(%ebp)), que guarda o conteúdo da “interrupt descriptor table register” (IDTR
) no operador destino.
O mais interessante sobre a instrução SIDT
é que ela pode ser executada sem controle de privilégio, mas pode retornar o conteúdo do registrador usado internamente pelo sistema operacional (Risc’s: $k0,$k1, Cisc’s: sensitive register, exclusivos do SO).
Como este é apenas um registrador de IDTR
, se estiver em uma MV teria ainda que ter outro(s) registradores do sistema operacional host. Então, a máquina virtual precisará recolocar o IDTR
do sistema que estiver rodando em um lugar seguro a fim de evitar um conflito com o SO host.
Contudo, a MV não pode saber onde o sistema virtual irá executar a instrução SIDT, desde que ela não gere uma exceção. Assim, nosso processo “captura” o endereço recolocado pela MV na IDTR.
“Red Pill” então detecta se o IDTR
e verifica o local dele, dependendo do que ela retorna sabe-se se o ambiente se o ambiente é virtualizado ou não.
Na VMWARE o endereço recolocado do IDTR
tem base 0xFFxxxxxx
e no VirtualPC é recolocado numa região 0xe8XXXXXX
. Segundo o autor do código ( http://invisiblethings.org ), “Red Pill” foi testado utilizando as MV: VmWare Workstation 4 e o Virtual PC 2004, ambos rodando em um host com WinXp.
Veja mais da pesquisa em: http://www.offensivecomputing.net/files/active/0/vm.pdf .
Eu testei o código utilizando a máquina virtual VirtualBox 1.5.6_OSE, utilizando como host os sistemas: WinXp Sp1, Linux Ubuntu 8.04 e Free BSD. Em todos foi detectado que o programa estava sendo executado diretamente no host.
Nas máquinas virtuais compilei e executei no Ubuntu e no Windows XP. Em ambos sistemas foi detectado que a execução ocorria em ambiente virtualizado. Logo, o código funciona com sucesso nas três máquinas virtuais e em todos os sistemas mencionados.
Nas figuras abaixo podemos ver um Host e uma máquina virtual rodando, ao mesmo tempo, o mesmo programa:
4. Algo Mais?
Como podemos observar, a técnica utilizada para detecção da Pílula Vermelha consiste em checar indícios da MV por processos e também checando a existência da MV buscando no SIDTR
(Store Interrupt Descriptor Table Register).
Esta técnica é a que permite dificultar mais o trabalho de cracking, desde que os processos fiquem monitorados de forma correta e as chamadas da função ficarem distribuídas pelo programa.
Como o código é pequeno (quase uma instrução da CPU), aplicando à Pílula Vermelha a técnica de inlining, torna-se um inferno para o cracker entender o local no código onde está a proteção.
Porém, há outras formas de se detectar a presença de um ambiente virtualizado: Checar hardwares (se há assinatura conhecida, como sendo de uma MV); ou procurar por MV nos registros ou em arquivos específicos. Contudo, estes métodos são mais fáceis de se burlar. Basta que o usuário modifique alguns registros no sistema.
5. Outros Códigos
Existem ainda outros códigos disponíveis que podem ser interessantes para o programador.
5.1 Scoopy-Doo
Trata-se de um código escrito por Tobias Klein http://www.trapkit.de. Atua testando registros na SIDTR
(Store Interrupt Descriptor Table Register), na SGDTR
( Store Global Descriptor Table Register) e na SLDT
( Store Local Descriptor Table ) e é focado para detectar a presença da VMWare.
É descrito como sendo uma “suíte” de instruções. Utiliza instruções que testam os IA32 SIDT, SGDT, e SLDT
. É um teste semelhante à Red Pill, mas com menor portabilidade.
A portabilidade também é prejudicada pelos testes de busca de hardware. O Scoopy-Doo busca pela presença de hardware virtualizado. Como os sistemas operacionais diferentes possuem forma de classificação de hardwares diferentes, deve-se ter versões de código alternativas para comunicação entre programa-SO.
No Linux o scoopy-Doo busca no diretório /proc pela string “VmWare”, associando com os componentes de E/S, portas-padrão utilizadas pelas MV ( /proc/iomem, /proc/ioports, /proc/scsi/scsi ) e ainda tenta encontrar a presença de máquinas virtuais buscando processos específicos – analisando o retorno do comando dmesg.
No Windows, procura por registros no sistema:
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port
0\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port
1\Scsi Bus 0\Target Id 0\Logical Unit Id 0\Identifier
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\
{4D36E968-E325-11CE-BFC1-08002BE10318}\0000\DriverDesc
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\
{4D36E968-E325-11CE-BFC1-
08002BE10318}\0000\ProviderName
Segundo o autor possui maior garantia de se identificar o ambiente. sendo três testes pelo preço de um! Realmente, é muito eficiente, mas contribui como fator limitante à portabilidade do código usuário.
A página do autor é: http://www.trapkit.de . Pode-se fazer o download dos fontes em: http://www.trapkit.de/research/vmm/scoopydoo/scoopy_doo.tar.gz (Unix-like) ou http://www.trapkit.de/research/vmm/scoopydoo/scoopy_doo_win.zip (Windows).
5.2 VmDetect[ion]
Escrito por lallous (http://www.codeproject.com/script/Membership/Profiles.aspx?mid=139861). Este código utiliza diferentes técnicas para detectar a presença das MV: VirtualPC e VmWare.
VmDetect consiste em determinar se o código está sendo executado em uma VirtualPC tentando executar uma instrução não-padrão do set de instruções x86 emulados/usados pelo VirtualPC.
Em uma máquina real o uso de uma instrução não-existente geraria um erro fatal na aplicação. O processador iria parar o programa e notificar o erro para o SO.
Porém, é possível ao programador prever uma situação de erro através dos controle de exceções. Cria-se uma função que, se o erro ocorrer, retorna true, senão retorna falso.
Essas funções são conhecidas como “error-handling code” e é este mecanismo que permite detectar a VirtualPC.
É um código muito efetivo na detecção. Muito recomendado quando se quer restringir funcionalidades para uma MV específica (por exemplo, a Microsoft construindo produtos que só executam na sua VirtualPC). Todavia, possui pouquíssima portabilidade e é muito dependente de arquitetura.
Os fontes e documentação podem ser encontrados em: http://www.codeproject.com/KB/system/VmDetect.aspx .
5.3 The Jerry Code
De forma semelhante ao código da VmDetection, Jerry utiliza uma instrução não-padrão da arquitetura para testa a presença da máquina virtual.
Também de autoria de Tobias Klein ( http://www.trapkit.de ), pode ser encontrado em http://www.trapkit.de/research/vmm/jerry/index.html .
5.4 ScoopyNg
Evolução do Jerry e do Scoopy Doo. Seu código pode ser encontrado em: http://www.trapkit.de/research/vmm/scoopyng/ScoopyNG.zip
6. Conclusão
A detecção de máquinas virtuais é um grande passo para os programadores que querem proteger seu código de um processo de análise online ou restringir recursos por questões de segurança.
Para um cracker é extremamente necessário saber da existência desses recursos para quando se deparar com código que “não se comporta como aparentemente deveria”. Uma das técnicas anti-cracking consiste neste tipo de detecção, antevendo os métodos de reversão.
A detecção de MV pode ser somente o primeiro passo para se testar acessos à regiões de dados que supostamente deveria estar protegida pelo sistema operacional.
Com o aumento crescente de recursos de hardware, a virtualização é um ramo da computação que vêm crescendo muito nesta última década. Com a crescente aceitação de sistemas operacionais livres coexistindo com Windows, a virtualização tende a estar cada dia mais presente entre os usuários comuns. Por isso se tornará mais e mais importante o uso de técnicas de detecção e, por conseqüência, anti-detecção de máquinas virtuais.