GIF
{{#tree:
- Introduction
- Misc
- Crypto
- Web
- Assembly
- Executable
- Reverse
- Pwn
- Android
- ICS
- Blockchain
}}
GIF[編輯]
文件結構[編輯]
一個GIF文件的結構可分為
- 文件頭(File Header)
- GIF 文件署名(Signature)
- 版本號(Version)
- GIF 數據流(GIF Data Stream)
- 控制標識符
- 圖象塊(Image Block)
- 其他的一些擴展塊
- 文件終結器(Trailer)
下表顯示了一個 GIF 文件的組成結構:
[[File:./figure/gif.png]]
中間的那個大塊可以被重複任意次
文件頭[編輯]
GIF 署名(Signature)和版本號(Version)。GIF 署名用來確認一個文件是否是 GIF 格式的文件,這一部分由三個字符組成:GIF
;文件版本號也是由三個字節組成,可以為 87a
或 89a
。
邏輯屏幕標識符(Logical Screen Descriptor)[編輯]
Logical Screen Descriptor(邏輯屏幕描述符)緊跟在 header 後面。這個塊告訴 decoder(解碼器)圖片需要占用的空間。它的大小固定為 7 個字節,以 canvas width(畫布寬度)和 canvas height(畫布高度)開始。
全局顏色列表(Global Color Table)[編輯]
GIF格式可以擁有global color table,或用於針對每個子圖片集,提供local color table。每個color table由一個RGB(就像通常我們見到的(255,0,0)紅色 那種)列表組成。
圖像標識符(Image Descriptor)[編輯]
一個 GIF 文件一般包含多個圖片。之前的圖片渲染模式一般是將多個圖片繪製到一個大的(virtual canvas)虛擬畫布上,而現在一般將這些圖片集用於實現動畫。
每個 image 都以一個 image descriptor block(圖像描述塊)作為開頭,這個塊固定為 10 字節。
[[File:./figure/imagesdescription.png]]
圖像數據(Image Data)[編輯]
終於到了圖片數據實際存儲的地方。Image Data是由一系列的輸出編碼(output codes)構成,它們告訴decoder(解碼器)需要繪製在畫布上的每個顏色信息。這些編碼以字節碼的形式組織在這個塊中。
文件終結器(Trailer)[編輯]
該塊為一個單欄位塊,用來指示該數據流的結束。取固定值0x3b.
更多參見 gif 格式圖片詳細解析
空間軸[編輯]
由於GIF的動態特性,由一幀幀的圖片構成,所以每一幀的圖片,多幀圖片間的結合,都成了隱藏信息的一種載體。
對於需要分離的GIF文件,可以使用convert
命令將其每一幀分割開來
$ convert cake.gif cake.png
$ ls
cake-0.png cake-1.png cake-2.png cake-3.png cake.gif
例題[編輯]
WDCTF-2017:3-2
打開gif後,思路很清晰,分離每一幀圖片後,將起合併得到完整的二維碼即可
from PIL import Image
flag = Image.new("RGB",(450,450))
for i in range(2):
for j in range(2):
pot = "cake-{}.png".format(j+i*2)
potImage = Image.open(pot)
flag.paste(potImage,(j*225,i*225))
flag.save('./flag.png')
掃碼後得到一串16進制字符串
03f30d0ab8c1aa5....74080006030908
開頭03f3
為pyc
文件的頭,恢復為python
腳本後直接運行得到flag
時間軸[編輯]
GIF文件每一幀間的時間間隔也可以作為信息隱藏的載體。
例如在當時在XMan選拔賽出的一題
XMAN-2017:100.gif
通過identify
命令清晰的列印出每一幀的時間間隔
$ identify -format "%s %T \n" 100.gif
0 66
1 66
2 20
3 10
4 20
5 10
6 10
7 20
8 20
9 20
10 20
11 10
12 20
13 20
14 10
15 10
推斷 20 & 10
分別代表 0 & 1
,提取每一幀間隔並進行轉化。
$ cat flag|cut -d ' ' -f 2|tr -d '66'|tr -d '\n'|tr -d '0'|tr '2' '0'
0101100001001101010000010100111001111011001110010011011000110101001101110011010101100010011001010110010101100100001101000110010001100101011000010011000100111000011001000110010101100100001101000011011100110011001101010011011000110100001100110110000101100101011000110110011001100001001100110011010101111101#
最後轉 ASCII 碼得到 flag。