搜索全站

输入至少 2 个字符,查找相关内容。

↑↓ 选择 Enter 打开Esc 关闭

只读挂载与安全接入

拿到镜像之后,最自然的想法是"挂载起来看看里面有什么"。这个想法在便利性上无可指摘,在证据安全上却极其危险——因为操作系统的挂载过程本身就会写盘。

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

一、概述

拿到镜像之后,最自然的想法是"挂载起来看看里面有什么"。这个想法在便利性上无可指摘,在证据安全上却极其危险——因为操作系统的挂载过程本身就会写盘。

即使加了 -o ro,某些文件系统仍会在挂载瞬间修改超级块、回放日志、更新访问时间:

后果 严重程度
原始哈希被破坏,镜像无法再证明与检材一致 ★★★★★ 证据灭失
$MFT / inode 时间戳被更新,污染时间线分析 ★★★★★ 破坏核心证据
损坏或恶意构造的文件系统可触发内核漏洞 ★★★★ 安全风险
挂载产生的新文件让文件系统状态与镜像初始状态不符 ★★★ 逻辑不自洽

更严重的是,Autopsy、X-Ways、FTK Imager 在设计时就假设"挂载会破坏证据",所以它们根本不提供直接挂载。这一取舍反映了取证学科对挂载风险的共识——这不是工具功能缺失,是刻意的设计。

本篇给出的三条路径,按安全等级排序:

路径 是否经过内核 是否可能写盘 适用场景
Sleuth Kit 直接解析 ❌ 否 绝无 取证分析的首选,绝大多数场景够用
只读环设备 + 内核挂载 ✅ 是 有条件(noload + noatime + blockdev --setro 三层) 需要 ls / cp / grep 等常规工具时
Windows 侧写保护器 + 挂载 ✅ 是 几乎必然 只在必要时,且必须有硬件写保护

核心原则一句话:能不挂载就不挂载。

Sleuth Kit 直接读镜像文件、自己解析数据结构,完全不经过内核文件系统驱动,因此在物理上不可能触发任何写入——这不是"风险较低",是"风险为零"。

为什么内核挂载挡不住写入是本篇最需要先理解的一件事:-o ro 只保证文件系统层不主动写,而内核 VFS 之外的机制仍会绕过它。

典型翻车场景是 ext4 报告 needs journal recovery(断电后的正常现象),你挂上去,内核照常回放日志,把几十 MB 的元数据改动写进镜像——挂完算一下哈希,已经和采集时完全不同了。

绕过 -o ro 的四类机制(具体对应第四章 4.1 组):

机制 触发条件 写入内容 对策
日志回放 ext3/ext4 标记为 dirty、XFS 有未完成日志 把日志里的元数据操作重做一遍 ext 用 -o noload,XFS 用 -o norecovery
超级块修复 文件系统检测到不一致 修正 s_state、空闲块计数 只能靠块层锁死(blockdev --setro)挡住
atime 更新 读取文件时(除非显式 noatime) inode 的访问时间 -o noatime
relatime 重算 相对时间戳重算 最多每 24 小时一次 同上

atime 这一条值得单独强调:读取证据文件会改变被读取文件的时间戳,而用 find 遍历目录、用反取证工具扫未分配空间时,几百上千个文件的访问时间会被一次性改掉。

而"最后访问时间"恰恰是取证里最有用的字段之一——不改 atime 不是保守,是保护证据。只要没有显式指定 noatime,现代内核的 relatime 默认也仍然会更新它。

在取证流程中的位置:本篇是分析环节的第一道关卡,处在镜像制作(镜像制作)之后、任何实际内容分析之前。上游是格式问题——E01 需要 EWF 库或先转 RAW,见证据格式与转换;它自己的前置是挂载前的哈希校验,镜像在被接入之前应该先确认哈希与采集记录一致,否则你连"自己有没有毁掉证据"都判断不了。

下游分两条:静态解析路线直接进NTFS 结构与 MFT 解析、ext4 结构与 inode;需要观察运行态行为的走虚拟机仿真。

两条路线的关键区别是:挂载给你文件系统视图,仿真给你程序执行能力——后者风险高一个量级。

镜像里有哪些分区、每个分区的起止扇区与文件系统类型、目录树长什么样、某个文件的元数据与内容是什么、删除文件与未分配空间里有什么、以及接入操作之后镜像是否还是原样(这是必须自己回答的问题)。

挂载不等于能读——加密卷挂上去也只是一堆随机字节,解密是另一条线(BitLocker 全卷加密取证);挂载给不了你程序运行态——正在运行的进程、内存里的凭据、网络连接的当前状态,静态挂载一个都看不到;以及挂载也不能替代仿真——恶意程序的行为、加密密钥的运行时驻留、自毁逻辑的触发条件,都必须在受控环境里实际运行才观察得到。

读者前提:你需要能读懂 mmls 的分区表输出(本篇 2.5 逐列解释了每个字段,不掌握它后面每一步都会算错偏移)、理解扇区与字节的换算(offset = Start × 512)、并清楚**"只读"是三层机制叠加的结果而不是一个参数**。

如果你还没做镜像或者还没确认镜像哈希,先回镜像制作。Linux 内核安全机制(Landlock、SELinux)的内容在扩展阅读里有指向,本篇不展开。

二、核心原理

2.1 三条接入路径的安全等级

路径 是否调用内核文件系统驱动 是否写盘 适用
Sleuth Kit 直接解析 ❌ 否 绝无 取证分析的首选
只读环设备 + 内核挂载 ✅ 是 有条件(见 2.2) 需要 ls/cp 等常规工具时
Windows 写保护器 + 挂载 ✅ 是 几乎必然 只在必要时,且必须用硬件写保护

核心原则:能不挂载就不挂载。 Sleuth Kit 直接读取镜像文件、自己解析数据结构,完全不经过内核,因此在物理上不可能触发任何写入。

2.2 为什么"只读挂载"仍然会写盘

这是本篇最重要的原理,也是最多人误解的地方。mount -o ro 只保证文件系统层不主动写,并不禁止一切写入。 内核的 VFS 层把超级块标记为只读,但以下机制仍可能绕过:

机制 触发条件 写入内容
日志回放(journal replay) ext3/ext4 标记为 dirty,XFS 有未完成日志 把日志中的元数据操作重做一遍
超级块修复 文件系统检测到不一致 修正 s_state、空闲块计数等
atime 更新 读取文件时(除非 noatime) inode 的访问时间
relatime 更新 相对时间戳的重算 最多每 24 小时一次

最典型的翻车场景:fsck 报告 ext4 "needs journal recovery"(断电时的正常现象),你用 mount -o ro 挂上去——内核照常回放日志,把几十 MB 的元数据改动写进镜像。挂完 sha256sum 一算,哈希和采集时完全不同了。

解决办法:ext 用 -o noload(不加载/回放 journal),XFS 用 -o norecovery(不回放 log),再叠加 blockdev --setro 从块层锁死只读。

2.3 为什么必须禁用 atime

Linux 传统上每次读文件都更新访问时间(atime)。现代内核默认使用 relatime(一天最多更新一次),但只要没有显式指定 noatime,这个行为就仍然存在。

对取证来说这是不可接受的:读取证据文件会改变被读取文件的时间戳;如果还用了 find 遍历目录、用反取证工具扫描未分配空间,几百上千个文件的 atime 会被改。而"访问时间"恰恰是取证分析中最有用的字段之一——它能证明"谁在什么时候碰过这个文件"。

不改 atime 不是保守,是保护证据。

规范做法:所有取证挂载一律加 noatime。如果环境里 /etc/fstab 配了 relatime,命令行参数也要显式覆盖。

2.4 Sleuth Kit 分层架构

TSK 的设计本身就是"不挂载"的产物——它把磁盘解析分成五层,每层工具只做一件事:

层 工具 职责
Image img_stat 镜像格式、扇区大小
Volume mmls / mmstat / mmcat 分区表解析,提取卷
File System fsstat / fls / icat / istat 目录树、文件元数据、文件内容
Data Unit blkcat / blkls 按块号提取
Journal jls / jcat 日志分析

这个架构带来三个直接好处:

  1. 零写入:所有工具都是 open() + pread() 模式
  2. 能访问内核不给看的东西:删除文件、未分配空间、文件系统元数据
  3. 能精确定位:每个文件有唯一的 inode/MFT 编号,可作为证据引用

2.5 mmls 输出格式详解

mmls 的输出是后续所有操作的基础,必须读懂每一列:

Slot      Start        End          Length       Description
00: Meta  0000000000   0000000000   0000000001   Primary Table (#0)
01: ----- 0000000000   0000000062   0000000063   Unallocated
02: 00:00 0000000063   0009510479   0009510417   NTFS (0x07)
03: ----- 0009510480   0009514259   0000003780   Unallocated
列 含义 用法
Slot 槽位编号 提取卷时作为参数(mmcat ... 02)
Start / End / Length 起止扇区与长度 全部以 512 字节为单位(除非另有提示)
Description 卷类型 MBR/GPT 类型代码 + 文件系统识别结果

00: 00:00 的含义:第一段 00: 是分区表序号(第几个分区表),第二段是该表内的槽位号。00:00 = 第一个分区表的第一项。嵌套的 DOS 扩展分区会出现 01:00 这样的二级编号。

Unallocated 行的价值:它们是分区表未占用的空间。这是反取证软件的默认藏身点,必须用 blkls 或关键字搜索专门检查。

关于 [ntfs] 这类方括号标记:TSK 工具在不同输出位置使用方括号表示"推测/强制"的类型,而 mmls 的 Description 列用的是 NTFS (0x07) 这种"类型名 + MBR 类型码"格式。写脚本解析时不要混淆这两种表示法。

换算偏移:offset_bytes = Start × 512。例如 Start = 0000000063 对应 32256 字节。


三、操作步骤

3.1 第 1 步:Sleuth Kit 不挂载访问(首选)

sudo apt install sleuthkit

img_stat /evidence/DigiForensics.dd     # Image type: raw  Sector size: 512
mmls /evidence/DigiForensics.dd         # 分区布局
fsstat -o 63 /evidence/DigiForensics.dd # 簇大小、$MFT 位置、卷序列号

# 递归全部目录,完整路径(-r 递归,-p 显示完整路径)
fls -o 63 -r -p /evidence/DigiForensics.dd > /evidence/full-listing.txt

# 提取文件(inode 编号从 fls 输出中获取)
icat -o 63 /evidence/DigiForensics.dd 1234-128-3 > /evidence/extracted-file.bin

# 提取文件系统内未分配块做关键字搜索(-o 是文件系统起始扇区,不是 Slot 号)
blkls -o 63 /evidence/DigiForensics.dd | strings | grep -i 'target-string'

# 按 Slot 提取整个卷
mmcat /evidence/DigiForensics.dd 2 > /evidence/partition2.dd

inode 编号格式:元数据地址-序列号-类型。NTFS 中 11443-128-4 表示 MFT 条目 11443、属性序列 128、类型 4($DATA)。

这个编号本身就是证据引用坐标——报告里写清"文件 X 对应 MFT 条目 11443",比"某文件夹里有个文件"精确得多。

整个流程零写入,可以在原始主副本上直接运行,无需制作工作副本。

3.2 第 2 步:只读环设备挂载

当确实需要用 ls/cp/grep 等常规工具浏览文件内容时才用。核心是三层只读防护:

# ① 建立只读环设备(-r 只读,-P 扫分区生成 /dev/loop0p1、p2 ...)
sudo losetup -r --find --show -P /evidence/DigiForensics.dd   # 输出:/dev/loop0

# ② 在块层锁死只读(双保险)
sudo blockdev --setro /dev/loop0
blockdev --getro /dev/loop0        # 输出 1 = 已只读

losetup -r 的关键意义:它让块设备层拒绝一切写请求,即使后续想 mount -o rw 也会失败——这是防止"手滑"最有效的技术手段。

挂载、浏览、卸载(必须完整走完):

sudo mkdir -p /mnt/evidence

# 挂载:noload / norecovery / noatime 三者缺一不可
sudo mount -t ext4  -o ro,noload,noatime,noexec     /dev/loop0p1 /mnt/evidence
sudo mount -t xfs   -o ro,norecovery,noatime,noexec /dev/loop0p2 /mnt/evidence
sudo mount -t ntfs3 -o ro,noatime,noexec            /dev/loop0p3 /mnt/evidence
# NTFS 备选:装 ntfs-3g 后用 -t ntfs-3g
# 裸文件系统镜像(无分区表):sudo mount -o ro,noload,noatime,loop 镜像文件 /mnt/evidence

# 浏览结束后立即卸载并分离环设备
sudo umount /mnt/evidence
sudo blockdev --setrw /dev/loop0        # 解除块层只读
sudo losetup -d /dev/loop0              # 分离,否则环设备泄漏

3.3 第 3 步:挂载后立即验证哈希

这是整个流程中不可省略的一步。 挂载前后各算一次:

sha256sum /evidence/DigiForensics.dd     # 挂载前
# ...执行挂载与浏览,卸载后...
sha256sum /evidence/DigiForensics.dd     # 卸载后

两个哈希必须一致。 不一致意味着挂载过程修改了镜像,此时立即停止所有操作,记录修改前后的哈希值,从主副本重新制作工作副本,并在报告中如实记录这一事件——隐瞒比事件本身更严重。

3.4 第 4 步:挂载 E01 镜像

E01 是压缩容器,不能直接 losetup,需先用 FUSE 桥接(命令见第七节)。ewfmount 暴露为原始镜像文件供 TSK 读取;xmount 支持更多格式且输出虚拟可写的 dd——所有写入只落在内存缓存中,源文件不受影响,这让它特别适合挂载疑似恶意或损坏的镜像:即使镜像