Detectando Máquinas Virtuais

July 30th, 2008 by SAWP | Filed under Virtualização

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.

  1. /*
  2.  * main.cpp
  3.  *
  4.  *  Created on: 23/07/2008
  5.  *  Author: SAWP
  6.  *
  7.  *
  8.  *  If on a VM, the programa dont execute. The program starter is called if
  9.  *        running on hardware.
  10.  *
  11.  * www.sawp.com.br
  12.  */
  13. #include
  14. using namespace std;
  15. using std::cout;
  16. using std::cin;
  17.  
  18. #include "SystemMon.h"
  19.  
  20. int main(void){
  21.         SystemMon *ptrSysMon = NULL;
  22.         ptrSysMon = new SystemMon();
  23.  
  24.         exit(0);
  25. }
  1. /*
  2.  * SystemMon.h
  3.  *
  4.  *  Created on: 23/07/2008
  5.  *  Author: SAWP
  6.  */
  7.  
  8. #ifndef SYSTEMMON_H_
  9. #define SYSTEMMON_H_
  10.  
  11. class SystemMon {
  12. public:
  13.         SystemMon();
  14.         virtual ~SystemMon();
  15. protected:
  16.         const bool isVirtualMachine(void) ;
  17.         void programStarter(void);
  18.         void program(void);
  19. };
  20.  
  21. #endif /* SYSTEMMON_H_ */
  1. /*
  2.  * SystemMon.cpp
  3.  *
  4.  *  Created on: 23/07/2008
  5.  *  Author: SAWP
  6.  */
  7.  
  8. #include "SystemMon.h"
  9. #include
  10.  
  11. /** ### Public ### **/
  12.  
  13. SystemMon::SystemMon() {
  14.         programStarter();
  15. }
  16.  
  17. SystemMon::~SystemMon() {
  18.         // TODO Auto-generated destructor stub
  19. }
  20.  
  21. /** ### Private ### **/
  22.  
  23. /**
  24.  * Detect if is on virtual machine.
  25.  *
  26.  * Based on: http://invisiblethings.org/tools/redpill.c
  27.  *
  28.  * modified from joanna at invisiblethings.org
  29.  * should compile and run on any Intel based OS
  30.  *
  31.  * http://invisiblethings.org
  32.  */
  33. const bool SystemMon::isVirtualMachine() {
  34.  
  35.         unsigned char m[ 6 ];
  36.         //unsigned char rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3";
  37.         unsigned char rpill[] = "\x0F\x01\x0D\x00\x00\x00\x00\xc3";
  38.  
  39.         *((unsigned*) &rpill[3]) = (unsigned) m;
  40.         ((void(*)()) &rpill)();
  41.  
  42.         if(m[5] > 0xd0){
  43.                 return true;
  44.         }
  45.  
  46.         return false;
  47. }
  48.  
  49. /**
  50.  * The program functionality
  51.  */
  52. void SystemMon::program(){
  53.         puts("Hello World, Im not in VM. Im the host!");
  54. }
  55.  
  56. /**
  57.  * start the program if running without VM
  58.  */
  59. void SystemMon::programStarter(){
  60.         // your start-check code here…
  61.  
  62.         if(!this->isVirtualMachine()){
  63.                 this->program(); //start the program if isnt in VM
  64.         }else{
  65.                 puts("Execute at host machine.");
  66.                 exit(EXIT_SUCCESS); // Don’t allow execute.
  67.         }
  68. }

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.

Tags: tag_icon [ Tags: Virtualização ]

Você pode ler as eventuais respostas desta entrada através do RSS 2.0 feed.
Você pode deixar uma resposta , ou trackback a partir do seu próprio site.