[TOC]

0x00 EXIF基础信息

Exif(Exchangeable image file format 可交换图像文件格式),是一种图像文件格式,其数据存储与JPEG格式是完全相同的,EXIF可以附加于JPEG、TIFF、RIFF、RAW等文件之中,为其增加有关数码相机拍摄信息的内容和索引图或图像处理软件的版本信息。

EXIF元数据藏在照片里的小秘密,那Exif是什么?
答:Exif的意思是“可交换图像文件“,实际上Exif格式就是在JPEG格式头部插入了数码照片的信息(它的数据存储与JPEG格式是完全相同的),包括拍摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及GPS全球定位系统数据、缩略图和其他信息。

比如:在JPEG数据中有一系列0xFF??格式的字符串,这些被称为“标志”,用来标记JPEG文件的信息段。
其中0xFFD8 表示SOI(Start of image 图像开始),0xFFD9表示EOI(End of image 图像结束),两个特殊的标志没有附加的数据,而其他的标志在标志后都会带有附加的数据。

1
2
3
4
5
6
7
8
9
10
0xFFE0 — 0xFFEF之间的标识符称为“应用标志”,在解码JPEG 图像的时候不是必需使用的;
其中Exif信息即存在应用标志中,以0xFFE1作为开头标记,后两个字节表示Exif信息的长度,内部采用TIFF格式存储。
FF E1 2F FE : 是APP1标志以及其长度(后面两个字节表示EXIF数据长度,这里的长度是包含自身的)
45 78 69 66 00 00 : 转换后就是ASCII字符串”Exif”加上两个字节的0x00
4D 4D :(Exif数据内容(用TIFF格式存储数据)表示采用大端字节顺序(定义TIFF数据采用什么字节顺序,如果是0x4949 = "II"就表示采用”Intel”的小端字节顺序,如果为0x4d4d = "MM",表示采用”Motorola”的大端字节顺序,这个字节只要知道下即可。)
00 2A : (标识)
00 00 00 08 : (APP1偏移地址)
00 0B (IFD 的个数)
#接下来的的字节就是各种IFD信息,IFD就相当于文件夹,然后在文件夹下面存放着各种资料,比如资料可以是GPS,设备信息,厂家等,
#EXIF元数据:同理IFD也有多个文件夹(IFD编号),比如IFD0(主图的元数据),IFD1(缩略图的元数据),这里说的元数据乍一听很抽象,不过仔细一听还是很抽象QAQ。

WeiyiGeek.jpg格式

WeiyiGeek.jpg格式

Exif有什么作用?
答:起初记录这些信息是为了帮助摄影爱好者分析自己在不同器材,不同环境和不同设备设置下拍摄的效果,帮助他们不断改进并提高自己的水平。
一方面:通过此数据还可以协助处理图片防止失真。一些地方也会使用它做原图检测,因为在编辑软件中修改后一些技术信息会丢失,同时Exif也会保存历史记录。
另一方面:Exif记录的元数据信息非常丰富,会暴露一些你不想让别人知道的个人的信息,比如:

  • 你拍照使用的设备型号是佳能?佳能的某个型号?还是某款手机?
  • 你拍摄照片的地理位置(GPS定位)高度等待
  • 你拍摄照片的时间
  • 你是否在软件中修过图

EXIF怎么看?

WeiyiGeek.MagicEXIF

WeiyiGeek.MagicEXIF

如何除去exif信息?
答:利用canvas去除Exif信息函数canvas.toDataURL(type, encoderOptions)或者使用Photoshop的存储为web所用格式;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<input id="file-input"  accept="image/png,image/gif,image/jpeg" type="file" />
var icanvas=document.getElementById("canvas");
var cxt=icanvas.getContext('2d');

function drawimg(file){
var img = new Image(); // 创建一个<img>元素
var filename=file.name;
var oFReader = new FileReader();
var rFilter = /^(?:image\/bmp|image\/cis\-cod|image\/gif|image\/ief|image\/jpeg|image\/jpeg|image\/jpeg|image\/pipeg|image\/png|image\/svg\+xml|image\/tiff|image\/x\-cmu\-raster|image\/x\-cmx|image\/x\-icon|image\/x\-portable\-anymap|image\/x\-portable\-bitmap|image\/x\-portable\-graymap|image\/x\-portable\-pixmap|image\/x\-rgb|image\/x\-xbitmap|image\/x\-xpixmap|image\/x\-xwindowdump)$/i;
if (!rFilter.test(file.type)) { alert("You must select a valid image file!"); return; }
oFReader.readAsDataURL(file);
oFReader.onload = function (oFREvent) {
img.src = oFREvent.target.result;
};

img.onload = function(){
icanvas.width=img.naturalWidth;
icanvas.height=img.naturalHeight;
cxt.drawImage(this,0,0,icanvas.width,icanvas.height);
downimgbtn(filename);
};
}

// 用来返回代表画布图像数据的字符串,也就是图片的base64,可以使用 type 参数其类型,默认为 PNG 格式。
//如果type传入的是 image/jpeg或者 image/webp,那么就可以设置encoderOptions了,这个参数是图片质量,取值范围为 0 到 1 。
//当图片过大的时候,下载会出现失败的情况,我们可以在canvas上右键保存,同样可以去掉图片的Exif信息。

function downimgbtn(filename){
var imageurl= icanvas.toDataURL("image/jpeg",1).replace("image/jpeg", "image/octet-stream");
$('#downloadimg').html("<a href="+imageurl+" download="+filename+">下载无exif信息的图片</a>")
}

在定位之前需要了解下IFD格式这样对于后面分析有很大帮助,IFD格式是一个 IFD 由四部分组成,每一个 IFD都是固定的12个字节,分别是

1
2
3
4
5
6
7
8
Bytes 0-1 Tag(用于标记这个IFD类别)
Bytes 2-3 Type(用于指定数据类型)
Bytes 4-7 Count(用于指数据的数量,比如纬度就用度、分、秒三个数来描述)
Bytes 8-11 Value Offset(真实数据所在的偏移地址(相对于 File header)而且需要注意的是,这里记录的值小于 4 个字节,则数据左
#Exif规范在定义时并没有规定必须包含哪些 IFD 及其顺序。但是,第一个 IFD 的位置是可以确定的,从第一个每隔十二字节又可以确定一个 IFD,再从 tag 确定 IFD 类别,然后依次类推找到对应IFD。
#还有一种比较简便的,十六进制0x8825 是GPS IFD入口,直接搜这个然后根据tag定位也可以。
88 25 00 04 #数据类型为Long
00 00 00 01 00 00 06 48 # 可以看出GPS信息位于地址68 48处。但是这个值是相对于 File Header (也就是地址 000C)产生的,因此真实的地址为16进制的6848 + 000C = 6854

比如寻找GPS IFD Pointer(这个东西可以理解成我们电脑的磁盘,比如C盘下存在11个文件夹,其中有个文件夹就是用来存放GPS,在GPS IFD下面又存在子文件夹也就是子tag,比如说经纬度,高度,视觉方向等等)

WeiyiGeek.8825

WeiyiGeek.8825

修改GPS的话主要关心的是经纬度与之相关的Tag如下(12字节为1组):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 4E00   
#GPSLatitudeRef
Tag = 1
Type = ASCII
Count = 2
Default = None
'N' = North latitude
'S' = South latitude
Other = reserved

#GPSLatitude
Tag = 2
Type = RATIONAL
Count = 3
Default = None

#GPSLongtitudeRef
Tag = 3
Type = ASCII
Count = 2
Default = None
'E' = East longtitude
'W' = West longtitude
Other = reserved

#GPSLongtitude
Tag = 4
Type = RATIONAL
Count = 3
Default = None

WeiyiGeek.4E00(案例1)

WeiyiGeek.4E00(案例1)

第一个横线处4E00是ASCII中的N,这个不需要修改,第二个横线处指出真实纬度的偏移位置 0702 + 000C = 070E
第三个横线处4500是ASCII中的E,这个也不需要修改,第四个横线处指出真实经度的偏移位置 071A + 000C = 0726
然后根据真实地址070E再进行跟进,这里采用WinHex分析,方便修改保存数据。

WeiyiGeek.案例

WeiyiGeek.案例

其中前4字节为分子/后4字节为分母,十六进制先转为十进制,之所以这样是因为之前说到的FID数据类型决定的,从而得到纬度数据为: (度)20.H/01.H = 32;(分) 01.H/0.H = 0; (秒) 0FE2.H/64.H = 40.66,对比得完全一致

WeiyiGeek.

WeiyiGeek.

实际案例:

WeiyiGeek.4E00(案例2)-12一组经纬度漂移地址

WeiyiGeek.4E00(案例2)-12一组经纬度漂移地址

1
2
3
4
5
6
7
8
9
比如:0706+000C = 0712 (Offset)  #纬度:28度59分54秒 
0000001C 00000001 #1C.H / 01.H = 28
0000003B 00000001 #3B.H / 01.H = 59
00001518 00000064 #1518.H / 64.H = 5400 / 100 =~ 54

071E+000C = 072A (Offset)
0000006A 00000001 #64.H / 01.H =106
0000003B 00000001 #3B.H / 01.H = 59
0000027C 00000064 #27C.H / 64.H = 36
WeiyiGeek.漂移地址数据

WeiyiGeek.漂移地址数据


示例exif数据示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#图片信息常规的exif提取(返回字典个数)
#说明:经纬度前面的英文字母代表:西经W 东经E 北纬N 南纬S
Image Make : Apple
Image Model : iPhone 5s
Image Orientation : Rotated 90 CW
Image XResolution : 72
Image YResolution : 72
Image ResolutionUnit : Pixels/Inch
Image Software : 11.2.5
Image DateTime : 2018:09:07 14:59:13 #图片照相时间
Image YCbCrPositioning : Centered
Image ExifOffset : 206
GPS GPSLatitudeRef : N #纬度
GPS GPSLatitude : [28, 53, 201/10] #需要进行转码
GPS GPSLongitudeRef : E #经度
GPS GPSLongitude : [107, 0, 2347/50]
GPS GPSAltitudeRef : 0 #高度
GPS GPSAltitude : 103926/127
GPS GPSTimeStamp : [6, 59, 12] #时间戳
GPS GPSSpeedRef : K #速度
GPS GPSSpeed : 5/4
GPS GPSDate : 2018:09:07
GPS Tag 0x001F : 100
Image GPSInfo : 1636
Thumbnail Compression : JPEG (old-style)
Thumbnail XResolution : 72
Thumbnail YResolution : 72
Thumbnail ResolutionUnit : Pixels/Inch
Thumbnail JPEGInterchangeFormat : 1976
Thumbnail JPEGInterchangeFormatLength : 11244
EXIF ExposureTime : 1/129
EXIF FNumber : 11/5
EXIF ExposureProgram : Program Normal
EXIF ISOSpeedRatings : 32
EXIF ExifVersion : 0221
EXIF DateTimeOriginal : 2018:09:07 14:59:13
EXIF DateTimeDigitized : 2018:09:07 14:59:13
EXIF ComponentsConfiguration : YCbCr
EXIF ShutterSpeedValue : 8496/1211
EXIF ApertureValue : 7983/3509
EXIF BrightnessValue : 9472/1679
EXIF ExposureBiasValue : 0
EXIF MeteringMode : Pattern
EXIF Flash : Flash did not fire, auto mode
EXIF FocalLength : 83/20
EXIF SubjectArea : [1631, 1223, 1795, 1077]
EXIF MakerNote : [65, 112, 112, 108, 101, 32, 105, 79, 83, 0, 0, 1, 77, 77, 0, 13, 0, 1, 0, 9, ... ]
EXIF SubSecTimeOriginal : 250
EXIF SubSecTimeDigitized : 250
EXIF FlashPixVersion : 0100
EXIF ColorSpace : sRGB
EXIF ExifImageWidth : 3264
EXIF ExifImageLength : 2448
EXIF SensingMethod : One-chip color area
EXIF SceneType : Directly Photographed
EXIF ExposureMode : Auto Exposure
EXIF WhiteBalance : Auto
EXIF FocalLengthIn35mmFilm : 29
EXIF SceneCaptureType : Standard
EXIF LensSpecification : [83/20, 83/20, 11/5, 11/5]
EXIF LensMake : Apple
EXIF LensModel : iPhone 5s back camera 4.15mm f/2.2
MakerNote Tag 0x0001 : 9
MakerNote Tag 0x0002 : [138, 0, 105, 0, 106, 0, 112, 0, 63, 0, 39, 0, 46, 0, 74, 0, 66, 0, 77, 0, ... ]
MakerNote Tag 0x0003 : [6, 7, 8, 85, 102, 108, 97, 103, 115, 85, 118, 97, 108, 117, 101, 89, 116, 105, 109, 101, ... ]
MakerNote Tag 0x0004 : 1
MakerNote Tag 0x0005 : 128
MakerNote Tag 0x0006 : 115
MakerNote Tag 0x0007 : 1
MakerNote Tag 0x0008 : [159186943/-89915392, 11401/83, 20/83]
MakerNote Tag 0x000E : 1
MakerNote Tag 0x0014 : 1
MakerNote Tag 0x0017 : 0
MakerNote Tag 0x0019 : 0
MakerNote Tag 0x001F : 0

经纬度(度分秒)转换与计算:

1
2
3
4
5
6
7
8
9
GPS GPSLatitudeRef  :  N  #纬度
GPS GPSLatitude : [28, 53, 201/10] #[度,分,秒] 需要进行转码成GPS
GPS GPSLongitudeRef : E #经度
GPS GPSLongitude : [107, 0, 2347/50]

#计算公式
度 + 分/60 + 秒/3600
28 + 53/60 + 201/10/3600 =
28 + 0.8833333333333333 + 0.0055833333333333 = 28.88891666666667

经纬度之间距离计算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#计算两个经纬度的距离,计算两点位置的距离,返回两点的距离,单位米,该公式为GOOGLE提供,误差小于0.2米

#地球半径单位米 : private const double EARTH_RADIUS = 6378137;

public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
double radLat1 = Rad(lat1); #第一个GPS地址纬度
double radLng1 = Rad(lng1); #第二个GPS地址经度
double radLat2 = Rad(lat2);
double radLng2 = Rad(lng2);
double a = radLat1 - radLat2;
double b = radLng1 - radLng2;
double result = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2))) * EARTH_RADIUS;
return result;
}


# 经纬度转化成弧度
# @d:根据经度来取值
# 1、弧度的定义:弧长等于半径的弧,其所对的圆心角为1弧度 360°=2π弧度
# 1弧度约为57.3°,即57°17'44.806''
# 1°为π/180弧度,近似值为0.01745弧度.
# 2、经纬度的定义:纬度是以赤道为0度,经度是以英国伦敦附近的格林尼治天文台为0度.
# 3、换算:经纬度的度数/360=弧度/2π(3.14)
# 说明:经纬度前面的英文字母代表:西经W 东经E 北纬N 南纬S
# 北纬39度91分72秒=40度32分12秒(经纬度分秒是60进制,1°=60′,1′=60〃)=40.537°
# 方式1: 40.537° / 57.3°=0.708弧度.
# 方式2:(度 + 分/60 + 秒/3600) / 180 / 3.1415926

private static double Rad(double d)
{
return (double)d * Math.PI / 180d;
}


示例代码演示(Python):
+ Github代码查看

WeiyiGeek.代码案例

WeiyiGeek.代码案例

示例代码演示(C++):
https://github.com/exiv2/exiv2
C++参考:https://blog.csdn.net/u010431493/article/details/78964075


0x01 EXIF利用

利用1:使用WinHex来修改图片定位地址:
修改GPS数据,比如这里修改成114.146288,22.440669(114°8’46.6368”,22°26’26.4084”)转换成16进制纬度为72.H 8.H 2e.H,经度(16.H 1a.H 1a.H)接下来写入,测试GPS经纬已经变化,原图检验也过了。

WeiyiGeek.测试

WeiyiGeek.测试

那我们如何鉴别图片是否被修改?
答:由于Exif信息是可以被任意编辑的,因此可以利用JPEGsnoop或者MagicEXIF工具判断Exif是原始的还是被修改过的(工具修改大多会留下痕迹),不过这种辨别方式也只能作为参考


利用2:exif之反社工利用
既然EXIF信息能修改,比如可以在别人社工之前提前修改好图片定位可以迷惑下对方,也可以在图片中注入xss,如果对方通过浏览器一些插件查看则会被触发,因此存在一定限制条件可以做为一个手法结合其他姿势搞搞。