Query-farm/vgi-evtx

GitHub: Query-farm/vgi-evtx

一个将 Windows .evtx 事件日志文件解析为 DuckDB 可查询结构化行的 Rust worker 插件,专为数字取证与事件响应(DFIR)场景设计。

Stars: 0 | Forks: 0

Vector Gateway Interface (VGI)

一个用于 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, 可视化界面, 子域名变形, 数据解析, 日志解析, 网络流量审计, 证书伪造, 通知系统