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.
Download

PortSP - XV Simpósio Brasileiro em Segurança da Informação e de