ADMIN

2022

09

2022-08-30T12:00:00

Datenbanken und Applikationen

PRAXIS

062

Security-Tipp

Sicherheit

Tool

Mit Binary Ninja auf Analysetour

Hohe Kampfkunst

von Dr. Matthias Wübbeling

Veröffentlicht in Ausgabe 09/2022 - PRAXIS

Die Analyse von Binärprogrammen gilt als die hohe Kunst der Aufarbeitung von Cyberangriffen und Schadsoftware-Befall. Für das als Reverse Engineering bezeichnete Verfahren gibt es aber auch andere Gründe. Der Security-Tipp in diesem Monat zeigt Ihnen, wie Sie mit "Binary Ninja" Binärprogramme statisch analysieren.

Wenn Sie genau wissen möchten, welche Operationen ein Programm auf Ihrem Computer tatsächlich ausführt, gibt es mehrere Möglichkeiten. Eine offensichtlich einfache ist, in den zur Verfügung stehenden Quellcode der Datei zu schauen. Bei Skriptsprachen wie Python oder quelloffener Software ist das ohne weiteres möglich. Wenn Programme vor der Auslieferung jedoch in Bytecode übersetzt werden, ist die Arbeit eines Analysten etwas komplizierter.
Beim Übersetzen von Code in ein lauffähiges Programm sind verschiedene Werkzeuge involviert, in den meisten Fällen mindestens ein Compiler (Übersetzer) und ein Linker (oder Binder). Ausgehend von den Befehlen einer Programmiersprache erstellt der Compiler die Maschinensprache. Dabei optimiert er die Ausführungsreihenfolge beziehungsweise einzelne Operationen je nach Konfiguration mal mehr und mal weniger, was am Ende durchaus große Auswirkungen auf den resultierenden Maschinencode haben kann. Beim Linken werden genutzte Bibliotheken statisch oder dynamisch mit dem Programm verbunden. Beim statischen Linken wird der verwendete Code aus den Bibliotheken dem resultierenden Programm hinzugefügt. Werden Bibliotheken dynamisch gelinkt, liegt der Code in anderen Dateien und wird erst beim Start des Programms in den Arbeitsspeicher des Prozesses eingebunden.
Binary Ninja [1] ist ein Werkzeug für die statische Programmanalyse. Ursprünglich für den Einsatz bei Capture-the-Flag- Wettbewerben entworfen, wird Binary Ninja mittlerweile kommerziell entwickelt. Für einen ersten Einblick in die Funktionsweise, wie ihn dieser Beitrag bietet, genügt aber die Nutzung der kostenfreien Testversion. Laden Sie sich zum Ausprobieren die passende Version für Ihr Betriebssystem herunter und installieren diese.
Wenn Sie genau wissen möchten, welche Operationen ein Programm auf Ihrem Computer tatsächlich ausführt, gibt es mehrere Möglichkeiten. Eine offensichtlich einfache ist, in den zur Verfügung stehenden Quellcode der Datei zu schauen. Bei Skriptsprachen wie Python oder quelloffener Software ist das ohne weiteres möglich. Wenn Programme vor der Auslieferung jedoch in Bytecode übersetzt werden, ist die Arbeit eines Analysten etwas komplizierter.
Beim Übersetzen von Code in ein lauffähiges Programm sind verschiedene Werkzeuge involviert, in den meisten Fällen mindestens ein Compiler (Übersetzer) und ein Linker (oder Binder). Ausgehend von den Befehlen einer Programmiersprache erstellt der Compiler die Maschinensprache. Dabei optimiert er die Ausführungsreihenfolge beziehungsweise einzelne Operationen je nach Konfiguration mal mehr und mal weniger, was am Ende durchaus große Auswirkungen auf den resultierenden Maschinencode haben kann. Beim Linken werden genutzte Bibliotheken statisch oder dynamisch mit dem Programm verbunden. Beim statischen Linken wird der verwendete Code aus den Bibliotheken dem resultierenden Programm hinzugefügt. Werden Bibliotheken dynamisch gelinkt, liegt der Code in anderen Dateien und wird erst beim Start des Programms in den Arbeitsspeicher des Prozesses eingebunden.
Binary Ninja [1] ist ein Werkzeug für die statische Programmanalyse. Ursprünglich für den Einsatz bei Capture-the-Flag- Wettbewerben entworfen, wird Binary Ninja mittlerweile kommerziell entwickelt. Für einen ersten Einblick in die Funktionsweise, wie ihn dieser Beitrag bietet, genügt aber die Nutzung der kostenfreien Testversion. Laden Sie sich zum Ausprobieren die passende Version für Ihr Betriebssystem herunter und installieren diese.
Testprogramm erzeugen
Um zum Einstieg ein möglichst einfaches Programm zur Analyse zu haben, schreiben Sie sich zunächst ein eigenes. Den Quellcode aus dem Listing "Testprogramm" können Sie beispielsweise unter Linux mit gcc übersetzen. Verwenden Sie dafür das Kommando gcc -O0 it-admin.c -o it-admin. Mit "-O0" schalten Sie die Optimierungen des Compilers soweit ab. Damit wird der Maschinencode näher am Source-Code bleiben, was sich insbesondere für Debugging im Rahmen der Entwicklung anbietet.
Listing 1: Testprogramm
#include <stdio.h> int main(int argc, char **argv) { printf("Hello World!"); return 0; }
Anwendungen analysieren
Zur Analyse des von Ihnen erstellten Programms starten Sie Binary Ninja und öffnen die Datei "it-admin". Die Analyseoberfläche zeigt Ihnen auf der linken Seite die Übersicht der im Binary enthaltenen Symbole, in der Mitte die Codeansicht und auf der rechten Seite in der Feature-Map die Struktur des Programms. Binary Ninja bietet Ihnen unterschiedliche Ebenen des erzeugten Programmcodes, was die Analyse deutlich erleichtern kann. Der Default "High Level IL" zeigt Ihnen generierten Code, der dem ursprünglichen C-Code Ihres Programms schon sehr ähnlich sieht. IL steht dabei für Intermediate Language, Binary Ninja bietet hier unterschiedliche Sprachebenen bis hin zu C-Code. In unserem Beispiel sieht die "main"-Funktion dem Listing im Kasten sehr ähnlich.
Wie Sie sehen, wurde der Aufruf von "printf " durch die darunterliegende Funktion "__printf_chk" ersetzt. Diese wird auf Codeebene von "printf " selbst aufgerufen und prüft vor der Ausgabe die Formatzeichenkette auf mögliche Stack-Overflows. Wenn Sie näher am Maschinencode arbeiten möchten, klicken Sie sich durch die verschiedenen Sprachebenen und schauen sich die unterschiedlichen Ableitungen an.
Dabei sollten Sie beachten, dass der angezeigte Code aus dem Maschinencode generiert wird. Es handelt sich also eher um eine Annäherung an die Programmierung in einer Hochsprache wie C oder C++ denn um eine tatsächliche Darstellung des zugrundeliegenden Codes. Das erkennen Sie auch an den generischen und meist wenig aussagenden Variablennamen, die sich an den Registern der CPU oder Speicherstellen orientieren. Diesen können Sie auch nicht ohne weiteres wieder kompilieren.
Wählen Sie nun "Disassembly", sehen Sie das Programm in Assembler-Code. Die "main"-Funktion enthält dann die Kommandos. Neben Operationen auf dem Stack-Pointer "rsp" sehen Sie hier, wie mit "lea" die Adresse der Zeichenkette "Hello World" – der Format-String steht ja im "Data"-Bereich des Binaries – in das Register "rsi" geladen wird. Anschließend folgt die Initialisierung der Register "edi" mit 1 und "eax" mit 0. Statt "xor eax, eax" könnten Sie auch "mov eax, 0x0" verwenden. Tatsächlich ist das schon eine Optimierung des Compilers, denn wie Sie sehen können, ist der Opcode 2 Bytes kürzer. Damit sind die Argumente für "__printf_chk" vorbereitet und mit "call" erfolgt der Aufruf der Funktion. Anschließend wird der Rückgabewert 0 in "eax" hinterlegt, der Stack-Pointer zurückgesetzt und die Funktion verlassen.
Zurzeit befinden Sie sich im ELF-Modus der Codeanzeige. ELF ist das Linux-Binärformat, das direkt von Binary Ninja ausgewertet und Ihnen entsprechend angezeigt wird. Wenn Sie das Menü öffnen, wo derzeit ELF ausgewählt ist, und in den RAW-Modus schalten, können Sie sich die ELF-Header selbst ansehen. Hier finden Sie zum Beispiel die Referenzen auf dynamische Bibliotheken oder die Stringund Symboltabellen.
Um anstatt der linearen Darstellung des Codes etwa den Ablaufgraphen anzuzeigen, wählen Sie im Menü rechts daneben – derzeit noch mit der Anzeige "Linear" – die Graph-Darstellung. In diesem kleinen Beispiel wird dort erst einmal nur die "main"-Funktion angezeigt. Wählen Sie links aus den Symbolen beispielsweise die Funktion "deregister_ tm_clones" zur Verwaltung von Arbeitsspeichertransaktionen, wird der Graph ein wenig größer. Öffnen Sie mit Binary Ninja ein richtiges Programm, können Sie mit dem Ablaufgraphen die Struktur und Zusammenhänge der Abläufe besser nachvollziehen.
Wählen Sie stattdessen "Hex" als Anzeigeformat aus, wird die Programmdatei in einem Hex-Editor angezeigt. Neben der Adresse auf der linken Seite und der hexadezimalen Darstellung in der Mitte sehen Sie rechts die druckbaren Zeichen. Hier können Sie zum Beispiel die Hex-Werte der Zeichenkette "Hello World" finden. Änderungen am Programm sind in diesem Modus jedoch nicht möglich.
Neben ELF-Programmen unterstützt Binary Ninja noch viele andere Formate ausführbarer Dateien. So können Sie die unter Windows-Systemen üblichen PEBinaries ebenso analysieren wie die von Apples Betriebssystem genutzten Mach- O-Binaries. Dabei sind Sie nicht auf x86- oder x64-Plattformen beschränkt, sondern können auch Programme disassemblieren, die für Architekturen wie ARM, MIPS oder PowerPC kompiliert wurden. Da Binary Ninja kein Debugger oder Decompiler ist, sondern Binärdaten entsprechend der jeweiligen Plattform-Assembler disassembliert, lassen sich interpretierte Sprachen oder solche mit einer Zwischenrepräsentation des Codes, wie etwa bei der Java-VM oder .NET, damit nicht sinnvoll analysieren.
Erweiterungen und Rechtliches
Mit einer gültigen Lizenz lässt sich die Python- API von Binary Ninja verwenden. Damit können Sie regelmäßig durchgeführte Arbeitsschritte automatisieren oder das Programm steuern, etwa Einstellungen oder Anzeigen ändern, sich im generierten Code bewegen oder Plug-ins starten. Für den Fall, dass Sie eine Funktion vermissen, erlaubt Binary Ninja Ihnen das Hinzufügen von Plug-ins. Diese können Sie natürlich auch selbst entwickeln. Dafür stehen Ihnen Schnittstellen zu C/C++, Rust oder Python zur Verfügung. Mit dem Plug-in- Manager können Sie auch Erweiterungen anderer Benutzer installieren.
Zur Analyse von Schadsoftware verwenden Experten regelmäßig Werkzeuge wie Binary Ninja oder das von der NSA entwickelte Ghidra [2] und übersetzen den vorliegenden Binärcode in andere Darstellungen. Das deutsche Urheberrecht setzt hier eigentlich enge Grenzen. So dürfen Sie eigentlich nur dann den Code übersetzen, wenn Sie selbst der Rechteinhaber sind oder damit die Interoperabilität eines vorliegenden Programms sichergestellt werden muss. Die Analyse von Schadsoftware zum Schutz der eigenen Infrastruktur ist dabei vermutlich unkritisch – ob Teile des Codes im Rahmen einer öffentlichen Berichterstattung verwendet werden dürfen, sollte im Einzelfall geprüft werden. Dass die selbst kriminellen Entwickler von Schadsoftware ihre Rechte in diesem Fall aber tatsächlich vor einem Gericht durchsetzen, erscheint sehr unwahrscheinlich.
Fazit
Binary Ninja ist ein übersichtliches Tool zur Analyse von Binärdaten und Programmen. Mit etwas Übung finden Sie sich leicht zurecht, wobei die Analyse fremden Codes mitunter etwas schwerfällig sein kann. Um einen schnellen Blick in ein Binary zu werfen, ist Binary Ninja mit umfassenden Darstellungsmöglichkeiten aber gut geeignet.
(dr)
Links