Inside MAM-Kompression
Eine Windows-XP-.pf-Datei beginnt am Offset null mit dem SCCA-Header. Sie lesen von dort weiter und das Format ergibt Sinn. Werfen Sie denselben Parser auf eine Windows-10-.pf-Datei und Sie erhalten Müll. Der Grund sind drei ASCII-Bytes: MAM.
Jeder, der schon einmal beobachtet hat, wie ein Python-Skript voller Überzeugung „Version 7761" für eine moderne Prefetch-Datei meldet, ist diesem Problem begegnet. Das Skript hat MAM\x04 als Anfang eines SCCA-Headers gelesen und M (0x4D) als unteres Byte eines u32 behandelt.
Das MAM-Framing
Ab Windows 8 komprimiert das Betriebssystem jede Prefetch-Datei, bevor SysMain sie auf die Festplatte schreibt. Die Datei auf der Festplatte beginnt mit einem acht Byte langen Framing-Header:
offset size field
0 3 ASCII signature "MAM"
3 1 compression algorithm (0x04 = Xpress Huffman)
4 4 uncompressed payload size (u32 little-endian)
8 .. compressed payload
Das Byte nach MAM identifiziert, welcher COMPRESSION_FORMAT_*-Algorithmus verwendet wurde. Microsoft definiert fünf davon in der gesamten Familie. Prefetch nutzt ausschließlich Xpress Huffman (0x04). Das Größenfeld teilt dem Dekompressor exakt mit, wie groß der Ausgabepuffer sein muss — praktisch, weil der SCCA-Payload an derselben Stelle kein eigenes Längenfeld hat.
Der Rahmen ist auf Windows 8, 8.1, 10 und 11 identisch. Alles, was sich zwischen den Versionen geändert hat, liegt im dekomprimierten SCCA-Inhalt, nicht im MAM-Umschlag.
Xpress Huffman in einem Absatz
Xpress Huffman ist eine blockorientierte LZ77-Variante, in Microsofts offenem Spezifikations-Korpus als [MS-XCA] beschrieben. Jeder Block beginnt mit einem Huffman-Alphabet mit 256 Einträgen — ein Nibble pro Symbol — das die Literal-Byte-Codes für 0..255 plus die Match-Längen-Codes definiert. Der kodierte Bitstrom wird als Huffman-Codes decodiert; jedes Symbol gibt entweder ein Literal aus oder signalisiert eine Rückreferenz in die bereits dekomprimierte Ausgabe. Nicht die schnellste LZ-Variante, die je ausgeliefert wurde, aber kompakt, deterministisch und gut genug für Dateien, die typischerweise 30 bis 100 KB groß sind.
Der Parser, den diese Seite ausführt (frnsc-prefetch), enthält einen reinen Rust-Xpress-Huffman-Decoder, sodass die gesamte Pipeline innerhalb eines WebAssembly-Moduls laufen kann, ohne native Shell-Aufrufe oder die ausschließlich auf Windows verfügbare API RtlDecompressBufferEx. Die C-Bibliothek libscca macht auf der Desktop-Seite dasselbe; PECmd nutzt die .NET-Implementierung, die mit dem Framework ausgeliefert wird.
Warum es operativ wichtig ist
Alles, was versucht, moderne Prefetch ohne Xpress-Huffman-Dekompressor in der Pipeline zu lesen, wird entweder abstürzen, höflich einen Fehler melden oder im schlimmsten Fall stillschweigend falsche Ausgaben produzieren. Die Zahl forensischer Berichte, die immer noch an „getestet unter Windows 7" hängen, ist nicht null. Ich habe PDFs gelesen, die voller Überzeugung einen v0-SCCA-Payload melden, weil das Python-Skript des Autors nie über Offset 4 hinausgekommen ist.
Wenn Ihre Toolchain ausfällt, sobald Sie Prefetch von einem Win10/11-Endpoint einsammeln, prüfen Sie als Erstes, ob sie tatsächlich mit MAM umgehen kann. PECmd und libscca (und dessen pyscca-Binding) tun es. Die prefetchparser.py-Linie älterer Python-Parser meistens nicht, je nachdem, welchen Fork Sie erwischt haben.
Die Kompression ist der einfache Teil
Sobald Sie den Payload dekomprimiert haben, sind die SCCA-Unterschiede zwischen v17, v23, v26, v30 und v31 das, was Ihr Parser tatsächlich korrekt behandeln muss. Acht Ausführungszeitstempel ab v26, geänderte Dateimetrik-Layouts zwischen v23 und v26, Padding-Anpassungen in v31 — dort divergieren Parser. Der äußere MAM-Rahmen ist seit einem Jahrzehnt ein erledigtes Problem.
Weiterführende Literatur
- Microsoft,
[MS-XCA]Xpress Compression Algorithm Specification — die kanonische Referenz für den Decoder. - Joachim Metz, libscca-Dokumentation — lesbare Beschreibung sowohl des MAM-Rahmens als auch des darunterliegenden SCCA-Payloads.
- Maxim Suhanov, Beiträge zu Windows-Kompressionsinterna — nützlich, wenn Sie einen fehlerhaften Block debuggen müssen.