通过将 header 维度与实际数据长度进行比较来检测截断的 jpeg 图像

detect truncated jpeg images by comparing header dimensions to actual data length

PowerShell 脚本检索从移动设备 phone 发送的入站邮件消息并将 jpeg 文件附件存储在数据库中。不幸的是,邮件消息通常是从手机服务较差的地区发送的,并且邮件消息会被截断,通常是 mid-attachment。即使邮件消息已被截断,邮件服务器仍会接受它们。正如 Stack Overflow 和其他地方的一些帖子中所述,检查附件是否完整的一种可能方法是查找标记 jpeg 文件结尾的 FF D9 字节:

$binaryReader = New-Object BinaryReader([File]::Open($filePath, [FileMode]::Open)) 
$binaryReader.BaseStream.Seek(-2, [SeekOrigin]::End)
[byte[]]$bytes = New-Object byte[] 2
$binaryReader.Read($bytes, 0, 2)
if (($bytes[0] -eq 0xFF) -and ($bytes[1] -eq 0xD9)) {

不幸的是,对于某些移动运营商或可能是移动运营商和 phone OS 的组合,jpeg 图像似乎附加了额外的字节。生成的 jpeg 图像不会被截断,可以在 ImageMagick 中加载并使用标准图形查看器查看,但上述测试将失败。许多 jpeg 附件以可变数据块结尾,以以下 eight-byte 序列结尾:0x57 0x40 0x40 0x43 0x72 0x65 0x65 0x66 但还有其他变体。

我突然想到,如果 jpeg headers 指定了图像的高度和宽度,那么可能有一种不同的方法来测试截断。代码可以加载图像并尝试读取 bottom-right 角处的像素并查看是否有错误。

$bitmap = [System.Drawing.Bitmap]::FromFile($filePath)
$pixelColor = $bitmap.GetPixel($bitmap.Width - 1, $bitmap.Height - 1)

我抓取了一个严重截断的 jpeg 文件 -- 一个文件大小很小的文件,当在图像查看器中显示时,照片顶部有一个可见的矩形条带,但其余部分是空白的。当 运行 上面针对文件的代码时,位图 object 的宽度和高度为 2560 x 1536,这是 non-trucated 文件的典型尺寸。我希望检索最后一个像素颜色的 GetPixel 调用会 return 为 null 或抛出异常,但事实并非如此。它 return 编辑了一个 RGB 值,就像文件没有被截断一样。

我 运行 此代码在 Windows Server 2012 上的 PowerShell 4 和 .NET Framework 4 下。我想也许在实例化位图时 object .NET 分配了一个足够大的内存缓冲区,可以根据 jpeg header 的尺寸保存位图,然后加载尽可能多的可用数据。然而,当我在 bottom-right 角附近采样各种像素时,颜色 object 有数据。这是位置 x=2559、y=1535 处的颜色值:R:114、G:113、B:111。

这看起来不是在没有可用数据时使用的默认灰色,因为其他相邻像素具有不同的值。对于它的价值,我在空白区域看到的小像素样本的 RGB 值往往在 110 到 116 的范围内。相比之下,top-left 角落的 RGB 值差异更大.

为什么这种方法不起作用?提供截断文件时,为什么 .NET Framework 位图 object 不会引发错误?幻像像素颜色值是否来自未初始化的内存?在提出可靠的截断测试的过程中,还有什么我应该尝试的吗?

ImageMagick 将检测 t运行 分类的 JPEG 文件。例如:

$ convert -regard-warnings truncated.jpg x.png
convert: Premature end of JPEG file `truncated.jpg' @ warning/jpeg.c/JPEGWarningHandler/352.
convert: Corrupt JPEG data: premature end of data segment `truncated.jpg' @ warning/jpeg.c/JPEGWarningHandler/352.
$ echo $?
1

-regard-warnings 标志使 convert return 在警告时成为非零退出代码。

或者,IJG JPEG decoder 将对 t运行 分类的文件发出警告。如果您准备编写一些 C,您可以 运行 在您的图像上。

这个过程是这样的:

  1. 将解压缩程序指向您的文件。

  2. 重复获取扫描线,直到看到整个图像。

  3. 检查错误管理器中的 num_warnings 字段。如果它>0,你就有问题了。

发行版中的example.c很有帮助。还有 libjpeg-turbo,它与 IJG 解码器 ABI 兼容,如果速度有问题,它会更快。

其实判断图片是否被截断很简单:不会有EOI市场。同样,如果在 EOS 市场之后有数据,则添加了一些额外的东西。

JPEG 解压缩过程将始终使用 SOF 标记中的图像大小来解码扫描。