Signal9Signal9 — home
online
all lessons

Evidence Acquisition: Imaging, Hashing & Read-Only Mounting

Intermediate40 min6 steps
What you'll learn
  • Explain why you analyze a bit-for-bit copy, never the original, and what chain of custody means
  • Image a whole device or partition to a file with dd, choosing block size and watching progress
  • Prove an image is a faithful copy by hashing the source and image with sha256sum and comparing
  • Mount an image strictly read-only via a loopback device so analysis can never alter the evidence
  1. Why you never touch the original

    Frame acquisition as preserving a crime scene: analyze a copy so the evidence stays pristine.

    Before you run a single forensic tool, you have a problem older than computers: the act of examining evidence can destroy it. Mount a disk the wrong way, open a file, let an antivirus scan kick off — and you've changed timestamps, replayed a journal, or overwritten a deleted file you needed. Once the original is altered, you can never get the pristine state back, and in any serious investigation your findings become worthless.

    The discipline that solves this is evidence acquisition: you make a perfect, verifiable copy of the storage first, lock the original away untouched, and do all your poking and prodding on the copy. If you wreck the copy, you make another from the sealed original. The original is the ground truth you protect at all costs.

    Think of a physical crime scene. The detective doesn't rearrange the furniture to look for clues — they photograph everything exactly as found, bag and tag each item with a label and a signature, and analyze in the lab. The sealed evidence bag, and the unbroken record of who held it when, is what lets the evidence stand up later. A forensic disk image is that photograph; the hash is the tamper-evident seal; the chain of custody is the signed log of every hand the evidence passed through.

  2. The acquisition order

    Learn the fixed sequence — image, hash, verify, mount read-only — and why order matters.

    Acquisition is a checklist run in order, and the order is not negotiable. Skip a step or swap two and you lose the property that makes the result trustworthy. The sequence is:

    • 1. Image — make a bit-for-bit copy of the source to an image file (dd and friends).
    • 2. Hash the source — compute a cryptographic hash of the original device so you have a fingerprint of exactly what you copied.
    • 3. Hash the image and compare — the image's hash must equal the source's hash. Equal hashes prove the copy is faithful.
    • 4. Mount read-only — attach the image through a loopback device with writes disabled, so every later tool physically cannot change it.
    • 5. Hash again afterward — re-hash the image when you're done analyzing. Same hash = you can prove your analysis altered nothing.

    The rule beneath all of it: handle the original as few times as possible, and only ever to read it. Once the verified image exists, the original goes back in the evidence locker and you never touch it again.

    Check yourself

    In forensic acquisition, when should you compute a cryptographic hash of the evidence image — before analysis, after analysis, or both?

  3. Image it: dd

    Copy a whole device or partition to an image file with dd, byte for byte.

    dd copies raw bytes from an input (if=) to an output (of=) and does no interpretation — it doesn't know or care whether the bytes are a filesystem, deleted files, or empty space. That ignorance is exactly what you want: a forensic image must capture everything, including unallocated space and slack where deleted data still lives. Point if= at the device (/dev/sdb, the whole disk) or a partition (/dev/sdb1), and of= at an image file on separate storage.

    The one parameter worth tuning is block size (bs=). dd reads and writes one block at a time; the default 512 bytes is slow because it does a syscall per tiny block. A larger block like bs=4M moves the same data in far fewer, larger chunks — much faster — without changing a single byte of the result.

    sudo dd if=/dev/sdb of=/evidence/case42.img bs=4M status=progress conv=noerror,sync
    8321499136 bytes (8.3 GB, 7.7 GiB) copied, 92 s, 90.4 MB/s
    1985+0 records in
    1985+0 records out
    8327790592 bytes (8.3 GB, 7.8 GiB) copied, 92.1 s, 90.4 MB/s
    if=/dev/sdb
    Input file — the SOURCE. /dev/sdb is the whole device; use /dev/sdb1 to image just one partition.
    of=/evidence/case42.img
    Output file — the image. Always write to SEPARATE storage, never back onto the source.
    bs=4M
    Block size: read/write in 4 MiB chunks. Bigger blocks = far fewer syscalls = much faster; the bytes copied are identical.
    status=progress
    Print a live throughput/progress line to stderr — without it dd is silent until done, and a multi-hour image looks hung.
    conv=noerror,sync
    On a read error, don't abort (noerror) and pad the bad block with zeros to keep byte offsets aligned (sync). Critical for failing disks.
    dd read the entire /dev/sdb device and wrote it to case42.img — an exact, mountable replica of the disk. The records in/records out lines should match (here 1985+0 both ways), confirming nothing was dropped. With conv=noerror,sync, a disk with a few bad sectors still produces a complete image; the unreadable blocks become zeros instead of stopping the copy and leaving you with a partial, useless image.

    Check yourself

    Why image the whole device (/dev/sdb) and copy unallocated space too, instead of just tar-ing up the visible files?

  4. Prove it's faithful: sha256sum

    Hash the source and the image and compare — equal hashes prove a perfect copy.

    An image you can't prove is faithful is just an unlabeled copy. A cryptographic hash turns any amount of data into a short fixed-length fingerprint with one essential property: change a single bit of the input and the output changes completely and unpredictably. So if the hash of the source device equals the hash of the image, the two are byte-for-byte identical — and if anyone alters the image later by even one bit, its hash will no longer match, exposing the tampering.

    Hash the source device and the image, then compare the two fingerprints. Equal means a verified, faithful acquisition.

    Example — not runnable on your lab box

    sudo sha256sum /dev/sdb /evidence/case42.img
    9f2c4e0b7a1d83c5e6f0b2a9d4c71e8f3b6a0c2d5e8f1a4b7c0d3e6f9a2b5c8d  /dev/sdb
    9f2c4e0b7a1d83c5e6f0b2a9d4c71e8f3b6a0c2d5e8f1a4b7c0d3e6f9a2b5c8d  /evidence/case42.img
    /dev/sdb
    Hash the raw source device — the fingerprint of exactly what you copied.
    /evidence/case42.img
    Hash the image you produced. List both on one line and the two hashes sit together for easy comparison.
    The two SHA-256 hashes are identical, character for character. That single fact certifies the acquisition: case42.img is a perfect replica of /dev/sdb, with no bytes added, dropped, or altered. Record this hash in your case notes now — it's the reference value you'll compare against again when analysis is finished. From here on, you work only on the image; the original disk goes back in the locker.

    Check yourself

    You hash the source disk and the image and the two SHA-256 values match exactly. What does that prove?

  5. Mount it read-only: losetup + mount -o ro

    Attach the image through a loopback device and mount it so analysis can never write to it.

    Now you want to browse the image's filesystem with normal tools — but a normal mount makes it writable, and the moment a filesystem is mounted read-write the kernel may write to it: updating access times, and worse, replaying the journal. Your job is to mount it so that physically nothing can change.

    An image is a file, not a block device, so first you wrap it in a loopback device with losetup — that exposes /evidence/case42.img as a fake disk like /dev/loop0 (with -P it even exposes its partitions as /dev/loop0p1, etc.). Then you mount that loop device with the write-protection flags: ro (read-only), and for journaled filesystems noload/norecovery so the kernel does not replay the journal.

    Example — not runnable on your lab box

    sudo losetup -fP --show /evidence/case42.img
    /dev/loop0
    -f
    Find the first FREE loop device automatically instead of you guessing /dev/loopN.
    -P
    Scan the image's partition table and expose each partition as /dev/loop0p1, /dev/loop0p2, …
    --show
    Print the loop device name that was allocated (here /dev/loop0) so you know what to mount.
    The image file is now backed by the block device `/dev/loop0` — Linux treats it as a disk. If case42.img was a whole-device image with partitions, you'd now also have /dev/loop0p1, /dev/loop0p2, and so on, and you'd mount the partition you care about rather than the whole device. Note: losetup by itself does not write to the image — it just maps it.

    Example — not runnable on your lab box

    sudo mount -o ro,noload,noexec,nodev /dev/loop0 /mnt/evidence
    -o ro
    Mount READ-ONLY — the kernel rejects every write to this filesystem.
    noload
    Do NOT replay the ext3/ext4 journal on mount (norecovery is the equivalent for xfs). This is what stops the kernel writing to your evidence.
    noexec
    Disallow executing binaries from the image — don't accidentally run the attacker's malware while browsing it.
    nodev
    Ignore any device nodes on the image — a planted device file can't be used to reach real hardware.
    No output means success. The evidence filesystem is now visible under /mnt/evidence, and it is read-only at the kernel level — not by politeness, but enforced. noload is the subtle hero here: a journaled filesystem that wasn't cleanly unmounted has a dirty journal, and a normal mount would replay it, writing to the disk to finish interrupted transactions. noload tells the kernel to skip that replay, so mounting changes nothing.
    mount | grep loop0
    /dev/loop0 on /mnt/evidence type ext4 (ro,noexec,nodev,relatime)
    Always verify the mount is actually read-only — don't assume. The (ro,...) in the options is the confirmation: ro is present, so writes are blocked. If you ever see rw here, stop and remount; something is exposing the evidence to modification. The real test is the next step: try to write and watch it fail.

    Check yourself

    Why add noload (or norecovery) when read-only mounting a seized ext4/xfs image, on top of ro?

  6. Try it in your live terminal

    Run the full image → hash → loopback → read-only → verify → clean-up workflow yourself.

    You have a real root shell. We'll rehearse the entire acquisition workflow on a small, throwaway file-backed image — no real disk involved, fully reversible. You'll create an 8 MiB image, put a filesystem on it, hash it, attach it as a loopback device, mount it strictly read-only, prove the read-only protection by watching a write fail, then tear it all down. This is the same sequence of moves you'd run on a real case; only the source (a tiny file instead of /dev/sdb) is different.

    Run the blocks in order.

    dd if=/dev/zero of=/tmp/et.img bs=1M count=8 status=progress
    Create an 8 MiB image file full of zeros — our stand-in for a seized disk. if=/dev/zero is an endless source of zero bytes; bs=1M count=8 writes eight 1-MiB blocks. On a real case this would be if=/dev/sdb reading an actual device.
    mkfs.ext4 -q /tmp/et.img
    Format the image with an ext4 filesystem so there's something real to mount. -q keeps it quiet. dd doesn't care what's inside the image — we add a filesystem only so the read-only mount step has structure to attach.
    sha256sum /tmp/et.img
    Fingerprint the image BEFORE we mount and explore it. Copy this hash somewhere — you'll re-run sha256sum at the end and the value must be unchanged, proving the read-only mount altered nothing.
    sudo losetup -fP --show /tmp/et.img
    Wrap the image file in a loopback block device and print its name (e.g. /dev/loop0). -f picks a free loop device; -P would expose partitions if it had any. Note the device name it prints — you'll need it to detach later.

    Example — not runnable on your lab box

    sudo mount -o ro,loop /tmp/et.img /mnt 2>/dev/null || { sudo mkdir -p /mnt/ev && sudo mount -o ro,loop /tmp/et.img /mnt/ev; }
    Mount the image READ-ONLY. -o ro,loop lets mount set up the loop device and apply read-only in one step. The || fallback creates /mnt/ev and mounts there if /mnt is busy — so this works whatever the sandbox layout is. Note which mountpoint succeeded (/mnt or /mnt/ev).
    mount | grep -E '/mnt(/ev)? '
    Verify the mount really is read-only: the options must contain ro. Don't trust that you typed it correctly — confirm the kernel applied it. This is the verification step you always perform on real evidence.

    Example — not runnable on your lab box

    sudo touch /mnt/proof 2>&1 || sudo touch /mnt/ev/proof 2>&1
    PROVE it's read-only by trying to write. You should get 'Read-only file system' — the kernel physically refusing the write. A failed write here is the success condition: it means analysis tools genuinely cannot alter the evidence.

    Example — not runnable on your lab box

    sudo umount /mnt 2>/dev/null || sudo umount /mnt/ev; sudo losetup -d $(losetup -j /tmp/et.img -O NAME --noheadings); rm -f /tmp/et.img; echo cleaned
    Tear down in reverse order: unmount the filesystem, detach the loop device (losetup -j finds which loop device backs our image, -d detaches it), then delete the throwaway image. Self-contained and reversible — the sandbox is back exactly as it started. On a real case you'd keep the image and its hash, not delete them.
Recap
  • Never analyze the original. Make a verified bit-for-bit copy, lock the original away, and do all work on the image — provability, not any single tool, is the foundation of DFIR.
  • dd (or forensic dc3dd/ddrescue) images a whole device or partition raw, capturing unallocated space and deleted data a file copy would miss; tune bs= for speed, use conv=noerror,sync for failing disks.
  • sha256sum the source AND the image and compare — matching hashes prove a faithful copy and seal it tamper-evident. Hash again after analysis to prove nothing changed. MD5 is broken; don't seal new evidence with it.
  • Mount evidence through a loopback (losetup) with mount -o ro,noload (or norecovery) so the kernel can't write or replay a dirty journal — then VERIFY read-only by checking the mount options and watching a write fail.
  • The write-blocking mindset: assume any access can write unless something actively prevents it. Put the read-only protection (software or hardware) in place BEFORE you connect the evidence.
Step → command → what it guarantees
StepCommandGuarantees / watch for
Image the sourcedd if=/dev/sdb of=img bs=4M status=progressExact raw copy incl. deleted/unallocated data; never swap if=/of=
Hash source + imagesha256sum /dev/sdb imgEqual hashes = faithful copy; use SHA-256, not broken MD5
Wrap as loop devicelosetup -fP --show imgExposes the image file as /dev/loopN (and partitions with -P)
Mount read-onlymount -o ro,noload loop /mntKernel-enforced no writes; noload stops journal replay
Verify read-onlymount | grep loop ; failed touchOptions show ro; a write must fail ('Read-only file system')
Re-hash when donesha256sum imgSame value as before = your analysis altered nothing