# jdvrif
***jdvrif***(*JPG Data Vehicle*)是一款快速、易用的隐写术命令行工具,可通过 **JPG** 图像隐藏和提取任意文件类型。***仅限 Linux***。
你的数据文件会先用 ***libdeflate/zlib*** 压缩,再用 ***XChaCha20-Poly1305***(***libsodium*** secretstream)加密,密钥由 ***Argon2id*** 从随机生成的 ***恢复 PIN*** 派生,最后嵌入到载体图像中。PIN 仅在 ***conceal*** 结束时显示一次,且不会存储在任何地方:没有它就无法恢复被隐藏的文件。
使用 ***默认隐藏模式***,你可以隐藏任意文件类型,生成的图像上限为 ***2GiB***。其他平台的隐藏模式以及兼容的社交媒体网站(*如下所列*)有各自***小得多***的大小限制和其他要求。
***jdvrif*** 还有一个[***网页版***](https://cleasbycode.co.uk/jdvrif/app/),你可以立即使用,作为下载并编译 CLI 源代码的便捷替代方案。网页文件上传限制为 **20MiB**。
对于对该语言感兴趣的用户,还提供了一个实验性的 ***Rust*** 移植版 [***jdvrif-rs***](https://github.com/CleasbyCode/jdvrif-rs)。它在格式上兼容:任一构建都能恢复对方生成的图像。
***jdvrif*** 部分源自安全研究员 ***[David Buchanan](https://www.da.vidbuchanan.co.uk/)*** 所***[实现的技术](https://www.vice.com/en/article/bj4wxm/tiny-picture-twitter-complete-works-of-shakespeare-steganography)***。

*演示图像:**"A place of concealment"** / ***PIN: 7912872750612437051****
## jdvrif 如何隐藏数据
与常见的 [***LSB***](https://ctf101.org/forensics/what-is-stegonagraphy/)(*最低有效位*)隐写术方法(将数据隐藏在载体图像的像素中)不同,***jdvrif*** 主要将数据隐藏在 ***JPG*** 图像的***应用段***(ICC、EXIF、XMP 等)中。
| 隐藏模式 | 数据存放位置 | 可在以下平台分享输出图像 |
| --- | --- | --- |
| *(无选项)* | APP2/ICC 配置文件段 | X-Twitter、Tumblr、Mastodon、Pixelfed、PostImage、ImgBB、ImgPile、Flickr |
| ***-b*** | 带框架的 EXIF / Photoshop / XMP 段 | 仅 ***Bluesky*** |
| ***-r*** | JPEG DCT 系数(***QIM***) | 仅 ***Reddit*** |
| ***-x*** | JPEG DCT 系数(***J-UNIWARD/STC***) | 仅 ***X-Twitter*** |
默认段存储方法有两个平台例外:***Reddit*** 和 ***X-Twitter***。两者都有各自的隐藏模式,且这两种模式都完全不使用元数据段:***-r*** 和 ***-x*** 改为将载荷承载在 ***JPG*** 图像的 ***DCT 系数***中。
***Reddit*** 会重新编码上传的图像,并丢弃默认模式所依赖的元数据段,因此 ***-r*** 是唯一在该平台有效的模式。***X-Twitter*** 确实会保留单个小型 ICC 段,因此默认模式在该平台上仍然有效,但仅适用于极小的载荷。
对于 ***Reddit*** 隐藏模式(***-r***),我们使用 [***QIM 隐写术方法***](https://www.google.com/search?q=QIM+steganography+method&sourceid=chrome&ie=UTF-8&source=chrome.ctxt)(*JPEG DCT 域量化索引调制*),因为这是目前唯一适用于 ***Reddit*** 的存储方法。载体图像会被转码为 baseline Q75 4:2:0,载荷承载在其亮度 DCT 块中。
为了最大化 ***Reddit*** 平台的存储容量,请使用尺寸较大的载体图像,例如 **2048x2048**、**4096x4096**、**8192x8192(最大)** 等。
对于此方法,载体图像的质量并不重要,对于最大尺寸应保持基础质量,以帮助最小化载体图像的文件大小。
虽然 ***X-Twitter*** 平台可以使用 ***jdvrif*** 提供的默认方法(数据隐藏在 APP2/ICC 段中),但 ***X-Twitter*** 将其限制为单个 ICC 段,最大大小仅为 **~10KiB**。
要承载超过 **~10KiB** 的数据,请使用 ***X-Twitter*** 平台隐藏模式(***-x***)。该模式完全放弃元数据段——不会向 ICC 配置文件写入任何内容——转而使用[***带网格编码(STC)的自适应 J-UNIWARD 隐写方法***](https://www.google.com/search?q=adaptive+J-UNIWARD+steganography+method+with+Syndrome-Trellis+Coding+(STC))。载体图像会被转码为渐进式 4:2:0,并采用其源派生质量(上限为 **Q97**)。
要最大化 ***X-Twitter*** 平台的存储容量,请使用高质量/细节丰富且尺寸较大的载体图像,如 **1024x1024**、**2048x2048**、**4096x4096(最大)** 等。
***-r*** 和 ***-x*** 载体使用 ***Argon2id*** 加密你的数据,并将数据位置与你的恢复 PIN 绑定,但嵌入操作仍会改变图像的 DCT 统计特征,因此专用的隐写分析可能揭示图像携带了隐藏数据。
使用 ***jdvrif v9.0*** 创建的 ***-r*** 和 ***-x*** 图像仍可恢复,但 ***v9.0*** 无法恢复使用 ***v9.1*** 创建的 ***-r*** 或 ***-x*** 图像。
两种 DCT 模式承载的数据量都远少于默认模式,因此在选择载荷之前,请使用 ***capsize*** 测量载体图像(参见[检查容量](#checking-capacity-with-capsize))。
## 要求与编译(Linux)
构建需要 **CMake 3.20 或更新版本**、来自 **util-linux** 的 `flock`,以及 **Ninja**(首选)或 **Make**。编译器必须为 **GCC 14 或更新版本**,或 **Clang 18 或更新版本**,并搭配实现了 `std::format` 和 `std::print` 等特性的 C++23 标准库。所需的原生库包括 **libsodium**、**libjpeg-turbo**(*同时需要 `turbojpeg` 和 `libjpeg` 两套 API*)、**zlib** 和 **libdeflate**。**OpenMP** 为可选项:若存在,它会并行化 ***-x*** 的 J-UNIWARD 代价计算过程。
```console
$ sudo apt update
$ sudo apt install g++ cmake ninja-build util-linux libsodium-dev libturbojpeg0-dev libjpeg-dev zlib1g-dev libdeflate-dev
$ g++ --version # confirm the reported version is 14 or newer
$ chmod +x compile_jdvrif.sh
$ ./compile_jdvrif.sh
$ sudo cp jdvrif /usr/bin
```
如果你的发行版将 GCC 14 作为带版本号的软件包提供,请安装 `g++-14` 并使用 `CXX=g++-14 ./compile_jdvrif.sh` 进行构建。
该封装脚本将依赖跟踪的目标文件保存在 `src/build/` 下,因此后续调用只会重新构建发生变化的部分,并且只有在完整、成功构建之后才会替换已发布的 `jdvrif` 二进制文件。设置 `JDVRIF_JOBS=<count>` 可更改并行度限制(默认值:CPU 数量,上限为 8),设置 `JDVRIF_BUILD_DIR=<path>` 可使用单独的构建缓存,或设置 `BUILD_MODE=sanitize` 以进行 ASan/UBSan 构建。
## 用法
```console
$ jdvrif
Usage: jdvrif conceal [-b|-r|-x] <cover_image> <secret_file>
jdvrif recover <cover_image>
jdvrif capsize [-r|-x] <cover_image>
jdvrif --info
```
运行 `jdvrif --info` 可查看关于模式、平台选项和大小限制的完整内置指南。
```console
$ jdvrif conceal your_cover_image.jpg your_secret_file.doc
Platform compatibility for output image:-
✓ X-Twitter
✓ Tumblr
✓ Mastodon
✓ Pixelfed
✓ PostImage
✓ ImgBB
✓ ImgPile
✓ Flickr
Recovery PIN: [***2166776980318349924***]
Important: Keep your PIN safe, so that you can extract the hidden file.
Saved "file-embedded" JPG image: jrif_4e87c566c.jpg (143029 bytes).
Complete!
$ jdvrif recover jrif_4e87c566c.jpg
PIN: *******************
Extracted hidden file: your_secret_file.doc (6165 bytes).
Complete! Please check your file.
```
jdvrif ***mode*** 参数:
***conceal*** - 压缩、加密并将你的秘密数据文件嵌入到 ***JPG*** 封面图像中。
***recover*** - 解密、解压缩并从 ***JPG*** 封面图像中提取隐藏的数据文件(*需要恢复 PIN*)。
***capsize*** - 报告封面图像在 ***-r*** 或 ***-x*** 模式下的载体容量。不会保存任何图像。
封面图像的要求:
● 仅限 JPEG,至少 **400x400** 像素,且为灰度或 YCbCr 色彩——CMYK/YCCK 图像必须先转换为 RGB。
● 默认和 ***-b*** 的上限尺寸为 **4096x4096px**;***-r*** 使用 **8192x8192px**,而 ***-x*** 也限制为 **4096x4096px**。
● 默认模式(*无选项*)还会拒绝估计 JPEG 质量高于 **Q97** 的封面图像;如果图像被拒绝,请以较低质量重新保存。
秘密数据文件的要求:
● 嵌入的文件名不得超过 **20 字节**,不得以 `.` 或 `-` 开头,也不得以空格或 `.` 结尾,并且必须是有效的 UTF-8,不含控制字符或双向格式字符,也不含 `/ \ : * ? " < > |` 中的任何字符。该名称存储在图像中,并在 ***recover*** 时恢复。
● 你的数据文件在加密前会被压缩,但已识别的已压缩文件类型(`.zip`、`.7z`、`.mp4`、`.jpg`、`.png` 等)除外。在默认和 ***-b*** 模式下,这些类型仅在大于 **10MiB** 时才跳过压缩;在 ***-r*** 和 ***-x*** 模式下,它们始终跳过压缩。对于其他任何要用于小型平台限制的文件,请考虑先自行压缩(*zip、rar、7z 等*),以便你确切知道其存储大小。
## 兼容平台
\******************
注意:***Bluesky*** 现在默认将图像保存为 ***WEBP***。
要将图像保存为 ***JPG***,以便你仍能使用 ***jdvrif*** 恢复隐藏数据:-
首先点击帖子中的图像将其打开,然后右键点击该图像。从菜单中选择 ***在新标签页中打开图像***。
选择新标签页,在地址栏中,将光标移到地址末尾并添加 ***@jpg***,然后按回车。
右键点击图像,从菜单中选择 ***保存图像...***
你的图像现在应已下载为 ***JPG***,此时即可与 ***jdvrif*** 配合使用。
如果你想使用 ***WEBP*** 图像隐藏数据并发布到 ***Bluesky***,可以使用我的 ***WEBP*** 隐写命令行工具 ***[wbpdv](https://github.com/CleasbyCode/wbpdv)***
\******************
*发布大小限制按 ***封面图像*** + ***压缩数据文件*** 的 ***合计*** 大小计算:*
● ***Flickr***(**200MiB**)、***ImgPile***(**100MiB**)、***ImgBB***(**32MiB**)、***PostImage***(**32MiB**)、***Pixelfed***(**15MiB**)。
*大小限制***仅***按 ***压缩数据文件大小*** 计算:*
● ***Mastodon***(**~6MiB**)、***Tumblr***(**~64KiB**)、***X-Twitter***(**~10KiB / 默认方法**)。
例如,对于 ***Mastodon***,如果你的封面图像为 **1MiB**,你仍然可以嵌入最大至 **~6MiB** 大小限制的数据文件。
**其他:拥有自己隐藏模式的平台:**
● ***Bluesky***(***-b 选项***)。完成的“*已嵌入文件*”***JPG*** 不得超过 **2,000,000 字节(~1.9MiB)**,因此封面图像和压缩数据文件共享同一预算。压缩数据文件本身不得超过 **~171KiB**。已经达到 2,000,000 字节的封面图像完全没有剩余空间,因此请让封面图像比限制至少小出你的压缩数据文件的大小。需要“***create_bsky_post.py***”脚本才能在 ***Bluesky*** 上发布这些图像。*有关此脚本的更多信息,请见本页下方。*
● ***Reddit***(***-r 选项***)。封面图像和数据文件各自不得大于 **20MiB**,但封面图像的实际载体容量***要小得多***,并且取决于其尺寸大小。使用 `jdvrif capsize -r` 来测量它。
● ***X-Twitter***(***-x 选项***)。封面图像和数据文件各自不得大于 **5MiB**,且封面不得超过 **4096x4096** 像素。实际载体容量***要小得多***,取决于图像质量和尺寸大小。使用 `jdvrif capsize -x` 来测量它。
在默认模式和 ***-b*** 模式下,封面图像在使用前还会进行无损优化,优化后不得超过 **4MiB**。
对于 ***X-Twitter***、***Reddit*** 和 ***Tumblr*** 等数据大小限制较小的平台,你可能需要专注于压缩效果好的数据,例如文本文件等。
https://github.com/user-attachments/assets/af330d09-f65d-4f75-8290-883078be34b3
https://github.com/user-attachments/assets/fc454d42-0240-4864-b44b-ce5ef7cfd94c
## 使用 capsize 检查容量
***capsize*** 会完全按照 ***conceal*** 的方式准备封面图像,然后报告它可以承载多少加密载荷。不会向磁盘写入任何内容。使用 ***-r*** 指定 ***Reddit*** 载体,使用 ***-x*** 指定 ***X-Twitter*** 载体(如果未给出任何选项,***-x*** 为默认值)。
```console
$ jdvrif capsize -r basic_img_large_dims.jpg
Reddit capacity check for conceal -r mode only.
Cover Image: 384KiB, 8192x8192, Baseline YCbCr 4:2:0, Standard Q75 quantization (C3).
Theoretical C3 capacity limit for this cover image: 436906 bytes (~426KiB).
Conservative maximum compressed capacity with a 20-character filename: 436792 bytes (~426KiB).
Recommended maximum compressed capacity with a 20-character filename: 435768 bytes (~425KiB).
```
所报告的数字是加密***信封***的总容量,而非原始秘密文件的上限:对于单帧载荷,文件名、加密和恢复元数据会占用 95 到 114 字节,而更大的载荷会增加分帧开销。不要以理论极限为目标——在容量允许的情况下,将压缩后的载荷保持在保守最大值以下至少 **1KiB**。由 ***conceal*** 执行的大小检查才是权威依据。
## Conceal 模式的平台选项
要创建兼容的“*数据隐藏*”***JPG*** 图像以便发布到 ***Reddit*** 平台,你必须在 ***conceal*** 模式下使用 ***-r*** 选项。
```console
$ jdvrif conceal -r my_image.jpg hidden.doc
```
这些图像仅兼容发布到 ***Reddit***。如果发布到其他平台,嵌入的数据文件将会丢失。
从 ***Reddit*** 保存/下载图像时,请务必先点击帖子中的图像将其完全展开,然后再保存。
https://github.com/user-attachments/assets/ead3c34c-dd9a-4018-9ea3-0746fbdfdb8b
要使用 J-UNIWARD 隐写方法创建兼容的“*data-concealed*”***JPG*** 图像以发布到 ***X-Twitter*** 平台,你必须将 ***-x*** 选项与 ***conceal*** 模式一起使用。
```console
$ jdvrif conceal -x my_image.jpg hidden.doc
```
这些图片仅兼容发布到 ***X-Twitter***。如果发布到其他平台,你嵌入的数据文件将会丢失。
从 ***X-Twitter*** 保存/下载图片时,请务必先点击帖子中的图片将其完全展开,然后再保存。
要创建兼容的“*文件嵌入*”***JPG*** 图片以发布到 ***Bluesky*** 平台,你必须在 ***conceal*** 模式下使用 ***-b*** 选项。
```console
$ jdvrif conceal -b my_image.jpg hidden.doc
```
这些图像仅兼容发布到 ***Bluesky***。如果发布到其他平台,你嵌入的数据文件将被移除。
你还需要使用 Python 脚本 [create_bsky_post.py](https://github.com/CleasbyCode/jdvrif/blob/main/src/bsky/create_bsky_post.py)(位于仓库的 ***src/bsky*** 文件夹中)将图像发布到 ***Bluesky***。
如果你通过浏览器网站或移动应用将图像发布到 ***Bluesky***,它将无法正常工作。
要使用该脚本,你需要从你的 ***Bluesky*** 账户创建一个 [***应用密码***](https://bsky.app/settings/app-passwords)。请通过环境变量传递你的凭据,而不是在命令行中传递,因为在命令行中其他本地用户可以通过 `ps` 等工具看到它们:
```console
$ pip install -r bsky/requirements.txt
$ export ATP_AUTH_HANDLE='you.bsky.social'
$ read -rsp 'Bluesky app password: ' ATP_AUTH_PASSWORD && export ATP_AUTH_PASSWORD
$ python3 bsky/create_bsky_post.py \
--image jrif_4e87c566c.jpg \
--alt-text "alt-text here [optional]" \
"standard post text here [required]"
$ unset ATP_AUTH_PASSWORD
```
有关完整的选项集合(*多张图片、回复、引用帖、链接卡片*)以及该脚本加固分支所防护的内容,请参阅 [src/bsky/README.md](https://github.com/CleasbyCode/jdvrif/blob/main/src/bsky/README.md)。
https://github.com/user-attachments/assets/1daef508-d304-491f-bfe2-2cdbb5d62081
## 测试
`src/tests` 中的每个测试脚本都接收要测试的二进制文件:
```console
$ bash tests/run_golden_tests.sh --bin ./jdvrif # recover pre-built images with known PINs
$ bash tests/run_roundtrip_tests.sh --bin ./jdvrif # fresh conceal/recover round-trips
$ bash tests/run_security_smoke.sh --bin ./jdvrif # negative-path and boundary checks
$ bash tests/run_reddit_tests.sh --bin ./jdvrif # -r QIM carrier
$ bash tests/run_twitter_tests.sh --bin ./jdvrif # -x J-UNIWARD/STC carrier
$ bash tests/run_io_security_tests.sh --bin ./jdvrif # input-size bounds, PIN signal/job-control handling
$ bash tests/run_jpeg_safety_tests.sh # JPEG parser/decoder safety (builds its own test binary)
$ bash tests/run_bsky_tests.sh # Bluesky posting helper
```
## 第三方软件与资源
### 核心应用程序
- [libsodium](https://github.com/jedisct1/libsodium) — 加密随机数生成、Argon2id
密钥派生和 XChaCha20-Poly1305 秘密流。作为系统库动态链接。
许可证:[ISC License](https://github.com/jedisct1/libsodium/blob/master/LICENSE)
版权所有 (c) 2013–2026 Frank Denis。
- [libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo) — JPEG 处理和无损转换。作为系统库动态链接。
本软件部分基于 Independent JPEG Group 的工作。
许可证:[Independent JPEG Group License、Modified BSD 3-Clause License
和 zlib License](https://github.com/libjpeg-turbo/libjpeg-turbo/blob/2.1.5/LICENSE.md)。
版权所有 © 1991–2020 Thomas G. Lane 和 Guido Vollbeding。
版权所有 © 2009–2023 D. R. Commander。保留所有权利。
版权所有 © 2015 Viktor Szathmáry。保留所有权利。
- [zlib](https://github.com/madler/zlib) — 流式 zlib 压缩和解压缩。作为系统库动态链接。
许可证:[zlib License](https://github.com/madler/zlib/blob/develop/LICENSE)
版权所有 (C) 1995–2026 Jean-loup Gailly 和 Mark Adler。
- [libdeflate](https://github.com/ebiggers/libdeflate) — 快速全缓冲区 zlib 格式压缩。作为系统库动态链接。
许可证:[MIT](https://github.com/ebiggers/libdeflate/blob/master/COPYING)
版权所有 2016 Eric Biggers。
版权所有 2024 Google LLC。
### 内置代码和资源
- [Compact ICC Profiles](https://github.com/saucecontrol/Compact-ICC-Profiles) — 内嵌的 Adobe-
兼容 ICC 配置文件。
许可证:[CC0 1.0 Universal](https://github.com/saucecontrol/Compact-ICC-Profiles/blob/master/license)
### 可选的 Bluesky 发布辅助工具
- Bryan Newbold / ATProto Hacker Cookbook — create_bsky_post.py — [分叉版](https://gist.github.com/CleasbyCode/1eb678ca1fa1975b1c1e20aeec33637e) Bluesky 发布辅助工具(src/bsky/create_bsky_post.py)的基础。
有关参考,请参阅 [Cookbook 副本](https://github.com/bluesky-social/cookbook/blob/main/python-bsky-post/create_bsky_post.py)
许可证:[CC0 1.0 Universal](https://github.com/bluesky-social/cookbook/blob/main/LICENSE-CC0)。
- Requests — HTTP 和 Bluesky API 请求。
许可证:[Apache 2.0](https://github.com/psf/requests/blob/main/LICENSE) / [NOTICE](https://github.com/psf/requests/blob/main/NOTICE)
版权所有 2019 Kenneth Reitz。
- Beautiful Soup 4 — 解析 HTML 和 Open Graph 元数据。
许可证:[MIT](https://pypi.org/project/beautifulsoup4/)
版权所有 (c) Leonard Richardson。
- Pillow — 图像验证、尺寸和宽高比处理。
许可证:[MIT-CMU](https://github.com/python-pillow/Pillow/blob/main/LICENSE)
PIL 版权所有 © 1997–2011 Secret Labs AB 和 © 1995–2011 Fredrik Lundh 及贡献者。
Pillow 版权所有 © 2010 Jeffrey “Alex” Clark 及贡献者。
##