MP3 Fuzz学习
Posted Ox9A82
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MP3 Fuzz学习相关的知识,希望对你有一定的参考价值。
这篇文章主要是学习一波MP3格式fuzz的知识。目录如下
0x0.MP3格式的构成
0x0.MP3格式的构成
MP3是一种通俗叫法,学名叫MPEG1 Layer-3。MP3是三段式的结构,依次由ID3V2、Frame、ID3V1构成。其中Frame为帧,这是MP3格式的基本单位,一个个的帧构成了整个MP3文件。先来看结构最简单的ID3V1,这个结构的大小是固定的,共128字节。可见都是一些MP3的相关信息如作者、标题之类的。
typedef struct tagID3V1 { char Header[3]; //固定为"TAG" char Title[30]; //标题 char Artist[30]; //作者 char Album[30]; //专辑 char Year[4]; //出品年代 char Comment[28]; //备注 char reserve; //保留 char track; //音轨 char Genre; //类型 }ID3V1,*pID3V1;
接下来是存放数据的Frame帧结构。其中FrameHeader一共4个字节共32位,这32位是当作标志来用的,就是说每个位对应不同的意义。其中CRC域有可能不存在,这是有FrameHeader中的位决定的。而且FrameHeader中的帧头记录的MainData的大小。
typedef struct _FRAME { char FrameHeader[4];//每个帧都有一个4字节的帧头 char CRC[2]; //帧头第16字节决定是否存在CRC char MainData[?]; //帧头决定大小 }FRAME,*PFRAME;
FrameHeader定义为AAAAAAAAAAA BB CC D EEEE FF G H II JJ K L MM,不同字母代表不同意义的域。以下是这些域的意义。
A-11 帧同步 B-2 表示版本,有1、2、2.5三种 C-2 表示Layer版本,即mp1、mp2、mp3 D-1 决定是否有CRC E-4 表示比特率 F-2 表示采样率 G-1 表示填充位 H-1 保留 I-2 表示声道 J-2 立体声的扩展模式 K-1 是否有版权信息 L-1 是否是原创 M-2 强调
前面已经说过了Frame的大小是由FrameHeader来决定的,
下面来看下ID3V2标签,ID3V2标签是MP3文件的第一个标签,由一个标签头和若干个标签帧构成。其中标签头如下所示
typedef struct tagID3V2 { char Header[3];//固定为"ID3" char Ver; //记录版本号,3表示ID3V2.3 char Revision; //副版本号,这里为0 char Flag; //定义标识位 char Size[4]; //定义标签大小,包括这10个字节 }ID3V2,*PID3V2;
Size算法是每个字节取后7位来组成28位数字,作为大小的值。
typedef struct Header { char FrameID[4];//帧标识,说明这个帧的含义 char Size[4]; //帧内容的大小,不包括帧头,不得小于1 char Flags[2]; //存放标识 }HEADER;
Size是32位的大小值
ID3V2的大小只取每个字节的前位进行计算,作为ID3V2块的总大小进行计算。
如上所示每个ID3V2所属的标签还有其他的标签头,每个小标签头所表示的标签具有一定的含义,含义由FrameID决定,下面列出了一些常见的FrameID。size的计算法就是当成4个字节的DWORD来计算的。Flag和FrameID更多的值可以在附件的PDF里面看。
1 TIT2 标题 2 TPE1 作者 3 TALB 专集 4 TRCK 音轨 5 TYER 年代 6 TCON 类型 7 COMM 备注
0x1.MP3格式的脆弱点
前面主要是介绍了一下MP3文件的文件格式,这里在就要对MP3文件格式进行FUZZING之前要搞清楚的一点是MP3文件的脆弱点在哪里,就是说哪里是可能出现问题的地方,搞清楚这一点对于Fuzzing样本的生成也有很大的好处。而且有一个问题就是MP3的帧大小问题。我们前面已经知道了一个MP3是由许多帧构成的,但是注意帧头中并没有size域,因为一个帧的大小是由比特率和采样率算出来的,而比特率和采用率储存在帧头中分别为E、F。计算公式如下。
帧长度(字节)= 每帧采样数 / 采样率(HZ) * 比特率(bps)/8 + 填充 例:LayerIII 比特率 128000,采样率 44100,填充0 =〉帧大小 417字节
这样来说还有一个简单的公式(144*比特率)/采样率+填充位
注意,比特率是以k为单位的,比如128是128k的比特率。
|
MPEG1 |
MPEG2 |
MPEG2.5 |
Layer1 |
384 |
384 |
384 |
Layer2 |
1152 |
1152 |
1152 |
Layer3 |
1152 |
576 |
576 |
【每帧采【每帧采样数表】
这样就有一个问题了,我们要不要对E、F进行随机生成呢?如果也对E、F进行随机生成那么帧大小就会乱掉了。但是如果不随机生成的话,那么这几个域就固定了。这点我还没想明白怎么搞的。
下面提供了一个解析MP3文件操作的步骤
解析方法
当你想读取MPEG文件的信息时,解析前三个字节,判断是否有ID3V2标签,有则根据上面的方法算出ID3V2标签的总大小,这样就找到了音频数据帧的第一帧,读取它的头信息,获取比特率、采样率、MPEG版本号、Layer描述号等信息,根据上面提供的方法算出
每帧的长度和每帧持续时间,对于定比特率的其它帧是相同的,也就是说解析第一帧就达到了目的。但这也不是所有情况。变比特率的MPEG文件使用使用所谓比特变换,也就是说每一帧的比特率依照具体内容变化。这时就需要你每一帧都解析。
这个解析步骤是从网上看到的,我不知道有哪些的解码器是用这种方法解码的,但是我们可以根据上面提供的这个操作猜测出一些信息。首先IDV2的size标签域不能随意生成,因为如果随意生成这个域的话,那么就没有办法定位到第一帧了,那么这个mp3文件就整个乱掉的了,对于这种情况估计播放器会直接说文件损坏根本起不到测试的效果了。与此类似的是数据帧的比特率和采样率也不能随意生成,因为这两个值是用来计算数据帧大小用的,如果随意生成那么帧的大小就会计算错误,根据上面的解析方法可以解析的方法就是逐大小往下移,如果一个帧的大小计算不对,那么整个mp3的解析都会出现问题。这样同样就是无法达到测试的效果的。但是如果都是动态生成呢?我觉得当然是可以的了,但是数据的逻辑关系就太复杂了。尤其是很蛋疼的有一个按位的运算,这个直接用标签好像难以实现的,应该需要内嵌pyhon脚本来实现。目前还不知道该怎么搞,所以先生成固定长度的内容。
以上是关于MP3 Fuzz学习的主要内容,如果未能解决你的问题,请参考以下文章
为啥将拆分为 wav 文件的旋律转换为拆分的 mp3 会在片段边界处产生不好的声音?