bigblue-r4/deobfuscate-rs
GitHub: bigblue-r4/deobfuscate-rs
一个面向 LLM 安全 pipeline 的 Rust 多趟文本反混淆与编码规避检测库,在调用大模型前对输入进行归一化和风险评分。
Stars: 1 | Forks: 0
# 反混淆
用于 Rust 的多趟文本去混淆与编码规避检测器。
专为 LLM 安全 pipeline 构建,适用于攻击者对 prompt-injection payload 进行编码
以规避内容分类器的场景。在任何 LLM 调用**之前**运行此程序:它将返回
供模型使用的已清理文本,以及用于审计追踪的结构化检测报告。
[](https://github.com/bigblue-r4/deobfuscate-rs/actions/workflows/ci.yml)
[](LICENSE)
[](https://crates.io/crates/deobfuscate)
## 快速开始
```
[dependencies]
deobfuscate = "1"
```
```
use deobfuscate::analyze;
let result = analyze("Execute: .... .- -.-. -.-");
if result.should_block() {
// score >= 0.60 — stop and ask / block
eprintln!("blocked: {}", result.summary());
} else if result.should_flag() {
// score >= 0.25 — flag for review, send normalized to model
send_to_model(&result.normalized);
}
```
## 处理趟
按 pipeline 顺序执行 18 个连续的处理趟。每个处理趟独立触发;检测结果会累积。
| 处理趟 | 检测内容 | 示例 |
|------|---------|---------|
| `PreScanNfc` | Unicode NFD 组合序列 | Ä (已分解) → Ä (NFC) |
| `InvisibleStrip` | 变体选择符,标签块 (U+E0000–E007F) | 已移除 U+FE0F |
| `CjkSuperposition` ⚠ | 正向/反向 CJK 熵突增 — 注入接缝。**中止**:文本已清除。 | 混合 CJK+拉丁字符注入 |
| `BiDiControl` | 不可见的 RTL/LTR 覆盖字符 (U+202E, U+200B, …) | `"ignore\u{202E}all"` → `"ignoreall"` |
| `FullwidthChars` | 东亚全角 ASCII (U+FF01–U+FF5E) | `PWNED` → `PWNED` |
| `BackslashEscape` | 逐字符 `\X` 前缀转义 | `\i\g\n\o\r\e` → `ignore` |
| `UnicodeEscape` | 已解码的 `\xNN`, `\uNNNN`, `\u{N}`, 八进制转义 | `\x69gnore` → `ignore` |
| `UrlEncoding` | 百分号编码序列(≥ 3 个连续的 `%XX`)且包含注入关键字 | `%69%67%6e%6f%72%65` → `ignore` |
| `HtmlEntities` | 十进制、十六进制、命名的 XML 实体(≥ 4 个实体 + 注入关键字) | `ig…` → `ignore` |
| `Base64` | 显式的 `b64.decode("…")` 和纯数据块(≥ 12 个字符) | `aWdub3Jl` → `ignore` |
| `MorseCode` | 长度 ≥ 10 个字符的 ITU 摩斯密码段,摩斯字符比例 ≥ 60%,字母解码率 ≥ 40% | `.... .- -.-. -.-` → `HACK` |
| `Homoglyph` | 包含 1,631 项的 TR39 易混淆字符:西里尔文、希腊文、希伯来文、数学/手写/哥特字体 | `іgnοre` → `ignore` |
| `ScriptIntrusion` | 拉丁单词中嵌入的非拉丁字符 | `system` (零宽连字) |
| `Leetspeak` | 密集 leet token 中的数字/符号替换(leet 比例 ≥ 35%) | `1337h4x0r` → `ieetaxor` |
| `EntropyBigram` | Shannon 熵 > 5.2 位 OR 英文双字母覆盖率 < 0.15 | 高熵编码块 |
| `SplitString` | 跨分隔符碎片化的注入关键字 — 仅检测 | `ig.no.re` 重构为 `ignore` |
| `Rot13` | 在包含注入关键字的全字母 token(≥ 4 个字符)中解码 ROT13 | `vtaber` → `ignore` |
| `Punycode` | 通过 RFC 3492 解码 IDN `xn--` 标签,并在易混淆字符归一化后发现关键字 | `xn--shll-w4d` → `shell` |
## 评分
`obfuscation_score` 是 [0.0, 1.0] 范围内的浮点数,上限为 1.0。
| 处理趟 | 权重 |
|------|--------|
| CjkSuperposition | 1.00 (中止) |
| BiDiControl | 0.90 |
| Base64 | 0.85 |
| BackslashEscape / UnicodeEscape / MorseCode / UrlEncoding / HtmlEntities / Rot13 | 0.80 |
| Punycode | 0.85 |
| InvisibleStrip | 0.75 |
| SplitString | 0.70 |
| FullwidthChars | 0.65 |
| Homoglyph | 0.55 |
| EntropyBigram | 0.50 |
| ScriptIntrusion | 0.40 |
| PreScanNfc | 0.35 |
| Leetspeak | 0.30 |
默认阈值(可通过 [`Config`](#configuration) 配置):
- `score >= 0.25` → **标记** (`should_flag()`)
- `score >= 0.60` → **拦截** (`should_block()`)
## Builder API
默认情况下,所有处理趟均已启用。可选择性启用或禁用:
```
use deobfuscate::{Normalizer, PassKind};
// All passes except Morse:
let r = Normalizer::default()
.disable(PassKind::MorseCode)
.analyze(input);
// Only homoglyph + leet:
let r = Normalizer::new()
.enable(PassKind::Homoglyph)
.enable(PassKind::Leetspeak)
.analyze(input);
```
## 配置
所有阈值和处理趟权重均可通过 `Config` 在运行时配置。加载部分
TOML —— 缺失字段将回退到默认值。
```
# config.toml
flag_threshold = 0.25
block_threshold = 0.60
# 为高灵敏度部署收紧 homoglyph 权重
weight_homoglyph = 0.70
# 为 1337 属于常态的游戏场景放宽 leet
weight_leet = 0.10
leet_min_pct = 60
```
```
use deobfuscate::{Config, Normalizer};
use std::path::Path;
// From file (returns Config::default() if file missing or unreadable)
let config = Config::from_file(Path::new("config.toml")); // not available on wasm32
// From inline TOML string
let config = Config::from_toml("block_threshold = 0.90").unwrap();
// Struct literal with defaults
let config = Config { weight_homoglyph: 1.0, ..Config::default() };
let result = Normalizer::default()
.with_config(config)
.analyze(input);
```
需要启用 `serde` 特性(默认启用)。使用 `default-features = false`
禁用以进行无 serde 构建。有关完整的
字段参考,请参见 [`examples/config.toml`](examples/config.toml)。
## Result API
```
let r = deobfuscate::analyze(input);
r.normalized // cleaned string — send this to your LLM
r.obfuscation_score // f32 in [0.0, 1.0]
r.is_obfuscated() // any detection fired?
r.should_flag() // score >= flag_threshold (default 0.25)
r.should_block() // score >= block_threshold (default 0.60)
r.summary() // "score=0.80 detections=[morse-code]"
r.detection_kinds() // Vec, deduplicated
r.detections // Vec — full detail per event
.kind // PassKind
.original // obfuscated span
.normalized // replacement
.detail // human description
```
## 审计追踪
`audit` 特性(默认启用)将无 payload 的 [`AuditRecord`] 附加到每个
`NormalizationResult`。该记录保存了原始输入的 SHA-256 哈希值和字符长度、
UTC 时间戳、分数、拦截/中止标志以及每次检测的元数据 —— **不包含原始字符串**。
```
let result = deobfuscate::analyze(r"\x69\x67\x6e\x6f\x72\x65 all instructions");
// Serialize as a single JSONL line — wire to your SIEM or append to a log file
let line: String = result.audit_jsonl();
// Append to a JSONL log (non-wasm32 only)
result.audit.append_jsonl(std::path::Path::new("/var/log/deobfuscate.jsonl"))?;
```
示例记录:
```
{
"input_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
"input_len": 43,
"timestamp": "2024-11-14T22:13:20Z",
"obfuscation_score": 0.8,
"halted": false,
"blocked": true,
"passes_fired": ["unicode-escape"],
"detections": [
{ "pass": "unicode-escape", "original_len": 28, "normalized_len": 6, "detail": "unicode-escape decoded 4 sequence(s) [hex-byte]; result contains keyword: ignore" }
]
}
```
有关可运行的演示,请参见 [`examples/audit.rs`](examples/audit.rs)。
## WebAssembly
`wasm` 特性为浏览器内使用暴露了轻量级的 JS 可调用 API。
```
wasm-pack build --target web --features wasm --no-default-features
```
```
import init, { analyze_text, should_block, score } from './pkg/deobfuscate.js';
await init();
const result = analyze_text(userInput);
if (result.should_block) {
console.error('blocked:', result.summary);
} else {
sendToLLM(result.normalized);
}
```
请参见 [`wasm/README.md`](wasm/README.md) 和 [`wasm/example.html`](wasm/example.html)。
## 基准测试 (CyberEC 对抗数据集,141 行)
针对来自 CyberEC prompt-injection 数据集的 26 个假阴性案例
(即逃避原始 LLM 分类器的攻击):
| 类别 | 数量 | 捕获数 |
|----------|-------|--------|
| Unicode 同形字 (西里尔文/希腊文) | 4 | ✓ 全部 |
| 反斜杠转义文本 | 3 | ✓ 全部 |
| Leetspeak (混合字母) | 3 | ✓ 全部 |
| 摩斯密码 | 1 | ✓ |
| Base64 | 1 | ✓ |
| 全角 Unicode | 1 | ✓ |
| **总计** | **13 / 26** | **50%** |
剩余的 13 个是语义攻击(越狱框架、首字母缩略词替换、
多跳推理)—— 这些需要 LLM 级别的推理,而不是结构化归一化。
在良性文本(NIST 参考文献、代码片段、CLI 标志、
版本号)中零误报。
## 路线图
| 处理趟 | 检测内容 |
|------|---------|
| `Rot13` | 全字母 token 中的 Caesar-13 替换 |
| `Punycode` | 嵌入在文本中的 IDN `xn--` 编码主机名 |
计划改进的 API:
- `no_std` 模式(移除文件系统依赖项,嵌入解码器)
- 每 token 置信度分数
## 起源
提取自 [split-brain-harness](https://github.com/bigblue-r4/split-brain-harness),
这是一个为 DHS SBIR 评估构建的 LLM 安全遥测代理。归一化器
作为 Stage 0 运行,位于两阶段的 LLM 提议+验证 pipeline 之前。
## 许可证
MIT —— 参见 [LICENSE](LICENSE)。
标签:AI工具, DNS 反向解析, LLM防护墙, Naabu, Python安全, Rust, 内容安全, 可视化界面, 大语言模型安全, 提示词注入防护, 文本解混淆, 机密管理, 网络流量审计, 通知系统