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

时间:2015-10-02 12:41:15

标签: c gzip zlib compression

我正在将项目代码从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 (?)

更新:正如@Joe所指出的,这可能是third-party modification。标题已相应更新。问题的其余部分仍然适用,如“我怎么能用今天的股票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;
}

输出结果为:

  

成功通胀:123456789012345X

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

任何想法出了什么问题?没有设置的排列似乎没有错误。

1 个答案:

答案 0 :(得分:4)

不,自从20多年前引入zlib接口以来,没有任何不兼容的更改。 use_crc没有uncompress()参数。

您给出的示例是一个双字节zlib标头,deflate-compressed数据,以 big -endian顺序排列的deflate数据的CRC-32,后跟一个四字节长度< em> little - 印度订单。这是zlib和gzip包装器的真正奇怪的混搭,并且与zip格式没有任何关系,你一直在提及。 (你的意思是“zip文件里面的有效负载”是什么意思?)zlib最后以big-endian顺序有一个Adler-32,而gzip有一个小端序的CRC-32,后面跟着一个四字节的长度 - 字节序。这个混合起来,包括字节排序,然后故意误导性地在事物上放置一个有效的zlib标题,这是对这个世界上所有好的和体面的冒犯。

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

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

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

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

  3. 在收缩数据完成后,读取接下来的四个字节,将它们以big-endian顺序组装成32位值,并将其与您计算的CRC-32进行比较。如果它不匹配,则流已损坏,或者它不是这些奇怪格式化的流之一。 (也许再试一次,把它解码为普通的zlib流。如果它有一个好的zlib头,那么可能就是它实际上是什么,而不是这些Frankenstein流中的一个。)

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

  5. 如果数据没有在此处结束,则会发生其他奇怪的事情。咨询醉酒的人。