Query-farm/vgi-tlsfp
GitHub: Query-farm/vgi-tlsfp
一个 DuckDB SQL 扩展,直接在查询中从 TLS 握手数据计算 JA3/JA3S/JA4 指纹,用于聚类和追踪 C2 与僵尸网络基础设施。
Stars: 0 | Forks: 0
# vgi-tlsfp — 在 DuckDB SQL 中进行 TLS 客户端/服务器指纹识别
**直接在 SQL 中计算 JA3、JA3S 以及基础 JA4(TLS 客户端)指纹**
基于原始的 `ClientHello`/`ServerHello` 字节 —— 或者基于你已经使用 Zeek/pcap
工具提取出的字段。将指纹计算应用于捕获的握手数据列,并对其执行
`GROUP BY` 以发现 C2 或僵尸网络家族,然后将生成的 VARCHAR 哈希值
JOIN 到证书([`vgi-x509`](https://github.com/Query-farm))和流量
([`vgi-netflow`](https://github.com/Query-farm))数据上,从而实现从指纹
到其背后证书和网络流量的关联追踪。
`tlsfp` 是一个 [VGI](https://query.farm) worker:一个独立的二进制文件,由 DuckDB 启动
并通过 Apache Arrow 进行通信。每个函数都是**纯的、确定性的计算 —
没有网络请求,没有状态。** 格式错误或截断的字节会按行返回 `NULL`,而
不会导致查询失败。
## 快速开始
`client_hello`/`server_hello` 输入是原始的 TLS 握手记录(例如
来自 Zeek 的 `ssl.log` 原始字段或 pcap 读取器)—— 可以是完整的 TLS 记录
(以 `0x16…` 开头)或是裸握手消息(`0x01…` / `0x02…`)。这是一个
*指纹识别*原语,而不是一个数据包捕获工具。
## 函数参考 (`tlsfp.main`)
| 函数 | 返回值 | 描述 |
| --- | --- | --- |
| `ja3(client_hello BLOB)` | `VARCHAR` | ClientHello 的 JA3 指纹(MD5,32 位十六进制)。无法解析时返回 `NULL`。 |
| `ja3_string(client_hello BLOB)` | `VARCHAR` | 哈希前的 JA3 字符串(用于审计 / 自定义重新哈希)。 |
| `ja3_from_parts(version INT, ciphers INT[], extensions INT[], curves INT[], point_formats INT[])` | `VARCHAR` | 基于已提取字段的 JA3。 |
| `ja3s(server_hello BLOB)` | `VARCHAR` | ServerHello 的 JA3S 指纹(MD5)。 |
| `ja3s_from_parts(version INT, cipher INT, extensions INT[])` | `VARCHAR` | 基于已提取字段的 JA3S。 |
| `ja4(client_hello BLOB)` | `VARCHAR` | JA4 基础 TLS 客户端指纹,例如 `t13d1516h2_8daaf6152771_e5627efa2ab1`。 |
| `ja4_raw(client_hello BLOB)` | `VARCHAR` | 未哈希的 JA4_r 格式。 |
| `ja4_from_parts(version INT, ciphers INT[], extensions INT[], sig_algs INT[], alpn VARCHAR)` | `VARCHAR` | 基于已提取字段的 JA4(`version` 是有效的 TLS 版本代码,例如 `772`)。 |
| `parse_client_hello(client_hello BLOB)` | `STRUCT(version INT, sni VARCHAR, ciphers INT[], extensions INT[], curves INT[], alpn VARCHAR[])` | ClientHello 的精确解码(保留 GREASE)。 |
| `is_tls_handshake(bytes BLOB)` | `BOOLEAN` | 彻底的防护:输入看起来像 TLS 握手吗?`NULL` 输入 → `NULL`。 |
| `tlsfp_version()` | `VARCHAR` | 正在运行的 worker 版本字符串。 |
### 约定
- **`*_string` / `*_raw` 配套函数**公开了哈希前的 JA3 字符串和
未哈希的 JA4_r 格式,用于审计/调试,以便你可以根据自己的策略重新进行哈希。
- **GREASE** 值(RFC 8701)已从每个指纹中剔除;两种输入
模式(字节 vs 字段)共享同一个实现且结果始终一致。
- **逐行安全性:** 格式错误/截断的字节将返回 `NULL`,绝不会因为报错而导致
扫描失败。如果你愿意,可以先使用 `is_tls_handshake(bytes)` 进行过滤。
- 输出是普通的 VARCHAR 哈希值,可以 JOIN 到 `vgi-x509`(服务器证书
指纹)和 `vgi-netflow`(5 元组),用于威胁狩猎的追踪。
## 示例
```
-- JA4 of a captured handshake
SELECT ja4(client_hello) FROM captured_handshakes;
-- JA3S of the server side
SELECT ja3s(server_hello) FROM captured_handshakes;
-- guard then fingerprint (skip non-handshake rows)
SELECT ja3(client_hello) FROM captured_handshakes
WHERE is_tls_handshake(client_hello);
-- decode a ClientHello's fields
SELECT (parse_client_hello(client_hello)).sni,
(parse_client_hello(client_hello)).ciphers
FROM captured_handshakes;
-- the two input modes agree
SELECT ja3(client_hello)
= ja3_from_parts(771, ciphers, extensions, curves, point_formats)
FROM zeek_ssl;
```
## 构建与测试
```
cargo build --release # produces target/release/tlsfp-worker
cargo test # unit + integration + proptest (no-panic fuzz)
cargo clippy --all-targets -- -D warnings
cargo fmt --check
```
### 端到端 SQL (haybarn)
`test/sql/*.test` sqllogictest 测试套件针对真实的 DuckDB `vgi`
扩展运行,覆盖**所有传输方式**(subprocess / http / unix)。请参见
[`ci/README.md`](ci/README.md):
```
cargo build --release
HAYBARN_UNITTEST=/path/to/haybarn-unittest \
WORKER_BIN="$PWD/target/release/tlsfp-worker" \
TRANSPORT=subprocess ci/run-integration.sh
```
### 元数据质量 (vgi-lint)
```
uvx --from vgi-lint-check vgi-lint lint "$PWD/target/release/tlsfp-worker" --fail-on info
```
## 架构
包含两个 crate:
- **`tlsfp-core`** —— 纯净、可模糊测试的引擎:一个最小化的边界检查
`ClientHello`/`ServerHello` 解析器以及 JA3/JA3S/JA4 字符串 + 哈希构建器。
没有 Arrow,没有 VGI,没有 I/O 操作。禁止使用 `unsafe`。**不包含 JA4+ / JARM 代码。**
- **`tlsfp-worker`** —— 轻量级的 Arrow 适配器,用于注册标量函数并通过
VGI 提供服务。
## 许可证
MIT —— 请参见 [LICENSE](LICENSE)。版权所有 2026 Query Farm LLC。是
[Query.Farm](https://query.farm) DuckDB worker 的 VGI 生态系统的一部分。
标签:可视化界面, 通知系统