Windows Vista stopped writing a Thumbs.db in each picture folder and keeps one set of
thumbnail files for each user instead, in
%LocalAppData%\Microsoft\Windows\Explorer. There is one cache file for each
thumbnail size (thumbcache_32.db, thumbcache_96.db,
thumbcache_256.db, thumbcache_1024.db,
thumbcache_sr.db and, in later versions, more), and an
index file,
thumbcache_idx.db. Microsoft has not published the format. What follows is
from the libwtcdb project’s document by Joachim Metz, Eric Kutcher’s
thumbcache_viewer and Vinetto, which agree on most things; where they differ, or where
nothing but one of them says it, this page says so. All numbers are little-endian.
File header
Every cache file starts with the signature CMMM, a format version and a
cache type. What follows depends on the version.
| Offset | Size | Name | Description |
|---|---|---|---|
| 0 | 4 | Signature | CMMM (43 4D 4D 4D). |
| 4 | 4 | Format version | See the table below. |
| 8 | 4 | Cache type | Which cache file this is; the numbers mean different files in each version. |
| 12 | 4 or more | The rest | See the next table. |
| Version | Hex | Written by | Header size | After the cache type |
|---|---|---|---|---|
| 20 | 0x14 | Windows Vista | 24 | 12: offset of the first entry; 16: offset of the first available entry; 20: entry count |
| 21 | 0x15 | Windows 7 | 24 | the same |
| 26 | 0x1A | an early build of Windows 8 | 24 | the same (thumbcache_viewer) |
| 28 | 0x1C | an early build of Windows 8 | 28 | 12: not documented; 16: first entry; 20: first available entry; 24: entry count (thumbcache_viewer, Vinetto) |
| 30 | 0x1E | Windows 8 | 24 | 12: not documented; 16: first entry; 20: first available entry; no count (Vinetto). libwtcdb has the same three fields at 12, 16 and 20 instead. |
| 31 | 0x1F | Windows 8.1 | 24 | as version 30 |
| 32 | 0x20 | Windows 10 and 11 | 24 | as version 30 |
libwtcdb and Vinetto place the Windows 8 and later offsets differently. Real Windows 8.1, 10 and 11 files (from the dissect.thumbcache test data, 14 files of each of Windows 10 and 11 and 11 of Windows 8.1) settle it: Vinetto’s placement is right. The word at 12 is zero in all of them, the first entry offset is at 16 (0x18 in every file, empty or not) and the first available entry offset at 20; there is no entry count. An empty file is just the 24-byte header, with the first entry offset equal to the first available one, 0x18. The first available entry is where the next thumbnail will be written, and its entry can fill the rest of the file (see Free entries). In Windows Vista and 7 files the count at 20 can be larger than the number of entries: a real Vista file counts 63 and holds 45, so it includes entries that have since been cleared, as libwtcdb warns. Versions 26, 28 and 30 (early Windows 8 builds and Windows 8) had no real file to check; their headers are as thumbcache_viewer and Vinetto give them.
Worked example, the 24-byte header of a Windows Vista file (libwtcdb’s
file_header.1):
43 4D 4D 4D 14 00 00 00 04 00 00 00 18 00 00 00 18 00 00 00 00 00 00 00
CMMM version 20 type 4 (sr) first = 24 avail = 24 count = 0
Cache types
The cache type numbers the file’s size class. The list grew with each Windows version and the numbers were reassigned, so the version decides what a number means. The sizes in the file names are the longest side of the thumbnail in pixels; the sizes in brackets are libwtcdb’s.
| Type | Versions 20, 21 (Vista, 7) | Versions 26 to 30 (Windows 8) | Version 31 (Windows 8.1) | Version 32 (Windows 10, 11) |
|---|---|---|---|---|
| 0 | 32 (32 × 32) | 16 | 16 | 16 |
| 1 | 96 (96 × 96) | 32 | 32 | 32 |
| 2 | 256 (256 × 256) | 48 | 48 | 48 |
| 3 | 1024 (1024 × 768) | 96 | 96 | 96 |
| 4 | sr | 256 | 256 | 256 |
| 5 | 1024 | 1024 | 768 | |
| 6 | sr | 1600 | 1280 | |
| 7 | wide | sr | 1920 | |
| 8 | exif | wide | 2560 | |
| 9 | exif | sr | ||
| 10 | wide_alternate | wide | ||
| 11 | exif | |||
| 12 | wide_alternate | |||
| 13 | custom_stream |
Each name is the middle of a file name: type 4 of version 21 is
thumbcache_sr.db. What sr, wide,
exif and custom_stream hold is not documented in the sources
beyond their names. The index file has no type number of its own; it is the
IMMM file.
Cache entries
The entries follow one another from the first entry offset. Each begins with
CMMM and a size that includes the signature and the size field itself, so
adding the size to the entry’s offset gives the next entry. The entry is a header, the
identifier string, some padding and the picture.
0Entry header signature, size, hash, sizes, checksums48 or 56…Identifier UTF-16, no terminatorIdentifier size…Padding zerosPadding size…Picture a whole BMP, JPEG or PNG fileData size
Windows 7 (version 21): 48-byte header
| Offset | Size | Name | Description |
|---|---|---|---|
| 0 | 4 | Signature | CMMM. |
| 4 | 4 | Entry size | The whole entry, in bytes. |
| 8 | 8 | Entry hash | A 64-bit hash that the index file repeats. The algorithm is not documented. |
| 16 | 4 | Identifier size | In bytes. |
| 20 | 4 | Padding size | In bytes. |
| 24 | 4 | Data size | The picture, in bytes. |
| 28 | 4 | Unknown | Zero in the sources’ files. |
| 32 | 8 | Data checksum | A CRC-64 of the picture; see Checksums. |
| 40 | 8 | Header checksum | A CRC-64 of bytes 0 to 39. |
Windows Vista (version 20): 56-byte header
As Windows 7, with eight more bytes after the hash, and every later field eight bytes further on.
| Offset | Size | Name | Description |
|---|---|---|---|
| 0 | 4 | Signature | CMMM. |
| 4 | 4 | Entry size | The whole entry, in bytes. |
| 8 | 8 | Entry hash | As above. |
| 16 | 8 | File extension | Four UTF-16 characters, not always terminated; the picture file’s extension, or empty. |
| 24 | 4 | Identifier size | In bytes. |
| 28 | 4 | Padding size | In bytes. |
| 32 | 4 | Data size | In bytes. |
| 36 | 4 | Unknown | Zero in the sources’ files. |
| 40 | 8 | Data checksum | CRC-64 of the picture. |
| 48 | 8 | Header checksum | CRC-64 of bytes 0 to 47. |
Windows 8 and later (versions 26 to 32): 56-byte header
| Offset | Size | Name | Description |
|---|---|---|---|
| 0 | 4 | Signature | CMMM. |
| 4 | 4 | Entry size | The whole entry, in bytes. |
| 8 | 8 | Entry hash | As above. |
| 16 | 4 | Identifier size | In bytes. |
| 20 | 4 | Padding size | In bytes. |
| 24 | 4 | Data size | In bytes. |
| 28 | 4 | Width | The picture’s width in pixels (thumbcache_viewer and Vinetto; libwtcdb lists these eight bytes as unknown). In every picture of the real Windows 8.1, 10 and 11 test files it equals the picture’s own width. |
| 32 | 4 | Height | The picture’s height in pixels, likewise confirmed. |
| 36 | 4 | Unknown | Zero in the sources’ files. |
| 40 | 8 | Data checksum | CRC-64 of the picture. |
| 48 | 8 | Header checksum | CRC-64 of bytes 0 to 47. |
The identifier
The identifier is a UTF-16 string with no terminator, Identifier size bytes
long. It is usually the picture’s thumbnail cache ID, a 64-bit number as
hexadecimal text without leading zeros (the Windows Search index,
windows.edb, relates these IDs to file names). In some Windows 7 entries
it is the entry hash in hexadecimal. Shell folders are stored by path, such as
::{645FF040-5081-101B-9F08-00AA002F954E} for the Recycle Bin, which
is longer than a file path can be.
The picture
The picture is a complete file of one of three kinds, recognised by its first bytes:
a BMP (BM, a 14-byte file header and a DIB, usually 32 bits per pixel with
an alpha channel), a JPEG (FF D8 FF, usually JFIF), or a PNG. Windows 7
thumbnails are mostly BMP or JPEG and later versions use PNG as well; which kind a
cache file holds is not fixed. The padding before the picture is zeros.
Free entries
The entry at the first available entry offset is empty: no identifier, no padding,
no picture, and its size can cover the rest of the file, which is then zero bytes. An
entry can also be cleared in the middle of the file, leaving its header with a zero
hash. thumbcache_viewer treats a zero hash as the end of the valid entries and scans
on for the next CMMM, since what follows may be the remains of entries
that were overwritten; the file explorer does the same, and lists the bytes it skips
as unrecognised bytes.
Worked example: the Windows 7 entry from libwtcdb’s cache_entry.2, 80
bytes, with no picture:
43 4D 4D 4D 50 00 00 00 5E 04 F2 17 05 50 2E AA CMMM, size 80, hash 0xAA2E500517F2045E
20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 identifier 32 bytes, padding 0, data 0, unknown 0
00 00 00 00 00 00 00 00 BD 12 E4 AC 7A E1 A2 EC data checksum 0, header checksum 0xECA2E17AACE412BD
61 00 61 00 32 00 65 00 35 00 30 00 30 00 35 00 identifier: "aa2e5005" ...
31 00 37 00 66 00 32 00 30 00 34 00 35 00 65 00 ... "17f2045e"
The identifier is the hash in lower case hexadecimal. The header checksum is the CRC-64 of the first 40 bytes (below).
The entry hash and the thumbnail cache ID
What is established, from real files:
- For a picture file, the entry hash is the picture’s thumbnail cache ID, the
64-bit property
System.ThumbnailCacheId, and the identifier string is the same number in hexadecimal. In the real Vista to Windows 11 test files, 215 of the 323 entries that have an identifier have it equal to the hash in hexadecimal. In a Windows 11 machine the shell was asked for the property of 43 pictures and then made thumbnails of them: every one of the 43 values is the hash of entries in the 32, 96, 256 and 1280 caches. The index file’s hash is the same number. - The other entries are named by a string that is not the hash: a shell folder such as
::{645FF040-5081-101B-9F08-00AA002F954E}(the same string has the same hash in Windows Vista, 7 and 8.1 files: 0x0924BC51F9B84EE8), a shell path, or in Windows 10 and 11 a text of the formname?hash?number. The hash of these is not the hexadecimal of the string. Windows 7’s hash function below, run over the string’s UTF-16 or UTF-8 bytes (129 such entries, six spellings, two starting values), gives none of them.
Where the ID of a picture comes from (Windows 7, as published by Yogesh Khatri on
swiftforensics.com in 2012; it is the algorithm of Windows Search, which Microsoft has not
documented): a 64-bit hash chained over four buffers, in this order, starting from the value
0x95E729BA2C37FD21: the volume’s GUID (16 bytes, the first three groups
byte-swapped), the NTFS file reference (8 bytes), the extension in UTF-16 with the dot, case
sensitive, and the modification time as a 4-byte DOS timestamp in UTC. One step is
seed ^= (seed >> 2) + 2080 × seed + byte (64-bit arithmetic).
seed = 0x95E729BA2C37FD21
for each of the four buffers, for each byte b of it:
seed = seed ^ ((seed >> 2) + 2080 * seed + b) (all modulo 2^64)
thumbnail cache ID = seed
This function could not be checked here on a Windows 7 file (no file with known inputs). On Windows 11 it does not reproduce the IDs the shell reports for 43 pictures, in about 18,000 combinations of the byte order of the GUID, the part of the file ID used, the case of the extension and the form of the time. Controlled experiments on Windows 11 (the ID after changing one property of a file) show which properties go into the ID there: it changes when the modification time changes, by as little as one 100-nanosecond tick (each of 29 times from one tick to an hour apart gave a different ID; for times in the year 1886 it did not change at all, so such times are probably not used); it does not change when the file is renamed or moved to another folder; it changes for a copy of the file (a new file ID), and for a different extension, including upper case against lower case. So the inputs are the same kinds as Windows 7’s (volume, file ID, extension, time), but the time is not a 4-byte DOS time, and how Windows 10 and 11 combine the inputs is not known: no arrangement of the bytes of the full time at the end of Windows 7’s chain fits the 29 values (330 placements tried).
Checksums
Both checksums are CRC-64 values with a table that Windows keeps in
thumbcache.dll. libwtcdb calls the polynomial unknown; it can be recovered
from the table, and the evidence is this. thumbcache_viewer prints the 256-entry table
(crc64.cpp, “found in thumbcache.dll”). Its entry for 128 is
0x92C64265D32139A4, which is what the table entry of a single high bit is in a
reflected, table-driven CRC with that generator; building the table from that one constant
by the usual eight shifts and conditional XORs reproduces all 256 printed entries
exactly (checked by program). In the normal, non-reflected form the polynomial is
0x259C84CBA6426349, the bit reversal of the same number. The
routine then matches stored values: the header checksum of both libwtcdb sample entries
(a Windows Vista and a Windows 7 entry), and the header and picture checksums of every
entry in 54 real files from Windows Vista, 7, 8.1, 10 and 11 (337 entries, none wrong).
table[i] = i
repeat 8 times: table[i] = (table[i] & 1) ? (table[i] >> 1) ^ 0x92C64265D32139A4 : table[i] >> 1
crc = start
for each byte b: crc = table[(crc ^ b) & 0xFF] ^ (crc >> 8)
There is no final inversion.
Header checksum
The CRC-64 of the entry’s first bytes up to, but not including, the header checksum
(40 bytes in Windows 7 entries, 48 in the others), starting from
0xFFFFFFFFFFFFFFFF. Both example entries in libwtcdb’s test data (a
Vista entry and the Windows 7 entry above) give the stored value.
Data checksum
Starting from zero. A picture of up to 1,024 bytes is covered whole. A longer one is covered by two CRCs xored together: the CRC of its first 1,024 bytes, and a second CRC that takes the rest in blocks of 400 bytes and hashes only the first four bytes of each block (the last block counts, with fewer than four bytes if that is all it has). This rule comes from thumbcache_viewer alone. An entry with no picture has a data checksum of zero.
Where the files are
The usual files are thumbcache_idx.db and a thumbcache_*.db
for each size. Windows 8 and later also keep iconcache_*.db files; thumbcache_viewer
offers them in the same open dialog and Vinetto lists them among the files it
takes, so they are taken to have this layout (no real one was available to check).
The file explorer opens any file with this layout, whatever it is called. A thumbnail’s hash is the
link between the files: the index file’s row for a picture holds, for each cache file,
the offset of that picture’s entry.
Sources
- libwtcdb, Windows Explorer Thumbnail Cache database file format, by Joachim Metz (GNU Free Documentation License); and its small test files, used here as worked examples.
- thumbcache_viewer by Eric Kutcher (GPL): the header and entry structures for each version, the cache type lists, and the CRC-64 table and checksum routines.
- Vinetto by Keven L. Ates (GPL): the header differences of the Windows 8 versions.
The text and tables here were written from reading these, not copied. The two
checksums were reproduced with two test entries; the rest of the layouts are as
the sources give them, and no real thumbcache_*.db file was available to
check the Windows 8, 8.1, 10 and 11 layouts against.