Tatort Internet: Matrjoschka in Flash

Seite 2: Der Pseudo-Trojaner

Inhaltsverzeichnis

Nach einer erstaunlich kurzen Gewöhnungsphase kann ich den Code schon recht flüssig lesen und versehe ihn mit Notizen. Am Ende baue ich mir aus den Kommentaren eine leichter verständliche Pseudo-Code-Darstellung. Die übersetzt zwar sicher kein Compiler dieser Welt mehr in lauffähigen Code, aber dafür erkennt mein darauf trainiertes Gehirn auf einen Blick, was da passiert.

 1 function Main()
2 {
3 var ii as Number;
4 var i as Number;
5 var j as Number;
6 var bytes as ByteArray;
7 var Gaa as Loader;
8
9 Gaa = NULL;
10 bytes = new ByteArray;
11
12 j = 0;
13 for (i=0; i < Eys.data.length; i++)
14 j = j + 1;
15 bytes[i] = (Eys.data[i] XOR Eys.Dae.charCodeAt(j));
16 if (j > Eys.Dae.length) {
17 j = 0; // zuruecksetzen
18 }
19 }

20
21 Gaa = new Loader;
22
23 ii = 0;
24 while (ii < 2) {
25 ii = ii + 1; // ergibt keinen Sinn, aber so ist es!!!
26 }
27
28 addChild(Gaa);
29 Gaa.loaderInfo.addEventListener(Event.COMPLETE, function(e:Event));
30 Gaa.loadBytes(bytes);
31}

Der grün markierte Code dieser Funktion verwendet die Werte in Eys.Dae als Maske, die in einem sich wiederholenden Muster über den großen Datenpuffer Eys.data gelegt wird, um die Werte dann mit XOR zu verknüpfen. i und j sind dabei Offsets in die jeweiligen Puffer. Während i linear vom ersten bis zum letzten Byte des Datenpuffers läuft, wird der Zähler j immer wieder auf 0 zurück gesetzt, wenn er das Ende der Maske erreicht.

Und jetzt wird's spannend: Nachdem die Funktion den Puffer dekodiert hat, lädt sie ihn via loader.loadBytes() und führt ihn damit als Flash-Datei aus. Also muss der dekodierte Puffer eine SWF-Datei im Arbeitsspeicher darstellen. Die will ich sehen!

Doch um da ranzukommen, brauche ich Eys.Dae und Eys.data. Etwas mehr Geschmöker im P-Code fördert die Struktur der Klasse Eys mit ihren zwei Properties Dae und data zu Tage. Und schon habe ich das "Sesam Öffne Dich"

7 findproperty Dae
9 pushstring "fx46RIu1kelToyIVefnbEF"
11 setproperty Dae

in Form des Strings Dae mit dem XOR-Codewort. data entpuppt sich als ein Feld mit 10.343 Bytes, die eins nach dem anderen über pushbyte befüllt werden:

   20    pushbyte          37
22 pushbyte 47
...
25909 pushbyte 21
25911 newarray [10343]

// fill data array with 10,343 values poped from stack
25914 setproperty data

Somit muss ich mir nur die „gepushten“ Werte besorgen, um sie dann selber zu dekodieren. Ich spiele kurz mit dem Gedanken, mir die aus dem formatierten Dump von abcdump zu holen, entscheide mich dann aber doch für die elegantere Variante, sie direkt aus der SWF-Datei herauszuoperieren. Die Opcodes der P-Code-Befehle hat Adobe ja fein säuberlich in der VM-Beschreibung dokumentiert.

Also such ich mir den Beginn der Lade-Routine, der sich in Zeile 20 des P-Code-Listings findet. Doch ich brauche die zugehörige Stelle in der SWF-Datei. Der direkt davor befindliche debug-Befehl hat den Opcode 0xEF gefolgt von vier Parametern; das folgende findproperty wird zu 0x5E und erwartet wie pushbyte mit 0x24 nur ein Argument:

13    debug          1 19 1 4    //  ef ?? ?? ?? ??
18 findproperty data // 5e ??
20 pushbyte 37 // 24 ??
22 pushbyte 47 // 24 ??

Das reicht, um die Stelle im Hex-Editor zu lokalisieren. Ich kopiere die komplette Routine mit dem Hex-Editor in die Datei „push_stub.bin“. Als nächstes hack ich mir schnell ein einfaches Dekodierprogramm in C++ zusammen. Etwas anspruchsvoller wird die Aufgabe durch die ebenfalls vorkommenden pushshort-Befehle (Opcode 0x25), die einen „unsigned integer“ mit 30 Bit schreiben. Gut, dass meine Programmierkenntnisse noch nicht allzu sehr eingerostet sind.

Fast hätte ich dabei die gelegentlich eingestreuten dup-Anweisungen übersehen, die den letzten auf den Stack geschriebenen Wert wiederholen. Letztlich bedeutet jedoch auch dies nur eine weitere if-Abfrage und drei Zeilen zusätzlichen Codes in meinem kleinen C++-Programm, das auch gleich das XOR für mich erledigt und die dekodierte Flash-Datei nach „decoded.bin“ schreibt.