Seregon/PkgToolBox

Toolbox for analyzing and editing pkg application files for psp,ps3, ps4 and ps5, includes the most useful functions you might need.

Python/57.3 KB/No license
Utilities/Trophy/ESMFDecrypter.py
PkgToolBox / Utilities / Trophy / ESMFDecrypter.py
1import os
2import re
3import logging
4from Crypto.Cipher import AES
5from Crypto.Util.Padding import unpad
6import xml.etree.ElementTree as ET
7import requests
8import time
9
10logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
11logger = logging.getLogger(__name__)
12
13class ESMFDecrypter:
14 def __init__(self):
15 self.trophy_key = bytes([
16 0x21, 0xF4, 0x1A, 0x6B, 0xAD, 0x8A, 0x1D, 0x3E,
17 0xCA, 0x7A, 0xD5, 0x86, 0xC1, 0x01, 0xB7, 0xA9
18 ])
19 self.valid_np_com_ids = []
20
21 def decrypt_esfm_file(self, file_path, np_com_id, output_folder):
22 logger.info(f"Starting decryption of file: {file_path}")
23
24 iv = bytes([0] * 16)
25 cipher = AES.new(self.trophy_key, AES.MODE_CBC, iv)
26 key = cipher.encrypt(np_com_id.ljust(16, '\0').encode())
27
28 with open(file_path, 'rb') as file:
29 encrypted_data = file.read()
30
31 total_size = len(encrypted_data)
32 chunk_size = AES.block_size
33
34 cipher = AES.new(key, AES.MODE_CBC, iv)
35 decrypted_data = bytearray()
36 for i in range(0, total_size, chunk_size):
37 chunk = encrypted_data[i:i + chunk_size]
38 decrypted_chunk = cipher.decrypt(chunk)
39 decrypted_data.extend(decrypted_chunk)
40 logger.debug(f"Decrypted {i + chunk_size} of {total_size} bytes")
41
42 decrypted_data = unpad(decrypted_data, AES.block_size)
43 decrypted_data = ''.join(chr(byte) for byte in decrypted_data if 32 <= byte <= 126 or byte in (9, 10, 13))
44
45 try:
46 decrypted_xml = ET.fromstring(decrypted_data)
47 logger.info("XML decrypted and parsed successfully")
48 except ET.ParseError as e:
49 logger.error(f"Error parsing decrypted XML: {e}")
50 return None
51
52 output_file_path = os.path.join(output_folder, os.path.basename(file_path)[:-5] + ".xml")
53 with open(output_file_path, 'w', encoding='utf-8') as output_file:
54 output_file.write(decrypted_data)
55
56 logger.info(f"Decrypted file saved to: {output_file_path}")
57 return output_file_path
58
59 @staticmethod
60 def validate_np_com_id(np_com_id):
61 return re.match(r'^NPWR\d{5}_\d{2}$', np_com_id) is not None
62
63 def brute_force_np_com_ids(self, start=1, end=25000, delay=1):
64 base_url = "https://m.np.playstation.com/api/trophy/v1/npCommunicationIds/{}/trophyGroups"
65 headers = {
66 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
67 }
68
69 for i in range(start, end + 1):
70 np_com_id = f"NPWR{i:05d}_00"
71 url = base_url.format(np_com_id)
72
73 try:
74 response = requests.get(url, headers=headers)
75 if response.status_code == 200:
76 data = response.json()
77 title_name = data.get('trophyTitleName', 'Unknown')
78 logger.info(f"Valid NP Communication ID found: {np_com_id} - {title_name}")
79 self.valid_np_com_ids.append((np_com_id, title_name))
80 else:
81 logger.debug(f"Invalid or non-existent NP Communication ID: {np_com_id}")
82 except Exception as e:
83 logger.error(f"Error checking NP Communication ID {np_com_id}: {str(e)}")
84
85 time.sleep(delay)
86
87 logger.info(f"Brute force completed. Found {len(self.valid_np_com_ids)} valid NP Communication IDs.")
88 return self.valid_np_com_ids
89
90def decrypt_esfm_file(file_path, np_com_id, output_folder):
91 decrypter = ESMFDecrypter()
92 if not decrypter.validate_np_com_id(np_com_id):
93 logger.error("Invalid NP communication ID. Correct format: NPWRYYYYY_ZZ")
94 return None
95 return decrypter.decrypt_esfm_file(file_path, np_com_id, output_folder)
96
97if __name__ == "__main__":
98 decrypter = ESMFDecrypter()
99 valid_ids = decrypter.brute_force_np_com_ids(start=1, end=100)
100
101 for np_com_id, title_name in valid_ids:
102 print(f"NP Communication ID: {np_com_id} - Title: {title_name}")
103
104
105 file_path = input("Enter the path of the ESFM file: ")
106 output_folder = input("Enter the output folder: ")
107 np_com_id = input("Enter the NP communication ID (format: NPWRYYYYY_ZZ): ")
108
109 decrypted_file_path = decrypt_esfm_file(file_path, np_com_id, output_folder)
110 if decrypted_file_path:
111 logger.info(f"Decryption completed. File saved to: {decrypted_file_path}")
112 else:
113 logger.error("Error during file decryption.")