如何在 2013 zlib API 界面中模仿(被黑)1998 uncompress() 上的 "use_crc" 标志?

How to mimic a "use_crc" flag on (hacked) 1998 uncompress() in 2013 zlib API interface?

我正在将一个项目的代码从 1998 版的 zlib 更新到 2013 版的 zlib。似乎发生了变化的一件事是,解压缩函数上曾经有一个 "use_crc" 标志,现在似乎不见了:

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

(UPDATE: 正如@Joe 所指出的,这很可能是 third-party modification。标题相应更新。剩下的问题是仍然适用,如 "how should I best do this with today's stock zlib".)

在我研究的代码中,uncompress() 被解构 .zip 的二进制格式并传入 "payload" 数据的东西调用。该代码一直将 crc 标志作为 1 传递。如果未使用该标志,它将获得 Z_DATA_ERROR (-3)。 (没有 use_crc 标志的 zlib 得到 Z_DATA_ERROR 就好像该标志是假的一样。)

在实验中,我发现非常小的文件在没有 use_crc 的情况下也能正常工作。然后小计数文件在 "12345678901234""123456789012345" 之间交叉到 not-working。原因是:这是第一个被缩小而不是未压缩存储的文件 (zip 所谓的节省“6%”)

在考虑让 zlib 接受它的选项时,我尝试了很多方法。这包括尝试 16 + MAX_WBITS。似乎没有什么能像旧代码那样处理 zip test.zip test.txt 中的有效负载。

如果我愿意从我的目标大小中减去一个,我似乎能够抑制错误检查...在丢失一个字节的情况下。这是带有硬编码的最小 zip 负载的简单测试程序:

#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] = '[=13=]';

    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;
}

输出为:

successful inflation: 123456789012345X

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

知道出了什么问题吗?似乎没有任何设置排列可以毫无错误地到达那里。

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

从来没有 use_crc 参数

你举的例子是一个two-byte zlib header, deflate-compressed数据,big中deflate数据的CRC-32 -endian 顺序,后跟 little-endian 顺序的 four-byte 长度。这是 zlib 和 gzip 包装器的真正奇怪的混搭,与您一直提到的 zip 格式没有任何关系。 ("payloads inside of zip files" 是什么意思?)zlib 在末尾有一个 big-endian 顺序的 Adler-32,而 gzip 有一个 little-endian 顺序的 CRC-32,后跟 four-byte little-endian 顺序的长度。这个把它们混在一起,包括字节顺序,然后故意误导性地在上面放一个有效的 zlib header,这是对这个世界上所有善良和体面的东西的侮辱。

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

为了对此进行解码,您需要:

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

  2. 使用raw deflate,用inflateInit2(&strm, -15)初始化,解压缩数据。解压缩时,跟踪总长度并使用 crc32().

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

  5. 读取接下来的四个字节和 assemble little-endian 顺序的字节,并将其与未压缩数据的长度进行比较。如果不匹配,那就是流损坏了,或者不是你想的那样。

  6. 如果数据没有到此结束,则说明还有其他奇怪的事情正在发生。请教喝醉的人