Um empacotador/protetor para binários de elfo x86-64 no Linux. O Kiteshield envolve binários de elfos com várias camadas de criptografia e as injeta com código de carregador que descriptografa, mapas e executa o binário empacotado inteiramente no espaço do usuário. Um mecanismo de tempo de execução baseado em PTRACE garante que apenas as funções na pilha de chamadas atuais sejam descriptografadas a qualquer momento e implementa adicionalmente uma variedade de técnicas de anti-fuga, a fim de tornar os binários empacotados o mais difícil possível de engenharia reversa. Os binários únicos e multithreaded são suportados.
Consulte as seções de arquitetura e layout da base de código abaixo para obter uma visão panorâmica de como o kiteshield funciona.
Kiteshield pretende ser um exercício acadêmico divertido em ofuscação binária, em vez de algo que pode ser usado no mundo real, dado o código -fonte e, portanto, como funciona, é público.
Nomeado para os escudos preferidos pelos normandos no século 11 (alternativamente: os kiteshields que são tão predominantes na antiga escolaridade).
Kiteshield exige que a biblioteca BitDefender Desmontagebler decodifique instruções no empacotador. É incluído como um submódulo no packer/bddisasm . Para construí -lo a partir de um clone novo, execute o seguinte (note que você precisará instalar o CMake):
git submodule update --init
cd packer/bddisasm
mkdir build
cd build
cmake ..
make
Agora você pode criar o Kiteshield no modo de liberação, executando make do diretório de nível superior. Como alternativa, você pode criar uma construção de depuração com make debug . As construções de depuração do KITESHIELD desativam todos os recursos anti-debugging e ativam o log de depuração muito detalhado do carregador.
Para depurar a funcionalidade anti-debuga real, você pode construir o Kiteshield no modo de depuração com funcionalidade anti-debugização ativada usando make debug-antidebug .
Para embalar um binário chamado program e produzir o binário embalado para packed.ks , execute:
./packer/kiteshield program packed.ks
agora pode ser packed.ks e deve ser funcionalmente equivalente ao program , mas criptografado e difícil de reverter engenheiro. Observe que, para que a criptografia da camada 2 seja aplicada, o binário de entrada não deve ser retirado, pois o Kiteshield se baseia na presença da tabela de símbolos. Na maioria das distritos Linux, os utilitários de sistemas padrão (por exemplo, /bin/ls ) são geralmente despojados.
No entanto, você ainda pode embalar binários despojados sem criptografia da camada 2 usando o sinalizador -n :
./packer/kiteshield -n program packed.ks
Isso produzirá um binário de saída embalado apenas com a criptografia da camada 1 e o motor de tempo de execução omitido.
Kiteshield é composto por duas partes separadas. O Packer é um aplicativo C regular que lê, instrumentos e criptografa binários de entrada. O carregador é um aplicativo C independente responsável pela função dinâmica de descriptografia e funcionalidade anti-debutação que é injetada em binários de entrada pelo empacotador. Ele recebe controle inicial do kernel, mapeia todos os segmentos apropriados do binário na memória (incluindo o ligante dinâmico, se aplicável), e sem o controle do aplicativo. O carregador também contém o mecanismo de tempo de execução, que descriptografa e criptografa dinamicamente as funções quando elas são inseridas e saíram em tempo de execução.
Como o carregador recebe controle inicial do kernel (ou seja, antes de qualquer biblioteca compartilhada ser mapeada pelo ligante dinâmico), ele não tem acesso ao LIBC e, portanto, toda a funcionalidade necessária fornecida pela LIBC é reimplementada no código do carregador.
O código Packer e Loader pode ser encontrado no packer/ e loader/ Diretórios, respectivamente. O código que é comum a ambos pode ser encontrado no common/ Directory. Uma visão geral de alto nível das partes importantes da base de código é a seguinte:
kiteshield
├── common # Code common to packer/loader
│ ├── include
│ │ ├── defs.h
│ │ ├── obfuscation.h
│ │ └── rc4.h
│ ├── obfuscation.c # Obfuscation utilities
│ └── rc4.c # RC4 stream cipher implementation
├── LICENSE
├── loader # Loader code
│ ├── anti_debug.c # Anti-debugging functionality
│ ├── bin_to_header.py # Script to "headerize" a compiled loader
│ ├── entry.S # Loader entry point code
│ ├── include
│ │ ├── anti_debug.h
│ │ ├── debug.h
│ │ ├── elf_auxv.h
│ │ ├── errno.h
│ │ ├── malloc.h
│ │ ├── obfuscated_strings.h # Generated file produced by string_obfuscation.py
│ │ ├── signal.h
│ │ ├── string.h
│ │ ├── syscalls.h
│ │ └── types.h
│ ├── link.lds # Custom linker script for building loader
│ ├── loader.c # Binary loading/mapping code (userspace exec)
│ ├── Makefile
│ ├── malloc.c # Freestanding malloc/free implementation
│ ├── runtime.c # Main body of runtime engine code
│ ├── string.c # String processing utilities (eg. strncat)
│ ├── string_obfuscation.py # String obfuscation helper script
│ ├── syscalls.c # System call implementations in inline assembly
│ └── test # Loader unit tests
│ ├── attounit.h
│ ├── Makefile
│ ├── test_main.c
│ ├── test_malloc.c
│ └── test_rc4.c
├── Makefile
├── packer # Packer code
│ ├── bddisasm # Bitdefender x86-64 disassembler library (submodule)
│ ├── elfutils.c # ELF binary reading/writing utilities
│ ├── include
│ │ └── elfutils.h
│ ├── kiteshield.c # Main body of packer code
│ └── Makefile
├── README.md
└── testing # Integration tests (see testing/README.md)
O Kiteshield envolve binários de elfo de entrada em duas (ou um, se estiver usando as camadas -n sinalizador) da criptografia RC4, de modo que o binário no disco seja bastante bem ofuscado. Essas camadas são despojadas em tempo de execução pelo carregador.
A primeira camada de criptografia (referida na base de código como a "camada externa") consiste em um único passe RC4 sobre todo o binário de entrada. Isso foi projetado principalmente para combater a análise estática. Devido à maneira como a tecla é desusada (que depende do código do carregador), a primeira camada de criptografia também verifica as somas do carregador antes de executar o binário empacotado, tornando o kiteshield resistente ao patch de código.
A segunda camada de criptografia (referida na base de código como a "camada interna") consiste na criptografia individual de quase todas as funções no binário de entrada (identificado através da tabela de símbolos em tempo de pacote). Um mecanismo de tempo de execução baseado em PTRACE é acionado em todas as entradas e saída de funções por meio da substituição das instruções de entrada de cada função e de todas as suas instruções de retorno com instruções int3 (que entregam um SIGTRAP quando executadas). Ao receber uma armadilha, o motor de tempo de execução procura a função atual e criptografa ou descriptografa -a, conforme necessário, de modo que apenas as funções na pilha de chamadas atuais sejam descriptografadas a qualquer momento.
Após remover a camada 1, o KITESHIELD reimplementa efetivamente o syscall exec no espaço dos usuários (consulte loader/loader.c mprotect Essa técnica é comumente referida na literatura como um " mmap Exec" ou "Userland Exec". Anexa à criança usando PTRACE).
Além da criptografia, o código do carregador da Kiteshield também contém vários recursos anti-desbotamento projetados para torná-lo o mais difícil possível de analisar um binário embalado em execução (consulte loader/anti_debug.c e loader/include/anti_debug.h ).
Para dar um exemplo concreto de Kiteshield em ação, considere o seguinte Programa Hello World, que faremos com o Kiteshield no modo de depuração.
#include <stdio.h>
int main ()
{
puts ( "Hello World!" );
return 0 ;
}Quando embalado no modo de depuração, o código do carregador em binários embalados registrará informações de depuração muito detalhadas. A seguir, o log de um binário empacotado correspondente ao programa acima. Foi anotado (e novas linhas adicionais adicionadas para maior clareza) para fornecer um exemplo concreto de Kiteshield em ação:
$ ./packed.ks
# Runtime startup
[kiteshield] starting ptrace runtime
[kiteshield] number of trap points: 5
[kiteshield] number of encrypted functions: 3
# List of points in memory that have been instrumented with an int3 instruction
[kiteshield] list of trap points:
[kiteshield] 8000011ac value: c3, type: ret, function: __libc_csu_init (#0)
[kiteshield] 800001150 value: 41, type: ent, function: __libc_csu_init (#0)
[kiteshield] 800001050 value: 31, type: ent, function: _start (#1)
[kiteshield] 80000114b value: c3, type: ret, function: main (#2)
[kiteshield] 800001135 value: 55, type: ent, function: main (#2)
# Runtime has started and is waiting on the packed app, begin mapping binary
# Stripping layer one encryption
[kiteshield] RC4 decrypting binary with key 85e19ad41fb8cc13e87e3cd1589a45fb
[kiteshield] decrypted 12336 bytes
# Mapping segments from packed binary program header table
[kiteshield] mapping LOAD section from packed binary at 800000000
[kiteshield] mapping LOAD section from packed binary at 800001000
[kiteshield] mapping LOAD section from packed binary at 800002000
[kiteshield] mapping LOAD section from packed binary at 800003000
# Mapping dynamic linker specified in INTERP header of packed binary
[kiteshield] mapping INTERP ELF at path /lib64/ld-linux-x86-64.so.2
[kiteshield] mapped LOAD section from fd at b00000000
[kiteshield] interpreter base address is b00000000
[kiteshield] mapped LOAD section from fd at b00001000
[kiteshield] mapped LOAD section from fd at b0001f000
[kiteshield] mapped extra space for static data (.bss) at b00029000 len 400
[kiteshield] mapped LOAD section from fd at b00027000
[kiteshield] binary base address is 800000000
# Modifying ELF auxiliary vector as needed for program execution
[kiteshield] taking 7ffff2997c60 as auxv start
[kiteshield] replaced auxv entry 9 with value 34359742544 (0x800001050)
[kiteshield] replaced auxv entry 3 with value 34359738432 (0x800000040)
[kiteshield] replaced auxv entry 7 with value 47244640256 (0xb00000000)
[kiteshield] replaced auxv entry 5 with value 11 (0xb)
[kiteshield] finished mapping binary into memory
[kiteshield] control will be passed to packed app at b00001090
# Mapping done
# Runtime attaches to the packed application with ptrace
[kiteshield] child is traced, handing control to packed binary
# Program is executing, functions are logged on entry/exit
[kiteshield] tid 13508: entering encrypted function _start decrypting with key 74c974f9d097e5a8c60049c3842c6110
[kiteshield] tid 13508: entering encrypted function __libc_csu_init decrypting with key 9124511baa87daa76c09b2426355dfca
[kiteshield] tid 13508: leaving function __libc_csu_init for address 7fa9a821d02a (no function record) via ret at 8000011ac
[kiteshield] tid 13508: entering encrypted function main decrypting with key 2f1c20e77ff3617b0b89a579669aba48
# Actual program output
Hello World!
# Packed application returns from main() and exits
[kiteshield] tid 13508: leaving function main for address 7fa9a821d09b (no function record) via ret at 80000114b
[kiteshield] tid 13508: exited with status 0
[kiteshield] all threads exited, exiting
O Kiteshield possui um extenso conjunto de testes de integração para verificar a correção em várias plataformas diferentes. Consulte testing/README.md para obter detalhes.
Kiteshield foi escrito com ofuscação em mente, não velocidade. A captura do tempo de execução do Kiteshield em todas as entradas e saída de funções é muito caro. Os programas repletos de criptografia da Camada 2 devem esperar um desempenho sério.
MIT © Rhys Rustad-Elliott