Create MD5 and SHA256 File Hashes on Windows, macOS, and Linux
A suspicious attachment, a memory dump, a .eml you just
converted from a .msg export — before you do
anything with a piece of evidence, you hash it. The hash pins the file's
exact content to a moment in time: any later change, accidental or malicious,
changes the hash. That gives you three things:
- Integrity proof. Re-hash before analysis or handoff; if the value still matches the one recorded at acquisition, the evidence is untouched. This is the technical backbone of a chain of custody.
- A universal identifier. Hashes are how the whole industry names samples — search a SHA256 on VirusTotal or in a threat-intel platform and you find every prior sighting without uploading anything.
- Deduplication. Two files with the same hash have the same content, regardless of filename.
Hashing is trivially easy — which is exactly why it gets done sloppily. This article is the deliberately foolproof version for all three platforms.
Why Record Both MD5 and SHA256
SHA256 is your integrity proof. MD5 has been broken for collisions for years — someone can construct two different files with the same MD5 — so an MD5 alone must never be your only evidence that a file is unchanged.
MD5 is still a useful identifier. Threat-intel feeds, sandbox reports, and older IOC lists reference samples by MD5 everywhere. Recording it at acquisition costs nothing and saves you from re-hashing later when a report only gives you an MD5 to pivot on.
So the habit is: record both, trust SHA256.
Windows: Get-FileHash (PowerShell)
Get-FileHash is the built-in cmdlet
(official reference).
Its default algorithm is SHA256, so the plain call already does the right
thing:
Algorithm Hash Path
--------- ---- ----
SHA256 3CBCFDDEC145E3382D592266BE193E5BE53443138EE6AB6CA09FF20DF609E268 C:\evidence\sample.zip
For the MD5, pass -Algorithm (accepted values: SHA1, SHA256, SHA384,
SHA512, MD5):
To compare against a known value — for example when re-verifying before
handoff — let PowerShell do the comparison instead of eyeballing 64
characters. -eq on strings is case-insensitive, so the uppercase output
matches a lowercase reference fine:
(Get-FileHash C:\evidence\sample.zip).Hash -eq '3cbcfddec145e3382d592266be193e5be53443138ee6ab6ca09ff20df609e268'
Anything other than True means the file is not the file you recorded.
Windows Fallback: certutil (cmd.exe)
On a system where PowerShell is restricted, certutil ships with every
Windows and hashes files too
(official reference),
with the syntax certutil -hashfile InFile [HashAlgorithm]. Always name the
algorithm explicitly rather than relying on a default:
certutil prints the hash in lowercase and has no built-in compare — you
copy the value into your notes and compare manually or in PowerShell later.
macOS: shasum and md5
macOS ships shasum (algorithm selected with -a) and a BSD-style md5
tool — note the differences from Linux before you script anything:
8790bea2d44ca10c6667124c29021ef77f724a9d029fb69f052c715f46d425d7 /Users/analyst/evidence/sample.zip
MD5 (/Users/analyst/evidence/sample.zip) = aedc12a3e2db1d8375271235db2660cf
The BSD md5 output format (MD5 (file) = hash) breaks tools that expect
Linux md5sum lines. md5 -r prints the Linux-style hash filename order
instead. Current macOS versions additionally include md5sum and sha256sum
themselves, so Linux-style commands work there too — but shasum and md5
are the ones you can rely on finding on any Mac.
To verify later, write the checksum line to a file at acquisition time and
let shasum -c do the comparison:
shasum -a 256 ~/evidence/sample.zip > ~/hashes/sample.zip.sha256
shasum -a 256 -c ~/hashes/sample.zip.sha256
Linux: sha256sum and md5sum
GNU coreutils provide one tool per algorithm:
Output is hash, two spaces, filename — one line per file, which is
exactly the format the -c verification mode reads back. The standard
workflow:
A modified file prints FAILED instead of OK and the command exits
non-zero, so the check also works unattended in scripts (--quiet
suppresses the OK lines, --status suppresses all output and leaves only
the exit code). The same flags apply to md5sum -c.
The Habits That Make Hashes Worth Anything
A hash only proves integrity relative to the moment it was taken, so the process matters as much as the command:
- Hash immediately at acquisition — before opening, unzipping, converting, or scanning the file. Every tool that touches evidence is a chance to modify it; the hash must capture the state before all of that.
- Store hashes separately from the evidence — in your case notes or ticket, not as a text file sitting in the same folder. If an attacker (or a sync client) can alter the evidence, it must not be able to alter the recorded hash alongside it.
- Re-verify at every boundary — before you start analysis, and again before handing evidence to another analyst, team, or law enforcement. "Matches the acquisition hash" is a one-line note that settles integrity questions months later.
- Copy-paste, never retype. A transcribed hash with one wrong character is worse than no hash. Case differences are harmless (hex is case-insensitive — PowerShell prints uppercase, Unix tools lowercase), but use a tool comparison rather than reading two long strings by eye.
MD5 alone proves nothing
MD5 collisions are practical: distinct files with identical MD5 values can be crafted deliberately. Record MD5 as a lookup key for threat intel, but any integrity or chain-of-custody claim must rest on the SHA256.