Query-farm/vgi-evtx
GitHub: Query-farm/vgi-evtx
一个将 Windows .evtx 事件日志文件解析为 DuckDB 可查询结构化行的 Rust worker 插件,专为数字取证与事件响应(DFIR)场景设计。
Stars: 0 | Forks: 0
一个用于 DuckDB 的 Query.Farm VGI worker。
# vgi-evtx
一个 [VGI](https://query.farm) worker(Rust,已编译的二进制文件),用于将 **Windows Event Log (`.evtx`) 文件**解析为基于 Apache Arrow 的 DuckDB / SQL 行。DuckDB 会启动该 worker 并通过 Arrow IPC 与其通信;这些函数位于 `evtx` catalog 的 `main` schema 下。
这是一个**防御性 DFIR 工具**:它将 `.evtx` 文件(通常来自*受损*主机,必须将其视为恶意输入)转化为可查询的结构化行,并保留完整的规范化事件 JSON,以便输出结果能与 [`vgi-sigma`](../vgi-sigma) 的 `sigma_match(event_json, rule)` 组合使用。它是纯粹的解析操作——不进行任何网络访问。
```
LOAD vgi;
ATTACH 'evtx' (TYPE vgi, LOCATION './target/release/evtx-worker');
SET search_path = 'evtx.main';
-- One row per event record. Input is the .evtx file as a BLOB …
SELECT record_id, event_id, provider, channel, time_created
FROM evtx_records((SELECT content FROM read_blob('Security.evtx')))
ORDER BY record_id;
-- … or as a VARCHAR path to open directly.
SELECT count(*) FROM evtx_records('Security.evtx');
-- How many records? Is it a real .evtx?
SELECT evtx_record_count((SELECT content FROM read_blob('Security.evtx'))); -- BIGINT
SELECT is_valid_evtx((SELECT content FROM read_blob('Security.evtx'))); -- BOOLEAN
-- Compose with vgi-sigma over the preserved full event JSON:
-- SELECT * FROM evtx_records('Security.evtx')
-- WHERE sigma_match(event_json, '
');
```
## 函数
### 表
| 函数 | 列 | 描述 |
| --- | --- | --- |
| `evtx_records(input)` | `record_id BIGINT, event_id INT, provider VARCHAR, channel VARCHAR, computer VARCHAR, level INT, time_created TIMESTAMP, event_json VARCHAR` | 每个事件记录对应一行。`event_json` 包含完整的规范化事件 JSON。 |
`input` 是一个**常量**(DuckDB 表函数接受常量参数):可以是一个作为 **BLOB**(内联字节)的 `.evtx` 文件,或者是一个指向 `.evtx` 文件的 **VARCHAR** 路径。为了方便使用而提取的列来自于 `Event.System` 块;其他任何内容(例如 `EventData`)均可在 `event_json` 中获取。
### 标量
| 函数 | 返回值 | 描述 |
| --- | --- | --- |
| `evtx_record_count(input)` | `BIGINT` | 事件记录的数量(对于格式错误/垃圾输入返回 `0`)。 |
| `is_valid_evtx(input)` | `BOOLEAN` | 如果输入包含 `ElfFile` 魔数且解析器构建成功,则返回 True。 |
| `evtx_version()` | `VARCHAR` | Worker 版本字符串。 |
对于标量函数,`input` 取自某一行的 BLOB 或 VARCHAR 路径列。
## 恶意输入处理
交给 DFIR 工具的 `.evtx` 文件通常来自受损主机,可能被截断、损坏,或者是被蓄意篡改以使解析器崩溃。每个入口点都经过了强化处理:
- **绝不 panic / 崩溃。** 解析器的构建和逐条记录的迭代都在 `catch_unwind` 下运行;捕获到的 panic 会被降级为“无行返回 / 无效输入”。
- **有界工作量。** 在解析开始前,输入大小被限制在 256 MiB,每个文件的输出记录数上限为 5,000,000。
- **魔数预检。** 没有包含 `ElfFile\0` 头的文件会直接被拒绝。
- **跳过损坏记录。** 单个无法解析的记录绝不会导致文件其余部分的解析中止;不可读的 VARCHAR 路径将不产生任何可用输入。
- **NULL 语义。** `NULL` 输入 → `NULL`(标量函数)/ 无行返回(`evtx_records`)。在同一批次中,与有效 BLOB 并列的垃圾 BLOB 不会影响有效结果。
## NULL 处理
标量函数遇到 `NULL` 输入会返回 `NULL`,而 `evtx_records` 则返回零行。格式错误 / 截断 / 垃圾输入会产生 `0` / `false` / 无行返回的结果。
## 构建与测试
```
cargo build --release # produces target/release/evtx-worker
cargo test --workspace # pure-Rust + Arrow-boundary unit/integration tests
make test-sql # DuckDB sqllogictest E2E (needs haybarn-unittest)
make lint # clippy -D warnings + rustfmt --check
```
SQL E2E 测试套件使用了 [`haybarn-unittest`](https://pypi.org/project/haybarn-unittest/)
(`uv tool install haybarn-unittest`)。
## `.evtx` 解析器
解析工作被委托给纯 Rust 编写的 [`evtx`](https://crates.io/crates/evtx) crate(omerbenamraw/evtx),该库采用 **MIT / Apache-2.0** 双重许可。我们刻意固定使用 `evtx =
"0.8.5"` 版本:因为 evtx ≥ 0.10 属于 edition 2024(使用了 let-chains)并要求 rustc ≥ 1.88,这将使工作区的 MSRV 提升到高于我们固定的 1.86 版本。0.8.5 属于 edition 2021,可以在 1.86 上干净地编译。我们使用 `default-features =
false` 来构建它,以去除其可选的日志功能(该功能会间接引入一个同样需要 rustc 1.88 的 `time` 库)。
## 测试固件
`test/sql/data/sample-security.evtx` 是一个 68 KB 的小型、干净的 Windows Security 事件日志文件,来自 `evtx` crate 自身的测试语料库。来源及归属信息请参见
`test/sql/data/README.md`。
## 许可证
MIT © Query Farm LLC。内置的 `.evtx` 解析器(`evtx` crate)采用 MIT/Apache-2.0 许可证。
## 作者与许可
由 [Query.Farm](https://query.farm) 编写。
版权所有 2026 Query Farm LLC - https://query.farm标签:DuckDB, Rust, 可视化界面, 子域名变形, 数据解析, 日志解析, 网络流量审计, 证书伪造, 通知系统