文章目錄
- 前言
- Python語法
- 自定義迭代器
- Python多執行緒
- Python腳本
- 單執行緒數字爆破
- 單執行緒字符爆破
- 多執行緒字典爆破
- 總結
前言
本文繼續記錄學習下 Python 的有趣應用:借助 Python 腳本暴力破解 ZIP 加密檔案的密碼,雖然有相關的工具 ARCHPR 可實作 RAR、ZIP 等壓縮加密檔案的可視化暴力破解,但是主要是為了學習 Python 編程應用,
Python語法
既然本意是學習 Python 編程,那自然是要對本實戰應用場景的編碼程序遇到的相關語法知識進行學習,
在此先推薦一個 Python 語法的官方站點:Python官方中文檔案,支持下載到本地,
自定義迭代器
迭代是 Python 最強大的功能之一,是訪問集合元素的一種方式,迭代器是一個可以記住遍歷的位置的物件,迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束,迭代器只能往前不會后退,迭代器有兩個基本的方法:iter() 和 next(),
1、迭代器物件可以使用常規 for 陳述句進行遍歷:
#!/usr/bin/python3
list=[1,2,3,4]
it = iter(list) # 創建迭代器物件
for x in it:
print (x, end=" ")
執行以上程式,輸出結果如下:
1 2 3 4
2、也可以使用 next() 函式:
#!/usr/bin/python3
import sys # 引入 sys 模塊
list=[1,2,3,4]
it = iter(list) # 創建迭代器物件
while True:
try:
print (next(it))
except StopIteration:
sys.exit()
執行以上程式,輸出結果如下:
1
2
3
4
3、Python 支持撰寫 class 來自定義迭代器,如何自定義一個迭代器:
- 在自定義的類中添加了
__iter__魔法方法可取得迭代器; - 在自定義的類中通過
__next__魔法方法指出所有的資料,
來看看一個簡單的自定義可迭代的類示例:
class TopTen(object):
def __init__(self):
self.num = 1
def __iter__(self):
return self
def __next__(self):
if self.num <= 10:
val = self.num
self.num += 1
return val
else:
raise StopIteration
if __name__ == "__main__":
values = TopTen()
for v in values:
print(v, end=' ')
代碼運行效果:

Python多執行緒
執行緒是 CPU 分配資源的基本單位,但一個程式開始運行后這個程式就變成了一個行程,而一個行程相當于一個或者多個執行緒,當沒有多執行緒編程時,一個行程也是一個主執行緒,但有多執行緒編程時,一個行程包含多個執行緒,包括主執行緒,使用執行緒可以實作程式的并發,Python 多執行緒快速入門可參見:python3 多執行緒編程,
Python3 執行緒中常用的兩個模塊為:
- (1)_thread;
- (2)threading (推薦使用)
其中 thread 模塊已被廢棄,用戶可以使用 threading 模塊代替,所以在 Python3 中不能再使用 “thread” 模塊,為了兼容性,Python3 將 thread 重命名為 “_thread”,
1、函式創建多執行緒
Python3 中提供了一個內置模塊threading.Thread,可以很方便的創建多執行緒,threading.Thread()一般接收2個引數:
- 1)執行緒函式名:要放置執行緒讓其后臺執行的函式,有用戶自己定義,主要不要加();
- 2)執行緒函式的引數:執行緒函式名所需的引數,以 tuple 元組的形式傳入,如果不需要引數,可以不指定,
下面來看看一個簡單的多執行緒示例:
import time
import threading
# 自定義執行緒函式
def my_threadfunc(name='python3'):
for i in range(2):
print("hello", name)
time.sleep(1)
if __name__=="__main__":
# 創建執行緒01,不指定引數
thread_01 = threading.Thread(target=my_threadfunc)
# 啟動執行緒01
thread_01.start()
# 創建執行緒02,指定引數,注意逗號不要少,否則不是一個tuple
thread_02 = threading.Thread(target=my_threadfunc, args=('Tr0e',))
# 啟動執行緒02
thread_02.start()
代碼運行效果:
2、類創建多執行緒
首先,自定義一個類,對這個自定義的類有兩個要求:
- 1)必須繼承 threading.Thread 這個父類;
- 2)必須重寫 run() 這個方法:run() 方法相當于第一種方法中的執行緒函式,可以寫自己需要的業務邏輯代碼,在start()后將會呼叫,
來看看示例代碼:
import time
from threading import Thread
class MyThread(Thread):
def __init__(self, name='Python3'):
super().__init__()
self.name = name
def run(self):
for i in range(2):
print("Hello", self.name)
time.sleep(1)
3、 join() 方法
多執行緒中 join() 作用是呼叫 join() 的執行緒阻塞直到某一執行緒結束才繼續執行,來看看示例代碼:
import threading
import time
class mythread(threading.Thread):
def run(self):
self.i = 1
print("子執行緒運行秒數:",'%d' % (self.i))
self.i = self.i + 1
time.sleep(1) # 睡眠一秒
print("子執行緒運行秒數:",'%d' % (self.i))
time.sleep(1)
print("子執行緒運行結束!")
if __name__ == '__main__':
ta = mythread() # 實體化執行緒
ta.start() # 開啟ta執行緒
ta.join() # 主執行緒等待 ta執行緒結束才繼續執行
print("主執行緒結束!")
代碼運行效果:
4、執行緒的同步——鎖
當一個行程擁有多個執行緒之后,如果他們各做各的任務互沒有關系還行,但既然屬于同一個行程,他們之間總是具有一定關系的,比如多個執行緒都要對某個資料進行修改,則可能會出現不可預料的結果,為保證操作正確,就需要引入鎖來進行執行緒間的同步,
Python3 中的 threading 模塊提供了 RLock 鎖(可重入鎖):
- 對于某一時間只能讓一個執行緒操作的陳述句放到 RLock 的 acquire 方法 和 release 方法之間;
- 即 acquire() 函式相當于給 RLock 鎖 上鎖,而 release() 函式相當于解鎖,
來看看一個簡單的演示案例:
import threading
class mythread(threading.Thread):
def run(self):
global x # 宣告一個全域變數
lock.acquire() # 上鎖,acquire()和release()之間的陳述句一次只能有一個執行緒進入,其余執行緒在acquire()處等待
x += 10
print('%s:%d' % (self.name, x))
lock.release() # 解鎖
x = 0
lock = threading.RLock() # 創建 可重入鎖
def main():
l = []
for i in range(5):
l.append(mythread()) # 創建5個執行緒,并把他們放到一個串列中
for i in l:
i.start() # 開啟串列中的所有執行緒
if __name__ == '__main__':
main()
代碼運行效果:
5、多執行緒函式小結:
t = Thread(target=func)
# 啟動子執行緒
t.start()
# 阻塞子執行緒,待子執行緒結束后,再往下執行
t.join()
# 判斷執行緒是否在執行狀態,在執行回傳True,否則回傳False
t.is_alive()
t.isAlive()
# 設定執行緒是否隨主執行緒退出而退出,默認為False
t.daemon = True
t.daemon = False
# 設定執行緒名
t.name = "My-Thread"
Python腳本
下面將從單執行緒、多執行緒兩種角度實作 ZIP 加密檔案的密碼爆破,
單執行緒數字爆破
先來生成一個用數字密碼(“101”)加密的 ZIP 壓縮檔案 password.zip,壓縮檔案為圖片 pasword.png(注意勾選 “ZIP 傳統加密” 的選項,后面的代碼不支持 WinRAR 新式的默認加密方式),如下圖所示:

爆破密碼的腳本也相對簡單,直接上代碼:
import zipfile
import os
import time
import sys
"""
獲取zip檔案
"""
def get_zipfile():
os.chdir(r'D:\Code\Python\MyTest\Basic')
files = os.listdir()
for file in files:
if file.endswith('.zip'):
return file
"""
爆破zip檔案
"""
def extract():
file = get_zipfile()
zfile = zipfile.ZipFile(file) # 讀取壓縮檔案
start_time = time.time()
for num in range(1, 99999): # 設定爆破的數字密碼區間
try:
zfile.extractall(path='.', pwd=str(num).encode('utf-8'))
print('解壓密碼是:', str(num))
end_time = time.time()
print('單執行緒破解壓縮包花了%s秒' % (end_time - start_time))
sys.exit(0) # 讓程式在得到結果后,就停止運行,正常退出
except Exception as e:
print(e)
#pass
if __name__ == "__main__":
extract()
以上代碼沒什么需要特別解釋的,簡單補充兩點:
- 需要注意的是在爆破程序需要使用例外處理機制避免密碼錯誤時程式直接終止;
- 對于 zipfile 庫的用法有疑問請參見官方檔案:ZipFile資料壓縮與存檔,
下面直接來看看 Pycharm 中運行腳本的效果:

單執行緒字符爆破
先來看看腳本:
import zipfile
import random
import time
import sys
class MyIterator():
# 用于暴力破解的字符集合
letters = 'abcdefghijklmnopqrstuvwxyz012345678'
min_digits = 0
max_digits = 0
def __init__(self, min_digits, max_digits):
# 下面的if-else是為了解決extract函式中,for回圈中傳遞的密碼長度可能前者的值大于后者,這一bug
if min_digits < max_digits:
self.min_digits = min_digits
self.max_digits = max_digits
else:
self.min_digits = max_digits
self.max_digits = min_digits
# 迭代器訪問定義,直接回傳self實列物件
def __iter__(self):
return self
# 通過不斷地輪循,生成密碼
def __next__(self):
rst = str()
for item in range(0, random.randrange(self.min_digits, self.max_digits + 1)):
# 從letters中隨機取選取一個值,并把選取幾次的結果拼接成一個字符,即一個密碼
rst += random.choice(MyIterator.letters)
return rst
def extract():
start_time = time.time()
zfile = zipfile.ZipFile("password.zip")
# 隨機迭代出5~6位數的密碼
for password in MyIterator(5, 6):
try:
zfile.extractall(path=".", pwd=str(password).encode('utf-8'))
print("the password is {}".format(password))
now_time = time.time()
print("spend time is {}".format(now_time - start_time))
sys.exit(0)
except Exception as e:
print(e)
pass
if __name__ == '__main__':
extract()
將 password.png 重新壓縮并將解壓密碼設定為 “ab12” 數字與字母組合的字串,上述利用自定義迭代器生成的字符組合范圍太廣了,爆破起來可能跑到天荒地老……故演示此代碼時我依據已知的密碼對代碼做了如下更改:
- 設定縮小字符范圍:
letters = 'abcd0123456789'; - 設定縮小遍歷的字串長度:
for password in MyIterator(3, 4),
來看看腳本運行效果,還足足跑了 78 秒之久:

多執行緒字典爆破
直接上腳本:
import zipfile
import threading
import sys
import os
'''
多執行緒爆破完成
'''
def extractfile(zip_file, password):
try:
zip_file.extractall(path='.', pwd=str(password).encode("utf-8"))
print('[+] 解壓密碼是:', password)
sys.exit(0)
except Exception as e:
print("當前爆破的密碼:%s 不正確!" % (password))
pass
def main():
os.chdir(r'D:\Code\Python\MyTest\Basic')
password_file = 'pwd.txt'
files = os.listdir()
for file in files: # 遍歷當前路徑下的所有檔案
if file.endswith('.zip'): # 爆破zip檔案
zip_file = zipfile.ZipFile(file)
pass_file = open(password_file)
for line in pass_file.readlines():
password = line.strip('\n')
t = threading.Thread(target=extractfile, args=(zip_file, password))
t.start()
if __name__ == '__main__':
main()
代碼運行效果:

總結
個人感覺最后的多執行緒腳本實際上意義不大,僅供簡單學習多執行緒使用……因為此程式中對每個密碼的嘗試都單開了一個執行緒、而嘗試密碼是否正常的邏輯函式 extractfile() 又十分簡單,沒有必要單開一個執行緒來浪費資源,除非說處理的邏輯函式 extractfile() 執行了十分耗時的操作(比如需要下載檔案、或者說每次執行 extractfile() 函式都對一個單獨的大型字典進行爆破等),
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292733.html
標籤:python
