PNG 文件不能直接末尾追加数据,必须在 IEND 块后插入并添加自定义标识头;WAV 可末尾追加但需同步更新 RIFF 头长度字段;MP3 不推荐隐写;JPEG 不适用 LSB,应利用 APP 段;Base64 密文须用标记头+长度前缀而非固定偏移。

用 System.Drawing 往 PNG 末尾追加数据会直接损坏文件
PNG 是有严格结构的二进制格式,不能像 TXT 那样随便往末尾写东西。直接用 FileStream 打开 PNG 并 Seek 到末尾再 Write,会导致校验失败、图片打不开或被浏览器静默丢弃。真正能安全写入的位置是 IEND 块之后——但 .NET 标准库根本不暴露块解析能力。
实操建议:
- 别碰
System.Drawing或ImageSharp的图像加载/保存流程来“塞数据”,它们会重写整个文件头和 CRC - 改用纯字节操作:先读取原始 PNG 文件全部字节,定位最后 8 字节(
IENDchunk 的固定结尾),在它后面插入自定义数据 + 自定义标识头(比如SECRET_START) - 读取时也跳过文件末尾,反向扫描找你的标识头,而不是依赖长度——因为用户可能另存、压缩、上传后平台二次处理
WAV 文件比 MP3 更适合隐写,但必须避开 RIFF 头长度字段
WAV 是 RIFF 容器,结构清晰:RIFF + 总长度 + WAVE + 子块。你在末尾追加数据没问题,但如果你修改了文件总大小,就必须同步更新第 4–7 字节(little-endian 表示的整个文件长度)。否则播放器读到错误长度会截断或报错 Invalid format。
实操建议:
- 用
BinaryReader读前 8 字节确认是RIFF****WAVE,再读第 4 字节开始的 4 字节得到当前声明长度 - 追加数据后,用
BinaryWriter把新长度(原长度 + 追加字节数)写回第 4 字节位置 - MP3 不推荐:ID3v2 可能出现在开头,ID3v1 在末尾,帧结构无固定边界,插入数据极易破坏解码同步 —— 除非你只往 ID3v2 和音频帧之间插,且确保不跨帧边界
Steganography 库不是万能的,LSB 隐写对 JPEG 无效
很多 NuGet 包(如 Stegano)默认用最低有效位(LSB)把数据藏进像素值里。这对 BMP/PNG 有效,但 JPEG 经过 DCT 变换和量化,任何 LSB 修改都会在解压时被抹掉——你写进去,读出来就是乱码。
实操建议:
- 对 JPEG,只能走「文件级隐写」:利用 JFIF 结构中 APP0–APP15 段的冗余空间,或在 SOI(
0xFFD8)后、APP0 前插入自定义 APP 段(如0xFFEE) - 不要相信标榜“支持所有格式”的库——检查源码是否真区分了编码格式处理路径
- 测试时务必用不同来源的图片:手机直出、微信转发后、Photoshop 另存为,JPEG 质量参数微调都可能导致 APP 段被清理
Base64 编码后的密钥长度会影响隐写位置偏移计算
你打算藏的是加密后的密文,不是明文。如果用 Convert.ToBase64String 编码,输出长度是 4 的倍数,且含 = 填充符。如果隐写逻辑硬编码了“从倒数第 1024 字节开始读”,而 Base64 后长度变了,就会读歪。
实操建议:
- 永远用标记头+长度前缀,而不是固定偏移:比如写入
SECRET_V1:000123:+ 密文,读取时先找SECRET_V1:,再解析冒号后 6 位数字得真实长度 - 避免用
=作分隔符——Base64 自身就用它,容易误判;改用少见组合如__SEPL__ - 密文本身建议 AES-GCM 加密并带上认证标签,否则即使数据没被破坏,也无法判断是否被篡改
最麻烦的不是怎么写进去,而是怎么让数据在各种编辑、压缩、格式转换后还活着。没有通用方案,每个载体格式都要单独抠结构、测边界、留退路。








