danindiana/pdf-malware-scanner
GitHub: danindiana/pdf-malware-scanner
面向大规模 PDF 档案库的分层恶意软件检测流水线,通过结构分析、YARA 签名和 ClamAV 逐级筛选并量化风险评分。
Stars: 0 | Forks: 0
# pdf-malware-scanner
**主机:** worlock (Ubuntu/Debian, Linux 6.8.12, 32 核心, 125 GB 内存)
用于大规模档案库的分层 PDF 恶意软件检测流水线。专为数以百万计的 PDF 文件集合而设计。可扫描恶意结构、YARA 签名匹配以及 ClamAV 已知威胁,并将所有结果记录到可查询的 SQLite 数据库中。
## 架构
四层按顺序运行,每一层仅将阳性结果传递给下一层:
```
All PDFs (5.1M+)
│
▼
Layer 1: PyMuPDF structural analysis ← all files, fast (~500–1000/s @ 8 workers)
│ Flags: JS, Launch, OpenAction,
│ EmbeddedFile (+ executable type check), XFA, Encrypted, Corrupt
│
▼ (has_js = 1)
Layer 1.5: pikepdf JS stream inspection ← JS-bearing files only
│ Decompresses streams, regex-matches obfuscation patterns:
│ eval/unescape, fromCharCode, %u-encoding, hex arrays, etc.
│
▼ (risk_score > 0)
Layer 2: YARA signature matching ← flagged subset only
│ Rules: Neo23x0/signature-base (PDF malware + CVE exploits)
│
▼ (risk_score >= 5)
Layer 3: ClamAV (clamd daemon) ← high-risk subset only
Result logged as clean/infected:/error
```
### 风险评分
| 指标 | 分数 |
|-----------|--------|
| JavaScript (`/JS`, `/JavaScript`) | +3 |
| JS 混淆 (eval/unescape, fromCharCode 等) | +3 |
| 启动动作 (`/Launch`) | +3 |
| 可执行嵌入文件 (PE/ELF/OLE/脚本) | +4 |
| 嵌入文件 (任何附件) | +2 |
| OpenAction / 文档级 AA | +1 |
| XFA 表单 | +1 |
| 已加密 (受密码保护) | +1 |
| 损坏 / 解析失败 | +1 |
| YARA 命中 | +5 (上限为 10) |
| ClamAV 感染 | 将分数设为 10 |
分数 0 = 干净。分数 ≥ 5 = 高风险 (运行 ClamAV)。分数 8–10 = 严重。
## 环境要求
- Python 3.10+
- `PyMuPDF` (`fitz`) — 结构分析
- `pikepdf` — JS 流解压缩,用于混淆检测
- `yara-python` — YARA 匹配
- `tqdm` — 进度显示 (可选)
- `yara` (apt 软件包) — 安装脚本使用的 CLI 工具
- `clamav` / `clamav-daemon` — 第三层
## 安装
```
bash install_deps.sh
```
这将执行以下操作:
1. `apt install yara clamav clamav-daemon`
2. `pip install yara-python pikepdf tqdm`
3. 将 [Neo23x0/signature-base](https://github.com/Neo23x0/signature-base) 克隆到 `~/programs/yara-rules/`
4. 编译与 PDF 相关的 YARA 规则 → `~/.local/share/pdf_scan/rules.yar`
5. 构建 `pdf-tui` (需要 Rust/cargo) 并将封装脚本安装到 `~/.local/bin/pdf-tui`
只有 TUI 需要 Rust。如果需要,请使用 `curl https://sh.rustup.rs -sSf | sh` 进行安装。
## 使用方法
### TUI (交互式)
```
pdf-tui # launch from anywhere after install_deps.sh
pdf-tui --db /mnt/raid0/scan.db # custom DB location
```
**向导操作流程:**
| 按键 | 动作 |
|-----|--------|
| `s` | 打开扫描向导 |
| 输入路径 + `Enter` | 添加目录 (例如 `/mnt/raid0/monolithic_pdf_folderv2`) |
| `Tab` | 下一步 (选项 → 确认) |
| 在“确认”上按 `Enter` | 开始扫描 |
| `r` | 查看结果 |
| `q` / `Esc` | 返回 / 退出 |
| `?` | 帮助叠加层 |
扫描会立即显示 **阶段 0 (发现中)**,然后显示带有实时进度条和吞吐量迷你图的 **阶段 1 (结构分析)**。
### 扫描 (CLI)
```
# 默认目录 (/mnt/raid0 + /mnt/sdf1) 的完整扫描
python3 pdf_scan.py
# 恢复中断的扫描 (跳过大小一致且已在 DB 中的文件)
python3 pdf_scan.py --resume
# Fast mode = 仅 Layer 1+1.5 (无 YARA/ClamAV);适用于初步调查
python3 pdf_scan.py --workers 8 --mode fast
# 使用自定义 DB 路径扫描特定目录
python3 pdf_scan.py --dirs /path/to/pdfs --db /tmp/results.db
# 低优先级长时间运行
nice -n 10 python3 pdf_scan.py --resume
```
CLI 参考:
```
--dirs DIR [DIR ...] Directories to scan (default: /mnt/raid0 /mnt/sdf1)
--workers N Parallel worker processes (default: 8 = min(cpu_count, 8))
--mode fast|full fast=Layers 1+1.5 only; full=all layers (default: full)
--db PATH SQLite output path (default: ~/.local/share/pdf_scan/results.db)
--rules PATH Compiled YARA rules path
--resume Skip files already in DB with matching size
--min-risk N Only store results with risk_score >= N (default: 0)
```
### 查询结果
```
# Risk-bucket 细分 + 检测摘要
python3 query_results.py summary
# 风险最高的 Top 100 文件
python3 query_results.py top --n 100
# 导出所有高风险文件的 CSV
python3 query_results.py export --min-risk 5 --out flagged.csv
# 单个文件的完整记录
python3 query_results.py inspect /path/to/file.pdf
# 汇总统计 (总扫描数、覆盖率、时间)
python3 query_results.py stats
```
### 隔离
将确认有威胁的文件从档案库中移出到一个暂存目录:
```
# 先 Dry run — 查看将要执行的操作
python3 query_results.py quarantine --dir /mnt/quarantine --min-risk 8 --dry-run
# 执行移动 (在 --dir 下保留目录结构)
python3 query_results.py quarantine --dir /mnt/quarantine --min-risk 8
```
隔离命令会使用目标路径和时间戳更新数据库中的 `notes`,以便您保留每个文件去向的记录。
## SQLite 架构
结果数据库位于 `~/.local/share/pdf_scan/results.db`:
```
CREATE TABLE scans (
id INTEGER PRIMARY KEY,
path TEXT UNIQUE,
sha256 TEXT, -- SHA-256 hex digest
size_bytes INTEGER,
scan_time TEXT, -- ISO8601 UTC
risk_score INTEGER, -- 0–10
has_js INTEGER, -- /JS or /JavaScript present
has_launch INTEGER, -- /Launch action present
has_openaction INTEGER, -- catalog-level OpenAction or AA
has_embeddedfile INTEGER, -- real file attachment (fitz API)
has_xfa INTEGER, -- XFA form
is_encrypted INTEGER, -- password-protected
is_corrupt INTEGER, -- failed to open/parse
yara_hits TEXT, -- JSON array of matched rule names
clamav_result TEXT, -- 'clean', 'infected:', 'error:...'
notes TEXT,
js_obfuscated INTEGER, -- 1 if obfuscation patterns found in JS streams
js_pattern TEXT, -- JSON list of matched pattern names
has_executable_embed INTEGER, -- 1 if embedded file is PE/ELF/OLE/script
embed_types TEXT -- JSON list of detected embed types
);
```
已在 `risk_score DESC`、`path` 和 `sha256` 上建立索引。
现有数据库会**自动迁移** — 在首次运行更新版扫描器时,将使用 `DEFAULT 0` 添加新列。
## 解读结果
- **分数 0** — 未检测到可疑特征
- **分数 1–2** — 轻微标记 (仅加密、损坏或 XFA);关注度低
- **分数 3–4** — 存在 JS 或嵌入的附件;检查 `yara_hits`
- **分数 5–7** — 高风险;已运行 YARA 和 ClamAV — 检查 `yara_hits` 和 `clamav_result`
- **分数 8–10** — 严重;已确认 YARA 或 ClamAV 命中,或存在可执行嵌入文件
**确认威胁的过滤器:**
```
SELECT path, risk_score, yara_hits, clamav_result, js_pattern, embed_types
FROM scans
WHERE yara_hits != '[]'
OR clamav_result LIKE 'infected:%'
OR has_executable_embed = 1
OR js_obfuscated = 1
ORDER BY risk_score DESC;
```
**误报说明:** 合法的交互式 PDF(IRS 表单、政府可填写表单)由于包含 JS + XFA + EmbeddedFile,得分在 3–6 之间。这些文件会显示 `clamav_result='clean'`、空的 `yara_hits`、`js_obfuscated=0` 和 `has_executable_embed=0`。请使用上面的确认威胁过滤器将其排除。
## 结果示例
**2026-04-30,对约 3,000 个 PDF 的测试运行 (/mnt/raid0/golang_progs):**
```
Risk bucket Count %
-----------------------------------
Clean (0) 3,111 77.8%
Low (1–2) 783 19.6%
Medium (3–4) 69 1.7%
High (5–7) 37 0.9%
Critical (8–10) 0 0.0%
-----------------------------------
ClamAV infected : 0
YARA hits : 0
JS obfuscated : 0
Executable embed : 0
```
分数为 6/7 的集群:IRS 表单和可填写的政府 PDF — 正如预期的那样,所有 ClamAV 扫描结果均为干净。
## 文件
```
pdf_scan.py Main pipeline script
query_results.py SQLite query/export/quarantine helper
install_deps.sh Dependency installer
```
运行时创建的外部路径:
```
~/.local/share/pdf_scan/results.db SQLite results database
~/.local/share/pdf_scan/rules.yar Compiled YARA rules (binary)
~/programs/yara-rules/ Neo23x0 signature-base clone
```
## 稳定性说明
- **ClamAV 崩溃**:libclamav 在处理某些格式错误的 PDF 时存在 bug。扫描器使用 `clamdscan` (守护进程模式),因此签名数据库只加载一次,并且某次崩溃的扫描不会导致守护进程终止。如果守护进程套接字不存在,则会回退到使用 `clamscan` 子进程。
- **MuPDF 崩溃**:精心构造的 PDF 可能导致 fitz 工作线程发生 SIGSEGV。系统会捕获 `BrokenExecutor`,刷新数据库,并且扫描会干净地停止。使用 `--workers 1 --mode fast` 来隔离出有问题的文件。
- **工作线程核心转储**:在启动时于所有工作线程进程中抑制 (`RLIMIT_CORE=0`)。
- **pikepdf 超时**:JS 流检查对每个文件有 15 秒的 `SIGALRM` 超时,以防止在处理恶意复杂 PDF 时出现挂起。
标签:ClamAV, Claude, CVE检测, Debian, DNS 反向解析, Go语言工具, GraphQL安全矩阵, JavaScript解混淆, PDF分析, pikepdf, PyMuPDF, Python, SQLite, Web 安全测试, YARA, 云安全监控, 云资产可视化, 可视化界面, 多进程, 大数据处理, 威胁情报, 安全扫描器, 开发者工具, 数据管道, 文件结构分析, 无后门, 沙箱分析, 网络安全, 评分系统, 软件工程, 逆向工具, 通知系统, 防病毒引擎, 隐私保护, 静态分析, 高并发