Skip to content

Artifacts (file inventory)

The artifact source builds an inventory of files that other sources have seen. When a process starts, a module loads, or a file is created or written, FreeDR opens the file read-only and records its identity, size, timestamps, SHA-256, PE header values, Authenticode verdict and alternate data stream names.

Collector ID freedr.artifact (health: artifact-inventory)
Windows mechanism Cooperative read-only open (NtCreateFile with FILE_OPEN_REQUIRING_OPLOCK and a Read-Handle oplock, sharing read/write/delete), WinVerifyTrust, GetFileInformationByHandleEx
event_type artifact.content_version (the only one)
Hunt events view, event_type = 'artifact.content_version'
Rule SQL source name artifact (plus artifact_path_state for "latest version of this path")
Hot tables artifact_v5 (history), artifact_path_state_v1 (one row per path)
Code crates/freedr-collect-artifact

FreeDR does not copy, quarantine or upload files. It reads the file body only to compute the hash.

What triggers acquisition

Trigger Source events acquisition_reason
Event-driven process.start and image.load (image path); file.create_open, file.write, file.rename, file.set_info; every retained usn.change 1 (Event)
Live MFT Hunt with enrich artifact Paths the user looks up 3 (LiveMft)
Case enrichment: file hash Alert and case enrichment needs a hash 3 (LiveMft)
Case enrichment: file signature Returns a verdict only and writes no row —

These do not trigger acquisition: process.exit, file.delete, registry, Windows Event Log, and artifact rows themselves. System32 DLL loads do not trigger either, because the process source never stores them. Reason 2 (Detection) is reserved and unused. There is no baseline scan of existing files.

Repeat collection

Rule Detail
New path The first trigger for a path not in artifact_path_state_v1 is collected
Known path Collected again only on a content change: file.write, or a USN reason in data overwrite/extend/truncation (also named streams), file create or stream change. Opens, process starts, image loads, renames and set-info do not re-collect.
Path key / becomes \, \\?\ is stripped from drive paths, and the path is ASCII upper-cased. Hard links are separate paths.
Unchanged content Produces the same deterministic event_id, so no new row
Path-state expiry Path-state rows older than retention_days are pruned. The next trigger then counts as new.
Failure suppression A path that failed as not_found, access_denied, path_mismatch or ineligible is not retried by events for 10 minutes (2,048 paths). Targeted requests are never suppressed.

What is recorded (artifact_v5)

Column Meaning
occurred_at File LastWriteTime, not the acquisition time
path Final normalized path from the open handle
volume_serial, file_id 64-bit volume serial and 128-bit NTFS file ID
file_size End-of-file size
created_at, modified_at CreationTime and LastWriteTime. Both are kept, so timestomping is visible.
sha256 SHA-256 of the main (unnamed) data stream. NULL only for files over 500 MiB.
authenticode_status 1 trusted, 2 invalid, 3 unsigned, 4 unknown / not checked
signer Certificate subject (up to 512 bytes), only for status 1 or 2. For 2 it is an unverified claim.
ads List of named $DATA streams (name, size). [] = none, NULL = unknown or truncated (more than 64 streams or names over 1,024 bytes). Stream content, including Zone.Identifier ZoneId/HostUrl, is not read.
pe_image_size, pe_checksum, pe_timestamp, pe_preferred_base From the PE headers (first 1 MiB). NULL if the file is not a valid PE.
acquisition_reason 1 Event, 3 LiveMft (2 reserved)

Not recorded: MD5, SHA-1, imphash, fuzzy hashes, version information (product name, original file name), sections and imports.

Signature verification

  • Only valid PE files up to 500 MiB are checked.
  • WinVerifyTrust runs with no revocation check and cache-only URL retrieval. It makes no network access.
  • If there is no embedded signature, the local catalog is checked (SHA-256, then SHA-1).
  • HRESULT mapping: success → trusted; TRUST_E_NOSIGNATURE → unsigned; certificate or signature errors (expired, revoked, bad digest, untrusted root, …) → invalid; anything else → unknown.
  • Store/MSIX package files without an embedded or catalog signature are reported as unknown, not unsigned.
  • Definite verdicts are cached in memory by SHA-256 (1,024 entries, 10 minutes).

Large files

Files over 500 MiB are not read. A row is still written with size, timestamps and ADS, but sha256, the PE columns and signer are NULL and authenticode_status is 4. Hash searches and hash-based rules do not match these rows.

Why a file is not collected

Rejected before queueing

Reason When
empty_path Empty path
unsupported_path Over 32 KiB, invalid characters, a component ending in . or a space, or a device name (CON, NUL, COM1, …)
network_path UNC \\server\…
relative_path Not X:\…. This includes NT/device paths.
directory Path is a directory or root
unresolved_path ./.. components or an unexpanded %VAR%
unsupported_volume Not a fixed local NTFS volume
excluded_path Under C:\ProgramData\FreeDR, C:\Windows\Logs\CBS\CBS.log, the agent machine root, or configured excluded roots (max 128)
invalid_host, invalid_identity Internal identity errors

Skipped after queueing (health counters)

Counter Cause 10-min retry suppression
not_found File or path gone Yes
access_denied Access denied Yes
sharing_violation File locked No
path_mismatch File at the path changed identity Yes
ineligible Directory, reparse point, offline/cloud placeholder (recall on open/access), delete pending, no read lease, no file version Yes
interrupted Oplock break or file changed while reading. The partial result is discarded. No

Other errors count in acquisition_errors and per cause in acquisition_error_<operation>:<code>. These are informational and do not mark health degraded.

Throughput and limits

Item Value
Queue 16,384 requests ([artifact] control_capacity, 1–16,384). When full, the request is dropped and counted in capacity_drops. Ingestion is never blocked.
Coalescing One pending request per path
Workers 1–8, autoscaled. An idle worker retires after 2 s.
I/O rate limit None
Commit Batches of up to 32 rows or 100 ms through the single database writer. Failed batches are retried per row 3 times, then commit_drops.
Durability Queued requests and unflushed rows are lost if the agent stops

Health (artifact-inventory)

Counters: queued, coalesced, excluded, rejected_format, rejected_queued, signatures, targeted_failures, suppressed, not_found, access_denied, sharing_violation, path_mismatch, ineligible, interrupted, dropped, capacity_drops, acquisition_errors, commit_drops, acquired, committed, write_failures, lookup_failures, coordinator_*. Gauges: control_capacity, pending_ram, completed_batch_ram.

The collector is degraded while capacity_drops, commit_drops, write_failures or lookup_failures are growing, then held for 30 minutes (see degraded hold).

Not available

Prefetch, Amcache, Shimcache, SRUM, autoruns and similar host artifact parsers are roadmap items. They are not part of this source.