Seregon/ShadPKG

A tool for deriving PKG packet encryption keys for ps4 written in c++

C++/47.3 KB/No license
scripts/rif_analyzer.py
ShadPKG / scripts / rif_analyzer.py
1#!/usr/bin/env python3
2"""
3RIF File Analyzer and Generator
4Analyzes PlayStation 4 RIF (Rights Information File) structure
5and generates dynamic RIF files for retail games
6"""
7 
8import os
9import struct
10import hashlib
11from typing import Dict, List, Tuple
12from pathlib import Path
13 
14class RIFAnalyzer:
15 def __init__(self):
16 self.rif_structure = {
17 'magic': b'RIF\x00', # 4 bytes: "RIF" + null terminator
18 'version': b'\x00\x01', # 2 bytes: version (0x0001)
19 'unknown1': b'\xFF\xFF', # 2 bytes: unknown field
20 'padding1': b'\x00' * 12, # 12 bytes: padding/reserved
21 'timestamp_offset': 20, # Offset where timestamp starts
22 'padding2_pattern': b'\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF' # 8 bytes pattern
23 }
24
25 def analyze_rif_file(self, filepath: str) -> Dict:
26 """Analyze a RIF file and extract its structure"""
27 with open(filepath, 'rb') as f:
28 data = f.read()
29
30 if len(data) != 1024:
31 raise ValueError(f"Invalid RIF file size: {len(data)} bytes (expected 1024)")
32
33 analysis = {
34 'filepath': filepath,
35 'size': len(data),
36 'magic': data[0:4],
37 'version': data[4:6],
38 'unknown1': data[6:8],
39 'padding1': data[8:20],
40 'timestamp': struct.unpack('>I', data[20:24])[0], # Big-endian 32-bit timestamp
41 'padding2': data[24:32],
42 'content_id': self.extract_content_id(filepath),
43 'header_hex': data[0:32].hex().upper()
44 }
45
46 return analysis
47
48 def extract_content_id(self, filepath: str) -> str:
49 """Extract content ID from filename"""
50 filename = os.path.basename(filepath)
51 if '_' in filename:
52 return filename.split('_')[0] + '_' + filename.split('_')[1]
53 return filename.replace('.rif', '')
54
55 def analyze_directory(self, directory: str) -> List[Dict]:
56 """Analyze all RIF files in a directory"""
57 results = []
58 rif_files = Path(directory).rglob('*.rif')
59
60 for rif_file in rif_files:
61 try:
62 analysis = self.analyze_rif_file(str(rif_file))
63 results.append(analysis)
64 print(f"Analyzed: {rif_file.name}")
65 except Exception as e:
66 print(f"Error analyzing {rif_file}: {e}")
67
68 return results
69
70 def find_patterns(self, analyses: List[Dict]) -> Dict:
71 """Find common patterns across RIF files"""
72 patterns = {
73 'common_magic': set(),
74 'common_version': set(),
75 'common_unknown1': set(),
76 'timestamp_range': [],
77 'common_padding2': set()
78 }
79
80 for analysis in analyses:
81 patterns['common_magic'].add(analysis['magic'])
82 patterns['common_version'].add(analysis['version'])
83 patterns['common_unknown1'].add(analysis['unknown1'])
84 patterns['timestamp_range'].append(analysis['timestamp'])
85 patterns['common_padding2'].add(analysis['padding2'])
86
87 patterns['timestamp_range'] = (min(patterns['timestamp_range']), max(patterns['timestamp_range']))
88
89 return patterns
90 
91class RIFGenerator:
92 def __init__(self):
93 self.base_structure = {
94 'magic': b'RIF\x00',
95 'version': b'\x00\x01',
96 'unknown1': b'\xFF\xFF',
97 'padding1': b'\x00' * 12,
98 'padding2': b'\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
99 }
100
101 def generate_timestamp(self, content_id: str) -> int:
102 """Generate a deterministic timestamp based on content ID"""
103 # Create a hash of the content ID and use it to generate a timestamp
104 hash_obj = hashlib.md5(content_id.encode())
105 hash_int = int(hash_obj.hexdigest()[:8], 16)
106
107 # Map to a reasonable timestamp range (2013-2024)
108 base_timestamp = 0x52000000 # Around 2013
109 max_offset = 0x10000000 # About 11 years range
110
111 timestamp = base_timestamp + (hash_int % max_offset)
112 return timestamp
113
114 def generate_rif_content(self, content_id: str) -> bytes:
115 """Generate RIF file content for a given content ID"""
116 # Start with base structure
117 rif_data = bytearray(1024) # Initialize 1024-byte array with zeros
118
119 # Fill in the known structure
120 offset = 0
121
122 # Magic number
123 rif_data[offset:offset+4] = self.base_structure['magic']
124 offset += 4
125
126 # Version
127 rif_data[offset:offset+2] = self.base_structure['version']
128 offset += 2
129
130 # Unknown field
131 rif_data[offset:offset+2] = self.base_structure['unknown1']
132 offset += 2
133
134 # Padding 1
135 rif_data[offset:offset+12] = self.base_structure['padding1']
136 offset += 12
137
138 # Timestamp (big-endian)
139 timestamp = self.generate_timestamp(content_id)
140 rif_data[offset:offset+4] = struct.pack('>I', timestamp)
141 offset += 4
142
143 # Padding 2
144 rif_data[offset:offset+8] = self.base_structure['padding2']
145 offset += 8
146
147 # Fill remaining bytes with pattern or zeros
148 # The rest of the file appears to be mostly zeros or encrypted data
149 # For a basic generator, we'll leave it as zeros
150
151 return bytes(rif_data)
152
153 def generate_rif_file(self, content_id: str, output_path: str) -> bool:
154 """Generate a RIF file for the given content ID"""
155 try:
156 rif_content = self.generate_rif_content(content_id)
157
158 with open(output_path, 'wb') as f:
159 f.write(rif_content)
160
161 print(f"Generated RIF file: {output_path}")
162 return True
163
164 except Exception as e:
165 print(f"Error generating RIF file: {e}")
166 return False
167 
168def main():
169 # Analyze existing RIF files
170 analyzer = RIFAnalyzer()
171 rif_directory = r"c:\Users\marco\source\repos\TEST-SC\ShadPKG\gift\RIF Files"
172
173 print("Analyzing existing RIF files...")
174 analyses = analyzer.analyze_directory(rif_directory)
175
176 print(f"\nAnalyzed {len(analyses)} RIF files")
177
178 # Find patterns
179 patterns = analyzer.find_patterns(analyses)
180 print("\nCommon patterns found:")
181 print(f"Magic: {patterns['common_magic']}")
182 print(f"Version: {patterns['common_version']}")
183 print(f"Unknown1: {patterns['common_unknown1']}")
184 print(f"Timestamp range: {hex(patterns['timestamp_range'][0])} - {hex(patterns['timestamp_range'][1])}")
185
186 # Example: Generate a new RIF file
187 generator = RIFGenerator()
188
189 # Test with a retail game content ID
190 test_content_id = "EP0001-CUSA12345_00-TESTGAMERETAIL01"
191 output_file = f"{test_content_id}.rif"
192
193 print(f"\nGenerating test RIF file for: {test_content_id}")
194 success = generator.generate_rif_file(test_content_id, output_file)
195
196 if success:
197 print(f"Test RIF file generated successfully: {output_file}")
198
199 # Analyze the generated file to verify structure
200 test_analysis = analyzer.analyze_rif_file(output_file)
201 print(f"Generated file analysis:")
202 print(f" Size: {test_analysis['size']} bytes")
203 print(f" Magic: {test_analysis['magic']}")
204 print(f" Version: {test_analysis['version']}")
205 print(f" Timestamp: {hex(test_analysis['timestamp'])}")
206 print(f" Header: {test_analysis['header_hex']}")
207 
208if __name__ == "__main__":
209 main()