預備知識
PNG文件格式
8字節 → PNG文件頭
再往后就是第一個數據塊:數據塊由4字節的數據域長度,4字節的類型碼,指定長度(前面提到的數據域長度,這里IHDR就是0x0D個字節也即13字節)的數據,和4字節的CRC碼組成。
而IHDR的組成為:4字節寬度,4字節高度,1字節位深度,1字節顏色類型,1字節壓縮方法,1字節濾波方法,1字節隔行掃描方法。
對什么數據做CRC
crc就是對類型碼和數據域進行計算得到的
實際操作
以‘Buuoj 大白’為例
1. 題目提示“是不是屏幕太小了”,打開dabai.png時報錯:“解碼錯誤,不支持或無效的文件”所以應該是圖片寬高的錯誤導致校驗不通過。該圖片寬679,高256,應該要調整高度。
2. 所以我們應當以二進制形式讀入該png文件,然后采集下圖紅框所圈的部分數據,計算其CRC,將結果與后4字節的校驗碼進行比對。因為調整高度,所以只需對藍框中的數據進行修改。
3. 代碼如下。
1 import struct 2 import binascii 3 from Crypto.Util.number import bytes_to_long 4 5 img = open("dabai.png", "rb").read() 6 7 for i in range(0xFFFF): 8 stream = img[12:20] + struct.pack('>i', i) + img[24:29] 9 crc = binascii.crc32(stream) 10 if crc == bytes_to_long(img[29:33]): 11 print(hex(i))
高度上限應該不是很大, 這里只遍歷 0~0xFFFF。此外,因為img[12:20]等輸出的結果是bytes, 所以這里需要利用struct對整型數據格式化, 將其打包為字節流。另外格式符i意味着,4字節的整型。
而且默認是小端輸出,需要改成大端輸出。大小端輸出方式對比如下(以0x100為例):
以上代碼的輸出結果為`0x1df`,所以利用winhex將圖片高度部分對應的數據改為0001DF即可正常打開該PNG文件。
Reference
2. https://dev.gameres.com/Program/Visual/Other/PNGFormat.htm
3. https://blog.csdn.net/qq_30638831/article/details/80421019