Seregon/ShadPKG

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

C++/47.3 KB/No license
ps4MEL/ps4_memory.cpp
ShadPKG / ps4MEL / ps4_memory.cpp
1/*
2 * ╔═══════════════════════════════════════════════════════════════════════════╗
3 * ║ PS4 MEMORY EMULATION - IMPLEMENTATION ║
4 * ╠═══════════════════════════════════════════════════════════════════════════╣
5 * ║ Uses mmap to allocate a region that simulates PS4 global address space. ║
6 * ╚═══════════════════════════════════════════════════════════════════════════╝
7 */
8 
9#include "ps4_memory.h"
10#include <cmath>
11#include <cstring>
12#include <iostream>
13 
14#ifdef __APPLE__
15#include <mach/mach.h>
16#include <sys/mman.h>
17#elif defined(__linux__)
18#include <sys/mman.h>
19#elif defined(_WIN32)
20#include <windows.h>
21#endif
22 
23namespace PS4Emu {
24 
25// Global memory block pointer
26static uint8_t *g_ps4GlobalMemory = nullptr;
27static bool g_initialized = false;
28 
29/*
30 * ┌─────────────────────────────────────────────────────────────────┐
31 * │ PLATFORM-SPECIFIC MMAP │
32 * └─────────────────────────────────────────────────────────────────┘
33 */
34 
35static void *AllocateMemoryBlock(size_t size) {
36#if defined(__APPLE__) || defined(__linux__)
37 // Use mmap with MAP_ANONYMOUS for a zeroed memory block
38 void *ptr = mmap(nullptr, // Let OS choose address
39 size,
40 PROT_READ | PROT_WRITE, // Read/write access
41 MAP_PRIVATE | MAP_ANONYMOUS, // Private anonymous mapping
42 -1, // No file descriptor
43 0 // No offset
44 );
45 
46 if (ptr == MAP_FAILED) {
47 std::cerr << "[PS4Emu] ERROR: mmap failed for global memory!" << std::endl;
48 return nullptr;
49 }
50 return ptr;
51 
52#elif defined(_WIN32)
53 void *ptr =
54 VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
55 if (!ptr) {
56 std::cerr << "[PS4Emu] ERROR: VirtualAlloc failed for global memory!"
57 << std::endl;
58 }
59 return ptr;
60#else
61#error "Unsupported platform"
62#endif
63}
64 
65static void FreeMemoryBlock(void *ptr, size_t size) {
66#if defined(__APPLE__) || defined(__linux__)
67 munmap(ptr, size);
68#elif defined(_WIN32)
69 VirtualFree(ptr, 0, MEM_RELEASE);
70#endif
71}
72 
73/*
74 * ┌─────────────────────────────────────────────────────────────────┐
75 * │ PUBLIC API IMPLEMENTATION │
76 * └─────────────────────────────────────────────────────────────────┘
77 */
78 
79bool InitializeMemory() {
80 if (g_initialized) {
81 std::cerr << "[PS4Emu] Warning: Memory already initialized" << std::endl;
82 return true;
83 }
84 
85 std::cout << "[PS4Emu] Initializing PS4 global memory emulation..."
86 << std::endl;
87 std::cout << "[PS4Emu] Allocating " << (PS4_GLOBAL_SIZE / 1024 / 1024)
88 << "MB for globals" << std::endl;
89 
90 g_ps4GlobalMemory =
91 static_cast<uint8_t *>(AllocateMemoryBlock(PS4_GLOBAL_SIZE));
92 
93 if (!g_ps4GlobalMemory) {
94 return false;
95 }
96 
97 // Zero-initialize the entire block
98 std::memset(g_ps4GlobalMemory, 0, PS4_GLOBAL_SIZE);
99 
100 g_initialized = true;
101 std::cout << "[PS4Emu] Global memory ready at host address: "
102 << static_cast<void *>(g_ps4GlobalMemory) << std::endl;
103 
104 // ┌─────────────────────────────────────────────────────────────────────────┐
105 // │ INITIALIZE CRITICAL GLOBAL STUBS │
106 // │ These addresses are heavily accessed and need valid pointers │
107 // └─────────────────────────────────────────────────────────────────────────┘
108 
109 // Create a "safe zone" for dummy objects at the end of our region
110 // Each dummy object is 256 bytes to hold a minimal VTable-like structure
111 constexpr size_t DUMMY_OBJECT_SIZE = 256;
112 constexpr size_t SAFE_ZONE_START =
113 PS4_GLOBAL_SIZE - (1024 * 1024); // Last 1MB
114 static size_t nextDummyOffset = SAFE_ZONE_START;
115 
116 auto allocateDummy = [&]() -> uint64_t {
117 uint64_t offset = nextDummyOffset;
118 nextDummyOffset += DUMMY_OBJECT_SIZE;
119 
120 // Get the HOST address of this dummy object
121 uint8_t *hostAddr = g_ps4GlobalMemory + offset;
122 uint64_t hostAddrValue = reinterpret_cast<uint64_t>(hostAddr);
123 
124 // Initialize the dummy with HOST pointers to itself
125 // This allows chained dereferences to work correctly
126 uint64_t *ptr = reinterpret_cast<uint64_t *>(hostAddr);
127 for (size_t i = 0; i < DUMMY_OBJECT_SIZE / sizeof(uint64_t); i++) {
128 ptr[i] = hostAddrValue; // Each slot points back to this same object (host addr)
129 }
130 
131 return hostAddrValue; // Return the HOST address, not the offset
132 };
133 
134 // List of critical globals that need initialization
135 // Format: {address, description}
136 struct GlobalInit {
137 uint64_t addr;
138 const char *desc;
139 };
140 
141 GlobalInit criticalGlobals[] = {
142 {0x995e78, "Main game context (36k refs)"},
143 {0x9bb298, "Object pool (4k refs)"},
144 {0xa21da8, "System table A"},
145 {0xa21d98, "System table B"},
146 {0x949e38, "Runtime state"},
147 {0xa21d88, "System table C"},
148 {0x995e60, "Engine pointer A"},
149 {0x995e68, "Engine pointer B"},
150 {0xa21d90, "System table D"},
151 {0xa21d80, "System table E"},
152 {0xa21da0, "System table F"},
153 {0x925500, "Resource manager A"},
154 {0x9254b0, "Resource manager B"},
155 {0x925420, "Resource manager C"},
156 {0x9d00d4, "Config value"},
157 {0x9bc3d0, "Memory allocator"},
158 {0x99a9e8, "Scene manager"},
159 {0x9992b0, "Render context"},
160 {0x96d060, "Audio manager"},
161 {0x959900, "Input manager"},
162 {0x9d9e28, "TLS context"}, // The one causing the first crash
163 {0x9d9e20, "TLS pointer A"},
164 {0x9d9e22, "TLS pointer B"},
165 {0x961800, "Module table A"},
166 {0x961850, "Module table B"},
167 {0x995e40, "Global State A"},
168 {0x997420, "Global State B"},
169 {0x997438, "Global State C"},
170 {0x99743a, "Global State D"},
171 {0x997430, "Global State E"},
172 // Additional managers discovered during runtime
173 {0x9bb2a0, "Object Pool B"},
174 {0x9bb2a8, "Object Pool C"},
175 {0x99a9f0, "Scene Manager B"},
176 {0x99a9f8, "Scene Manager C"},
177 {0x9992b8, "Render Context B"},
178 {0x9992c0, "Render Context C"},
179 {0x96d068, "Audio Manager B"},
180 {0x96d070, "Audio Manager C"},
181 {0x959908, "Input Manager B"},
182 {0x959910, "Input Manager C"},
183 {0x959918, "Input State"},
184 {0x9bc3d8, "Memory Allocator B"},
185 {0x9bc3e0, "Memory Allocator C"},
186 {0x9d00d8, "Config Value B"},
187 {0x9d00e0, "Config Value C"},
188 // Frame timing and loop control
189 {0x9d9e30, "Frame Counter"},
190 {0x9d9e38, "Delta Time Ptr"},
191 {0x9d9e40, "Last Frame Time"},
192 // Additional critical pointers from crash analysis
193 {0x961858, "Module Table C"},
194 {0x961860, "Module Table D"},
195 {0x995e80, "Engine Pointer C"},
196 {0x995e88, "Engine Pointer D"},
197 };
198 
199 std::cout << "[PS4Emu] Initializing "
200 << (sizeof(criticalGlobals) / sizeof(GlobalInit))
201 << " critical global stubs..." << std::endl;
202 
203 for (const auto &g : criticalGlobals) {
204 uint64_t dummyAddr = allocateDummy();
205 // Write the dummy address into the global slot
206 *reinterpret_cast<uint64_t *>(g_ps4GlobalMemory + g.addr) = dummyAddr;
207 }
208 
209 std::cout << "[PS4Emu] Global stubs initialized successfully" << std::endl;
210 
211 // ┌─────────────────────────────────────────────────────────────────────────┐
212 // │ INITIALIZE TIMING CONSTANTS │
213 // │ These are float values used in frame timing calculations │
214 // │ Without them, loops multiply by 0 forever │
215 // └─────────────────────────────────────────────────────────────────────────┘
216 
217 // Frame timing values (addresses 0x7d69c4 - 0x7d69d4)
218 // These appear to be reciprocals of loop iteration counts
219 float timingConstants[] = {
220 1.0f / 65536.0f, // 0x7d69c4 - reciprocal of 0x10000
221 65536.0f, // 0x7d69c8 - loop count
222 1.0f / 65536.0f, // 0x7d69cc - reciprocal of 0x10000 (causes loop!)
223 65536.0f, // 0x7d69d0 - loop count
224 0.5f, // 0x7d69d4 - offset
225 };
226 
227 uint32_t timingAddrs[] = {0x7d69c4, 0x7d69c8, 0x7d69cc, 0x7d69d0, 0x7d69d4};
228 
229 for (size_t i = 0; i < 5; i++) {
230 *reinterpret_cast<float *>(g_ps4GlobalMemory + timingAddrs[i]) =
231 timingConstants[i];
232 }
233 
234 // Additional float constants found in the code
235 *reinterpret_cast<float *>(g_ps4GlobalMemory + 0x7cb0f4) = 1.0f;
236 *reinterpret_cast<float *>(g_ps4GlobalMemory + 0x7cb0f8) = 1.0f;
237 *reinterpret_cast<float *>(g_ps4GlobalMemory + 0x7d6a50) = 1.0f;
238 *reinterpret_cast<float *>(g_ps4GlobalMemory + 0x7d6a54) = 1.0f;
239 *reinterpret_cast<float *>(g_ps4GlobalMemory + 0x7d6a58) = 1.0f;
240 
241 std::cout << "[PS4Emu] Timing constants initialized" << std::endl;
242 
243 // ┌─────────────────────────────────────────────────────────────────────────┐
244 // │ PRE-POPULATE TRIG TABLES │
245 // │ The decompiler corrupted the loop that builds sine/cosine tables. │
246 // │ We pre-populate them here so the game skips the broken init code. │
247 // └─────────────────────────────────────────────────────────────────────────┘
248 
249 // Allocate trig table: 65536 entries * 4 bytes = 256KB
250 constexpr size_t TRIG_TABLE_ENTRIES = 0x10000;
251 constexpr size_t TRIG_TABLE_SIZE = TRIG_TABLE_ENTRIES * sizeof(float);
252 constexpr size_t TRIG_TABLE_OFFSET = SAFE_ZONE_START - TRIG_TABLE_SIZE;
253 
254 float *trigTable =
255 reinterpret_cast<float *>(g_ps4GlobalMemory + TRIG_TABLE_OFFSET);
256 
257 // Fill with sin/cos values (common game engine lookup table)
258 for (size_t i = 0; i < TRIG_TABLE_ENTRIES; i++) {
259 float angle = (float(i) / TRIG_TABLE_ENTRIES) * 2.0f * 3.14159265358979f;
260 trigTable[i] = std::sin(angle);
261 }
262 
263 // Store pointer to trig table at 0x9d9e28 (the address the loops check)
264 // This makes reg_rax valid so the check passes and loops are skipped
265 *reinterpret_cast<uint64_t *>(g_ps4GlobalMemory + 0x9d9e28) =
266 reinterpret_cast<uint64_t>(trigTable);
267 
268 std::cout << "[PS4Emu] Trig tables pre-populated (" << TRIG_TABLE_ENTRIES
269 << " entries)" << std::endl;
270 
271 // ┌─────────────────────────────────────────────────────────────────────────┐
272 // │ INITIALIZE FRAME COUNTER FOR DEBUGGING │
273 // │ This allows us to track if the main loop is actually running │
274 // └─────────────────────────────────────────────────────────────────────────┘
275
276 // Frame counter at a known location for debugging
277 *reinterpret_cast<uint64_t *>(g_ps4GlobalMemory + 0x9d9e30) = 0;
278
279 // Delta time initialized to 16.67ms (60 FPS)
280 *reinterpret_cast<float *>(g_ps4GlobalMemory + 0x9d9e38) = 0.01667f;
281
282 // Last frame time
283 *reinterpret_cast<uint64_t *>(g_ps4GlobalMemory + 0x9d9e40) = 0;
284 
285 std::cout << "[PS4Emu] Frame counter initialized at 0x9d9e30" << std::endl;
286 
287 return true;
288}
289 
290void ShutdownMemory() {
291 if (g_ps4GlobalMemory) {
292 std::cout << "[PS4Emu] Shutting down memory emulation..." << std::endl;
293 FreeMemoryBlock(g_ps4GlobalMemory, PS4_GLOBAL_SIZE);
294 g_ps4GlobalMemory = nullptr;
295 g_initialized = false;
296 }
297}
298 
299void *TranslateAddress(uint64_t ps4_addr) {
300 // ═══════════════════════════════════════════════════════════════════
301 // LOOP DETECTION: Count calls and exit after limit
302 // ═══════════════════════════════════════════════════════════════════
303 static uint64_t s_call_count = 0;
304 static constexpr uint64_t LOG_INTERVAL = 1000000; // Log every 1M calls
305 static constexpr uint64_t CALL_LIMIT = 50000000; // Exit after 50M calls
306 
307 s_call_count++;
308 
309 if (s_call_count % LOG_INTERVAL == 0) {
310 std::cout << "[PS4Emu] TranslateAddress calls: " << s_call_count
311 << " (last addr: 0x" << std::hex << ps4_addr << std::dec << ")"
312 << std::endl;
313 }
314 
315 if (s_call_count >= CALL_LIMIT) {
316 std::cerr << "[PS4Emu] LIMIT REACHED: " << CALL_LIMIT
317 << " memory accesses. Exiting to prevent hang." << std::endl;
318 std::exit(1);
319 }
320 
321 // Safety check: ensure memory is initialized
322 if (!g_initialized || !g_ps4GlobalMemory) {
323 std::cerr << "[PS4Emu] ERROR: Accessing global memory before "
324 "initialization! addr=0x"
325 << std::hex << ps4_addr << std::endl;
326 return nullptr;
327 }
328 
329 // Check if address is within the PS4 global range
330 if (ps4_addr >= PS4_GLOBAL_SIZE) {
331 // Silent remapping - too noisy otherwise
332 ps4_addr = ps4_addr % PS4_GLOBAL_SIZE;
333 }
334 
335 // Translate: PS4 address -> our allocated block
336 return g_ps4GlobalMemory + ps4_addr;
337}
338 
339// ┌─────────────────────────────────────────────────────────────────────────┐
340// │ DEBUG: Get frame counter to check if main loop is running │
341// └─────────────────────────────────────────────────────────────────────────┘
342uint64_t GetFrameCounter() {
343 if (!g_initialized || !g_ps4GlobalMemory) {
344 return 0;
345 }
346 return *reinterpret_cast<uint64_t *>(g_ps4GlobalMemory + 0x9d9e30);
347}
348 
349void IncrementFrameCounter() {
350 if (!g_initialized || !g_ps4GlobalMemory) {
351 return;
352 }
353 uint64_t *counter = reinterpret_cast<uint64_t *>(g_ps4GlobalMemory + 0x9d9e30);
354 (*counter)++;
355
356 // Log every 60 frames (approximately 1 second at 60 FPS)
357 if (*counter % 60 == 0) {
358 std::cout << "[PS4Emu] Frame: " << *counter << std::endl;
359 }
360}
361 
362void *GetGlobalMemoryBase() {
363 return g_ps4GlobalMemory;
364}
365 
366} // namespace PS4Emu
367