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:

both_destacado

both

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.