从加密的 zip 存档中提取文件时,会要求用户输入密码才能读取原始文件。
加密的 ZIP 如何检测用户何时提供了正确的密码?
显然它没有与某些后端服务连接,并且它不包含要比较的实际密码。那么它究竟是如何检查的呢?存档中是否包含原始密码的一些哈希?很容易找到这个哈希吗?
从加密的 zip 存档中提取文件时,会要求用户输入密码才能读取原始文件。
加密的 ZIP 如何检测用户何时提供了正确的密码?
显然它没有与某些后端服务连接,并且它不包含要比较的实际密码。那么它究竟是如何检查的呢?存档中是否包含原始密码的一些哈希?很容易找到这个哈希吗?
循环冗余校验 (CRC) 字段用于确定文件是否被正确解密。引自原始ZIP 格式规范:
标头解密后,Buffer 中的最后 1 或 2 个字节应该是被解密文件的 CRC 的高位字/字节,以 Intel 低字节/高字节顺序存储。2.0 之前的 PKZIP 版本使用 2 字节 CRC 校验;2.0 之后的版本使用 1 字节 CRC 校验。这可以用来测试提供的密码是否正确。
更新:从Info-ZIP的unzip源码可以看出,CRC值是用来检查密码是否正确的:
https://github.com/LuaDist/unzip/blob/master/crypt.c#L617
#ifdef ZIP10 /* check two bytes */
c = hh[RAND_HEAD_LEN-2], b = hh[RAND_HEAD_LEN-1];
Trace((stdout,
" (c | (b<<8)) = %04x (crc >> 16) = %04x lrec.time = %04x\n",
(ush)(c | (b<<8)), (ush)(GLOBAL(lrec.crc32) >> 16),
((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff))));
if ((ush)(c | (b<<8)) != (GLOBAL(pInfo->ExtLocHdr) ?
((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff) :
(ush)(GLOBAL(lrec.crc32) >> 16)))
return -1; /* bad */
#else
b = hh[RAND_HEAD_LEN-1];
Trace((stdout, " b = %02x (crc >> 24) = %02x (lrec.time >> 8) = %02x\n",
b, (ush)(GLOBAL(lrec.crc32) >> 24),
((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff));
if (b != (GLOBAL(pInfo->ExtLocHdr) ?
((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff :
(ush)(GLOBAL(lrec.crc32) >> 24)))
return -1; /* bad */
#endif
/* password OK: decrypt current buffer contents before leaving */
for (n = (long)GLOBAL(incnt) > GLOBAL(csize) ?
(int)GLOBAL(csize) : GLOBAL(incnt),
p = GLOBAL(inptr); n--; p++)
zdecode(*p);
return 0; /* OK */
加密的 ZIP 如何检测用户何时提供了正确的密码?
显然它没有与某些后端服务连接,并且它不包含要比较的实际密码。那么它究竟是如何检查的呢?
简短的回答:大多数 zip 提取程序可能不检查密码。他们只是尝试解密数据,并可能检查它是否看起来像真实数据。Selcuk 的回答表明,一些程序可能会使用文件中包含的 CRC 来获得一定程度的数据正确解密的信心,但是从文本中的“应该”和“可以使用”来看,这听起来像是可选的(尽管鼓励) 用于使用 zip 格式的应用程序。
顺便说一下,这也可能允许在文件损坏的情况下进行(部分)数据恢复,因为应用程序可以简单地忽略 CRC 检查并从加密的 zip 中提取它可以从加密的 zip 中提取的内容,依靠用户找到坏文件(这可能与错误的密码取决于规范的其余部分是如何编写的)。