wmarcelod/pdf_steg

GitHub: wmarcelod/pdf_steg

PDF文本层隐写工具包,利用PDF视觉渲染与软件文本提取之间的不一致性来隐藏和提取秘密消息。

Stars: 0 | Forks: 0

# pdf_steg [Português](README.pt-BR.md) · English ![pdf_steg](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/78c0cc1216212939.png) ![demo](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/e3905b3827212941.gif) 基于 [PyMuPDF](https://pymupdf.readthedocs.io/) 构建。 ## 功能说明 一个 PDF 有两层可能会产生不一致:人类所看到的内容(渲染的视觉字形) 和软件提取的内容(文本层/字符对象)。本工具 专门用于操作这种不一致。 两种技术,对应两个子命令: | 子命令 | 可见页面 | 文本层内容 | | ---------- | ---------------------------------- | ----------------------------------------- | | `hide` | 与原件相同(已光栅化) | 仅包含秘密消息中选定的字母,位于其原始位置 | | `embed` | 与原件相同 | 原始文本 + 以不可见方式渲染的 `[STG::STG]` 负载 | 这两种方式生成的 PDF 在人类读者看来都与原件完全相同。 区别在于 `pdftotext` / `page.get_text()` / Ctrl+A 返回的内容。 ## 存在意义 此功能具有双用途。文档记录的合法用途包括: - **对您拥有所有权的系统进行 AI 智能体安全测试** —— 验证文档 摄取管道(RAG、摘要、OCR)是否会受到人类审阅者不可见内容的 影响。这与应用于 PDF 输入的提示注入(prompt-injection)研究理念相同。 - **水印** —— 嵌入一个能在复制粘贴后依然存在,且不会污染可见版面的属性/追踪字符串。 - **隐写术研究与 CTF 竞赛** —— 清晰展示文本层与图像层的差异。 - **教育** —— 向学生展示 PDF 文本提取与视觉渲染之间的区别。 超出范围的行为:未经授权针对第三方系统,或在对抗性部署中逃避 检测。请在您拥有或已获授权测试的基础设施上进行测试。 ## 安装 ``` pip install pymupdf ``` ## 用法 ### `analyze` —— 字母清单 ``` python pdf_steg.py analyze input.pdf ``` 打印 PDF 中每种字符的数量。使用此命令可了解您的 秘密消息是否可以通过 `hide` 技术进行编码(消息中的每个字母 必须至少在文档中出现一次)。 ### `hide` —— 选择性光栅化 ``` python pdf_steg.py hide input.pdf -m "secret message" -o out.pdf [--mode MODE] [--seed N] [--dpi N] [--strict] ``` 将每一页渲染为图像,然后重新插入一个仅包含 `--message` 字符的 *不可见文本层*(`render_mode=3`),每个字符都放置在 源 PDF 中的原始位置。此操作后,复制粘贴整个 PDF 仅会返回 秘密消息;其余所有内容均为图像,因此无法被选中。 默认情况下,该工具会尝试嵌入消息中的**所有**字符 —— 字母、 数字、标点符号和空白。如果某个非字母数字字符(例如 `@`、 空格)无法被放置(在 PDF 中没有出现过,或没有可行的排列顺序), 它将自动从嵌入消息中被丢弃,并在标准错误输出(stderr)中报告遗漏情况。 字母数字是必不可少的 —— 丢失其中一个就会破坏消息 —— 因此缺少字母是一个硬性错误。 传递 `--strict` 可使**任何**缺失字符都成为硬性错误。 `--mode` 控制如何从可用的事件中选取位置: | 模式 | 行为 | | -------- | ------------------------------------------------------------------ | | `greedy` | 游标后的首个匹配 —— 字符聚集在起始位置附近 | | `spread` | **默认。** 分层随机 —— 每个字符通过抖动(jitter)定位到文档中属于自己的槽位,当理想槽位为空时向前落入下一个槽位 | | `even` | 确定性槽位中心 —— 均匀分布,无随机性 | 如果消息是可行的(每个必不可少的字符至少有一个有序的出现), 则 `spread` 和 `even` 保证能将其插入;它们绝不会在后期失败。 `--seed N` 使 `spread` 具有可重复性。`--dpi` 控制光栅化分辨率(默认 220)。 ### `reveal` —— 读取 `hide` 生成的 PDF ``` python pdf_steg.py reveal out.pdf ``` 转储 PDF 文本层两次:按提取原样输出(包含因字符处于不同视觉行而产生的换行符) 以及去除了所有空白的“紧凑”变体,后者即为消息。 ### `embed` —— 不可见文本负载 ``` # default: 保持可见文本层完整 python pdf_steg.py embed input.pdf -m "secret message" -o out.pdf # 同时光栅化可见文本,使 payload 成为唯一可提取的文本 python pdf_steg.py embed input.pdf -m "secret message" -o out.pdf --rasterize [--dpi N] ``` 将消息编码为 `[STG::STG]`,并将其作为 1 磅(pt)的不可见文本块添加到 第一页。该页面的渲染效果与原件相同。 | 标志 | 可见页面 | `get_text()` 返回内容 | | ---------------- | -------------------- | --------------------------------------------------- | | (无,默认) | 与源文件相同 | 源文本 + `[STG:...:STG]` | | `--rasterize` | 与源文件相同 | 仅 `[STG:...:STG]`(可见文本变为图像) | base64 编码层意味着即使嵌入的字体缺少某些字形, 消息也可以包含任何 UTF-8 字符(重音符号、表情符号等)。 ### `extract` —— 读取 `embed` 生成的 PDF ``` python pdf_steg.py extract out.pdf ``` 在页面文本中搜索 `[STG:...:STG]` 标记,对负载进行 base64 解码, 并打印结果。 ## 限制 - **源 PDF 必须具有可提取的文本层。** 未经 OCR 处理的扫描版 PDF 没有可供操作的文本 —— 请先对其进行 OCR 识别。 - **`hide` 要求消息中的每个基本(字母数字)字符都存在于 源文件中。** 重音符号会被标准化(例如将 `á` 匹配为 `a`)。 非字母数字字符(空格、标点符号、符号)则尽最大努力处理: 工具会尝试嵌入它们,但如果 PDF 中没有可用的事件,则会在丢弃它们时输出一条 stderr 警告。 添加 `--strict` 可在此情况下直接报错退出。 - **`embed`(默认模式)在文本层中保留可见文本。** 任何人 在渲染的 PDF 上执行 Ctrl+A 时,都会在剪贴板中的某处看到 `[STG:...:STG]`。请使用 `--rasterize` 以获得更强的隐蔽性。 - **无加密。** 负载是 base64 编码,而非加密的。如果您需要在 隐匿性之上保证机密性,请在传入消息之前自行对其进行加密。 ## 文件 - [`pdf_steg.py`](pdf_steg.py) —— CLI 命令行工具 - [`make_sample.py`](make_sample.py) —— 生成小型测试 PDF - `sample.pdf` / `big.pdf` —— 示例输入(运行 `make_sample.py` 后获得) ## 许可证 [MIT](LICENSE) © 2026 Marcelo Duchene
标签:AI安全测试, CTF工具, DNS 反向解析, PDF处理, PDF隐写, PyMuPDF, Python, RAG管道测试, 不可见文本, 信息隐藏, 内容篡改, 字符对象操作, 对抗性机器学习, 提示注入研究, 搜索语句(dork), 数字水印, 数据提取, 数据隐写, 文本层隐写术, 文本提取, 无后门, 漏洞搜索, 逆向工具, 隐蔽通信