忘記zip密碼咋辦?python在手密碼我有

整理資料時發現幾個 zip 文件的密碼忘記了,因而嘗試用python暴力破解python

首先是讀取和解壓zip文件,使用 zipfile 庫算法

import zipfile
z = zipfile.ZipFile(r'./file.zip')
z.extractall(pwd=password.encode('utf-8'))

定義一個密碼元字符串,每次從裏面取出一些字符,好比:app

meta_str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

隨機密碼

使用 random.sample 生成指定長度的密碼,而後出現過的密碼放入一個 setdom

choiced_set = set()
rand_str = ''.join(random.sample(meta_str, 4))
if rand_str not in choiced_set:
    choiced_set.add(rand_str)

爲了避免斷的產生密碼並嘗試解壓,此處應該有循環,那麼將嘗試解壓和生成密碼封閉爲函數:函數

import zipfile
import random

meta_str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
choiced_set = set()

def guess(z, password):
    try:
        z.extractall(pwd=password.encode('utf-8'))
        print('guessed pwd: {}'.format(password))
        return True
    except Exception:
        print('invalid password [{}]'.format(password))
        return False

def main():
    z = zipfile.ZipFile(r'./file.zip')
    while True:
        rand_str = ''.join(random.sample(meta_str, 4))
        if rand_str not in choiced_set:
            choiced_set.add(rand_str)
            if guess(z, rand_str):
                raise Exception('found')

此方法代碼裏固定寫了只支持4個字符長度,若密碼不是4個字符,則會無止境的循環下去,顯然不科學。
爲了支持n位密碼字符,需對代碼稍加修改:code

  1. 增長n位字符, for x in range(n) 能夠解決
  2. n位密碼長度的停止條件

密碼個數

因爲將生成的密碼所有放入set,因此只須要判斷set的長度,就知道當前生成了多少密碼。
而密碼字符能夠重複,因此'abcd' 可生成的2位密碼有:orm

'aa', 'ab', 'ac', 'ad',
'ba', 'bb', 'bc', 'bd',
'ca', 'cb', 'cc', 'cd',
'da', 'db', 'dc', 'dd,

至關於對字符串'abcd' 和 'abcd' 做笛卡爾積,那麼一共是 len('abcd') * len('abcd') = 16 個密碼,
因此n位密碼的個數就是 len(meta_str) ** nip

random.sample 不支持重複字符,替換爲 numpy.random.choice(sq, n, replace=True)utf-8

引入 numpy 並修改 main 函數:字符串

def main():
    z = zipfile.ZipFile(r'./file.zip')
    meta_str_len = len(meta_str)
    meta_str_list = list(meta_str)
    while True:
        for n in range(1, 5):
            while len(choiced_set) != meta_str_len ** n:
                rand_str = ''.join(np.random.choice(meta_str_list, n, replace=True))
                if rand_str not in choiced_set:
                    choiced_set.add(rand_str)
                    if guess(z, rand_str):
                        raise Exception('found')
            choiced_set.clear()

耗時

以上代碼雖然能夠正常運行,可是解一個4位長度的密碼須要計算 62 * 62 * 62 * 62 = 14776336 次,並且隨着 choiced_set 的增加,生成非重複密碼的機率愈來愈低,幾乎是一個不可能完成的任務。

字母順序密碼

若是按順序生成密碼,總有一個密碼能知足條件,好比: 元字符'abcd'生成2位密碼,是一個笛卡爾積,其實就是一個字符全排列的過程,因此這裏可使用深度優先算法生成字符

調用 dfs_str(0) 便可不斷生成 4 位長度的密碼
一個簡單的栗子:

def guess(v):
    if v == 'abcd':
        print('guessed pwd: {}'.format(v))
        return True
    else:
        print('invalid pwd: {}'.format(v))
        return False

meta_str = 'abcd1234'
chars = []
def dfs_str(step):
    if step >= 4:
        if guess(''.join(chars)):
            raise Exception('found')
        return
    for ch in meta_str:
        chars.append(ch)
        dfs_str(step + 1)
        chars.pop()

密碼生成器

使用這種方式 guess 函數處於深度優先搜索算法中,耦合緊密,那麼有沒有辦法一次生成一個密碼,而後傳給 guess 呢?固然是有的,這時候輪到生成器出場了
實現一個密碼生成器,只須要一些小的改動,在函數裏增長 yield 關鍵字

def dfs_str_gen(step):
    if step >= 4:
        yield ''.join(chars)
        return
    for ch in meta_str:
        chars.append(ch)
        yield from dfs_str_gen(step + 1)
        chars.pop()

密碼生成器類

上面的代碼已經限定了只能生成 4 位長度的密碼,然而咱們的密碼多是 1 2 3 4 5 甚至8位以上,難道每次都要手動修改嗎?或者是在 dfs_str_gen 裏傳入參數?
固然傳參能夠,離個人目標很近了,然而更好的辦法是包裝成一個 class, 初始化時傳入想要生成密碼的長度:

class PasswordGenerator:
    meta_str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    def __init__(self, n=4):
        self.maxlen = n
        self.endstr = self.meta_str[-1] * self.maxlen # 標記循環結束字符串
        self.chars = []
        self.g = self._proxy_gen()

    def _dfs_str(self, step):
        if step >= self.maxlen:
            yield ''.join(self.chars)
            return
        for ch in self.meta_str:
            self.chars.append(ch)
            yield from self._dfs_str(step + 1)
            self.chars.pop()

    def _proxy_gen(self):
      while True:
        yield from self._dfs_str(0)
    
    def __iter__(self):
        return self

    def __next__(self):
        s = self.g.send(None)
        if s == self.endstr:
            self.g.close()
        return s

調用 PasswordGenerator(n) 便可生成 n 位長度的密碼

itertools 生成器

寫了這麼多,其實標準庫 itertools 已經有了解決方案

import itertools
meta_str = 'abcdefg'
itertools.product(meta_str, repeat=4)
相關文章
相關標籤/搜索