Decompressing/inflating zlib-compressed 没有 adler32 校验和的数据

Decompressing/inflating zlib-compressed data without adler32 checksum

更新 2(最新)

情况如下:

一个外国应用程序正在以这种格式存储 zlib 压缩(压缩)数据:

78 9C BC (...data...) 00 00 FF FF - 我们称它为 DATA1

如果我使用原始 XML 文件并在 Java 或 Tcl 中将其压缩,我得到:

78 9C BD (...data...) D8 9F 29 BB - 我们称它为 DATA2

  1. DATA2 中的最后 4 个字节肯定是 Adler-32 校验和,在 DATA1 中被替换为 zlib FULL-SYNC 标记(为什么?我不知道)。
  2. 第 3 个字节的值不同 1。
  3. DATA1 和 DATA2 之间的 (...data...) 相等。
  4. 现在是最有趣的部分:如果我更新 DATA1,将第 3 个字节从 BC 更改为 BD,则保留最后 8 个字节不变(所以0000FFFF) 并用 new Inflater(true) (https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/zip/Inflater.html#%3Cinit%3E(boolean)) 膨胀此数据,我能够正确解码它! (因为这种模式下的Inflater不需要zlib Adler-32 checksum和zlib header)
问题:
  1. 为什么将 BC 更改为 BD 有效?在所有情况下都安全吗?我检查了几个案例,每次都工作。
  2. 为什么任何应用程序都会输出 BC 的不正确 (?) 缩小值?
  3. 为什么应用程序会以 zlib header (78 9C) 开头,但不会生成兼容的 zlib 结构(FLUSH-SYNC 而不是 Adler-32)?它不是一个小型的业余爱好应用程序,而是一个广泛使用的商业应用程序(我会说有数十万商业用户)。
### 更新 1(旧)

经过进一步分析,我似乎有一个 zlib-compressed 字节数组错过了最终校验和 (adler32)。

根据 RFC 1950,正确的 zlib 格式必须以 adler32 校验和结尾,但出于某种原因,我使用的数据集有 zlib 字节,缺少该校验和。它总是以 00 00 FF FF 结尾,在 zlib 格式中是 SYNC FLUSH 的标记。一个完整的zlibobject,后面应该有adler32,但是有none.

这样的数据应该还是可以膨胀的吧?

如前所述(在下面的原始问题中),我试图将此字节数组传递给 Java inflater(我也尝试过使用 Tcl 中的一个),但没有成功。生成这些字节的应用程序能够以某种方式正确读取它(如下文所述)。

如何解压?

原始问题,更新前:

上下文

有一个应用程序(封闭源代码)连接到 MS SQL 服务器并将压缩的 XML 文档存储在 image 类型的列中。此应用程序 - 在请求时 - 可以将文档导出到本地磁盘上的常规 XML 文件,因此我可以直接在数据库中访问纯文本 XML 数据以及压缩数据.

问题

我希望能够使用我自己的连接到 SQL 服务器的代码解压缩此列中的任何值。

问题是它是某种奇怪的 zlib 格式。它确实以典型的 zlib header 字节 (78 9C) 开头,但我无法解压缩它(我使用了 Java Decompress a string compressed with zlib deflate 中描述的方法)。

整个数据看起来像789CBC58DB72E238...7E526E7EFEA5E3D5FF0CFE030000FFFF(当然,点意味着里面更多的字节——总共1195)。

我已经尝试过的

引起我注意的是结尾0000FFFF,但即使我截断它,解压仍然失败。我实际上试图解压缩它从末尾截断所有字节数(在循环中,每次迭代切掉最后一个字节) - none 迭代也有效。

我还将原始 XML 文件压缩成 zlib 字节,看看它看起来如何,除了 2 个 zlib header 字节,然后可能还有 5-6 个字节,剩下的的数据是不同的。输出字节数也不同(更小),但不多(大约 1180 对 1195 字节)。

deflate 方面的不同之处在于,外部应用程序正在使用 Z_SYNC_FLUSHZ_FULL_FLUSH 将目前提供的数据刷新到压缩流中。您正在(正确地)使用 Z_FINISH 结束流。在第一种情况下,您最终会得到一个未终止且没有检查值的部分放气流。相反,它只是以一个空的存储块结束,这导致最后的 00 00 ff ff 字节。在第二种情况下,您最终会得到一个完整的 deflate 流和一个带有检查值的 zlib 预告片。在这种情况下,恰好只有一个deflate块(数据肯定比较小),所以第一个块是最后一个块,并用1标记为第一个字节的低位.

  1. 您正在做的是在第一个块上设置最后一个块位。这通常并不总是有效,因为流可能有多个块。在这种情况下,需要设置流中间的其他一些位。

  2. 我猜您得到的是部分压缩数据,但不是全部。到目前为止,有一个允许传输数据的刷新,但通常随后会继续压缩和更多这样的刷新数据包。

  3. (与#2相同的问题,相同的答案。)