Im vorherigen Artikel haben wir untersucht, was JavaScript-Code-Obfuskation ist und warum sie verwendet wird. Obfuskierter Code ist schwer zu lesen und zu analysieren, was sowohl ein Vorteil für das Verbergen von Logik als auch ein Nachteil für das Debugging und die Wartung von Code ist. Aber was, wenn wir verstehen müssen, wie ein obfuskiertes Skript funktioniert? Hier kommt die Deobfuskation – der Prozess der Wiederherstellung des Codes in eine lesbare Form – zur Rettung.
Warum JavaScript-Deobfuskation notwendig ist
Deobfuskation ist in mehreren Fällen erforderlich:
Analyse von bösartigen Skripten. Hacker und Malware-Autoren obfuskieren oft ihren Code, um bösartige Funktionalität zu verbergen. Um zu verstehen, wie Malware funktioniert, ist es notwendig, den Code wiederherzustellen.
Finden und Beheben von Fehlern. Obfuskation erschwert das Debugging, da es schwierig ist, Haltepunkte zu setzen und Variablenwerte in minifiziertem Code zu betrachten. Deobfuskation vereinfacht die Fehlererkennung.
Untersuchung versteckter Funktionalitäten. Manchmal obfuskieren Entwickler Code mit undokumentierten Fähigkeiten. Durch Deobfuskation können Sie diese Funktionen kennenlernen.
Modifizierung proprietären Codes. In geschlossener Software ist der Code normalerweise obfuskiert. Deobfuskation eröffnet Möglichkeiten, die Logik nach Ihren Bedürfnissen zu ändern.
Es ist wichtig zu bedenken, dass die Deobfuskierung von proprietärem Code möglicherweise gegen die Lizenzvereinbarung verstößt. Überprüfen Sie immer die Nutzungsbedingungen, bevor Sie Reverse Engineering betreiben.
Grundlegende Deobfuskationsmethoden
Es gibt mehrere Hauptansätze zur Wiederherstellung lesbaren Codes:
1. Formatierung (Beautify)
Der erste Schritt bei der Deobfuskation ist die Formatierung minifizierten Codes. Dies wird kein vollständig lesbares Ergebnis liefern, aber es wird die weitere Analyse vereinfachen. Für die Formatierung können Sie verwenden:
- Browser-Tools, wie „Pretty print“ in Chrome DevTools
- Plugins für beliebte Code-Editoren.
2. Umbenennen von Variablen
Die nächste Phase ist die Vergabe aussagekräftiger Namen für Variablen und Funktionen anstelle von einbuchstabigen. Gute Deobfuskatoren tun dies automatisch basierend auf dem Kontext der Namensverwendung.
Zum Beispiel würde die Variable „a“ in „userName“ umbenannt werden, wenn sie in der Zeichenkette „Hello “ + a verwendet wird.
3. Entfernen von totem Code
Obfuskatoren fügen oft unerreichbaren Code ein, der nie ausgeführt wird, aber die Analyse stark beeinträchtigt. Während der Deobfuskation muss er entfernt werden. Zum Beispiel:
if (false) { x = 10; }
4. Wiederherstellung des Kontrollflusses
Obfuskation verwirrt die Sequenz der Codeausführung mit bedeutungslosen Bedingungen und Sprüngen. Die Aufgabe des Deobfuskators ist es, die normale Struktur mit linearen Codeblöcken und minimalen Bedingungen wiederherzustellen.
Beispiel mit überflüssiger Bedingung:
// Obfuskiert function check(x) { if (x >= 0) { if (x > 0) { console.log('Positiv'); } else { console.log('Null'); } } } // Deobfuskiert function check(x) { if (x > 0) { console.log('Positiv'); } else if (x === 0) { console.log('Null'); } }
5. Wertsubstitution
Viele Obfuskatoren verschieben Zeichenketten und Zahlen in separate Arrays und greifen dann über den Index darauf zu. Während der Deobfuskation müssen diese Werte wieder in den Code eingebettet werden.
// Obfuskiert const strings = ['Hallo', 'Welt', '!']; console.log(strings[0] + ' ' + strings[1] + strings[2]); // Deobfuskiert console.log('Hallo' + ' ' + 'Welt' + '!');
6. Vereinfachen von Ausdrücken
Obfuskatoren komplizieren arithmetische und logische Ausdrücke. Zum Beispiel x * 1
anstelle von x
oder !!x
anstelle von x
. Bei der Wiederherstellung des Codes müssen solche Überschüsse entfernt werden.
Vorher:
if (!!x && y !== undefined) { result = x * 1 + y / 2; }
Nachher:
if (x && y !== undefined) { result = x + y / 2; }
Tools für die Deobfuskation
Manuelle Deobfuskation ist ein mühsamer Prozess, der viel Zeit in Anspruch nehmen kann. Glücklicherweise gibt es Tools zur Automatisierung der Code-Entwirrung:
JS Nice – ein Online-Service, der nicht nur formatiert, sondern auch Variablen umbenennt, toten Code entfernt und den Kontrollfluss wiederherstellt.
de4js – ein weiterer Online-Deobfuskator mit Unterstützung für Entpacken, String-Substitution und Kontrollvereinfachung.
JavaScript Deobfuscator – ein Desktop-Deobfuskator mit umfangreichen Fähigkeiten zur AST-Analyse (Abstract Syntax Tree).
Browser-Erweiterungen, wie JavaScript Deobfuscator für Chrome, die Deobfuskation direkt in DevTools ermöglichen.
Allerdings liefern selbst die fortschrittlichsten Tools keinen 100% lesbaren Code, besonders bei nicht-standardmäßigen Obfuskationstechniken. Daher ist oft manuelle Analyse für die vollständige Logikwiederherstellung erforderlich.
Schritt-für-Schritt Deobfuskationsbeispiel
Lassen Sie uns die praktische Wiederherstellung lesbaren Codes aus einem obfuskierten Skript untersuchen.
Angenommen, wir haben den folgenden Code:
(function() { var _0x5d2a = ['log', 'Hello\x20World']; (function(_0x25a336, _0x5d2afa) { var _0x41fb0f = function(_0x9fdc11) { while (--_0x9fdc11) { _0x25a336['push'](_0x25a336['shift']()); } }; _0x41fb0f(++_0x5d2afa); }(_0x5d2a, 0x78)); var _0x41fb = function(_0x25a336, _0x5d2afa) { _0x25a336 = _0x25a336 - 0x0; var _0x41fb0f = _0x5d2a[_0x25a336]; return _0x41fb0f; }; console[_0x41fb('0x0')](_0x41fb('0x1')); })();
Schritt 1. Formatierung
Formatieren wir den minifizierten Code, um ihn etwas strukturierter zu machen:
Schritt 2. Analyse des String-Arrays
In der ersten Codezeile wird ein Array _0x5d2a
mit zwei Elementen deklariert: ‚log‘ und ‚Hello\x20World‘.
Merken wir uns das und fahren mit der Analyse fort.
Schritt 3. Analyse der IIFE
Der nächste Block ist ein sofort aufgerufener Funktionsausdruck (IIFE).
Er nimmt das Array _0x5d2a
und die Zahl 0x78 (120 im Dezimalsystem), die dann um 1 erhöht wird.
Innerhalb der IIFE wird eine Funktion _0x41fb0f
deklariert, die eine Zahl _0x9fdc11 entgegennimmt und in einer Schleife push
und shift
auf dem Array _0x25a336
aufruft. Im Wesentlichen ist dies eine Mischung des Arrays.
Nach der Deklaration von _0x41fb0f
wird sie sofort mit dem Argument 0x79 (121 im Dezimalsystem) aufgerufen.
Schritt 4. Analyse der Getter-Funktion
Als Nächstes wird eine Funktion _0x41fb
deklariert, die 2 Argumente entgegennimmt.
Das erste Argument _0x25a336
wird um 0x0 (0 im Dezimalsystem) verringert, was bedeutet, dass es unverändert bleibt.
Dann wird ein Element aus dem Array _0x5d2a
am Index _0x25a336 entnommen und zurückgegeben. Dies ist also ein Wrapper für den Zugriff auf das Array über den Index.
Schritt 5. Wertsubstitution
In der letzten Zeile wird eine Methode mit dem Index ‚0x0‘ (0) auf dem Konsolenobject aufgerufen mit dem Argument ‚0x1‚ (1).
Im Array _0x5d2a
ist ‚log‘ am Index 0 und ‚Hello\x20World‘ am Index 1 gespeichert.
Wenn wir diese Werte einsetzen, erhalten wir:
console['log']('Hello\x20World');
Oder vereinfacht:
console.log('Hello World');
Ergebnis
Der ursprüngliche obfuskierte Code gab einfach ‚Hello World‚ auf der Konsole aus, wenn auch auf sehr umständliche Weise.
Wenn wir alle Deobfuskationsschritte zusammenfassen, erhalten wir diesen Code:
(function() { console.log('Hello World'); })();
Tipps zur Analyse von obfuskiertem Code
Beginnen Sie mit der Formatierung und Analyse von Variablendeklarationen. Dies gibt Ihnen einen allgemeinen Überblick über die Codestruktur.
Wenn Sie unverständliche Zahlen im Code sehen, versuchen Sie, sie in andere Zahlensysteme umzuwandeln (dezimal, ASCII).
Achten Sie auf anfällige eingebaute Funktionen wie
eval()
undFunction()
. Andere Teile des obfuskierten Codes könnten durch sie ausgeführt werden.Verwenden Sie einen Debugger und schrittweise Ausführung, um Variablenwerte und die Abfolge von Operationen zu betrachten.
Wenn Sie die Logik eines Codeabschnitts nicht verstehen, versuchen Sie, ihn vorübergehend zu entfernen oder durch einen Platzhalter zu ersetzen. Es könnte die Hauptfunktionalität nicht beeinflussen.
Suchen Sie nach Mustern und Wiederholungen. Obfuskatoren generieren schablonenhaften Code, sodass die gleichen Konstruktionen in verschiedenen Teilen verwendet werden können.
Seien Sie geduldig. Deobfuskation kann zeitaufwendig sein, besonders bei der Analyse von bösartigen Skripten.
Fazit
JavaScript-Code-Deobfuskation ist eine wertvolle Fähigkeit, die sich bei der Analyse von bösartigen Skripten, dem Finden von Fehlern, versteckten Funktionen und der Anpassung von closed-source Code als nützlich erweist. Die Hauptmethoden der Deobfuskation umfassen Formatierung, Umbenennen von Variablen, Entfernen von totem Code, Wiederherstellung des Kontrollflusses, Wertsubstitution und Vereinfachung von Ausdrücken.
Es gibt Online-Dienste wie JS Nice und de4js sowie Desktop-Utilities und Browser-Erweiterungen zur Automatisierung von Deobfuskationsschritten. Bei komplexer Obfuskation ist jedoch oft manuelle Analyse erforderlich. Der Deobfuskationsprozess selbst besteht aus der sequentiellen Analyse von Codeabschnitten, der Substitution von Variablenwerten und der Vereinfachung der Logik. Er erfordert Geduld, JavaScript-Kenntnisse und deduktive Fähigkeiten.
Um obfuskierten Code effektiver zu analysieren, beginnen Sie mit der Formatierung und dem Parsen von Variablendeklarationen. Achten Sie auf verdächtige eingebaute Funktionen, verwenden Sie einen Debugger für die schrittweise Ausführung. Suchen Sie nach Mustern und Wiederholungen in vom Obfuskator generiertem Code. Das Studium der Deobfuskation entwickelt Fähigkeiten im sorgfältigen Codelesen, Debugging und Reverse Engineering. Es ist eine Superkraft für einen Programmierer, die sich definitiv in Ihrer Arbeit oder Teilnahme an CTF-Wettbewerben als nützlich erweisen wird.
Ich wünsche Ihnen viel Erfolg bei dem nicht-trivialen, aber spannenden Prozess des Entwirrens von obfuskiertem JavaScript-Code! Denken Sie daran, dass hinter den Komplexitäten immer die Eleganz der ursprünglichen Absicht des Autors steht.