Ein ConfuserEx2 Deobfuscator mit Unterstützung für Anti-Tamper, Compressor, Constants, Control Flow und Resource Recovery.
Verbesserter Fork von UnconfuserEx mit verbesserter Unterstützung für moderne ConfuserEx2-Varianten, Anti-Tamper-Wiederherstellung, Compressor-Entfernung, Kontrollfluss-Wiederherstellung und Ressourcenrekonstruktion.
https://github.com/user-attachments/assets/de2c7fd9-6736-4f39-83c0-3c25aa9c1f24
Wenn du jemals mit Malware-Beispielen gearbeitet hast, sind einige davon mit ConfuserEx verschleiert, und ich habe einige öffentliche Deobfuskatoren gegen die neueste Version ausprobiert, aber es funktionierte einfach nicht, also habe ich beschlossen, einen öffentlichen zu forken, der gegen diese neueste Version funktioniert hat, und ihn einfach an meine Bedürfnisse anzupassen.
Dieses Repository ist ein Fork von MadMin3r/UnconfuserEx. Auch ihm gebührt Anerkennung. Das ursprüngliche Projekt hat den schwierigen Teil der Erstellung eines fokussierten ConfuserEx2-Deobfuskatoren übernommen, der tatsächlich echte Schutzmaßnahmen entfernen konnte.
Das ist es, was dieses Projekt tut. Es führt eine Liste von Entfernern in einer festgelegten Reihenfolge aus, schreibt Methodenkörper/Ressourcen/Metadaten neu, wo es möglich ist, und gibt eine neue Assembly aus. Wichtig ist die Reihenfolge. Compressor und Anti-Tamper müssen früh passieren, weil der Rest des Moduls möglicherweise noch nicht einmal echtes IL ist.
Die Upstream-Version war bereits nützlich, aber immer wieder traten einige Fälle auf.
Einer davon war der LZMA-Pfad. Manche Samples liefern Bytes, die wie die Konstanten-/Ressourcen-Payload aussehen, aber die LZMA-Eigenschaften sind Unsinn. Wenn man das direkt in den Decoder gibt, erhält man lächerliche Dictionary-Größen und schließlich Ausnahmen, weil die Array-Dimensionen den unterstützten Bereich überschreiten. Daher überprüft diese Version die Eigenschaften, begrenzt die Dictionary-Größe, begrenzt die unkomprimierte Größe und bricht ab, bevor der Decoder etwas Unmögliches alloziert.
LZMA properties => CE FD 62 5F 9F
Invalid LZMA properties byte 0xCE or unreasonable dictionary size
Konstanten hatten ein weiteres dummes, aber reales Problem. Ein Großteil des Auflösercodes erwartet, dass die ID, die vor dem Getter-Aufruf steht, ein ldc.i4 ist. Manchmal ist es aber nicht mehr eine einzelne Anweisung. Es ist ein kleiner arithmetischer Ausdruck.
ldc.i4 0x1234
ldc.i4 0x55
xor
call string <const getter>(int32)
Der ursprüngliche Fork sieht xor, ruft GetLdcI4Value() auf und stirbt, weil xor offensichtlich kein Integer-Load ist. Diese Version geht zurück über die kleine arithmetische Sequenz, emuliert den Stack, reduziert sie auf ein einziges ldc.i4 und lässt dann den normalen Auflöser weitermachen.
Anstatt dies als eine völlig andere Konstantenschutzart zu behandeln, wird daraus:
ldc.i4 0x1261
call string <const getter>(int32)
Dann kann der vorhandene normale/x86-Konstantenauflöser seine Arbeit tun.
Der Kontrollfluss war eher mittelmäßig
Der Switch-Entferner kann die normale ConfuserEx-Switch-Dispatcher-Form verarbeiten. Er durchläuft Blöcke, stellt das nächste Ziel wieder her, löscht tote Blöcke und gibt einen sinnvollen Methodenkörper aus. Es gibt jedoch Samples, bei denen nur ein Teil der Methode verstanden wird. Wenn man eine halbe Methode mutiert und dann feststellt, dass sie immer noch verschleiert ist, ist die Ausgabe schlimmer als nutzlos, weil man jetzt kaputtes IL hat und keine saubere Möglichkeit, nachzuvollziehen, was passiert ist.
Daher erstellt diese Version eine Momentaufnahme des Methodenkörpers, bevor sie ihn anfasst:
instructions
exception handlers
Wenn die Deobfuskation fehlschlägt oder die Methode danach immer noch verschleiert aussieht, wird der ursprüngliche Körper wiederhergestellt. Das Log kann weiterhin sagen „Diese Methode wurde nicht gelöst“, aber die Assembly wird nicht stillschweigend korrumpiert, nur weil eine Methode einen seltsamen Dispatcher hatte.
Jump-/Trampolin-Kontrollfluss hat ebenfalls einen eigenen Durchlauf bekommen. Manche Methoden haben keine Switch-Dispatcher, sondern kleine Branch-Trampoline, die aneinandergereiht sind, bis der echte Block erreicht ist. Diese werden jetzt erkannt und gefaltet, anstatt vom reinen Switch-Pfad ignoriert zu werden.
Der Compressor-Entferner ist der Teil, der vor allen anderen ausgeführt werden muss.
ConfuserEx-Compressor-Stubs behalten normalerweise die echte Assembly komprimiert, booten einen kleinen Lader, dekomprimieren die Payload und laden sie zur Laufzeit.
Der Entferner findet die Lader-Form, extrahiert die eingebettete Payload, dekomprimiert sie und tauscht das Modul gegen die echte Assembly aus. Normale und kompakte Compressor-Layouts werden beide behandelt.
[+] Compressor detected
[+] Extracted compressed module payload
[+] Decompressed real module
[+] Continuing pipeline on unpacked assembly
Anti-Tamper hat jetzt zwei Pfade.
Normaler/dynamischer Anti-Tamper entschlüsselt Methodenkörper aus geschützten Abschnitten und schreibt die wiederhergestellten Körper zurück in das Modul. JIT-Anti-Tamper ist nerviger, weil die Körper erst materialisiert werden sollen, wenn die Laufzeit danach fragt.
Die grobe Form ist:
find init
extract keys
find encrypted JIT body section
derive per method key
read body
write CilBody back
Dies ist immer noch musterbasierend. Wenn sich der Stub genug geändert hat, wird es das natürlich übersehen.
Ressourcen werden ähnlich wie Konstanten behandelt: Finde den verschlüsselten Ressourcen-Blob, stelle den Schlüssel/die Verschlüsselungsform wieder her, entschlüssele, dekomprimiere falls nötig und setze die Ressourcen dort hin, wo normale .NET-Tools sie erwarten.
Es gibt auch einen optionalen Pfad zur Rekonstruktion eingebetteter PEs. Einige geschützte Samples tragen eine verwaltete PE in einer Ressource. Wenn die Rekonstruktion aktiviert ist, versucht der Entferner, auch diese Payload zu parsen und neu zu schreiben, anstatt eine deobfuskierte äußere Assembly mit einer unberührten inneren zu hinterlassen.
UnConfuserEx.exe sample.exe sample.clean.exe --rebuild-embedded-pe
Verwende das, wenn du weißt, dass das Sample eine weitere verwaltete Assembly in Ressourcen versteckt. Wenn die Payload keine verwaltete PE ist, sollte der Rekonstruktionspfad sie in Ruhe lassen.
Baue es:
dotnet build .\UnConfuserEx.sln -c Release
Führe es aus:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe
Oder gib einen expliziten Ausgabepfad an:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe
Wenn du keinen Ausgabepfad angibst, wird neben der Eingabe eine Datei mit dem Zusatz -deobfuscated im Namen erstellt.
Für eingebettete verwaltete Payloads:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe --rebuild-embedded-pe
Dies ist die aktuelle Unterstützungsliste. Es bedeutet nicht, dass jeder mögliche ConfuserEx-Fork funktioniert. Es bedeutet, dass dies die Formen sind, nach denen die Pipeline zu suchen weiß.