解密带隐藏信息的图片

我花了三个小时,解密才在一张看似普通的藏信风景照里,找到那串藏了十二年的图片数字。不是解密用肉眼看,是藏信用十六进制编辑器打开文件,在PNG图像数据的图片末尾,有一长串零。解密那些零不是藏信随机填充,它们构成了一个压缩包的图片二进制流。解压出来,解密是藏信一份2009年的聊天记录。

这种把信息塞进图片里的图片手法,有个正式名字叫隐写术。解密跟加密不同,藏信加密是图片告诉别人“这里有秘密”,隐写是让秘密看起来像垃圾数据。最粗糙的做法,就是把文字直接追加在JPEG文件的结束标记FF D9之后。任何图片查看器都会忽略那之后的字节,但用记事本打开图片,就能看到明文。我见过有人把银行密码藏在公司logo的白色背景里,每三个像素的蓝色通道值末尾,藏着一位数字。放大五十倍,肉眼依然看不出任何异常。

稍微讲究点的,会用最低有效位算法。图片每个像素的颜色值由RGB三个字节组成,每个字节最低的那一位,改动了人眼根本察觉不到。一张1920乘1080的图片,有超过六百万个像素,每个像素能藏三位信息。理论上,你可以把一本《三体》三部曲塞进一张高清壁纸里,画质损失小到连专业显示器都分辨不出差异。我做过实验,藏入2MB文本后,图片文件大小会膨胀,但如果你把载体图片选成带有大量噪点的夜景照片,那点体积变化混在原有噪声里,几乎无迹可寻。

真正让我脊背发凉的,是另一种思路——不修改图片本身,而是利用文件名。有个朋友给我发来一张叫“DSC_0014.jpg”的照片,他坚持说里面有线索。我检查了所有像素位、EXIF信息、文件尾部,一无所获。后来我注意到,文件名的每个字符,ASCII码的二进制排列,恰好是一段摩斯电码的变体。D对应-..,S对应...,C对应-.-.,连起来读,是“look in my old blog”。这招儿连技术门槛都没有,却比多数算法更隐蔽,因为没人会怀疑文件名本身。

检测隐写信息比藏信息难得多。最笨但有效的方法,是统计图片的颜色直方图。正常的JPEG压缩会产生大量相近但不相等的颜色值,而经过最低有效位修改的图片,颜色值会出现异常的成对分布——相邻数值的像素数量几乎完全一致。我拿这个办法戳穿过同事的恶作剧,他把年会抽奖的作弊代码藏在一张表情包里,用LSB算法填了三个像素,直方图上立刻露出马脚。

但道高一尺,魔高一丈。现在的隐写工具已经学会对抗分析,它们会先对秘密信息做加密,再打散成随机分布的数据嵌入图片,让颜色直方图看起来跟自然图像无异。你唯一能做的,是比对原始图片——如果手头有同一场景、同一设备拍的其他照片,对比像素噪声的分布模式,能发现被篡改区域留下的微弱痕迹。这活儿跟考古差不多,得耐心,得运气。

说到底,图片隐写之所以迷人,是因为它利用了人类视觉的生理缺陷。我们的大脑天生擅长识别形状和颜色,却对像素级的统计偏差毫无感知。下次有人发你一张莫名其妙的图片,别急着放大看细节,先查查文件末尾有没有多余字节,再看看文件名是不是藏着ASCII码,最后用调色板工具扫一遍蓝色通道的最低有效位。也许,那只是一张普通图片。但也许,你正站在某个秘密的门口,而钥匙,就明晃晃地挂在门框上,只是没人抬头看。