SecurityRonin/snss-forensic
GitHub: SecurityRonin/snss-forensic
一个用 Rust 编写的 Chromium 系浏览器 SNSS 会话文件只读取证解码器,用于验证、解码并重放浏览器会话恢复数据中的标签页状态。
Stars: 0 | Forks: 0
# snss-forensic
[](https://crates.io/crates/snss-core)
[](https://docs.rs/snss-core)
[](https://www.rust-lang.org)
[](LICENSE)
[](https://github.com/sponsors/h4x0r)
[](https://github.com/SecurityRonin/snss-forensic/actions/workflows/ci.yml)
[](https://github.com/SecurityRonin/snss-forensic/actions/workflows/ci.yml)
[](https://github.com/rust-secure-code/safety-dance/)
[](deny.toml)
**面向 Rust 的 Chromium/Brave/Edge SNSS 会话文件取证工具 —— 一个无 panic、
只读的解码器,用于验证 `SNSS` 命令流,将其拆分为
带长度前缀的记录,解码 navigation-command 的 `base::Pickle` payload,并
将它们重放为浏览器启动时恢复的各窗口 tab 状态。**
SNSS 是 Chromium 系浏览器用来持久化
会话和 tab 状态的追加写入(append-only)命令日志格式 —— 即“恢复你的标签页”背后的
`Session_*`、`Tabs_*` 和 `Apps_*` 文件(以及
现代的 `Sessions/` 文件夹)。每个文件由一个 4 字节的
`SNSS` magic 加上一个版本头组成,其后跟着带有 `u16` 长度前缀的命令
记录;导航命令包含一个 Chromium `base::Pickle` payload。
[`snss-core`](https://crates.io/crates/snss-core) 如实解码该结构,并且
不做任何主观判断 —— 没有 `unsafe`,没有 C 绑定,也没有写入路径。
## 30秒读取会话文件
```
[dependencies]
snss-core = "0.1"
```
```
use std::io::Cursor;
// 1. Validate the SNSS header and split the command stream into records.
let stream = snss::read_records(Cursor::new(bytes))?;
// 2. Replay the commands into the per-window tab tree the browser would restore.
let replayed = snss::replay(&stream, snss::Dialect::Session);
for window in &replayed.windows {
for tab in &window.tabs {
let nav = tab.current_nav(); // the current entry of each open tab
println!("tab {} -> {} ({})", tab.id, nav.url, nav.title);
}
}
// Non-fatal anomalies (a truncated live-file tail, a bad navigation Pickle) are
// surfaced, never silently dropped:
for w in &stream.warnings { eprintln!("{w:?}"); }
# Ok::<(), snss::SnssError>(())
```
`Tabs_*` 文件(最近关闭的恢复列表)使用 `Tabs` 方言;请传入
`snss::Dialect::Tabs`。要一次性遍历 profile 目录下的每一个会话源,
请使用 `snss::SessionStore::open_dir(profile_dir)`(或者
`open_default_profile()`),它会读取每个源并保留各源的警告。
## 解码内容
| 入口 | 读取内容 | 产物 |
|---|---|---|
| `read_records` | `SNSS` magic + 版本头,`u16` 长度记录 | `RecordStream { version, records, warnings }` |
| `decode_navigation` | 一个导航命令的 `base::Pickle` payload | `NavCommand { tab_id, index, url, title }` |
| `replay` | 一个 `RecordStream` + 方言 | `Replayed { windows }` — `Window`/`Tab`/`Nav` 树 |
| `SessionStore::open_dir` | 一个 profile 目录 | 所有发现的 `Source` + 警告 |
`base::Pickle` 解码器是 4 字节对齐的,且完全按照
Chromium 写入它的方式带有长度前缀(UTF-8 URL,UTF-16-LE 标题);重放时对 `(tab, index)` 应用“最后写入生效”(last-write-wins)原则,并解析每个 tab 的当前条目及固定状态。
## 信任但要验证
SNSS 文件是未经信任的、可被攻击者控制的输入,因此该 crate 在构建时经过了严格的安全加固:
- **`#![forbid(unsafe_code)]`** 覆盖整个工作区 —— 任何地方都不允许使用 `unsafe`。
- **设计上即为只读** —— 解码器**不提供写入路径**;通过此 API
在结构上是不可能修改浏览器会话存储的。
- **无 panic** —— 每个记录长度、Pickle 字段长度以及对齐步骤
在使用前都经过了边界检查;精心构造的长度字段无法引发
越界读取或内存分配炸弹。格式错误的输入会以类型化的
`SnssError` 或非致命的 `Warning` 呈现,绝不会是静默的默认值。
- **经过模糊测试** —— `cargo-fuzz` 目标涵盖了记录流读取器(`records`)和
导航 `base::Pickle` 解码器(`navigation`);其不变性是“绝不能发生 panic。”
- **基于真实 Chromium 数据验证** —— 读取并重放了真实的 Brave `Session_*`/`Tabs_*`/`Apps_*`
文件(测试夹具被 gitignore 忽略了 —— 因为它们包含个人
历史记录),并辅以字节精确的合成命令流。请参阅
[`docs/validation.md`](docs/validation.md)。
## 计划中:`snss-forensic` 分析器
该工作区今天发布了读取器(`snss-core`)。一个同级的 `snss-forensic`
分析器 crate —— 针对
会话恢复异常(例如悬空或前向引用的 tab 索引、
截断尾部恢复、重放不一致)输出严重性分级的
[`forensicnomicon::report`](https://crates.io/crates/forensicnomicon) 结果 —— 是一个计划中的后续项目,以便将会话文件的异常情况与其他所有 artifact 层统一聚合。目前还没有
分析器逻辑;这里也没有对其进行存根或伪造。
## 参考
- **Chromium `components/sessions`** —— 标准的 SNSS 写入器/读取器(命令
帧封装、`SNSS` magic + 版本头、追加写入日志):
- **Chromium `base::Pickle`** —— 导航 payload 使用的字段编码格式:
[隐私政策](https://securityronin.github.io/snss-forensic/privacy/) · [服务条款](https://securityronin.github.io/snss-forensic/terms/) · © 2026 Security Ronin Ltd
标签:Chromium, Rust, 云资产清单, 可视化界面, 数字取证, 数据解析, 网络流量审计, 自动化脚本, 逆向工程, 通知系统