ShimCache parsen: das Binärformat von Windows 10 und 11

4 Min. Lesezeit

TL;DR. Erstes u32 = Header-Größe (typisch 0x300x36). An diesem Offset beginnt jeder Eintrag mit der vier Byte langen Magic 10ts, gefolgt von einem 12-Byte-Header und einem variablen Körper: UTF-16LE-Pfad (längen-präfixiert, nicht null-terminiert), FILETIME-mtime und einem Trailing-Daten-Blob. Das Ausführungs-Flag wurde in Windows 10 entfernt.

Seit XP hat jede Windows-Version ein anderes ShimCache-Layout ausgeliefert. Die Windows-10/11-Variante ist die heute am häufigsten anzutreffende — und auch die nachsichtigste beim Parsen, sobald man die Regeln kennt. Dieser Artikel geht das Format von Anfang bis Ende durch.

Binärlayout eines Windows-10/11-ShimCache-Eintrags

Der Blob, den Sie parsen

Sie starten mit dem Binärwert unter:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache\AppCompatCache

Wenn Sie diesen Wert aus der SYSTEM-Hive extrahiert haben (siehe wo der ShimCache gespeichert wird), haben Sie einen einzigen Blob, typischerweise zwischen wenigen zehn und mehreren hundert Kilobyte. Der Blob enthält einen kleinen Header gefolgt von einer Folge von Einträgen, von denen jeder mit einer bekannten Magic-Sequenz beginnt.

Der Header

Die ersten vier Little-Endian-Bytes des Blobs geben den Offset des ersten Eintrags an. Auf Windows 10 / 11 sind das typischerweise 0x30, 0x34, 0x35 oder 0x36, je nach Build. Manche Hives setzen hier 0x80 — Signal für ein Windows-8.x-Layout, das dieselbe Eintragssignatur 10ts / 00ts nutzt.

Ein defensiver Parser hardcodet die Header-Größe nicht; er liest das erste u32 und validiert, dass das Byte an diesem Offset den Start eines bekannten Eintrags markiert. Erscheinen weder 10ts noch 00ts am deklarierten Offset, scannen Sie vorwärts nach 10ts und nehmen ab dort auf — so handhabt man leicht außerhalb der Spec liegende Hives, die echte Systeme manchmal produzieren.

Ein Windows-10/11-Eintrag, Byte für Byte

Jeder moderne Eintrag beginnt mit der vier Byte langen Signatur 10ts (0x31 0x30 0x74 0x73). Das vollständige Layout, der Reihe nach:

OffsetGrößeFeld
0x004Magic — "10ts"
0x044Sequenz / unbekannt (oft von Parsern ignoriert)
0x084Eintragsdaten-Größe — Bytes nach diesem Feld
0x0C2Pfadgröße in Bytes (UTF-16LE)
0x0ENPfad-Bytes (UTF-16LE, kein Terminator)
0x0E + N8Letzte Änderung — FILETIME (100 ns seit 1601-01-01 UTC)
0x16 + N4Größe der Schlussdaten
0x1A + NMSchlussdaten (meist leer oder klein)

Die Gesamtgröße des Eintrags ist somit 0x0C + entry_data_size. Der nächste Eintrag beginnt direkt danach.

Zwei praktische Hinweise:

  1. Der Pfad ist UTF-16LE und nicht null-terminiert. Verwenden Sie das Pfadgrößen-Feld; lesen Sie nicht bis zu einer Null.
  2. Der FILETIME ist die $STANDARD_INFORMATION-mtime der Datei, nicht der Ausführungszeitpunkt des Programms. Der Artikel zum Ausführungsnachweis erklärt, warum das wichtig ist.

Was in Windows 10 entfernt wurde

Frühere Windows-Versionen packten ein Executed-Flag in jeden Eintrag. In Windows 10 und 11 wurde es vollständig entfernt. Es gibt im Cache keinen Weg zu sagen, ob eine Binärdatei wirklich gelaufen ist — dafür brauchen Sie Prefetch, AmCache oder Ereignisprotokolle.

In der Praxis

Ein robuster Parser:

  1. Validiert die vier Byte Magic am vom Header angegebenen Offset.
  2. Iteriert Einträge, indem er das Eintragsdaten-Größenfeld liest und vorrückt.
  3. Decodiert den Pfad als UTF-16LE.
  4. Konvertiert die FILETIME in das Zeitstempelformat, das Ihr Downstream-Tooling bevorzugt (Unix-ms ist praktisch).
  5. Hebt das OS-Format-Label hervor (Windows 10/11 vs. 8.x vs. 8.0) für nachgelagertes Filtern.

Eine funktionierende Referenz: Der Open-Source-Shimcache Parser setzt genau den obigen Algorithmus in Rust um und liefert ihn als WebAssembly an Ihren Browser — kein Server, kein Upload.

Referenz-Parser

Verwandte Artikel