SecurityRonin/wire-desktop-forensic
GitHub: SecurityRonin/wire-desktop-forensic
Wire 桌面客户端 IndexedDB 取证解析器,从本地存储中提取会话与时间线并如实标记加密不可恢复的消息内容。
Stars: 0 | Forks: 0
# wire-desktop-forensic
[](https://crates.io/crates/wire-desktop-core)
[](https://crates.io/crates/wire-desktop-forensic)
[](https://docs.rs/wire-desktop-core)
[](https://www.rust-lang.org)
[](LICENSE)
[](https://github.com/sponsors/h4x0r)
[](https://github.com/SecurityRonin/wire-desktop-forensic/actions/workflows/ci.yml)
[](docs/validation.md)
[](https://github.com/rust-secure-code/safety-dance)
[](fuzz)
[](deny.toml)
**以调查人员所需的方式读取 Wire 桌面端聊天存储——从 Chromium IndexedDB 中提取类型化记录和时间线,并如实标记加密的消息正文,而不是伪造它们。**
Wire 桌面端是 Wire Web 客户端的 Electron 封装;其所有证据
都存在于同一个 Chromium IndexedDB 存储中
(`IndexedDB/https_app.wire.com_0.indexeddb.leveldb`),以 Dexie 对象
存储的形式排列。本套件建立在 fleet 的 Chromium IndexedDB 读取器之上,并
将这些通用记录转换为 Wire 会话、事件、用户和客户端——同时
告诉您哪些消息正文可以读取,哪些不能。
## 概览
```
use wire_desktop_core::read_profile;
use wire_desktop_forensic::audit_store;
// Point at a Wire profile directory (Electron userData, e.g.
// ~/Library/Application Support/Wire) — the store path is resolved from the
// forensicnomicon Wire spec, not hardcoded here.
let store = read_profile(std::path::Path::new("/evidence/Wire"))?;
for f in audit_store(&store) {
println!("[{:?}] {} — {}", f.severity, f.code, f.note);
}
// [Some(Low)] WIRE-MESSAGE-CLEARTEXT — a cleartext message body was recovered …
// [Some(Info)] WIRE-MESSAGE-ENCRYPTED-UNRECOVERABLE — client-side (Proteus) encrypted; key not in OS Safe Storage …
// [Some(Medium)] WIRE-RECORD-DELETED-RESIDUAL — a deleted Wire record survives as a LevelDB tombstone …
# Ok::<(), wire_desktop_core::WireError>(())
```
## 两个 crate
| Crate | 角色 |
|---|---|
| **`wire-desktop-core`** | 读取器 — 将 IndexedDB Dexie 存储解释为类型化的 [`WireRecord`]s(`conversation` / `event` / `user` / `client`)+ 按时间顺序的时间线。不包含调查发现。 |
| **`wire-desktop-forensic`** | 分析器 — 将已解释的存储审计为标准的 `forensicnomicon::report::Finding`s。 |
它通过 Wave-2 [`chromium-storage-indexeddb`](https://github.com/SecurityRonin/chromium-storage-forensic)
读取器(LevelDB 键编码 + Blink V8 值解码)进行读取,并复用了来自
[`forensicnomicon`](https://github.com/SecurityRonin/forensicnomicon) 的 Wire 规范——它本身不
包含任何 LevelDB 或路径知识。
## 异常代码
| 代码 | 严重程度 | 类别 | 含义 |
|---|---|---|---|
| `WIRE-MESSAGE-CLEARTEXT` | 低 | 残留 | 从存储中恢复了明文消息正文 |
| `WIRE-MESSAGE-ENCRYPTED-UNRECOVERABLE` | 信息 | 来源 | 消息正文是客户端(Proteus)加密的;密钥不在 OS Safe Storage 中,因此明文无法恢复 |
| `WIRE-RECORD-DELETED-RESIDUAL` | 中 | 残留 | 一个已 tombstone(删除)的 Wire 记录在 LevelDB 中存活并被恢复 |
| `WIRE-OTR-KEY-PRESENT` | 高 | 威胁 | Wire 的 `otr_key`(附件密钥)存在于 IndexedDB 中——可恢复的密钥材料 (MITRE T1552) |
## 加密内容——真实,而非伪造
Wire 在客户端(Proteus)对消息内容进行加密。该密钥**不**存储在
Chromium OS Safe Storage 中(与 Chrome 的 cookie 密钥或 Signal 的密钥不同),因此它
**无法从此工件中恢复**。加密的正文会显示为
`PayloadState::Encrypted`,同时其明文元数据(会话、发送者、
时间)保持完整;请求明文会返回一个类型化的
`WireError::EncryptedPayloadUnrecoverable`——绝对不会返回看似合理但错误的数据。该
crate 不执行解密,也不提供占位符加密技术。
## 信任,但要验证
- **模糊测试** — 两个 `cargo-fuzz` 目标(`fuzz_interpret_records`、`fuzz_forensic`)
在任意输入上驱动解释器和完整的审计流水线;其
不变式是*永不 panic*。
- **通过 lint 实现 panic-free** — `#![forbid(unsafe_code)]`,在生产代码中禁止
`clippy::unwrap_used` / `expect_used`,以及带有边界检查的值处理。
- **基于真实的 Chromium 生成的字节进行验证** — 读取器已针对
由 Google Chrome 根据 Wire 文档化的 Dexie schema(tier-2)生成的真实 IndexedDB 存储进行了测试。有关
真实的 tier 讨论以及如何升级它,请参阅 [docs/validation.md](docs/validation.md)。
[隐私政策](https://securityronin.github.io/wire-desktop-forensic/privacy/) · [服务条款](https://securityronin.github.io/wire-desktop-forensic/terms/) · © 2026 Security Ronin Ltd
标签:IndexedDB, Rust, 即时通讯取证, 可视化界面, 数字取证, 数据解析, 网络流量审计, 自动化脚本, 通知系统