feat(refit): complete S1-S6 — case abstraction, grounding, log-odds, plugins, coref, multi-source
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>
This commit is contained in:
305
DESIGN.md
Normal file
305
DESIGN.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# 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. 设计原则(贯穿全文的不变式)
|
||||
|
||||
1. **LLM 提议,代码裁决**。LLM 负责语言/分类/感知;它**不持有案件状态、
|
||||
不产出数值、不写入未经核验的事实**。所有「真相」在符号层。
|
||||
2. **每条记录的事实都可从一次工具调用重新推导**。结论可被独立复核。
|
||||
3. **推理核心与设备类型无关**。设备特定逻辑全部位于「能力插件」中;
|
||||
支持一种新设备 = 写插件,绝不改核心。
|
||||
4. **看似不可逆的操作(如实体归并)实为可逆、带证据的论断**,可被推翻。
|
||||
|
||||
这四条不是口号——下文每个设计决策都对应其中一条。
|
||||
|
||||
---
|
||||
|
||||
## 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` 网关前置条件**:
|
||||
|
||||
1. 每个 fact 必须引用一次**本 agent 本任务内真实发生过的** `invocation_id`。
|
||||
2. 代码校验 `fact.value` 命中该次调用的输出:
|
||||
- 文本输出 → 逐字 substring 匹配;
|
||||
- 结构化/二进制工具输出 → 与解析后的字段匹配。
|
||||
3. 任一 fact 不通过 → **整条拒绝写入**,返回失败的 fact,agent 须修正重试。
|
||||
4. 通过 → 写入;`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. 设计取舍与未决问题
|
||||
|
||||
1. **grounding 对自由文本的边界**:只硬核验 `verified_facts` 里的结构化原子,
|
||||
`interpretation` 不做逐字核验(诚实划界)。可加一个二级 lint:扫描
|
||||
interpretation 中形似路径/时间戳/哈希但未被任何引用调用覆盖的串并告警。
|
||||
2. **LR 标定表初值人定**:先用第 4.5 节的初值跑通;「从标注案例学习 LR」是后续工作。
|
||||
3. **安卓 userdata 加密**:能否取得解密密钥决定 4.7 安卓插件的证据深度——需尽早探明。
|
||||
4. **实体解析的破坏性 vs 可逆**:本设计选**可逆的 `same_as` 边**而非破坏性归并——
|
||||
牺牲一点查询效率换取完全可审计可回滚,符合原则 4。
|
||||
5. **报告粒度**:定为「一案一份综合报告」,内嵌每证据小节 + 跨源关联,
|
||||
而非每证据独立成篇。
|
||||
Reference in New Issue
Block a user