1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! AppCompatCache (ShimCache) on-disk format constants, by Windows build.
//!
//! The Application Compatibility Cache — "ShimCache" — lives in the `SYSTEM`
//! hive at `…\Control\Session Manager\AppCompatCache` (value `AppCompatCache`,
//! `REG_BINARY`). Presence of a path in the cache proves the binary *existed*
//! on disk and was seen by the application-compatibility subsystem; it is NOT
//! proof of execution. The per-entry timestamp is the binary's `$STANDARD_INFO`
//! last-modified time (FILETIME), not an execution time.
//!
//! The on-disk layout is undocumented by Microsoft but thoroughly
//! reverse-engineered. This module is the fleet's single source of truth for
//! the header signatures, entry markers, and entry-body field offsets — facts
//! only; the decoding algorithm lives in the consuming reader
//! (`winreg-artifacts::shimcache`), per forensicnomicon's knowledge-only charter.
//!
//! # Authoritative sources
//!
//! - Andrew Davis (Mandiant/FireEye), *Leveraging the Application Compatibility
//! Cache in Forensic Investigations* — the original whitepaper that
//! reverse-engineered the XP→Windows 7 layouts and the ShimCacheParser tool:
//! <https://dl.mandiant.com/EE/library/Whitepaper_ShimCacheParser.pdf>
//! - Eric Zimmerman, `AppCompatCacheParser` — the canonical, maintained
//! implementation; format dispatch and per-build entry walks:
//! <https://github.com/EricZimmerman/AppCompatCacheParser>
//! (`AppCompatCache/AppCompatCache.cs` — version detection by the marker at
//! offset 128; `AppCompatCache/Windows8x.cs` — the 8.0/8.1 entry body;
//! `AppCompatCache/Windows10.cs` — the 1507/1607+ entry body).
//! - libyal `winreg-kb`, *Application compatibility cache key*:
//! <https://github.com/libyal/winreg-kb/blob/main/docs/sources/system-keys/Application-compatibility-cache.md>
//! - Mandiant ShimCacheParser (Python reference for the legacy headers):
//! <https://github.com/mandiant/ShimCacheParser>
//!
//! # Format families (header signature → build)
//!
//! | First dword / marker | Windows build | Entry body |
//! |---------------------------------|---------------------------------------|------------|
//! | `0xDEADBEEF` (400-byte header) | XP 32-bit | XP |
//! | `0xBADC0FFE` | Server 2003 / Vista / Server 2008 | NT5.2/6.0 |
//! | `0xBADC0FEE` (128-byte header) | Windows 7 / Server 2008 R2 | Win7 |
//! | `"00ts"` marker at offset 128 | Windows 8.0 / Server 2012 | Win8.x |
//! | `"10ts"` marker at offset 128 | Windows 8.1 / Server 2012 R2 | Win8.x |
//! | first dword `0x30` (header len) | Windows 10 1507 | Win10 |
//! | first dword `0x34` (header len) | Windows 10 1607+ / Windows 11 | Win10 |
//!
//! The reliable discriminant between the Win8.x and Win10 families is the
//! **ASCII marker at byte offset 128**, exactly as Zimmerman's parser tests it
//! (`"00ts"`/`"10ts"` ⇒ Win8.x; otherwise the first dword is the Win10
//! entry-array offset). The Win8.x header's first dword is documented as `0x80`
//! by libyal but has been observed as `0x00000000` in the wild (Case-001 DC01,
//! a Server 2012 R2 domain controller), so the marker — not the first dword —
//! must gate the format.
/// Windows XP AppCompatCache header magic (first dword, LE), 400-byte header.
pub const SIG_WINXP: u32 = 0xDEAD_BEEF;
/// Server 2003 / Vista / Server 2008 header magic (first dword, LE).
pub const SIG_WIN2003_VISTA: u32 = 0xBADC_0FFE;
/// Windows 7 / Server 2008 R2 header magic (first dword, LE), 128-byte header.
pub const SIG_WIN7: u32 = 0xBADC_0FEE;
/// Windows 10 1507 header: the first dword is the header **length** `0x30`,
/// immediately followed by the `"10ts"` entry array.
pub const WIN10_1507_HEADER_LEN: u32 = 0x30;
/// Windows 10 1607+ / Windows 11 header: the first dword is the header
/// **length** `0x34`, immediately followed by the `"10ts"` entry array.
pub const WIN10_1607_HEADER_LEN: u32 = 0x34;
/// Cache-entry marker for Windows 8.0 / Server 2012 (`"00ts"`).
pub const ENTRY_MARKER_WIN80: = *b"00ts";
/// Cache-entry marker for Windows 8.1 / Server 2012 R2 **and** all Windows 10
/// builds (`"10ts"`). The same four bytes (`0x73743031` LE) tag every entry in
/// the 8.1 and 10 streams; the surrounding header/body differ, not the marker.
pub const ENTRY_MARKER_WIN81_WIN10: = *b"10ts";
/// `"10ts"` as a little-endian `u32` (`0x73743031`) — convenience for callers
/// that compare the first dword of a header-less entry stream.
pub const ENTRY_MARKER_WIN81_WIN10_U32: u32 = 0x7374_3031;
/// Byte offset at which the Win8.0/8.1 (and Server 2012/2012 R2) entry stream
/// begins — i.e. the fixed header length for those families. The `"00ts"` /
/// `"10ts"` marker sits here.
pub const WIN8X_ENTRY_STREAM_OFFSET: usize = 128;
/// Length of the per-entry framing that precedes every entry body, shared by
/// the Win8.x and Win10 families: `signature(4) | unknown(4) | ce_data_size(4)`.
/// `ce_data_size` is the length of the body that follows these 12 bytes.
pub const ENTRY_FRAMING_LEN: usize = 12;
/// Win8.x entry body: bytes between the end of the variable-length path and the
/// FILETIME — `package_len(2) | package[package_len] | insertion_flags(4) |
/// shim_flags(4)`. The FILETIME therefore sits at
/// `path_end + 2 + package_len + WIN8X_PATH_TO_FILETIME_FIXED`.
///
/// (`insertion_flags` bit `0x2` = "Executed"; `shim_flags` follow. Source:
/// Zimmerman `Windows8x.cs`; libyal winreg-kb "Windows 8.1 application compat
/// cache entry". libyal orders the trailing 10 bytes as
/// `insertion(4) | shim(4) | unknown(2)`; Zimmerman as `pkg_len(2) | pkg |
/// ins(4) | shim(4)` — the two are indistinguishable when `package_len == 0`,
/// which holds for every non-Store entry. The fleet follows Zimmerman, the
/// maintained reference.)
pub const WIN8X_PATH_TO_FILETIME_FIXED: usize = 8;
/// Win10 entry body: the FILETIME immediately follows the variable-length path
/// (`path_size(2) | path | FILETIME(8) | data_size(4) | data`), i.e. at
/// `path_end + WIN10_PATH_TO_FILETIME` with no intervening flags.
pub const WIN10_PATH_TO_FILETIME: usize = 0;