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

大家好,
我是Mandai,Wild团队负责开发工作的成员。
最近,:手机上的照片方向都错了。奇怪的现象我遇到了一个
二进制数据是容易我希望通过这个过程,人们能够意识到
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代表图像文件目录(Image File Directory),它代表一组补充信息。
想象一下,多个标签被组合在一个IFD中。IFD
根据数据类型进行分类。
IFD 中的数据被划分为标签,每个标签的长度固定为 12 字节。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 厂商笔记本 |
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 兼容 FlashPix 版本 |
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文档,希望这篇文章能对你有所帮助。
就这样。
19
