Das PE-Format: Windows-Programme von innen lesen

Lesezeit: 6 Min.

Jede EXE- und DLL-Datei unter Windows folgt demselben Bauplan, dem Portable-Executable-Format, kurz PE. Der Kopf der Datei beschreibt, wie Windows das Programm in den Speicher lädt, welche Funktionen es braucht und wo es startet. Diese Angaben lassen sich lesen, ohne das Programm auszuführen, und verraten oft mehr, als dem Autor lieb ist.

Der Aufbau einer PE-Datei

Eine PE-Datei besteht aus mehreren Teilen, die nacheinander in der Datei liegen:

  1. DOS-Kopf: beginnt mit MZ. Er stammt aus MS-DOS-Zeiten und enthält heute vor allem einen Verweis auf den eigentlichen PE-Kopf. Dazwischen steht meist der Satz „This program cannot be run in DOS mode“.
  2. PE-Kennung und Dateikopf (COFF): Prozessorarchitektur, Anzahl der Sektionen, Zeitstempel und ob es sich um eine EXE oder DLL handelt.
  3. Optionaler Kopf: trotz des Namens immer vorhanden. Er enthält den Einsprungpunkt, die bevorzugte Ladeadresse, das Subsystem (Konsole oder grafische Oberfläche) und Schutzmerkmale.
  4. Datenverzeichnisse: Verweise auf wichtige Tabellen, etwa Importe, Exporte, Ressourcen, Signatur, TLS und .NET-Daten.
  5. Sektionstabelle: eine Liste der Sektionen mit Name, Größe, Position und Zugriffsrechten.
  6. Sektionen: die eigentlichen Inhalte, also Code, Daten und Ressourcen.
  7. Overlay (optional): Daten, die hinter der letzten Sektion angehängt sind. Windows lädt sie nicht mit, das Programm kann sie aber selbst lesen.

Was zeigt das Werkzeug?

Bei Windows-Programmen erscheinen die Reiter PE-Details und Importe. PE-Details zeigt die Kopfdaten, die Sektionstabelle und das Overlay. Die Importe haben eine eigene Seite: Verdächtige API-Aufrufe.

Die Kopfdaten

  • Architektur und 32/64 Bit: x86, x64, ARM oder ARM64. Ein 32-Bit-Programm auf einem 64-Bit-Rechner ist normal und kein Warnsignal.
  • EXE oder DLL: DLLs sind Bibliotheken und werden von anderen Programmen geladen. Schadsoftware kommt oft als DLL, die mit rundll32.exe oder über einen legitimen Prozess gestartet wird.
  • Subsystem: Grafische Oberfläche, Konsole oder nativ bzw. Treiber. Ein angeblicher Rechnungsbetrachter als Treiber ergibt keinen Sinn.
  • Zeitstempel: der angebliche Zeitpunkt des Kompilierens. Mehr dazu unten.
  • Einsprungpunkt: die Stelle, an der das Programm startet, und in welcher Sektion sie liegt.
  • ASLR, DEP, CFG: Schutzfunktionen, die moderne Compiler automatisch aktivieren. Fehlen sie, ist das Programm alt, mit ungewöhnlichen Werkzeugen gebaut oder absichtlich so erzeugt.
  • .NET: Das Programm ist in C# oder einer anderen .NET-Sprache geschrieben. Dann sagen Importe wenig, und für die tiefere Analyse brauchst du einen .NET-Decompiler.
  • Signatur vorhanden: Das Programm trägt eine Authenticode-Signatur. Das Werkzeug prüft sie bewusst nicht. Ob sie gültig ist und wem das Zertifikat gehört, zeigt Windows in den Dateieigenschaften.
  • TLS: Das Programm hat TLS-Callbacks. Code darin läuft, bevor der eigentliche Einsprungpunkt erreicht ist. Legitime Programme nutzen das selten gezielt, manche Schadsoftware nutzt es gegen Debugger.

Die Sektionen

Die Sektionsnamen sind Konvention, nicht Pflicht. Typisch sind:

  • .text: der Programmcode, lesbar und ausführbar.
  • .rdata: schreibgeschützte Daten, oft mit Import-Tabellen und Texten.
  • .data: veränderbare Daten.
  • .rsrc: Ressourcen wie Symbole, Dialoge und Versionsinformationen.
  • .reloc: Angaben, die Windows braucht, wenn das Programm an eine andere Adresse geladen wird.
  • .pdata: Tabellen zur Ausnahmebehandlung bei 64-Bit-Programmen.

Programme aus MinGW oder GCC haben manchmal Sektionen mit Namen wie /4 oder /18. Das sind Verweise auf lange Namen, meist Debug-Informationen. Das Werkzeug löst sie auf, etwa zu /4 (.debug_info).

Für jede Sektion zeigt das Werkzeug die Rechte (R lesen, W schreiben, X ausführen), die Größe in der Datei und im Speicher sowie die Entropie. Diese Markierungen erscheinen als Hinweis:

  • beschreibbar und ausführbar: Code, der sich selbst verändert oder zur Laufzeit hineingeschrieben wird. Typisch für Packer und Shellcode-Loader. Normale Compiler trennen das.
  • Packer-Name: Namen wie UPX0, .MPRESS1, .themida oder .vmp0 verraten das Packprogramm.
  • Rohgröße 0: Die Sektion hat in der Datei keinen Inhalt, bekommt im Speicher aber Platz. Dorthin entpackt ein Packer den eigentlichen Code.
  • hohe Entropie: komprimierte oder verschlüsselte Daten, siehe Entropie.

Wie interpretierst du die Befunde?

Kein einzelner PE-Befund beweist Schadsoftware. Aussagekräftig ist die Kombination. Diese Fragen helfen:

Liegt der Einsprungpunkt, wo er hingehört?

Normalerweise startet ein Programm in der ersten ausführbaren Sektion, meist .text. Liegt der Einsprungpunkt in einer anderen Sektion, etwa in .rsrc, in einer zweiten Code-Sektion am Ende oder in gar keiner Sektion, wurde das Programm nachträglich verändert. Packer und manche Schadprogramme, die sich in bestehende Programme einnisten, erzeugen genau dieses Bild.

Stimmt der Zeitstempel?

Der Zeitstempel im Kopf wird beim Kompilieren gesetzt und lässt sich beliebig fälschen. Trotzdem ist er interessant:

  • In der Zukunft: gefälscht oder ein Fehler.
  • Sehr alt, aber moderne Merkmale: Ein angeblich 2005 gebautes Programm mit Funktionen aus Windows 10 passt nicht zusammen.
  • 19. Juni 1992: typisch für Programme aus Delphi, die diesen festen Wert setzen. Kein Grund zur Sorge, aber ein Hinweis auf die Programmiersprache.
  • Scheinbar zufälliges Datum weit in Zukunft oder Vergangenheit: Moderne Microsoft-Compiler schreiben bei reproduzierbaren Builds einen Prüfwert statt eines Datums. Das Werkzeug kennzeichnet solche Werte als „vermutlich kein echtes Datum“.
  • Mehrere Dateien mit identischem Zeitstempel: Wenn sie sonst ähnlich sind, stammen sie oft aus demselben Build.

Was hängt am Ende?

Das Overlay ist alles hinter der letzten Sektion. Häufige harmlose Gründe:

  • Signatur: Die Authenticode-Signatur liegt immer am Dateiende. Das Werkzeug rechnet sie aus der Overlay-Größe heraus.
  • Installer: NSIS, Inno Setup und viele andere hängen ihre komprimierten Dateien an den Installer an. Hohe Entropie ist dort normal.
  • Selbstentpackende Archive: gleiches Prinzip.

Verdächtig wird ein Overlay, wenn es groß ist, eine hohe Entropie hat und das Programm kein Installer zu sein scheint. Dann transportiert es womöglich eine zweite, versteckte Nutzlast.

Struktur beschädigt oder manipuliert

Zeigt das Werkzeug solche Hinweise, stimmen Angaben im Kopf nicht mit der Datei überein, etwa Verweise ins Leere oder Tabellen, die über das Dateiende hinausreichen. Das kann eine abgeschnittene oder beschädigte Datei sein. Es kann aber auch Absicht sein, weil manche Schadsoftware Analysewerkzeuge mit fehlerhaften Strukturen aus dem Tritt bringen will. Windows selbst ist beim Laden oft toleranter als Analysewerkzeuge.

Typische Fehlinterpretationen

  • „Signatur vorhanden heißt vertrauenswürdig.“ Nur eine gültige Signatur eines bekannten Herstellers hat Gewicht, und selbst die kann von einem gestohlenen Zertifikat stammen. Das Werkzeug prüft die Signatur nicht.
  • „Keine Signatur heißt verdächtig.“ Viele kleine Tools, Open-Source-Programme und interne Anwendungen sind nicht signiert.
  • „Der Zeitstempel beweist, wann die Datei erstellt wurde.“ Nein. Er kann gefälscht, auf einen festen Wert gesetzt oder ein Prüfwert sein.
  • „UPX bedeutet Malware.“ UPX ist ein legitimes Open-Source-Werkzeug, das auch harmlose Programme verwenden. Es macht die Analyse schwerer, aber nicht die Datei bösartig.
  • „Eine DLL ist harmloser als eine EXE.“ Im Gegenteil: Viele Angriffe laden bösartige DLLs über legitime Programme nach (DLL Sideloading).
  • „.NET-Programme haben kaum Importe, das ist verdächtig.“ Bei .NET ist das normal. Die eigentliche Logik steckt im .NET-Code.

Wie geht es weiter?

Für eine tiefere statische Analyse eignen sich kostenlose Werkzeuge wie PeStudio, Detect It Easy (erkennt Packer und Compiler), PE-bear oder für .NET-Programme dnSpyEx. Wer skripten möchte, nutzt das Python-Modul pefile, dessen Imphash-Berechnung auch dieses Werkzeug nachbildet. Für die Frage, was ein Programm tatsächlich tut, führt kein Weg an einer dynamischen Analyse in einer isolierten Sandbox vorbei.

Zum Werkzeug