Howto confuse PEiD ================== Dieses Tutorial ist ein kurzer PoC zu einem aktuellen Bug in PEiD (Stand 29.08.2006). Verbesserungsvorschlaege, Anregungen und Kritik bitte an info at cryptocrack dot de. 1. Unpacking PEiD 0.93 ---------------------- Zunächst analysieren wir PEiD mit PEiD. Wie wir feststellen ist es gepackt, und zwar mit "SPLayer 0.08 -> Jibz". Das sollte kein allzu großes Problem darstellen. Wir laden die EXE in Olly und beginnen mit F8 zu tracen. An Adresse 425FE5 gelangen wir in eine Decryption-Loop. Anstatt jeden Schleifendurchlauf einzeln zu durchlaufen setzen wir an Adresse 425FEC einen Breakpoint und lassen das Ganze mit F9 laufen. Dort angelangt tracen wir weiter, bis wir Adresse 425FF8 erreicht haben. Dem Jump folgen wir durch einen Druck auf die F7- oder F8-Taste. Um den eben entschlüsselten Code zu analysieren, klicken wir auf "Analysis, Analyse code". An Adresse 425FD3 angelangt, laufen wir wieder in eine Decryption-Loop, die wir entsprechend zur ersten mit einem Breakpoint an Adresse 425FD8 überspringen. Die nächsten zwei Befehle (ein PUSH, der die Adresse auf dem Stack ablegt und ein RETN, das die Adresse von Stack holt und einen Jump zu dieser ausführt) bewirken einen Sprung zu 401000. Per "Analysis, Remove analysis from module" entfernen wir die Codeanalyse zunächst, um uns den nun folgenden Code genauer anzuschauen. Wir können getrost bis zu Adresse 401016 tracen, wo wir an einer Exception landen. Da zuvor jedoch ein SEH eingerichtet wurde, können wir die Exception mit [Strg] + F8 abfangen, dem Handler übergeben und die Codeausführung fortsetzen. Abschließend tracen wir weiter, bis wir den Jump zum OEP an Adresse 47964F erreicht haben. Wir folgen dem Jump per F8, dumpen den Prozess und verwenden ImportREC, um die Imports zu rekonstruieren. 2. Analyse von PEiD 0.93 ------------------------ Als nächstes wollen wir den Codeabschnitt finden, der dafür verantwortlich ist, die PE zu prüfen. Am einfachsten erreichen wir dies, indem wir die gedumpte Version in OllyDbg laden und unter "Search for, All referenced text strings" nach "Not a valid PE file" (dem String, der ausgegeben wird, wenn PEiD die EXE als ungültig identifiziert) suchen. Um nicht alle Strings durchsuchen zu müssen, können wir ebenso die Suchfunktion ([Strg] + L) verwenden. Neben einem Verweis auf den String selbst finden wir an Adresse 4400D2 einen PUSH-Befehl, der das Offset des gesuchten Strings auf dem Stack ablegt - sehr verdächtig. Per Eingabetaste folgen wir dem verantwortlichen Code im Disassembler-Fenster. Einige Befehle über dem PUSH sehen wir den Code, der dafür verantwortlich ist, dass unsere Fehlermeldung angezeigt wird: Gibt die Unterroutine, die an Adresse 4400C7 aufgerufen wird, 0 zurück, so erscheint die Meldung "Not a valid PE file" - anderenfalls wird an Adresse 4400EC fortgefahren. Um uns genauer anzusehen, was diese Funktion macht, klicken wir auf den Funktionsaufruf an Adresse 4400C7 und folgen diesem durch einen Druck auf die Enter-Taste. Kurz zusammengefasst führt die Routine nun folgende Tests durch: * An Adresse 448E54 wird geprüft, ob die Dateigröße nicht die Minimalgröße von 352 Bytes unterschreitet. * An Adresse 448E6D wird die EXE-Signatur überprüft. * An Adresse 448E82 wird der Pointer auf den PE-Header verifiziert. * An Adresse 448E8C wird die Dateigröße im Bezug auf den PE-Header überprüft (die Datei muss genügen Platz für den PE-Header bieten). * An Adresse 448E93 wird die PE-Signatur überprüft. * An Adresse 448EB9 wird die Signatur des IMAGE_OPTIONAL_HEADERs überprüft (der MagicValue). Die Unterroutine, die schließlich an Adresse 448F20 aufgerufen wird, prüft zu guter letzt noch, ob der EntryPoint innerhalb einer Section liegt; doch genau hier liegt die Schwachstelle: Die Prüfung erfolgt, indem PEiD jede Section einzeln durchläuft und jeweils schaut, ob der EP zwischen der virtuellen Adresse der Section und dem Ende der Section liegt. Zur Berechnung der Section-Größe zieht PEiD das VirtualSize-Mitglied herbei. Hier lässt sich jedoch tricksen: wählt man diesen Wert so, dass der EntryPoint außerhalb der angegebenen Größe liegt, so denkt PEiD, die EXE währe invalid. Allerdings sagt die MSDN: > This is the size before rounding up to the nearest file alignment multiple. Da das FileAlignment standardmäßig bei 512 Bytes liegt, wird nicht nur der Code innerhalb der angegebenen VirtualSize, sondern auch der gesamte restliche Code - bis ein Vielfaches unseres FileAlignments erreicht ist - geladen. So liegt der EP außerhalb der angegebenen virtuellen Größe und wird dennoch in den Speicherbereich der Section geladen. Um diesen Effekt zu demonstrieren, habe ich ein kleines Programm geschrieben (PEiD 0.93 confusion, siehe Anhang). 3. Die "Verbesserung" PEiD 0.94 ------------------------------- In PEiD 0.94 soll dieser Bug gefixed sein. Aus Neugier habe ich mir die neue Version heruntergeladen und ebenfalls analysiert. Der alte Bug war tatsächlich gefixed und mein PEiD 0.93 confusion wurde als gültige PE erkannt. Bei der genaueren Analyse wurde ich jedoch enttäuscht - anstatt wie in der vorigen Version die VirtualSize auszulesen und den Wert richtig aufzurunden, hat der Autor sich beholfen, indem er nun das SizeOfRawData-Mitglied an Stelle des VirtualSize-Mitglieds ausliest und NICHT aufrundet. Selbstverständlich können wir auch diesen Wert manipulieren - zu Demonstrationszwecken habe ich entsprechend zur Version 0.93 das Programm PEiD 0.94 confusion geschrieben, dass sowohl PEiD 0.93 als auch die neue Version 0.94 außer Gefecht setzt.