Skip to content

BAM Value Data Format: FILETIME, Paths and Internals

Byte-level walkthrough of a BAM registry value: the 24-byte REG_BINARY, the FILETIME, device paths vs package names, Version, SequenceNumber and key times.

Published on 6 min read

TL;DR. Every BAM value is name = executable, data = REG_BINARY. The first 8 bytes of the data are a little-endian FILETIME (100 ns ticks since 1601-01-01 UTC); on the builds studied the data is 24 bytes long and the remaining 16 bytes are not needed to read the time. Value names are either NT device paths (\Device\HarddiskVolume3\…) or package family names. Version and SequenceNumber are bookkeeping, not programs. The SID key's own last-write time is a free extra timestamp.

If you only ever read BAM through a tool, this is the article that tells you what the tool is doing and where it can go wrong. Background first: the BAM/DAM forensics guide and where the BAM key lives.

Anatomy of a SID key

A typical bam\State\UserSettings\<SID> key looks like this (fictional sample data from this site):

Value nameTypeData (hex)
VersionREG_DWORD01 00 00 00
SequenceNumberREG_DWORD76 00 00 00
\Device\HarddiskVolume3\Users\alice\Downloads\Invoice_2026-0914.pdf.exeREG_BINARY80 0d 7c 1d 29 44 dd 01 + 16 bytes
MSTeams_8wekyb3d8bbweREG_BINARY24 bytes

Three kinds of value, three rules:

  1. Bookkeeping DWORDs — Version and SequenceNumber. Skip them when listing programs. Their semantics aren't publicly documented; treat any interpretation (for example, that SequenceNumber grows with updates) as a hypothesis to test on your own reference system.
  2. Executable entries — REG_BINARY, name is a path, data is at least 8 bytes.
  3. Anything else — a value that isn't REG_BINARY or is shorter than 8 bytes is not a BAM record. A robust parser ignores it rather than crashing. Ours does exactly that, and reports unreadable values as warnings instead of dropping them silently.

Decoding the FILETIME

A FILETIME is a 64-bit count of 100-nanosecond intervals since 1 January 1601 UTC (Microsoft Learn). It is stored little-endian, so read the bytes right to left:

bytes      80 0d 7c 1d 29 44 dd 01
as u64     0x01DD44291D7C0D80 = 134338507270000000
seconds    13433850727            (÷ 10,000,000)
minus      11644473600            (1601 → 1970 offset)
unix       1789377127             → 2026-09-14 09:12:07 UTC

Two things to keep in mind:

  • It's UTC. The registry does not store the host's time zone in the value. The SYSTEM hive does record the configured zone (Control\TimeZoneInformation\TimeZoneKeyName), which our tool displays in the header so you can reason about local time. The tool's "Local" toggle converts to your browser's time zone, not the suspect's.
  • Precision. FILETIME has 100 ns resolution. JavaScript Date only has milliseconds, so browser tools that convert through Date round. The BAM/DAM Parser keeps the full seven decimal places in UTC view and exports the raw FILETIME as a decimal string (a 64-bit integer doesn't fit in a JSON number without loss).

What event the time marks

Maxim Suhanov's analysis of bam.sys shows the timestamp is updated both when the process is created and when it terminates (BAM internals). So the value is the latest of those events that BAM persisted. For long-running programs, prefer to call it "last recorded activity" in reports, and use Prefetch or process-creation logs if you need the start time.

The remaining 16 bytes

On the Windows 10 builds Suhanov examined, BAM values are 24 bytes: the FILETIME, then 16 more bytes. His analysis links one of the trailing DWORDs to the moderation state (whether the user left background activity to Windows or changed the setting for that app), and notes that entries with a non-default state are not subject to the age-based cleanup (source). Beyond that, the layout is not publicly documented.

Practical consequences:

  • Don't assume a fixed 24-byte size; read the first 8 bytes and keep the rest.
  • Keep the raw bytes in your export. Our tool shows the full data in the entry detail panel ("Raw value data") and in the DataHex CSV column, so you can revisit the trailing bytes if new research appears.

Value names: device paths and packages

NT device paths

Most names look like \Device\HarddiskVolume3\Windows\System32\cmd.exe. That is the kernel's NT device path: the volume object, then the path on that volume. There is no drive letter because drive letters are a user-mode mapping (Microsoft Learn: defining an MS-DOS device name).

To map HarddiskVolumeN to a letter offline:

  • Most of the time the system volume is the one holding \Windows\System32\… entries; everything with that volume number is on C:.
  • For other numbers, correlate with other artifacts that record both forms — for example Prefetch files, which store volume device paths and serial numbers (libscca format). Live-response output that recorded both the device path and the drive letter helps too. (The MountedDevices key in the same hive maps letters to volume identifiers, not to HarddiskVolumeN numbers, so it is only a partial help.)
  • Volume numbers are assigned by the running system and are not a stable identifier across hosts, or even necessarily across configuration changes on one host. Don't compare them across machines.

Package family names

Packaged apps (Store, MSIX) appear by package family name: Microsoft.WindowsCalculator_8wekyb3d8bbwe. The part after the underscore is the publisher ID hash, the part before is the package name (Microsoft Learn: package identity). There is no path and no executable name. Our parser shows the package name as the program and tags the row as an app, and skips the file-path heuristics for it.

Other shapes

Occasionally you will meet a name that is neither — for example a path without the \Device\ prefix. Don't discard it; the tool classifies it as "other" and still decodes the timestamp.

The SID key's last-write time

Every registry key has a last-written FILETIME in its key node (msuhanov/regf specification). For a BAM SID key, that time moves whenever any value under it changes. It gives you:

  • A sanity check: the key time should be at or after the newest value time for that user. A key time far later than every value suggests an entry was deleted or rewritten afterwards — see BAM anti-forensics.
  • A lower bound on when the hive was last updated for that user.

Our tool shows it as "SID key last written" in the detail panel and exports it as SidKeyLastWriteUtc.

Hive-level fields that matter

A few base-block fields change how much you trust the values (libregf):

OffsetFieldWhy it matters for BAM
0regf signatureConfirms it's a hive at all
4 / 8Primary / secondary sequence numbersDifferent values = dirty hive: recent BAM writes may be in the logs
12Last written FILETIMEWhen the primary file was last flushed
48Embedded file nameDistinguishes SYSTEM from SOFTWARE, SAM… when files are renamed

How the parser maps all this

Output columnSource
LastExecutionUtcFirst 8 bytes of the value data, as ISO-8601 with 100 ns
FileTimeSame value, raw decimal
ProgramFile name, or package name without publisher hash
Path, VolumeValue name; HarddiskVolumeN when present
SID, UserKey name; account from ProfileList or well-known SID label
Service, ControlSet, ActiveControlSet, LegacyLayoutWhere in the hive the key was found
SidKeyLastWriteUtcKey node timestamp
DataHexFull value data

Drop a hive on the BAM/DAM Parser and open any row's detail panel to see each of these side by side. For the same exercise with other registry artifacts, Registry Parser covers the wider hive.

Sources

Related articles