PortSP: Uma API para Técnicas de Proteção de Software Portáveis Mateus Gregorio1, Davidson Boccardo2, Rafael Costa1,2, Luci Pirmez1,2 1 2 Universidade Federal do Rio de Janeiro (UFRJ) Rio de Janeiro, RJ – Brasil Instituto Nacional de Metrologia, Qualidade e Tecnologia (INMETRO) Duque de Caxias, RJ – Brasil [email protected], [email protected], [email protected], [email protected] Abstract. Software protection techniques, used to protect programs against Man-At-The-End attacks (MATE), can be expressed as a sequence of code transformations. The implementation of such transformations for low-level code produces non-portable protection solutions, requiring reimplementation for each different architecture that the software is compiled. We propose the creation of a programming interface that allows the manipulation of assembly code in a transparent and portable manner, ensuring the implementation of software protection independent of the architecture. Resumo. Técnicas de proteção de software, utilizadas para proteger programas contra ataques Man-At-The-End (MATE), podem ser expressas como uma sequência de transformações de código. A implementação de tais transformações para código de baixo nível produz soluções de proteção não portáveis, tornando-se necessário reimplementar as técnicas de proteção para cada nova arquitetura que o software for compilado. Propomos a criação de uma interface de programação que possibilite a manipulação de código assembly de maneira transparente e portável, assegurando a implementação de proteção de software independente da arquitetura. 1. Introdução Ano após ano, a indústria de software vem sofrendo enormes prejuízos devido a pirataria de software. Segundo pesquisa realizada pela Business Software Alliance [BSA 2014], 43% do software instalado em computadores pessoais no mundo inteiro em 2013 não era licenciado, o que representou um prejuízo de 62,7 bilhões de dólares. O estudo mostra ainda que houve um aumento na porcentagem de softwares não licenciados instalados em relação ao estudo global anterior, realizado em 2011. Além de prejuízos financeiros para os fabricantes de software, que deixam de receber o valor comercial das licenças de uso pirateadas, violações de softwares podem trazer outros prejuízos ainda mais críticos em alguns casos, não só para os fabricantes de software como também para as organizações que os utilizam. Um atacante poderia tentar violar um software com o objetivo de: furtar propriedade intelectual, como algoritmos que representam vantagem competitiva em relação a empresas concorrentes, arquitetura e o projeto do software; furtar dados sensíveis contidos no programa, como chaves criptográficas e número serial; adulterar o funcionamento do software a fim de obter vantagens pessoais, como burlar a verificação de licença de uso; e identificar vulnerabilidades no software para posteriormente explorá-las. Ataques desse tipo são denominados ataques Man-At-The-End (MATE) [Akhunzada et al. 2015]. Métodos de proteção contra ataques MATE são conhecidos como técnicas anti-manipulação, proteção à propriedade intelectual ou, mais comumente, técnicas de proteção de software [Falcarin et al. 2011]. Dentre as principais técnicas de proteção de software, destacam-se as técnicas de ofuscação, marca d’água e incorruptibilidade (ou tamper-proofing) [Collberg e Thomborson 2002]. Técnicas de ofuscação de código dificultam a engenharia reversa através de modificações sintáticas que aumentam o grau de ininteligibilidade do código, mas mantém a sua semântica original. Técnicas de marca d’água agem como uma defesa contra pirataria de software, através da inserção de informações no código do programa que permitam rastrear sua autoria ou propriedade. Por fim, técnicas de incorruptibilidade asseguram que o software executa como esperado, através da adição de código auto verificável capaz de detectar tentativas de modificação ou monitoração do software e coibir a sua continuidade. Observa-se que as técnicas de proteção de software consistem em um conjunto de transformações aplicadas diretamente sobre o código do programa, visando protegêlo contra ataques de adversários [Boccardo et al. 2010]. Além disso, verificamos que tais técnicas utilizam-se de uma base comum de transformações de código para serem construídas. Por exemplo, em muitas técnicas de proteção de software, sejam elas de ofuscação, marca d’água ou incorruptibilidade, é perfeitamente usual a execução de transformações como: inserir novos trechos de código, substituir determinados trechos de código por outros equivalentes, remover e reordenar trechos de código. Desenvolver técnicas de proteção de software compostas por transformações de código de baixo nível mostra-se um desafio em relação a portabilidade da solução. Código de baixo nível é, por definição, um código específico para lidar com as características da arquitetura para a qual foi projetado. Logo, as transformações de código aplicadas sobre código de baixo nível e, consequentemente, as técnicas de proteção compostas por elas, tornam-se dependentes da arquitetura. Isso obriga desenvolvedores a sempre reimplementarem as técnicas de proteção de acordo com a arquitetura para a qual o software a ser protegido foi compilado. Este trabalho propõe a criação de uma API (Application Programming Interface), chamada PortSP, que possibilite a implementação de técnicas, bem como a construção de programas de proteção de software, capazes de serem utilizados para proteger softwares compilados para diferentes arquiteturas de hardware. Para tanto, a API proposta fornece um conjunto de funções básicas que permitem a execução de transformações de código assembly de diferentes arquiteturas, de maneira que bastará a um desenvolvedor simplesmente combinar o uso de tais funções para implementar técnicas e construir programas de proteção de software independentes de arquitetura. O restante deste artigo está organizado da seguinte forma: a Seção 2 apresenta trabalhos relacionados; a Seção 3 e suas subseções apresentam as premissas e abstrações utilizadas no projeto e implementação da API PortSP; a Seção 4 apresenta os resultados obtidos através da utilização de um protótipo da API e, finalmente, a Seção 5 apresenta as conclusões e trabalhos futuros. 2. Trabalhos Relacionados Encontramos muitas ferramentas comerciais para proteção de software na internet, dentre elas, diversos ofuscadores de código fonte escrito em linguagem de alto nível [Semantic Designs, Stunnix, MTC Group, Gapotchenko] e ferramentas de proteção mais completas que combinam o uso de técnicas de ofuscação e incorruptibilidade [GuardSquare] e protegem softwares contra pirataria, manipulação e engenharia reversa (possivelmente combinando o uso de técnicas de marca d’água, incorruptibilidade e ofuscação) [Sciensoft Systems]. Porém, todas elas possuem foco diferente deste trabalho, pois são ferramentas de código fechado que oferecem métodos prontos para proteção de software, enquanto o intento de nossa proposta é o de facilitar a implementação de técnicas e a construção de programas para proteção de software por desenvolvedores e pesquisadores da área de segurança. Uma área de pesquisa que está indiretamente relacionada a este trabalho é a área de análise de malware (malicious software). Analistas de malware estudam técnicas de ofuscação de código, código auto modificável, dentre outras técnicas utilizadas por desenvolvedores de malwares para camuflar seus programas maliciosos diante de escaneamentos realizados por programas antivírus. Nesse contexto, Jain [Jain 2012] apresenta um transformador de código assembly que aplica técnicas de ofuscação de código sobre malwares, com o objetivo de criar variantes de malwares não identificáveis por programas antivírus e computar o grau de similaridade entre o malware original e suas variantes produzidas. Por fim, Buck e Hollingsworth [Buck e Hollingsworth 2000] propõem uma API para patching de programas em tempo de execução, que permite a instrumentação e modificação de programas durante a sua execução. Essa API permite a implementação de programas de instrumentação de binários independentes de máquina, tendo sido concebida, primariamente, para facilitar a construção de ferramentas de depuração (debugging) de código. 3. A API PortSP Esta Seção dedica-se a apresentar as premissas e abstrações utilizadas, o projeto e a implementação da API PortSP, e está organizada da seguinte forma: a subseção 3.1 apresenta uma breve discussão sobre a escolha do nível de aplicação das transformações de código; a subseção 3.2 expõe características importantes da linguagem assembly para a API; a subseção 3.3 apresenta as especificações da API e, finalmente, a subseção 3.4 fornece uma descrição em alto nível da implementação de um protótipo da API. 3.1. Escolhendo o Nível de Aplicação das Transformações As transformações de código podem ser aplicadas tanto em código de alto nível (código fonte) quanto em código de baixo nível (código assembly ou binário). Transformações aplicadas sobre código de alto nível (código fonte) ocorrem antes do processo de compilação, visando gerar um binário mais complexo. Lidar com código de alto nível facilita a aplicação das transformações de código, dada a facilidade de compreensão e escrita em relação a códigos de nível mais baixo. Além disso, os trechos de código inseridos pelas transformações passarão pelo processo de otimização do compilador, gerando código binário mais eficiente. Já as transformações de código aplicadas sobre código de baixo nível (assembly ou binário) ocorrem após o processo de compilação. É necessário, nesse caso, lidar com código dependente de plataforma e de compreensão e escrita mais difícil, o que dificulta a aplicação das transformações. A vantagem de trabalhar em nível mais baixo está na possibilidade de aplicação de transformações de código mais granulares, diretamente sobre instruções de máquina. Isso amplia o leque de transformações de código que podem ser aplicadas sobre o programa, sendo bastante útil, por exemplo, na proteção contra engenharia reversa. Optamos então pela aplicação de transformações sobre código de baixo nível escrito em linguagem assembly, a fim de prover funções que permitam a execução de transformações de código mais granulares, sobre instruções de máquina. A escolha pelo código assembly foi feita para amenizar parte da dificuldade de compreensão e escrita de código, que seria ainda maior ao lidar diretamente com código binário. 3.2. Destrinchando Códigos Assembly A linguagem assembly nada mais é do que uma linguagem simbólica, utilizada para representar de maneira mais legível as instruções de máquina dos processadores de uma determinada arquitetura. Segundo Blum [Blum 2005], todo programa escrito em linguagem assembly consiste de três componentes que são utilizados para definir as operações do programa: mnemônicos de códigos de operação, seções de dados e diretivas. Infelizmente, diferente de outras linguagens de programação, não existe um formato padrão utilizado por todos os montadores (assemblers) – programas que transformam o código assembly em código de máquina –, de forma que cada montador utiliza formatos ligeiramente distintos para representar códigos de operação, dados e diretivas. Além dos três componentes utilizados para definir operações nos programas, montadores também fazem uso de outros elementos para a composição de programas em linguagem assembly. Por exemplo, é comum à diversas arquiteturas de hardware o uso de registradores e de pelo menos um segmento de pilha para o armazenamento de dados. A utilização de rótulos (labels) para referenciar posições de memória também é uma prática muito utilizada por diversos montadores. O que pode variar de um montador para outro é a sintaxe utilizada para se referir a tais elementos, bem como as características dos elementos que são inerentes à arquitetura de hardware para a qual o código assembly é gerado, como é o caso, por exemplo, do conjunto de registradores e do segmento de pilha, que podem variar em quantidade e tamanho (largura em bits). 3.3. Especificação da API A API PortSP provê uma interface de programação com funções básicas que facilitam a escrita de transformações de código assembly. Funções que permitem encontrar, inserir, remover e substituir instruções e rótulos, identificar e reordenar blocos básicos, por exemplo, facilitam a manipulação do código assembly de tal forma, que basta combinar o uso dessas funções para implementar diversas transformações de código utilizadas nas técnicas de ofuscação, marca d’água e incorruptibilidade de software. O esquema da Figura 1 apresenta a ideia geral da API e algumas das funções que compõem a sua interface de programação. Figura 1. Ideia geral da API PortSP e algumas de suas funções. A fim de atender ao requisito de portabilidade das transformações de código, foi idealizada uma camada de abstração que permite lidar com instruções e elementos da linguagem assembly de múltiplas arquiteturas, de forma transparente e portável. Essa camada é composta por instruções virtuais – formadas por operações e elementos –, bem como informações específicas sobre cada arquitetura suportada pela API, de forma que as instruções virtuais são mapeadas para as instruções correspondentes na arquitetura do software alvo das transformações de código. O esquema apresentado na Figura 2 ilustra de forma simplificada a estrutura interna de funcionamento da API. Figura 2. Estrutura interna de funcionamento da API PortSP As operações que compõem as instruções virtuais consistem basicamente em um conjunto de expressões que representam, de forma genérica, mnemônicos de instruções assembly de múltiplas arquiteturas. Foram definidas operações para representar os mnemônicos de instruções comumente utilizadas em técnicas de proteção de software, pertencentes aos seguintes grupos de instruções: instruções de movimentação de dados, instruções lógicas e aritméticas e instruções de desvio de fluxo. A Tabela 1 apresenta algumas das operações definidas, acompanhadas de uma breve descrição. Já os elementos são uma forma genérica de se referir a outros recursos existentes na arquitetura alvo que são utilizados pelas linguagens assembly na composição dos programas. Em geral, os elementos são utilizados em conjunto com as operações, fazendo o papel dos operandos de instruções assembly nas instruções virtuais. No caso de registradores e rótulos, por exemplo, a API fornece funções que proveem tais elementos dinamicamente, tomando como base as informações especificadas para a arquitetura alvo. Um registrador é provido ao usuário por uma função que escolhe um registrador que não tenha sido fornecido previamente, dentre o conjunto de registradores de uso geral especificados para a arquitetura. Fazemos a ressalva de que o estado do registrador fornecido deverá ser salvo antes do seu uso e restaurado após ele, a fim de evitar que o programa quebre durante a execução ou tenha a sua semântica alterada. Já um rótulo é gerado dinamicamente de acordo com as regras de nomenclatura de nomes especificadas para a arquitetura. Tabela 1. Algumas das operações definidas que compõem a camada de abstração da API PortSP. Operação MOVE PUSH_ONTO_STACK EXCLUSIVE_OR AND ADD JUMP_IF_ZERO Descrição Movimenta dados entre registradores e memória. Coloca um valor na pilha. Ou exclusivo lógico. E lógico. Soma dois valores. Desvia o fluxo para o endereço apontado pelo operando se a flag zero está ativada. Por fim, a validação e tradução das instruções virtuais para as instruções correspondentes na arquitetura alvo são feitas por meio de mapeamentos pré-definidos. Para cada arquitetura suportada, são estabelecidos mapeamentos 1 para N das operações para as instruções assembly correspondentes na arquitetura alvo. A cardinalidade é de 1 para N, pois podem existir variantes de instruções com o mesmo objetivo definido pela operação. Nesse caso, o mapeamento é feito em ordem prioritária, de forma que as funções para inserir instruções no código assembly utilizem por padrão a primeira variante mapeada, ao passo que as funções de busca, substituição e remoção devem considerar todas as variantes possíveis. Cada mapeamento é composto pelos seguintes itens: operação da instrução virtual, mnemônico da instrução assembly, tipos dos operandos aceitos pela instrução assembly e sintaxe de escrita da instrução assembly. Para que a API não venha a inserir instruções assembly sintaticamente inválidas, ou com a semântica incorreta, no programa alvo das transformações de código, é fundamental que as informações sobre os itens que compõem os mapeamentos das instruções virtuais para as instruções assembly sejam inseridas corretamente em cada mapeamento. 3.4. Implementação Inicialmente, foram implementadas funções base para a execução de transformações de código que permitem a implementação de algumas técnicas de ofuscação de software. O protótipo desenvolvido é capaz de lidar com códigos assembly gerados pelo montador GNU Assembler [Free Software 2014], para as arquiteturas x86 e ARM, utilizando a sintaxe de instruções INTEL. Parte da camada de abstração da API foi implementada utilizando arquivos XML, visando prover extensibilidade às abstrações e facilitar o suporte a novas arquiteturas. Um único arquivo XML é utilizado para definir as operações genéricas que compõem as instruções virtuais, enquanto dois arquivos XML são utilizados para cada arquitetura, sendo um deles para especificar informações sobre o conjunto de registradores, regras de geração de rótulos e características dos demais elementos disponíveis na arquitetura, e outro para especificar o mapeamento das operações para as instruções assembly da arquitetura. Já as interfaces de programação foram implementadas utilizando a linguagem de programação C++, com o intuito de, futuramente, disponibilizar a API PortSP como uma biblioteca dessa linguagem. A Figura 3 apresenta um diagrama com as classes que compõem a API. Figura 3. Diagrama de classes da API PortSP A seguir, damos uma breve descrição das principais classes da API. AssemblyManipulator é a classe principal da API e a única que deve ser utilizada diretamente pelo programador. Essa classe concentra todas as funções que o programador necessita para implementar as transformações de código. Contém as funções para encontrar, inserir, remover e substituir instruções e rótulos, além das funções para prover registradores e rótulos dinamicamente. As funções acionam métodos das demais classes a seguir para prover tais funcionalidades, fazendo da classe AssemblyManipulator uma interface centralizadora das funções providas pela API. VirtualInstructionMapper é a classe responsável por fazer o mapeamento das instruções virtuais utilizadas pelo programador para a(s) instrução(ões) assembly correspondente(s), de acordo com a arquitetura do programa a ser modificado. Essa classe utiliza o arquivo XML com os mapeamentos das operações para as instruções assembly a fim de compor a instrução assembly final, que deverá ser formada obrigatoriamente por um mnemônico e, opcionalmente, por operandos, e estar escrita na sintaxe correta, de acordo com as especificações da arquitetura alvo. AssemblyCodeEditor é a classe que efetivamente aplica as transformações de código sobre o arquivo assembly. As funções dessa classe recebem as instruções assembly em seu formato final (após o mapeamento) e executam as transformações de código acionadas. 4. Resultados A fim de avaliar o protótipo criado, realizamos um experimento que consiste em compilar um mesmo programa para duas arquiteturas diferentes, utilizar um programa de proteção implementado a partir da API PortSP para aplicar técnicas de ofuscação de código sobre ambas as versões compiladas e, por fim, gerar os binários correspondentes para cada arquitetura a partir dos programas ofuscados e executá-los, a fim de avaliar se nenhuma das instruções inseridas quebrou o programa ou alterou a sua semântica. O programa de proteção implementado aplica conjuntamente duas técnicas de ofuscação de código apresentadas por Branco [Branco 2012]. A primeira técnica, chamada de “Substituição de Instrução”, consiste em trocar uma instrução assembly (ou um conjunto delas) por outras semanticamente equivalentes. Já a segunda, denominada “Inserção de Código Espúrio”, consiste em inserir instruções que nunca serão executadas pelo programa. As Figuras 4 e 5 apresentam o código de implementação do programa de proteção utilizando a API PortSP. Entrada: Programa p escrito em assembly, Arquitetura a para a qual p foi compilado Saída: Programa p’ modificado após aplicação das técnicas de ofuscação 1 void main( int argc, char* argv[] ) { 2 AssemblyManipulator am = new AssemblyManipulator( p, a ); 3 fakeCodeInsertion( am, “JUMP” ); 4 instructionSubstitution( am, “JUMP” ); 5 } Figura 4. Função principal que faz uso das técnicas de ofuscação de código Como alvo do programa de proteção, implementamos um programa gerador de matrizes e o compilamos para as arquiteturas x86 de 64 bits (x86_64) e ARM de 32 bits (ARMv5TEJ) [ARM 2005], gerando, respectivamente, os programas matriz_x86.s e matriz_arm.s escritos em linguagem assembly. Executamos o programa de proteção duas vezes, uma vez para cada programa a ser protegido, sendo que, a cada execução, foram informados dois parâmetros de entrada: o programa p alvo da proteção (escrito em assembly) e a arquitetura a para a qual o programa foi compilado. Na primeira execução, os parâmetros informados foram p = matriz_x86.s e a = x86_64. Já na segunda execução, os parâmetros informados foram p = matriz_arm.s e a = ARMv5TEJ. 1 void instructionSubstitution( AssemblyManipulator& am, string instruction ) { 1 void fakeCodeInsertion( AssemblyManipulator& am, string 2 size_t pos = am.find_instruction( instruction ); 2 size_t pos = am.find_instruction(instruction_after_fake_code); 3 while( pos ) { 3 while( pos ) { instruction_after_fake_code ) { 4 am.remove_instruction( pos ); 4 string reg_xor = am.giveMeARegister(); 5 string reg_xor = am.giveMeARegister(); 5 string reg_mov = am.giveMeARegister(); 6 am.insert_instruction( “EXCLUSIVE_OR”, reg_xor, reg_xor ); 6 string rotulo_destino = am.giveMeALabel(); 7 string rotulo_fake = am.giveMeALabel(); 8 string immediate = am.giveMeAnImmediate( 40 ); 9 am.insert_instruction( “EXCLUSIVE_OR”, reg_xor, reg_xor ); am.insert_label( rotulo ); 10 am.insert_instruction( “JUMP_IF_NOT_ZERO”, rotulo_fake ); pos = am.find_instruction( instruction ); 11 am.insert_instruction( “JUMP”, rotulo_destino ); 11 } 12 am.insert_label( rotulo_fake ); 12 } 13 am.insert_instruction( “ADD”, reg_xor, immediate ); 14 am.insert_instruction( “MOVE”, reg_mov, reg_xor ); 15 am.insert_instruction( “RETURN_FROM_FUNCTION” ); 16 am.insert_label( rotulo_destino ); 17 pos = am.find_instruction( instruction_after_fake_code ); 7 string rotulo = am.giveMeALabel(); 8 am.insert_instruction( “JUMP_IF_ZERO”, rotulo ); 9 10 18 } 19 } (a) (b) Figura 5. Implementação das técnicas de ofuscação utilizando a API PortSP: substituição de instrução (a) e inserção de código espúrio (b). A Figura 6 apresenta a comparação entre os trechos de código dos programas compilados para as arquiteturas x86_64 e ARMv5TEJ, antes e após a aplicação das técnicas de ofuscação. 1 call time 1 call time 1 bl 1 bl 2 mov edi, eax 2 mov edi, eax 2 mov r3, r0 2 mov r3, r0 3 call srand 3 call srand 3 mov r0, r3 3 mov r0, r3 4 mov DWORD PTR [rbp-8], 0 4 mov DWORD PTR [rbp-8], 0 4 bl srand 4 bl 5 mov r3, #0 5 mov r3, #0 jmp .L2 5 xor ecx, ecx 6 str r3, [fp, #-16] 6 str 6 jnz 7 b .L2 7 eors r1, r1, r1 7 jmp LBL1 5 LBL2 time srand r3, [fp, #-16] 8 bne LBL2 8 LBL2: 9 b 9 add ecx, 40 10 LBL2: 10 mov eax, ecx 11 adds 11 ret 12 movs r7, r1 13 movs pc, lr 12 LBL1: (a) time LBL1 r1, r1, #40 13 xor ebx, ebx 14 LBL1: 14 jz .L2 15 eors r3, r3 16 beq .L2 (b) (c) (d) Figura 6. Trechos de código do programa compilado para x86 e ARM antes (a) (c) e após (b) (d) a aplicação das técnicas de ofuscação, respectivamente. Para finalizar o experimento, geramos os binários a partir das versões ofuscadas dos programas e observamos que ambos executaram normalmente, apresentando o mesmo funcionamento do programa original. 5. Conclusão Neste trabalho, apresentamos o projeto e a construção de um protótipo da API PortSP para facilitar a implementação de técnicas de proteção de software em contextos de diferentes arquiteturas. Os resultados obtidos por meio da utilização de um protótipo da API foram considerados satisfatórios, uma vez que foi possível, com poucas linhas de código, implementar técnicas de proteção para proteger softwares compilados para arquiteturas distintas e, de forma extremamente simples, aplicá-las sobre versões de um programa compiladas para arquiteturas distintas. Em trabalhos futuros, pretendemos evoluir o protótipo para dar suporte à arquitetura AVR e permitir a implementação de técnicas de marca d’água e incorruptibilidade. Referências Akhunzada, A., Sookhak, M., Anuar, N., Gani, A., Ahmed, E., Shiraz, M., Furnell, S., Hayat, A. and Khan, M. (2015). “Man-At-The-End Attacks: Analysis, Taxonomy, Human Aspects, Motivation and Future Directions”, Journal of Network and Computer Applications, vol. 48, p. 44-57. ARM LTD (2005). “ARM Architecture Reference Manual (ARM DDI 0100I)”, https://silver.arm.com/download/ARM_Architecture/AR550-DA-70002-r0p000rel0/DDI%2001001.pdf, Julho. Blum, Richard (2005), “Professional Assembly Language”, Wrox, 1st edition. Boccardo, D. R., Machado, R. C. S., Carmo, L. F. R. C. (2010). “Transformações de código para proteção de software”, X Simpósio Brasileiro em Segurança da Informação e de Sistemas Computacionais, Minicursos, cap. 3, p. 103-148. Branco, R. R., Barbosa, G. N., Neto, P. D. (2012). “Scientific but Not Academical Overview of Malware Anti-Debugging, Anti-Disassembly and Anti-VM Technologies”, Black Hat USA. Buck, B., Hollingsworth, J. K. (2000). “An API for Runtime Code Patching”, International Journal of High Performance Computing Applications, vol. 14(4), p. 317-329. BSA, Software Alliance (2014). “BSA Global Software Survey: The Compliance Gap”, http://globalstudy.bsa.org/2013/downloads/studies/2013GlobalSurvey_Study_en.pdf, Julho. Collberg, C. and Thomborson, C. (2002). “Watermarking, Tamper-Proofing, and Obfuscation – Tools for Software Protection”, IEEE Transactions on Software Engineering, vol. 28, no. 8, p. 735-746. Falcarin, P., Collberg, C., Atallah, M., Jakubowski, M. (2011). "Software Protection", Guest Editors' Introduction, IEEE Software, vol. 28(2), p. 24-27. Free Software Foundation, Inc. (2014). “Using as – The GNU Assembler, version 2.25”, https://www.sourceware.org/binutils/docs-2.25/as/index.html, Julho. Gapotchenko (2015). “Eazfuscator.NET http://www.gapotchenko.com/eazfuscator.net, Julho. Version 4.9”, GuardSquare NV (2015). “DexGuard”, https://www.guardsquare.com/dexguard, Julho. Jain, S. (2012). “Malware Obfuscator for Malicious Executables”, Global Trends in Information Systems and Software Applications: 4th International Conference, ObCom 2011, Part II, p. 461-469. MTC Group LTD (2012). “Morpher 2.0”, http://morpher.com, Julho. Sciensoft Systems (2015). http://www.sciensoft.com/products/eleckey, Julho. “ElecKey 2.0.8.30”, Semantic Designs, Inc. (2015). “Thicket™ Family of Source Code Obfuscators”, http://www.semdesigns.com/Products/Obfuscators/index.html, Julho. Stunnix (2015). “C++ Obfuscator”, http://stunnix.com, Julho.