mirror of
https://codeberg.org/Nixietab/EOSSDK-Holos.git
synced 2026-08-21 20:23:28 -04:00
added re-encription tool
This commit is contained in:
parent
47f8eb112e
commit
7b7cf916ba
1 changed files with 132 additions and 0 deletions
132
keyDumper/encrypt_bundles.py
Normal file
132
keyDumper/encrypt_bundles.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os, sys, struct, argparse
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
KEY_DEFAULT = b"rK7CcATuZk7LAmhqqU4iBLNmAq8QbK3s"
|
||||
KEY_ALTERNATE = b"x4DZmD6D2HhkzT6qD8HpKeZdgM9HCmXP"
|
||||
|
||||
|
||||
def xor_crypt(data, key):
|
||||
cipher = AES.new(key, AES.MODE_ECB)
|
||||
out = bytearray(len(data))
|
||||
for off in range(0, len(data), 16):
|
||||
ctr = off // 16 + 1
|
||||
ks = cipher.encrypt(struct.pack('<q', ctr) + b'\x00' * 8)
|
||||
for j, b in enumerate(data[off:off+16]):
|
||||
out[off+j] = b ^ ks[j]
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def process_file(inpath, outpath, key, dry_run=False):
|
||||
try:
|
||||
with open(inpath, 'rb') as f:
|
||||
data = f.read()
|
||||
if data[:7] != b'UnityFS':
|
||||
print(f" Warning: '{os.path.basename(inpath)}' does not start with UnityFS magic")
|
||||
if dry_run:
|
||||
print(f" Would encrypt: {os.path.basename(inpath)} -> {os.path.basename(outpath)}")
|
||||
return True
|
||||
encrypted = xor_crypt(data, key)
|
||||
os.makedirs(os.path.dirname(outpath) or '.', exist_ok=True)
|
||||
with open(outpath, 'wb') as f:
|
||||
f.write(encrypted)
|
||||
print(f" {os.path.basename(inpath)} -> {os.path.basename(outpath)} ({len(data)} bytes)")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f" ERROR {os.path.basename(inpath)}: {e}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Re-encrypt decrypted Unity bundles for Holo's Hanafuda")
|
||||
parser.add_argument('input', help='Input .unity3d file or directory of .unity3d files')
|
||||
parser.add_argument('-o', '--output', help='Output file or directory')
|
||||
parser.add_argument('--key-alternate', action='store_true',
|
||||
help='Use alternate bundle key candidate (x4DZmD6D2HhkzT6qD8HpKeZdgM9HCmXP)')
|
||||
parser.add_argument('--key-hex', metavar='HEX',
|
||||
help='64-char hex AES-256 key (overrides defaults)')
|
||||
parser.add_argument('--in-place', action='store_true',
|
||||
help='Write output next to the original .bundle in StandaloneWindows64/')
|
||||
parser.add_argument('--dry-run', action='store_true',
|
||||
help='List what would be encrypted without writing')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.key_hex:
|
||||
key = bytes.fromhex(args.key_hex)
|
||||
if len(key) != 32:
|
||||
print("Error: --key-hex must be exactly 64 hex chars (32 bytes)", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
elif args.key_alternate:
|
||||
key = KEY_ALTERNATE
|
||||
else:
|
||||
key = KEY_DEFAULT
|
||||
|
||||
inpath = args.input
|
||||
|
||||
if os.path.isdir(inpath):
|
||||
files = sorted(f for f in os.listdir(inpath) if f.endswith('.unity3d'))
|
||||
if not files:
|
||||
print(f"No .unity3d files found in '{inpath}'")
|
||||
return
|
||||
|
||||
if args.output:
|
||||
outdir = args.output
|
||||
elif args.in_place:
|
||||
outdir = os.path.normpath(os.path.join(inpath, '..', '..', 'StandaloneWindows64'))
|
||||
else:
|
||||
outdir = os.path.normpath(os.path.join(inpath, '..', 'reencrypted'))
|
||||
|
||||
if not args.dry_run:
|
||||
os.makedirs(outdir, exist_ok=True)
|
||||
|
||||
ok = errs = 0
|
||||
for fname in files:
|
||||
fpath = os.path.join(inpath, fname)
|
||||
base = fname.removesuffix('.unity3d')
|
||||
outpath = os.path.join(outdir, base + '.bundle')
|
||||
if process_file(fpath, outpath, key, args.dry_run):
|
||||
ok += 1
|
||||
else:
|
||||
errs += 1
|
||||
|
||||
total = len(files)
|
||||
print(f"\n{ok} OK, {errs} errors of {total}")
|
||||
if not args.dry_run:
|
||||
print(f"Output directory: {outdir}/")
|
||||
|
||||
else:
|
||||
if not os.path.exists(inpath):
|
||||
print(f"Error: '{inpath}' not found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.output:
|
||||
outpath = args.output
|
||||
elif args.in_place:
|
||||
base = os.path.splitext(os.path.basename(inpath))[0]
|
||||
bundle_name = base + '.bundle'
|
||||
candidates = [
|
||||
os.path.join(os.path.dirname(inpath), bundle_name),
|
||||
os.path.normpath(os.path.join(
|
||||
os.path.dirname(inpath), '..', '..', 'StandaloneWindows64', bundle_name)),
|
||||
]
|
||||
outpath = None
|
||||
for c in candidates:
|
||||
if os.path.exists(c):
|
||||
outpath = c
|
||||
break
|
||||
if not outpath:
|
||||
print(f"Error: --in-place but no existing .bundle found for '{inpath}'",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
basedir = os.path.dirname(inpath) or '.'
|
||||
basename = os.path.splitext(os.path.basename(inpath))[0] + '.bundle'
|
||||
outpath = os.path.join(basedir, basename)
|
||||
|
||||
process_file(inpath, outpath, key, args.dry_run)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue