【问题标题】:zipfile.BadZipFile: Bad CRC-32 when extracting a password protected .zip & .zip goes corrupt on extractzipfile.BadZipFile:提取受密码保护的 .zip 时出现错误的 CRC-32,并且 .zip 在提取时损坏
【发布时间】:2023-03-03 19:38:01
【问题描述】:

我正在尝试提取一个受密码保护的 .zip,它有一个 .txt 文档(在这种情况下说 Congrats.txt)。现在Congrats.txt 中有文本,因此它的大小不是 0kb。它放在一个 .zip 中(为了这个线程,我们将这个 .zip 命名为zipv1.zip),密码为dominique,为了这个线程。该密码与其他单词和名称一起存储在另一个 .txt 文件中(为了这个问题,我们将其命名为 file.txt)。

现在,如果我通过执行python Program.py -z zipv1.zip -f file.txt(假设所有这些文件与Program.py 在同一个文件夹中)运行下面的代码,我的程序会将dominique 显示为zipv1.zip 的正确密码以及其他词/ file.txt 中的密码并提取 zipv1.zipCongrats.txt 为空且大小为 0kb。

现在我的代码如下:

import argparse
import multiprocessing
import zipfile

parser = argparse.ArgumentParser(description="Unzips a password protected .zip", usage="Program.py -z zip.zip -f file.txt")
# Creates -z arg
parser.add_argument("-z", "--zip", metavar="", required=True, help="Location and the name of the .zip file.")
# Creates -f arg
parser.add_argument("-f", "--file", metavar="", required=True, help="Location and the name of file.txt.")
args = parser.parse_args()


def extract_zip(zip_filename, password):
    try:
        zip_file = zipfile.ZipFile(zip_filename)
        zip_file.extractall(pwd=password)
        print(f"[+] Password for the .zip: {password.decode('utf-8')} \n")
    except:
        # If a password fails, it moves to the next password without notifying the user. If all passwords fail, it will print nothing in the command prompt.
        pass


def main(zip, file):
    if (zip == None) | (file == None):
        # If the args are not used, it displays how to use them to the user.
        print(parser.usage)
        exit(0)
    # Opens the word list/password list/dictionary in "read binary" mode.
    txt_file = open(file, "rb")
    # Allows 8 instances of Python to be ran simultaneously.
    with multiprocessing.Pool(8) as pool:
        # "starmap" expands the tuples as 2 separate arguments to fit "extract_zip"
        pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])


if __name__ == '__main__':
    main(args.zip, args.file)

但是,如果我使用与 zipv1.zip 相同的方法进行另一个压缩 (zipv2.zip),唯一的区别是 Congrats.txt 位于一个文件夹中,该文件夹与 Congrats.txt 一起压缩,我确实得到与 @987654352 相同的结果@ 但这次Congrats.txt 沿着它所在的文件夹解压缩,而Congrats.txt 完好无损;里面的文字和大小都完好无损。

所以为了解决这个问题,我尝试阅读zipfile's documentation,我发现如果密码与.zip 不匹配,它会抛出RuntimeError。所以我确实将代码中的except: 更改为except RuntimeError: 并在尝试解压缩zipv1.zip 时出现此错误:

(venv) C:\Users\USER\Documents\Jetbrains\PyCharm\Program>Program.py -z zipv1.zip -f file.txt
[+] Password for the .zip: dominique

multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 121, in worker
result = (True, func(*args, **kwds))
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 47, in starmapstar
return list(itertools.starmap(args[0], args[1]))
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 16, in extract_zip
zip_file.extractall(pwd=password)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1594, in extractall
self._extract_member(zipinfo, path, pwd)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1649, in _extract_member
shutil.copyfileobj(source, target)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\shutil.py", line 79, in copyfileobj
buf = fsrc.read(length)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 876, in read
data = self._read1(n)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 966, in _read1
self._update_crc(data)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 894, in _update_crc
raise BadZipFile("Bad CRC-32 for file %r" % self.name)
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 38, in <module>
main(args.zip, args.file)
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 33, in main
pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 276, in starmap
return self._map_async(func, iterable, starmapstar, chunksize).get()
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 657, in get
raise self._value
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'

同样的结果也会发生;密码在file.txt 中找到,zipv1.zip 被提取但Congrats.txt 为空且大小为 0kb。所以我再次运行程序,但这次是zipv2.zip,结果得到了这个:

(venv) C:\Users\USER\Documents\Jetbrains\PyCharm\Program>Program.py -z zipv2.zip -f file.txt
[+] Password for the .zip: dominique

multiprocessing.pool.RemoteTraceback:
"""
Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 121, in worker
result = (True, func(*args, **kwds))
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 47, in starmapstar
return list(itertools.starmap(args[0], args[1]))
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 16, in extract_zip
zip_file.extractall(pwd=password)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1594, in extractall
self._extract_member(zipinfo, path, pwd)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 1649, in _extract_member
shutil.copyfileobj(source, target)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\shutil.py", line 79, in copyfileobj
buf = fsrc.read(length)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 876, in read
data = self._read1(n)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 966, in _read1
self._update_crc(data)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\zipfile.py", line 894, in _update_crc
raise BadZipFile("Bad CRC-32 for file %r" % self.name)
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 38, in <module>
main(args.zip, args.file)
  File "C:\Users\USER\Documents\Jetbrains\PyCharm\Program\Program.py", line 33, in main
pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 276, in starmap
return self._map_async(func, iterable, starmapstar, chunksize).get()
  File "C:\Users\USER\AppData\Local\Programs\Python\Python37\lib\multiprocessing\pool.py", line 657, in get
raise self._value
zipfile.BadZipFile: Bad CRC-32 for file 'Congrats.txt'

同样的结果;其中文件夹已成功提取,Congrats.txt 也已提取,其中包含文本,并且其大小完好无损。

我确实看过this 类似的线程,以及this 线程,但它们没有帮助。我还检查了zipfile's documentation,但对这个问题没有帮助。

编辑

现在由于一些未知和奇怪的原因实现了with zipfile.ZipFile(zip_filename, 'r') as zip_file:;该程序可以读取/处理一个小的单词列表/密码列表/字典,但如果它很大(?)则不能。

我的意思是说zipv1.zip中存在一个.txt文档;命名为Congrats.txt,文本为You have cracked the .zip!zipv2.zip 中也存在相同的 .txt,但这次放在名为 ZIP Contents 的文件夹中,然后进行压缩/密码保护。两个拉链的密码都是dominique

请注意,每个 .zip 都是使用 Deflate 压缩方法和 ZipCrypto 加密在 7zip 中生成的。

现在密码在Line 35(35/52 行)John The Ripper Jr.txtLine 1968 中用于John The Ripper.txt(1968/3106 行)。

现在,如果您在 CMD(或您选择的 IDE)中执行 python Program.py -z zipv1 -f "John The Ripper Jr.txt";它将创建一个名为Extracted 的文件夹并将Congrats.txt 与我们之前设置的句子放在一起。 zipv2 也是如此,但 Congrats.txt 将位于 ZIP Contents 文件夹中,该文件夹位于 Extracted 文件夹内。在这种情况下,提取 .zip 没有问题。

但是,如果您在 CMD(或您选择的 IDE)中使用 John The Ripper.txtpython Program.py -z zipv1 -f "John The Ripper.txt" 尝试相同的操作,它将在两个 zip 中创建 Extracted 文件夹;就像John The Ripper Jr.txt 一样,但这次Congrats.txt 将出于某种未知原因对他们俩都

我的代码和所有必要的文件如下:

import argparse
import multiprocessing
import zipfile

parser = argparse.ArgumentParser(description="Unzips a password protected .zip by performing a brute-force attack.", usage="Program.py -z zip.zip -f file.txt")
# Creates -z arg
parser.add_argument("-z", "--zip", metavar="", required=True, help="Location and the name of the .zip file.")
# Creates -f arg
parser.add_argument("-f", "--file", metavar="", required=True, help="Location and the name of the word list/password list/dictionary.")
args = parser.parse_args()


def extract_zip(zip_filename, password):
    try:
        with zipfile.ZipFile(zip_filename, 'r') as zip_file:
            zip_file.extractall('Extracted', pwd=password)
            print(f"[+] Password for the .zip: {password.decode('utf-8')} \n")
    except:
        # If a password fails, it moves to the next password without notifying the user. If all passwords fail, it will print nothing in the command prompt.
        pass


def main(zip, file):
    if (zip == None) | (file == None):
        # If the args are not used, it displays how to use them to the user.
        print(parser.usage)
        exit(0)
    # Opens the word list/password list/dictionary in "read binary" mode.
    txt_file = open(file, "rb")
    # Allows 8 instances of Python to be ran simultaneously.
    with multiprocessing.Pool(8) as pool:
        # "starmap" expands the tuples as 2 separate arguments to fit "extract_zip"
        pool.starmap(extract_zip, [(zip, line.strip()) for line in txt_file])


if __name__ == '__main__':
    # Program.py - z zipname.zip -f filename.txt
    main(args.zip, args.file)

Program.py

zipv1.zip

zipv2.zip

John The Ripper Jr.txt

John The Ripper.txt

John The Ripper v2.txt

我不确定为什么会发生这种情况,也无法在任何地方找到此问题的答案。据我所知,它完全未知,我找不到调试或解决此问题的方法。

无论单词/密码列表如何,这种情况都会继续发生。尝试使用相同的Congrats.txt 生成更多.zip,但使用来自不同单词列表/密码列表/字典的不同密码。同样的方法;使用了较大和较小版本的 .txt,并获得了与上述相同的结果。

但是我确实发现如果我删掉John The Ripper.txt 中的前2k 个单词并制作一个新的.txt;说John The Ripper v2.txt; .zip 解压成功,Extracted 文件夹出现,Congrats.txt 出现,其中包含文本。所以我相信它与密码后的行有关。所以在这种情况下Line 1968; Line 1968 之后脚本在哪里没有停止?我不确定为什么这会起作用。我猜这不是解决方案,而是迈向解决方案的一步......

编辑 2

所以我尝试使用“池终止”代码:

import argparse
import multiprocessing
import zipfile

parser = argparse.ArgumentParser(description="Unzips a password protected .zip by performing a brute-force attack using", usage="Program.py -z zip.zip -f file.txt")
# Creates -z arg
parser.add_argument("-z", "--zip", metavar="", required=True, help="Location and the name of the .zip file.")
# Creates -f arg
parser.add_argument("-f", "--file", metavar="", required=True, help="Location and the name of the word list/password list/dictionary.")
args = parser.parse_args()


def extract_zip(zip_filename, password, queue):
    try:
        with zipfile.ZipFile(zip_filename, "r") as zip_file:
            zip_file.extractall('Extracted', pwd=password)
            print(f"[+] Password for the .zip: {password.decode('utf-8')} \n")
            queue.put("Done")  # Signal success
    except:
        # If a password fails, it moves to the next password without notifying the user. If all passwords fail, it will print nothing in the command prompt.
        pass


def main(zip, file):
    if (zip == None) | (file == None):
        print(parser.usage)  # If the args are not used, it displays how to use them to the user.
        exit(0)
    # Opens the word list/password list/dictionary in "read binary" mode.
    txt_file = open(file, "rb")

    # Create a Queue
    manager = multiprocessing.Manager()
    queue = manager.Queue()

    with multiprocessing.Pool(8) as pool:  # Allows 8 instances of Python to be ran simultaneously.
        pool.starmap_async(extract_zip, [(zip, line.strip(), queue) for line in txt_file])  # "starmap" expands the tuples as 2 separate arguments to fit "extract_zip"
        pool.close()
        queue.get(True)  # Wait for a process to signal success
        pool.terminate()  # Terminate the pool
        pool.join()


if __name__ == '__main__':
    main(args.zip, args.file)  # Program.py -z zip.zip -f file.txt.

现在,如果我使用它,两个 zip 都将成功提取,就像之前的实例一样。 但是这次zipv1.zipCongrats.txt完好无损;里面有消息。但不能说zipv2.zip 仍然是空的。

【问题讨论】:

  • 你能分享你的 ZIP 文件吗?
  • zipv1 zipv2 两者都是使用 7zip 生成的,因为 WinRAR 压缩文件不会使用 zipfile 提取(如果受密码保护)。 7zip中使用Deflate作为压缩方式,ZipCrypto作为加密方式。
  • 我能够在两个 .zip 文件上成功运行代码,无一例外,文件被提取,使用一个虚拟 file.txt 组成12 行(其中一行显然是 dominique)。
  • 一个朋友说的和他自己做了几十个单词的wordlist/.txt一样;一个是dominique 当然。然后我告诉他用我使用的单词表 (Link) 尝试同样的事情,他告诉我当找到密码时他确实得到了一个空的 .txt。所以这个问题与 wordlist/file.txt 的长度有关。我尝试在找到密码后立即终止多进程,如here 所示,但它没用......
  • 我也倾向于 subprocess 假设。但是我能够使用新的 .txt 文件运行程序,我什至用 将它加倍(也尝试了 0、1、2 次出现 dominique) Python 3.63.7 32/64,(支持 f 字符串),但它仍然作品。 :(

标签: python extract zip crc32


【解决方案1】:

抱歉,停顿了这么久……看来你有点吃不消了。

回顾

  • 处理受密码保护的 .zip 文件
  • 使用文件中的密码尝试暴力破解 (ciobaneste)
  • 正确的密码在(上一步)文件中,但尽管如此,有些文件没有正确解压

1。调查

场景很复杂(我想说,与 MCVE 相去甚远),有很多事情可以归咎于这种行为。

zipv1.zip / zipv2.zip 不匹配开始。仔细一看,zipv2 似乎也搞砸了。如果 zipv1 很容易发现(Congrats.txt 是唯一的文件),对于 zipv2"ZIP Contents/Black -Large.png" 的大小为 0
它可以用任何文件重现,等等:它适用于 zf.namelist 返回的 1st 条目(不是目录)强>。

所以,事情开始变得更清楚了:

  • 正在解压文件内容,因为 dominique 存在于密码文件中(不知道在那之前会发生什么)
  • 稍后,.zip 的 1st 条目被截断为 0 个字节

查看尝试使用错误密码提取文件时抛出的异常,有3种类型(其中最后2种可以组合在一起):

  1. RuntimeError: 文件密码错误...
  2. 其他:
    • zlib.error:解压缩数据时出现错误 -3 ...
    • zipfile.BadZipFile:文件的 CRC-32 错误 ...

我创建了自己的存档文件。为了一致性起见,我将从现在开始使用它,但所有内容也适用于任何其他文件。

  • 内容:
    • DummyFile0.zip (10 字节) - 包含:0123456789
    • DummyFile1.zip10 个字节) - 包含:0000000000
    • DummyFile2.zip10 个字节) - 包含:AAAAAAAAAA
  • 使用 Total Commander (9.21a) 内部 zip 打包程序将 3 个文件存档,并使用 dominique进行密码保护>(zip2.0 加密)。生成的存档(命名为 arc0.zip(但名称不相关))为 392 个字节长

code.py

#!/usr/bin/env python3

import sys
import os
import zipfile


def main():
    arc_name = sys.argv[1] if len(sys.argv) > 1 else "./arc0.zip"
    pwds = [
        #b"dominique",
        #b"dickhead",
        b"coco",
    ]
    pwds = [item.strip() for item in open("orig/John The Ripper.txt.orig", "rb").readlines()]
    print("Unpacking (password protected: dominique) {:s},"
          " using a list of predefined passwords ...".format(arc_name))
    if not os.path.isfile(arc_name):
        raise SystemExit("Archive file must exist!\nExiting.")
    faulty_pwds = list()
    good_pwds = list()
    with zipfile.ZipFile(arc_name, "r") as zip_file:
        print("Zip names: {:}\n".format(zip_file.namelist()))
        for idx, pwd in enumerate(pwds):
            try:
                zip_file.extractall("Extracted", pwd=pwd)
            except:
                exc_cls, exc_inst, exc_tb = sys.exc_info()
                if exc_cls != RuntimeError:
                    print("Exception caught when using password ({:d}): [{:}] ".format(idx, pwd))
                    print("    {:}: {:}".format(exc_cls, exc_inst))
                    faulty_pwds.append(pwd)
            else:
                print("Success using password ({:d}): [{:}] ".format(idx, pwd))
                good_pwds.append(pwd)
    print("\nFaulty passwords: {:}\nGood passwords: {:}".format(faulty_pwds, good_pwds))


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

输出

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q054532010]> "e:\Work\Dev\VEnvs\py_064_03.06.08_test0\Scripts\python.exe" code.py arc0.zip
Python 3.6.8 (tags/v3.6.8:3c6b436a57, Dec 24 2018, 00:16:47) [MSC v.1916 64 bit (AMD64)] on win32

Unpacking (password protected: dominique) arc0.zip, using a list of predefined passwords ...
Zip names: ['DummyFile0.txt', 'DummyFile1.txt', 'DummyFile2.txt']

Exception caught when using password (1189): [b'mariah']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid code lengths set
Exception caught when using password (1446): [b'zebra']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid block type
Exception caught when using password (1477): [b'1977']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid block type
Success using password (1967): [b'dominique']
Exception caught when using password (2122): [b'hank']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid code lengths set
Exception caught when using password (2694): [b'solomon']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid distance code
Exception caught when using password (2768): [b'target']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid block type
Exception caught when using password (2816): [b'trish']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid code lengths set
Exception caught when using password (2989): [b'coco']
    <class 'zlib.error'>: Error -3 while decompressing data: invalid stored block lengths

Faulty passwords: [b'mariah', b'zebra', b'1977', b'hank', b'solomon', b'target', b'trish', b'coco']
Good passwords: [b'dominique']

查看 ZipFile.extractall 代码,它尝试提取所有成员。 1st 引发了一个异常,因此它开始更清楚为什么它的行为方式如此。但是,为什么在尝试使用 2 个错误密码提取项目时会出现行为差异?
从 2 种不同抛出异常类型的回溯中可以看出,答案就在 ZipFile.open 的末尾。

经过更多调查,原来是因为一个

2。 zip 加密弱点确定的冲突

根据[UT.CS]: dmitri-report-f15-16.pdf - Password-based encryption in ZIP files((最后)强调是我的):

3.1 传统PKWARE加密

最初的加密方案,通常称为 PKZIP 密码,由 罗杰·沙弗利 [1]。在 [5] 中,Biham 和 Kocher 证明了密码是弱的,并证明了 需要 13 字节明文的攻击。已经开发了进一步的攻击,其中一些 根本不需要用户提供明文[6]。 PKZIP 密码本质上是一种流密码,即通过生成伪密码对输入进行加密。 随机密钥流并将其与明文异或。密码的内部状态包括 三个 32 位字:key0key1key2。这些初始化为 0x123456780x234567890x34567890。该算法的核心步骤涉及使用 单字节输入...

...

在加密存档中的文件之前,首先将 12 个随机字节添加到其压缩文件中 内容和生成的字节流然后被加密。解密后,前 12 个字节 需要丢弃。根据规范,这样做是为了呈现明文 攻击数据无效。 规范还指出,在 12 个前置字节中,实际上只有前 11 个 随机,最后一个字节等于未压缩的CRC-32的高位字节 文件的内容。这提供了快速验证给定密码是否正确的能力 通过将解密的 12 字节报头的最后一个字节与实际的高位字节进行比较 包含在本地文件头中的 CRC-32 值。这可以在解密之前完成 文件的其余部分。

其他参考资料:

算法的弱点:由于仅对一个字节进行微分,对于 256 个不同(并且经过仔细选择) 是错误的密码,会有一个(至少)会生成与正确密码相同的数字

该算法会丢弃大部分错误密码,但也有一些不会。

返回:尝试使用密码提取文件时:

  • 如果根据文件密码的最后一个字节计算的“哈希”与文件 CRC 的高位字节不同,则会引发异常
  • 但是,如果它们相等:
    • 一个新的文件流已打开以供写入(如果文件已存在,则清空该文件)
    • 尝试解压缩:
      • 密码错误(已通过上述检查),解压失败(但文件已清空)

从上面的输出中可以看出,对于我的 (.zip) 文件,有 8 个密码搞砸了。请注意:

  • 每个存档文件的结果都不同
  • member 文件名和内容是相关的(至少对于第一个st 而言)。更改其中任何一个都会产生不同的结果(对于“相同”的存档文件)

这是一个基于我的 .zip 文件中的数据的测试:

>>> import zipfile
>>>
>>> zd_coco = zipfile._ZipDecrypter(b"coco")
>>> zd_dominique = zipfile._ZipDecrypter(b"dominique")
>>> zd_other = zipfile._ZipDecrypter(b"other")
>>> cipher = b'\xd1\x86y ^\xd77gRzZ\xee'  # Member (1st) file cipher: 12 bytes starting from archive offset 44
>>>
>>> crc = 2793719750  # Member (1st) file CRC - archive bytes: 14 - 17
>>> hex(crc)
'0xa684c7c6'
>>> for zd in (zd_coco, zd_dominique, zd_other):
...     print(zd, [hex(zd(c)) for c in cipher])
...
<zipfile._ZipDecrypter object at 0x0000021E8DA2E0F0> ['0x1f', '0x58', '0x89', '0x29', '0x89', '0xe', '0x32', '0xe7', '0x2', '0x31', '0x70', '0xa6']
<zipfile._ZipDecrypter object at 0x0000021E8DA2E160> ['0xa8', '0x3f', '0xa2', '0x56', '0x4c', '0x37', '0xbb', '0x60', '0xd3', '0x5e', '0x84', '0xa6']
<zipfile._ZipDecrypter object at 0x0000021E8DA2E128> ['0xeb', '0x64', '0x36', '0xa3', '0xca', '0x46', '0x17', '0x1a', '0xfb', '0x6d', '0x6c', '0x4e']
>>>  # As seen, the last element of the first 2 arrays (coco and dominique) is 0xA6 (166), which is the same as the first byte of the CRC

我用其他解包引擎(使用默认参数)做了一些测试:

  1. WinRar:密码错误时文件不会被修改,但密码错误会被截断(与此处相同)
  2. 7-Zip:询问用户是否覆盖文件,无论解压结果如何都会覆盖
  3. Total Commander 的内部 (zip) 解包器:与 #2 相同。

3。结论

  • 我认为这是一个 zipfile 错误。指定此类错误(和错误)密码不应覆盖现有文件(如果有)。或者至少,行为应该是一致的(对于所有错误的密码)
  • 快速浏览并没有发现 Python 上的任何错误
  • 我没有看到一个简单的解决方法,因为:
    • zip 算法无法改进(以便更好地检查密码是否正常)
    • 我想到了几个修复方法,但它们要么会对性能产生负面影响,要么会在某些(极端)情况下引入回归

我已提交 [GitHub]: python/cpython - [3.6] bpo-36247: zipfile - extract truncates (existing) file when bad password provided (zip encryption weakness),其中 已关闭分支 3.6(仅在 安全修复中 模式)。不确定它会产生什么结果(在其他分支中),但无论如何,它不会很快推出(比如说在接下来的几个月里)。

作为替代方案,您可以下载补丁并在本地应用更改。查看[SO]: Run/Debug a Django application's UnitTests from the mouse right click context menu in PyCharm Community Edition? (@CristiFati's answer)Patching utrunner 部分)了解如何在 Win 上应用补丁(基本上,每一行都以 one 开头) +" 符号进入,以 一个 "-" 符号开头的每一行都退出)。我正在使用 Cygwinbtw
如果你想保留你的 Python 安装原始文件。

【讨论】:

  • 所以你告诉我的是我的问题是由 zipfile 本身引起的,对吧?无论我的代码是(或将是)什么,由于 zipfile 的行为方式,它总是会破坏我的 zips/extracts?如果是这样的话,还有其他选择吗?
  • 是的。核心问题在于 zip 压缩核心本身,结果是 zipfile (和其他工具)处理该问题的方式。但是我的回答还不够清楚吗?无论如何,我找到了解决方案,将其提交给Python,但遇到了各种流程和文档问题。很快就会更新答案。
  • 用您的修复程序here 进行了尝试,它可以工作!谢谢 如果您的编辑被接受,我认为它将在下一个 Python 上运行,对吗?不知道这些事情进展如何,因为我认为自己是 Python 的新手/业余爱好者。另外,如果我复制zipfile.py,我不应该将其命名为与文件相同的名称,对吗? zippy.py 而不是 zipfile.py 对吗?
  • 正如我所说,它可能会被拒绝。如果被接受,它将在下一个版本中发布(没有检查何时会发布,但我认为这需要一些时间)。同时,您可以在本地解决您的问题。从我担心的角度来看,可以修改您的原始 zipfile.py (如果您之前备份过它),这样如果事实证明我的修复引入了一些令人讨厌的问题(我不认为是这样)你可以恢复它。或者,您可以安全起见,将文件复制到项目中并在那里进行更改。我建议不要重命名它(例如 zippy 或其他名称),...
  • (继续)因为您必须修改所有导入它的文件。这就是为什么(但只有在你不确定的情况下,我仍然会修改原始文件),创建一个包含此类文件的目录,并仅在其中修改它们。然后确保目录位于 PYTHONPATH 中,以便在标准 Python 库中的同名模块之前加载模块。检查stackoverflow.com/questions/42705279/…。如果这回答了您的问题,请将其标记为解决方案。一个upvote也很好:) Thx。
【解决方案2】:

我遇到了这个问题,经过一番挖掘后发现,问题在于我选择的 zip 文件加密。我从 7-Zip 提供的默认值 ZipCrypto 更改为 AES-256,一切都很好。

【讨论】:

  • 能详细点吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多