zip密码破解器中的多线程

Multithreading in zip password cracker

我正在学习如何使用字典攻击来破解 zip 文件。这是代码:

import zipfile
from threading import Thread

def extractFile(zFile, password):
    try:
        zFile.extractall(pwd=password)
        print '[+] Found password ' + password + '\n'
    except:
        pass

def main():
    zFile = zipfile.ZipFile('evil.zip')
    passFile = open('dictionary.txt')
    for line in passFile.readlines():
        password = line.strip('\n')
        extractFile(zFile, password)

if __name__ == '__main__':
    main()

我在上面使用线程

import zipfile
from threading import Thread

def extractFile(zFile, password):
    try:
        zFile.extractall(pwd=password)
        print '[+] Found password ' + password + '\n'
    except:
        pass

def main():
    zFile = zipfile.ZipFile('evil.zip')
    passFile = open('dictionary.txt')
    for line in passFile.readlines():
        password = line.strip('\n')
        t = Thread(target=extractFile, args=(zFile, password))
        t.start()

if __name__ == '__main__':
    main()

但是,当我对两个程序计时时,完成第一个需要 90 秒,而完成第二个需要将近 300 秒。该词典包含 459026 个词条。我对为什么会这样感到困惑。我还尝试将线程数限制为 10,20,依此类推。但是循环在每个实例中仍然执行得更快。谁能解释为什么会这样??还有没有机会改进这个程序。

编辑 我尝试按照 Ray 的建议进行切片,如下所示:

import zipfile
from threading import Thread

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

def extractFile(zFile, passwords):
    for password in passwords:
        try:
            zFile.extractall(pwd=password)
            print '[+] Found password ' + password + '\n'
            sys.exit(0)
        except:
            continue

def main():
    zFile = zipfile.ZipFile('evil.zip')
    with open('dictionary.txt', 'rb') as pass_file:
        passwords = [i.strip() for i in pass_file]
    passes = list(chunks(passwords, 10))
    for pas in passes:
        t = Thread(target=extractFile, args=(zFile, pas))
        t.start()

if __name__ == '__main__':
    main()

还需要 3-4 分钟

multiprocessing 这不能正常工作的一个原因;您必须在每个子进程中打开 zip 文件,否则您可能会因共享文件句柄而受到伤害。然后只创建少数(比如 2 * 核心数)子进程,并让单个子进程测试多个密码。

因此我们得到:

import zipfile
from multiprocessing import Process


def extract_file(passwords):
    with zipfile.ZipFile('evil.zip') as zipf:
        for password in passwords:
            try:
                zipf.extractall(pwd=password)
                print('[+] Found password {}\n'.format(password))
            except Exception as e:
                pass


def main():
    with open('dictionary.txt', 'rb') as pass_file:
        passwords = [i.strip() for i in pass_file]

    N_PROC = 8
    for i in range(N_PROC):
        p = Process(target=extract_file, args=[passwords[i::N_PROC]])
        p.start()


if __name__ == '__main__':
    main()

Can anybody explain why this is so??

我认为,除了全局解释器锁(GIL)的问题之外,您可能使用的线程不正确。

从循环判断,您正在为文件中的每个密码行启动一个全新的线程 - 即只是为了进行 单次 尝试。正如您所发现的那样,仅尝试一次就启动一个新线程是昂贵的,而且不会像您预期的那样工作。如果您使用 multiprocessing 执行此操作,那么它会更慢,因为创建一个全新的进程只是为了一次尝试比创建一个线程更昂贵。

Is there any chance to improve the program at all?

我建议你:

  • 将密码分成几个 sub-lists/groups(即切片)
  • 为每个子列表创建一个线程(或进程)
  • 让每个 thread/process 消耗一组(即进行多次尝试并从中获得更多)

例如,如果文件中有 100 行,您可以将其分成 4 部分(即每个子列表 25 个密码)并使用它们来提供 4 threads/processes(即每个子列表一个)子列表)。

在这里使用 multiprocessing 会比较有利,因为您可以避免 GIL。 但是,请记住您仍然有多个进程同时访问同一个文件,因此请确保在尝试提取文件等时考虑到这一点。

您应该注意不要让您的 PC 内核不堪重负。您可能想要使用进程池(请参阅 python 文档)并将您创建的进程数量限制为 PC 中的最大内核数量(可能 your_core_count - 1 以保持响应速度)。

然后,当每个进程消耗一个子列表并终止时,将创建一个新进程(或者重新分配现有进程,如果使用进程池)来处理队列中等待的另一个子列表。如果其中一个子进程成功完成,那么您可能希望让父进程杀死所有其他子进程以避免不必要的资源使用。