3

Problem:Get deobfuscated Typoskript Aufrufliste verschleiert von Javascript-Code

I-Log-Dateien von einem Server, der die Callstacks vom geworfenen Fehler enthält, welche die Erstellung dieser Protokolldatei ausgelöst. Die Server-Anwendung wird in typescript mit nodejs geschrieben, aber gest in javascript übersetzt, und der JavaScript-Code wird mit dem Google-Closure-Compiler verschleiert. Nun ist mein Callstack ziemlich schwer zu interpretieren, was ich durch Entschlüsseln des js-Codes ändern wollte, indem ich eine Source-Map verwendete, die vom Closing-Compiler erstellt wurde, und dann den js-Callstack wieder in den Typescript-Callstack umwandelte.

Meine Einschränkungen

Ich habe Zugriff auf die Quellkartierungen, den Quellcode (ts und js) und der verschleierten Code, aber ich kann den Code selbst, ändern, so mit den aktuellen Callstacks stecken im. Ich habe auch Zugriff auf alle Optionen und den Code/das Werkzeug, der den Code verschleiert, also kann ich vielleicht einige benötigte Informationen in einer Datei (Informationen, die nicht in der Quellkarte enthalten sind) wie zusätzliche Zuordnungen speichern.

Ideen und Versuche

Erster Versuch war, einfach die Quelle Karten zu interpretieren und mit diesen Informationen deobfuscate die Aufrufhierarchie (deobfuscating ist der schwierige Teil), aber ich nach dem Versuch, die Art und Weise der cc erzeugt die Quelle zu verstehen Karten hatte ich einige Probleme: Die cc nicht nur einen Namen zu einem anderen zuordnen, weil er bestimmte Namen mehrmals (wie a, f oder diese Art von "Namen") wiederverwendet. So könnte es eine Funktion mit einigen anonymen Funktionen oder verschachtelten Funktionen geben, wobei der Name f mehrmals verwendet wird, aber in jedem Kontext aufgrund der Bereiche eine andere Bedeutung hat.

Nächste Idee war einfach dem Callstack vertrauen. Um zu verstehen, was ich meine, Sie haben zu verstehen (wenn ich das richtig verstanden), wie der cc erstellt und verwaltet die Zuordnungen:

   return method.call(thisObj, args[0], args[1]); 

Diese Linie dazu verschleiert wird (i verließ die Whitespaces die Indizierung besser zu verstehen) :

 return f.call(d, a[0], a[1]); 

Nun gibt es mehrere Zuordnungen für diese einzelne Zeile erstellt ein einzelnes Mapping wie folgt aussehen:

export interface MappingItem { 
source: string; 
generatedLine: number; 
generatedColumn: number; 
originalLine: number; 
originalColumn: number; 
name: string | null; 
} 

Die einzige wichtige Informationen in diesem mapp In der Instanz sind die Spalten und der Name. Einige Zuordnungen enthalten einen anderen Namen nicht. Diejenigen, die keine Namen enthalten, werden verwendet, um einen Bereich um diejenigen mit Namen zu erstellen, um herauszufinden, wo ein Name/ersetzter Name begonnen und beendet wurde (Index).

Ein Beispiel für diese Logik über die beiden Anweisungen mit:

Generated │ Original │ Name   │ Scope 
0   │  16  │ null   │ ━━━┓ 
15   │  23  │ method   │ x │ 
16   │  23  │ call   │ x │ 
21   │  23  │ null   │ ━┓ │ 
22   │  35  │ thisObject  │ x│ │ 
23   │  23  │ null   │ ━┛ │ 
25   │  44  │ args   │ x │ 
26   │  44  │ null   │ ━┓ │ 
27   │  49  │ null   │ ?│ │ 
28   │  44  │ null   │ ━┛ │ 
29   │  23  │ null   │ ━━┓│ 
31   │  53  │ args   │ x ││ 
32   │  53  │ null   │ ━┓││ 
33   │  58  │ null   │ ?│││ 
34   │  53  │ null   │ ━┛││ 
35   │  23  │ null   │ ━━┛│ 
36   │  16  │ null   │ ━━━┛ 

Mit dieser Aufrufliste, ich will alles von applications.js lösen. Der ganze transpilierte und verschleierte js-Code ist da drin. Der Rest ist irrelevant:

at do2 (c:\Users\me\test\js\test.js:14:11) 
at do1 (c:\Users\me\test\js\test.js:11:5) 
at Server.<anonymous> (c:\Users\me\test\js\test.js:6:5) 
at f (c:\Users\me\build\transpiled\obfuscated\application.js:235:18) 
at Object.a.safeInvoke (c:\Users\me\build\transpiled\obfuscated\application.js:285:27) 
at Server.g.getWrappedListener (c:\Users\me\build\transpiled\obfuscated\application.js:3313:17) 
at emitTwo (events.js:106:13) 
at Server.emit (events.js:191:7) 
at HTTPParser.parserOnIncoming [as onIncoming] (_http_server.js:546:12) 
at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23) 

Jetzt mit der Info aus dem sourcemap es ist einfach, die ursprüngliche Zeile und Spalte zu bekommen, aber die Namen nicht. Ich habe versucht, zuerst ohne Informationen aus dem Code zu versuchen, indem ich die vorherige Position (Zeile und Spalte) bereitstelle, um auf den Namen der nächsten Zeile zu verweisen.

Also, wenn ich f lösen will, würde ich ausgesehen haben, wo sie (285: 18) genannt wurden und es dann in der Quell Karte sehen, wo ich seinen Namen finden würde. Aber für diesen Prozess muss ich immer wissen, wo es hieß. Jetzt ist das das Problem. Denn wenn die Funktion in einer Variablen gespeichert wäre oder anonym oder sonstwie gewesen wäre, hätte ich ein Problem.

f.call(d, a[0], a[1]); 

Auch habe ich bemerkt, dass bestimmte Methoden wie Anruf in diesem Zusammenhang nicht in der Aufrufliste aufgeführt erhalten, die ein weiteres Problem. So kann ich jetzt zumindest Namen auflösen, wenn ich sicher sein kann, wenn ich weiß, wo sie angerufen wurden und ob sie im Callstack sind. Aber ich mache keine halbe Lösung so.

Mein zweiter Versuch war ein vielversprechendes Javascript-Modul gefunden: stacktrace-js

Dieses Modul ist für Browser gemacht js obwohl und schlechte Typoskript Dokumentation/Typisierungen hat, obwohl es eindeutig in Typoskript geschrieben. Dies führt auch dazu, dass Dateien lokal nicht gelesen werden können, da sie immer mit xmlhttprequests aufgerufen werden. Es gibt einige Problemumgehungen für diesen Teil, aber das Modul ist so komplex (wahrscheinlich weil es transpilierter Code ist), dass es auch andere Teile gibt, die mich nicht unterstützen und lokale Dateien verwenden. Es ist einfach zu viel, um es neu zu schreiben/ändern richtig mit NodeJS zu arbeiten ....

Wissen Sie, eine saubere Art und Weise mit dem Modul zu tun? Ich dachte auch, einen Quellcode-Parser zu verwenden, um mehr Kontext zu bekommen, um die Quellkarten zu unterstützen (im Fall dieser bösartigen .call-Methoden). Vielleicht könnte ich meine eigenen Quellcode-Parser schreiben, wenn es eine Dokumentation zu allen Ausnahmen war ich habe, um zu sehen, wenn Sie den Code Parsen und Interpretieren ... Vielleicht gibt es einen anderen Weg, um diese, die ich zur Zeit beaufsichtigte ...

+0

Ist der Fehler so schwierig zu reproduzieren, dass Sie die unverdorbene Version nicht laden können, produzieren und debuggen Sie den Fehler dort? Es wäre ein leichter Ausweg, zumindest diesmal. – ASDFGerte

+0

Gerade jetzt Ihre Frage sieht aus wie die Suche nach Ratschlägen, die Off-Thema ist. Lass uns genauer werden. Können Sie das Problem mit ein paar Zeilen Code in einer '.ts' Datei reproduzieren? Welche TSC-Flags verwendest du? Welche cc Flags benutzt du? Wie sieht die Ausgabedatei aus? Und schließlich, was ist das erwartete Verhalten, das Sie nicht bekommen. – styfle

Antwort

2

Zusammengesetzte Quellkarten

Zunächst einmal, stellen Sie sicher, dass Sie eine vollständig komponierte Quellkarte haben. Sie erwähnen zwei Tools, die Quellzuordnungen generieren, den Typescript-Compiler und den Closing-Compiler. War Closure-Compiler Quellkarten zur Verfügung gestellt? Wenn dies der Fall ist, würde es eine Quellkarte ausgeben, die die ursprünglichen Dateien erwähnt. Wenn nicht, haben Sie die doppelte Arbeit für Sie. Es ist möglich, die source-map package zu verwenden, um Quellkarten nachträglich zu erstellen.

richtig verstehen Quelle Karten

Es ist klar, aus Ihrer ursprünglichen Frage, die Sie nicht vollständig eine Quelle Karte verstehen. Zum Beispiel sind Einträge ohne name oft Sprachsemantik. Zum Beispiel:

document.createElement('div') 

Eine Quelle Karte könnte enthalten Zuordnungen für document und createElement, sondern auch für die . und ( Zeichen. Hier besteht kein Spielraum.

Visualisierungstools

Es gibt mehrere Visualisierungstools, die hier helfen können.Einige meiner Favoriten sind:

Die Idee dabei ist, dass Sie die Quelle Karten und Quellen im Werkzeug zu laden, dann um klicken, um zu sehen, wie die Dinge abgebildet. Es dauert etwas herumzustochern, aber Sie sollten in der Lage sein, die Zeile und die Spalte in der ursprünglichen Quelle zu finden, die mit den Zeilen-/Spalteninformationen in der Stapelüberwachung übereinstimmt.

den Prozess automatisieren

Tools wie https://sentry.io/ aus einem bestimmten Grund gibt. Es wird automatisch einen Call-Stack für Sie de-obfuscate.

+0

Wenn Sie sagen, der Compiler Compiler kann mit einer Source-Map als Eingabe bereitgestellt werden, meinen Sie, dass diese Funktion in der cc integriert ist? Oder eher wie ein Plugin oder selbst geschriebener Code? Weil ich nicht finde, wie man die Dateien eingibt, um sie wie erwähnt zu integrieren (https://github.com/google/closure-compiler/wiki/Source-Maps siehe hier). –

+0

Es ist eingebaut. Siehe das '--source_map_input' Flag. Die beste Dokumentation ist das 'help' Flag um es zu sehen. –

+0

mein Verständnis ist, dass mit diesem Flag der CC liest in einer Quellkarte, aber nicht in die neue Quellkarte zu integrieren, stattdessen er verwendet es nur für die eigene Fehlerausgabe der CC produziert –

Verwandte Themen