Last active
February 8, 2021 16:00
-
-
Save Arnie97/249c4846cf9025e2e0356307d9f17e80 to your computer and use it in GitHub Desktop.
bkcrack wrapper (https://github.com/kimci86/bkcrack)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
"""inflate.py: full archive decompression helper for bkcrack | |
usage: %s path/to/encrypted.zip kx ky kz | |
""" | |
import os | |
import subprocess | |
import sys | |
import time | |
import zipfile | |
import zlib | |
BKCRACK_BINARY_PATH = "bkcrack" | |
ZIP_FLAG_BIT_ENCRYPTED = 0x01 | |
def recover_timestamp(info: zipfile.ZipInfo): | |
"""Set the file modification time based on zip headers.""" | |
timestamp = time.mktime((*info.date_time, 0, 0, 0)) | |
os.utime(info.filename, (timestamp, timestamp)) | |
def inflate(path: str): | |
"""Decompress the deflated file in-place.""" | |
with open(path, "rb+") as f: | |
inflated = zlib.decompress(f.read(), -zlib.MAX_WBITS) | |
f.seek(0) | |
f.write(inflated) | |
f.truncate() | |
def decrypt(archive: zipfile.ZipFile, item: zipfile.ZipInfo, master_keys): | |
"""Call bkcrack with master keys to extract an encrypted file.""" | |
command = [ | |
BKCRACK_BINARY_PATH, | |
'-C', archive.filename, | |
'-c', item.filename, | |
'-d', item.filename, | |
'-k'] + master_keys | |
subprocess.run(command, check=True) | |
def process(archive: zipfile.ZipFile, item: zipfile.ZipInfo, master_keys): | |
"""Extract a single entry from the zip archive.""" | |
# directory entry | |
if item.is_dir(): | |
os.makedirs( | |
item.filename, | |
mode=item.external_attr >> 16, | |
exist_ok=True) | |
# cipher text: maybe compressed, maybe not | |
elif item.flag_bits & ZIP_FLAG_BIT_ENCRYPTED: | |
decrypt(archive, item, master_keys) | |
if item.compress_type == zipfile.ZIP_STORED: | |
pass | |
elif item.compress_type == zipfile.ZIP_DEFLATED: | |
inflate(item.filename) | |
else: # other algorithms are rarely used, but technically possible | |
raise ValueError("unsupported compress type", item.compress_type) | |
# plain text | |
else: | |
archive.extract(item.filename) | |
recover_timestamp(item) | |
def main(): | |
if len(sys.argv) != 5: | |
return print(__doc__ % sys.argv[0]) | |
_, archive_path, *master_keys = sys.argv | |
with zipfile.ZipFile(archive_path) as archive: | |
for item in archive.infolist(): | |
process(archive, item, master_keys) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment