Consolidates the long-running refit work (DESIGN.md as authoritative spec)
into a single baseline commit. Six stages landed together:
S1 Case + EvidenceSource abstraction; tools parameterised by source_id
(case.py, main.py multi-source bootstrap, .bin extension support)
S2 Grounding gateway in add_phenomenon: verified_facts cite real
ToolInvocation ids; substring / normalised match enforced; agent +
task scope checked. Phenomenon.description split into verified_facts
(grounded) + interpretation (free text). [invocation: inv-xxx]
prefix on every wrapped tool result so the LLM can cite.
S3 Confidence as additive log-odds: edge_type → log10(LR) calibration
table; commutative updates; supported / refuted thresholds derived
from log_odds; hypothesis × evidence matrix view.
S4 iOS plugin: unzip_archive + parse_plist / sqlite_tables /
sqlite_query / parse_ios_keychain / read_idevice_info;
IOSArtifactAgent; SOURCE_TYPE_AGENTS routing.
S5 Cross-source entity resolution: typed identifiers on Entity,
observe_identity gateway, auto coref hypothesis with shared /
conflicting strong/weak LR edges, reversible same_as edges,
actor_clusters() view.
S6 Android partition probe + AndroidArtifactAgent; MediaAgent with
OCR fallback; orchestrator Phase 1 iterates every analysable
source; platform-aware get_triage_agent_type; ReportAgent renders
actor clusters + per-source breakdown.
142 unit tests / 1 skipped — full coverage of the new gateway, log-odds
math, coref hypothesis fall-out, and orchestrator multi-source dispatch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18 KiB
MASForensics 系统改造设计
目标:把当前「单台 Windows 磁盘取证」系统改造为能处理多设备、多行为人、 异构证据、需跨源关联的复杂取证系统。本文是唯一的权威设计文档 (已合并早先的
REFIT_PLAN.md/RESEARCH_DESIGN.md两份草稿)。触发本次改造的实际案件:2025 美亚杯资格赛 Individual —— 5 份证据 (1 USB E01、1 安卓整盘
blk0_sda.bin、3 份 iOS 提取、1 组交易截图), 跨 LEUNG YL / CHAN MH / FUNG CC 至少 3 人。
1. 设计原则(贯穿全文的不变式)
- LLM 提议,代码裁决。LLM 负责语言/分类/感知;它不持有案件状态、 不产出数值、不写入未经核验的事实。所有「真相」在符号层。
- 每条记录的事实都可从一次工具调用重新推导。结论可被独立复核。
- 推理核心与设备类型无关。设备特定逻辑全部位于「能力插件」中; 支持一种新设备 = 写插件,绝不改核心。
- 看似不可逆的操作(如实体归并)实为可逆、带证据的论断,可被推翻。
这四条不是口号——下文每个设计决策都对应其中一条。
2. 现状问题诊断
| # | 问题 | 位置 | 后果 |
|---|---|---|---|
| P1 | 单镜像假设深植:工具是闭包绑死 image_path,图是单源,主程序只选一个镜像 |
tool_registry.py:148 register_all_tools、main.py:91-153 |
无法摄取多份证据,无法跨设备关联 |
| P2 | 反幻觉只写在提示词里 | base_agent.py system prompt |
LLM 一旦不听话,错误事实进入案件记录且事后无法识别 |
| P3 | 置信度公式无统计含义且有序依赖缺陷:delta=weight*(1-conf)(正)/weight*conf(负),正负边混合时更新结果与边的到达顺序有关 |
evidence_graph.py:26-33 |
置信度不可校准、不可辩护 |
| P4 | 工件分类是 Windows 专属:靠 hive 名 / .pf / mirc 关键词 |
tool_registry.py:80-107 _auto_categorize |
iOS/安卓工件全部落入 other |
| P5 | 案件信息硬编码 cfreds_hacking_case |
config.yaml:35-50 |
换案即需改代码 |
| P6 | 镜像发现靠扩展名 glob,.bin 不在列表 |
main.py:28 _IMAGE_GLOBS |
blk0_sda.bin 不被发现 |
| P7 | Phenomenon 无来源标注 | evidence_graph.py:85 Phenomenon |
不知道某发现出自哪台设备,跨源关联无锚点 |
改造同时解决「接入新证据」与「修掉 P1-P7 这些固有缺陷」。
3. 目标架构
case.yaml ──► Case ──► N × EvidenceSource
├ id / type / owner / path
└ access_mode: image | tree
│
┌──────────────┴───────────────┐
image-backed tree-backed
(TSK, inode 寻址) (路径寻址:已挂载/已解包)
│ │
└────────────┬─────────────────┘
▼
SourceRegistry ── source_id → SourceHandle(解析 path/offset/mode)
│
ToolRegistry ── 工具按 access_mode 注册,调用时绑定 source_id
│
┌──────────────────────┼───────────────────────┐
▼ ▼ ▼
Knowledge-Source Graph Write Gateway ToolInvocationLog
Agents (LLM) ──► (唯一写入口,强制 (每次工具调用留痕:
只能经网关写图 前置条件 = grounding) args / 输出 / sha256)
│ │
└──────────────────────┴──► Grounded Evidence Graph (GEG)
Phenomenon / Hypothesis / Entity
置信度 = 对数几率累加
保留现有的五阶段流水线、断连恢复、运行归档、工具结果缓存、
AgentFactory 动态组合——这些设计是好的,不重写,只适配。
4. 核心设计
4.1 证据源抽象(解决 P1/P5/P6/P7,地基)
新增 case.py:
EvidenceSource数据类:id、label、type、owner(关联人)、path、access_mode、meta(类型特定,如分区 offset / 解包后根目录)。Case:持有list[EvidenceSource]+ 案件元数据,从case.yaml加载。access_mode是关键设计区分:image:块设备/磁盘镜像,用 TSK 按 inode 寻址(USB E01、安卓blk0_sda各分区)。tree:已挂载文件系统或已解包目录,按路径寻址(iOS 提取解压后、归档展开后)。- 工具按 access_mode 分族注册(见 4.2)。一份证据可经「准备」从 image 变为 tree (如分区 mount、zip 解包)。
main.py 的 select_image_interactive(:91-153)改为加载/构造 Case;
_IMAGE_GLOBS 改为类型探测(mmls 试探 + 文件头嗅探),不再靠扩展名。
config.yaml 删除 cfreds_hacking_case,案件信息移入 case.yaml。
4.2 工具注册按源参数化(解决 P1)
现状:register_all_tools(image_path, offset, ...) 把单一镜像闭包进每个工具
(tool_registry.py:159+)。改造:
- 工具执行器签名增加
source_id;执行时经SourceRegistry解析出真实 path/offset/mode。 TOOL_CATALOG按access_mode标注工具适用性;agent 拿到的工具集由其 负责的源类型决定。- 「当前源」上下文:编排器为 agent 设置 current source(类比现有
graph._current_agent),工具默认作用于它——LLM 不必每次传source_id(减少出错)。跨源工具(时间线合并、实体查询)显式跨源。 - 缓存键
_cache_key(tool_registry.py:41)纳入source_id,防止跨源串味。
4.3 图写入网关(解决 P2,落实原则 1)
现状:agent 通过 add_phenomenon 等工具直接写图,约束只在 prompt。改造:
- 所有图变更(
add_phenomenon/add_hypothesis/link/observe_identity…) 收敛到一个写入网关。网关在代码层强制前置条件。 - 现有 prompt 里的「反幻觉规则」下沉为网关的硬校验。LLM agent 的四阶段工作流 (INVESTIGATE→RECORD→LINK→ANSWER)不变——变的是 RECORD 这一步底下的网关变严。
base_agent.py的mandatory_record_tools机制保留(它保证 agent 真的记录了东西)。
4.4 证据落地约束 Grounding(解决 P2,落实原则 2)
这是系统可靠性的核心机制。
ToolInvocationLog:每次工具调用留痕一条记录
{invocation_id, source_id, tool, args, output, output_sha256, agent, ts}。
现有结果缓存(tool_registry.py:29)已存确定性输出,扩展为完整留痕即可。
Phenomenon 一分为二——把「事实」和「解读」分开:
verified_facts:list[{type, value, invocation_id}],type ∈ {path, timestamp, inode, hash, identifier, count, ...}。interpretation: 自由文本,agent 的分析叙述。
add_phenomenon 网关前置条件:
- 每个 fact 必须引用一次本 agent 本任务内真实发生过的
invocation_id。 - 代码校验
fact.value命中该次调用的输出:- 文本输出 → 逐字 substring 匹配;
- 结构化/二进制工具输出 → 与解析后的字段匹配。
- 任一 fact 不通过 → 整条拒绝写入,返回失败的 fact,agent 须修正重试。
- 通过 → 写入;
verified_facts每条带invocation_id(可重跑复核),interpretation标记为「未核验分析」。
效果:在系统里「记录一条工具输出未支撑的路径/时间戳/哈希/标识符」
结构性地不可能。LLM 仍可能写错 interpretation,但报告会把
verified facts(带重跑指令的引证)与 interpretation(明确标注的分析)
分开渲染,人类调查员一眼可辨。这是诚实划定边界的可靠性保证。
现有
_make_auto_record(tool_registry.py:126)把工具输出直接转 phenomenon—— 那是「平凡落地」的特例(描述即输出),新设计是它的一般化与形式化。
4.5 假设置信度:似然比 / 对数几率(解决 P3)
把 evidence_graph.py:26 的 _DEFAULT_EDGE_WEIGHTS 从「拍脑袋的 delta」
换成基于**似然比(LR)**的对数几率累加:
-
每条
Phenomenon → Hypothesis边代表一个似然比。LLM 仍只做离散分类 (这条证据对这条假设是 direct_evidence / supports / weakens / contradicts …), 数值log₁₀(LR)由标定表查得——LLM 绝不吐数字(延续现有「LLM 选类型、 代码算数值」哲学并赋予统计基础)。 -
置信度更新:
L_post = L_prior + Σ log₁₀(LR_i) # 对数几率,可交换 → 无序依赖 confidence = 1 / (1 + 10^(−L_post)) -
边类型 →
log₁₀(LR)标定表(初值,后续可由标注案例校准):边类型 log₁₀LR direct_evidence+2.0 supports/consequence_observed+1.0 prerequisite_met+0.5 weakens−0.5 contradicts−2.0 -
阈值不变(≥0.8 supported / ≤0.2 refuted),只是改由
L_post推出。 -
prior_prob成为可配置量(默认 0.5 →L_prior=0)。 -
简化假设说明:多条边按独立处理(朴素贝叶斯)。同类证据反复出现并非 完全独立——加一个旋钮:同
(hypothesis, edge_type)的边数封顶或衰减,避免 「同一发现被多 agent 重复入图」虚高置信度(现有 Jaccard 去重已部分缓解)。
附带产出一个 假设 × 证据矩阵视图,供报告与线索选择使用。
4.6 跨源实体解析(解决「复杂场景」的关联难题,落实原则 4)
复杂取证的核心难题:iPhone keychain 里的 Apple ID、安卓短信库里的号码、 USB 文件作者、交易截图里的钱包地址——哪些指向同一行为人?
关键设计:「身份共指」本身就是一条假设——于是实体解析不是独立子系统, 而是 4.5 假设机制的复用:
- agent 观察到标识符即经网关
observe_identity,记一条类型化的标识符 (强标识符:IMEI / 钱包地址 / email / 电话号;弱标识符:昵称 / 显示名), 挂到暂定Entity。 - 「Entity A ≡ Entity B」登记为一条
Hypothesis;共享强标识符 = 强 +LR 边, 共享弱标识符 = 弱 +LR 边,冲突的强标识符 = 强 −LR 边——用 4.5 同一套计算打分。 - 不做破坏性归并:跨阈值时在两个 Entity 间加一条
same_as边(由该 coref 假设背书)。查询时把same_as连通分量视作同一行为人。完全可逆、可审计、 可被后续 contradicts 证据推翻(落实原则 4)。 - Blocking:只在「至少共享一个标识符或名称高相似」的实体对间建 coref 假设, 避免 O(n²)。
跨设备时间线、「谁在何时做了什么」由 same_as 连通后的实体图自然涌现。
4.7 能力插件层(接入 5 类证据)
每类证据 = 一个 (摄取 handler, 工具集, 知识源 agent) 三元组。推理核心不动。
| 插件 | 摄取 | 新工具 | 知识源 agent |
|---|---|---|---|
| iOS 提取 | unzip 解包为 tree 源 |
parse_plist(含二进制 plist)、sqlite_tables/sqlite_query(sms.db、WhatsApp ChatStorage.sqlite、通讯录)、parse_ios_keychain、read_idevice_info |
iOSArtifactAgent |
| 安卓整盘 | mmls 分区→各分区 image 源;可 mount 为 tree |
复用 TSK;ext4/F2FS 读取;fsstat 探明加密 |
复用 filesystem + AndroidArtifactAgent |
| 磁盘镜像(E01) | 已支持(TSK 含 ewf) | 现有 TSK 工具链 | 现有 filesystem/registry |
| 归档 | unzip_archive 通用解包 |
—— | —— |
| 媒体/截图 | —— | ocr_image(tesseract;注意 DeepSeek 无视觉能力,必须走 OCR) |
MediaAgent |
安卓风险:blk0_sda 的 userdata 分区大概率 FBE 加密。先 fsstat 各分区
探明:未加密→TSK 直接用;加密且无密钥→只能分析 EFS/PARAM/system 等非加密区。
tool_registry.py:80 的 _auto_categorize 改为可扩展:分类由源插件提供自己的
工件分类表,而非全局 Windows 关键词表(解决 P4)。
4.8 Agent 体系重组
现有 7 个 agent 按 Windows 工件命名(registry、communication=邮件/IRC、 network=浏览器/PCAP)。改为按调查职能组织,并增加平台特定 agent:
agent_factory.py的_AGENT_CLASSES(:34-40)扩充:新增ios_artifact、android_artifact、financial(钱包/交易)、media。communication泛化:邮件 + IM + 短信,跨平台。- 新增 源类型 → 适任 agent 映射,供 Phase 1 逐源派 triage agent。
create_specialized_agent(:69)的动态组合机制保留——它本就是应对能力缺口的 正确手段,只是工具目录变大后选择空间更丰富。
4.9 编排器多源流水线
| 阶段 | 改造 |
|---|---|
| Phase 1 | 「单镜像初勘」→ 逐源并行 triage,每源派类型适配的 agent |
| Phase 2 | 假设跨源生成;身份共指假设在此首次登记 |
| Phase 3 | leads 派发到源感知 agent;假设×证据矩阵实时更新 |
| Phase 4 | 跨源时间线合并,按源做时区归一(iOS UTC vs 安卓本地时间) |
| Phase 5 | 一案一份综合报告:含假设结论、实体关联图、每条结论的 provenance 引证 |
断连恢复、运行归档逻辑保留,graph_state.json 增量纳入新字段。
5. 数据模型变更汇总
| 节点/结构 | 变更 |
|---|---|
EvidenceSource |
新增一等节点(src-*) |
ToolInvocation |
新增留痕记录(inv-*),随 graph 持久化 |
Phenomenon |
+ source_id;description 拆为 verified_facts[] + interpretation;澄清/移除语义含混的 confidence(默认 1.0),观测的可靠性由 grounding 表达 |
Hypothesis |
+ prior_prob、log_odds(累加量);confidence 改为派生值 |
Entity |
+ 类型化标识符集合;通过 same_as 边跨源连通 |
| Phenomenon→Hypothesis 边 | 携带 edge_type,映射到 log₁₀(LR)(替换 _DEFAULT_EDGE_WEIGHTS) |
| Entity→Entity 边 | 新增 same_as(由 coref 假设背书,可逆) |
evidence_graph.py 的 VALID_EDGE_TYPES、序列化/反序列化、Jaccard 去重相应适配。
6. 组件改动清单
| 文件 | 改动 |
|---|---|
case.py |
新建:Case / EvidenceSource / SourceRegistry |
main.py |
选源逻辑改为加载 Case;类型探测替代扩展名 glob |
tool_registry.py |
工具按 source_id 参数化;缓存键含 source;_auto_categorize 改可扩展;ToolInvocationLog |
evidence_graph.py |
数据模型变更(第 5 节);LR/对数几率置信度;写入网关 + grounding 校验 |
base_agent.py |
RECORD 走网关;add_phenomenon 改为 verified_facts+interpretation 接口 |
agent_factory.py |
_AGENT_CLASSES 扩充;源类型→agent 映射 |
orchestrator.py |
Phase 1 逐源;Phase 4 跨源时区归一;Phase 5 综合报告 |
agents/ |
新增 ios_artifact.py / android_artifact.py / financial.py / media.py;communication.py 泛化 |
tools/ |
新增 mobile_ios.py(plist/sqlite/keychain)、media.py(OCR)、archive.py(解包) |
config.yaml / case.yaml |
删除 cfreds_hacking_case;新建 case.yaml 证据清单 |
7. 构建顺序(按依赖排序)
| 阶段 | 内容 | 依赖 | 价值 |
|---|---|---|---|
| S1 | 4.1 证据源抽象 + 4.2 工具参数化 + 修 P6 | —— | 地基;先只在 USB E01 上跑通验证不破坏现有逻辑 |
| S2 | 4.3 写入网关 + 4.4 grounding + ToolInvocationLog | S1 | 可靠性核心;可量化「零幻觉录入」 |
| S3 | 4.5 LR/对数几率置信度 | 独立(可与 S2 并行) | 修 P3;置信度可辩护 |
| S4 | 4.7 iOS 插件 + 4.8 agent 重组 | S1 | 覆盖率 1/5 → 4/5 |
| S5 | 4.6 跨源实体解析 | S1+S3 | 跨设备关联,复杂场景能力成型 |
| S6 | 4.7 安卓 + 媒体插件 + 4.9 编排器适配 | S1+S4 | 全 5 份证据接入 |
S1+S2+S3 是「把系统改对」;S4-S6 是「把能力铺全」。建议严格按序—— S1 不稳,后面全是空中楼阁。
8. 设计取舍与未决问题
- grounding 对自由文本的边界:只硬核验
verified_facts里的结构化原子,interpretation不做逐字核验(诚实划界)。可加一个二级 lint:扫描 interpretation 中形似路径/时间戳/哈希但未被任何引用调用覆盖的串并告警。 - LR 标定表初值人定:先用第 4.5 节的初值跑通;「从标注案例学习 LR」是后续工作。
- 安卓 userdata 加密:能否取得解密密钥决定 4.7 安卓插件的证据深度——需尽早探明。
- 实体解析的破坏性 vs 可逆:本设计选可逆的
same_as边而非破坏性归并—— 牺牲一点查询效率换取完全可审计可回滚,符合原则 4。 - 报告粒度:定为「一案一份综合报告」,内嵌每证据小节 + 跨源关联, 而非每证据独立成篇。