天天看點

修複損壞的gzip壓縮檔案方法之實用篇

在上一篇中已知,修複一個損壞的gzip檔案的關鍵環節在于找到下一個正常壓縮包的起始點。根據結構圖中的資訊可知,每個壓縮包的開始結構中有是否到達尾部标志、使用的哈夫曼樹類型、以及3個哈夫曼樹的樹元素個數等。如果某個gzip檔案中間有一個壞扇區,要找到壞扇區後的一個正常起點,僅需按位右移,一直移位到可以正常解壓的某個位,就可能找到了正确的壓縮包起始。而根據gzip檔案的壓縮作業視窗為32KB大小推算,這個周遊不會超過64KB即可找到。在記憶體中快速循環可以很快找到,但需要有明确的判斷錯誤的方法。

首先可以明确的是結尾标志,應該為0(我們是從損壞的點向後查)。而哈夫曼樹類型也大緻應該是動态哈夫曼(0x02),cl1的元素個數應該取值為257到286之間(包含邊界),cl2的元素個數應小于等于30,ccl的元素個數取值可為1-15(包含邊界)。

其實,還可以參考的東西有,解開的哈夫曼樹是否異常,或者通過規律性原則找到最後一個取值為256的值,但這些算法應該是較為麻煩的,有上面的算法連續校驗幾個壓縮塊就足夠了。

具體方法是對gzip的源碼做修改,進行周遊。因時間關系,未做成通用工程,僅快速修改了部分代碼。大緻的修改點為:

一,找到損壞點:

在unzip.c中,

error("invalid compressed data--format violated");

這一行前,擷取目前解碼位元組位置即可。

二、周遊找到損壞點:

1、inflate.c檔案中,改

if (nl > 286 || nd > 30)
#endif
return 1;
           

為:

if (nl > 286 || nd > 30||nl <257 || nd <1)
#endif
return 1;
           

2、inflate.c檔案中,在int inflate_block(e)函數中

在如下代碼前

bb = b;
bk = k;
           

加入代碼:

if ((t != 2) || (*e != 0))
    return 2;
           

3、inflate.c檔案中,在int inflate_block(e)函數尾部

把if (t == 0) 與if (t == 1)的情況都直接傳回錯誤值2。

4、inflate.c檔案中,函數int inflate()中,改

if ((r = inflate_block(&e)) != 0)
   return r;
end
           

為:

unsigned t;           /* block type */
register ulg b;       /* bit buffer */
register unsigned k;  /* number of bits in bit buffer */
while (inptr <= insize)
{
    unsigned int tptr = inptr;
    unsigned int tbk = bk;
    unsigned long tbb = bb;
    unsigned int twp = wp;
    long long tstart = *(long long*)(inbuf + tptr);
    if ((r = inflate_block(&e)) != 0)
    {
        inptr = tptr;
        bb = tbb;
        bk = tbk;
        wp = twp;
        b = bb;
        k = bk;
        NEEDBITS(1)
        DUMPBITS(1)
    }
    else
    {
        printf("get by www.datahf.net!"); //也可輸出tstart,bb,bk 值,轉載時請保留版權資訊:www.datahf.net張宇
    }
} 
           

此4步完成後,試着調試這個錯誤的.gz檔案,當然,也可以在代碼中解釋完頭部結構後加一個seek,直接seek到損壞位置。

通常情況下,輸出printf(“get by www.datahf.net!”)這行代碼時,已經找到了正确的起始位。

找到起始位後,也可以構造或拷貝一個正常的gzip檔案頭,再拼接好找到的位流,即可解壓了。(如果位流不是位元組對齊的,可能要全部做位移)。拼接後很多壓縮檔案就可以打開甚至于解壓了,不過,有可能會報錯,主要是尾部的校驗和大小錯,其實可以忽略。

如果拼接好了linux下,不能直接用“gzip –d”解壓,因其crc有錯誤,會導緻解壓到99%後報錯,然後把檔案删除,換成管道指令即可:

繼續閱讀