ThePicpixel/Triage
GitHub: ThePicpixel/Triage
基于 LangGraph 构建的智能体恶意软件研判 pipeline,自动完成静态与动态分析并通过本地 LLM 综合生成结构化判定结论。
Stars: 0 | Forks: 0
# triage
一个基于 [LangGraph](https://github.com/langchain-ai/langgraph) 构建的智能体恶意软件研判 pipeline。它接收任意二进制文件,运行静态分析(熵、字符串、导入表、节),有条件地在沙箱中进行动态/行为分析(CAPEv2、capa、YARA、pcap、内存取证),并通过本地 LLM(Ollama)综合生成结构化判定结论 —— `benign` / `suspicious` / `malicious` / `inconclusive` —— 同时附带证据和 MITRE ATT&CK 映射。
## 工作原理
```
┌─────────┐
sample ───► │ intake │ sha256, file type
└────┬────┘
│
┌──────────▼──────────┐
│ static_analysis │ parallel fan-out (Send API):
│ (subgraph) │ entropy · strings · imports · sections
└──────────┬──────────┘
│
┌──────▼──────┐
│ triage_gate │ conditional edge: weighted finding score ≥ 0.5?
└──┬───────┬──┘
suspicious │ │ benign-enough
│ │
┌──────────▼─────┐ │
│ dynamic_runner │ │ subgraph: detonate (CAPE) → capa · yara · pcap · memory
└──────────┬─────┘ │
│ │
┌▼───────▼┐
│ report │ LLM (Ollama) synthesizes a structured Verdict
└─────────┘
```
状态(`triage/state.py`)是一个贯穿每个节点的单一 `TypedDict`。并行 worker 通过 `add` reducer 向共享的 `findings` 列表中追加内容,并且每个子图(`static`、`dynamic`、`memory`)将其 dict 浅合并到状态中,而不会覆盖同级数据。每当静态风险评分较低时,将跳过动态分析;如果引爆失败或超时,会降级为一条发现结果,而不是导致 graph 崩溃 —— 报告节点始终会运行。
## 环境要求
- Python **3.11+**
- 本地运行 [Ollama](https://ollama.com/),并拉取了模型(默认为 `llama3.1:8b`,但也已在 `gemma4:e4b` 上测试过)—— 用于最终判定结论的合成
- 可选的外部工具,如果缺失,每个都会优雅降级为低严重程度的“不可用”发现:
- `PATH` 路径下的 [`capa`](https://github.com/mandiant/capa) —— ATT&CK 技术提取
- `PATH` 路径下的 [`yr`](https://github.com/VirusTotal/yara-x) (YARA-X) —— 规则匹配
- [Volatility 3](https://github.com/volatilityfoundation/volatility3) (`python3 -m volatility3.cli`) —— 内存取证
- `libmagic`(`python-magic` 所需;在 Debian/Ubuntu 上使用:`apt install libmagic1`,在 macOS 上使用:`brew install libmagic`)
- 一个可访问的 [CAPEv2](https://github.com/kevoreilly/CAPEv2) REST API,用于实际引爆(请参阅下方的[沙箱 / 实验环境设置](#sandbox--lab-setup)) —— 没有它,`detonate` 会优雅超时,并且 pipeline 依然会仅根据静态分析结果生成报告
## 安装说明
```
python3 -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
# 拉取用于报告综合的 model
ollama pull llama3.1:8b
```
## 配置说明
所有配置均通过环境变量进行;每一个都有合理的默认值。
| 变量 | 默认值 | 用途 |
|---|---|---|
| `OLLAMA_MODEL` | `llama3.1:8b` | 报告节点用于合成判定结论的模型 |
| `REPORTS_DIR` | `reports` | 写入 `.md` 和 `.json` 报告的目录 |
| `CAPE_URL` | `http://localhost:8000` | CAPEv2 REST API 的基础 URL |
| `CAPE_MAX_POLLS` | `60` | 等待引爆完成时的最大轮询尝试次数 |
| `CAPE_POLL_INTERVAL` | `5` | 轮询之间的秒数 |
## 用法
### 单一样本
```
from triage.graph import graph
result = graph.invoke({"file_path": "/path/to/sample", "findings": []})
print(result["verdict"]) # structured Verdict dict
print(result["report_path"]) # reports/-.md
print(result["json_path"]) # reports/-.json
```
每次运行都会将一份 Markdown 报告(判定结论、置信度、关键证据、ATT&CK 表、IOC)和包含完整状态(发现、静态/动态/内存数据、判定结论)的 JSON 转储写入 `REPORTS_DIR`。
### 批量 / 语料库模式
对目录中的每个文件运行 graph 并获取 CSV 汇总:
```
python -m triage.batch samples/ # defaults to reports/ for output
python -m triage.batch samples/ my_output_dir
```
这将写入 `my_output_dir/batch-.csv`,每个样本占一行:`filename, sha256, classification, confidence, report_path, error`。在处理过程中抛出异常的样本会被记录为 `classification=error`,而不是中止整个批处理。
### 冒烟测试
EICAR 测试字符串(`samples/eicar.com`)是一种安全、标准的方式,用于端到端测试整个 pipeline,而无需处理真实的恶意软件:
```
from triage.graph import graph
result = graph.invoke({"file_path": "samples/eicar.com", "findings": []})
```
## 沙箱 / 实验环境设置
动态分析需要在 `CAPE_URL` 处可访问的正在运行的 CAPEv2 实例。
**安全规则,不可妥协:**
- 切勿在隔离环境(没有真实网络路由的容器/虚拟机)之外运行未知的二进制文件。
- 在每个样本之间获取/恢复干净的引爆环境 —— 切勿重复使用“脏”的 worker。
- 仅分析您获得授权可以处理的样本。
## 测试说明
```
pytest
```
需要 `samples/eicar.com` 或 `samples/minimal.exe` 的测试会在这些文件不存在时自动跳过。外部工具(CAPE、capa、yara-x、Volatility)在测试中均被模拟 —— 运行测试套件不需要真实的沙箱。
## 项目布局
```
triage/
├── state.py # TriageState TypedDict, Finding & Verdict pydantic models
├── graph.py # top-level graph: static → triage_gate → (dynamic |) → report
├── batch.py # corpus mode: directory in, CSV of verdicts out
├── nodes/
│ ├── intake.py # hash + file-type identification
│ ├── static.py # static subgraph, parallel fan-out over static tools
│ ├── triage_gate.py # conditional edge: weighted finding score
│ ├── dynamic.py # dynamic subgraph: detonate then fan out to capa/yara/pcap/memory
│ └── report.py # LLM verdict synthesis + Markdown/JSON report rendering
└── tools/
├── static_tools.py # file_identity, compute_entropy, extract_strings, parse_imports, parse_sections
└── dynamic_tools.py # detonate (CAPE), run_capa, scan_yara, analyze_pcap, analyze_memory
samples/ # EICAR + minimal PE test fixtures
reports/ # generated .md / .json triage reports (gitignored)
tests/ # pytest suite, mocks external tools/sandbox
```
## 发现与判定结论 schema
每个分析器都会输出归一化的发现结果,这些结果将输入到研判关卡和最终报告中:
```
{"source": "entropy", "signal": "high_entropy_section",
"severity": "high", "evidence": "section '.text' entropy=7.42", "weight": 0.5}
```
报告节点要求 LLM 严格基于这些发现给出答案,并选择 `inconclusive` 而不是进行猜测:
```
class Verdict(BaseModel):
classification: Literal["benign", "suspicious", "malicious", "inconclusive"]
confidence: float # 0–1
summary: str
key_evidence: list[str]
mitre_attack: list[str] # ATT&CK technique IDs, from capa
iocs: list[str] # IPs, domains, hashes
```
标签:AI风险缓解, DAST, LangGraph, 云安全监控, 恶意软件分析, 沙箱检测, 逆向工具, 静态分析