Skip to the content.

Hiding Techniques

This directory explains the anti-forensic techniques nemo implements: what they are, how they work on disk, how much data they hold, how they get detected, and where the code lives. It assumes you can read Go and a hex dump but have never touched filesystem forensics.

Read the shared vocabulary below first, then the technique you are working on:

Where each technique sits in the codebase

Every technique is one Technique implementation in internal/technique (namedStreamTechnique, slackSpaceTechnique, timestompTechnique), selected by name in technique.Get. A technique never imports a filesystem package. It type-asserts the Entry it is handed against the one capability interface it needs and returns technique.ErrUnsupported when the assertion fails (internal/technique/technique.go). The capability interfaces are defined in internal/filesystem/filesystem.go:

Technique Capability interface Methods
named-stream filesystem.NamedStreamCapable WriteStream / ReadStream / DeleteStream
slack-space filesystem.SlackSpaceCapable SlackRegions
timestomp filesystem.TimestompCapable SetTimestamp (no reader, see below)

Support matrix (current)

Filesystem named-stream slack-space timestomp
ext4 done, internal/filesystem/ext4/ext4.go and xattr.go not built done, internal/filesystem/ext4/timestomp.go
APFS done, internal/filesystem/apfs/namedstream.go (in-place B-tree leaf rewrite, no allocation) not built not built
NTFS parser only, NamedStreams stubbed not built not built
fakefs done done done (test double, internal/filesystem/fakefs)

fakefs is an in-memory filesystem that implements all three capabilities against a byte-slice image. It is how internal/technique gets tested without a real NTFS, APFS, or ext4 image. A real filesystem’s Entry implements the same interfaces fakefs.Entry already does.

nemo features prints this matrix at runtime from the Techniques field each filesystem declares on its filesystem.Detector, so it cannot drift from what is actually registered. See docs/architecture.md.

Shared vocabulary

Cluster / block. The smallest unit a filesystem allocates. NTFS calls it a cluster (default 4 KiB); ext4 and APFS call it a block (also 4 KiB by default). A 1-byte file still consumes a whole one.

Allocated size vs. used size. The allocated size is how many bytes the filesystem reserved, always a whole number of clusters. The used size (sometimes “valid data length”) is how many of those bytes hold the file’s content. The gap is slack. See slack-space.md.

Resident vs. non-resident. NTFS and APFS can store a small attribute value inline inside the file’s metadata record (resident) rather than in its own allocated block (non-resident). The threshold is roughly the free space left in the record: about 700 bytes for a 1 KiB NTFS MFT record, about 3 KiB for an APFS xattr. Resident data has no cluster slack of its own; it shares the record’s slack.

MACB. The four timestamps a forensic examiner builds a timeline from: Modified (content changed), Accessed (content read), Changed (metadata changed, the NTFS/ext4 ctime), Birth (created, crtime). Not every filesystem stores all four, and no two store them the same way. See timestomping.md.

“Detectable.” A technique is detectable when an examiner has a reliable signal that data was hidden: a structural anomaly, a second copy of the same information that was not updated in lockstep, or a journal entry. “nemo can’t detect it” (as with timestomp) describes nemo’s current capabilities, not whether the technique can be detected at all.

Custody log. Every WriteAt nemo performs goes through the internal/custody decorator, which SHA-256-hashes and logs it. A technique cannot opt out; the logging is structural, not a convention. Detection is read-only and never writes to the custody log.

Techniques nemo does not implement

These are out of scope per docs/overall-plan.md. Recognise them so you do not reach for one by accident, and so you know where nemo’s guarantees stop.

Unallocated-space hiding and file carving. Writing a payload into blocks the filesystem currently marks free, or recovering files from there. The next allocation overwrites it, and recovering it is a data-recovery problem rather than a filesystem-parsing one. nemo only touches space the filesystem considers allocated.

Reserved metadata regions. NTFS keeps about 16 MFT records reserved ($MFT through $Extend), and the $Boot file’s tail is padding. ext4 reserves inodes 1 through 10 and pads its group descriptors. Data parked there survives normal use, but any fsck or chkdsk flags or clobbers it, and writing there risks corrupting the volume.

$BadClus and bad-block-list marking. Telling NTFS ($BadClus) or ext4 (the bad-block inode) that a good cluster is bad so the filesystem routes around it, then reading it directly. Every modern forensic suite checks for this first.

Deleted-inode and deleted-MFT-record reuse. Writing into the record of a just-deleted file before the filesystem reuses it. Racy and short-lived.

Steganography inside file formats. Hiding a payload in the unused bits of a JPEG, PNG, or PDF. This has no filesystem component and is a large field of its own.

Sparse-file holes. A sparse file’s unwritten ranges read as zeros and occupy no blocks, so there is nothing to hide in, only a size-versus-allocation mismatch to notice.

Snapshot and VSS stashing. Putting data in a Volume Shadow Copy or an APFS snapshot and deleting the live copy. This is “hide a normal file where most tools do not look,” not a filesystem-structure technique, and it depends on snapshot retention you do not control.