Confuser DeConfused
Ein kleines Update zum aktuellen Projekt, dem schreiben eines Unpackers für den Confuser 1.7.0.0 und 1.8.0.0. Es wird vorallem um das Proxy-System und die raffinierte Object-Verschlüsselung gehen. Hard stuff, aber richtig cool wenn man es mal kapiert hat.
Original bin ich auf den Confuser durch Malware gestossen, diese war mit 1.7.0.0 geschützt. Die Malware war der Inbegriff von Unübersichtlichkeit. Wie sollte man dort abstrahieren um einen DeObfuscator zu schreiben? Daher wurde ein simples Hello-World-Programm erstellt (mit 1.8.0.0) und dieses für weitere Analysen verwendet. Diese Hello-World Programm wird auch im folgenden für die Erklärungen verwendet.
Das Proxy-System ist einfach und genial zugleich. Anstatt direkte Methodenaufrufe wie System.Console.WriteLine(String) zu verwenden, wird ein Wrapper um den gesamten Call gepackt. Ein Delegate wird dann mit der richtigen Funktion (WriteLine) verknüpft und im Wrapper dieses verknüpfte Delegate aufgerufen. Dabei errechnet sich das Token (eine Art MethodenID) dem Namen der Delegate-Funktion. Soweit verstanden? In der Praxis sieht das so aus:
1.) ist eine Instanz des aktuellen Delegates. Diese Instanz wird in 2.) mit der richtigen Methode verknüpft, 3.) ist der intere Aufruf des Delegates und wird nicht verwendet. 4.) ist der eigenliche Wrapper welcher die Instanz des Delegates aufruft. Dabei wird die originale Funktion gecalled. Leider geil, oder?
Das ganze gibt es aber nicht nur für Methoden, sondern auch für Objektinstanzen. Dabei wird der Konstruktur (.ctor) mit dem Delegate verknüpft und dann jeweils das Delegate aufgerufen, welches ein neues Objekt zurück liefert. Diese Form des Proxy-Systems war relativ leicht zu fixxen:
// PT = ProxyType, eine Klasse mit dem Typ des delegates. // Je nachdem welche Funktion im .cctor aufgerufen wird // wird ProxyTypeDelegate gesetzt. if (PT.ProxyTypeDelegate == ProxyTypeDelegate.NewObjectCall) { Int32 TokenOfOriginalCall = GetTokenForFieldDecode(PT); // Löst den Proxy-Call auf und liefert das Originaltoken von //z.B. System::StringBuilder::.ctor() MethodReference MR = this.AD.MainModule.Import(ADR.GetModules()[0].ResolveMethod(TokenOfOriginalCall)); // Das Token wird zu einer MethodenReferenz in Cecil Instruction[] arIns = CecilHelper.GetInstructionsWithCallToMethod(this.AD, PT.MethodCalledByCode); // Nun suchen wir alle Calls des Delegates // Und ersetzen sie mit unserer MethodenReferenz (-> "newObject System::StringBuilder::cctor()") for (int i = 0; i < arIns.Length; i++) { arIns[i].OpCode = OpCodes.Newobj; arIns[i].Operand = MR; } } |
Dadurch ist man in der Lage den kompletten Delegate Type zu löschen
Der ObjectDecrypter war auch eine Sache für sich. Dabei verzweifelte ich Stundenlang an der Tatsache, dass die Objekte (Strings, Zahlen etc) nicht aus der direkten Ressourcendatei gelesen werden (natürlich alles etwas komplizierter als nur ausgelesen), sondern erst aus dieser Ressourcen-Datei eine weitere Assembly geladen wird. Diese weitere Assembly beinhaltet wiederrum eine Ressource, welche die Objekte verschlüsselt läd. Dort werden sie dann als Objekt zurückgeliefert und gecastet.
Da man die Assembly nicht Rebuilden kann (es gehen die MethodTokens verloren) konnte ich den direkten Vorgang auch nicht mit einem Debugger verfolgen. Die Anti-Debugger von Confuser sind nicht schlecht und haben mich die ganze Zeit geärgert ^^
Das war die Zusammenfassung der Arbeit der letzten Stunden. Wer nicht alles verstanden hat (würde ich warscheinlich selbst nicht) hat immerhin ein Überblick bekommen wie komplex der Confuser Obfuscator ist. Jetzt gilt es die Objekte direkt aufzulösen und in den IL-Code einzuflechten.
Greez Easy
Du beschreibst so gut. Das versteht man, wenn man weiß was Delegates sind ^^.
Ich freue mich auf das WriteUp :).
Wär cool, wenn du bei confuser noch n bisschen mehr ins detail gehen könntest, insbesondere wie man mit 1.9 gepackte assemblys editable bekommt. Danke für die erklärungen
Meinst du mit gepackten Assemblys die gedumpten Module (XXX.__netmodule). Diese kann man Dumpen, allerdings sind alle weiteren Informationen (Assemblymetadata, Manifestinfo etc) nicht dabei. Diese kann man daher nicht in eine andre Packen…