Python工具箱系列(四十二)
RAR文件操作
RAR是广受好评,使用广泛的压缩格式,开发者为尤金·罗谢尔(俄语:Евгений Лазаревич Рошал,拉丁转写:Yevgeny Lazarevich Roshal),RAR的全名是“Roshal ARchive”,即“罗谢尔的归档”之意。尤其是winrar一度成为windows上的必备软件。
它的特点如下:
◆RAR通常情况比ZIP压缩比高,但压缩/解压缩速度较慢。
◆分卷压缩:压缩后分割为多个文件。
◆固实压缩:把要压缩的视为同一个文件以加大压缩比,代价是取用包中任何文件需解压整个压缩包。
◆恢复记录:加入冗余数据用于修复,在压缩包本身损坏但恢复记录够多时可对损坏压缩包进行恢复。
◆加密:RAR 2.0使用AES-128-cbc,(RAR5.0以后为AES-256CBC)。之前RAR的加密算法为私有。目前均未被直接攻破(至少没有公开),没有密码时只有暴力破解。
RAR解码器的源代码已经开放,RAR解码器许可证以不许发布编译RAR兼容编码器为条件,允许有条件自由发布与修改,而RAR编码器一直是有专利的。格式并不开放,所以python对于RAR格式只能够是读取,不能够进行其它操作(创建与修改)。本文介绍如何使用第三方库来操作RAR文件。安装过程很简单,使用以下命令安装。
# 千万不能够用pip install rarfile安装最新版本(4.0)
pip install rarfile==3.3
rarfile 4.0版本的功能有问题,无法正确的解压加密的RAR文件。安装rarfile 3.x版本即可。基本功能的示例代码如下:
import rarfile
import os
rar_file = r"d:\foobox-cn.rar"
rar_file_passwd = r'd:\Imhex.rar'
target_dir = r'd:\test'
def rarinfo(filename):
"""
获得RAR文件中的文件列表
Args:
filename (string): 要打开的RAR文件
"""
rf = rarfile.RarFile(filename)
if rf.needs_password():
print("RAR文件需要密码")
# 输出文件名与大小
for f in rf.infolist():
print(f.filename, f.file_size)
if "README.md" in f.filename:
print(rf.read(f).decode('utf-8'))
def unrar(filename, targetdir=None, password=None):
"""
解压RAR文件。winrar的路径要设置在WINDOWS的PATH环境变量中
Args:
filename (string): 要打开的RAR文件
"""
rf = rarfile.RarFile(filename)
if targetdir:
if not os.path.exists(targetdir):
os.mkdir(targetdir)
rf.extractall(path=targetdir, pwd=password)
else:
rf.extractall(pwd=password)
rarinfo(rar_file)
rarinfo(rar_file_passwd)
unrar(rar_file, target_dir)
unrar(rar_file_passwd, target_dir, password='88488848')
winrar产生以下2个目标文件。其中一个有密码,一个无。从互联网下载RAR文件,有可能由于时间久远,忘记当时的密码设置,需要对密码进行破解。
其基本的思路是:
◆利用人性的弱点,先使用常用密码进行尝试。可以建立自己的一个常用密码本,或者从互联网收集常用的密码。
◆暴力破解,组合各种可能性后,挨个试。
为了方便记忆,大部分人选择极为简单的密码,例如2016年全球最常用的密码就是:123456。笔者在国内酒店住宿,大部分WIFI密码是88888888,或者是房间号,相当于不设防,这就是人性的弱点。许多电影里神乎其神的黑客破解也无外于此。除了简单的密码外,还有些人使用出生年月、手机号码等也很容易猜。如果用户认真设置口令,则RAR在算法本身上没有缺陷,只能够暴力破解,也就是在所有可能的字母、符号与数字的海量组合中进行尝试,这个非常消耗时间。以下代码示例了这个过程:
import itertools
import rarfile
import numba
password_file = r'd:\password.txt'
target_file = r'd:\ImHex.rar'
target_dir = r'd:\test'
full_pwdstr = 'abcdefghijklmnopqrstuvwxyz0123456789'
digit_pwdstr = '0123456789'
def get_passwd() -> str:
for x in itertools.product(digit_pwdstr, repeat=8):
yield ''.join(x)
def cracker(filename):
"""
暴力破解RAR文件
Args:
filename (string): 要破解的RAR文件
"""
rf = rarfile.RarFile(filename)
crack_ok = False
with open(password_file) as fpPwd:
for pwd in fpPwd.readlines():
pwd = pwd.rstrip()
print(f"try password: {pwd}")
try:
rf.extractall(path=target_dir, pwd=pwd)
print('Success! ====>'+pwd)
crack_ok = True
break
except:
pass
if not crack_ok:
# 暴力组合
for pwd in get_passwd():
try:
rf.extractall(path=target_dir, pwd=pwd)
print('Bingo! ====>'+pwd)
crack_ok = True
break
except:
pass
cracker(target_file)
虽然我们知道口令是88488848,但是程序必须遍历所有的组合才能够获得真正的密码,极为耗时。此时,python比较慢的特点就表现的淋漓尽致。如果想进一步提高效率。
可以考虑一下路径:
◆PyPy
◆Cython
◆Numba
以上三个方案选择时考虑:
◆通用性:在三个方案中,Cython和Numba的兼容性都非常好,而Pypy对于部分库的支持较差(如Numpy,Scipy)。
◆速度:这三种方案的速度相差不大。
◆易用性:易用性最好的无疑是Pypy,Pypy是Python的解释器,我们针对纯Python使用Pypy,除了Pypy不支持的部分库外,不需要进行任何改动。然后是Numba,Numba使用装饰器来优化函数,易用性也很高,最后是Cython。