Ein Packer/Protektor für X86-64-ELF-Binärdateien unter Linux. Kiteshield wickelt Elf -Binärdateien mit mehreren Verschlüsselungsschichten ein und injiziert sie mit Loader -Code, die die vollgepackte Binärdehnung vollständig im UserSpace entschlüsselt, karten und ausführt. Eine Ptrace-basierte Laufzeit-Engine stellt sicher, dass nur Funktionen im aktuellen Call-Stack zu einem bestimmten Zeitpunkt entschlüsselt werden und zusätzlich eine Vielzahl von Anti-Debugging-Techniken implementieren, um gepackte Binärdateien so schwer wie möglich umzukehren. Sowohl Einzel- als auch Multithread -Binärdateien werden unterstützt.
In den Abschnitten Architektur- und Codebasis-Layouts finden Sie eine Vogelperspektive, wie Kiteshield funktioniert.
Kiteshield soll eher eine unterhaltsame akademische Übung in der binären Verschleierung sein als etwas, das in der realen Welt angesichts des Quellcodes verwendet werden kann und wie es funktioniert, ist öffentlich.
Benannt nach den Schildern, die von den Normannen im 11. Jahrhundert bevorzugt wurden (alternativ: die Kiteshields, die in Runenlandschaft der alten Schule so weit verbreitet sind).
Kiteshield benötigt die Bitdefender -Disassembler -Bibliothek, um Anweisungen im Packer zu dekodieren. Es ist als Submodul bei packer/bddisasm enthalten. Um es aus einem frischen Klon zu bauen, führen Sie Folgendes aus (beachten Sie, dass CMake installiert werden muss):
git submodule update --init
cd packer/bddisasm
mkdir build
cd build
cmake ..
make
Sie können jetzt Kiteshield im Release -Modus erstellen, indem Sie aus dem Verzeichnis der obersten Ebene make . Alternativ können Sie mit make debug einen Debug -Build erstellen. Debug-Builds von Kiteshield deaktivieren alle Anti-Debugging-Funktionen und schalten Sie die ausführliche Debug-Protokollierung des Laders ein.
Um die tatsächliche Anti-Debugging-Funktionalität zu debuggen, können Sie Kiteshield im Debug-Modus mit Anti-Debugging-Funktionalität mithilfe von make debug-antidebug erstellen.
Um ein binäres program zu packen und die gepackte Binärdehnung für packed.ks Ausgaben auszugeben.
./packer/kiteshield program packed.ks
packed.ks können jetzt ausgeführt werden und sollten funktionell gleichwertig mit program entsprechen, aber verschlüsselt und schwer zu dem Ingenieur umkehren. Beachten Sie, dass die Eingangsbinärdatei für die Verschlüsselung der Schicht 2 nicht als Kiteshield auf die vorhandene Symboltabelle abhängt. Bei den meisten Linux -Distributionen werden im Allgemeinen Standard -System -Dienstprogramme (z. B. /bin/ls ) gestrichen.
Sie können jedoch gestreifte Binärdateien ohne Schicht 2 mit dem -n -Flag gepackt:
./packer/kiteshield -n program packed.ks
Dadurch werden ein aus der Layer 1 -Verschlüsselung gepacktes Ausgangsbinär gepackt und der Laufzeitmotor weggelassen.
Kiteshield besteht aus zwei getrennten Teilen. Der Packer ist eine reguläre C -Anwendung, die Instrumente, Instrumente und verschlüsselt Eingabebinärdateien. Der Lader ist eine freistehende C-Anwendung, die für die Entschlüsselung der dynamischen Funktion und die Anti-Debugging-Funktionalität verantwortlich ist, die vom Packer in Eingangsbinärdateien injiziert wird. Es empfängt eine anfängliche Kontrolle vom Kernel, zeichnet alle geeigneten Segmente des Binärs in den Speicher (einschließlich des dynamischen Linkers) ab und übergibt die Kontrolle über die Anwendung. Der Lader enthält auch die Laufzeit -Engine, die die Funktionen dynamisch entschlüsselt und verschlüsselt, wenn sie zur Laufzeit eingegeben und verlassen werden.
Da der Loader eine anfängliche Steuerung vom Kernel erhält (dh, bevor alle gemeinsam genutzten Bibliotheken normalerweise vom dynamischen Linker abgebildet werden), wird kein Zugriff auf LIBC und somit alle benötigten Funktionen, die von LIBC bereitgestellt werden, im Loader-Code neu implementiert.
Packer- und Loader -Code finden Sie im packer/ bzw. loader/ Verzeichnissen. Code, der beides gemeinsam ist, findet sich im common/ Verzeichnis. Ein hochrangiger Überblick über die wichtigen Teile der Codebasis lautet wie folgt:
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)
Kiteshield wickelt Eingangs -Elf -Binärdateien in zwei (oder einer, wenn sie die -n -Flag -) -Schulen der RC4 -Verschlüsselung verwenden, so dass die Binärdatei auf der Festplatte ziemlich gut verschleiert wird. Diese Schichten werden zur Laufzeit vom Lader abgezogen.
Die erste Verschlüsselungsschicht (in der Codebasis als "äußere Schicht" bezeichnet) besteht aus einem einzelnen RC4 -Pass über den gesamten Eingangsbinär. Dies ist in erster Linie zur Bekämpfung der statischen Analyse konzipiert. Aufgrund der Art und Weise, wie der Schlüssel deobfusziert ist (was vom Loadercode abhängt), überprüft die erste Verschlüsselungsschicht auch den Loader -Code effektiv, bevor die verpackte Binärdatei ausgeführt wird, wodurch Kiteshield resistent gegen das Code -Patching ist.
Die zweite Verschlüsselungsschicht (in der Codebasis als "innere Schicht" bezeichnet) besteht aus der individuellen Verschlüsselung fast jeder Funktion in der Eingabe-Binärdatei (über die Symboltabelle zur Packzeit identifiziert). Ein Ptrace-basiertes Laufzeit-Engine wird bei jedem Funktionseintrag und dem Beenden durch den Austausch der Eintragsbefehl der einzelnen Funktionen und alle Rückgabeanweisungen mit int3 Anweisungen (die bei der Ausführung eine SIGTRAP liefern) ausgelöst. Nach Erhalt einer Falle schaut die Laufzeit -Engine die aktuelle Funktion nach und verschlüsselt oder entschlüsselt sie nach Bedarf, sodass zu jedem Zeitpunkt nur Funktionen innerhalb des aktuellen Anrufstacks entschlüsselt werden.
Nach dem Abzug von Layer 1 wird Kiteshield die exec mprotect SYSCALL im Userspace (siehe mmap loader/loader.c . C. Ptrace).
Zusätzlich zur Verschlüsselung enthält der Ladercode von Kiteshield eine Reihe von Anti-Debugging-Funktionen, die es so schwierig wie möglich machen, eine laufgepackte Binärin zu analysieren (siehe loader/anti_debug.c und loader/include/anti_debug.h ).
Um ein konkretes Beispiel für Kiteshield in Aktion zu geben, betrachten Sie das folgende Hello World -Programm, das wir im Debug -Modus mit Kiteshield packen werden.
#include <stdio.h>
int main ()
{
puts ( "Hello World!" );
return 0 ;
}Wenn Sie im Debug -Modus verpackt sind, protokolliert Loader -Code in verpackten Binärdateien sehr ausführliche Debug -Informationen. Das Folgende ist das Protokoll aus einem gepackten Binary, das dem obigen Programm entspricht. Es wurde kommentiert (und zusätzliche Neulinen für Klarheit hinzugefügt), um ein konkretes Beispiel für Kiteshield in Aktion zu liefern:
$ ./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
Kiteshield verfügt über umfangreiche Integrationstests, um die Korrektheit über verschiedene Plattformen hinweg zu überprüfen. Weitere Informationen finden Sie unter testing/README.md .
Kiteshield wurde unter Berücksichtigung der Verschleierung geschrieben, nicht die Geschwindigkeit. Das Einfangen in die Kiteshield -Laufzeit bei jedem Funktionseingang und jedem Ausgang ist sehr teuer. Programme mit der Verschlüsselung von Layer 2 sollten einen ernsthaften Leistungsverlust erwarten.
MIT © Rhys Rustad-Elliott