1 ESCOLA SUPERIOR ABERTA DO BRASIL – ESAB CURSO LATO SENSU EM ENGENHARIA DE SISTEMAS RONILDO CESAR DE MOURA O USO DA ENGENHARIA REVERSA NO DESENVOLVIMENTO DE SOFTWARE SEGURO VITÓRIA 2008 2 RONILDO CESAR DE MOURA O USO DA ENGENHARIA REVERSA NO DESENVOLVIMENTO DE SOFTWARE SEGURO Monografia apresentada à ESAB – Escola Superior Aberta do Brasil, sob orientação do (a) Prof. (a) Beatriz Christo Gobbi. VITÓRIA 2008 3 RONILDO CESAR DE MOURA O USO DA ENGENHARIA REVERSA NO DESENVOLVIMENTO DE SOFTWARE SEGURO Aprovada em ____ de __________ de 2008. _____________________________________ _____________________________________ _____________________________________ VITÓRIA 2008 4 Este trabalho é dedicado aos meus pais e à minha esposa, pois só eles conhecem todo o esforço que fizemos para chegar até aqui. 5 RESUMO O estudo teve por objetivo apresentar meios que contribuam no desenvolvimento de software seguro, por meio de utilização de engenharia reversa de software. A engenharia reversa de software tem o propósito de recuperar as informações de projeto perdidas durante a fase de desenvolvimento, de documentar o real estado do software e poder auxiliar o processo de gerenciamento da manutenção. Dessa forma, a engenharia reversa pode ser aplicada em qualquer etapa do ciclo de vida, seja para recuperar níveis de abstração ou para fornecer uma nova visão em um grau de abstração mais alto. O objetivo da engenharia reversa é a produção de informações que possam aumentar o conhecimento geral de sistemas de software. Essas informações podem ser utilizadas em atividades como manutenção, reuso, teste e controle de qualidade de software. Existe um debate legal ao redor de engenharia reversa que acontece há vários anos. Isto normalmente revolve ao redor da pergunta de qual o impacto social e econômico a engenharia reversa está causando na sociedade como um todo. Todavia, para calcular este tipo de impacto, em grande parte, depende de como a engenharia reversa é usada. Em alguns países a prática de engenharia reversa é considerada ilegal por ter praticantes interessados em obter acesso não autorizado a recursos de software não oferecidos gratuitamente pelos fabricantes que vendem o produto. Por outro lado, demonstra a utilização da própria engenharia reversa como ferramenta de proteção contra a sua prática ilegal. A partir de técnicas aplicadas nesta área é possível modelar melhores proteções para o software como anti-descompiladores, códigos mutantes e armadilhas. 6 LISTA DE FIGURAS Figura 1 – Comparação dos níveis de abstração e das visões ................................. 14 Figura 2 – Relação entre a engenharia progressiva e a engenharia reversa ............ 15 Figura 3 – Software IDA Pro...................................................................................... 53 Figura 4 – Sofware VB Decompiler ........................................................................... 54 Figura 5 – Software Understand for Delphi ............................................................... 56 Figura 6 – Visão geral do método FUSION-RE/I ....................................................... 57 Figura 7 – Síntese do método FUSION-RE/I............................................................. 58 7 SUMÁRIO 1 INTRODUÇÃO ...................................................................................................... 9 1.1 CONTEXTO ................................................................................................... 9 1.2 PROBLEMA ................................................................................................. 10 1.3 JUSTIFICATIVA ........................................................................................... 10 1.4 OBJETIVOS ................................................................................................. 10 1.5 METODOLOGIA .......................................................................................... 11 2 CONCEITOS E DEFINIÇÕES ............................................................................ 12 3 ENGENHARIA REVERSA DE SISTEMA ........................................................... 17 4 5 3.1 SOFTWARE DE BAIXO NÍVEL .................................................................... 17 3.2 LINGUAGEM ASSEMBLY ........................................................................... 18 3.3 COMPILADORES ........................................................................................ 19 3.4 MÁQUINAS VIRTUAIS E BYTECODES ...................................................... 20 3.5 SISTEMAS OPERACIONAIS ....................................................................... 21 3.6 DISASSEMBLERS ....................................................................................... 21 3.7 DEPURADORES.......................................................................................... 21 3.8 DECOMPILADOR ........................................................................................ 22 ASPECTOS LEGAIS .......................................................................................... 23 4.1 COMPATIBILIDADE..................................................................................... 23 4.2 SÃO PONTOS FUNDAMENTAIS DA LEI DE SOFTWARE: ........................ 26 4.3 FALHAS DE SEGURANÇA .......................................................................... 27 4.3.1 Um pouco de história ............................................................................. 27 4.3.2 Alguns conceitos importantes ................................................................ 28 4.3.3 Chaves públicas e privadas ................................................................... 31 4.3.4 Exemplos de falhas ............................................................................... 32 TÉCNICAS DE ENGENHARIA REVERSA ......................................................... 37 8 5.1 TÉCNICAS DE ENGENHARIA REVERSA SEM O CÓDIGO-FONTE ......... 37 5.2 TÉCNICAS DE ENGENHARIA REVERSA COM O CÓDIGO-FONTE ........ 38 5.2.1 Extração das Informações ..................................................................... 38 5.2.2 Tratamento das informações ................................................................. 40 5.3 5.3.1 Análise de propagação .......................................................................... 43 5.3.2 Objetização ............................................................................................ 43 5.3.3 Reengenharia de interfaces ................................................................... 44 5.4 6 TÉCNICAS COMPLEXAS DE ENGENHARIA REVERSA ........................... 43 ENGENHARIA REVERSA PARA SEGURANÇA ......................................... 44 5.4.1 Software malicioso................................................................................. 44 5.4.2 Métodos de proteção ............................................................................. 45 5.4.3 Técnicas Anti-reversão .......................................................................... 49 FERRAMENTAS DE ENGENHARIA REVERSA ................................................ 53 6.1 IDA Pro......................................................................................................... 53 6.2 VB Decompiler ............................................................................................. 54 6.3 Understand for Delphi .................................................................................. 56 6.4 MÉTODO FUSION-RE/I ............................................................................... 57 7 CONCLUSÃO ..................................................................................................... 59 8 REFERENCIAS BIBLIOGRAFICAS ................................................................... 61 9 1 INTRODUÇÃO Engenharia Reversa, Engenharia de Software, Segurança. 1.1 CONTEXTO O estudo procura demonstrar como o desenvolvimento de software pode se beneficiar com a utilização da engenharia reversa. O processo de desenvolvimento possui diversos requisitos, entre eles a segurança. Todavia, nem sempre os programadores, analistas e gestores dão a devida atenção que o tema requer. Com a disseminação da internet, de novas tecnologias e de ferramentas cada vez mais poderosas, a informação tornou-se artigo valioso. Contudo, é preciso torná-la protegida da ação de pessoas não autorizadas. Para tal, a elaboração de programas de computador com um nível maior de proteção requer a utilização de ferramentas específicas. Nesse contexto, surgem técnicas que podem reduzir esse risco, entre elas o emprego da engenharia reversa como meio de proteger o código dos sistemas. A necessidade do uso da engenharia reversa tem vários motivos, dentre eles: os sistemas terem sido desenvolvidos há muito tempo; possuírem pouca ou nenhuma documentação atualizada; os desenvolvedores do produto já não trabalharem mais na empresa; o sistema ter sido criado com métodos diferentes, nem sempre padronizados. Outras necessidades devem-se a adaptação às novas tecnologias de hardware e software (novas linguagens e métodos); adaptação às novas economias (moeda, legislação, impostos); adaptação de novas funcionalidades, acessibilidade, correção de erros, etc. 10 1.2 PROBLEMA Devido à quantidade de vulnerabilidades que os sistemas atuais têm enfrentado, aumentar a segurança no desenvolvimento de software tornou- se tão necessário quanto o uso dos computadores nas empresas e nas residências. Uma das soluções é tornar os programas imunes aos vários tipos de ataques que podem sofrer, para isso ressurge a engenharia reversa como alternativa na elaboração de códigos mais seguros. 1.3 JUSTIFICATIVA Um dos principais motivos do estudo é descrever a engenharia reversa como algo imprescindível na engenharia de software. Rever os conceitos que colaboram para o uso perverso de suas técnicas na pirataria e a violação dos direitos autorais. A partir desse conhecimento, propor alternativas que impeçam ou diminuam o acesso a dados confidenciais, a reprodução de cópias ilegais e a invasão de sistemas. 1.4 OBJETIVOS Para tal, este trabalho procura identificar alguns métodos que podem colaborar na redução dos riscos. Apresentar a engenharia reversa como ferramenta de segurança no desenvolvimento de software confiável. Identificar seus conceitos, assim como seus aspectos legais; Examinar as falhas de segurança de software e suas conseqüências; Levantar as técnicas de engenharia reversa e apresentar métodos de proteção. 11 1.5 METODOLOGIA Para alcançar o objetivo proposto, a metodologia adotada procura identificar na literatura as falhas dos sistemas, apresentando os conceitos de engenharia reversa, os métodos empregados na proteção de código dos programas, as técnicas e os procedimentos empregados na engenharia de software que visam diminuir as vulnerabilidades e aprimorar a segurança de software. Utiliza-se de livros, revistas, manuais, dissertações e textos de internet sobre o assunto e suas co-relações, além de citar alguns dos softwares e ferramentas disponíveis no mercado. 12 2 CONCEITOS E DEFINIÇÕES Segundo Saleh e Boujarwah (1996), o mercado de software vem crescendo a cada dia e com ele o uso de técnicas de desenvolvimento, muitas vezes informais. Como resultado, a manutenção de tais softwares torna-se problemática, uma vez que a documentação associada ao software, na maioria das vezes, não está de acordo com o código implementado. Além disso, as constantes modificações e adições de novas características ao software acarretam efeitos colaterais inesperados que não estão presentes na documentação. Dessa forma, quando diante da manutenção do produto, o engenheiro de software encontra uma documentação informal e incompleta, que não reflete o software existente, tornando impossível o gerenciamento do processo de manutenção segundo Saleh e Boujarwah (1996). Neste contexto, a engenharia reversa de software, tem o propósito de recuperar as informações de projeto perdidas durante a fase de desenvolvimento, de documentar o real estado do software e poder auxiliar o processo de gerenciamento da manutenção. Rugaber (1992) afirma que a maior parte do esforço de desenvolvimento de software é gasto na manutenção de sistemas existentes e não no desenvolvimento de sistemas novos, e que grande parte do processo de manutenção é dirigido ao entendimento do sistema em manutenção. Sendo assim, se queremos melhorar o desenvolvimento de software, é necessário facilitar o processo de compreensão de sistemas existentes. A engenharia reversa ataca diretamente o problema de compreender o software. Segundo Chikofsky e Cross II (1990) o termo engenharia reversa teve sua origem na análise de hardware, onde a prática de decifrar o projeto de produtos prontos é comum. O mesmo conceito pode ser aplicado a software, com uma diferença: enquanto a engenharia reversa de hardware tem por objetivo reproduzir o sistema, a de software se destina a criar visões do sistema em diferentes níveis de abstração, facilitando seu entendimento com o principal objetivo de ajudar na manutenção do sistema. 13 Uma definição de abstração é dada como a habilidade de se ignorar os aspectos de assuntos não relevantes para o propósito em questão, tornando possível uma concentração maior nos assuntos principais (Oxford, 1986). Segundo Feltrim, V. D. (1999), o ciclo de vida do software expressa bem a diferença entre níveis de abstração. Vale ainda ressaltar a existência de dois conceitos relativos a abstração: nível e grau. Diferentes níveis de abstração ocorrem entre diferentes etapas do desenvolvimento (as informações na etapa de projeto são outras além das informações da etapa de análise), enquanto diferenças no grau de abstração ocorrem dentro de uma mesma etapa (pode-se representar as informações de uma mesma etapa do desenvolvimento de forma geral ou de forma mais detalhada). Segundo Harandi e Ning (HARANDI e NING, 1990), um sistema pode ser visualizado a partir de diferentes níveis de entendimento. Baseado nos níveis de abstração, as visões são classificadas em quatro categorias: • Visão em nível implementacional: abstrai características da linguagem de programação e, especificamente, da implementação. Exemplos de visões em nível implementacional são informações a respeito da sintaxe e da semântica da linguagem, e informações de implementação; • Visão em nível estrutural: abstrai detalhes da implementação para revelar sua estrutura a partir de diferentes perspectivas. O resultado é uma representação explicita das dependências entre os componentes do sistema; • Visão em nível funcional: abstrai a função de um componente do sistema de modo a identificar o que o componente faz. Relaciona partes do programa à suas funções a fim de revelar as relações lógicas entre elas; • Visão em nível de domínio: abstrai o contexto em que o sistema está operando, ou seja, relato o porquê do sistema ter sido desenvolvido. Dessa forma, a engenharia reversa pode ser aplicada em qualquer etapa do ciclo de vida, seja para recuperar níveis de abstração ou para fornecer uma nova visão em 14 um grau de abstração mais alto (é consenso de que quanto mais alto o grau de abstração, menos detalhes são fornecidos.) A engenharia reversa segue o sentido oposto ao da engenharia progressiva. A engenharia progressiva segue a seqüência que vai desde os requisitos, passa pelo projeto até a implementação. Na engenharia progressiva o sistema é o resultado do processo de desenvolvimento. Na engenharia reversa, o sistema geralmente é o ponto inicial do processo, conforme Chikofsky e Cross II (1990). A figura 1 representa esquematicamente, uma forma comparativa das direções inversas por quais se orientam as etapas envolvidas em cada uma das engenharias. O processo de engenharia reversa caracteriza-se pelas atividades retroativas do ciclo de vida, que partem de um baixo nível de abstração para um alto nível de abstração. Fig. 1 – Comparação dos níveis de abstração e das visões. Existem várias definições de engenharia reversa. A definição de Pressman (1995, p.546) diz que é o “processo de análise num esforço de criar uma representação do programa em um nível de abstração mais alto que o código fonte”. Assim sendo, pode-se dizer que o objetivo da engenharia reversa é a produção de informações que possam aumentar o conhecimento geral de sistemas de software. Essas informações podem ser utilizadas em atividades como manutenção, reuso, teste e controle de qualidade de software. 15 A figura 2 ilustra a relação entre a engenharia progressiva e a engenharia reversa de acordo com as fases do ciclo de vida do software, sendo que essas fases podem ser agrupadas em três atividades fundamentais: • Sistema (engenharia de sistemas): envolve o contexto em que o sistema está operando, ou seja, o porquê do sistema ser desenvolvido; • Requisitos (análise): são estabelecidos os serviços a serem fornecidos pelo sistema e as restrições sob as quais ele deve operar, ou seja, o que o sistema deve fazer e sob quais circunstâncias; • Desenvolvimento (projeto, codificação e testes): cria-se um planejamento da solução, ou seja, como o sistema cumprirá o que foi estabelecido na atividade de requisitos e a implementação dessa solução, incluindo a codificação, os testes, a depuração e a entrega do sistema. A fase de manutenção é vista como reiteração das atividades prévias. Fig. 2 – Relação entre a engenharia progressiva e a engenharia reversa. Em Costa (1997) encontramos a definição de engenharia reversa como “o processo de exame e compreensão do sistema existente, para recapturar ou recriar os requisitos atualmente implementados pelo sistema, apresentando-os em um grau ou 16 nível mais alto de abstração. Não envolve mudanças no sistema ou criação de um novo sistema”. Segundo Chikofsky e Cross II (1990), engenharia reversa de software é um processo de investigação, não um processo de mudança ou reprodução. Rugaber (1992) identifica o propósito da engenharia reversa como sendo entender um sistema de software com o objetivo de facilitar atividades como: expansão, correção, documentação, re-projeto ou reprogramação em outra linguagem de programação. Chikofsky e Cross II (1990) também identificam duas importantes categorias da engenharia reversa: a redocumentação (visualização de código) e a recuperação de projeto (entendimento de programa). A categoria redocumentação compreende a criação ou revisão de uma representação semanticamente equivalente dentro de um mesmo nível de abstração. Esse processo visa criar novas visões do sistema através da análise do código fonte, com o objetivo de melhorar a compreensão do sistema. A criação dessas visões adicionais do código, geralmente gráficas, tem como objetivo recriar a documentação que já existiu ou que deveria ter existido sobre o sistema, conforme Costa (1997). No entanto, as informações recuperadas com esse tipo de análise fornecem apenas visões estruturais, de forma que informações como a função e os propósitos do sistema exigem um maior esforço de entendimento. A engenharia reversa categorizada como recuperação de projeto é um subconjunto da engenharia reversa no qual o domínio de conhecimento, informação externa e dedução são adicionados às observações sobre o sistema para identificar abstrações de mais alto nível que sejam significativas, além daquelas obtidas diretamente pelo exame do sistema em si, segundo Biggerstaff (1989). Esse tipo de processo de entendimento visa recuperar todas as informações necessárias para se compreender melhor o que o sistema faz, como faz e porque o faz. Recuperação de projeto é a forma mais critica de engenharia reversa porque tenta recuperar não só a funcionalidade do sistema, mas o processo no qual ele foi desenvolvido, conforme Costa (1997). Esse tipo de análise recupera mais do que visões estruturais, obtendo visões funcionais e até mesmo de domínio. 17 3 ENGENHARIA REVERSA DE SISTEMA Engenharia reversa de sistema envolve várias ferramentas e utiliza o sistema operacional para obter informação, inspecionando programas executáveis. Pois toda interação que um programa tem com o mundo externo tem que ir pelo sistema operacional. Esta é a razão da necessidade em entender a operação do sistema. Segundo Eilam, E. (2005), a Engenharia reversa de Código é uma arte. Conceitos de extração de algoritmos de um programa binário é um processo complexo que requer um domínio de várias técnicas acrescidas de um entendimento sólido de desenvolvimento de software, da CPU, e do sistema operacional. O software pode ser altamente complexo, até mesmo com acesso a programas bem escritos e corretamente documentados, o código fonte é freqüentemente difícil de ser compreendido. Para isso observa-se o código de baixo nível, procurando detalhes de como o software opera. Muitos destes detalhes são gerados automaticamente pelo compilador e não manualmente pelo desenvolvedor de software. 3.1 SOFTWARE DE BAIXO NÍVEL Segundo Eilam, E. (2005), o software de baixo nível (também conhecido como software de sistema) é um nome genérico para a infra-estrutura do mundo de software. Algumas ferramentas de desenvolvimento como compiladores, linkers, e depuradores são software de infra-estrutura para operar sistemas e linguagens de programação de baixo nível como a linguagem assembly. Esta é a camada que separa o desenvolvedor de software e aplicação do hardware físico. As ferramentas de desenvolvimento separam o desenvolvedor de software de arquiteturas de processador e da linguagem assembly, enquanto sistemas operacionais separam o desenvolvedor de software de dispositivos de hardware específicos e simplifica a interação com o usuário final, administrando a exibição, o mouse, o teclado, e assim por diante. 18 Antigamente, os programadores sempre tiveram que trabalhar no baixo nível porque era o único modo possível para escrever software de infra-estrutura de baixo nível. Hoje em dia, sistemas operacionais modernos e ferramentas de desenvolvimento ocultam os detalhes do mundo do baixo nível. Isto simplifica profundamente o processo de desenvolvimento de software, porém à custa da redução do poder e controle geral do sistema. Para se tornar um engenheiro reverso pleno, é preciso desenvolver um sólido conhecimento de software de baixo nível e programação de baixo nível. Isso porque os aspectos de baixo nível de um programa freqüentemente permitem o domínio dos detalhes de engenharia reversa de alto nível. A maioria dos desenvolvedores de software usa a linguagem de alto-nível que é de fácil compreensão. Por exemplo, comandos que criam uma janela, carregam uma página de Web, ou exibem um quadro é inacreditavelmente simples, enquanto o significado disso é traduzido em milhares ou até mesmo milhões de comandos nas mais baixas camadas. Engenharia reversa requer um entendimento sólido destas mais baixas camadas. Para tanto, tem que estar literalmente atento a qualquer coisa que se coloca entre a fonte de programa e a CPU. 3.2 LINGUAGEM ASSEMBLY Segundo Eilam, E. (2005), a linguagem assembly é o mais baixo nível na cadeia de software possibilitando inacreditavelmente satisfazer a engenharia reversa. Se o software executa uma operação, deve ser visível no código de linguagem assembly. Esta é a linguagem da engenharia reversa, seu domínio requer do desenvolvedor um entendimento sólido da linguagem assembly na plataforma escolhida. Outro conceito importante do código de máquina (freqüentemente chamado de código binário ou código de objeto). As pessoas às vezes cometem o erro de pensar que o código de máquina é "mais rápido" na linguagem assembly. Isso é um 19 engano, código de máquina e linguagem assembly são duas representações diferentes da mesma coisa. ACPU lê código de máquina, que nada mais é que uma sucessão de pedaços que contêm uma lista de instruções para a CPU executar. Em vez de números de hexadecimal secretos nós podemos ver em instrução textual nomes como MOV (Mova), XCHG (Troca), e assim por diante. Cada comando de linguagem assembly é representado por um número, chamando o código de operação, ou opcode. Código de objeto é essencialmente uma sucessão de opcodes e outros números estão relacionados a opcodes para executar operações. CPU’s constantemente lêem código de objeto de memória, decodifica-o, e agem baseados em instruções embutidas. Quando o desenvolvedor escreve código em linguagem assembly (uma ocorrência bastante rara estes dias), eles usam um programa de montador para traduzir o código de linguagem assembly textual em código binário que pode ser decodificado por uma CPU. Na outra direção, um disassembler faz o oposto exato. Lê código de objeto e gera o textual traçando cada instrução nela. Esta é uma operação relativamente simples para executar porque a linguagem assembly textual simplesmente é uma representação diferente do código de objeto. Disassembler é uma ferramenta fundamental para a engenharia reversa. 3.3 COMPILADORES Segundo Eilam, E. (2005), um arquivo de texto que contém instruções que descrevem o programa em um alto-nível de linguagem é produzido pelo compilador. O compilador é um programa que lê um arquivo fonte e gera um arquivo de código de máquina correspondente. Dependendo do alto-nível da linguagem, este código de máquina pode ser um objeto de plataforma específica que é decodificado diretamente pela CPU ou pode ser codificado em uma plataforma especial independente chamado bytecode. Compiladores tradicionais usam linguagens de programação como C e C++ diretamente para gerar código objeto de máquina legível a partir do código fonte 20 textual. O que isto significa é que o código de objeto resultante, quando traduzido pela linguagem assembly por um disassembler, é essencialmente um programa de máquina gerado. Claro que, não é completamente nenhum código de máquina, porque o desenvolvedor de software descreveu ao compilador o que precisada ser feito na linguagem de alto-nível. A barreira maior em decifrar código gerado pelo compilador é a otimização aplicada pela maioria dos compiladores modernos. Compiladores empregam uma variedade de técnicas, isso minimiza o tamanho de código e melhora o desempenho da execução. O problema é que o código aperfeiçoado é freqüentemente difícil de ler. Por exemplo, ao aperfeiçoar os compiladores substituem instruções com operações matemáticas equivalentes cujo propósito pode estar longe do óbvio à primeira vista. 3.4 MÁQUINAS VIRTUAIS E BYTECODES Segundo Eilam, E. (2005), compiladores para linguagem de alto-nível como Java geram um bytecode em vez de um código de objeto. Bytecodes são semelhantes a códigos, a não ser que eles sejam decodificados por um programa, em vez de uma CPU. A idéia é ter um compilador que gere o bytecode, e usar um programa chamando a máquina virtual para então decodificar o bytecode e executar as operações descritas nele. Claro que, a máquina virtual terá que converter em algum ponto o bytecode em código de objeto, que é compatível com a CPU subjacente. Há vários benefícios em usar a linguagem baseada em bytecode. Uma vantagem significativa é a independência de plataforma. A máquina virtual pode ser instalada em plataformas diferentes que habilitam o mesmo programa binário em qualquer CPU, contanto que tenha uma máquina virtual compatível. Claro que, indiferentemente da plataforma na qual a máquina virtual está executando atualmente, o bytecode formado fica o mesmo. Isto significa teoricamente que o desenvolvedor de software não precisa se preocupar sobre a compatibilidade de plataforma. Tudo que eles têm que fazer são prover os clientes com uma versão de bytecode do programa deles. 21 3.5 SISTEMAS OPERACIONAIS Segundo Eilam, E. (2005), um sistema operacional é um programa que administra o computador, incluindo o hardware e aplicações de software. Um sistema operacional tem o controle de muitas tarefas diferentes e pode ser visto como um tipo de coordenador entre diferentes elementos em um computador. Sistemas operacionais são fundamentais em um computador, tanto que a engenharia reversa necessita do conhecimento profundo do que eles fazem e como eles trabalham. 3.6 DISASSEMBLERS Segundo Eilam, E. (2005), disassemblers são programas que transformam um programa executável em arquivos textuais que contêm código de linguagem assembly de um programa inteiro ou partes dele. Isto é um processo relativamente simples considerando que código de linguagem assembly é a cartografia textual do código de objeto. Um disassembler de alta qualidade é um componente fundamental no kit de ferramentas da engenharia reversa, contudo alguns preferem usar disassemblers embutidos em certos depuradores de baixo nível. 3.7 DEPURADORES O depurador é um programa que permite ao desenvolvedor de software observar o programa enquanto está funcionando. As características básicas do depurador são a habilidade para fixar pontos de ruptura e a habilidade para localizar por código. Pontos de ruptura permitem os usuários selecionar certa função ou linha de código em qualquer lugar dentro do programa e instrui o depurador para interromper a execução do programa uma vez que esta linha é alcançada. Quando o programa alcança o ponto de ruptura, o depurador pára e exibe o estado atual do programa. Neste ponto, é possível iniciar o depurador e o programa continuará executando ou começar a traçar o programa. 22 Depuradores permitem aos usuários localizar por um programa enquanto estiver executando. O programa executa uma linha de código e então gerada, permite o usuário observar ou até mesmo alterar o estado do programa. O usuário pode executar a próxima linha então e pode repetir o processo. Isto permite ao desenvolvedor ver o fluxo exato de um programa. Instalando pontos de ruptura e localizando por programas, o desenvolvedor pode assistir a um programa de perto, como executa uma seção problemática de código e tentar determinar fontes do problema. Os depuradores apresentam o programa em código fonte, e permitem ao desenvolvedor fixar pontos de ruptura e localizar por linhas, embora o depurador esteja trabalhando de fato com o código de máquina, segundo Eilam, E. (2005). 3.8 DECOMPILADOR O decompilador está a um passo acima do disassembler. Um decompilador transforma um arquivo binário executável em linguagem de alto nível. A idéia é tentar reverter o processo de compilação, obter o arquivo de fonte original ou algo semelhante. Na maioria das plataformas, a recuperação atual do código fonte original não é realmente possível. Existem elementos significantes na maioria das linguagens de alto-nível, porém são omitidos no processo de compilação e é impossível recuperar. Decompiladores são ferramentas poderosas que em algumas situações e ambientes podem reconstruir um código fonte altamente legível de um programa binário, segundo Eilam, E. (2005). 23 4 ASPECTOS LEGAIS A engenharia reversa pode também gerar problemas de legalidade, como uma empresa querendo criar uma cópia de um produto que vende bem. No entanto, a questão legal depende das leis de cada país. E, mesmo assim, ainda existem países que não possuem leis específicas sobre o assunto. O debate legal ao redor de engenharia reversa acontece há vários anos. Isto normalmente revolve ao redor da pergunta de qual o impacto social e econômico a engenharia reversa está causando na sociedade como um todo. Claro que, para calcular este tipo de impacto, em grande parte, depende de como a engenharia reversa é usada. As seguintes seções discutem os aspectos legais dessas aplicações, com uma ênfase nos Estados Unidos , segundo Eilam, E. (2005). Porém nota-se a dificuldade em afirmar quando tal procedimento é legal ou não, pois depende de muitos fatores. 4.1 COMPATIBILIDADE Em tecnologia, especialmente em informática (independentemente de plataforma), é dito de um produto que este possui compatibilidade reversa (ou compatibilidade descendente) quando é capaz de assumir o lugar de um produto mais antigo, interagindo com outros produtos que foram desenhados para funcionar com a versão anterior. Segundo Eilam, E. (2005), a engenharia reversa utilizada para tal finalidade produz vários benefícios, simplificando o processo de desenvolvimento e melhorando as tecnologias existentes. Todavia, quando usada como ferramenta de competição de produtos, a situação torna-se mais complexa. Por exemplo, o roubo de segmentos de códigos dos concorrentes e sua reprodução integral demonstram claramente a violação dos direitos autorais. Entretanto, quando se utiliza de métodos de 24 descompilação de programas e recompilação com funcionalidades idênticas mas com códigos diferentes, torna-se mais difícil sua comprovação. Uma das leis mais conhecidas é o “Digital Millenium Copyright Act, (DMCA)” dos Estados Unidos, aprovado em 1998 que, entre várias medidas para proteger direitos autorais na informática, também faz restrições em relação à engenharia reversa. O DMCA considera proibido: • Burlar sistemas protegidos por direitos autorais: Isto significa que uma pessoa não pode burlar a tecnologia do Digital Rights Management (DRM), até mesmo para uso pessoal. • O desenvolvimento de tecnologias de alteração: Isto significa que uma pessoa não pode desenvolver ou possa disponibilizar qualquer produto ou tecnologia que iluda uma tecnologia de DRM. Exemplo, um programa de keygen. Na realidade, uma pessoa que desenvolve um keygen viola esta seção, e uma pessoa que usa um keygen viola o item anterior. Um keygen é um programa que gera um número de série rapidamente para programas que pedem números consecutivos durante a instalação. Keygens estão (ilegalmente) disponíveis on-line para praticamente qualquer programa que requer um número de série. Afortunadamente, o DMCA faz várias exceções nas quais a engenharia reversa é permitida. São isenções providas pelo DMCA: • Compatibilidade: inverter e evitar tecnologias de DRM podem ser permitidas em circunstâncias onde tal trabalho é necessário para a compatibilidade com o produto de software em questão. Por exemplo, se um programa foi codificado com a finalidade de proteção de cópia, um desenvolvedor de software pode decifrar o programa em questão se o único objetivo for a compatibilidade com o mesmo. • Investigação de encriptação: está é uma cláusula altamente restringida no DMCA, que permite os investigadores de tecnologias de encriptação para evitar tecnologias de proteção de direitos autorais em produtos de 25 encriptação. A reversão só é permitida se as tecnologias de proteção interferem com a avaliação da tecnologia de encriptação. • Teste de segurança: Uma pessoa pode inverter e pode evitar proteção de direitos autorais de software com a finalidade de avaliar ou melhorar a segurança de um sistema de computador. • Instituições educacionais e bibliotecas públicas: Estas instituições podem evitar uma tecnologia de proteção de direitos autorais para avaliar o trabalho registrado antes de comprá-lo. • Investigação de governo: Não surpreendentemente, as agências de investigações do governo não são afetadas pelo DMCA. • Regulamento: Podem ser evitadas Tecnologias de DRM com o propósito de regular os materiais acessados por menores na Internet. Assim, um produto teórico que não permite monitoração dos acessos e navegação na Internet, pode ser burlado com a finalidade de controlar seu uso por menores. • Proteção de privacidade: Produtos que armazenam ou transmitem informação pessoal pode ser revertido e qualquer tecnologia de proteção que eles incluem pode ser alterada. Na União Européia, o “Eu Copyright Directive”, de 2001, é similar ao “Digital Millenium Copyright Act”, porém não é tão restritivo. Só são feitas restrições caso o objetivo final da engenharia reversa seja a cópia de algum programa ou quebra de patente com objetivo de lucro. Caso seja para fins acadêmicos ou de compatibilidade, à princípio não existem restrições. Entretanto, alguns países partem do princípio de que o software comprado é propriedade particular e, portanto, pode ser alterado como qualquer outra manufatura. Na Suíça, a lei a respeito do assunto é bastante curiosa e, de certo modo, polêmica. A lei Suíça de Concorrência Desleal de 1986 exige dos competidores a realização de investimentos em engenharia reversa mesmo quando a tecnologia não seja secreta. Os tribunais suíços, porém, têm rejeitado ou limitado severamente a aplicação de tal norma, pela inexistência de prazo e limites. 26 No Japão, a Lei Japonesa de Concorrência Desleal de 1993 proíbe a imitação servil, mesmo no caso de produtos não patenteados, nem protegidos por direitos autorais. A lei japonesa impõe limites claros à aplicação da norma de apropriação ilícita: o “lead time” vigora apenas por três anos; não se protegem as idéias e os conceitos técnicos; e ressalva-se o caso de modificações ou aperfeiçoamento técnico efetuado pelo competidor com base no item copiado; a necessidade de padronização e compatibilização de produtos e o uso de elementos de caráter estritamente funcional. Ou seja, a proibição de imitação não impede o progresso técnico, ressalva o domínio das patentes para proteger idéias e conceitos, e o interesse social na padronização e compatibilização industrial. No Brasil não existe uma lei específica sobre Engenharia Reversa. Apesar disso, quando ocorre costuma-se proceder de duas maneiras: caso a engenharia reversa não tenha como objetivo a pirataria ou infração de algum direito autoral, não é considerado crime; porém caso contrário, a Lei N. 9.609, de 19 de fevereiro de 1998 (Lei de Software) e também a Lei N. 9.610 de Direitos Autorais de 1998 protege seus autores. 4.2 SÃO PONTOS FUNDAMENTAIS DA LEI DE SOFTWARE: Segundo Júnior, J. B. (2006), a definição de “programa de computador” está no artigo primeiro da lei; O regime de proteção dos direitos de autores de programas de computador, conforme o artigo 2º da lei, é o mesmo das obras literárias; Não há discussão sobre danos morais quanto ao uso irregular de softwares, ressalvado ao proprietário o direito de fazer cessar as alterações que seu software tenha sofrido ou venha sofrendo; A tutela dos direitos autorais sobre software é por 50 anos, a partir de 1º de janeiro do ano subseqüente à publicação ou criação do software; A proteção aos direitos independe de registro do software; Há reciprocidade de reconhecimento de direitos aos estrangeiros domiciliados no exterior, desde que seu país também reconheça os mesmos direitos em relação aos brasileiros e estrangeiros domiciliados no Brasil; Ao autor também cabe o direito 27 exclusivo de autorizar ou proibir o aluguel comercial de seu software (aplicável somente aos softwares cujo objeto do contrato não seja a locação em si); Os programas de computador poderão ser registrados em órgãos competentes; As informações para registro do programa de computador deverão ser capazes de identificá-lo, e são de caráter sigiloso, sendo reveladas apenas em razão de interesse do proprietário dos direitos ou por ordem judicial; segundo Júnior, J. B. (2006). A cópia de um só exemplar para salvaguarda não é considerada ofensa a direitos autorais. Portanto, se em nossa empresa possuímos 10 licenças legalmente adquiridas de Windows, podemos ter também 10 cópias (uma de cada licença), e isso não será considerado ilegal. Atenção: essas cópias de salvaguarda não poderão estar instaladas, sob pena de violação de direitos autorais; Em resumo, podemos dizer que a lei de direitos autorais – no tocante à área de informática – veio delimitar a ação criminosa e trazer as sanções civis aplicáveis ao contrafator (nome técnico do “pirata”). O processo de verificação de violação de diretos autorais usará sempre a lei de software como base, eis que o objeto perseguido é a proteção de direitos de autor de programa de computador. Segundo Júnior, J. B. (2006), ele será, também, baseado na lei de direitos autorais, eis que esta define o que é o ato de contrafação (artigo 5º), assim como a multa civil aplicável ao contrafator em favor do autor lesado. 4.3 FALHAS DE SEGURANÇA 4.3.1 Um pouco de história Anos atrás, os operadores de um computador ENIAC se depararam com uma coisa curiosa. Um inseto havia ficado preso dentro da máquina e estava atrapalhando o funcionamento da mesma. Daí surgiu o termo bug (inseto) que virou sinônimo de 28 falha. Hoje quando se descobre um erro em algum programa, se diz: “novo bug descoberto”. De lá pra cá, as coisas evoluíram muito, mas os bugs continuam a existir. Muitos deles são frutos da história do próprio programa ou sistema. O Windows por exemplo. O Windows NT foi construído a partir do zero, mas o Windows ME não. Desde o início da criação de sua primeira interface gráfica, a Microsoft vêm tendo problemas com erros graves em seu sistema operacional. Já o sistema Unix, foi criado pelos desenvolvedores da linguagem C, para ser um sistema versátil e poderoso. Para Assunção, M. F. A. (2001), a Internet também têm seus problemas ligadas à história de sua origem. Desde que se chamava Arpanet e foi criada pelo exército americano para resistir à guerra fria, a rede evoluiu muito e foram criados novos serviços como E-mail, World Wide Web, Gopher, Wais e outros. Milhões de computadores se juntaram a ela e seus recursos são cada vez mais sofisticados. Mas alguns problemas bem antigos ainda são perturbadores hoje. Uma falha na implementação do TCP/IP (conjunto de protocolos em que a Internet se baseia), por exemplo, possibilita que o ataque de Spoof aconteça. 4.3.2 Alguns conceitos importantes Hackers: Alguém que estuda sistemas ou qualquer tipo de conhecimento humano pelo simples desafio de dominá-los. No sentido original da palavra, o Hacker é alguém que usa seus conhecimentos para ajudar outros, direta ou indiretamente. Hackers foram os principais responsáveis pelo desenvolvimento da Internet, criaram o Linux, o MP3 e a filosofia do software livre. Atualmente o termo vem sendo muito usado em relação aos Crackers, segundo Morimoto, C.E. (2005). Crackers: O cracker é um vândalo virtual, alguém que usa seus conhecimentos para invadir sistemas, quebrar travas e senhas, roubar dados, etc. Alguns tentam ganhar dinheiro vendendo as informações roubadas, outros buscam apenas fama ou divertimento. Na hierarquia Hacker, o Cracker está acima do Lamer (que sabe muito pouco), mas abaixo do Hacker, que é alguém de mais maturidade, que ao invés de 29 usar seu conhecimento para destruir tudo que vê pela frente, o utiliza para construir coisas, desenvolver novos sistemas (principalmente de código aberto), etc. Para Morimoto, C.E. (2005), outra definição, mais branda, é alguém que quebra travas de segurança de programas e algoritmos de encriptação, seja para rodar jogos sem o CD-ROM, ou gerar uma chave de registro falsa para um determinado programa, quebrar as travas anti-cópias usadas em alguns softwares ou quebrar o sistema de encriptação do DVD (este último realmente importante, pois permitiu que os usuários do Linux e outros sistemas não Windows pudessem assistir DVDs). Ou seja, nesta definição o Cracker é alguém na margem da lei, cujas ações ainda são ilegais, embora muitas vezes eticamente justificáveis (os usuários têm direito a fazer cópias de CDs legalmente comprados, tem direito de assistir DVDs no Linux e assim por diante). Trojans: O nome trojan é uma alusão à história do antigo cavalo de tróia, em que o governante da cidade de Tróia na antiga Grécia foi presenteado com um cavalo de madeira “recheado” de soldados inimigos, segundo Assunção, M. F. A. (2001). Possui muitas características similares aos vírus, tais como: perda de arquivos, falhas na memória, erros em periféricos, etc. A grande diferença é que o trojan pode ser considerado um vírus inteligente, pois é controlado à distância pela pessoa que o instalou. Esse indivíduo então consegue “enxergar” o seu computador, podendo realizar desde as mais simples tarefas como mexer o mouse à utilização do seu IP como ponte para outros ataques. Conseguem ficar escondidos em arquivos de inicialização do sistema operacional e se iniciam toda vez que a máquina é ligada. Sniffers: Os sniffers ou farejadores são os tipos de programas mais usados para conseguir senhas em uma rede. Eles ficam residentes na memória como um cavalo de tróia, analisando todo o tráfego que ali passa. Qualquer entrada ou saída de dados é capturada, seja em um servidor FTP, uma página de chat ou um e-mail digitado. O sniffer captura os pacotes recebidos em seu estado bruto e os transforma em texto puro para serem lidos. Sempre foram mais usados em sistemas Unix, mas ultimamente todos os outros sistemas contam com poderosos sniffers, segundo Assunção, M. F. A. (2001). 30 Syn-flood: O tipo de ataque usado para gerar o ip spoof. A autenticação por Syn é feita em três vias. Para Assunção, M. F. A. (2001), o ataque consiste em não completar essas três vias. Mais ou menos assim. No caso do ping, ele é em duas vias, apenas envia o pacote e recebe a resposta. Para o Syn-flood, primeiro é enviado o pacote Syn e logo depois teria que ser enviado o Ack para a conexão se estabelecer, mas ele não é enviado, fazendo com que a máquina alvo consuma seus recursos ao receber muitos Syns e esperar muitos Acks. O ataque por ping é parecido, é enviado vários pings com grandes pacotes fazendo com que um sistema trave. Mas é mais difícil de ocorrer o travamento do que o ataque por syn. OOB: Ataque Out-of-Band ou popularmente conhecido como WinNuke. Consiste em mandar pacotes malformados para uma porta Netbios do Windows. Geralmente usado nas portas 135, 137 e 139, essa última sendo a mais usada. O sistema não consegue lidar com os pacotes, trava e mostra a famosa tela azul de erro. No Windows 95 esse ataque era mais eficaz, agora está se tornando obsoleto. Smurf: Enviam pacotes ICMP (protocolo que informa condições de erro) spoofados para centenas, talvez milhares de sites. Enviam-se os pacotes com o endereço IP da vítima, assim fazendo com que ela receba muitos pacotes ping de resposta ao mesmo tempo, causando um travamento total. Ainda não existe uma proteção eficaz contra esse tipo de ataque. Um programa bom (para Windows) que realiza o smurf é o WinSmurf, conforme Assunção, M. F. A. (2001). Scanners: São programas que analisam um sistema ou rede em busca de falhas de qualquer tipo. Existem dezenas de scanners diferentes, cada um com suas vantagens. Aprendendo melhor sobre eles, poderá se proteger melhor e evitar que algum invasor malicioso dê um passo à sua frente. Senhas: Serve para autenticar o usuário, ou seja, é utilizada no processo de verificação da identidade do usuário, assegurando que este é realmente quem diz ser. Engenharia social: Método de ataque, onde alguém faz uso da persuasão, muitas vezes abusando da ingenuidade ou confiança do usuário, para obter informações 31 que podem ser utilizadas para ter acesso não autorizado a computadores ou informações, segundo Vale e Ulbrich, (2004). Vulnerabilidades: É uma falha no projeto ou implementação de um software ou sistema operacional, que quando explorada por um atacante resulta na violação da segurança de um computador, para Eilam, E. (2005). Criptografia é a arte da escrita oculta usada desde a antiguidade, por exemplo: pelos egípcios na sua antiga escrita. Ela é muito importante hoje em dia na internet. Mandar um e-mail confidencial da maneira convencional é muito inseguro, ele pode ser interceptado no meio da transmissão ou posteriormente, por isto a necessidade do uso de programas eficientes, como o PGP. Esses programas possibilitam uma espécie de “código especial” entre você e o receptor da mensagem, fazendo com que mesmo que alguém consiga obtê-la no meio do caminho, ela será impossível de se ler, segundo Assunção, M. F. A. (2001). 4.3.3 Chaves públicas e privadas Em Assunção, M. F. A. (2.001), na década de 1970 o padrão na criptografia era a criptografia simétrica onde existia uma única chave (senha) para encriptar e desencriptar, tanto para o emissor quanto para o destinatário. O grande problema desse método era como transmitir com segurança esta senha. No final da década de 70 foi desenvolvido o método da criptografia assimétrica e a tecnologia das chaves pública e privada. Você encriptava com a chave pública do destinatário e somente ele poderia desencriptar utilizando a sua chave privada e também haveria como saber com certeza absoluta se a pessoa que mandou a mensagem era realmente quem dizia ser, segundo Soares et al. (1995). Um sistema computacional é dito seguro se este atende a três requisitos básicos relacionados aos recursos que o compõem: confidencialidade, integridade e disponibilidade. 32 A confidencialidade diz que a informação só está disponível para aqueles que devidamente autorizados; a integridade diz que a informação não é destruída ou corrompida e o sistema tem um desempenho correto, e a disponibilidade diz que o serviços/recursos do sistema estão disponíveis sempre que forem necessários. Todos os sistemas têm falhas, segundo Assunção, M. F. A. (2001). Elas consistem em pequenos erros na criação dos programas, possibilitando que crackers os utilizem para tomar o controle de alguma máquina. Existem em absolutamente todo tipo de software, desde um simples tocador de mp3, um aparentemente inofensivo editor de texto, um jogo de computador e até mesmo o próprio sistema operacional. Essas falhas por mais insignificantes que pareçam, podem comprometer a segurança de uma rede inteira. E a maior de todas as falhas é o desinteresse dos muitos administradores de hoje que acham que o termo bug é algum desenho do Walt Disney. O bug, ou falha, surge a partir do momento que o programador comete um erro. Ou seja, indiretamente é um erro humano que gera a falha nos programas. Por serem pequenos erros e não aqueles cabeludos que fazem o compilador até rir do programador, muitas vezes passam despercebidos e só são descobertos por algum hacker ou analista de segurança. Os erros do Windows, por exemplo. A grande maioria das falhas descobertas são os próprios usuários que descobrem. Os criadores que têm o código fonte e conhecem o programa como a palma da mão raríssimas vezes percebem algum erro. Para ser mais seguro, um programa têm que ser testado de todas as maneiras possíveis. Algo que nem sempre é feito. 4.3.4 Exemplos de falhas Algumas falhas são tão bobas que é difícil de acreditar. Geralmente, no sistema Windows, que de longe é o que possui mais falhas. O Windows 98 possui muitos erros, mas três são interessantes. O primeiro é que não consegue executar nem abrir nenhum link com a url c:\con\con. Se você tentar ir em iniciar e executar, o 33 sistema travará e mostrará a famosa tela azul. Os outros dois são do netbios. O primeiro possibilita que você acesse o diretório system do Windows por um compartilhamento de impressora. É só mapear o compartilhamento padrão printer$. O último possibilita que se descubra a senha do netbios sabendo apenas o primeiro caractere. Por exemplo: coloca-se no disco C compartilhado a senha herodes. Se alguém tentar o primeiro h já consegue acesso à rede. O Windows 2000 também possui algumas falhas, como deixar o netbios ativo em sua instalação. Saindo um pouco dos sistemas operacionais, alguns programas também possuem falhas graves, segundo Assunção, M. F. A. (2001). Erros de Active X possibilitam que ao visitar um site, o Internet Explorer instale um programa no seu computador e o execute sem ser percebido. Outro erro está no anexo de e-mail. Erros no Outlook fazem com que só de receber os e-mails, os anexos sejam executados automaticamente. O Internet Information Server, servidor de homepages da Microsoft, possui erros graves. Unicode, RDS, existem muitos. O mais recente é uma falha no printer .isapi , fazendo com que se consiga acesso ao Windows 2000 pelo IIS 5.0 . O sistema Unix possui muitas falhas também, como no sendmail e no Apache. 4.3.4.1 Buffer overflows O buffer overflow é um ataque usado há muito tempo e que ainda será muito usado. Compreende em lotar os buffers (memória disponível para aplicativos) de um servidor e incluir na sua lista de processos algum programa tal como um keylogger ou um trojan. Todos os sistemas são vulneráveis a buffer overflows e a solução é a mesma, procurar se já existem correções existentes. Novos erros desse tipo surgem todo dia, até o XP já têm alguns. A atualização do software deve ser contínua. Segundo Assunção, M. F. A. (2001), um dos usos famosos do buffer overflow é o telnet reverso. Ele consiste em fazer a máquina alvo conectar-se a um servidor no computador do cracker, fornecendo-lhe um shell (prompt) de comando. O netcat, chamado de “canivete suíço do TCP/IP”, é uma espécie de “super-telnet”, pois 34 realiza conexões por UDP, serve como servidor, entre outras tarefas. Ele é o mais utilizado para a realização do telnet reverso, e pode ser usado tanto na arquitetura NT quanto no Unix. A versão para Windows está disponível em ftp.technotronic.com . A exploração de código vulnerável a buffer overflow exige alguma habilidade. Entretanto, o conhecimento necessário para tal tarefa pode ser facilmente adquirido pelo material difundido na rede e experimentação exaustiva. A tarefa de codificar software seguro é difícil, mas deve ser tomada com máxima seriedade. Principalmente quando se está desenvolvendo software de segurança ou projetado para ser executado com privilégios de super-usuário ou usuário especial do sistema. Chega a impressionar o número de vulnerabilidades a buffer overflow encontradas em software de utilização ampla, dada a simplicidade das técnicas em evitá-las. 4.3.4.2 Race condition O Race condition ou condição de corrida é mais comum no Unix e no Linux. Consiste em fazer algum programa que rode como root (super-usuário) executar alguma falha que possa lhe enviar para o shell do sistema. O programa que mais teve problemas de race condition até hoje é o sendmail , serviço de e-mail padrão do Unix. É possível encontrar falhas até em versões mais recentes. 4.3.4.3 Descobrindo se algum sistema tem falhas Segundo Assunção, M. F. A. (2001), para o programador experiente é mais fácil verificar se um sistema tem falhas, utilizando de recursos de debug que checam por erros de buffer overflow e outros. Para o usuário é bem mais difícil descobrir algo, principalmente o usuário comum. O interessante seria visitar páginas especializadas no assunto, que a cada dia publicam novos tipos de erros descobertos. 35 Scanners de vulnerabilidade (ou falhas). • Typhon (Só funciona em Windows NT/2000/XP): esse scanner é excelente pois é rápido e dá algumas informações muito boas sobre possíveis falhas e desconfigurações do sistema alvo. Não é muito completo, mas é o ideal para se usar antes do Stealth, que como veremos, é inigualável. • Stealth: um dos melhores scanner de vulnerabilidades. É um programa de uma empresa brasileira, já ganhou destaque internacional pois consegue identificar cerca de 15.000 falhas em sistemas. Poucos conseguem escapar ilesos a essa potente arma. 4.3.4.4 Utilizando exploits Exploits são programas criados para explorar falhas. O exemplo do printer .isapi do IIS 5.0 mencionado anteriormente, possui um exploit chamado iishack2000. Ele possibilita que somente digitando o IP de algum computador, seja possível conseguir acesso direto ao interpretador de comandos (ou shell). Permitindo fazer o que quiser com o sistema. Existe também o iishack (sem o 2000), que utiliza um erro de buffer overflow do IIS 4.0 para fazer com que o servidor execute qualquer tarefa (como um trojan). Cada exploit corresponde a uma falha, por isso geralmente os grandes sites de segurança publicam os dois juntos. Geralmente vêm em códigofonte C, Perl e alguns poucos em executáveis comuns, segundo Assunção, M. F. A. (2001). 4.3.4.5 Instalando patches Os patches são atualizações de programas geradas pelos fabricantes. Toda vez que um erro for descoberto, deve-se visitar a página do fabricante do programa e baixar a correção. Conforme Assunção, M. F. A. (2001), isso não pode ser feito de mês em mês, e sim no máximo de três em três dias. Os erros aparecem muito rapidamente, e um sistema é composto de muitos softwares. Todos devem ser checados. É 36 interessante também assinar uma lista de discussão sobre segurança, assim toda vez que uma falha for descoberta, serão enviados e-mail’s para alertar. A Microsoft (www.microsoft.com), a Securenet (www.securenet.com.br) e Security-focus (www.securityfocus.com) possuem algumas. O fato é que até mesmo com o código fonte às vezes pode ser difícil de distinguir código seguro inofensivo de vulnerabilidades de segurança perigosas. 37 5 TÉCNICAS DE ENGENHARIA REVERSA 5.1 TÉCNICAS DE ENGENHARIA REVERSA SEM O CÓDIGO-FONTE Segundo Anquetil, N. (2002), a engenharia reversa de software pode ser efetuada por vários métodos.Três grupos principais da engenharia de software são: • Análise de fluxo de dados: Análise através da observação da troca de informações que envolvem “analisadores de bus” e “pacotes de sniffers”, por exemplo, para "ouvir" dentro do bus de um computador ou uma conexão de rede, revelando o tráfico de dados "escondidos". O comportamento dos dados no bus ou na rede podem então ser analisados para produzir uma nova implementação do software que imita o mesmo comportamento. Isto é especialmente utilizado na engenharia reversa de drivers de dispositivos. • Desassemblar: Usando um desassembler, conseguimos obter a linguagem de máquina diretamente do programa. Este código é lido e entendido nos seus próprios termos, apenas com a ajuda de “mnemonics” da linguagem de máquina. Isto funciona em qualquer programa de computador, mas pode levar um bom tempo, especialmente para alguém que não esteja acostumado ao código de máquina. • Decompilação: Neste método utiliza-se um decompilador, um programa que tenta recriar o código-fonte em uma linguagem de alto nível, tendo disponível apenas o código de máquina. 38 5.2 TÉCNICAS DE ENGENHARIA REVERSA COM O CÓDIGOFONTE 5.2.1 Extração das Informações O primeiro trabalho que se deve fazer é coletar informações sobre o sistema a ser estudado. As atividades da engenharia reversa se fazem sobre essas informações extraídas, mais do que sobre o próprio sistema. As informações podem ser extraídas de várias fontes: o código fonte, a execução, os dados (por exemplo, em banco de dados), a “documentação”, ou outras fontes. 5.2.1.1 Código (análise estática) A primeira fonte, o código, é a mais usada. A análise do código é chamada também de análise estática (por oposição à análise dinâmica que é a execução do sistema). Ela permite extrair as informações mais básicas do sistema: • Quais são os componentes básicos do sistema: arquivos, rotinas, tipos, variáveis, classes, etc.; • Relações de definição conectam um componente com seu conteúdo (onde ele se encontra); • Relações de referência conectam um componente com aqueles que o usam (se uma rotina A chamar uma outra rotina B. A depende de B porque uma modificação na definição de B pode ter conseqüências sobre a execução de A). Essas relações podem ser memorizadas como as relações de definição/referência. Na extração das informações, gravam-se apenas as informações que vem diretamente do código, mas depois outras podem ser deduzidas a partir dessas. 39 Ferramentas para fazer essa análise são chamadas de “parser”. O “parsing” é a primeira etapa da compilação do código fonte. Para fazer isso, é preciso conhecer a sintaxe da linguagem de programação usada. Dependendo das necessidades, pode-se usar parsers específicos que vão procurar só um tipo particular de informação. Por exemplo, num programa Pascal, pode-se extrair o nome de todas as funções definidas com um parser muito simples. 5.2.1.2 Trace de execução (análise dinâmica) A análise estática pode extrair muitas informações de um programa, mas nem todas. Por exemplo, qual parte de uma instrução IF é realmente usada pode depender dos dados com que o programa foi chamado. Para descobrir esse tipo de informação, precisamos da análise dinâmica, que consiste em executar o programa e monitorar os valores das variáveis, quais funções são chamadas, etc. 5.2.1.3 Dados Os bancos de dados podem ser usados como fonte de informação para ajudar na engenharia reversa de um sistema. Mas a engenharia reversa de dados é também um trabalho específico que pode ser feito independentemente de qualquer sistema que possa manipular esses dados. Por exemplo, querer converter um velho banco de dados de um “mainframe” para um banco de dados relacional e distribuído sobre vários PCs. Podem ser extraídos de um banco de dados: a estrutura, os dados, os relacionamentos e até os relatórios e as interfaces com o usuário. 40 5.2.1.4 Documentação Chama-se de documentação tudo o que não está usado pelo computador para fazer funcionar o sistema, mas se destina aos engenheiros que usam o código: relatórios, comentários no código, diagramas da análise ou do projeto, etc. Como ela se destina aos seres humanos, é difícil de ser analisada automaticamente. A abordagem mais usada é usar as palavras da documentação. Isso pode permitir extrair os conceitos importantes do domínio de aplicação do sistema. Esta abordagem está baseada sobre a suposição que os conceitos importantes aparecem com mais freqüência na documentação. 5.2.1.5 Outras fontes de informação Finalmente, é possível usar outras fontes de informação. Por exemplo, para definir sub-sistemas, poderíamos procurar quem escreveu cada porção do código. É razoável pensar que se duas partes do código foram desenvolvidas pela mesma pessoa elas tem maior probabilidade de pertencer ao mesmo sub-sistema. Outras informações seriam: quais programas são chamados, em que linguagem são escritos, tipo de máquina que roda os programas. 5.2.2 Tratamento das informações Essas são algumas das principais atividades envolvidas na engenharia reversa. Essas atividades usam as informações que foram extraídas do sistema. Podemos resumir o objetivo geral dessas atividades da seguinte forma: elas tentam abstrair informações de mais alto nível de abstração das informações do sistema. O que quer dizer que elas vão eliminar os inumeráveis detalhes. O problema é decidir nessa massa de informações o que é importante ou não, segundo Anquetil, N. (2002). 41 5.2.2.1 Anomalias no código Num software legado, o código pode conter várias anomalias, como partes do programa que nunca podem ser executadas (código morto) e trechos de código que foram copiados e levemente modificados, denominados clones. Essas anomalias complicam o código inutilmente, fazendo ele mais longo do que deveria ser e multiplicando as coisas que um programador tem que estudar e entender. Para resolver esse problema pode-se apagar o código morto, e no caso dos clones é possível modificar o código para suprimi-los ou então fazer comentários no código sobre a existência de clones. 5.2.2.2 Encapsulamento O encapsulamento é uma técnica de re-engenharia mais do que de engenharia reversa. Em vez de reestruturar um sistema, ela propõe esconder o velho código dentro de uma nova camada. 5.2.2.3 “Slicing” “Slicing” (fatiar) é uma técnica de decomposição do código de acordo com a utilização das variáveis. O slicing de uma parte do código consiste em extrair dela todas as instruções que têm uma influência sobre o valor de uma variável definida a um ponto definido do código. Ajuda na descoberta da origem dos bugs, limitando a pesquisa nas únicas instruções realmente necessárias. 5.2.2.4 (Re-)Modularização 42 A (re-)modularização é a decomposição de um conjunto de componentes de software em sub-partes (os módulos). Em engenharia de software, normalmente, se espera que os módulos tenham uma forte coesão interna e um pequeno acoplamento com o exterior. Essa atividade de agrupar arquivos em sub-sistema não é trivial. Um sistema legado pode ser composto de milhares de arquivos, em várias linguagens de programação (por exemplo: uma linguagem procedural, assembly, dados e alguns “scripts” para compilações e execuções). Para agrupar os componentes são utilizados algoritmos que “medem as distâncias” entre os componentes com base em informações extraídas do sistema. Existem muitos algoritmos de agrupamento (clustering) diferentes. Os mais típicos são: • Algoritmo por construção: são os mais rápidos porque trabalham num passe único. Os componentes são introduzidos um depois do outro. Agrupando um novo componente com um módulo existente. • Algoritmos por otimização: começam com uma partição do conjunto de componentes e tentam melhorar esta partição usando heurísticas para medir a qualidade desta. • Algoritmos hierárquicos: constroem uma hierarquia de módulos e não necessitam de informações do usuário. Segundo Anquetil, N. (2002), os algoritmos apresentam dois problemas principais: • O resultado pode ser difícil de entender; • Eles agrupam todos os componentes segundo algumas restrições. Se as restrições forem erradas, o resultado também será. • O resultado desta técnica é globalmente bom. Mas sempre há detalhes errados em alguns módulos. Esses algoritmos são bons quando existem muitos componentes para tratar e como uma primeira abordagem para desbravar o sistema. 43 5.2.2.5 Reconhecimento de Clichés Um “clichê” ou plano é um padrão que descreve uma maneira geral de implementar um conceito de programação. A atividade de reconhecimento de clichês trabalha com um banco de clichês e procura esses clichês no código. A idéia é que um clichê é implementado com várias linhas de código. Reconhecer um clichê pode simplificar o código porque isso vai substituir um só conceito a essas linhas. O reconhecimento de clichê sofre de várias dificuldades: • Construção do banco de clichês; • Dualidade, precisão e cobertura (a descrição do clichê deve ser geral, porém corre-se o risco de ocorrerem falso-positivos); • Tempo de execução; • Clichês deslocalizados ou interligados (podem existir outras instruções entre as que implementam o clichê ou dois clichês cujas instruções estão misturadas). 5.3 TÉCNICAS COMPLEXAS DE ENGENHARIA REVERSA 5.3.1 Análise de propagação Utilizada em manutenção de software, consiste no estudo do código para descobrir as conseqüências de uma modificação. 5.3.2 Objetização 44 Consiste na extração da representação orientada a objetos de um código procedural. Baseia-se: • nas funcionalidades: isolar uma funcionalidade do código e construir um objeto para encapsulá-la; • nos dados: procurar tipos estruturados que representem conceitos da aplicação. Agrupam-nos com as rotinas para formar classes. • e os objetos: fazer uma análise orientada a objetos do domínio do software para descobrir os objetos pertencentes. 5.3.3 Reengenharia de interfaces Seu objetivo é disponibilizar uma nova interface ou reorganizar a existente para facilitar a utilização de funcionalidades já existentes. Possui duas atividades: identificação das informações envolvidas na execução de uma tarefa e a identificação da sequência de mensagens necessária para cumpri-la. 5.4 ENGENHARIA REVERSA PARA SEGURANÇA Para algumas pessoas a conexão entre segurança e engenharia reversa pode não ser tão clara. A engenharia reversa está relacionada a vários aspectos diferentes na segurança de computador. Por exemplo, engenharia reversa foi empregada em pesquisa de encriptação, o investigador inverte um produto encriptado e avalia o nível de segurança deste. Também é usada com relação a software malicioso, em ambos os casos: pelo desenvolvedor de malware e os que desenvolvem seus antídotos. 5.4.1 Software malicioso Segundo Eilam, E. (2005), a Internet mudou completamente a indústria de computador e o aspectos de segurança de informação em particular. Software 45 malicioso, tal como vírus e worms, espalham mais rápido em um mundo onde milhões de usuários estão conectados diariamente à Internet. Há 10 anos, um vírus normalmente teria que se copiar a um disquete e ser carregado em outro computador para que o vírus se espalhasse. O processo de infecção era bastante lento e a defesa era muito mais simples porque os canais de infecção eram poucos e exigia a intervenção humana para espalhar. Isso é história antiga, a Internet criou uma conexão virtual em quase todos os computadores da terra. Hoje em dia, vírus modernos podem se espalhar automaticamente a milhões de computadores sem qualquer intervenção humana. A engenharia reversa é extensivamente usada em ambos os fins da cadeia de software maliciosa. O desenvolvedor de software malicioso usa freqüentemente a reversão para localizar vulnerabilidades em sistemas operacionais e outros softwares. Tais vulnerabilidades podem ser usadas para penetrar a defesa do sistema, estendendo a infecção, normalmente através da Internet. Além da infecção, às vezes eles empregam técnicas de engenharia reversa para localizar vulnerabilidades de software que permitem que um programa malicioso ganhe acesso à informação sensível ou até mesmo tome o controle total do sistema. O outro lado da cadeia diz respeito ao desenvolvedor de software antivírus, pois permite a análise de todo programa malicioso que se tem contato. Eles usam técnicas reversas para localizar os passos que o programa leva e avalia o dano que possa causar ou a taxa esperada de infecção, como poderia ser removido de sistemas infetados e se a infecção pode ser evitada completamente. 5.4.2 Métodos de proteção Alguns cuidados precisam ser levados em consideração para garantir a segurança de um sistema. As boas práticas de programação, mesmo sem se considerar a segurança em si, garantem um código mais robusto, confiável e, conseqüentemente, seguro, segundo Albuquerque (2002). Código seguro não significa necessariamente segurança em código ou código que implemente alguma função para segurança, 46 mas código que é escrito para suportar os atacantes maliciosos. O código seguro é também código robusto, Howard (2002). Albuquerque et al. (2002) considera que o código seguro muita vezes implica em certa perda de desempenho. Entretanto, ressalva que a perda de performance pode ser compensada por um hardware mais rápido, o contrário, porém, não é verdadeiro. A perda de performance pode ser compreendida por sugestões como as de Viega et al. (2003): “assuma que toda entrada de dados é culpada até que se prove o contrário” ou “quanto mais você compreende o dado, mais facilmente você o filtrará”. Torres (2003) também é cauteloso ao tratar as entradas de dados, especialmente em uma arquitetura cliente-servidor e, sobretudo, nas solicitações provenientes de ambientes inseguros – ou pouco controlado – como é o caso da Internet. Existem três fatos a serem considerados no desenvolvimento de software mais seguro: processo repetível, educação e métrica, segundo Lipner (2004). Segundo Lipner et al., é importante que se tenha na organização um grupo responsável pelo desenvolvimento e evolução das melhores práticas de segurança; melhorias no processo, que sirva de fonte de conhecimento para a organização de uma forma geral e que faça a Revisão Final de Segurança (em inglês FSR – Final Security Review) antes da liberação da release. Enquanto muitas organizações preferem ter uma equipe central de segurança, outras optam por contratar terceiros, a experiência demonstrou – na visão de Lipner et al. – que a existência de um grupo interno de segurança é um fator vital para o sucesso na implementação de um ciclo de vida de desenvolvimento. A experiência com segurança de software tem demonstrado que um conjunto de princípios de alto nível para desenvolver software mais seguro precisam ser estabelecidos. A Microsoft refere-se a esses princípios como SD3+C – Seguro por Design, Seguro por Default, Seguro na Distribuição e Comunicação. Uma descrição sucinta desses princípios seria: • Seguro por Design: Uma arquitetura pode ser desenhada para utilizar criptografia 3DES (triplo DES) para dados sensíveis antes de serem armazenados no banco de dados e o uso do protocolo SSL para transportar o 47 dado através da Internet. Todo código é bastante verificado em busca de vulnerabilidades comuns usando ferramentas manuais e automáticas. A modelagem de ameaças é criada durante o processo de design do software. • Seguro por Default: O software é empacotado com medidas de segurança e os componentes potencialmente vulneráveis são desativados. • Seguro na Distribuição: Atualizações de segurança são fáceis de serem localizadas e instaladas – eventualmente são instaladas automaticamente – e ferramentas são disponibilizadas para o levantamento e gerenciamento dos riscos de segurança em toda a organização. • Comunicação – os desenvolvedores precisam estar preparados para descobrir vulnerabilidades nos produtos e comunicar aberta e responsavelmente os usuários finais e/ou administradores para ajudá-los a tomarem as ações preventivas necessárias. 5.4.2.1 Criptografia Soares et al. (1995) comenta que "a criptografia surgiu da necessidade de se enviar informações sensíveis através de meios de comunicação não confiáveis, ou seja, em meios onde não é possível garantir que um intruso não irá interceptar o fluxo de dados para leitura (intruso passivo) ou para modificá-lo (intruso ativo). A forma de contornar esse problema é utilizar um método que modifique o texto original da mensagem a ser transmitida (texto normal), gerando texto criptografado na origem, através de um processo de codificação definido por um método de criptografia. O texto (ou a mensagem) criptografado é então transmitido e, no destino, o processo inverso ocorre, isto é, o método de criptografia é aplicado agora para decodificar o texto criptografado transformando-o no texto normal original". "Existem basicamente dois tipos de criptografia: a simétrica e a assimétrica" Albuquerque (2002). Júlio César empregava um método de criptografia que consistia em substituir as letras de uma mensagem pela terceira letra após sua posição no alfabeto (sendo a sucessor de z) Tanenbaum (1989). Esse é um exemplo de criptografia simétrica ou baseada em chave secreta, pois a mesma chave será 48 usada para cifrar e decifrar a mensagem. O problema clássico dessa solução é a dificuldade de entregar a chave de forma segura a quem vai decifrar a mensagem, segundo Rufino (2002). Existem diversos algoritmos conhecidos de criptografia simétrica, como DES, IDEA, TRIPLE-DES e BlowFish, conforme Albuquerque (2002). O algoritmo DES codifica blocos de 64 bits de texto normal gerando 64 bits de texto criptografado, diz Tanenbaum (1989). O conceito de chaves assimétricas é baseado na idéia que o momento de maior vulnerabilidade da chave é quando ela está em trânsito, Rufino (2002), Disnei (2002). Em 1976, Diffie e Hellman (1976) propuseram um método que revolucionou os sistemas de criptografia. Baseado na utilização de chaves distintas: uma para codificação (E) e outra para decodificação (D), tal que a derivação de D a partir de E fosse, em termos práticos, senão impossível, pelo menos muito difícil de ser realizada. Portanto, o que uma cifrasse somente a outra poderia revelar. Dessa maneira, uma poderia ser tornada pública e trafegar sem necessidade de canais seguros, desde que a outra, privada, permanecesse em local seguro, conforme Rufino (2002) e Soares (1995). Entre os algoritmos de criptografia assimétrica ou de chave pública de uso prático, os mais comuns são o RSA e as Curvas Elípticas. O RSA, cujo nome deriva das inicias dos autores Rivest, Shamir e Adleman (1978), baseia-se na dificuldade de fatorar números muito grandes. Acreditava-se em 1977 que uma mensagem criptografada pelo RSA-129 levaria milhões de anos para ser quebrada Gardner (1977). Foi quebrada em 1994 usando computação distribuída Leutwyler (1994), Lima (2002). 5.4.2.2 Privacidade Albuquerque et al. (2002) explica que: [...] privacidade é a capacidade de um usuário realizar ações em um sistema sem que seja identificado. É completamente diferente de confidencialidade, que define que apenas usuários autorizados podem ter acesso à determinada informação. 49 Um exemplo de sistema que emprega privacidade como atributo essencial é o usado pelo processo eleitoral brasileiro, no qual não pode haver forma de ligação entre o eleitor e seu voto. O Common Criteria for Information Technology Security Evaluation (Critério Comum para Avaliação de Segurança da Tecnologia da Informação) é o nome do padrão que originou a norma ISO/IEC 15408, muitas vezes chamada apenas de Common Criteria, conforme ISO JTC 1/SC (1999). A norma objetiva fornecer um conjunto de critérios fixos que permitem especificar a segurança de uma aplicação de forma não ambígua a partir de características do ambiente da aplicação e definir formas de garantir a segurança da aplicação para o cliente final. As formas de privacidades destacadas pelo Common Criteria são: • Anonimato - garante que um usuário possa usar um recurso ou serviço sem ter sua identidade revelada. • Pseudônimo - garante que um usuário possa usar um recurso ou serviço sem ter sua identidade revelada, porém suas ações são rastreadas e reveladas, geralmente, em situações especiais. Portanto, permite a responsabilização e privacidade ao mesmo tempo. Se usado com não-rastreamento, para cada ato deve ser usado um pseudônimo diferente. • Não-rastreamento - garante que um usuário possa fazer uso de vários recursos e serviços sem que outros possam ligá-lo a esses usos. • Invisibilidade - garante que um usuário possa usar um recurso ou serviço sem que outros, principalmente terceiros, possam saber que o recurso ou serviços está sendo usado. 5.4.3 Técnicas Anti-reversão Algumas aplicações têm uma necessidade especial por medidas de anti-reversão. Um excelente exemplo é a tecnologia de proteção de cópia e a administração de tecnologia de propriedade digital. Prevenir ou obstruir a “quebra” de tecnologias de proteção é um passo crucial de criar meios efetivos de proteção. 50 Segundo Eilam, E. (2005), algumas plataformas de desenvolvimento de software realmente necessitam de medidas anti-reversão, porque caso contrário o programa pode ser mesmo facilmente convertido numa representação próxima do código fonte. Isto é verdade para a maioria das plataformas baseadas em bytecode como a Java e .NET, e é a razão de possuírem tantas obstruções de código para tais plataformas (entretanto também é possível programas de obstrução que foram compilados num processador de idioma nativo da máquina). Um bloqueador é uma ferramenta automatizada que reduz a legibilidade de um programa modificando ou eliminando certa informação. Existem várias formas de evitar a engenharia de software, cada uma com sua vantagem e desvantagem. Pode-se utilizar uma ou várias formas combinadas. 5.4.3.1 Eliminação de Informação Simbólica O primeiro passo e mais óbvio em impedir a reversão é eliminar qualquer informação textual óbvia do programa. Em um programa compilado baseado em bytecode regular, isto simplesmente tira toda a informação simbólica do programa executável. Segundo Eilam, E. (2005), em programas baseados em bytecode, os executáveis contêm freqüentemente grandes quantias de informação simbólica interna como nomes de classe, nomes de objetos globais, etc. Isto é comum em linguagens como Java e plataformas como .NET. Esta informação pode ser extremamente útil na engenharia reversa, por isto deve ser eliminada de programas onde a reversão é uma preocupação. Outra característica fundamental de quase todo ofuscador de bytecode é renomear todos os símbolos em sucessões sem sentido de caracteres. 5.4.3.2 Encriptação de código de programa 51 É um método comum para prevenir a análise estática. É realizado codificando o programa em algum ponto depois da compilação (antes de ser entregue ao cliente) e embutindo algum tipo de desencriptação codificada dentro do executável. Infelizmente, esta alternativa normalmente cria nada mais que uma inconveniência para o analista hábil, porque na maioria dos casos tudo que o desencriptador requer está no arquivo executável. 5.4.3.3 Ofuscação de Programa É um nome genérico para várias técnicas que são apontadas em reduzir a vulnerabilidade do programa a qualquer tipo de análise estática como o processo de engenharia reversa manual. Isto é realizado modificando o plano do programa, lógica, dados e organização, de certo modo isso mantém a funcionalidade idêntica, porém menos legível. Há muitas formas diferentes de obstrução, segundo Eilam, E. (2005). 5.4.3.4 Transformação de dados É uma técnica de ofuscação que focaliza a estrutura de dados do programa. Pois entender a estrutura de dados permite conhecer como ele trabalha. 5.4.3.5 Embutir código antidebugger É outra técnica comum de impedir a análise por programas de depuração para determinar detalhes. A idéia é intencionalmente executar operações que danificariam de alguma maneira ou incapacitariam um depurador de obter informações sobre o sistema. Algumas destas ações simplesmente envolvem a descoberta do depurador presente e encerrar o programa, enquanto outros envolvem meios mais sofisticados de interferir com depuradores no caso de um 52 usuário presente. Existem vários programas anti-depuração e muitos deles são específicos para cada plataforma. 5.4.3.6 Mudança no fluxo de controle São transformações que alteram a ordem e o fluxo de um programa com a finalidade de reduzir a legibilidade humana. O controle nas transformações de fluxo são categorizadas como transformações de computação, transformações de agregação, e ordenação. As transformações de computação são as que reduzem a legibilidade do código, modificando a estrutura de fluxo de controle original do programa, de modo que traga um programa funcionalmente equivalente, porém mais difícil de traduzir em linguagem de alto-nível. Isto pode ser feito removendo o controle da informação do fluxo do programa ou somando ao fluxo de controle novas declarações que complicam o programa e não podem ser traduzidas facilmente em linguagem de alto-nível. Transformações de agregação destroem a estrutura de alto-nível do programa quebrando as abstrações de alto-nível criadas pelo programador enquanto o programa estava sendo escrito. A idéia básica é quebrar tais abstrações assim que a organização de alto-nível do código ficar ilegível. Transformações de ordenação são transformações menos poderosas, pois reordenam a ordem das operações em um programa de forma que sua legibilidade seja reduzida. 53 6 FERRAMENTAS DE ENGENHARIA REVERSA 6.1 IDA Pro Fig. 3 – Software IDA Pro. IDA Pro (Interactive Disassembler) é um software avançado que tem como características: análise de código hostil, de vulnerabilidades, pesquisa e engenharia reversa. Possui multiprocessador, disassembler e depurador, funciona tanto em Windows quanto Linux. Suporta uma variedade de formatos de executáveis para diferentes processadores e sistemas operacionais. Ele também pode ser usado como um depurador PE para Windows, Mac OS X Mach-O, e Linux Executáveis ELF. Embora IDA desempenhe um elevado grau automático de análise de código, em certa medida, alavancando referências cruzadas entre código seções, o conhecimento dos parâmetros das chamadas API, e outras informações, é centrada no uso interativo. Um usuário terá início com uma lista gerada automaticamente da desmontagem e, em seguida, renomea, anota, ou adiciona informações à lista, até que se torne claro seu funcionamento, criando um modelo de engenharia reversa. Criado como uma aplicação shareware por Ilfak Guilfanov, foi mais tarde transformado em um produto comercial por DataRescue, uma empresa belga. 54 6.2 VB Decompiler Fig. 4 – Sofware VB Decompiler. VB Decompiler - é um descompilador de programas (executáveis, DLL ou OCX) criados em Visual Basic 6.0. Os programas criados pelo Visual Basic 6.0 podem ser compilados nas opções P-Code ou Codigo Nativo. Como a opção P-Code monta comandos de alto nível, existe a real possibilidade de descompilar um executável até chegar ao código fonte (mas como regra, nome de variáveis, funções, etc. não serão descompilados). VB Decompiler restaura muitas instruções em P-Code e fornece a opção de inseri-las em um projeto do Visual Basic, criado automaticamente. O descompilador irá simplificar o problema do aprendizado da análise algorítmica e parcialmente resgatar o código fonte original. Se um programa foi compilado em Código Nativo, a restauração das instruções de máquina iniciais não pode ser garantida. Mas nesse caso, VB-Decompiler pode também ajudar na análise do programa. Ele contém um poderoso "Disassembler" 55 que suporta comandos no Pentium Pro incluindo MMX e SSE e permite desmontar qualquer função. O software VB-Decompiler possui também um analisador de código que procura todas as chamadas de função API. Outro recurso disponível é a "Referência de Strings", uma lista com todos os textos contidos no executável (após a descompilação). Em geral, VB-Decompiler pode ser bastante útil na análise de programa e especialmente recomendado para os casos de perda do código fonte ou restauração parcial do projeto. Para iniciar a descompilação você precisa selecionar um programa (executável EXE, DLL ou OCX) clicando em Open VB program no menu File. Selecione a pasta que contém o arquivo executável e abra-o. O software confirmará a descompilação. Pressione SIM (YES) para iniciar a descompilação ou NAO (NO) para fazê-lo mais tarde. Se for selecionado <<NAO>>, o nome do arquivo será adicionado na caixa de texto FileName e quando for preciso descompilá-lo basta clicar no botão Decompile (Descompilar). Se for necessário gerar o projeto para Visual Basic 6.0 para facilitar a análise, será preciso selecionar o menu File, "Save decompiled project" e selecionar a pasta que conterá o novo projeto (incluindo funções e módulos), exceto arquivos forms frx que salvam os ícones de programas, objetos de formulários, gráficos e outros dados binários que serão gerados automaticamente. Para os programadores em Visual Basic, é possível proteger o seu código da descompilação. Para isso, VB-Descompiler contém um "obfuscator" que pode deletar o nome de todas as funções, forms, módulos e objetos, incluindo diferentes tipos de assinaturas no arquivo executável (EXE). Isso fará com que a análise de descompilação seja muito complicada. Outros descompiladores não conseguirão descompilar. Para adicionar esta proteção a seus programas, abra o seu programa com o VB-Descompiler e em seguida clique em Tools -> Obfuscate. Este procedimento é rápido e seguro, sendo extremamente recomendado como medida preventiva de segurança. 56 6.3 Understand for Delphi Fig. 5 – Software Understand for Delphi. Understand for Delphi é uma ferramenta que suporta todas as versões da linguagem Delphi e de Turbo Pascal da Borland. Também suporta a ISO 7185: 1990 (também conhecida como Unextended Pascal) com as extensões DEC Pascal. Também pode habilitar suporte para Ingres embedded SQL statements. O projeto do banco de dados é armazenado num formato binário proprietário. Este formato de arquivo utiliza o formato rede/objeto para otimizar seu desempenho. Understand for Delphi analisa seu software e cria um banco de dados que contém informação sobre as entidades e suas relações. O banco de dados permite várias janelas para facilitar sua utilização. Estas são divididas em quatro tipos: 57 • Informação: mostra tudo sobre uma determinada entidade. • Hierarquia: mostra o relacionamento entre as entidades. Cada janela demonstra um relacionamento (por exemplo, "chamadas") da entidade entre pais e filhos. • Estrutura: mostra a estrutura de qualquer entidade que é adicionada à estrutura do software (por exemplo, um pacote, função, procedimento, tarefa) • Espetáculo: mostra a origem de um determinado link. Possibilitando descobrir como a informação no código de fonte está relacionada ou ligada a outra informação. 6.4 MÉTODO FUSION-RE/I Fig. 6 – Visão geral do método FUSION-RE/I. Masiero, P. C. (1995). 58 O método de engenharia reversa Fusion-RE/1 (Fusion-Reverse Engineering / Interface) desenvolvido no ICMC-USP, baseia-se nos conceitos e idéias de engenharia reversa orientada a objetos, e fornece mecanismos para abstrair visões funcionais (modelos de análise de sistemas de acordo com o método Fusion de Coleman e outros (1996)) e visões estruturais partindo, inicialmente, dos aspectos operacionais e de dados disponíveis na interface usuário-computador. O método FUSION-RE/I é constituído de duas etapas: 1ª. Etapa – recuperar visões funcionais: parte-se de considerações “lógicas”, obtidas por meio de aspectos operacionais e visões de dados disponíveis via interface, para a recuperação de visões funcionais do sistema, ou seja, a abstração da funcionalidade do sistema, representada por meio dos modelos de ciclo de vida, de operações e de objetos do sistema. 2ª. Etapa – recuperar visões estruturais: parte-se de considerações “físicas”, obtidas por meio do código-fonte, e das visões funcionais, para a recuperação de visões estruturais do sistema, ou seja, a identificação dos procedimentos que implementam as operações da interface do sistema, representados através dos quadros de chamadas de procedimentos e de operações-procedimentos de implementação. Fig. 7 - Síntese do método de Engenharia Reversa FUSION-RE/I. 59 7 CONCLUSÃO A pesquisa teve por objetivo geral propor a utilização da engenharia reversa como ferramenta de proteção no desenvolvimento de software. A partir dos conceitos presentes, conclui-se que as técnicas adotadas atualmente, ao esquecerem o uso da segurança, permitem que os sistemas estejam acessíveis a todo tipo de contaminação. Para tal, através da revisão de literatura, demonstra que são fatores preponderantes, o uso de procedimentos que tornem o software confiável, seguro, disponível. Dentro desse escopo, quanto aos objetivos específicos, pode-se destacar a identificação dos conceitos de engenharia reversa e seus aspectos legais. O exame das falhas de segurança de software e suas conseqüências, entre outras. Bem como a apresentação das principais técnicas de engenharia reversa e de métodos de proteção utilizados segundo a bibliografia estudada. Porém, foram identificadas algumas dificuldades durante a elaboração desta pesquisa. Entre elas, citam-se a complexidade do assunto em relação às implicações legais do uso da engenharia reversa devido a sua interpretação e critérios de cada país; bem como da quantidade de ferramentas disponíveis para avaliação, nem sempre confiáveis ou de caráter acadêmico; além do grande volume de informações sobre o tema, às vezes conflitantes entre si. Portanto, o presente trabalho não tem a pretensão de esgotar o assunto, pois existe muito material pouco explorado, abordando um conjunto de processos específicos. Todavia, a generalização dos resultados não fica comprometida, devido às peculiaridades da pesquisa. Quanto às limitações da pesquisa, um aspecto a ser considerado diz respeito à metodologia utilizada, de revisão bibliográfica e testes de softwares, Para trabalhos posteriores, sugere-se a implementação do tema proposto, aplicando-o dentro dos outros contextos organizacionais e da utilização de metodologias de pesquisa complementares, entre elas a quantitativa. Sugerem-se pesquisas que identifiquem 60 as funções desempenhadas pouco estruturadas em uma organização e os fluxos de informação nem sempre claros e perceptíveis. Espera-se que, à medida que os desenvolvedores de software utilizem a engenharia reversa intensamente, investigações futuras possam analisar estes pontos, bem como estabelecer um referencial para a criação de legislação específica do assunto, além de determinar o impacto econômico do uso ilegal da engenharia reversa. 61 8 REFERENCIAS BIBLIOGRAFICAS ALBUQUERQUE, R; RIBEIRO, B. Segurança no Desenvolvimento de Software, Editora Campus, 2002. ANQUETIL, N., Engenharia Reversa, COOPE, UFRJ, Rio de Janeiro, 2002. ASSUNÇÃO, M.F.A., O guia do hacker brasileiro, 2001. BIGGERSTAFF, T. Design Recovery for Maintenance and Reuse. IEEE Computer, v.22, n.7, p-36-49, 1989. CHIKOFSKY, E.J.; CROSS II, J.H. Reverse Engineering and Design Recovery: A Taxonomy. IEEE Software, v.7, n.1, p.13-17, 1990. COLEMAN, D, et al. Desenvolvimento Orientado a Objetos: O Método Fusion. Ed. Campos, Rio de Janeiro, 1996. COSTA, R.M. Um método de engenharia reversa para auxiliar a manutenção de software. Dissertação (mestrado). ICMC-USP, São Carlos, 1997. DIFFIE, W.; HELLMAN, M. E. New Directions in Cryptography, IEEE Trans. on Inform. Theory, Vol IT-22, Novembro, 1976. DISNEI, Aplicações de Curvas Elípticas em Criptografia, III Seminário de Informática - Segurança da Informação, Instituto Metodista Bennett e Instituto Militar de Engenharia, 2002. EILAM, E., Reversing: Secrets of Reverse Engineering. Wiley Publishing, Inc., 2005. FELTRIM, V. D., Apoio à Documentação de Engenharia Reversa de Software por meio de Hipertextos, São Carlos, 1999. Dissertação (Mestrado em Ciências de Computação e Matemática Computacional) – Instituto de Ciências Matemáticas e de Computação – USP São Carlos. GARDNER, M. Mathematical Games: A New Kind of Cipher that Would Take Millions of Years to Break, Scientific American 237, Agosto, 1977. HARANDI, M.T.; NING, J.Q. Knowledge-base program analysis. IEEE Software, 7: p.74-81, 1990. HOWARD, M.; LEBLANC, D. Writing Secure Code, Microsoft Press, 2002. ISO JTC 1/SC 27 Commitee ISO/IEC 15408-1:1999 Information Technology – Security Techniques - Evaluation Criteria for IT Security - Part 1: Introduction @ General Model, ISO Online Catalogue, 1999. 62 JÚNIOR, J. B., Legislação aplicada à Informática: livro didático. Palhoça: UnisulVirtual, 2006. LEUTWYLER, K. Superhack: Forty Quadrillion Years Early, a 129-Digit Code is Broken, Scientific American 271, 1994. LIMA, A. P. Algoritmos de Chave Pública: Estado da Arte, III Seminário de Informática - Segurança da Informação, Instituto Metodista Bennett e Instituto Militar de Engenharia, 2002. LIPNER, S.; HOWARD, M. The Trustworthy Computing Security Development Lifecycle, IEEE Annual Computer Security Applications Conference, 2004. MASIERO, P. C.; Análise Orientada a Objetos: Uma Introdução ao Método Fusion. In: IX Simpósio Brasileiro de Engenharia de Software. Documento preparado como apoio ao tutorial homônimo, Recife, 1995. MORIMOTO, C.E., Dicionário Técnico de Informática, 3a. ed., 2005. (http://www.guiadohardware.net) OXFORD, Dictionary of Computing Oxford, University Press, 1986. PRESSMAN, R.S. Engenharia de Software. 3a. ed., Makron Books, 1995. RIVEST, R. L., SHAMIR, A.; ADLEMAN, L. On a Method for Obtaining Digital Signature and Public Key Cryptosystems, Commun, ACM, Vol 21, Fevereiro, 1978. RUFINO, N. M. O. Segurança Nacional - Técnicas e Ferramentas de Ataque e Defesa de Redes de Computadores, Novatec Editora, 2002. RUGABER, S. Program Comprehension for Reverse Engineering. In: AAAI Workshop on AI and Automated Program Understanding, San Jose, California, p.106-110. July 1992. (http://www.cc.gatech.edu/reverse/papers.html) SALEH, K.; BOUJARWAH, A. Communications Software Reverse Engineering: A Semi-Automatic approach. Information and Software Technology, Oxford, n.38, p.379-390, 1996. SOARES, L. F. G.; LEMOS, G.; COLCHER, S. Redes de Computadores - Das Lans, Mans e Wans às Redes ATM, Editora Campus, 1995. TANENBAUM, A. S. Computer Networks, Prentice Hall, 2a. Edição, 1989. TORRES, D. Segurança Máxima de Software, Brasport, 2003. VIEGA, J.; MESSIER, M. Secure Programming Cookbook for C and C++, O'Reilly, 2003. 63 VALE, J.; ULBRICH, H. C. Universidade H4CK3R, Digerati Books, 4a. Edição, 2004.