Z_DATA_ERROR中途膨胀

时间:2018-05-13 05:36:28

标签: c++ zlib compression deflate inflate

我需要解压缩游戏保存数据中的一些zlib压缩文件。我无法访问游戏的来源。每个文件都以0x789C开头,告诉我它们确实是用zlib压缩的。但是,对这些文件进行充气的所有调用都无法完全解压缩并返回Z_DATA_ERROR。使用zlib版本1.2.5,1.2.8和1.2.11具有相同的结果。

尽管zlib告诉我输入数据已损坏,但我确信它不会因为游戏能够解压缩这些文件而没有任何问题并且这不会被隔离到单个数据流。我以相同的方式压缩了数十万的唯一数据流,并且它们都在解压缩过程中的某处抛出Z_DATA_ERROR

此外,IS成功解压缩的部分解压缩数据是正确的。输出完全符合预期。

此外,大约10%的时间,zlib将解压缩整个文件,但结果不正确。大块的解压缩数据包含反复重复的相同字节,这告诉我这是一个误报。

这是我的减压代码:

//QByteArray is a Qt wrapper for a char *
QByteArray Compression::DecompressData(QByteArray data)
{
    QByteArray result;

    int ret;
    z_stream strm;
    static const int CHUNK_SIZE = 1;//set to 1 just for debugging
    char out[CHUNK_SIZE];

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = data.size();
    strm.next_in = (Bytef*)(data.data());

    ret = inflateInit2(&strm, -15);
    if (ret != Z_OK)
    {
        qDebug() << "init error" << ret;
        return QByteArray();
    }

    do
    {
        strm.avail_out = CHUNK_SIZE;
        strm.next_out = (Bytef*)(out);

        ret = inflate(&strm, Z_NO_FLUSH);
        qDebug() << "debugging output: " << ret << QString::number(strm.total_in, 16);//This tells me which input byte caused the failure
        Q_ASSERT(ret != Z_STREAM_ERROR);

        switch (ret)
        {
        case Z_NEED_DICT:
            ret = Z_DATA_ERROR;
        case Z_DATA_ERROR:
        case Z_MEM_ERROR:
            (void)inflateEnd(&strm);
            return result;
        }

        result.append(out, CHUNK_SIZE - strm.avail_out);
    } while (strm.avail_out == 0);

    inflateEnd(&strm);
    return result;
}

以下是示例文件的数据compressed data的pastebin,其中0x789C并删除了尾随CRC。我可以提供无穷无尽的示例文件。所有这些都有同样的问题。

通过上述函数运行该数据将正确解压缩流的开头,但输入字节0x18C失败。当文件的开头以0x000B开头并且解压缩的数据长于输入数据时,您可以正确地解压缩它。

我希望自己能更多地了解deflate压缩以解决这个问题。我最初的想法是游戏决定使用自定义版本的zlib,或者需要为zlib提供额外的参数才能正确解压缩。我已经问了好几天并尝试了很多东西,我真的需要有关于这个问题的知识的人在这里权衡。谢谢你的时间!

1 个答案:

答案 0 :(得分:2)

提供的数据确实是一个无效的deflate流,距离太远,以及deflate流结束后的8个字节的垃圾。你的代码没有明显的错误。

正如您所指出的那样,在偏移396处,十个距离中的第一个距离太远。膨胀停止的地方。在偏移量3472处,几乎在最后,存在一个存储块,其长度不会与其补码相对应。

对于距离太远,您可以尝试在inflateSetDictionary()之后使用inflateInit2()设置32K零字节的字典。然后进行解压缩,用零填充给定位置。这可能是也可能不是游戏正在做的事情。存储块错误没有明显的补救措施。

游戏作者确实可能会故意搞砸你或任何试图解压缩内部数据的人,修改zlib供自己使用。