前言
本文继续记录学习下 Python 的有趣应用:借助 Python 脚本暴力破解 ZIP 加密文件的密码。虽然有相关的工具 ARCHPR 可实现 RAR、ZIP 等压缩加密文件的可视化暴力破解,但是主要是为了学习 Python 编程应用。
Python语法
既然本意是学习 Python 编程,那自然是要对本实战应用场景的编码过程遇到的相关语法知识进行学习。
在此先推荐一个 Python 语法的官方站点:Python官方中文文档,支持下载到本地。
自定义迭代器
迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,迭代器有两个基本的方法:iter() 和 next()。
1、迭代器对象可以使用常规 for 语句进行遍历:
2、也可以使用 next() 函数:
3、Python 支持编写 class 来自定义迭代器,如何自定义一个迭代器:
- 在自定义的类中添加了__iter__魔法方法可取得迭代器;
- 在自定义的类中通过__next__魔法方法指出所有的数据。
来看看一个简单的自定义可迭代的类示例:
代码运行效果:
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 元组的形式传入,如果不需要参数,可以不指定。
下面来看看一个简单的多线程示例:
代码运行效果:
2、类创建多线程
首先,自定义一个类,对这个自定义的类有两个要求:
- 1)必须继承 threading.Thread 这个父类;
- 2)必须重写 run() 这个方法:run() 方法相当于第一种方法中的线程函数,可以写自己需要的业务逻辑代码,在start()后将会调用。
来看看示例代码:
3、 join() 方法
多线程中 join() 作用是调用 join() 的线程阻塞直到某一线程结束才继续执行。来看看示例代码:
代码运行效果:
4、线程的同步——锁
当一个进程拥有多个线程之后,如果他们各做各的任务互没有关系还行,但既然属于同一个进程,他们之间总是具有一定关系的。比如多个线程都要对某个数据进行修改,则可能会出现不可预料的结果。为保证操作正确,就需要引入锁来进行线程间的同步。
Python3 中的 threading 模块提供了 RLock 锁(可重入锁):
- 对于某一时间只能让一个线程操作的语句放到 RLock 的 acquire 方法 和 release 方法之间;
- 即 acquire() 函数相当于给 RLock 锁 上锁,而 release() 函数相当于解锁。
来看看一个简单的演示案例:
代码运行效果:
5、多线程函数小结:
Python脚本
下面将从单线程、多线程两种角度实现 ZIP 加密文件的密码爆破。
单线程数字爆破
先来生成一个用数字密码(“101”)加密的 ZIP 压缩文件 password.zip,压缩文件为图片 pasword.png(注意勾选 “ZIP 传统加密” 的选项,后面的代码不支持 WinRAR 新式的默认加密方式),如下图所示:
爆破密码的脚本也相对简单,直接上代码:
以上代码没什么需要特别解释的,简单补充两点:
- 需要注意的是在爆破过程需要使用异常处理机制避免密码错误时程序直接终止;
- 对于 zipfile 库的用法有疑问请参见官方文档:ZipFile数据压缩与存档。
下面直接来看看 Pycharm 中运行脚本的效果:
单线程字符爆破
先来看看脚本:
将 password.png 重新压缩并将解压密码设置为 “ab12” 数字与字母组合的字符串,上述利用自定义迭代器生成的字符组合范围太广了,爆破起来可能跑到天荒地老……故演示此代码时我依据已知的密码对代码做了如下更改:
- 设置缩小字符范围:letters = 'abcd0123456789';
- 设置缩小遍历的字符串长度:for password in MyIterator(3, 4)。
来看看脚本运行效果,还足足跑了 78 秒之久:
多线程字典爆破
直接上脚本:
代码运行效果:
总结
个人感觉最后的多线程脚本实际上意义不大,仅供简单学习多线程使用……因为此程序中对每个密码的尝试都单开了一个线程、而尝试密码是否正常的逻辑函数 extractfile() 又十分简单,没有必要单开一个线程来浪费资源,除非说处理的逻辑函数 extractfile() 执行了十分耗时的操作(比如需要下载文件、或者说每次执行 extractfile() 函数都对一个单独的大型字典进行爆破等)。