以 JPEG Exif 信息为起点来处理二进制数据

你好。
我是Mandai,负责Wild 开发团队。

前几天,智能手机上照片方向错误的奇怪现象,于是研究了一下Exif信息。在这个过程中,我能够读取Exif信息的二进制版本,所以我想解释一下Exif信息的结构。

也希望通过这个过程,人们能够意识到

 很容易被操纵

Exif 分析中使用的是什么?

用于分析 Exif 信息的工具可以从互联网上获取。
您可以使用以下三种方式读取 Exif 信息:

首先,我查阅了标准资料。
日本电子信息技术产业协会的资料,那就没问题了,但那本书相当难懂,所以我用Excel逐条核对。

为了查看二进制数据,我使用了一款名为
Stirling这款软件似乎已经停止维护了,但它的品质非常出色,我至今还没有找到比它更好的同类工具。

在 Linux 环境中,有一个名为 od 的命令可以显示二进制数据,因此您可以使用 od -x [路径] 查看类似的内容。
在这种情况下,转储列表会以长行滚动显示,所以您需要根据需要使用 head、tail 和 grep 命令来仅显示您想要查看的部分。

使用 Exif 信息的图像格式包括 JPEG、TIFF 和 JPEG XR。
由于 Exif 信息通常会记录在这些文件中,因此很容易找到示例。让
我们导入一张用您的智能手机拍摄的随机照片。

 

解码二进制信息

二进制数据不是文本信息,而是符合扩展名所指示格式的数据集合,因此从一开始就按照 JPEG 数据格式排列数据。

我提取了我手头上的一个JPEG文件的第一部分。

FF D8 FF E1 6C 0F 45 78 69 66 00 00 49 49


 每个 JPEG 文件都以两个字节的数据“FF D8”开头。
这是 JPEG 行业中称为 SOI 的特殊区域,其余部分存储图像元数据。

元数据是指除图像数据之外存储的数据,包括Exif信息。
这些数据分为必需项和其他数据,其他数据可由拍摄照片的设备或编辑照片的应用程序独立添加。

以下数据为“FF E1”,表示名为 APP1 应用段的元数据段的开始。
由于这是可变长度数据,接下来的两个字节存储了 APP1 应用段的数据长度。
数据长度为“6C0F”字节,因此 APP1 应用段中的数据延伸至地址 6C13。

下一条数据是“45 78 69 66 00 00”,它表示是否存在Exif信息。
这次,数据似乎存在。

这是与整个 APP1 段相关的所有信息。

以下信息均为补充信息,并非必需。

让我们提取以下数据。

49 49 2A 00 08 00 00 00


 首先是TIFF文件头,它指示字节顺序、TIFF版本以及到第0个IFD起始位置的偏移量信息。
在本例中,字节顺序为“49 49”,表示数据采用小端序排列。
如果是大端序,则为“4D 4D”。
从这里开始,每条信息的顺序都会根据其是小端序还是大端序而反转。如果您使用苹果产品拍摄照片,则照片采用的是大端序,因此请务必注意。
维基百科对“字节顺序”一词提供了详细的
非常重要,因为它将作为后续数据地址偏移量的参考点

接下来是 TIFF 版本,“2A 00”。

接下来是第 0 个 IFD。IFD 代表图像文件目录,它包含一系列附加信息。
可以将其理解为单个 IFD 中包含的多个标签的集合。IFD
根据数据类型进行划分。

IFD 中的数据被分成若干标签,每个标签的长度固定为 12 字节。IFD
的数据结构如下:

IFD结构
第 0 个 IFD 标签数量 2 字节
标签 1 12 字节
标签 2 12 字节
标签 3 12 字节
・・・
标签 N 12 字节
偏移值到第一IFD 4 字节
第 0 个 IFD 中的值 多变的
  • “标签数量”表示第 0 个 IFD 中有多少个标签。
  • “到第一个 IFD 的偏移值”是指到下一个 IFD(第 0 个 IFD 对应第 1 个 IFD,第 1 个 IFD 对应第 2 个 IFD)起始地址的偏移值。(如果该值为 0,则表示没有下一个 IFD。)
  • “第 0 个 IFD 中的值”包含的数据超出了分配给每个标签的字节数。(其结构稍后会解释。)

每个标签的结构如下:

标签结构
第 0 个 IFD 标签 1 标签编号 2 字节
类型 2 字节
数数 4 字节
偏移量 4 字节
标签 2 标签编号 2 字节
类型 2 字节
数数 4 字节
偏移量 4 字节
  • “标签号”的种类繁多,无法一一列举。相关规范,但这工作量巨大,所以我将逐一介绍与获取Exif数据相关的标签号。
  • “类型”是一种数据类型。任何有使用类型化语言编程经验的人都会熟悉它。类型只有八种,我们稍后会介绍。
  • “计数”表示标签中包含的数据数量。
  • 如果数据大于 4 字节,“偏移值”将无法放入此区域,因此会分配一个单独的变量区域来存储该值,该区域即成为该数据位置的偏移值。但也有例外:小于或等于 4 字节的数据将存储在为该偏移值保留的区域中。

下面列出了各种类型。
由于不同类型的计数方法不同,我也在这里一并列出。

类型列表
类型值 类型 解释 如何计数
1 字节 8 位无符号整数
2 ASCII 一个字符串。字符数包含终止符 NULL。 对于“BEYOND”、“B”、“E”、“Y”、“O”、“N”、“D”、“\0”→ 7 个计数
3 短的 16 位(2 字节)无符号整数 5 算作 1
4 长的 32 位(4 字节)无符号整数 5 算作 1
5 合理的 对于两个 LONG 值,第一个 LONG 值是分子,第二个 LONG 值是分母。 1 比 5/4
7 不明确的 8 位数据,其值可根据字段定义而定。 0xFF、0x01、0x45、0x11、0xDD:5 个计数(8 位 = 1 个计数)
9 32 位(4 字节)有符号整数 5 算作 1
10 理性 在 SLONG2 古典日语中,第一个 SLONG 是分子,第二个 SLONG 是分母 5/4 算作 1

光看表格是看不出什么问题的,所以我们来逐条分析数据。

 

阅读IFD

我从实际照片数据中提取了第 0 个 IFD 部分。

0B 00 0F 01 02 00 05 00 00 00 92 00 00 00 10 01 02 00 07 00 00 00 98 00 00 00 12 01 03 00 01 00 00 00 01 00 00 00 1A 01 05 00 01 00 00 00 A0 00 00 00 1B 01 05 00 01 00 00 00 A8 00 00 00 28 01 03 00 01 00 00 00 02 00 00 00 31 01 02 00 14 00 00 00 B0 00 00 00 32 01 02 00 14 00 00 00 C4 00 00 00 13 02 03 00 01 00 00 00 01 00 00 00 69 87 04 00 01 00 00 00 D8 00 00 00 25 88 04 00 01 00 00 00 2C 52 32 52 00 00


 此表,首先提取前两个字节。
“0B 00”部分表示第 0 个 IFD 中包含的标签数量。
此数据的字节顺序为小端序,因此人眼读取时为 0x000B,
即 11。
这意味着第 0 个 IFD 包含 11 个标签。

一个标签是 12 字节的数据块,因此标签数据为 12*11=132 字节。

让我们逐个提取标签。

0F 01 02 00 05 00 00 00 92 00 00 00 10 01 02 00 07 00 00 00 98 00 00 00 12 01 03 00 01 00 00 00 01 00 00 00 1A 01 05 00 01 00 00 00 A0 00 00 00 1B 01 05 00 01 00 00 00 A8 00 00 00 28 01 03 00 01 00 00 00 02 00 00 00 31 01 02 00 14 00 00 00 B0 00 00 00 32 01 02 00 14 00 00 00 C4 00 00 00 13 02 03 00 01 00 00 00 01 00 00 00 69 87 04 00 01 00 00 00 D8 00 00 00 25 88 04 00 01 00 00 00 2C 52 00 00


 我会将标签分解成各个元素,并创建一个表格。
查阅规范,并将其添加到表格中。

标签编号/标签名称 类型 数数 对值的偏移量
0F 01
制造商
02 00
ASCII
05 00 00 00
数到5
92 00 00 00
10 01
模型
02 00
ASCII
07 00 00 00
计数 7
98 00 00 00
12 01
图像方向
03 00
短的
01 00 00 00
计数 1
01 00 00 00
1A 01
图像宽度分辨率
05 00
合理的
01 00 00 00
计数 1
A0 00 00 00
1B 01
图像高度分辨率
05 00
合理的
01 00 00 00
计数 1
A8 00 00 00
28 01
图像宽度和高度分辨率单位
03 00
短的
01 00 00 00
计数 1
02 00 00 00
31 01
软件
02 00
ASCII
14 00 00 00
计数 20
B0 00 00 00
32 01
文件修改时间
02 00
ASCII
14 00 00 00
计数 20
C4 00 00 00
13 02
YCC像素配置(Y和C的位置)
03 00
短的
01 00 00 00
计数 1
01 00 00 00
69 87
指向 Exif IFD 的指针
04 00
长的
01 00 00 00
计数 1
D8 00 00 00
25 88
指向 GPS IFD 的指针
04 00
长的
01 00 00 00
计数 1
2C 52 00 00

实际值的获取方式遵循一定的模式:如果数据类型为 BYTE、SHORT、LONG 或 SLONG,且计数为 1,则值存储在偏移量部分。
对于 ASCII 和 UNDEFINED 数据类型,如果计数小于等于 4,则值存储在偏移量部分。

否则,,并检索指定类型和数量的数据。
检索结果如下:

标签名称 价值
制造商 53 6F 6E 79 00
索尼
模型 53 4F 2D 30 34 48 00
SO-04H
图像方向 1
图像宽度分辨率 48 00 00 00 01 00 00 00
72/1
图像高度分辨率 48 00 00 00 01 00 00 00
72/1
图像宽度和高度分辨率单位 2
软件 33 35 2E 30 2E 42 2E 32 2E 32 37 32 5F 30 5F 66 36 30 30 00
35.0.B.2.272_0_f600
文件修改时间 32 30 31 36 3A 31 30 3A 32 31 20 31 35 3A 32 30 3A 35 34 00
2016:10:21 15:20:54
YCC像素配置(Y和C的位置) 1
指向 Exif IFD 的指针 D8 00 00 00
指向 GPS IFD 的指针 2C 52 00 00
偏移值到第一IFD 32 52 00 00

我的手机型号因此暴露了,但这说明从一张 JPEG 图片中提取这么多信息是多么容易。

现在我们知道了 Exif IFD 的位置,让我们来看看主要内容——Exif 信息。

 

读取Exif信息

该步骤与第 0 个 IFD 的步骤完全相同。让我们从 Exif 信息部分提取二进制数据。

1C 00 9A 82 05 00 01 00 00 00 2E 02 00 00 9D 82 05 00 01 00 00 00 36 02 00 00 27 88 03 00 01 00 00 00 A0 00 00 00 00 90 07 00 04 00 00 00 30 32 32 30 03 90 02 00 14 00 00 00 3E 02 00 00 04 90 02 00 14 00 00 00 52 02 00 00 01 91 07 00 04 00 00 00 01 02 03 00 01 92 0A 00 01 00 00 00 66 02 00 00 04 92 0A 00 01 00 00 00 6E 02 00 00 07 92 03 00 01 00 00 00 05 00 00 00 08 92 03 00 01 00 00 00 00 00 00 00 09 92 03 00 01 00 00 00 10 00 00 00 0A 92 05 00 01 00 00 00 76 02 00 00 7C 92 07 00 70 4楼00 00 7E 02 00 00 90 92 02 00 07 00 00 00 EE 51 00 00 91 92 02 00 07 00 00 00 F6 51 00 00 92 92 02 00 07 00 00 00 FE 51 00 00 00 A0 07 00 04 00 00 00 30 31 30 30 01 A0 03 00 01 00 00 00 01 00 00 00 02 A0 04 00 01 00 00 00 60 17 00 00 03 A0 04 00 01 00 00 00 26 0D 00 00 05 A0 04 00 01 00 00 00 0E 52 00 00 01 A4 03 00 01 00 00 00 00 00 00 00 02 A4 03 00 01 00 00 00 00 00 00 00 03 A4 03 00 01 00 00 00 00 00 00 00 04 A4 05 00 01 00 00 00 06 52 00 00 06 A4 03 00 01 00 00 00 00 00 00 00 0C A4 03 00 01 00 00 00 00 00 00 00 00 00 00 00

 

标签编号 类型 数数 偏移值到实际值/数据
9A 82
曝光时间
05 00
合理的
01 00 00 00
计数 1
2E 02 00 00
0A 00 00 00 40 01 00 00 (10/320)
9D 82
F 数
05 00
合理的
01 00 00 00
计数 1
36 02 00 00
14 00 00 00 0A 00 00 00 (20/10)
27 88
拍摄灵敏度
03 00
短的
01 00 00 00
计数 1
A0 00 00 00
10
00 90
Exif 版本
07 00
不明确的
04 00 00 00
计数 4
30 32 32 30
0220
03 90
原始图像数据的创建日期和时间
02 00
ASCII
14 00 00 00
计数 20
3E 02 00 00
32 30 31 36 3A 31 30 3A 32 31 20 31 35 3A 32 30 3A 35 34 (2016:10:21 15:20:54)
04 90
数字数据创建的日期和时间
02 00
ASCII
14 00 00 00
计数 20
52 02 00 00
32 30 31 36 3A 31 30 3A 32 31 20 31 35 3A 32 30 3A 35 34 (2016:10:21 15:20:54)
01 91
每个组成部分的含义
07 00
不明确的
04 00 00 00
计数 4
01 02 03 00
其他(Y、Cb、Cr)
01 92
快门速度
0A 00
SRATIONAL
01 00 00 00
计数 1
66 02 00 00
F4 01 00 00 64 00 00 00 (500/100)
04 92
曝光校正值
0A 00
SRATIONAL
01 00 00 00
计数 1
6E 02 00 00
00 00 00 00 03 00 00 00(0/3)
07 92
光度法
03 00
短的
01 00 00 00
计数 1
05 00 00 00
5(分屏计量)
08 92
光源
03 00
短的
01 00 00 00
计数 1
00 00 00 00
0(未知)
09 92
闪光
03 00
短的
01 00 00 00
计数 1
10 00 00 00
频闪灯
0A 92
镜头焦距
05 00
合理的
01 00 00 00
计数 1
76 02 00 00
A7 01 00 00 64 00 00 00 (423/100)
7C 92
Makernote
07 00
不明确的
70 4F 00 00
计数 20336
7E 02 00 00
由于数据量过大,已省略
90 92
日期时间(亚秒)
02 00
ASCII
07 00 00 00
计数 7
EE 51 00 00
36 38 34 37 37 32(684772)
91 92
DateTimeOriginal 的亚秒
02 00
ASCII
07 00 00 00
计数 7
F6 51 00 00
36 38 34 37 37 32(684772)
92 92
数字化日期时间的亚秒
02 00
ASCII
07 00 00 00
计数 7
FE 51 00 00
36 38 34 37 37 32(684772)
00 A0
兼容闪存拨片版本
07 00
不明确的
04 00 00 00
计数 4
30 31 30 30
0100(Flashpix 格式版本 1.0)
01 A0
色彩空间信息
03 00
短的
01 00 00 00
计数 1
01 00 00 00
sRGB
02 A0
有效图像宽度
04 00
长的
01 00 00 00
计数 1
60 17 00 00
5984
03 A0
有效像高
04 00
长的
01 00 00 00
计数 1
26 0D 00 00
3366
05 A0
兼容性 IFD 指针
04 00
长的
01 00 00 00
计数 1
0E 52 00 00
0E 52 00 00
01 A4
单个图像处理
03 00
短的
01 00 00 00
计数 1
00 00 00 00
0
02 A4
曝光模式
03 00
短的
01 00 00 00
计数 1
00 00 00 00
0(自动曝光)
03 A4
白平衡
03 00
短的
01 00 00 00
计数 1
00 00 00 00
0(自动白平衡)
04 A4
数码变焦放大倍率
05 00
合理的
01 00 00 00
计数 1
06 52 00 00
64 00 00 00 64 00 00 00(100/100)
06 A4
拍摄场景类型
03 00
短的
01 00 00 00
计数 1
00 00 00 00
0(标准)
0C A4
主题距离范围
03 00
短的
01 00 00 00
计数 1
00 00 00 00
0(未知)

接下来会显示 GPS 信息,这意味着您可以立即看到照片的拍摄地点。
对于旅行途中拍摄的照片,将其显示在地图上会很方便,但如果对在家拍摄的照片也这样做,就会出现问题。

成功读取Exif信息让我很有成就感,但在PHP中,可以使用`read_exif_data`函数轻松获取上述数据。
如果您手头只有二进制编辑器和Exif文档,希望本文能对您有所帮助。

 
就是这样。

如果您觉得这篇文章有帮助,请点赞!
18
加载中...
18 票,平均:1.00 / 118
30,899
X Facebook 哈特纳书签 口袋

写这篇文章的人

关于作者

万代洋一

我的主要工作是为社交游戏开发 Web API,但我也很幸运能够做很多其他工作,包括营销。
此外,我在 Beyond 中的肖像权被视为 CC0。