使用 JPEG Exif 信息作为起点来处理二进制数据
你好。
我是Mandai,负责Wild 开发团队。
前几天,一个奇怪的现象,我的智能手机上的图像面向明天的方向时,我对Exif信息做了很多研究,在这个过程中我能够读取二进制Exif信息,所以我来解释一下Exif信息的结构。
希望
通过这个过程,人们会了解到二进制数据是微不足道的
Exif 分析用什么
用于 Exif 分析的工具可在 Internet 上找到。
如果具备以下三点,就可以读取Exif信息。
首先,有关标准的信息。
日本电子信息技术产业协会提供的资料就好了,但由于这本书比较难懂,我就用Excel一一过一遍。
Stirling的软件
来引用二进制数据看起来维护已经结束了,但是它制作得太精良了,我在这个领域还没有看到更好的工具。
在Linux环境下,有一个命令叫od来显示二进制数据,所以用od -x [path]可以看到类似的东西。
这种情况下,转储列表会流动得很慢,所以要酌情使用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 文件始终以 2 字节数据“FF D8”开头。
在JPEG行业中,这是一个称为SOI的特殊区域,在一段时间内它仍将是存储图像元数据的区域。
元数据是除图像数据之外还保存的数据,并且包括Exif信息。
该数据分为必需的项目和其他数据,这些数据可以由拍摄图像的设备或编辑图像的应用程序独立添加。
接下来的数据是“FF E1”,它表示称为APP1应用程序段的元数据段的开始。
由于这是变长数据,因此接下来的2个字节包含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”,说明数据是按照little endian排列的。
在大端的情况下,是“4D 4D”。
从现在开始,由于小端和大端的差异,每个信息的顺序将颠倒,所以如果你有用苹果产品拍摄的照片,请小心,因为它是大端。
我认为
维基百科对字节顺序这个词有很好的解释成为该数据之后出现的数据地址偏移的参考位置
接下来是 TIFF 版本,即“2A 00”。
接下来,出现了称为 0th IFD 的东西。 IFD是Image File Directory的缩写,表示附加信息的集合。
形象地说,几个标签被组合在一个 IFD 中。
IFD是根据数据类型来划分的。
IFD中的数据被分为标签,每个标签长12字节。
IFD数据结构如下。
第0个IFD | 标签数量 | 2字节 |
标签1 | 12字节 | |
标签2 | 12字节 | |
标签3 | 12字节 | |
・・・ | ||
标签N | 12字节 | |
第一个 IFD 的偏移值 | 4字节 | |
IFD 中的第 0 个值 | 多变的 |
- “标签数”表示第 0 个 IFD 中有多少个标签。
- “到第 1 个 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获取相关的东西。
- “类型”是数据类型。这是任何有过类型化语言编程经验的人都会理解的。只有8种,稍后我会介绍。
- “计数”表示标签内包含多少条数据。
- “值到值的偏移”不适用于数据大于4字节的情况,因此单独分配一个变量区并存储值,因此到数据位置的偏移值为值。有一个例外,小于 4 字节的数据将存储在该偏移值部分中。
我列出了类型。
根据类型的不同,计数方法也不同,所以我把它们放在一起了。
类型值 | 模具 | 解释 | 如何计算 |
1 | 字节 | 8 位无符号整数 | |
2 | ASCII码 | 细绳。字符数还包括标记字符串结尾的最后一个 NULL。 | 对于“BEYOND”、“B”、“E”、“Y”、“O”、“N”、“D”、“\0”→ 7 个计数 |
3 | 短的 | 16 位(2 字节)无符号整数 | 1 数 5 |
4 | 长的 | 32 位(4 字节)无符号整数 | 1 数 5 |
5 | 合理的 | 对于 2 个 LONG,第一个 LONG 是分子,第二个 LONG 是分母。 | 1 为 5/4 |
7 | 不明确的 | 8 位数据可以取任何值,具体取决于字段定义 | 0xFF、0x01、0x45、0x11、0xDD、5 个计数(8 位中的 1 个计数) |
9 | 斯隆 | 32 位(4 字节)有符号整数 | 1 数到 5 |
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 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 0 0 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 0 0 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 10 00 00 00 0A 92 05 00 01 00 00 00 76 02 00 00 7C 92 07 00 70 4F 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 5 1 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 00 04 A4 05 00 00 00 00 00 00 006 52 00 006 A4 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 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 理性 |
01 00 00 00 数1 |
66 02 00 00 F4 01 00 00 64 00 00 00 (500/100) |
04 92 曝光校正值 |
0A 00 理性 |
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 制造商注释 |
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 DateTimeDegitized 的亚秒 |
02 00 ASCII码 |
07 00 00 00 数7 |
FE51 00 00 36 38 34 37 37 32(684772) |
00 A0 兼容flash pix版本 |
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 材料,我希望本文会对您有所帮助。
就是这样。