哈希与完整性校验

比对 2 TB 的镜像和原始介质是不是同一份,靠逐字节比对要跑几天。哈希把它压成 64 个十六进制字符。

最后更新 2026-10-09版本 v1.0维护 DigiForensics查看历史 0

关键词:MD5、SHA-1、SHA-256、SHA-512、碰撞攻击、哈希数据库、hashcat 难度:入门 前置知识:取证流程与原则

一、概述

比对 2 TB 的镜像和原始介质是不是同一份,靠逐字节比对要跑几天。哈希把它压成 64 个十六进制字符。

这里有一个必须先分清的区分:取证需要的是哈希函数三个性质里的前两个,不是第三个。确定性和单向性至今完好,前者让你能跨机比对,后者让你无法从哈希反推内容。

抗碰撞性防的是这样一类对手:既能准备两份哈希相同、内容不同的文件,又能让其中一个的文件和另一个文件的哈希值同时进入记录。取证现场常见的情形并不是这样——哈希由取证人自己计算并写进记录,事后篡改数据的人无从影响这个已经落在纸面上的值,他改动文件时必须连哈希一起重算,于是留下的是"新文件的哈希不等于记录值",这属于确定性和单向性就能发现的情形,不是碰撞。

这不等于抗碰撞性无关。当哈希值不是由你独家计算,而是由对方提供、预置或早已在别处登记——送检材料附带的校验值、多方共同持有的哈希、去重系统里的内容指纹——此时能够同时操纵文件与登记值的对手才真正存在,碰撞构造才有意义。取证中凡遇到哈希值不是你算的这一情形,就退回抗碰撞性这一侧来评估。

这个区分解释了 2.3 为什么说"MD5 可以用,但不能作为唯一依据":自带哈希值的材料(例如从对方系统导出的校验文件、镜像分卷附带的 .md5)一旦被采信为比对依据,MD5 的碰撞现实就会变成直接风险,而不只是理论上的算法强度问题。

哈希贯穿全流程四个节点,但它不是一个阶段,是一个持续动作。

源介质 ──采集──> 镜像 ──复制──> 分析副本 ──分析──> 结论
  H0              H1              H2                H3
   └──── 必须相等 ────┘            └─── 必须相等 ───┘

H0 与 H1 相等,证明采集无损;H2 与 H3 相等,证明分析过程没动数据。这两组相等关系是取证的核心证明——第五章案例里四个节点全部记录且一致,支撑的就是这一句话。

两组相等各有各的作用对象。H0 是采集时同步算出的源介质哈希,H1 是事后从已落盘的镜像重新读的,两条读取路径撞在同一个值上,才排除了"写盘时错了一块、而错的那块又被一起算进去了"这种自证。H2 若取自 E01 的内嵌 Hash section,而那个 section 是采集时写进容器的,独立性就只来自 H1 与 H0 的独立读取,不来自它自己。

上游依赖镜像制作:dcfldd hash=... hashlog=... 必须在采集时同步计算,事后对已有镜像补算是另一回事(步骤 1 强调这一点,第四章陷阱 5 会讲为什么补算无效)。dcfldd 的价值在于一次读取同时算源和镜像的哈希,避免"dd 完再 sha256sum"造成的二次读盘和中间态风险。

下游支撑证据保管链与委托证明:链上最有价值的一行就是哈希值,它能证明数据没变,但不能证明数据从哪来。

落到具体产物上,DigiForensics.hashlog 是 dcfldd 边读边算写出的日志,它是 H0 的唯一来源,事后无法重建。DigiForensics.dd 的 sha256sum 给出 H1/H2/H3 三个值,它们来自同一个文件,分别在采集完成、分析开始、分析结束时计算。

E01 的 Hash section 是容器内嵌摘要,它针对的是解压后的原始字节流,不是压缩后容器(第四章陷阱 8)。ewfverify 的输出提供独立验证通道,与外部记录的哈希互为印证;它不带 -f 时用容器内嵌的 MD5 作为默认目标算法。

NSRL / 已知文件库是以 MD5 为主索引的已知文件集合,用途是排除而非证明:命中说明这是常见系统文件,不命中说明它不常见,两者都不能单独定性。哈希值只能在同算法之间比较,用 SHA-256 去查一个 MD5 索引的库,得到的是"无匹配"而不是"不匹配"——两件事的含义完全不同。

空字符串 MD5 d41d8cd98f00b204e9800998ecf8427e 常被用作"文件为空"的特征信号。密码哈希($1$/$5$/$6$/$argon2 等前缀)是另一种完全不同的哈希对象,它的用途是路径 B(密码恢复),不是路径 A(完整性验证),第四章陷阱 12 讲的是把两者混用的后果。hash_chain.log 是分析前后追加记录的日志文件。

据此可以判断几件事:MD5 现在还能不能用(能,但不能作唯一依据)、为什么 SHA-1 和 MD5 属于同一档、完整一次取证该出现几次哈希(四次)、以及遇到故障盘时怎么用分窗哈希把损坏定位到一个区间。

哈希能证明内容相同,为什么不能证明来源合法,那是保管链的事,见证据保管链与委托证明;两个文件内容相似但不完全相同怎么判断同源,相似度是另一套判据,见近似匹配与模糊哈希;SHA-3 / BLAKE3 的性能优势属于格式与工具方向,术语表与官方文档更合适;密码怎么恢复不在这里展开,步骤 6 只讲怎么识别哈希类型与格式,实际恢复强调授权 + 最小化 + 脱敏,见通用密码恢复。

上手前需要先知道取证流程分几个阶段(见取证流程与原则),否则"分析前后各算一次"这句话里的"分析"没有边界。另外建议先实际跑过 sha256sum 和 md5sum,亲手验证 2.2 那张表里 abc 的四个哈希值。这张表的价值不在背诵,在于让你看到 MD5 与 SHA-256 输出长度的真实差异——128 位与 256 位不是"短一点长一点"的关系,抗碰撞空间差 2 的 128 次方倍。

二、核心原理

2.1 哈希函数的三个性质

性质 含义 取证中的意义
确定性 相同输入必得相同输出 同一文件任何时候算都一样,可跨机比对
单向性 已知输出难以反推输入 无法从哈希"还原"文件,密码才安全
抗碰撞 难以找到两个不同输入产生相同输出 已被 MD5 和 SHA-1 攻破;哈希值由他人提供时直接相关

关键理解:前两条至今完好,支撑日常取证。抗碰撞性平时用不上,在哈希值由他人提供或早已在别处登记时才会成为决定性判据——这就是 MD5 在取证中仍有位置、却不能单独承担完整性的原因。

2.2 四种算法的实测对比

用输入 abc 实测(可用 printf 'abc' | <工具> 自行验证):

算法 输出长度 abc 的实测哈希值 状态
MD5 128 bit (32 字符) 900150983cd24fb0d6963f7d28e17f72 已不安全,见 2.3
SHA-1 160 bit (40 字符) a9993e364706816aba3e25717850c26c9cd0d89d 已不安全
SHA-256 256 bit (64 字符) ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad 当前推荐
SHA-512 512 bit (128 字符) ddaf35a1...454d4423643ce80e2a9ac94fa54ca49f 高安全场景

空字符串的 MD5 恒为 d41d8cd98f00b204e9800998ecf8427e,常被用作"文件为空"的特征信号。

2.3 为什么取证要弃用 MD5(但不能全盘否定)

MD5 的问题不在于"能算错",而在于碰撞可被主动构造。

事件 含义
2004 年发现实用碰撞 首次证明 MD5 碰撞可行
2008 年 SHAttered 首个公开的相同前缀碰撞,两份不同文件拥有相同 MD5
2019 年 chosen-prefix collision 更实用的攻击:攻击者可在两个文件各自前缀后自由插入碰撞块

这意味着什么:如果一份文件被"合法地"修改过,攻击者可以计算出修改后版本的 MD5,使其与原始版本的 MD5 完全相同。于是"源盘 MD5 == 镜像 MD5"这个证明就失效了——两份不同的数据拥有同一个指纹。

⚠️ 这正是"为什么现在必须用 SHA-256"的直接原因:SHA-256 目前没有实用的碰撞构造方法。

但 MD5 不是完全无用。以下场景仍然合理:

合理场景 理由
兼容历史系统与格式 E01 早期版本、很多司法鉴定规范、NSRL 等数据库仍以 MD5 为主索引
快速粗筛 计算速度比 SHA-256 快得多,批量扫描上亿文件时先跑 MD5 缩小范围
交叉校验 同时记录 MD5+SHA-256,MD5 作为辅助、便于与外部数据比对

实务原则:新做的案件至少要有 SHA-256;MD5 可以并列记录,但不能作为唯一完整性依据。报告中写"MD5 一致"而没有 SHA-256,在严谨质证时会被追问。

2.4 SHA-1 的特殊尴尬地位

SHA-1 情况比 MD5 更微妙。Git 等大量系统仍在用 SHA-1(Git 直到近年才逐步迁移),但它同样有已公开的碰撞构造(SHA-1 is a Shambles 首次展示了实际可行的碰撞)。

结论:取证中 SHA-1 和 MD5 属于同一档,都只能作辅助。若设备/格式只内嵌了 SHA-1,你需要用外部记录的 SHA-256 补齐。

2.5 哈希链:一次计算远远不够

一次完整取证应至少出现四次哈希:

H0  源介质哈希       —— 采集时同步计算(dcfldd hash=...)
H1  镜像文件哈希     —— 采集完成立刻计算
      验证:H0 == H1  ← 这一步是取证的核心证明
H2  分析副本哈希     —— 开始分析前计算
H3  分析后哈希       —— 分析结束重新计算
      验证:H2 == H3  ← 证明分析过程没改动数据
源介质 ──采集──> 镜像 ──复制──> 分析副本 ──分析──> 结论
  H0              H1              H2                H3
   └──── 必须相等 ────┘            └─── 必须相等 ───┘

dcfldd 的价值就在这里:一次读取同时算源和镜像的哈希,避免"dd 完再 sha256sum"造成的二次读盘和中间态风险。

2.6 哈希数据库的实战价值

已知文件库是哈希最大的非密码学用途:

数据库 作用 取证用途
NSRL / known-good 数亿已知文件的哈希 快速排除系统文件,剩余的都是"值得看"的
malware 库 已知恶意样本哈希 命中即定性
本机基线库 自己构建(装机即哈希全部文件) 企业内网取证,发现"非标准"文件

效果示例:对一块 500 GB 的镜像做全盘哈希后与 NSRL 比对,通常能过滤掉 60%–80% 的文件——因为绝大多数是操作系统和常用软件自带的。剩下的部分才需要逐个人工审阅。

2.7 哈希在密码恢复中的角色

破解的前提永远是"拿到哈希值"。取证中常见的来源有这几处:

来源 典型哈希类型 备注
Windows SAM/SYSTEM NTLM 需先离线提取
Linux /etc/shadow SHA-512 crypt ($6$) 或 bcrypt 现代发行版多为 SHA-512
浏览器密码库 AES 密钥 + DPAPI/Keychain 见板块 5
数据库配置 明文/salt+hash 宝塔、WordPress 等
无线网络 WPA-PMKID 需抓取握手/EAPOL

这是两条完全不同的使用路径。路径 A(证据用途)用哈希证明文件没变,用 sha256sum,不涉及破解;路径 B(还原用途)用哈希尝试还原明文密码,用 hashcat/john。

本篇重点讲 A。B 的攻击手法详见板块 5。


三、操作步骤

步骤 1:采集时同步计算(推荐方式)

# 一次读盘,同时算源与镜像的哈希 —— 最佳实践
sudo dcfldd if=/dev/sdX of=DigiForensics.dd \
     hash=md5,sha256 hashlog=DigiForensics.hashlog \
     bs=4M conv=noerror,sync status=on

# 查看生成的哈希日志
cat DigiForensics.hashlog

💡 hashwindow 参数可让 dcfldd 每 N 字节记一次分窗哈希。对疑似故障盘很有价值:如果两次读取同一区段结果不同,能精确定位不稳定区域。

步骤 2:独立复算验证

# 用独立工具复算镜像,确保不是 dcfldd 的 bug
sha256sum DigiForensics.dd
md5sum    DigiForensics.dd

# 对 E01 容器,用容器自身的验证能力
ewfverify DigiForensics.E01

两条路径都应通过。若 dcfldd 记录值与 sha256sum 复算值不一致,说明流程中有问题,必须查明。

步骤 3:分析前后各算一次

# 分析前
sha256sum DigiForensics.dd | tee /case/h_before.txt

# ... 分析操作 ...

# 分析后
sha256sum DigiForensics.dd | tee /case/h_after.txt

# 自动比对
cut -d' ' -f1 /case/h_before.txt
cut -d' ' -f1 /case/h_after.txt

步骤 4:分窗哈希定位损坏位置

# 每 512 MB 记一次哈希,用于故障盘的精确定位
sudo dcfldd if=/dev/sdX of=DigiForensics.dd \
     hash=sha256 hashwindow=512M hashlog=DigiForensics.window.log \
     bs=4M conv=noerror,sync status=on

# 比对两次运行的分窗哈希,找出不稳定区段
diff <(awk '{print $1}' run1.window.log) <(awk '{print $1}' run2.window.log)

步骤 5:批量文件哈希(用于 NSRL 比对)

# 单文件
sha256sum "/mnt/case/Users/Alice/report.docx"

# 递归整个目录,输出可直接喂给比对工具
find /mnt/case -type f -exec sha256sum {} \; > allfiles.sha256

# 与已知文件库比对(需预先导入 NSRL 到比对数据库)
# 常见工具:hashdeep、hfind(NsrlUpdate 项目)
hfind allfiles.sha256

步骤 6:识别与破解密码哈希(路径 B)