strootle002/historia-navigationis
GitHub: strootle002/historia-navigationis
一款本地运行的轻量级浏览器历史记录查看与分析工具,支持自动识别并可视化解析 Chromium 与 Firefox 的数据库。
Stars: 0 | Forks: 0
# Historia Navigationis
一个用于基于 Chromium 和 Firefox 浏览器历史的本地查看器。打开浏览器历史 SQLite 数据库的副本(Chrome 的 `History` 或 Firefox 的 `places.sqlite`),搜索并过滤访问记录,按列对它们进行分组,浏览直方图或导航图,将它们划分为浏览会话,并将结果导出为 CSV。
所有操作都在你的机器上本地运行。不会有任何数据离开你的设备。

## 支持的浏览器
支持基于 Chromium 的浏览器和 Firefox,并在打开时自动检测文件格式:
- **基于 Chromium 的浏览器**(Chrome、Edge、Brave、Opera、Vivaldi、Arc 等)— 使用包含 `urls` 和 `visits` 表的 `History` SQLite 文件。
- **Firefox** — 使用包含 `moz_places` 和 `moz_historyvisits` 表的 `places.sqlite` 文件。
## 定位你的历史记录文件
浏览器在运行时会锁定其数据库,因此请先关闭浏览器(或者直接打开一个副本——该应用会在读取前将文件复制到一个按会话划分的工作目录中)。
**基于 Chromium 的浏览器** — `History` 文件:
| 平台 | 路径 |
| --- | --- |
| Linux | `~/.config/google-chrome/Default/History`(将 `google-chrome` 替换为 `chromium`、`microsoft-edge`、`BraveSoftware/Brave-Browser` 等) |
| macOS | `~/Library/Application Support/Google/Chrome/Default/History` |
| Windows | `%LOCALAPPDATA%\Google\Chrome\User Data\Default\History` |
**Firefox** — `places.sqlite` 文件:
| 平台 | 路径 |
| --- | --- |
| Linux | `~/.mozilla/firefox//places.sqlite` |
| macOS | `~/Library/Application Support/Firefox/Profiles//places.sqlite` |
| Windows | `%APPDATA%\Mozilla\Firefox\Profiles\\places.sqlite` |
`` 是一个看似随机的文件夹名称,例如 `a1b2c3d4.default-release`;请选择包含你书签的那个。
如果你没有看到该文件,你的浏览器配置文件可能存储在非默认位置——请检查浏览器设置或 `about:profiles` (Firefox) / `chrome://version` (Chromium) 以获取实际路径。
## 背景
分析浏览器历史记录对于 DFIR 调查至关重要,但我从未喜欢过现有的工具。在寻找适合我工作流程的解决方案时,我发现现有选项要么感觉太臃肿,要么在视觉上差强人意。像 Autopsy 这样的完整取证套件非常强大,但当你只需要快速检查单个历史记录文件时,它们通常过于消耗资源。另一方面,使用 DB Browser for SQLite 处理原始数据是可行的,但需要手动将时间戳转换为可读视图并理解其含义。像 NirSoft 的 BrowserHistoryView 这样的专用工具是最接近的,但其界面和偶尔出现的打开文件时的怪异问题让我感到不满意。
我开始在周末的业余时间构建这个应用程序,作为一个有趣的项目来解决这一痛点。我不再使用那些过于复杂或过于死板的工具,而是利用现有资源构建了一个轻量级、专注的解决方案,以高效地读取和可视化历史记录文件。为了使数据真正有助于分析,该应用程序包含了会话图表、过滤、分组以及一些基本设置。虽然它可能并不适合所有人,但希望它能对某些人有所帮助。
## 下载
每个 [GitHub release](https://github.com/strootle002/historia-navigationis/releases) 都附带了预构建的安装包:
- **Windows** — `.msi` (WiX) 和 `.exe` (NSIS)
- **Linux** — `.deb` 和 `.rpm`
- **macOS** — `.app` 和 `.dmg`
选择与你的平台相匹配的资产。macOS 上的 `.app` 不需要安装程序——只需将其拖入 Applications 即可。
## 功能
- **打开任何 Chrome 或 Firefox 历史记录数据库** — 文件会被复制到一个按会话划分的工作目录中,以避免与正在运行的浏览器发生锁定冲突。Chrome 的 `History`(`urls` + `visits` 表)和 Firefox 的 `places.sqlite`(`moz_places` + `moz_historyvisits` 表)会被自动检测。
- **事件查看器** — 虚拟化表格,支持调整列宽、排序、单元格级别的包含/排除过滤器、分组以及全屏事件视图。包含一个可折叠的直方图;在图表上拖动以限定自定义时间范围。
- **导航图** — 随时间可视化扫描访问记录,带有持续时间编码的标记;点击标记可展开当天的访问记录。
- **会话** — 自动将访问记录分割成会话,以 30 分钟的不活动间隔进行划分。
- **CSV 导出** — 将过滤后的行保存到用户选择的目录,或通过浏览器下载。
- **持久化设置** — 主题、时区、列宽、单元格过滤器和时间范围存储在 `localStorage` 中,并在多次启动间保持。
## 截图
使用 [`samples/demo-History`](./samples/demo-History) 中的虚拟示例数据库捕获(仅包含公开的示例 URL)。
### 事件查看器(深色)

带有在网格上方展开的直方图的虚拟化访问表格。
### 事件查看器(浅色)

浅色主题下的同一个事件查看器。
### 过滤和分组

在一个工作流中进行搜索、时间范围过滤、直方图范围限定以及列分组。
### 导航图

使用持续时间编码的标记和可展开的每日列表,随时间扫描访问记录。
### 会话

使用 30 分钟不活动间隔进行自动会话分割。
## 环境要求
- **Node.js 22+** 和 **npm**
- **Rust stable**(用于 Tauri 后端)
- **Tauri 2 系统先决条件** — 有关你平台的信息,请参阅 [Tauri 先决条件指南](https://tauri.app/start/prerequisites/)。
## 开发
```
npm install
npm run tauri dev
```
这将启动前端的 Vite 并运行 Tauri shell。
## 生产环境构建
在本地构建:
```
npm run tauri build
```
打包产物将输出到 `src-tauri/target/release/bundle/`,格式为 `.deb`、`.rpm`、`.nsis` 和 `.msi`(具体取决于宿主工具链)。
对于多平台发布,位于 `.github/workflows/release.yml` 的 GitHub Actions 工作流会在每次 GitHub release 时构建 Windows、Linux 和 macOS 安装程序,并将它们作为 release 资产上传。请参阅 [`.github/workflows/release.yml`](./.github/workflows/release.yml)。
## 项目结构
```
.
├── src/ React + TypeScript frontend
│ ├── components/ UI components
│ ├── hooks/ Reusable stateful hooks
│ └── utils/ Pure helpers (CSV, time conversion, histogram bucketing)
├── src-tauri/ Rust backend (Tauri commands)
│ ├── src/
│ │ ├── lib.rs History loading + CSV writing commands
│ │ └── main.rs App entry point
│ ├── capabilities/ Tauri permission grants
│ └── tauri.conf.json Tauri configuration
├── public/ Static assets
└── .github/workflows/ CI workflows
```
## 架构
该应用程序是一个 Tauri 2 桌面应用程序:在 WebView2/WebKit2GTK 进程中运行的 React/TypeScript 前端,通过 Tauri 的 IPC bridge 与 Rust 后端进行通信。Rust 进程拥有所有文件系统和 SQLite 的访问权限;前端仅负责渲染状态和分发命令。
```
┌──────────────────────────────────────────────────────────────┐
│ WebView process (React + TypeScript) │
│ │
│ ┌────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ App.tsx │ │ Components/ │ │ Hooks/ │ │
│ │ (state, │→ │ Toolbar, │← │ useHistory, │ │
│ │ routing) │ │ Histogram, │ │ useFilters, │ │
│ │ │ │ NavGraph, │ │ useSorting, │ │
│ │ │ │ Sessions, │ │ useGrouping, │ │
│ │ │ │ DetailModal │ │ useColumns │ │
│ │ │ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │ │ │
│ │ │ ▼ ▼ │
│ │ │ ┌──────────────────────────────────────┐ │
│ │ │ │ utils/ (pure helpers) │ │
│ │ │ │ time.ts histogram.ts helpers.ts │ │
│ │ │ └──────────────────────────────────────┘ │
│ └────────────┘ │
│ │ │
│ │ invoke("…") / listen("load-progress") │
│ ▼ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Tauri IPC bridge (window.__TAURI__) │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Rust process (src-tauri) │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ lib.rs — #[tauri::command] handlers │ │
│ │ │ │
│ │ copy_history_to_workspace ──► fs::copy + uuid │ │
│ │ load_browser_history ──► detect schema │ │
│ │ ├─► Chrome loader │ │
│ │ └─► Firefox loader │ │
│ │ save_csv_file ──► fs::write │ │
│ │ │ │
│ │ Sessions dir: $LOCALDATA/HistoriaNavigationis/ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌────────────────────────────┐ │
│ │ Chrome or │ │ User-picked export dir │ │
│ │ Firefox history │ │ (CSV output destination) │ │
│ │ (SQLite, │ │ │ │
│ │ copied) │ │ │ │
│ └──────────────────┘ └────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
```
**“打开文件”的数据流:**
1. 用户在工具栏中点击*打开文件…* → `useHistory.openFile()` 调用 Tauri 的 `open()` 对话框插件来选择 `History` 或 `places.sqlite` 文件。
2. 选定的路径被发送到 `copy_history_to_workspace` (Rust),该函数将文件复制到 `$LOCALDATA/HistoriaNavigationis/sessions//`,以避免与正在运行的浏览器发生锁定冲突。`-wal` 和 `-shm` 附属文件会尽力复制(仅限 Chrome;Firefox 使用单个文件)。
3. 复制的路径被发送到 `load_browser_history` (Rust),该函数使用 `rusqlite` 以只读方式打开它,通过检查 `sqlite_master` 检测浏览器(Chrome 为 `urls`/`visits`,Firefox 为 `moz_places`/`moz_historyvisits`),运行适当的 join 查询,并在以 `HistoryRow` 结构体流式传输回行数据时发出进度事件(`load-progress-count`、`load-progress`)。Firefox 的时间戳在后端从 Unix 微秒转换为 WebKit 微秒,因此前端保持与浏览器无关。
4. 前端将行存储在 `useHistory` 状态中,然后 `indexRowsForSearch` 为每一行附加一个预计算的小写“草垛”(haystack),以便进行快速搜索过滤。
5. 从那时起,所有的过滤、排序、分组和视图渲染都在 WebView 内存中发生;在用户执行导出之前,不会再发生任何进一步的后端调用。
## 隐私
Historia Navigationis 不会连接到任何网络服务。它会读取你选择的 SQLite 文件,在本地复制它以避免锁定冲突,并完全在设备上渲染数据。CSV 导出文件要么写入你选择的目录,要么通过浏览器下载。
## 权限
该应用程序需要标准的 Tauri `core` 和 `dialog` 权限,以便 React 前端可以调用 Rust 命令并显示文件选择器。CSV 文件由 Rust 使用 `std::fs` 写入,该操作完全在设备上运行,并且仅当用户通过目录对话框明确选择目标位置时才会触发。该应用程序不请求任何 `fs:` 插件权限。
## 许可证
MIT — 详见 [LICENSE](./LICENSE)。
## 作者
Elijah Stroot
标签:SQLite, 代码示例, 可视化界面, 数据分析, 本地工具, 浏览器历史, 自动化攻击