這篇文章主要是學習一波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指令碼來實現。目前還不知道該怎麼搞,所以先生成固定長度的內容。