Eye Describe Anatomy

AmCache: structure, contents and evidential limits

A registry hive that no HKEY points at, holding an inventory of the software on the machine with hashes attached. What each field means, and the difference between having been catalogued and having been run.

What this page covers

  1. An unmounted registry hive
  2. The hive records that make up an AmCache entry
  3. What the hive contains
  4. A file entry, field by field
  5. What an entry supports, and what it does not
  6. How Crow-Eye reads the hive
  7. Schema changes across Windows releases
  8. Reference
  9. Anti-forensic handling, and what survives it

An unmounted registry hive

Windows keeps an inventory of the software it has seen, with SHA-1 hashes attached, in a registry hive that nothing mounts.

C:\Windows\AppCompat\Programs\Amcache.hve the whole artifact, one file

It is a registry hive in every structural sense: a regf base block — the 64-byte header every hive file opens with — then hive bins, the 4 KB blocks a hive is allocated in, each holding cells, the variable-length records that everything else is made of. A cell tagged nk is a key node and one tagged vk is a value. Beside the file sit its own .LOG1 and .LOG2 transaction logs, which hold writes Windows has not yet merged into the hive itself. All six are the registry's own machinery, and are taken apart in Windows Registry Internals. What it is not is mounted. No HKEY points at it, regedit will not show it, and the registry API cannot reach it. The only way in is to read the file.

That single fact explains most of the artifact's forensic character. Nothing routinely inspects it, nothing cleans it, and most tooling that touches the registry never sees it at all — which is also why almost everything below had to be measured rather than looked up.

File format
regf
A registry hive like any other, so a reader that understands SYSTEM understands this. Only the contents differ.
Mounted under
no HKEY
Nothing points at it. Regedit cannot show it and the registry API cannot reach it — which is why nothing cleans it either.
Allocation unit
4,096
Bytes per hive bin, always. The hive grows by appending one rather than rewriting the file, which is what leaves deleted entries behind.
Bytes hashed
31,457,280
30 MiB and no more. A larger binary's FileId can never equal its published SHA-1, and a non-match there means nothing.

The hive records that make up an AmCache entry

AmCache keeps ordinary typed registry values rather than a packed binary structure, so the byte-level structure that applies is the hive's own - the base block, a hive bin, the nk key node that is an entry, and the vk values hanging off it - plus the one AmCache field with an encoding of its own, FileId. All of it read from Amcache.hve on the reference system.

Click any byte in a map below, or any row of the table beside it, to read the field that byte belongs to, what it decodes to and why it matters. The tabs switch between record types, and the map redraws for whichever one is selected.

Structure is real and measured. The hash shown for FileId is a placeholder of the same length, so every offset still adds up and nothing here identifies the machine it came from.

Figure 1

Where the records sit in the file

The base block, then the hive bins, then the first bin opened up. Every cell below is at its real offset and its real length; the six the map dissects are picked out. Every one of them can be hovered.

The file base block hive bins 0 4,096 8,904,704 not to scale - the bins are 2,173 times the base block The first hive bin, 4,096 to 8,191 - to scale the 32-byte hbin header - click to open its bytes nk at 4128, 120 bytes - click to open its bytes sk at 4248, 144 bytes - click to open its bytes vk at 4392, 40 bytes - click to open its bytes data at 4432, 240 bytes - click to open its bytes value list at 4672, 16 bytes - click to open its bytes 4688, 40 bytes 4728, 64 bytes 4792, 88 bytes lh at 4880, 16 bytes - click to open its bytes 4896, 104 bytes 5000, 48 bytes 5048, 16 bytes 5064, 32 bytes 5096, 72 bytes 5168, 216 bytes 5384, 120 bytes 5504, 40 bytes 5544, 96 bytes 5640, 16 bytes 5656, 32 bytes 5688, 96 bytes 5784, 120 bytes 5904, 40 bytes 5944, 96 bytes 6040, 16 bytes 6056, 32 bytes 6088, 96 bytes 6184, 120 bytes 6304, 40 bytes 6344, 96 bytes 6440, 16 bytes 6456, 32 bytes 6488, 96 bytes 6584, 120 bytes 6704, 40 bytes 6744, 96 bytes 6840, 16 bytes 6856, 32 bytes 6888, 96 bytes 6984, 104 bytes 7088, 40 bytes 7128, 96 bytes 7224, 16 bytes 7240, 32 bytes 7272, 96 bytes 7368, 104 bytes 7472, 40 bytes 7512, 96 bytes 7608, 72 bytes 7680, 32 bytes 7712, 48 bytes 7760, 64 bytes 7824, 32 bytes 7856, 24 bytes 7880, 40 bytes 7920, 24 bytes 7944, 40 bytes 7984, 32 bytes 8016, 32 bytes 8048, 16 bytes 8064, 40 bytes 8104, 24 bytes 8128, 40 bytes 8168, 16 bytes 8184, 8 bytes nk 4128 sk 4248 vk 4392 data 4432 value list 4672 lh 4880 32-byte bin header, then 65 cells. A cell is allocated when its size is negative; freed cells keep their contents.
allocatedfreed - still holds its contentsdissected below, in its own colour

65 cells in that one bin. The whole hive is bins like it, appended rather than rewritten.

Figure 2

How the records reach each other

Every arrow is a pointer that was read and followed. Hover an arrow or a record to read what it is; click to open its bytes in the map below.

root key offset: the base block names exactly one key, and this is it, resolves to 4128 root key offset security descriptor offset: descriptors are shared, so this points at a cell many keys point at, resolves to 4248 security descriptor offset value list offset: to a bare list of offsets, not to the values, resolves to 4672 value list offset subkey list offset: the pointer a tree walk follows - and the one unlinked on delete. It lands on an lh; the lh the map dissects is a richer one elsewhere in the hive, resolves to 4880 subkey list offset first entry: one hop per value; an entry's properties are scattered, resolves to 4392 first entry first pair: each pair is an offset and a hash of the child's name - read from the root's own list, not from the lh on display, resolves to 4792 first pair parent key offset: measured on the child, because a hive's root key has no parent, resolves to 4128 parent key offset data offset: unless the inline bit is set, in which case there is no arrow, resolves to 4432 data offset key name: not a pointer key name value data: not a pointer value data Show the Base block bytesBase blockregf, 4096 B Show the Key node bytesKey nodenk Show the Security descriptor bytesSecurity descriptorsk Show the Value list bytesValue listno signature Show the Subkey list bytesSubkey listlh / lf Show the Value bytesValuevk Show the Child key node bytesChild key nodenk Show the Value data bytesValue datano signature Show the FileId bytesFileId44 characters

8 pointers, each re-read from the hive when this page was built - an arrow whose target does not carry the signature it claims fails the build rather than being drawn. The dashed links are not pointers: FileId is not a record, it is the content of a key name and of a value.

Figure 3

The records byte by byte

Live Byte Selection
Table 1

Every field of every record

The same fields as a table. Both are drawn from one definition, so the map and the table cannot disagree with each other. Every field has its own write-up: none is shared with another, which the generator now refuses to allow.

OffsetSizeFieldWhat it is
Table 2

Every key under Root, and where it lands

The categories the Appraiser catalogues, what Crow-Eye does with each, and - behind each row - what every one of that table's columns means. Ten keys are stored one row per registry value rather than as fixed columns: DeviceCensus, which carries a couple of hundred distinct value names and gains more every Windows release, and nine that were empty on every system available here. For those nine the columns could not be verified against real data, so none were invented - what the key is FOR is still worth stating, and is stated.

The From column is measured, not asserted, and every column in this table and the next says where it was read from. value ProgramId is a named registry value, spelled as the hive spells it rather than as the database column is named - those differ, because the column name is normalised. Every named value lives in a vk record: its name at vk +0x14, its data reached through vk +0x08, both dissected in Figure 3 above. That is true of all of them, so it is said here once instead of in every row. nk +0x04 and the like are not values at all: they are fields of the records themselves, cited at the offset the byte map gives. from x is normalised from another column in the same table. the carve is neither - it is where a recovered cell sat, or the fact that it was found in free space at all. bookkeeping is parsed_at, which has no origin in the hive and should not appear to have one. value, absent here is a value name the parser expects and this hive does not carry. Names and types only - no values from the reference system appear anywhere on this page.

Key under RootCrow-Eye tableStored asWhat it holds
DeviceCensusDeviceCensusone row per valueMachine identity - Entra device id, activation channel, whether this is a VM. Hundreds of distinct value names, and more with every Windows release.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
DriverPackageExtendedDriverPackageExtendedone row per valueExtended attributes of driver packages, beyond what InventoryDriverPackage records. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryAcpiPhatHealthRecordInventoryAcpiPhatHealthRecordone row per valueFirmware component health, as ACPI's Platform Health Assessment Table reports it - a machine's own firmware saying whether a component is failing. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryAcpiPhatVersionElementInventoryAcpiPhatVersionElementone row per valueFirmware component versions from the same ACPI table, which is how firmware-level change becomes visible at all. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryApplicationInventoryApplication26 columnsInstalled programs: install date and source, uninstall string, the Uninstall key, and sometimes the installing user's SID.
What its 26 columns hold
ColumnFromTypeWhat it means
program_idvalue ProgramIdREG_SZThe identifier for this installed program, and the join key to InventoryApplicationFile. Derived by Windows from the name, version, publisher and language, so the same product installed on two machines gets the same one.
namevalue NameREG_SZThe display name of the program, as an installer registered it.
versionvalue VersionREG_SZThe program's version string, as its installer declared it.
publishervalue PublisherREG_SZThe publisher string from the installer - not a signature, and not verified by anything. It is whatever was typed.
languagevalue LanguageREG_DWORDThe installer's language, as a Windows LCID. 65535 means language-neutral.
sourcevalue SourceREG_SZWhere Windows learned about this program - Msi, AddRemoveProgram, AppxPackage and others. The published enumerations of this field are incomplete: this hive also carries AddRemoveProgramPerUser and Steam.
root_dir_pathvalue RootDirPathREG_SZThe directory the program installed itself into.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
program_instance_idvalue ProgramInstanceIdREG_SZA per-installation identifier, distinct from ProgramId: the same product installed twice gets one ProgramId and two of these.
store_app_typevalue StoreAppTypeREG_SZFor a Store application, which kind it is - UWP, Centennial and so on. Empty for a desktop installer.
inbox_modern_appvalue InboxModernAppREG_DWORD1 when this Store app shipped with Windows rather than being installed. Separates what the image brought from what somebody added.
manifest_pathvalue ManifestPathREG_SZPath to the app manifest, for a packaged application.
package_full_namevalue PackageFullNameREG_SZThe full package identity of a Store app - name, version, architecture and publisher hash in one string.
install_datevalue InstallDateREG_SZThe install date as the program's own registration recorded it, as a LOCAL-TIME string in whatever format the installer chose. It is not normalised and not UTC - see install_date_utc.
hidden_arpvalue HiddenArpREG_DWORD1 when the program hides itself from Add/Remove Programs. Legitimate for components; also exactly what something that does not want to be uninstalled does.
uninstall_stringvalue UninstallStringREG_SZThe command Add/Remove Programs would run to remove it - which names the uninstaller binary, and therefore a path the installer wrote to.
registry_key_pathvalue RegistryKeyPathREG_SZThe Uninstall key this program was read from. It points at the registry evidence directly, so the claim can be checked in SOFTWARE or NTUSER.
msi_package_codevalue MsiPackageCodeREG_SZFor an MSI install, the package code GUID - it identifies the .msi file itself.
msi_product_codevalue MsiProductCodeREG_SZFor an MSI install, the product code GUID - it identifies the product, and stays the same across its minor updates.
msi_install_datevalue MsiInstallDateREG_SZThe install date MSI recorded, as its own string. Normalised into msi_install_date_utc beside it.
bundle_manifest_pathvalue BundleManifestPathREG_SZPath to the bundle manifest, for a packaged bundle.
user_sidvalue UserSidREG_SZThe SID of the user this program was installed for, when the install was per-user. It attributes an installation to an account, which is why a per-user install is worth more here than a machine-wide one.
install_date_utcfrom install_datenormalisedinstall_date parsed and normalised to UTC by Crow-Eye. The raw string is kept beside it and never overwritten, because the order of its month and day is ambiguous and the original is the only way to check the reading.
msi_install_date_utcfrom msi_install_datenormalisedmsi_install_date parsed and normalised to UTC. NULL when the string could not be read as a plausible date, rather than guessed at.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryApplicationAppVInventoryApplicationAppVone row per valueApp-V packages - applications delivered virtualised rather than installed, which leave a different trail from an ordinary install. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryApplicationDriverInventoryApplicationDriverone row per valueDrivers shipped as part of an application rather than for a device. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryApplicationFileInventoryApplicationFile25 columnsEvery executable the Appraiser has seen: path, SHA-1, size, publisher, and the ProgramId that links it to an installed program.
What its 25 columns hold
ColumnFromTypeWhat it means
program_idvalue ProgramIdREG_SZThe installed program this file belongs to, or empty. Joins to InventoryApplication; when it joins to nothing the binary belonged to no installed program, which is what program_association records.
file_idvalue FileIdREG_SZThe 44-character FileId: four zeros then a SHA-1. The four zeros are not part of the hash - substr(file_id, 5) is. It hashes only the first 30 MiB of the file.
lower_case_long_pathvalue LowerCaseLongPathREG_SZThe full path of the binary, lower-cased. The path AmCache saw; the file itself may be long gone.
namevalue NameREG_SZThe file name alone, without its directory.
binary_typevalue BinaryTypeREG_SZThe image's architecture - pe32, pe64_amd64, pe32_arm and so on. A 32-bit binary in a 64-bit system directory is worth a look.
link_datevalue LinkDateREG_SZThe PE link timestamp, as a string. It describes when the binary was COMPILED, not when it arrived or ran, and it is attacker-controlled - a value in the future or in 1970 means it was forged, not that a clock was wrong.
sizevalue SizeREG_QWORDThe file's size in bytes, as a 64-bit value, when AmCache saw it.
languagevalue LanguageREG_DWORDThe binary's language resource, as a Windows LCID.
usnvalue UsnREG_QWORDThe USN journal sequence number of the change that brought this file to the Appraiser's attention. It orders file activity on the volume, and it is the join to the USN journal itself. Explained in full: the USN record
bin_file_versionvalue BinFileVersionREG_SZThe file version from the PE version resource, as the binary declares it.
bin_product_versionvalue BinProductVersionREG_SZThe product version from the PE version resource, as the binary declares it.
product_versionvalue ProductVersionREG_SZThe product version Windows resolved for this file, which can differ from the resource above.
versionvalue VersionREG_SZThe file version Windows resolved for this file.
product_namevalue ProductNameREG_SZThe product name from the PE version resource. Freely settable by whoever built the binary.
publishervalue PublisherREG_SZThe company name from the PE version resource. It is metadata, not a signature - a binary claiming Microsoft here has proved nothing.
original_file_namevalue OriginalFileNameREG_SZThe name the binary was BUILT with, from its version resource. When it disagrees with name the file was renamed after it was compiled, and that disagreement is one of the cheapest renamed-tool detections there is.
appx_package_full_namevalue AppxPackageFullNameREG_SZThe Store package this file belongs to, when it belongs to one.
is_os_componentvalue IsOsComponentREG_DWORD1 when Windows considers this file part of the operating system. It is what separates the image's own binaries from everything added to it.
appx_package_relative_idvalue AppxPackageRelativeIdREG_SZThe application's identity within its Store package, for a package that ships more than one.
link_date_utcfrom link_datenormalisedlink_date normalised to UTC by Crow-Eye. Still a compile time - normalising it does not make it an execution time.
file_id_is_partialfrom file_idnormalisedCrow-Eye's own flag: 1 when the file was larger than 30 MiB, so its FileId hashes only the first 31,457,280 bytes and can never equal a published SHA-1. Without it a non-match reads as "not this file". Decided from the cached size, so it works against an image with no filesystem; NULL when there is no size, rather than guessed.
file_id_verifiedfrom file_idnormalisedCrow-Eye re-hashed the file on disk and compared. Only ever populated on a live parse, and only when asked - an offline parse has no filesystem to hash.
program_associationvalue, absent here-associated when this file's ProgramId resolves to an installed program, unassociated when it does not. Unassociated is where dropped and portable binaries live. Computed in one pass after both tables are written, because the files are parsed before the programs.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryApplicationFrameworkInventoryApplicationFrameworkone row per valueFrameworks an application depends on - runtimes and framework packages that arrive with something else rather than on their own. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryApplicationShortcutInventoryApplicationShortcut7 columnsShortcuts - a Start menu or desktop presence.
What its 7 columns hold
ColumnFromTypeWhat it means
shortcut_pathvalue ShortcutPathREG_SZWhere the .lnk sits - a Start menu or desktop location. A shortcut is a presence, not an execution.
shortcut_target_pathvalue ShortcutTargetPathREG_SZWhat the shortcut points at.
shortcut_aumidvalue ShortcutAumidREG_SZThe Application User Model ID the shortcut launches, for a packaged or modern app. The same identity the Jump Lists and the taskbar use.
shortcut_program_idvalue ShortcutProgramIdREG_SZThe installed program this shortcut belongs to, joining back to InventoryApplication.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDeviceContainerInventoryDeviceContainer18 columnsDevices as containers, including things attached once.
What its 18 columns hold
ColumnFromTypeWhat it means
model_namevalue ModelNameREG_SZThe device model as the device reports it.
iconvalue IconREG_SZThe icon resource Windows shows for the device.
friendly_namevalue FriendlyNameREG_SZThe name a user sees, and often one a user CHOSE - a phone or headset named after its owner attributes the device to a person.
model_numbervalue ModelNumberREG_SZThe manufacturer's model number for the device.
manufacturervalue ManufacturerREG_SZThe device's manufacturer string.
model_idvalue ModelIdREG_SZA GUID identifying the device model.
primary_categoryvalue PrimaryCategoryREG_SZThe device's main category - audio, imaging, printer, phone.
categoriesvalue CategoriesREG_SZAll categories the device claims, not only the primary one.
is_machine_containervalue IsMachineContainerREG_SZ1 for the container that represents this computer itself, rather than something attached to it.
discovery_methodvalue DiscoveryMethodREG_SZHow Windows found the device - USB enumeration, network discovery, pairing.
is_connectedvalue IsConnectedREG_SZWhether the device was connected when the Appraiser last looked. A snapshot of that moment, not a history.
is_activevalue IsActiveREG_SZWhether the device was active at that moment.
is_pairedvalue IsPairedREG_SZWhether the device is paired - a Bluetooth device that was deliberately associated with this machine.
is_networkedvalue IsNetworkedREG_SZWhether the device is reached over the network.
statevalue StateREG_DWORDThe container's state, as a numeric code.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDeviceInterfaceInventoryDeviceInterface23 columnsSensor capabilities the machine reports.
What its 23 columns hold
ColumnFromTypeWhat it means
accelerometer3_dvalue Accelerometer3DREG_SZWhether a three-axis accelerometer interface is present. Note the value name: Accelerometer3D defeats the obvious CamelCase rule, which is why the parser normalises both sides before matching.
activity_detectionvalue ActivityDetectionREG_SZWhether an activity-detection sensor is present.
ambient_lightvalue AmbientLightREG_SZWhether an ambient light sensor is present - the one that drives automatic screen brightness.
barometervalue BarometerREG_SZWhether a barometric pressure sensor is present.
customvalue CustomREG_SZWhether a vendor-defined custom sensor interface is present.
floor_elevationvalue FloorElevationREG_SZWhether a floor-elevation sensor is present.
geomagnetic_orientationvalue GeomagneticOrientationREG_SZWhether a geomagnetic orientation sensor - a compass - is present.
gravity_vectorvalue GravityVectorREG_SZWhether a gravity-vector sensor is present.
gyrometer3_dvalue Gyrometer3DREG_SZWhether a three-axis gyrometer is present.
humidityvalue HumidityREG_SZWhether a humidity sensor is present.
linear_accelerometervalue LinearAccelerometerREG_SZWhether a linear accelerometer, with gravity removed, is present.
magnetometer3_dvalue Magnetometer3DREG_SZWhether a three-axis magnetometer is present.
orientationvalue OrientationREG_SZWhether an absolute device-orientation sensor is present.
pedometervalue PedometerREG_SZWhether a step-counting sensor is present.
proximityvalue ProximityREG_SZWhether a proximity sensor is present - the one that detects a hand or a face near the device.
relative_orientationvalue RelativeOrientationREG_SZWhether a relative orientation sensor, measured from a starting position, is present.
simple_device_orientationvalue SimpleDeviceOrientationREG_SZWhether the coarse portrait/landscape orientation sensor is present.
temperaturevalue TemperatureREG_SZWhether a temperature sensor is present.
energy_metervalue EnergyMeterREG_SZWhether an energy-metering interface is present.
hinge_anglevalue HingeAngleREG_SZWhether a hinge-angle sensor is present - a foldable or dual-screen machine.
presence_capabilitiesvalue PresenceCapabilitiesREG_DWORDA bitmask of human-presence capabilities: what the machine can detect about somebody sitting in front of it.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDeviceMediaClassInventoryDeviceMediaClass4 columnsAudio render and capture drivers.
What its 4 columns hold
ColumnFromTypeWhat it means
audio_render_drivervalue Audio_RenderDriverREG_SZThe driver behind audio OUTPUT - the playback devices.
audio_capture_drivervalue Audio_CaptureDriverREG_SZThe driver behind audio INPUT. It names what could record on this machine, which is the half of audio that matters in an investigation.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDevicePciInventoryDevicePcione row per valuePCI devices in bus terms - what is physically in the slots. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDevicePnpInventoryDevicePnp39 columnsDevices with InstallDate and FirstInstallDate. Live device properties are denied to a running system, so for a device attached once this is often the only source.
What its 39 columns hold
ColumnFromTypeWhat it means
modelvalue ModelREG_SZThe device model string, as PnP reports it.
manufacturervalue ManufacturerREG_SZThe device manufacturer string, as PnP reports it.
driver_namevalue DriverNameREG_SZThe driver file serving this device - the join to InventoryDriverBinary.
parent_idvalue ParentIdREG_SZThe device instance this one hangs off. It rebuilds the PnP tree, which is how a USB device is tied to the port and hub it was plugged into.
matching_idvalue MatchingIDREG_SZThe hardware id the INF actually matched to pick this driver, out of everything the device advertised.
classvalue ClassREG_SZThe device setup class - USB, DiskDrive, Net, Keyboard.
class_guidvalue ClassGuidREG_SZThe GUID of that setup class.
descriptionvalue DescriptionREG_SZThe device description from its INF.
enumeratorvalue EnumeratorREG_SZWhich bus enumerated it - USB, PCI, SWD, BTH. It says how the thing was attached.
servicevalue ServiceREG_SZThe kernel service or driver that owns the device, as named in CurrentControlSet\Services.
install_statevalue InstallStateREG_SZWhether the device installed successfully.
device_statevalue DeviceStateREG_SZA bitmask of the device's current PnP state.
infvalue InfREG_SZThe INF file that installed the driver. An oem*.inf name means a third-party driver rather than an in-box one.
driver_ver_datevalue DriverVerDateREG_SZThe driver's version date, as the string the INF carries.
install_datevalue InstallDateREG_SZWhen this device was first installed on this machine. Live device properties are denied even to an elevated process, so for a device attached once and never again this is often the only record that it was here at all.
first_install_datevalue FirstInstallDateREG_SZThe first time a device of this kind was installed. It survives a reinstall of the same device, so this and install_date disagreeing means the device came back.
driver_package_strong_namevalue DriverPackageStrongNameREG_SZThe full identity of the driver package - the join to InventoryDriverPackage.
driver_ver_versionvalue DriverVerVersionREG_SZThe driver version the INF declares.
container_idvalue ContainerIdREG_SZThe container this device belongs to - the join to InventoryDeviceContainer. One physical thing shows up as several PnP devices sharing one container id.
problem_codevalue ProblemCodeREG_SZThe PnP problem code, when the device did not start cleanly. Non-zero is the Device Manager warning triangle.
providervalue ProviderREG_SZWho provided the driver, as the INF declares it.
driver_idvalue DriverIdREG_SZThe identifier of the driver serving this device.
bus_reported_descriptionvalue BusReportedDescriptionREG_SZThe description the DEVICE itself reported over the bus, rather than anything the INF said. For a USB stick it is often the only place its own product string is written down.
hwidvalue HWIDREG_SZEvery hardware id the device advertised, in priority order. For USB it carries the VID and PID, which identify the make and model of the actual thing.
extended_infsvalue ExtendedInfsREG_SZAny extension INFs layered on top of the base driver.
compidvalue COMPIDREG_SZThe compatible ids the device advertised - the looser match, used when no hardware id matches.
stackidvalue STACKIDREG_SZThe device stack identifiers.
upper_class_filtersvalue UpperClassFiltersREG_SZFilter drivers layered above every device of this class. A filter driver is a legitimate mechanism and a well-worn place to sit in the path of a device's data.
lower_class_filtersvalue LowerClassFiltersREG_SZFilter drivers layered below every device of this class.
upper_filtersvalue UpperFiltersREG_SZFilter drivers layered above this device specifically.
lower_filtersvalue LowerFiltersREG_SZFilter drivers layered below this device specifically.
device_interface_classesvalue DeviceInterfaceClassesREG_SZThe interface classes this device exposes - what it lets software ask of it.
location_pathsvalue LocationPathsREG_SZWhere the device physically sits, as a path through the buses - which PCI slot, which USB port.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
install_date_utcfrom install_datenormalisedinstall_date normalised to UTC by Crow-Eye, with the raw string kept beside it.
first_install_date_utcfrom first_install_datenormalisedfirst_install_date normalised to UTC by Crow-Eye.
driver_ver_date_utcfrom driver_ver_datenormaliseddriver_ver_date normalised to UTC. It dates the DRIVER, not the installation.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDeviceSensorInventoryDeviceSensorone row per valueSensors as devices, as opposed to InventoryDeviceInterface, which records only which sensor capabilities exist. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDeviceUsbHubClassInventoryDeviceUsbHubClass4 columnsHow many user-connectable USB ports the machine has.
What its 4 columns hold
ColumnFromTypeWhat it means
total_user_connectable_portsvalue TotalUserConnectablePortsREG_DWORDHow many USB ports a person can physically reach on this machine. It bounds how many things could have been plugged in at once.
total_user_connectable_type_cportsvalue TotalUserConnectableTypeCPortsREG_DWORDHow many of those ports are Type-C. The value name is TotalUserConnectableTypeCPorts, which no CamelCase rule splits correctly - the second reason the parser normalises both sides.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDriverBinaryInventoryDriverBinary23 columnsDrivers, with signing state. Where an unsigned driver shows up.
What its 23 columns hold
ColumnFromTypeWhat it means
driver_namevalue DriverNameREG_SZThe driver file's path. This is the row a suspicious kernel driver appears in.
infvalue InfREG_SZThe INF that installed it. An oem*.inf name means third-party.
driver_versionvalue DriverVersionREG_SZThe driver's version string.
productvalue ProductREG_SZThe product the driver belongs to.
product_versionvalue ProductVersionREG_SZThe product version from the driver's version resource.
wdf_versionvalue WdfVersionREG_SZThe Windows Driver Framework version it was built against, when it uses WDF at all.
driver_companyvalue DriverCompanyREG_SZThe company named in the driver binary's version resource. Metadata, not a signature.
driver_package_strong_namevalue DriverPackageStrongNameREG_SZThe driver package this binary came from - the join to InventoryDriverPackage.
servicevalue ServiceREG_SZThe service name the driver runs under, in CurrentControlSet\Services.
driver_in_boxvalue DriverInBoxREG_SZ1 when the driver shipped with Windows. Everything else was put here by somebody.
driver_signedvalue DriverSignedREG_SZWhether the driver is signed. This is where an unsigned kernel driver becomes visible - and note the name: several published references call this field DigitalSignature, which does not exist in the hive.
driver_is_kernel_modevalue DriverIsKernelModeREG_SZ1 for a kernel-mode driver. Kernel mode is the difference between a bad program and a bad machine.
driver_idvalue DriverIdREG_SZThe driver's identifier, joining back to the PnP devices.
driver_last_write_timevalue DriverLastWriteTimeREG_SZThe driver file's last write time, as a string. Published references call this field LastModified; the hive does not.
driver_typevalue DriverTypeREG_DWORDA bitmask describing what kind of driver this is.
driver_time_stampvalue DriverTimeStampREG_DWORDThe driver's PE link timestamp, as a Unix epoch integer - not a FILETIME, and not the same encoding as the string dates elsewhere in this table.
driver_check_sumvalue DriverCheckSumREG_DWORDThe PE header checksum of the driver image.
image_sizevalue ImageSizeREG_DWORDThe size of the driver image in memory, in bytes.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
driver_last_write_time_utcfrom driver_last_write_timenormaliseddriver_last_write_time normalised to UTC by Crow-Eye.
driver_time_stamp_utcfrom driver_time_stampnormaliseddriver_time_stamp converted from its Unix epoch to UTC by Crow-Eye. Reading it as a FILETIME instead produces a plausible date in the wrong century, with no error.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryDriverPackageInventoryDriverPackage17 columnsDriver packages - INF, provider, hardware ids.
What its 17 columns hold
ColumnFromTypeWhat it means
class_guidvalue ClassGuidREG_SZThe setup class GUID the package installs into.
classvalue ClassREG_SZThe setup class name.
directoryvalue DirectoryREG_SZThe DriverStore directory the package was staged into. The package survives there after the device is gone.
datevalue DateREG_SZThe package's date, as the string the INF carries.
versionvalue VersionREG_SZThe driver package version.
providervalue ProviderREG_SZThe provider named in the INF.
submission_idvalue SubmissionIdREG_SZThe Windows Hardware submission id, for a package that went through Microsoft's signing process. A third-party package with none did not.
driver_in_boxvalue DriverInBoxREG_SZ1 when the package shipped with Windows.
infvalue InfREG_SZThe INF file name of the package.
flight_idsvalue FlightIdsREG_SZFlighting identifiers, when the package came through one.
recovery_idsvalue RecoveryIdsREG_SZRecovery identifiers associated with the package.
is_activevalue IsActiveREG_SZWhether this package is the one currently serving its devices. Superseded packages stay in the store.
hwidsvalue HwidsREG_SZEvery hardware id this package claims to drive.
sysfilevalue SYSFILEREG_SZThe driver system file the package installs.
date_utcfrom datenormaliseddate normalised to UTC by Crow-Eye, with the raw string kept beside it.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryMiscellaneousInventoryMiscellaneous4 columnsAssorted per-machine flags.
What its 4 columns hold
ColumnFromTypeWhat it means
existsvalue ExistsREG_DWORDWhether the thing this row is about is present. The row's key name says what that thing is; this is the answer.
valuevalue ValueREG_SZThe measurement itself, for a row that carries one rather than a yes/no.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryMiscellaneousMemorySlotArrayInfoInventoryMiscellaneousMemorySlotArrayInfo13 columnsPhysical memory slots.
What its 13 columns hold
ColumnFromTypeWhat it means
slotvalue SlotREG_DWORDWhich physical memory slot this row describes.
typevalue TypeREG_DWORDThe memory type, as an SMBIOS code - DDR4, DDR5 and so on.
type_detailsvalue TypeDetailsREG_DWORDSMBIOS type detail bits for the module.
speedvalue SpeedREG_DWORDThe module's rated speed.
capacityvalue CapacityREG_QWORDThe module's capacity in bytes, as a 64-bit value.
modelvalue ModelREG_SZThe memory module's model, as the module reports it.
manufacturervalue ManufacturerREG_SZThe memory module's manufacturer.
total_widthvalue TotalWidthREG_DWORDThe module's total data width in bits, including ECC.
data_widthvalue DataWidthREG_DWORDThe module's data width in bits, excluding ECC. Compared with the total width it says whether the module carries error correction.
memory_error_correctionvalue MemoryErrorCorrectionREG_DWORDThe error-correction scheme, as an SMBIOS code.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryMiscellaneousUserInventoryMiscellaneousUser7 columnsPer-user identifiers such as AdvertisingID.
What its 7 columns hold
ColumnFromTypeWhat it means
original_namevalue OriginalNameREG_SZWhat this per-user item is - the key's own subject, such as an advertising identifier.
existsvalue ExistsREG_DWORDWhether the item is present for this user.
valuevalue ValueREG_SZThe identifier itself. This is where the AdvertisingID lands - a per-user value that follows the account rather than the machine.
user_idvalue UserIdREG_DWORDThe local identifier of the user this row belongs to.
standard_user_hashvalue StandardUserHashREG_DWORDA hash standing in for the user's identity, so the telemetry can be per-user without carrying a name.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryMiscellaneousUUPInfoInventoryMiscellaneousUupInfo8 columnsUpdate packages.
What its 8 columns hold
ColumnFromTypeWhat it means
identifiervalue, absent here-The update package's identifier.
versionvalue, absent here-The version this update package delivers.
sourcevalue, absent here-Where the update came from - the servicing channel. Not an install source: the same column name in InventoryApplication means something else.
previous_versionvalue, absent here-The version being replaced. With version it gives the before and after of a servicing step, and dates when the image changed.
last_activated_versionvalue, absent here-The last version actually activated.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
InventoryMiscellaneousWAMAccountsInventoryMiscellaneousWAMAccountsone row per valueWeb Account Manager accounts - the Microsoft, work and school accounts this machine has signed in. Empty here, so its columns are stored one row per value.
What its 5 columns hold
ColumnFromTypeWhat it means
entrynk +0x4Ckey nameThe key this row came from - the subkey name under this Root key. Every row of one entry shares it, which is how the rows are grouped back into an entry.
namevk +0x14value nameThe registry value's name, exactly as the hive spells it.
valuevk +0x08 → datavalue dataThe registry value's data, as text.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
MareMare10 columnsCompatibility entries. The real install directory is inside the restore value; sdbentryguid names the shim database entry.
What its 10 columns hold
ColumnFromTypeWhat it means
flagsvalue flagsREG_DWORDThe compatibility flags applied to this entry.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
restorevalue restoreREG_SZA delimited blob, and the useful field is inside it - RootDirPath|C:\...;AddPlaceholderCustom|.... Kept whole, because a decode should be checkable against the original.
root_dir_pathvalue, absent here-The real install directory, decoded out of restore by Crow-Eye. A decode of a field that is present, not a reconstruction: it stays NULL when restore carries no RootDirPath. It replaced a path that was being built by hand, and the difference is measurable - 245 of 362 of these exist on disk, against 0 of 200 of the invented ones.
sdbentryguidvalue sdbentryguidREG_SZThe shim database entry this compatibility fix comes from. It names the entry inside the SDB, which is where a custom shim database - an old and still-working persistence technique - would show up.
pathvalue pathREG_SZThe path of the executable this compatibility entry is about.
program_idvalue programIdREG_SZThe installed program this entry belongs to, joining to InventoryApplication. Note the hive spells it programId here and ProgramId elsewhere - one of the reasons column matching is case-insensitive.
farvalue farREG_QWORDA 64-bit value the compatibility layer keeps with the entry.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's key. In Mare specifically it orders nothing: on the reference system EVERY row shares one timestamp, because the Appraiser wrote them in a single batch.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
MareBackupAppsMareBackupApps5 columnsCarries SidState, which contains a user SID.
What its 5 columns hold
ColumnFromTypeWhat it means
hashvalue HashREG_QWORDA 64-bit hash identifying the backed-up application entry.
sid_statevalue SidStateREG_SZCarries a user SID, which is what makes this small table worth reading: it attributes the entry to an account.
default_valuevalue (unnamed)variesThe key's unnamed default value. Read because a value with no name is still a value, and most AmCache readers drop it silently.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
Table 3

Three tables that are not keys under Root

Crow-Eye writes these itself. They carry no value the Appraiser wrote, which is exactly why they are worth reading: two of them are entries somebody deleted, and the third is the evidence that nothing was quietly skipped. Every column below is read from the carved record itself, or from where it was found, and the From column names which.

Crow-Eye tableStored asWhat it holds
AmcacheCarvedKeys9 columnsDeleted entries recovered from freed cells. No reference AmCache parser produces this at all. It needs free space to read, so it pays off on a hive taken from an image or a shadow copy - a live export is a reorganised hive whose free space has already gone, and the parse says so rather than reporting nothing.
What its 9 columns hold
ColumnFromTypeWhat it means
cell_offsetthe carvefile offsetWhere in the hive file the recovered cell sits. It is the row's provenance - the claim can be checked by going back to that offset in the evidence.
key_namenk +0x4Ckey nameThe deleted key's own name, still sitting in the freed cell. For a file entry that name IS the FileId.
key_pathnk +0x10 chainedpathThe path the key had, when the chain of parents up to a surviving key could be walked.
parent_resolvedfrom key_pathflagWhether that walk succeeded. When it did not, the key is recorded without a path rather than being given a guessed one.
key_last_writenk +0x04FILETIMEThe deleted key's LastWriteTime, recovered with it - when the Appraiser wrote the entry that somebody later removed.
subkey_countnk +0x14UInt32How many subkeys the deleted key claimed.
value_countnk +0x24UInt32How many values the deleted key claimed.
record_statethe carvealways deletedHow the record was found - which is the difference between a cell that was freed and one that was only unlinked. It is the honesty column: it says what kind of recovery this row is.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
AmcacheCarvedValues10 columnsThe values belonging to those recovered entries, grouped back onto them by the cell offset of their parent key.
What its 10 columns hold
ColumnFromTypeWhat it means
cell_offsetthe carvefile offsetWhere in the hive file this recovered value cell sits.
parent_cell_offsetthe carvefile offsetThe cell offset of the key this value belonged to, which is how recovered values are grouped back into recovered entries.
key_pathnk +0x10 chainedpathThe path of that parent key, when it could be resolved.
value_namevk +0x14value nameThe deleted value's name.
value_typevk +0x0CREG_* codeThe registry type the deleted value declared.
data_sizevk +0x04UInt32How many bytes of data the value declared.
is_inlinevk +0x04 bit 31flag1 when the data was four bytes or fewer and stored inside the value record itself rather than in a cell of its own. Inline data survives carving better, because there is no second cell to have been reused.
datavk +0x08 → databytesThe recovered data, as text.
record_statethe carvealways deletedHow this value record was found.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.
UnknownSubkeys4 columnsAnything under Root this parser has no schema for, kept whole. A new Windows release adding a key lands here instead of being dropped in silence.
What its 4 columns hold
ColumnFromTypeWhat it means
subkey_namenk +0x4Ckey nameThe name of a key under Root that this parser has no schema for. It is the coverage column: a new Windows release adding a key shows up here instead of being dropped in silence.
datank +0x28 → valuesJSONEverything that key held, kept as text so nothing is lost.
key_last_writenk +0x04FILETIMEThe LastWriteTime of this entry's own registry key: when the Appraiser wrote the entry. The Appraiser writes in batches, so how much ordering it supports varies by table - see the nk tab above.
parsed_atbookkeeping-When Crow-Eye read the hive. Bookkeeping - it belongs on no timeline and describes the examination, not the evidence.

Where these figures come from. The figures here were measured from the Amcache.hve of a live Windows 11 system - the reference system. The file hash is replaced by a placeholder of the same length; the key counts, value counts and sizes are real.

What the hive contains

AmCache is written by the Application Experience service, whose job is to know what software is on the machine so compatibility fixes can be applied to it. The hive is that knowledge, written down. Every key under Root is a category of thing the service catalogues.

Each of those categories is one key under Root, and each thing catalogued is one key beneath that, with its properties as registry values. There is no packed record format to decode: the registry already provides one, which is why Figure 3 maps hive records rather than AmCache ones.

How it gets written

Nothing writes AmCache as programs run. The Microsoft Compatibility Appraiser - a scheduled task running compattelrunner.exe - wakes on its own schedule, walks what it is configured to look at, and writes what it found. Three consequences follow from that single fact, and they explain most of the mistakes made with this artifact:

What survives that process is unusually durable. Deleting the file does not remove its entry, so AmCache routinely describes binaries that are no longer on disk: 790 of the 2,423 recorded paths on the reference system, 32.6 per cent, no longer resolve to a file.

The association between a file entry and an installed program is weaker than it is often taken to be. On the reference system 3,786 of 5,212 file entries - 72.6 per cent - carry a ProgramId that matches no InventoryApplication row, so “belongs to no installed program” describes most of the table and separates almost nothing. Narrowing it to entries that also hold an on-disk path and are not flagged IsOsComponent leaves 930 entries, 17.8 per cent, and that is the population in which a dropped or portable executable would sit.

A file entry, field by field

Table 4

Entries are not uniform in size, and the distribution is bimodal. Of the 5,212 file entries on the reference system, 2,789 hold no path at all: every one carries an AppxPackageFullName, and each is described by one or two values. The remaining 2,423 hold a path and carry a median of 16 values, ranging from 8 to 18. The table below lists the value names of the second population. The names are the schema; the contents belong to the reference system and are not reproduced.

ValueWhat it holdsWorth knowing
FileIdthe file's SHA-1prefixed with four zeros. A lookup against the unmodified string matches nothing
LowerCaseLongPathfull path, lowercasedlowercasing is lossy on a case-sensitive comparison
Namethe file nameas catalogued, which may differ from the path's tail
Sizefile size in bytesa cheap first check against a suspected binary
LinkDatePE compile timefrom the binary's own header, and trivially forged
BinaryTypePE32 or PE64-
ProductName, ProductVersion, Publisherversion resourceattacker-controlled: it is whatever the binary claims
UsnUSN journal record numberties the entry to the file system journal, if it still holds that record
ProgramIdlink to InventoryApplicationhow a file joins to the program that installed it

FileId is not a hash until the prefix is removed

FileId is the SHA-1 with four leading zeros in front of it. Submitted to a hash set unmodified it matches nothing, and the negative result is readily misread as evidence that the file is not known malware when it establishes only that the string submitted was not a hash. The failure is silent and its direction is towards a clean verdict, which is what makes it consequential.

What an entry supports, and what it does not

Presence, not execution

An AmCache entry means the Application Experience service catalogued the file. That happens at install time, during scheduled inventory scans, and when the file is examined - not only when it runs. Treating every entry as an execution turns a list of what was on the disk into a list of what someone did, and those are different claims.

The timestamps come from several different clocks

LinkDate is from the PE header and is whatever the compiler wrote, or whatever an attacker set. The key's own last-written time is when the service catalogued it. The install date on the parent program entry is a third thing again. They are routinely quoted interchangeably and they mean quite different things.

Where it is strong: AmCache is one of the few places a deleted binary leaves a SHA-1 behind. The file is gone, Prefetch may have rolled over, but the inventory still names it, sizes it and hashes it. For an executable that ran once and was cleaned up, it is often the only surviving fingerprint.

How Crow-Eye reads the hive

Two paths, sharing one schema. Against a collected image or a hive file it opens Amcache.hve and replays its .LOG1 and .LOG2 first, because AmCache is written continuously and a copy taken from a running machine is mid-transaction by default. Skipping that replay is how an inventory ends up missing its most recent entries, which are exactly the ones an incident is about.

On a live machine it exports the hive with backup privilege (NtSaveKeyEx) and does not replay anything - there is nothing to replay. That export is a snapshot of the hive as the kernel currently holds it, which already contains everything the logs would have applied. Handing that open handle to the replay step instead of a path is not a subtle mistake: it raised TypeError and the entire live AmCache parse produced nothing.

The registry internals guide covers the hive format underneath, the transaction logs, and how records deleted from a hive can still be recovered from its freed cells.

Schema changes across Windows releases

Table 5

The schema has been replaced twice. Each row below records a change that alters what a reader may conclude, not only how the bytes are arranged; a parser written against one schema returns nothing against another, and does so without error.

WindowsWhat changedWhat it means for a reader
7RecentFileCache.bcf - a flat file, no hashesPaths only. Not a hive and not comparable.
8Amcache.hve arrives, keyed by File\{volume GUID}The old schema. 101 is the SHA-1, 15 the path. Numbered value names, not readable ones.
10 (1607+)The Inventory* schema replaces itAnything written before 2016 uses value names that no longer exist. Parsers that only know one schema return nothing on the other, with no error.
10 and 11InventoryApplicationFile, InventoryDriverBinary, InventoryDeviceContainer and more26 keys under Root on the reference system.

Reference

Keys of primary interest

Table 6

The full list of keys and the table each is written to appears in Table 2. The keys below carry the highest evidential density on the reference system.

Key under RootHolds
InventoryApplicationFileExecutables: path, SHA-1, size, publisher, link date, and the ProgramId that joins a file to the program that installed it.
InventoryApplicationInstalled programs, install date and source, the uninstall string, the registry key it registered under, and on some entries the SID of the user who installed it.
InventoryDriverBinaryDrivers, with signing state. Where an unsigned driver shows up.
InventoryDevicePnpDevices with InstallDate and FirstInstallDate, manufacturer and hardware id. Live device properties are denied to a running system, so for a device attached once this is often the only source.
InventoryDeviceContainerDevices as containers, including things attached once.
InventoryApplicationShortcutShortcuts, which imply a Start menu or desktop presence.
MareCompatibility entries. The real install directory is inside the restore value, not in a field of its own.
MareBackupAppsCarries SidState, which contains a user SID.
DeviceCensusMachine identity - Entra device id, activation channel, whether it is a VM. Hundreds of distinct value names, and more with every Windows release.

Commonly misread fields

Table 7
FieldReads likeActually is
FileIdA SHA-1Four zeros followed by a SHA-1. The unmodified string matches nothing.
LinkDateWhen the file arrivedThe PE compile timestamp, set by the linker and trivially forged.
UsnA timestampA USN journal sequence number. Orders events, is not a clock.
ProgramIdAn identifier of the fileAn identifier of the installed PRODUCT. Many files share one.
Key's last-written timeWhen the program ranWhen the inventory scan wrote the key.

Anti-forensic handling, and what survives it

Table 8

The preceding sections describe what an intact hive supports. This section describes the hive after an attempt to remove or evade it, which is the condition in which it is frequently encountered. In most rows the attempt leaves a trace of its own.

TechniqueWhat is doneWhat survives
Deleting the fileThe binary is removed after use.This is what AmCache is for. The entry and its SHA-1 survive the file, which is often the only remaining identification of something that is gone.
Deleting Amcache.hveThe hive itself is removed.Windows rebuilds it, and the rebuild is dated. A hive far younger than the operating system is an anomaly worth explaining.
Running from removable mediaThe binary never resided on the system disk.The entry still records the path it ran from, including the drive letter, which pairs with the USB history in the registry.
Trusting an absenceThe program is not in AmCache.Not evidence it did not run. The inventory is written by a scheduled task, so a binary that ran and was deleted before the next scan never appears at all.