Toolbox for analyzing and editing pkg application files for psp,ps3, ps4 and ps5, includes the most useful functions you might need.
| 1 | import os |
| 2 | import logging |
| 3 | from Crypto.Cipher import AES |
| 4 | from Crypto.Util.Padding import pad, unpad |
| 5 | from Crypto.Util.number import bytes_to_long |
| 6 | from Crypto.Random import get_random_bytes |
| 7 | |
| 8 | # Constants |
| 9 | AES_KEY_LEN_128 = 16 |
| 10 | |
| 11 | # AES context |
| 12 | class AES_ctx: |
| 13 | def __init__(self): |
| 14 | self.key = None |
| 15 | self.iv = None |
| 16 | |
| 17 | def set_key(self, key): |
| 18 | self.key = key |
| 19 | |
| 20 | def set_iv(self, iv): |
| 21 | self.iv = iv |
| 22 | |
| 23 | def encrypt(self, data): |
| 24 | cipher = AES.new(self.key, AES.MODE_CBC, self.iv) |
| 25 | return cipher.encrypt(pad(data, AES.block_size)) |
| 26 | |
| 27 | def decrypt(self, data): |
| 28 | cipher = AES.new(self.key, AES.MODE_CBC, self.iv) |
| 29 | return unpad(cipher.decrypt(data), AES.block_size) |
| 30 | |
| 31 | # AES functions |
| 32 | def AES_set_key(ctx, key, key_len): |
| 33 | ctx.key = key[:key_len] |
| 34 | ctx.iv = get_random_bytes(AES.block_size) |
| 35 | |
| 36 | def AES_cbc_encrypt(ctx, data, out): |
| 37 | out[:] = ctx.encrypt(data) |
| 38 | |
| 39 | def AES_cbc_decrypt(ctx, data, out): |
| 40 | out[:] = ctx.decrypt(data) |
| 41 | |
| 42 | class PackagePS4: |
| 43 | def __init__(self, original_file): |
| 44 | self.original_file = original_file |
| 45 | |
| 46 | def is_encrypted(self): |
| 47 | """Check if package is encrypted""" |
| 48 | try: |
| 49 | # Controlla se il PKG è cifrato verificando il flag di crittografia |
| 50 | with open(self.original_file, 'rb') as f: |
| 51 | # Vai all'offset del flag di crittografia (0x1A nel header PS4) |
| 52 | f.seek(0x1A) |
| 53 | # Leggi il flag (2 byte) |
| 54 | encryption_flag = int.from_bytes(f.read(2), byteorder='little') |
| 55 | # Se il flag è diverso da 0, il PKG è cifrato |
| 56 | return encryption_flag != 0 |
| 57 | except Exception as e: |
| 58 | logging.error(f"Error checking encryption: {str(e)}") |
| 59 | return False |
| 60 | |
| 61 | def extract_with_passcode(self, passcode, output_dir): |
| 62 | """Extract encrypted PKG with passcode""" |
| 63 | if not self.is_encrypted(): |
| 64 | raise ValueError("Package is not encrypted") |
| 65 | |
| 66 | try: |
| 67 | # Verifica il formato del passcode |
| 68 | if len(passcode) != 32: |
| 69 | raise ValueError("Invalid passcode length") |
| 70 | |
| 71 | # Converti il passcode in chiave AES |
| 72 | key = bytes.fromhex(passcode) |
| 73 | |
| 74 | # Decripta il PKG usando la chiave |
| 75 | self.decrypt_pkg(key, output_dir) |
| 76 | |
| 77 | return True |
| 78 | except ValueError as e: |
| 79 | raise e |
| 80 | except Exception as e: |
| 81 | raise ValueError(f"Failed to decrypt with passcode: {str(e)}") |
| 82 | |
| 83 | def decrypt_pkg(self, key, output_dir): |
| 84 | # Implementa la decriptazione del PKG usando la chiave AES |
| 85 | pass |
| 86 | |
| 87 |