Engenharia Reversa Aplicada a Segurança de Executáveis
-
Upload
filipe-carvalho -
Category
Software
-
view
396 -
download
0
Transcript of Engenharia Reversa Aplicada a Segurança de Executáveis
ANHANGUERA EDUCACIONAL S.A.
Faculdade Anhanguera de Rondonópolis
Curso de Sistemas de Informação
1158386326 – Filipe de Souza Carvalho
Engenharia Reversa Aplicada a Segurança de Executáveis
Rondonópolis
2015
1158386326 – Filipe de Souza Carvalho
Engenharia Reversa Aplicada a Segurança de Executáveis
Monografia apresentada, como exigência parcial para a obtenção do grau de Bacharel em Sistemas de Informação, na Faculdade Anhanguera Rondonópolis, sob a orientação do Humberto Roque Kuhn.
Rondonópolis
2015
1158386326 – Filipe de Souza Carvalho
Engenharia Reversa Aplicada a Segurança de Executáveis
Trabalho de Conclusão de Curso apresentado como exigência parcial para a obtenção do grau de Bacharel em Sistemas de Informação da Faculdade Anhanguera Rondonópolis.
Monografia aprovada em ____/____/_______.
________________________________
Orientador Prof. Esp. Humberto Roque Kuhn
Faculdade Anhanguera de Rondonópolis
Orientador
________________________________
Prof. Esp.
Faculdade Anhanguera de Rondonópolis
Avaliador
________________________________
Prof. Esp. Pierre Inácio
Faculdade Anhanguera de Rondonópolis
Coordenador do Curso de Sistemas de Informação
Rondonópolis
2015
RESUMO
Com a popularidade e importância da informática na atualidade, grandes transformações ocorrem frequentemente nos processos de criação de software, com intuito de se aumentar produtividade, reduzir custos de manutenção, se diferenciar da concorrência, etc. Com isto em mente, o antigo conceito de engenharia reversa, que pode ser descrito objetivamente como “desmontar algo para entender seu funcionamento”, foi adaptado à indústria de software para facilitar este processo, muitas vezes de forma controversa, considerada por alguns como algo ilegal. Serão utilizados exemplos práticos utilizando ferramentas específicas com linguagens de baixo nível para reforçar os conceitos de engenharia reversa. Palavras-Chave: segurança, engenharia reversa, software, usuários
ABSTRACT
With the current popularity and importance of computing, big changes occur frequently in the software creation process, with the intent of increasing productivity, reducing maintenance costs, differentiation of the competition, etc. With this in mind, the old concept of reverse engineering, that can be objectively described as “disassemble something to understand its behavior”, was adapted to the software industry to make this process easier, often controversial, and considered by many as something illegal. Practical examples will be used utilizing specific tools with low level languages to reinforce the concepts of reverse engineering. Keywords: security, reverse engineering, software, users
LISTA DE ILUSTRAÇÕES
Figura 1 – Interface da ferramenta OllyDbg
Figura 2 - Código fonte em linguagem Delphi
Figura 3 - Tela console com o resultado do exemplo em Delphi
Figura 4 - Código em linguagem Assembly equivalente ao Código Fonte em Delphi
Figura 5 - Código em linguagem Assembly com alteração da operação aritmética em
debugger
Figura 6 - Tela console com o resultado do exemplo em Delphi depois de ser
alterado no debugger OllyDbg
Figura 7 - Código Fonte em linguagem C#
Figura 8 - Tela console com o resultado do exemplo em C# original
Figura 9 - Exemplo em C# descompilado na ferramenta ILSpy
Figura 10 - Código do Exemplo em C# descompilado na linguagem CIL pelo plugin
Reflexil
Figura 11 - Tela console com o resultado do exemplo em C# depois de ser alterado
na ferramenta ILSpy
Figura 12 – Código JavaScript sem ofuscação
Figura 13 – Código JavaScript depois de sua ofuscação
LISTA DE SIGLAS
API – Application Programming Interface
DLL – Dynamic-link Library
JVM – Java Virtual Machine
CIL – Common Intermediate Language
MSIL – Microsoft Intermediate Language
IDE – Integrated Development Environment
SUMÁRIO
1 - INTRODUÇÂO .................................................................................................... 8
2 – ASPECTOS JURIDICOS ................................................................................. 10
3 –DEFINIÇÕES E CONCEITOS........................................................................... 12
3.1 Difusão .......................................................................................................... 12
3.2 Debugging .................................................................................................... 12
3.3 Domínios hierárquicos de proteção .............................................................. 13
4 – ESTUDO DE CASO: ENGENHARIA REVERSA NA PRÁTICA ....................... 15
4.1. Ferramentas................................................................................................. 15
4.1.1 OllyDbg ................................................................................................... 15
4.1.2 ILSpy ...................................................................................................... 16
4.1.3 Reflexil .................................................................................................... 17
4.2. Exemplo 1: Linguagem Delphi ..................................................................... 17
4.3 Exemplo 2: Linguagem C# ............................................................................ 22
4.3: Complementação ......................................................................................... 28
5 – SEGURANÇA .................................................................................................. 29
5.1: Ofuscação .................................................................................................... 29
5.2: Compressão ................................................................................................ 30
6 – CONSIDERAÇÕES FINAIS ............................................................................. 32
REFERÊNCIAS ...................................................................................................... 34
8
1 - INTRODUÇÂO
Programas de computador são comumente distribuídos para seus usuários
finais somente em forma de binários, enquanto seus desenvolvedores mantém sua
versão entendível por humanos (ou código fonte) como um “segredo comercial”. Na
versão binária, é virtualmente impossível para um usuário “lê-la” enquanto se
mantém nesta forma. Já desenvolvedores desejando estudar sua estrutura, ou até
alterá-la, seria necessário obter acesso à este programa na forma de código fonte.
Por esta razão, desenvolvedores que não possuem este acesso utilizam a
prática chamada “engenharia reversa”, que emula as etapas originalmente
envolvidas em criar o programa em questão.
A engenharia reversa é o processo de analisar um sistema, identificar seus
componentes e suas inter-relações, representando-os de outra forma ou nível maior
de abstração. O conceito surgiu décadas antes da era da computação, apenas foi
adaptada de indústrias como a automobilística, onde as fabricantes compravam
veículos de suas concorrentes para desmontá-los, analisar seus componentes e
assim melhorar seus próprios veículos.
Segundo Saleh e Boujarwah (1996), o mercado de software todo o dia cresce
mais e mais, e com ele o uso de técnicas de programação, muitas vezes informais.
Como resultado, a manutenção destes produtos se torna problemática, uma vez que
a documentação associada aos mesmos, na maioria das vezes, não estão de
acordo com o código implementado.
Diariamente ao utilizar o computador, executamos uma variedade de
softwares sem saber realmente quais operações são realizadas. O desenvolvimento
de software atualmente é um processo tão complexo e interconectado que o
desenvolvedor comumente não tem conhecimento de todas as repercussões e
recursos dos projetos que ele mesmo mantém.
Além disso, as constantes modificações e adições de novas características ao
produto acarretam efeitos colaterais inesperados que não estão presentes na
documentação. Dessa forma, quando diante da manutenção do produto, o
desenvolvedor encontra uma documentação informal e incompleta, que não reflete o
software existente, tornando impossível o gerenciamento do processo de
9
manutenção, segundo Saleh e Boujarwah (1996). Neste contexto, a engenharia
reversa tem o objetivo de recuperar informações de projeto perdidas durante a fase
de programação, de documentar o estado real do software e poder auxiliar o
processo de gerenciamento da manutenção.
10
2 – ASPECTOS JURIDICOS
Na indústria de software, a engenharia reversa tem um papel controverso,
onde sempre serão discutidos seus limites entre legalidade e ilegalidade. Sua prática
contribui para a evolução tecnológica, ajuda a criação de conceitos mais eficazes em
relação a segurança, reduz o custo de desenvolvimento dos produtos através do
aproveitamento de ideias, assim economizando tempo. Porém, ao evitar “reinventar
a roda”, em certos casos esta economia é obtida infringindo leis que protegem a
propriedade intelectual.
Existem duas legalidades relacionadas à engenharia reversa: proteção de
copyright (protege a forma e aparência do software) e proteção de patente (protege
a ideia por trás da funcionalidade do software).
Uma patente é uma espécie de alerta para desencorajar competidores.
Quando há mérito em uma ideia, um competidor fará o seguinte: negociar uma
licença para utilizar esta ideia, fazer uma alteração sutil e declarar que a ideia
alterada não é protegida por uma patente ou declarar que a ideia não é
revolucionaria e que é uma evolução óbvia em um determinado campo.
Na investigação de quebra de copyright, instituições também utilizam
engenharia reversa para analisar se certas funcionalidades de um sistema foram
desenvolvidas através de uma cópia de código-fonte. Esta prática não é efetiva
quando o desenvolvimento da funcionalidade copiada e a engenharia reversa do
software original são realizados por equipes diferentes, onde a segunda faz a
análise e definições, assim disponibilizando para a primeira.
A cópia de funcionalidades também é sujeita a quebra de copyright, quando
empresas registram suas ideias em forma de patentes. Empresas como Apple e
Samsung frequentemente se encontram nos tribunais quando seus smartphones e
tablets são lançados com funcionalidades similares a de seus concorrentes.
Em 2015 a empresa Oracle publicou um texto, reclamando que alguns de
seus clientes estão contratando consultores de segurança para realizar engenharia
reversa em suas ferramentas na tentativa de achar falhas de segurança, assim
infringindo seus termos de licença.
11
Uma das empresas que a Oracle direcionou com a publicação respondeu que
os softwares que utilizam as ferramentas mencionadas são de extrema importância,
envolvidos em assuntos como saúde, segurança pessoal e bem-estar. Desencorajar
seus clientes de procurar vulnerabilidades em seus produtos é uma tentativa de
regredir os progressos feitos para melhorar a segurança do software em geral.
Praticar engenharia reversa em um programa que foi legitimamente comprado
é perfeitamente legal, pelo menos na União Europeia, contando que seja para uso
pessoal ou educacional.
Por mais que a engenharia reversa é uma prática legal contando que não haja
cópias explicitas de outro produto, este debate ético não tem previsão de terminar.
Um argumento para sua legalidade pode ser resumido à analogia dita por Jennifer
Granick, diretora dos departamentos de lei e tecnologia da Universidade de
Stanford, “Você possui um carro, mas não tem permissão de abrir o capô”.
12
3 –DEFINIÇÕES E CONCEITOS
3.1 Difusão
Atualmente há centenas de empresas espalhadas pelo mundo com vagas de
emprego que esperam de seus candidatos conhecimento avançado em linguagens
como Assembly e C++, ferramentas de Debugging de binários, exclusivamente para
a procura de vulnerabilidades de softwares. Entre essas empresas, estão:
Empresas de segurança privada sob contratação, que atendem outras
empresas desenvolvedoras de software de Finanças, Educação, Saúde,
Governo, etc., respeitando diversos regulamentos internacionais de
privacidade, com a intenção de proteger seus produtos de vazamento de
informações confidenciais.
Empresas desenvolvedoras de software antivírus, que realizam análises
em executáveis maliciosos, assim entendendo seu padrão de
funcionamento interno e adicionando tais regras, chamadas “heurísticas”,
nos algoritmos de detecção de ameaças.
3.2 Debugging
Debugging é a prática de testar e analisar aplicações com o intuito de se
achar erros ou comportamentos indesejados. A maioria das IDEs para
desenvolvimento atualmente possuem um recurso embutido para a pratica de
Debugging.
Seu funcionamento consiste em executar o código uma linha de cada vez,
assim permitindo o desenvolvedor analisar as alterações dos valores de variáveis ou
comportamento do algoritmo em geral, sendo necessário conhecimento do
funcionamento de memória, processador e endereços de variáveis.
Debuggers possuem um recurso chamado breakpoint, que é uma forma de
notificar a IDE que a execução necessita ser pausada no momento que sua posição
chegar à uma determinada linha ou instrução. Outros recursos são oferecidos, como
13
ferramentas de análise de consumo de memória e processamento da CPU. Mesmo
com tantos recursos e tecnologias das linguagens e IDEs atuais, grande parte do
tempo de um desenvolvedor é gasto em debugging.
Para a engenharia reversa de binários nativos, gerados na linguagem
Assembly, a utilização de um debugger é primordial. Comandos nesta linguagem,
otimizados para entendimento do processador, e sem possibilidade de se obter o
código fonte a partir de tal binário, tornam a análise dos mesmos uma tarefa quase
impossível sem um debugger. Com a sua capacidade de executar linha por linha,
analisando o valor dos registradores e endereços de memória, grande facilidade é
obtida para achar o lugar certo de realizar uma edição em seu código.
3.3 Domínios hierárquicos de proteção
Os domínios hierárquicos de proteção, comumente chamados de anéis de
proteção, são mecanismos para proteger dados e funcionalidades de falhas de
código e atividades maliciosas. Estes anéis são ordenados em uma hierarquia de
mais confiável (menor nível, geralmente de número zero) à menos confiável
(geralmente com o maior número). Na maioria dos sistemas operacionais, o anel 0 é
o ambiente com mais privilégios e interage diretamente com o hardware, tornando
possível a interação com o usuário.
O sistema operacional Windows possui somente dois anéis, 0 (kernel) e 3
(modo de usuário). Este sistema de segurança funciona de uma forma em que
softwares normais (que utilizam o anel 3, sendo separados por processos), como os
utilizados em comunicação virtual, não terão permissão de utilizar um periférico
como Webcam sem o consentimento do usuário.
No ambiente kernel (anel 0) são executadas aplicações independentes de
processos (chamadas drivers), onde qualquer falha de programação imediatamente
interrompe a execução do sistema operacional, tornando o desenvolvimento das
mesmas muito mais difícil. Este ambiente onde todo código nele contido é executado
com permissão máxima, também é utilizado por softwares antivírus para monitorar a
atividade dos códigos executados no anel 3, tráfego de rede, utilização de arquivos
no disco, etc. Outro exemplo de utilização deste ambiente são malwares avançados,
14
chamados Rootkits, que podem alterar funções primordiais do sistema operacional,
dificultando sua detecção por softwares antivírus.
Geralmente a prática de engenharia reversa em executáveis se limita ao anel
3 pela disponibilidade das ferramentas necessárias. Em drivers de modo kernel,
sendo binários nativos, seus comandos são muito complexos e seria necessário um
debugger para melhor entendimento. Tipicamente, para utilizar um debugger em um
driver em modo de desenvolvimento dois computadores são requeridos, assim sua
comunicação sendo efetuada via rede local. Sem a posse de código fonte, não há
conhecimento de ferramentas que realizem tais requerimentos.
15
4 – ESTUDO DE CASO: ENGENHARIA REVERSA NA PRÁTICA
Neste capítulo serão demonstrados exemplos simples de códigos, onde seus
executáveis serão analisados e alterados por ferramentas que facilitam a prática de
engenharia reversa. Os exemplos realizam uma operação aritmética e o objetivo é
alterar o resultado da mesma.
4.1. Ferramentas
4.1.1 OllyDbg
Figura 1 – Interface da ferramenta OllyDbg
16
A ferramenta OllyDbg é um debugger 32 bits de análise em nível Assembly
para o sistema operacional Windows. É conhecido pela sua praticidade em análise
de código binário tornando a tarefa fácil quando não há posse de código fonte. Sua
licença é Shareware, necessitando registro para uso comercial, caso contrário pode
ser utilizado gratuitamente.
É muito rico em recursos, como carregamento direto e Debugging de DLLs,
localização de rotinas em objetos e bibliotecas, suporte ao desenvolvimento de
plugins, suporte à aplicações com várias threads, breakpoints de hardware, etc.
No fim de 2001, quando as primeiras versões da ferramenta apareceram,
havia outro software chamado SoftICE, que estava em declínio, e a ferramenta IDA
(que hoje é a mais popular) ainda não era muito conhecida. Nesta época OllyDbg se
tornou a ferramenta de referência para a prática de engenharia reversa em todo o
planeta.
Atualmente a ferramenta sofre uma queda de popularidade, porque não há
suporte completo à executáveis com instruções 64 bits, sem receber uma versão
desde 2013.
4.1.2 ILSpy
A ferramenta ILSpy é um descompilador e navegador open-source de binários
gerados para a plataforma .NET. Seu desenvolvimento foi iniciado depois que a
empresa desenvolvedora da ferramenta Reflector anunciou que sua versão gratuita
deixaria de existir no final de fevereiro de 2011.
Seu funcionamento é baseado na famosa biblioteca para .NET Mono.Cecil,
desenvolvida desde 2004 e que tem como função analisar e modificar binários .NET
em nível arquivo, sem necessidade de carrega-los em memória.
Atualmente a ferramenta ILSpy é uma das mais populares entre as capazes
de realizar engenharia reversa em binários .NET, possuindo recursos importantes
como o suporte de plugins, escolha de linguagem para descompilação (C# ou
VB.NET), navegação prática via links, etc.
Seu desenvolvimento continua ativo, com vários recursos já planejados, como
suporte à edição de binários.
17
4.1.3 Reflexil
A ferramenta Reflexil é um editor de binários .NET open-source distribuído na
forma de plugin para as ferramentas Reflector, ILSpy e JustDecompile. Como a
ferramenta ILSpy, Reflexil também é baseado na biblioteca Mono.Cecil.
Como atualmente as ferramentas que se destina somente suportam a
descompilação de binários .NET, Reflexil é um complemento importante,
adicionando a funcionalidade de edição e injeção de código aos binários sendo
analisados.
Possui recursos que suportam a alteração quase completa dos elementos de
um binário .NET, como suas referências, instruções, assinaturas digitais, etc.
4.2. Exemplo 1: Linguagem Delphi
A linguagem Delphi, anteriormente chamada de Object Pascal, é uma
linguagem muito utilizada no desenvolvimento de sistemas empresariais, devido a
sua facilidade e velocidade.
Figura 2 - Código fonte em linguagem Delphi
18
O exemplo exibirá um texto simulando a operação aritmética, utilizando os
valores das variáveis n1, n2 e n3.
Figura 3 - Tela console com o resultado do exemplo em Delphi
Seu binário é gerado no formato nativo do sistema operacional Windows,
onde todas as suas instruções são convertidas para a linguagem Assembly, que
opera diretamente com os registradores do processador, assim otimizando sua
velocidade de execução.
O executável compilado a partir do código fonte exibido na Figura 2, aberto
para análise na ferramenta OllyDbg, pode ser visto na Figura 4.
19
Figura 4 - Código em linguagem Assembly equivalente ao Código Fonte em
Delphi
Em uma linguagem otimizada para entendimento do processador, com
operações de baixo nível e com registradores limitados, a prática de engenharia
reversa se torna bastante demorada e crítica. É necessário conhecimento dos
comandos e parâmetros chamados mnemônicos, números hexadecimais, etc.
No processo de compilação, todos os nomes de funções e variáveis são
perdidos, e uma linha em código Delphi é convertida em várias linhas na linguagem
Assembly, onde qualquer alteração incorreta completamente invalida a execução do
bloco de código.
Comumente compiladores possuem um parâmetro para a ativação da
geração de um arquivo chamado map, que possui uma lista com todas as funções e
seus respectivos endereços de memória, também chamados de offsets, para ajudar
ferramentas de análise a localizar pontos de execução.
20
Normalmente um executável possui milhares de linhas de código Assembly, e
sua análise sem um arquivo map seria quase impossível sem algumas técnicas
como as seguintes:
- Busca de texto: Os blocos de texto originais, chamados Strings, não
são perdidos no processo de compilação, assim facilitando a localização dos
códigos que utilizam os mesmos. No exemplo exibido na Figura 4, os textos “ + “ e “
= “, que são usados para exibir o resultado da operação aritmética para o usuário,
ajudam a localizar o bloco de código Assembly correto, equivalente ao código fonte
Delphi exibido na Figura 2.
- Chamadas de APIs: Por mais que os nomes de funções são perdidos
no processo de compilação, chamadas às APIs do Windows são completamente
mantidas. APIs como a MessageBoxA, que está declarada na DLL user32.dll, são
comumente usadas para localizar blocos Assembly onde exibem uma caixa de
diálogo ao usuário.
Brechas de segurança podem ser ilusórias e difíceis de se definir. O fato é que mesmo com posse do código-fonte as vezes pode ser difícil para se distinguir código seguro de perigosas vulnerabilidades de segurança. Ainda, quando você sabe que tipo de problemas está procurando e com consciência de certas áreas de alto risco, é definitivamente possível de estimar se uma função é segura ou não praticando engenharia reversa na mesma. Só é necessário o entendimento do sistema e o que torna um código seguro ou inseguro. (EILAM, 2005, p. 271).
Para realizar a alteração da operação de adição do exemplo para uma
subtração, é necessário fazer uma análise das linhas de Assembly equivalentes à
operação em Delphi:
● Linha 0040828C: Preenche o espaço de memória de endereço 40A798
(equivalente a variável n1) com o valor 0A (equivalente a 10, depois da conversão
hexadecimal -> decimal).
● Linha 00408296: Preenche o espaço de memória de endereço 40A79C
(equivalente a variável n2) com o valor 2.
● Linha 004082A0: Preenche o registrador EAX com o valor 10, que está
armazenado no endereço de memória 40A798 (equivalente a variável n1).
● Linha 004082A5: Adiciona no registrador EAX (que atualmente possui
o valor 10) o valor 2, que está armazenado no endereço de memória 40A79C
(equivalente a variável n2). Nesta linha ocorre a operação aritmética que será
alterada.
21
Figura 5 - Código em linguagem Assembly com alteração da operação
aritmética em debugger
Alterando o comando ADD pelo comando SUB, a operação de adição entre o
registrador EAX e o endereço de memória 40A79C se torna uma operação de
subtração.
Como a alteração foi realizada somente no momento de produzir o valor da
variável n3, que é equivalente ao resultado da operação no texto do console, o
mesmo continuará sendo exibido como se a operação fosse uma adição, como é
mostrado na figura 6:
22
Figura 6 - Tela console com o resultado do exemplo em Delphi depois de ser
alterado no debugger OllyDbg
Em aplicações reais de engenharia reversa, dependendo de sua
complexidade para chegar no resultado final, são necessárias alterações simples
como a utilizada neste exemplo, alterações que envolvem várias linhas de código
Assembly, ou até injeções de código, onde o ponto de execução atual é
redirecionado para outro endereço de memória, localizado no próprio executável ou
em bibliotecas DLL carregadas dinamicamente, para depois voltar ao seu ponto de
execução original.
4.3 Exemplo 2: Linguagem C#
A linguagem C# também é uma linguagem compilada, porém seu executável
não é gerado com instruções Assembly, e sim com instruções IL.
A linguagem CIL (Common Intermediate Language, também conhecida como
Microsoft Intermediate Language - MSIL) é o resultado da compilação de todas as
linguagens que fazem parte do .NET Framework. Este nível de abstração é utilizado
para executar um mesmo binário em várias plataformas ou arquiteturas diferentes,
pois quem interpreta o código CIL é a máquina virtual do .NET Framework, e não o
sistema operacional em si. Este conceito também é utilizado na linguagem Java,
onde sua máquina virtual JVM pode ser instalada em diversos tipos de plataformas e
assim permitindo a execução de um mesmo binário.
23
Figura 7 - Código Fonte em linguagem C#
Para comparação, o código C# do exemplo é equivalente ao exemplo na
linguagem Delphi, porém com a operação aritmética padrão sendo a subtração ao
invés de adição.
Figura 8 - Tela console com o resultado do exemplo em C# original
Assim como Assembly, a linguagem CIL também é uma linguagem de
programação, ou seja, é possível escrever algoritmos diretamente utilizando-as. É
muito importante possuir o conhecimento de ambas sintaxes para além de realizar
alterações em códigos existentes via engenharia reversa, poder criar algoritmos de
qualquer tamanho sem posse do código fonte.
Em linguagens de compilação nativa como Delphi, o binário já é gerado para
entendimento do processador pois no momento de sua geração é necessário
informar ao compilador a plataforma desejada. Ou seja, em compilação para estas
linguagens, é formado um binário diferente para cada plataforma ou arquitetura,
24
como x86, x64, ARM, Windows, Linux, etc., assim delegando a tarefa de fornecer
compatibilidade para o sistema operacional.
Por exemplo, em Windows, a maioria de binários gerados para a arquitetura
x86 podem ser executados em instalações x64, porém em um sistema operacional
x86 não é possível executar um binário compilado com instruções x64.
Já em linguagens que dependem de uma máquina virtual, seus binários são
compilados com instruções “universais” entendíveis por humanos que são
convertidas para linguagem de máquina em sua primeira execução. Esta conversão
é realizada somente neste momento pois a máquina virtual utilizará a arquitetura
atual de execução para gerar o código de máquina otimizado para posteriores
execuções.
Esta falta de otimização, que mantém informações de nomes de objetos,
funções, namespaces, etc., são completamente mantidos. Por mais que a
velocidade de execução é reduzida, há benefícios como a possibilidade de
desenvolver soluções utilizando várias linguagens de programação (como VB.NET e
C#, no caso do .NET Framework), recursos de linguagem para criação de código em
tempo de execução, ferramentas de produtividade embutidas na IDE como
ReSharper para Visual Studio, etc.
Desta forma, para a engenharia reversa estes binários não otimizados podem
ser analisados e revertidos quase que identicamente à sua sintaxe original. Em
binários CIL, é possível até escolher qual linguagem será utilizada no processo de
reversão.
Atualmente existem dezenas de ferramentas que conseguem “descompilar”
binários CIL, fornecendo recursos de navegação de namespaces, classes, métodos
e suas chamadas, entre outros. Neste exemplo será utilizada a ferramenta ILSpy,
pois além de realizar reversão, é permitido o desenvolvimento de plugins. O plugin
Reflexil, que suporta ser embutido em várias ferramentas, incluindo o ILSpy, permite
a alteração de instruções, posteriormente salvando um binário novo.
25
Figura 9 - Exemplo em C# descompilado na ferramenta ILSpy
Como exibido na figura 9, binários CIL contém toda a informação necessária
para uma análise profunda de seu comportamento, diferente de binários nativos. É
possível ver suas dependências — na forma de referências —, classes e métodos,
todos de uma forma estruturada.
Na parte de código, por mais que os nomes de variáveis não foram
identicamente mantidos, e recursos de facilidade foram abstraídos (concatenação
direta do texto foi substituída por uma chamada de função Concat do objeto String,
para melhor entendimento da máquina virtual), a lógica do programa se mantém
completamente entendível por humanos depois da ferramenta ILSpy analisar o
código CIL armazenado no binário e realizar a reversão.
Para a edição da operação aritmética, será utilizado o plugin Reflexil,
embutido na ferramenta ILSpy. Em modo de edição, o código é exibido na
linguagem CIL, da mesma forma que está armazenado no binário como exibido na
figura 10.
26
Figura 10 - Código do Exemplo em C# descompilado na linguagem CIL pelo
plugin Reflexil
27
A figura 10 exibe exatamente como o código CIL é armazenado no binário,
antes de sua reversão e formatação na ferramenta ILSpy. Seu modelo de instruções
é bem similar à linguagem Assembly, utilizando mnemônicos de forma linear para
construir os algoritmos, porém com uma quantidade de informações bem maior.
Segue a análise de seus comandos, equivalente ao código em C#:
Linha 01: Adiciona o valor 10 no topo da pilha.
Linha 02: Preenche a variável de índice 0 da função atual com o valor
armazenado no topo da pilha (equivalente a 10), removendo-o da mesma.
Linha 03: Adiciona o valor 2 no topo da pilha.
Linha 04: Preenche a variável de índice 1 da função atual com o valor
armazenado no topo da pilha (equivalente a 2), removendo-o da mesma.
Linha 05: Adiciona o valor da variável de índice 0 da função atual
(equivalente a variável n1, possuindo o valor 10) no topo da pilha.
Linha 06: Adiciona o valor da variável de índice 1 da função atual
(equivalente a variável n2, possuindo o valor 2) no topo da pilha.
Linha 07: Subtrai os dois valores que estão no topo da pilha em ordem
inversa (10 e 2 após a inversão), adicionando o resultado no topo da pilha.
Linha 08: Preenche a variável de índice 2 da função atual com o valor
armazenado no topo da pilha (equivalente a 8), removendo-o da mesma.
Como no exemplo em Delphi, de forma inversa, alteraremos o comando SUB
armazenado na linha 07 para ADD, alterando a forma que o algoritmo produz o valor
da variável n3, assim enganando o programa a exibir um texto de uma operação
aritmética com um valor errado.
Figura 11 - Tela console com o resultado do exemplo em C# depois de ser
alterado na ferramenta ILSpy
28
4.3: Complementação
Por mais que a prática de engenharia reversa é muito mais fácil e flexível
quando praticada em binários utilizados por máquinas virtuais, como exibido no
exemplo 2, sua prática em binários nativos é muito mais popular, pois a maioria de
produtos direcionados ao usuário final utilizando o modelo Desktop focados pela
pirataria são desenvolvidos em linguagens como C, C++ ou Delphi, tendo
performance como principal motivo.
Produtos focados pela pirataria através da engenharia reversa
frequentemente alteram seus métodos de proteção, como a troca de algoritmos de
criptografia, análise de códigos serial, compactação, etc., porém eventualmente
estes métodos se tornam obsoletos, assim tornando este problema um ciclo que
sempre fará parte da manutenção do produto.
29
5 – SEGURANÇA
A prática de engenharia reversa em executáveis é o principal método que
crackers utilizam para disponibilizar aplicativos pagos de forma gratuita ou extrair
informações confidenciais.
Por mais que não seja possível prevenir tal prática, foram desenvolvidos
vários métodos para aumentar o tempo necessário para sua conclusão, assim
necessitando maior conhecimento do invasor.
5.1: Ofuscação
Esta técnica, bastante utilizada em binários que dependem de máquinas
virtuais como .NET, Java e até páginas web como JavaScript, consiste em renomear
elementos de sua estrutura, como métodos, propriedades, eventos, variáveis, etc.,
tornando a análise da lógica de seus algoritmos um processo bem mais demorado.
Sua prática em códigos JavaScript é amplamente utilizada, pois como a
linguagem é executada pelo navegador, qualquer usuário tem acesso completo ao
código fonte. Desta forma, sua análise se torna mais complicada com a remoção de
comentários, espaços utilizados na indentação e na troca de nomes de variáveis e
funções por combinações de uma ou duas letras, assim diminuindo a largura de
banda necessária para o usuário realizar o download dos arquivos. Também é
comum utilizar esta técnica em linguagens de marcação como HTML e CSS, porém
o ganho é obtido somente com performance de largura de banda.
Exemplo:
30
Figura 12 – Código JavaScript sem ofuscação
Figura 13 – Código JavaScript depois de sua ofuscação
5.2: Compressão
Esta técnica é utilizada quase que exclusivamente em executáveis nativos,
que possuem Assembly como sua linguagem final. Se tornou popular quando
computadores tinham espaço em disco limitado, compatíveis com a capacidade de
disquetes, onde eram lentos e de baixa capacidade. Assim permitindo o
armazenamento de uma maior quantidade de softwares em uma determinada
quantidade de espaço, sem a inconveniência de precisar extrair os mesmos toda vez
que o usuário precisar executá-los.
Esta compressão é realizada de uma forma onde o executável original é
compactado e armazenado em um executável novo — chamado Stub — que possui
um algoritmo de descompactação.
Quando este Stub é executado, ele descompacta o binário original em
memória e o executa de uma forma transparente, onde o usuário não percebe tal
operação.
Há perda de performance ao utilizar esta técnica, pois para realizar a
descompactação é necessário que o computador possua mais memória, porque nela
31
será armazenado o algoritmo de descompactação, os dados compactados e o
código descompactado, além de processamento adicional para gerar o mesmo.
Suas vantagens incluem menor espaço em disco para armazenamento de
binários, menos tempo requerido para upload ou download via internet, invalida a
prática de engenharia reversa em binários diretamente, assim sendo só possível
através de um debugger em tempo de execução, onde a descompactação já foi
executada e o código original já está disponível na memória.
Algumas ferramentas de compressão que tem seu principal foco dificultar a
prática de engenharia reversa, possuem recursos de descompactação onde o local
em memória que o código original será armazenado é aleatoriamente gerado a cada
execução, assim não sendo possível o desenvolvimento de patchers para a
alteração de instruções na memória em tempo de execução.
É comum sua utilização em softwares maliciosos, para confundir softwares
antivírus e passar despercebidos em suas análises.
Programas maliciosos podem ser traiçoeiros e complicados. Eles farão o necessário para se manter invisíveis e parecerem inocentes. Educar usuários finais em como estes programas funcionam e no que prestar atenção é uma tarefa importante, porém não suficiente. Desenvolvedores de aplicações e sistemas operacionais precisam melhorar constantemente a forma que seus produtos tratam código inseguro e convincentemente transmitir para seus usuários o fato de que eles simplesmente não deveriam deixar um programa desconhecido ser executado em seu sistema a não ser que exista uma razão excelente para isso. (EILAM, 2005, p. 306).
Alguns softwares antivírus simplesmente detectam qualquer executável
gerado com certas ferramentas de compressão como vírus porque o algoritmo de
descompactação armazenado no Stub compartilham algumas características que
ferramentas maliciosas possuem.
32
6 – CONSIDERAÇÕES FINAIS
A engenharia reversa está fundamentalmente direcionada à descoberta e
aprendizado. Desenvolvedores não se profissionalizam somente lendo artigos, indo
à conferências ou mantendo produtos de suas empresas, mas também analisando
produtos dos outros. Aprendendo com o que já foi feito anteriormente por outros
frequentemente é convertido à novos produtos e avanços de conhecimento.
Segundo EILAM (2005), qualquer barreira baseada em software pode ser
quebrada simplesmente modificando-o. Atualmente é impossível prevenir a prática
da técnica de engenharia reversa em executáveis quando há posse dos mesmos,
independentemente de linguagem. Existem várias técnicas que dificultam sua
prática, como ofuscação, compressão, detecção de debuggers, vínculos com chaves
de hardware, etc.
O dilema de investir em proteger de engenharia reversa os binários de uma
aplicação é que quanto mais se gasta nisso, menos do custo do projeto está sendo
investido em adicionar reais funcionalidades para o usuário final, porém uma
aplicação sem prevenção nenhuma torna mais fácil para empresas concorrentes
obterem informações sigilosas da mesma.
Não é coincidência que recentemente a maioria das propostas para restringir
a engenharia reversa venha do contexto de produtos baseados em informática,
como software ou semicondutores. Estes tipos de produtos carregam consigo uma
grande quantidade de propriedade intelectual, que custaram muitas pesquisas,
tempo e dinheiro, assim tornando-os mais vulneráveis que outros tipos de produtos
manufaturados. Isto é especialmente verdade quando a informação está em forma
digital. Copiar e distribuir software é essencialmente instantâneo e sem nenhum
custo em um ambiente como a internet, utilizando ferramentas como o protocolo
torrent.
Todos os dias a engenharia reversa causa milhares de dólares em prejuízos
às desenvolvedoras de software, pois ela torna possível a criação de cracks e
keygens, assim tornando seus produtos pagos vítimas da pirataria. Exemplos:
Windows, Office, Photoshop, AutoCAD. Porém sem essa técnica não existiriam
softwares antivírus, tornando os prejuízos imensuráveis para os usuários e
33
empresas do mundo inteiro com a proliferação sem controle de softwares
maliciosos.
É possível proteger certas regras e lógicas da prática de engenharia reversa
disponibilizando seu uso pelo executável através de um serviço hospedado em um
ambiente web controlado. Esta técnica está limitada à aplicações com acesso à
internet obrigatório ou aplicações empresariais multiusuário conectadas à um
servidor.
A forma mais segura de se desenvolver uma aplicação sem se preocupar com
engenharia reversa é possuindo uma interface Web, pois assim os usuários não
terão posse dos binários, se limitando somente a ataques como SQL Injection e
XSS.
34
REFERÊNCIAS
EILAM, Eldad. Reversing: Secrets of Reverse Engineering, Indianapolis: Wiley
Publishing, 2005.
SECUNIA – Jobs. Disponível em: <http://secunia.com/company/jobs/reverse-
engineer/>. Acesso em: 24 set. 2015.
Núcleo (Software). Disponível em: <https://pt.wikipedia.org/wiki/Núcleo_(software).>.
Acesso em: 24 set. 2015.
SALEH, K.; BOUJARWAH, A. Communications Software Reverse Engineering: A
Semi-Automatic approach. Information and Software Technology, Oxford, n.38,
p.370-395, 1996.
Engenharia Reversa. Disponível em:
<http://www.ebah.com.br/content/ABAAAf2CEAJ/50-apostilas-hacker-engenharia-
reversa>. Acesso em: 24 set. 2015.
Aplicativo OllyDbg. Disponível em: <http://www.ollydbg.de/>. Acesso em: 24 set.
2015.
Sistema Operativo. Disponível em: <https://pt.wikipedia.org/wiki/Sistema_operativo>.
Acesso em: 24 set. 2015.
Aplicativo ILSpy. Disponível em: <http://ilspy.net/>. Acesso em: 24 set. 2015.
Plugin Reflexil para o aplicativo ILSpy. Disponível em: <http://reflexil.net>. Acesso
em: 24 set. 2015.