Engenharia Reversa e códigos criptografados

Engenharia Reversa
Imagem Ilustrativa – Engenharia Reversa

Introdução

Softwares capazes de criptografar a escrita de códigos fonte em diversas linguagens estão espalhados pela rede. Geralmente pagos, tais programas são capazes de fornecer ao usuário certa confiabilidade de que seu código não será modificado por terceiros e a vantagem do encapsulamento de toda a lógica de programação envolvida na criação do programa em questão. Você vai encontrar muitos destes códigos principalmente em produtos vendidos com uma licença, onde mesmo portando todos os arquivos necessários para o funcionamento, estes são ilegíveis para o usuário. Podemos citar alguns exemplos utilizados na linguagem PHP como ionCube, ZendGuard, phpSHIELD e sourceguardian.

Limitações de software

Infelizmente, alguns destes produtos possuem pouca ou nenhuma documentação (não sei se é porque os autores os consideram autoexplicativos ou tem preguiça mesmo) tornando dolorosa a integração no seu ambiente de desenvolvimento. Caso a empresa fornecedora ainda disponibilize uma API, a situação complica ainda mais  pois a possível integração com outros programas faz com que o detalhamento de uma documentação forte (tipos dos parâmetros, modelo de resposta de sucesso/erro, tratamentos, etc) e concisa seja mais necessária ainda.

Caindo nesta situação ou não, códigos compilados sempre são uma dor de cabeça para quem precisa conhecer mais detalhes de implementação. “Ah mas se alguém fez isso, a intenção é que essa pessoa não quer a edição do código e sim que ele seja simplesmente usado, sem alterações.  Além disso, por causa de licenças, é até ilegal tentar reverter ou obter detalhes específicos da implementação do autor”. Certo, não vou entrar em detalhes do que se pode/deve fazer com códigos criptografados, inclusive muitos deles vêm com uma mensagem de alerta já sobre este tipo de prática e.g “You may not reverse engineer, decompile, defeat license encryption mechanisms, or disassemble this software product or software product license”. O caso aqui é apenas informativo, estamos aplicando conhecimento, não incentivando a prática de sabotagem e violação de direitos e políticas de uso.

Técnicas de engenharia reversa

Estamos trabalhando com a linguagem PHP neste artigo, portanto iremos usar as ferramentas disponibilizadas nativamente pela linguagem para tentar descobrir detalhes de implementação de arquivos os quais não conseguimos acesso ao fonte original. Não mencionarei aqui o uso de debuggers como Xdebug (que pode ser uma boa opção) pois gostaria de dar foco diretamente a classes e funções da linguagem para que vocês possam dar uma olhada em um nível um pouco mais baixo, assim verão a variedade de técnicas que podemos usar para obter detalhes de uma “caixa preta”.

Estão listadas aqui as principais funções/classes do PHP que nos serão úteis para dar um debug em determinadas funcionalidades:

get_declared_functions() – Retorna um array com todas as funções definidas em tempo de execução;
get_defined_vars() – Retorna um array com todas as variáveis declaradas em tempo de execução;
get_declared_classes() – Retorna um array com o nome das classes definidas em tempo de execução;
ReflectionFunction – Reporta informações sobre uma função (a API inteira do Reflection por si já desempenha o papel na engenharia reversa) ;
ReflectionClass – Reporta informações sobre uma classe (a API inteira do Reflection por si já desempenha o papel na engenharia reversa) ;
debug_backtrace() – Gera um backtrace a partir do ponto que foi chamado
debug_print_backtrace() – Lista o backtrace de chamadas de funções no php (mais simples que o debug_backtrace)

Utilizar estas ferramentas pode ou não ser uma tarefa fácil. Se o autor comentou corretamente o cabeçalho das funções com o tipo dos parâmetros, o retorno, alguma explicação e outros detalhes, há uma chance maior de conhecermos a lógica da programação da classe ou função. Percebam que este é um trabalho investigativo e por vezes cansativo e frustrante. Porém, é deste jeito que a engenharia reversa funciona. Nada é tão simples e a cada passo que dermos, dependendo da complexidade do que investigamos, novos horizontes serão postos em jogo para que possamos inspecioná-los.

Exemplo

Usarei aqui um exemplo de um arquivo criptografado pelo ionCube. Observe o conteúdo:

<?php 
//  foo.class.php
//  Geralmente aqui se encontra algum comentário onde te desencoraja a usar engenharia reversa, decriptografação e outros meios capazes de desencapsular/modificar o conteúdo abaixo.
if(!extension_loaded('ionCube Loader')){$__oc=strtolower(substr(php_uname(),0,3));$__ln='ioncube_loader_'.$__oc.'_'.substr(phpversion(),0,3).(($__oc=='win')?'.dll':'.so');if(function_exists('dl')){@dl($__ln);}if(function_exists('_il_exec')){return _il_exec();}$__ln='/ioncube/'.$__ln;$__oid=$__id=realpath(ini_get('extension_dir'));$__here=dirname(__FILE__);if(strlen($__id)>1&&$__id[1]==':'){$__id=str_replace('\\','/',substr($__id,2));$__here=str_replace('\\','/',substr($__here,2));}$__rd=str_repeat('/..',substr_count($__id,'/')).$__here.'/';$__i=strlen($__rd);while($__i--){if($__rd[$__i]=='/'){$__lp=substr($__rd,0,$__i).$__ln;if(file_exists($__oid.$__lp)){$__ln=$__lp;break;}}}if(function_exists('dl')){@dl($__ln);}}else{die('The file '.__FILE__." is corrupted.\n");}if(function_exists('_il_exec')){return _il_exec();}echo('Site error: the file <b>'.__FILE__.'</b> requires the ionCube PHP Loader '.basename($__ln).' to be installed by the website operator. If you are the website operator please use the <a href="http://www.ioncube.com/lw/">ionCube Loader Wizard</a> to assist with installation.');exit(199);
?>
HR+cPwXaWbBeoNPbuTKNSVpZqpN+oAtPNtNjlfYiPCc2mlwAqsXxDbZcfl2K6WO0CHKddChRMfI/
IVNJ83hk6hyzoI47wqPFfbHRgOjZWb1GPYoH173wv83BSp6GlKFvSFTqsomiTXdoA2HhAT9ebTOR
10oyg/f2/IfCZ6IxmOVboj+DrpT4bc47lwUfZejB//SLL02UsJ2Bj4rkkmeAKFNccgQP+/yUmWaZ
/Sk7AqsrFLdhPR3hmn8LsW0MgIqXvg38aL5RRo+CKITc2GjAiKWtDTfstxHvjHzm3QzAohn6E1Vr
pVH9zTUG9eGcUYPhh/8iIRnkwFdbuvtyVL3oWvfgUc+UkxMoiAlKPVld2+3QupSMAfefCJW19oH0
3ObqC3tGraI0zkA+LNnAq6ngbh+GjPlPANSYAf5B4BMj1XCsI2z58EpDilAC+KjvDEiUpf40I8ES
7mVfwLAtQGqTWcDQccA7Q9i2UHzI770hIa2Z3OLOu+Lan8AgKOTOwvkg0gzGj7oJv9VxgglQZBXi
D65kK+jOLiF+uzggyp5GU8XOt553qNc/kxDwda356DIJSVvKbubsy8dm4H4iqMjM61T1ntWSb7HI
/F6eW8A4d/9mSM5NTlD2T8Xa8WoyHv42Ucne73gAy9KG/p5W8dVoT2885eTKLQMHmv1LgcWEr6op
yOh4Qlk8bLV5Y4xVOMx+VPv3kyOJh9v7p2FYxg/mmLgwuW9ec168w5954yee2wVcUwmn+dctdrmH
qwTAvnyeyrMHGti4+RaxA83N4U1Kv5VaeCHHdcgxu5BNo3urgUwexfmW1qD/5PlV3nfX0UiEt819
HR9MX1+VTj9q54jl772sclpA8VBrf+9uIq8kQdT4L4pdPMLbKg0h3+bHohMtp+qF7ANjZFhibySE
kxapXBcpLEI5IDR/6f2v97jWMM/NdOJ2eF9CmBqrDc8xlKyoYX3sqiwe9djDhjZV/9nNjzcp3aTN
y/I3sLfAgRapC0Kx5nrfD2gx0ByBbWNxdXOh2zixCTVH0VxZTNIea9m98fcZdqrq+gf2HJ00KThu
g+Rc84X+Q9qTNy9ZL7KvSXbE6wOX0mYQ6sumw7FctbSa94f+4d9pkQtdrc8klaxWfMwOumkX66cA
7zS2OFWjIL2r7naC9U3vWGDmZkbE0mpr4Ox2Hd/SJKlC2kD524Y+hy9fB1+5DNRQtuh61lEN1pdS
SkomStrIv/5Wo53bOVoyv/h9L+lhofln95Cq9SdnT4KNhuLaBiG4+B1FnzTvAXYnGkgJGaRFRYGZ
wtfdkoJdblh06hyV06Fo5UahTpiPgfmv8sx6N10W4V+QTWJeeD1mLmJY9rGiGSBNV52/WNWHwwxX
DprWWWXy8VS85e/wBJD9RD0F1lr8m8iUMHvCnHQUSBLG3WjrznmtDkeRgmccbOpG40PjMvcLxOxz
5NNoDdORj6122Ki5yrc7yLAgXsOZDYQ+flbExF/2fu23/J1VNGklLB9ot34xW8n+mL6rZDYU9ad0
IxNwkx668/i4WcyHcAk0wOYYwjtfHTJ7QoB3miWgWphCWmspsELboaLzgIno1tF7cBqtPpaG21+T
t5OaIXPoz6TcKOeWEK7vHMQKJi5Y/PIT8sVlHfr7XB5taarmolHahJQQMlMZAJ3KpX1Wr7LDDZHs
8YJIjsAFcL+5ViIpyzYMiV1p4OCIHunKiGUPvyvuZy6Ply4NenyIPdm=

Dada a situação de um código ilegível, vamos aplicar, sequencialmente, algumas das técnicas mostradas acima. No caso,

get_declared_classes()
ReflectionClass()

pois, por inspeção (diretório do arquivo, nome do arquivo, sequenciamento da lógica executada até o ponto, etc) sei que se trata de uma classe e que esta, possivelmente não trabalha sozinha, ou seja, precisa da integração com outras classes nas formas de agregação ou composição.

<?php
	include 'foo.class.php';
	echo '<pre>';
	var_dump(get_declared_classes());

	echo new ReflectionClass('Foo'); // __toString()
?>

Como retorno obteremos

array(133) {
  [0]=>
  string(8) "stdClass"
  [1]=>
  string(9) "Exception"
  [2]=>
  string(14) "ErrorException"
(...)
  [131]=>
  string(13) "XSLTProcessor"
  [132]=>
  string(3) "Foo"
}

/**
     * Foo
     * 
     * Alguma descrição virá aqui
     * Isto é bom
     *
     * @author Alguém
     */
Class [  class Foo implements Bar ] {
  @@ /var/www/html/testes/index.php 14-55

  - Constants [1] {
    Constant [ integer CONSTANTE ] { 5 }
  }

  - Static properties [0] {
  }

  - Static methods [0] {
  }

  - Properties [2] {
    Property [  private $a ]
    Property [  private $b ]
  }

  - Methods [2] {
    /**
     * teste
     *
     * Esta função faz x, y e resulta em z
     *
     * @return	string
     */
    Method [  public method teste ] {
      @@ /var/www/html/testes/index.php 41 - 43
    }

    /**
     * novo
     *
     * Recebe um inteiro e retorna um bool
     *
     * @params	bool	$param
     * @return	int
     */
    Method [  public method novo ] {
      @@ /var/www/html/testes/index.php 52 - 54

      - Parameters [1] {
        Parameter #0 [  $param ]
      }
    }
  }
}

Vejam que ao chamar as funções de classes definidas, observamos, após a listagem de todas as classes pré-definidas do PHP, uma classe Foo, da qual não temos certeza se é ou não nativa da linguagem.

Inspecionamos com o ReflectionClass e por fim podemos entender um pouco da estrutura daquela classe que inicialmente estava obscura devido ao uso do ionCube.

Conclusão

Vimos então que, pelo menos para o PHP, é possível obter detalhes de implementação independente de como o software foi organizado e seu acesso disponibilizado. Não precisamos essencialmente do acesso direto ao conteúdo lógico e sequencial do arquivo, basta incluí-lo e inspecionar o comportamento de suas classes, funções e variáveis.

Espero que este artigo tenha lhe proporcionado uma luz sobre códigos “obscuros”. Lembre-se que você também pode utilizar estas funções para debugar seu próprio código (que acredito ser a ideia original delas), funcionando como uma ferramenta de apoio para a sequência lógica dos seus programas.