Skip to content

Instantly share code, notes, and snippets.

@Arnie97
Last active February 8, 2021 16:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Arnie97/249c4846cf9025e2e0356307d9f17e80 to your computer and use it in GitHub Desktop.
Save Arnie97/249c4846cf9025e2e0356307d9f17e80 to your computer and use it in GitHub Desktop.
#!/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