zlib的是與周圍的 DEFLATE算法壓縮的數據的簡單封裝和在RFC1950定義:
A zlib stream has the following structure:
0 1
+---+---+
|CMF|FLG| (more-->)
+---+---+
(if FLG.FDICT set)
0 1 2 3
+---+---+---+---+
| DICTID | (more-->)
+---+---+---+---+
+=====================+---+---+---+---+
|...compressed data...| ADLER32 |
+=====================+---+---+---+---+
所以它增加了至少兩個,可能六個字節之前和4個字節的 ADLER32原始DEFLATE壓縮數據後的校驗和。
第一字節包含CMF(壓縮方法和標誌),其被劃分 成CM(壓縮方法)(前4位)和CINFO(壓縮信息)(最後 4比特) 。
由此可以明顯看出,zlib流的前兩個字節 已經很不一樣了,具體取決於使用了哪些壓縮方法和設置。
幸運的是,我偶然發現了ADLER32 算法的作者Mark Adler的帖子,他在那裏他lists the most common and less common combinations of those two starting bytes。
有了這樣的方式,讓我們來看看我們如何可以使用Python來檢查的zlib:
>>> import zlib
>>> msg = 'foo'
>>> [hex(ord(b)) for b in zlib.compress(msg)]
['0x78', '0x9c', '0x4b', '0xcb', '0xcf', '0x7', '0x0', '0x2', '0x82', '0x1', '0x45']
所以(使用默認選項)通過Python的zlib
模塊創建zlib的數據與 78 9c
開始。我們將使用它來創建一個腳本,該腳本編寫一個自定義文件格式 ,其中包含一個序言,一些zlib壓縮數據和一個頁腳。
然後我們寫掃描兩個字節模式文件的第二個腳本, 開始解壓遵循作爲zlib的流計算出 一切,當流結束,頁腳開始。
create.py
import zlib
msg = 'foo'
filename = 'foo.compressed'
compressed_msg = zlib.compress(msg)
data = 'HEADER' + compressed_msg + 'FOOTER'
with open(filename, 'wb') as outfile:
outfile.write(data)
下面我們就來msg
,與zlib的壓縮,並與頁眉和頁腳 我們寫出來的文件之前,圍繞着它。
頁眉和頁腳是在本例中固定長度的,但它們當然可以 具有任意的,未知的長度。
現在對於試圖在這樣的文件中找到zlib的流腳本。因爲 這個例子中,我們確切地知道標記期望我使用的只有一個,但顯然 名單ZLIB_MARKERS
可填充有從上述 後所有標記。
ident.py
import zlib
ZLIB_MARKERS = ['\x78\x9c']
filename = 'foo.compressed'
infile = open(filename, 'r')
data = infile.read()
pos = 0
found = False
while not found:
window = data[pos:pos+2]
for marker in ZLIB_MARKERS:
if window == marker:
found = True
start = pos
print "Start of zlib stream found at byte %s" % pos
break
if pos == len(data):
break
pos += 1
if found:
header = data[:start]
rest_of_data = data[start:]
decomp_obj = zlib.decompressobj()
uncompressed_msg = decomp_obj.decompress(rest_of_data)
footer = decomp_obj.unused_data
print "Header: %s" % header
print "Message: %s" % uncompressed_msg
print "Footer: %s" % footer
if not found:
print "Sorry, no zlib streams starting with any of the markers found."
的想法是這樣的:
,找到流的末尾不如尋找兩個標記 字節微不足道。 zlib流既不是由固定字節序列終止的,也不是在任何頭部字段中指示的長度。取而代之的是,一個四字節的ADLER32校驗和終止於 ,這個校驗和必須匹配到目前爲止的數據。
它的工作方式是內部的C函數inflate()
汽車無保留 嘗試,因爲它讀取它來解壓縮流,如果它遇到一個 匹配校驗信號,要它的調用者,表明的休息 數據不再是zlib流的一部分。
在Python中,當使用解壓縮對象而不是簡單的 調用zlib.decompress()
時,會暴露此行爲。在Decompress
對象 上調用decompress(string)
將解壓縮string
中的zlib流並返回作爲流一部分的解壓縮數據。該流後面的所有內容都將存儲在unused_data
之後,並且可以在之後檢索到 。
這應該產生與所述第一 腳本創建的文件中的下列輸出:
Start of zlib stream found at byte 6
Header: HEADER
Message: foo
Footer: FOOTER
的例子可以很容易地被修改以寫入未壓縮消息到文件 不是打印它的。然後,您可以進一步分析以前的zlib 壓縮數據,並嘗試識別 分隔出的頁眉和頁腳中元數據中的已知字段。
我理解你的問題的正確性:你沒有適當的文件格式規範,並且想知道反向工程技術來識別文件格式的結構和佈局嗎? –
@LukasGraf不要太看重我,但答案是......是的!這是我們公司採用這種文件格式的最後努力,但任何進展對我們都很重要。 – heltonbiker
好的,只是想確保我正確理解問題。我的意思不是聽起來很有道理:)但是,一旦你完成逆向工程的文件格式(這可能比實現本身需要更多的時間),Python可能只適用於實現的語言。 –