Python工具箱系列(四十二)

发布时间 2023-09-11 16:04:12作者: 西安衍舆航天

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。