import argparse
from pathlib import Path
from typing import Iterable
KEY = b"quantaninjagirl24"
HEADER = bytes([0xEF, 0xFE, 0x80])
CHUNK_DECRYPT = 128
def rc4_init(key: bytes) -> list[int]:
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + key[i % len(key)]) & 0xFF
s[i], s[j] = s[j], s[i]
return s
def rc4_crypt(key_state: list[int], data: bytes) -> bytes:
s = key_state.copy()
i = j = 0
out = bytearray()
for byte in data:
i = (i + 1) & 0xFF
j = (j + s[i]) & 0xFF
s[i], s[j] = s[j], s[i]
k = s[(s[i] + s[j]) & 0xFF]
out.append(byte ^ k)
return bytes(out)
def decrypt_texture(buf: bytes) -> bytes | None:
if not buf.startswith(HEADER):
return None
payload = buf[3:]
to_dec = payload[:CHUNK_DECRYPT]
rest = payload[len(to_dec):]
state = rc4_init(KEY)
dec_first = rc4_crypt(state, to_dec)
return dec_first + rest
def process_file(path: Path, out_dir: Path | None):
raw = path.read_bytes()
dec = decrypt_texture(raw)
if dec is None:
return False
out_path = (
out_dir / path.name if out_dir
else path.with_suffix(".dec.png")
)
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_bytes(dec)
return True
def walk_inputs(targets: Iterable[Path]):
for p in targets:
if p.is_dir():
yield from p.rglob("*.png")
elif p.is_file():
yield p
def main():
ap = argparse.ArgumentParser(
description="decrypt")
ap.add_argument("input")
ap.add_argument("-o", type=Path)
args = ap.parse_args()
hits = 0
for f in walk_inputs(Path(i) for i in args.input):
if process_file(f, args.o):
hits += 1
if __name__ == "__main__":
main()
1 个赞