如何在2013 zlib API接口中模仿(hacked)1998 uncompress()上的"use_crc"标志?

Hos*_*ork 6 c compression gzip zlib

我正在将项目的代码从1998版的zlib更新为2013版的zlib.似乎改变的一件事是,uncompress函数曾经有一个"use_crc"标志,似乎已经丢失了:

int ZEXPORT uncompress (dest, destLen, source, sourceLen, use_crc)
    Bytef *dest;
    uLongf *destLen;
    const Bytef *source;
    uLong sourceLen;
    int use_crc; // <-- vanished (?)
Run Code Online (Sandbox Code Playgroud)

(更新:正如@Joe所指出的,这很可能是第三方修改.标题也相应更新.问题的其余部分仍然适用,例如,"我应该如何利用今天的股票zlib做到最好".)

在我正在研究的代码中,uncompress()被解析为.zip的二进制格式并传入数据的"有效负载".代码已经将crc标志传递为1.如果未使用该标志,则会获得Z_DATA_ERROR(-3).(没有use_crc标志的zlib获得Z_DATA_ERROR,就像标志为false一样.)

在实验中,我发现非常小的文件没有use_crc.然后小的计数文件交叉到"12345678901234"和之间不工作"123456789012345".原因是:这是第一个被放气而不是存储未压缩的文件(在什么拉链称为"6%"的节省)

在挣扎着让zlib接受它的选项时,我尝试了很多东西.这包括尝试16 + MAX_WBITS.似乎没有任何东西像zip test.zip test.txt那样处理旧有代码的方式.

如果我愿意从我的目的地大小中减去一个,我似乎能够抑制错误检查......丢失一个字节.这是简单的测试程序,最小拉链有效负载硬编码:

#include <stdio.h>
#include "zlib.h"

int main(int argc, char *argv[]) {
    char compressed[] = { 0x78, 0x9C, 0x33, 0x34, 0x32, 0x36, 0x31, 0x35, 0x33,
        0xB7, 0xB0, 0x34, 0x30, 0x04, 0xB1, 0xB8, 0x00, 0x31, 0x30, 0xB1, 0x30,
        0x10, 0x00, 0x00, 0x00 }; // last 4 bytes are size (16)

    char uncompressed[16 + 1]; // account for null terminator
    int ret; z_stream strm;

    memset(uncompressed, 'X', 16);
    uncompressed[16] = '\0';

    strm.zalloc = strm.zfree = strm.opaque = Z_NULL;
    strm.total_out = 0;
    strm.avail_in = 25;
    strm.next_in = compressed;

    ret = inflateInit2(&strm, MAX_WBITS /* + 16 */); // it is Z_OK

    strm.avail_out = 15; // 16 gives error -3: "incorrect header check" 
    strm.next_out = uncompressed;
    ret = inflate(&strm, Z_NO_FLUSH);

    if (ret != /* Z_STREAM_END */ Z_OK) { // doesn't finish... 
        printf("inflate() error %d: %s\n", ret, strm.msg);
        return 2;
    }

    inflateEnd(&strm);
    printf("successful inflation: %s\n", uncompressed);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

成功通胀:123456789012345X

显示数据正在解压缩,但我们需要所有16个字节. (应该接收的文件中有一个换行符.) 16 + MAX_WBITS甚至无法获得.

有什么想法会出错吗?没有设置的排列似乎没有错误.

Mar*_*ler 5

不,自 20 多年前推出以来,zlib 接口没有发生不兼容的更改。从来没有use_crc争论过uncompress()

你给的例子是一个两字节的zlib头,DEFLATE压缩数据,该CRC-32在放气数据的-endian顺序,随后在一个四字节的长度-endian顺序。这是 zlib 和 gzip 包装器的真正奇怪的混搭,与您一直提到的 zip 格式没有任何关系。(您的意思是“zip 文件中的有效负载”是什么意思?)zlib 在大端顺序的末尾有一个 Adler-32,而 gzip 有一个小端顺序的 CRC-32,后跟一个四字节长度的小端-端序。这个混淆了这些,包括字节顺序,然后故意误导性地将一个有效的 zlib 标头放在这个东西上,这是对这个世界上所有美好和体面的侮辱。

我很确定当时提出这种格式的人喝醉了。

为了解码这个,你需要:

  1. 丢弃流的前两个字节。(您可以检查它是否是有效的 zlib 标头,但结果证明在解释流的其余部分时毫无意义。)

  2. 使用原始 deflate,用 , 初始化inflateInit2(&strm, -15)来解压数据。解压缩时,请跟踪总长度并使用 计算 CRC-32 crc32()

  3. deflate 数据完成后,读取接下来的四个字节,按大端顺序将它们组合成 32 位值,并将其与您计算的 CRC-32 进行比较。如果不匹配,则该流已损坏,或者它不是这些格式奇怪的流之一。(也许再试一次,将其解码为普通的 zlib 流。如果它有一个好的 zlib 标头,那么也许这就是它的实际情况,而不是这些 Frankenstein 流之一。)

  4. 读取接下来的四个字节并按小端顺序组装它们,并将其与未压缩数据的长度进行比较。如果不匹配,则流已损坏,或者不是您所想的。

  5. 如果数据没有在这里结束,那么其他奇怪的事情正在发生。咨询醉酒的人。

  • 拜托,拜托,请不要修改 zlib 来处理这种暴行,如果你这样做了,看在上帝的份上,永远不要分发这样的东西。正如我所描述的,zlib 可以按原样使用来处理这些数据。 (3认同)