前言
今晚下班后微信收到一跳消息!是前女友的闺蜜发过来的,然后让我自己去猜密码,本来我是不打算理她的!但是她后面说里面有你想不到的福利!这句话对于一枚(lsp)不对一枚宅男程序员没有抵抗力,毕竟我对她映像最深的就是那一双大长腿!穿啥都好看的那种!好了废话不多说,技术时刻来袭!
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()执行以上程序,输出结果如下:
3、Python 支持编写 class 来自定义迭代器,如何自定义一个迭代器:
- 在自定义的类中添加了
__iter__
魔法方法可取得迭代器; - 在自定义的类中通过
__next__
魔法方法指出所有的数据。
来看看一个简单的自定义可迭代的类示例:
class TopTen(object):def __init__(self):self.num = 1def __iter__(self):return selfdef __next__(self):if self.num <= 10:val = self.numself.num += 1return valelse:raise StopIterationif __name__ == "__main__":values = TopTen()for v in values:print(v, end=' ')
代码运行效果:
Python多线程
线程是 CPU 分配资源的基本单位,但一个程序开始运行后这个程序就变成了一个进程,而一个进程相当于一个或者多个线程。当没有多线程编程时,一个进程也是一个主线程,但有多线程编程时,一个进程包含多个线程,包括主线程。使用线程可以实现程序的并发,
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)# 启动线程01thread_01.start()# 创建线程02,指定参数,注意逗号不要少,否则不是一个tuplethread_02 = threading.Thread(target=my_threadfunc, args=('Tr0e',))# 启动线程02thread_02.start()
代码运行效果:
2、类创建多线程 首先,自定义一个类,对这个自定义的类有两个要求: 1)必须继承 threading.Thread 这个父类; 2)必须重写 run() 这个方法:run() 方法相当于第一种方法中的线程函数,可以写自己需要的业务逻辑代码,在start()后将会调用。 来看看示例代码:
import time
from threading import Threadclass MyThread(Thread):def __init__(self, name='Python3'):super().__init__()self.name = namedef run(self):for i in range(2):print("Hello", self.name)time.sleep(1)
3、 join() 方法
多线程中 join() 作用是调用 join() 的线程阻塞直到某一线程结束才继续执行。来看看示例代码:
import threading
import timeclass mythread(threading.Thread):def run(self):self.i = 1print("子线程运行秒数:",'%d' % (self.i))self.i = self.i + 1time.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 threadingclass mythread(threading.Thread):def run(self):global x # 声明一个全局变量lock.acquire() # 上锁,acquire()和release()之间的语句一次只能有一个线程进入,其余线程在acquire()处等待x += 10print('%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)#passif __name__ == "__main__":extract()
爆破密码的脚本也相对简单,直接上代码:
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)#passif __name__ == "__main__":extract()
以上代码没什么需要特别解释的,简单补充两点:
- 需要注意的是在爆破过程需要使用异常处理机制避免密码错误时程序直接终止;
下面直接来看看 Pycharm 中运行脚本的效果:
单线程字符爆破
先来看看脚本:
import zipfile
import random
import time
import sysclass MyIterator():# 用于暴力破解的字符集合letters = 'abcdefghijklmnopqrstuvwxyz012345678'min_digits = 0max_digits = 0def __init__(self, min_digits, max_digits):# 下面的if-else是为了解决extract函数中,for循环中传递的密码长度可能前者的值大于后者,这一bugif min_digits < max_digits:self.min_digits = min_digitsself.max_digits = max_digitselse:self.min_digits = max_digitsself.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 rstdef 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)passif __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))passdef 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() 函数都对一个单独的大型字典进行爆破等)。
好了到这里就结束了,剩下的内容就我自己慢慢看了哈!你们是来学技术的 不是来看大长腿的!
⑥项目源码案例分享有
如果你用得到的话可以直接拿走,在我的QQ技术交流群里群号:754370353(纯技术交流和资源共享,广告勿入)以自助拿走
点击这里 领取