“Zwei mal die kreativen Crypter, bitte!”
Dieser Artikel wird sich mal wieder auf direktes .NET RE ohne de4dot und andere Tools beziehen. Mir wurden zur Malwareanalyse zwei Dateien zugespielt, welche mit jeweils .NET Crytern gepackt wurden. Da es sich bei beiden um besonders kreative Ansätze handelt möchte ich diese Absätze hier vorstellen. Ich darf die Files nicht veröffentlichen, aber ich denke die Funktionweisen der Stub werden trotzdem ganz gut vermittelt.
Beide Crypter sind für .NET, als auch für native PE Files geeignet. Das schöne an gepackten .NET Files ist, dass diese selten durch einen weiteren Obfuscator oder Packer geschützt sind. Zwar hätte man die Files auch einfach dumpen können, aber ich hatte keine Lust mir Malware einzufangen… Aber dazu später mehr, fangen wir doch sofort mit dem ersten packed File an:
Wie man schön sehen kann gibt es nur eine Main-Funktion, meistens an dem ersten Parameter String[] (Args) zu erkennen. Aber diese hat es in sich:
Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes({7B6E9A88-AD7D-44BB-8B77-16DBFDFEF0DC}.Ag T(), new byte[8]); byte[][] bufferArray = new byte[][] { new byte[] { // Byte Array 1 // Byte Array 2 // usw. } |
Wie unschwer zu erkennen ist werden erst aus dem “internen” .NET Namespace 8 Bytes als Key ausgelesen. Dieser “interne” Namespace beinhaltet Funktionen zum initialisieren von Arrays und anderen, marshallten Funktionen. Normalerweise bekommt man das nicht zu Gesicht, in diesem Fall wurde aber (ob nun Zufall oder nicht) die Initialwerte in diesen internen Namespace verlegt. Danke Visual Studio
Als nächstes wird ein zweidimensionales Byte-Array erzeugt. Es folgen eine Menge an Bytes, jeweils in verschiedene Byte-Arrays unterteilt.
byte[] buffer = new byte[0x8200]; for (int i = 1; i < bufferArray.Length; i++) { Array.Resize(ref bufferArray[0], bufferArray[0].Length + bufferArray[i].Length); bufferArray[i].CopyTo(bufferArray[0], (int) (bufferArray[0].Length - bufferArray[i].Length)); } bufferArray[0] = new RijndaelManaged().CreateDecryptor(bytes.GetBytes(0x20), bytes.GetBytes(0x10)).TransformFinalBlock(bufferArray[0], 0, bufferArray[0].Length); new DeflateStream(new MemoryStream(bufferArray[0]), CompressionMode.Decompress, false).Read(buffer, 0, buffer.Length); |
Was hier passiert ist schon interessanter: Die Byte-Arrays werden alle miteinander verknüft und in ein großes Byte-Array (byte[] buffer] kopiert. Das passiert allerdings in der umgekehrten Reihenfolge, der finale Buffer wird also von “hinten” befüllt.
Danach wird dieses große Byte-Array entschlüsselt und noch dekomprimiert, in diesem Fall wurde es mit GZip gepackt.
Danach haben wir die Binary in der Rohform und diese wird an die native oder managed RunPE-Funktion übergeben. Mein Patch setzte natürlich an dieser Stelle an, dieser entfernte die RunPE-Funktion und setze stattdessen ein File.WriteAllBytes(“Dumped.exe”,buffer) hin. Fertig war das unpacking!
Das zweite packed File benutzt statt “Verwirrung” eine unbekannte Technik des Methodenaufrufs, daher ist dieses so kreativ
Lobenswert ist, dass eine eigene String-Verschlüsselung verwendet wurde, welche jeweils über Ressourcen arbeitet. Die Funktion DeCode(String) wendet eine spezielle Form des XOR’s an, gemischt mit der Cäsar-Verschlüsselung. Immerhin
Die Main-Funktion ist leicht erklärt:
byte[] buffer = Ulityls.Proper_RC4(Transfer.rA15UE2OssPdKO9HVdfq("1", "DATA"), Encoding.Default.GetBytes("AdjEnAjenAigoEnY")); try { Ulityls.AA1JJ2JJ4(buffer); } catch { Ulityls.wwdW20jDUdoYl49xioAN(buffer); } |
Was passiert hier? Die Transfer-Klasse bietet Zugriff auf NATIVE Resourcen, also werden diese Resourcen über FindResource und LoadResource in den nativen Speicher geschrieben und dort in ein managed Array kopiert. Kreativ Kreativ Die Bytes werden ausgelesen und über den RC4 Algorithmus entschlüsselt (Key = AdjEnAjenAigoEnY). Dort könnte man sie nun dumpen.
Aber uns interessiert die native RunPE Methode, die .NET RunPE besteht einfach aus:
Assembly.Load(buffer).Entrypoint.Invoke(null,null); |
Da eine Exception geworfen wird (die Assembly ist ja nativ), wird in die Catch-Sektition gesprungen. Dort erwartet uns folgender Code:
public static void wwdW20jDUdoYl49xioAN(byte[] MaByte) { foreach (MethodInfo info in Assembly.Load(Proper_RC4(Resources.mynewdll, Encoding.Default.GetBytes("myamazingdllencryptionke"))).GetType("Haze").GetMethods()) { if (info.Name == "Weed") { info.Invoke(null, new object[] { MaByte, Environment.GetEnvironmentVariable("WINDIR") + @"\Microsoft.NET\Framework\v2.0.50727\vbc.exe" }); } } } |
Die fleißigen Blogleser werden vielleicht etwas damit anfangen können, für die anderen folgt hier die Erklärung: Zunächst wird eine Ressource ausgelesen (Ressources.mynewdll) und entschlüsselt (Key = myamazingdllencryptionke). Dann wird die Assembly geladen und von dieser geladenen Assembly der Type “Haze” ausgelesen. Über die GetMethods()-Methode bekommen wir nun alle Methoden des Types “Haze” Form eines MethodInfo-Arrays. Wenn der Funktionsname nun “Weed” ist, wird diese Funktion dynamisch aufgerufen.
Das besondere daran ist der anonyme Funktionsaufruf. Das .NET Framework weis nicht, wieviele Parameter und welche Types von Parametern diese Funktion erwartet. Daher wird ein Object-Array erstellt und diese Funktion mit diesem Array als Parametern aufgerufen. Die “Weed”-Funktion ist natürlich einfach ein RunPE mit CreateRemoteThread etc.
Gerade an dem zweiten Beispiel kann man sehen, dass Malwarecoder doch nicht alles zusammen kopieren und sogar kreative Wege der Verschlüsselung finden (Stringverschlüsselung, Native Ressourcen API, Verwendung von Reflection). In diesem Sinne: Weiter so
Easy
Hey Easysurfer,
erstmal wollte ich mal wieder Hallo sagen. Schön dass du wieder da bist!Das du jetzt Kontaktmöglichkeiten anbietest ist natürlich cool, aber wurdest du dir eventuell für deine eMail GPG/PGP einrichten?
mfg
Danke! Bin leider nicht früher von selbst drauf gekommen, PGP Schüssel ist nun drin.
Viel Spass weiterhin beim Lesen
2te Stub ist von mir
Danke
Nice Work