文章目录
前奏
上次做了那个CTFBlink之后,对.jpg图片挺好奇的,于是准备来研究一下
jpg图片对比
jpg图片格式详解
JPEG图片格式组成部分:SOI(文件头)+APP0(图像识别信息)+DQT(定义量化表)+SOF0(图像基本信息)+DHT(定义Huffman表)+DRI(定义重新开始间隔)+SOS(扫描行开始)+EOI(文件尾)
JPEG文件的每个段都一定包含两部分一个是段的标识,它由两个字节构成:第一个字节是十六进制0xFF,第二个字节对于不同的段,这个值是不同的。
紧接着的两个字节存放的是这个段的长度(除了前面的两个字节0xFF和0xXX,X表示不确定。它们是不算到段的长度中)。
段的一般结构
名称 | 字节数 | 数据 | 说明 |
---|---|---|---|
段标识 | 1 | FF | 每个新段的开始标识 |
段类型 | 1 | 类型编码(称作“标记码”) | |
段长度 | 2 | 包括段内容和段长度本身,不包括段标识和段类型 | |
段内容 | <=65533字节 |
段类型
段类型 | 标记码 | 说明 |
---|---|---|
SOI | D8 | 文件头 |
EOI | D9 | 文件尾 |
SOF0 | C0 | 帧开始(标准 JPEG) |
SOF1 | C1 | 同上 |
DHT | C4 | 定义Huffman表(霍夫曼表) |
SOS | DA | 扫描行开始 |
SOS | DB | 定义量化表 |
DRI | D0 | 定义重新开始间隔 |
APP0 | E0 | 定义交换格式和图像识别信息 |
COM | FE | 注释 |
说明:有的文章也将DNL段(标记码=DC,定义扫描行数)列为必须段
PS:段类型有30种,但只有10种是必须被所以程序识别的,其它的类型都可以忽略
SOI文件头
JPEG文件的开始2个字节都是FF D8这是JPEG协议规定的
名称 | 字节数 | 值 |
---|---|---|
段标识 | 1 | FF |
段类型 | 1 | D8 |
也就是这两个字节构成了JPEG文件头
APP0图像识别信息
APP0(图像识别信息)
名称 | 字节数 | 值 | 说明 |
---|---|---|---|
段标识 | 1 | FF | |
段类型 | 1 | E0 | |
段长度 | 2 | 0010 | 如果有RGB 缩略图就=16+3*n(以下为段内容) |
交换格式 | 5 | 4A46494600 | “JFIF”的ASCII码 |
主版本号 | 1 | ||
次版本号 | 1 | ||
密度单位 | 1 | 0=无单位;1=点数/英寸;2=点数/厘米 | |
X像素密度 | 2 | 水平方向的密度 | |
Y像素密度 | 2 | 垂直方向的密度 | |
缩略图X像素 | 1 | 缩略图水平像素数目 | |
缩略图Y像素 | 1 | 缩略图垂直像素数目 | |
(如果“缩略图X像素”和缩略图Y像素的值均>0,那么才有下面的数据) | |||
n=缩略图像素总数=缩略图X像素*缩略图Y像素 |
JFIF
是JPEG File Interchange Forma的缩写,即JPEG文件交换格式,另外还有TIFF等格式,很少用- “如果有RGB缩略图就=
16+3*n
”是什么意思呢?比如说“缩略图X像素”和“缩略图Y像素”的值均为48,就表示有一个48*48
像素的缩略图(n=48*48
),缩略图是24位真彩位图,用3个字节来表示一个像素,所以共占有3*n
个字节。但大多数JPG文件都没有这个缩略图
- 图像识别信息头:2个字节 FF E0
- 段长度:00 10 =>16个字节
- 交换格式:4A 46 49 46 00=>此处为’JFIF’,一般我们用的都是JFIF的jpeg交换格式,但是也有TFIF的jpeg的交换格式,如果camera sensor直接输出的是jpeg图片,那么在其sensor寄存器可以设置使用JFIF还是TFIF
- 主版本号和次版本号一共2个字节:01 01 =>说明主版本号和次版本号都为1
- 单位密度1个字节:00=>表示此处使用的是无单位
- X像素密度2个字节:00 01 =>水平像素密度是1
- Y像素密度2个字节:00 01 =>水平像素密度是1
- 缩略图X像素:00=>没有缩略图
- 缩略图Y像素:00=>没有缩略图
- RGB缩略图:此处没有缩略图,所以是空的
DQT定义量化表
DQT(定义量化表)
名称 | 字节数 | 值 | 说明 |
---|---|---|---|
段标识 | 1 | FF | |
段类型 | 1 | DB | |
段长度 | 2 | 43 | 其值=3+n(当只有一个QT时)(以下为段内容) |
QT信息 | 1 | 0~3 :QT号 ;4~7 位:QT精度(0=8bit,1字节;否则=16bit,2字节) | |
QT | n | n=64*QT精度的字节数 |
说明:
- JPEG文件一般有2个DQT段,为Y值(亮度)定义1个,为C值(色度)定义1个
- 一个DQT段可以包含多个QT,每个都有自己的信息字节
此处有两个DQT数据,第一个是亮度的,第二个是色度的
- 定义量化表的头2个字节:FF DB
- 段长度2个字节: 00 43=>3(段长度2个字节,QT信息1个字节)+QT量化表的长度,此处QT量化表的长度是64
- QT信息1个字节:00=>0-3位是QT号,4-7位QT精度,此处是0,所以精度是8bit,即1个字节
- QT量化表:这个长度时根据QT信息确定的,上面QT精度为8bit,所以此处是64*1=64个字节
- 定义量化表的头2个字节:FF DB
- 段长度2个字节: 00 43=>3(段长度2个字节,QT信息1个字节)+QT量化表的长度,此处QT量化表的长度是64
- QT信息1个字节:01=>0-3位是QT号,即QT号是1,4-7位QT精度,此处是0,所以精度是8bit,即1个字节
- QT量化表:这个长度时根据QT信息确定的,上面QT精度为8bit,所以此处是64*1=64个字节
SOF0图像基本信息
SOF0(图像基本信息)
名称 | 字节数 | 值 | 说明 |
---|---|---|---|
段标识 | 1 | FF | |
段类型 | 1 | C0 | |
段长度 | 2 | 其值=8+组件数量*3 (以下为段内容) | |
样本精度 | 1 | 8 | 每个样本位数(大多数软件不支持12和16) |
图片高度 | 2 | ||
图片宽度 | 2 | ||
组件数量 | 1 | 3 | 1=灰度图,3=YCbCr/YIQ,4=CMYK 彩色图(以下每个组件占用3个字节) |
组件 ID | 1 | 1=Y,2=Cb,3=Cr,4=I,5=Q | |
采样系数 | 1 | 0-3位:垂直采样系数 ;4-7位:水平采样系数 | |
量化表号 | 1 |
说明:
- JPEG大都采用yCrCb色彩模型(y表示亮度,Cr表示红色分量,Cb表示蓝色分量),所以组件数量一般=3
- 样本就是单个像素的颜色分量,也可理解为一个样本就是一个组件
- 采样系数是实际采样方式与最高采样系数之比,而最高采样系数一般=0.5(分数表示为1/2)。比如说,垂直采样系数=2,那么
2*0.5=1
,表示实际采样方式是每个点采一个样,也就是逐点采样;如果垂直采样系数为1,那么:1*0.5=0.5
(分数表示为1/2),表示每两个点采一个样
- 图像基本信息:FF C2(不知道为什么我这里是C2,我查到的是C0)
- 段长度2个字节:00 11 =>17=8+3*3,说明组件数量有3个
- 样本精度1个字节:08,每个样本的信息是8bit
- 样本高度2个字节:01 84 =>388图片高度与实际一致
- 样本宽度2个字节:02 6C =>620图片宽度与实际一致
- 组件数量1个字节:03=>代表YCbCr彩色图,有三个组件分别是Y,Cb,Cr
- 每个组件占用3个字节:第一个字节是组件ID,第二个字节是采样系数,第三个字节是量化表号
此处是:
- 01 11 00=>Y组件,垂直采样系数和水平采样系数都是1,量化标号是0
- 02 11 01=>Cb组件,垂直采样系数和水平采样系数都是1,量化标号是1
- 03 11 01=>Cr组件,垂直采样系数和水平采样系数都是1,量化标号是1
//此处可知三个组件都是隔点采样(标准的YUV422
数据:Y采样是逐点采样,CbCr都是隔点采样)
DHT定义huffman表
DHT(定义Huffman表)
名称 | 字节数 | 值 | 说明 |
---|---|---|---|
段标识 | 1 | FF | |
段类型 | 1 | DB | |
段长度 | 2 | 其值=19+n(当只有一个HT表时)(以下为段内容) | |
HT信息 | 1 | 0-3:HT号;4位:HT类型,0=DC表,1=AC表;5-7位:必须=0 | |
HT位表 | 16 | 这16个数的和应该<=256 | |
HT值表 | n | n=表头16个数的和 |
说明:
- JPEG文件里有2类Haffman表:一类用于DC(直流量),一类用于AC(交流量)。一般有4个表:亮度的DC和AC,色度的AC和DC。最多可有6个
- 一个DHT段可以包含多个HT表,每个都有自己的信息字节
- HT表是一个按递增次序代码长度排列的符号表
- 定义Huffman表头2个字节:FF C4
- 段长度2个字节:00 1D=>29=19(段长度2字节+HT信息1个字节+HT位表16个字节)+10(这个数代表HT表有10字节)
- HT信息1个字节:0-3位是HT号,4位是HT类型(0=DC表,1=AC表),5-7位必须为0。HT号位0,此表为DC表
- HT位表16个字节:这10个数字值和小于等于256
01 00 02 02 03 01 01 00 00 00 00 00 00 00 00 00=>共16个字节,加起来10(此处和段长度是相匹配的),说明HT表有10字节
- HT值表:
00 07 08 01 06 02 04 05 09 03
- 定义Huffman表头2个字节:FF C4
- 段长度2个字节:00 1C=>28=19(段长度2字节+HT信息1个字节+HT位表16个字节)+9(这个数代表HT表有9字节)
- HT信息1个字节:0-3位是HT号,4位是HT类型(0=DC表,1=AC表),5-7位必须为0。HT号位1,此表为AC表
- HT位表16个字节:这10个数字值和小于等于256
01 00 01 05 01 01 00 00 00 00 00 00 00 00 00 00=>共16个字节,加起来9(此处和段长度是相匹配的),说明HT表有9字节
- HT值表:
00 01 02 03 04 05 07 06 08
SOS扫描行开始(图像数据开始)
SOS扫描行开始
名称 | 字节数 | 值 | 说明 |
---|---|---|---|
段标识 | 1 | FF | |
段类型 | 1 | DA | |
段长度 | 2 | 000C | 其值=6+2*扫描行内组件数量 (以下为段内容) |
扫描行内组件数量 | 1 | 3 | 必须>=1,<=4(否则错误),通常=3(以下每个组件占用2字节) |
组件ID | 1 | 1=Y,2=Cb,3=Cr,4=I,5=Q | |
Huffman表号 | 1 | 0-3位:AC表号(其值=0……3);4-7位:DC表号(其值=0……3) | |
剩余3个字节 | 3 | 最后3个字节用途不明,忽略 |
说明:
- 紧跟SOS段后的是压缩的图像数据(一个个扫描行),数据存放顺序是从左到右,从上到下(经过压缩算法处理过后的)。
- 扫描行开始的头:FF DA
- 段长度2个字节:00 0C=>12=6(2个字节的段长度+1个字节扫描行内组件数量+3个字节的剩余位)+2*3(扫描行内组件数量,每个组件2个字节)
- 扫描行内组件数量,1个字节:03=>代表组件数量数3
- 每个组件占用2个字节:
第一个字节是组件ID(1=Y,2=Cb,3=Cr,4=I,5=Q);第二个字节0-3AC表,4-7位是DC表。表号的值是0-3
01 00=>Y组件,AC表号是0,DC表号是0
02 10=>Cb组件,AC表号是0,DC表号是1
03 10=>Cr组件,AC表号是0,DC表号是1
EOI扫描行结尾
名称 | 字节数 | 值 |
---|---|---|
段标识 | 1 | FF |
段类型 | 1 | D9 |
这两个字节构成了JPEG文件尾
其他段
COM(注释)
名称 | 字节数 | 值 | 说明 |
---|---|---|---|
段标识 | 1 | FF | |
段类型 | 1 | FE | |
段长度 | 2 | 其值=注释字符的字节数+2 | |
段内容 | 注释字符 |
说明:有的JPEG文件没有这个段
DRI(定义重新开始间隔)
名称 | 字节数 | 值 | 说明 |
---|---|---|---|
段标识 | 1 | FF | |
段类型 | 1 | DD | |
段长度 | 2 | 4(以下为段内容) | |
开始间隔 | 2 | n | 复位标记的间隔距离 |
说明:
- 开始间隔表示在压缩数据流中,每隔n个MCU块就有一个RST标记,RST标记将Huffman的解码数据流复位,DC也重新从0开始,因此,RST标记是一种复位标记
- RST标记是一种特殊的段,它只具有段标识和段类型(长度=2字节),但它不是独立的段,只能穿插在数据流中(文件头和文件尾也只有段标识和段类型,却都是独立的段)
- RST标记共有8个(RST0-RST7),从RST0起开始使用,然后是RST1……RST7,再从RST0重复
- RST标记的标识码是FFD0-FFD7,对应RST0-RST7
- 补充:这个不是必须段,很多JPEG都没有
JPEG压缩编码实例
DC是指直流系数,是8X8个像素的平均值;AC是交流系数,是8X8个像素的其它值,压缩数据的排列方式是:亮度DC,AC,色差DC,AC,色差DC,AC
- 每个分量如Y分量(DC+AC)完成后,如果还剩下位数,应该舍弃,后面的Cb是从下一个字节重新计算
- 如果编码到后面没有压缩数据了,后面实际编码数据用0填充
- 如果编码已经完成,那么剩余压缩数据用1填充
红色色块编码
51 45 00 14 51 45 00 7F
01010001 01000101 00000000 00010100 01010001 01000101 00000000 01111111
计算Y亮度分量=>
0101-0001 01-0001-01
首先有一个DC系数需要计算出来
DC亮度
计算第一个色差分量 =>
在这里插入代码片
黑色色块编码
28 A2 8A 00 28 A2 8A 00
00101000 10100010 10001010 00000000 00101000 10100010 10001010 00000000
对比结果
开头:
结尾
省略部分分析
至F0行开始就是SOS扫描行,一直往下扫描,被省略的是B30行~4E60行
,全是扫描行数据(也就是说全是图像数据),且加密数据,
所以我们尝试一下SOS扫描行开始段结束这里,加一个扫描行结束标志FF D9
(那么图片肯定是可以显示出来的(也就是说可以打开),因为它的格式框架是正确的,只是没有内容而已)
然后保存
猜想正确,直接连马赛克都没了。所以的话,马赛克出现的原因只是因为绘图未完整而已(至于怎么个未完整法,因为是加密数据………………)