Deobfuscation .NET
In diesem Post beschreibe ich meine Versuche zum .NET Metaformat mit dem Ziel einen kleinen Deobfuscator zu schreiben. Das ganze hört sich villeicht am Anfang relativ langweilig an, aber wer sich ein bisschen für .NET Reversing intressiert wird da sowieso nicht drumrum kommen
Ich werde in diesem Post NICHT irgendwelche Sachen bezüglich dem Metaformat erklären und wenn überhaupt nur anschneiden, denn das können andere viel besser, präziser und ausführlicher. Sondern dieser Post wird sich auf die persönlichen Erfahrungen beziehen, die ich in den letzen Wochen gemacht habe.
Wenn man etwas umgehen und reversen möchte, so muss man das System natürlich extremst gut kennen. SQL-Injections gehen nicht ohne gute (My)SQL-Kentnisse, einen Bufferoverflow kann man nicht schreiben wenn man keine Ahung hat was ein Stack ist. Und so ist es auch mit .NET. Daher begann ich mich in das “Hinter die Kulissen” einer .NET Anwendung einzuarbeiten.
Als Einstieg benutze ich die Anatomy of a .NET Assembly, geschrieben von Simon Cooper, einem Mitentwickler des Redgate .NET Reflectors. Paralell dazu verfolgte ich alles gelesene im CFF Explorer, ein einzigartiges Tool um in .NET Dateien hineinzu schauen. Nachdem soweit alle Grundlagen saßen, wurde der geniale Artikel The .NET File Format als Referenz zum Programmieren benutzt. Der Artikel ist übrigens vom Autor des CFF Explorers. Alternativ kann man sich die offizielle .NET Metadata Referenz von Microsoft anschauen, von dieser ich persönlich mir allerdings nicht viel abgewinnen konnte…
Nach ein bisschen rumprobieren ist dann also ein Prototyp des “Metadata Readers” geschrieben worden. Den Code bekommt hoffentlich nie jemand zu Gesicht…^^ Im Grunde besteht er aus dem Lesen von der .NET Datei und dem anschließenden Casten in Strukturen.
Doch bevor ich mich an die mühsame Arbeit der Erstellung und Verarbeitung der Metadata Tables machte, wollte ich erstmal die Änderungen von Hand vollziehen. Daher hier eine kleine Auflistung was ein Durchschnittsobfuscator alles an einer Assembly macht:
- Die Ersetzung aller Klassen, Funktions und Membernamen durch Random Strings (z.B. 0×02)
- Die Ersetzung aller Hardcoded Strings durch verschlüsselte Strings aus Ressourcen (Runtime-Decryption)
- Zufällige Sprünge im Code
- Verwirrung von Tools wie dem Reflector durch falsche Metadata-Header Werte, falsche Module, falsche lokale Stacks, mehrfache Medatadata-Streams mit falschen Werten etc.
Vorallem habe ich mich auf den ersten Punkt spezialisiert, genauer gesagt vorläufig auf die Funktionsnamen. Alle String wie Funktionsnamen etc. sind in dem #Strings-Stream gespeichert. Die Strings liegen alle dort vor und werden über einen Offset in der Metadata-Table angesprochen. Was muss man also tun wenn man ein Funktionsnamen ändern will? Solange der Funktionsname gleich oder kürzer ist kann man ihn einfach verändern, da diese Strings durch einen Null-Terminator beendet werden. Doch sobald der neue Name größer ist als der alte String, werden alle absoluten Offsets in der #Strings ungültig und man muss von Hand nachhelfen. Also alle Tables durchgehen und Offset + Zusätzliche Zeichen rechnen. Dann muss die Size der #Strings gefixxt werden, die anderen Metadata-Streams ebenfalls verschoben, die Import Table sowie alle anderen RVAs etc. Auf jeden Fall haute es einfach nicht hin, zurückzuführen auf mein doch sehr begrenztes Wissen über das PE-Format. Und ja, ich probierte wirklich Stunden rum, ohne zu einem Resultat zu kommen. Also gab es jetzt 2 Wege weiterzumachen: Die Cecil-Lib zur Verwenden (Reflexil ist hier das Stichwort) oder mit System.Reflection und IL-Codes zu arbeiten und schließlich den IL-Assembler eine neue PE builden zu lassen. Ich entschied mich für letzteres, dass ist was bisher rausgekommen ist (eine Vollbildanzeige tut wahre Wunder ):
Das Programm ist in der Lage Methoden umzubennen und dabei zwischen (nested) Klassen zu unterscheiden. Das Problem ist bisher nur, dass einige Obfuscatoren für die selbe Funktion mehrere Überladungen mit dem selben Funktionsnamen haben und bisher habe ich noch keine Untescheidung der Parameter drin. In den nächsten Version wird man die Klassen und Felder ebenfalls umbenennen können, ildasm und ilasm werden automatisch aufgerufen werden etc, aber bis dahin… Die IL-Parser Lib ist übrigens von hier, ein sehr cooles Teil
So Far
Easy
könntest du mir die tools mal zukommen lassen? sieht sehr interessant aus
Welche Tools genau meist du?
Hi,
kannst du den Code von deinem Metadata Reader veröffentlichen?
vg
Der Reader macht nix anderes als sich durch ein paar PE-Header zu hangeln. Bis auf das Lesen des Metadata-Headers (EntryPoint etc) sind keine weiteren Funktionen implementiert. Wenn du wirklich die einzelnen Metadata-Tables auslesen willst, so bietet die Mono.Cecil einen kompletten Metadata-Reader (Mono.Cecil.MetadataReader) mit allen Tables (MethodRef, Fields etc).
Viel Erfolg!