Seregon/ShadPKG

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

C++/47.3 KB/No license
ps4MEL/ps4_runtime_dispatch.h
ShadPKG / ps4MEL / ps4_runtime_dispatch.h
1#pragma once
2// ╔═══════════════════════════════════════════════════════════════════════════╗
3// ║ PS4 Runtime Dispatch System ║
4// ║ Intercepts vtable/indirect calls and routes them to backend impls ║
5// ╚═══════════════════════════════════════════════════════════════════════════╝
6 
7#include <cstdint>
8#include <functional>
9#include <unordered_map>
10#include <string>
11#include <iostream>
12#include <mutex>
13 
14namespace PS4Runtime {
15 
16// ═══════════════════════════════════════════════════════════════════════════
17// Semantic IDs for known PS4 functions
18// ═══════════════════════════════════════════════════════════════════════════
19enum class SemanticID : uint32_t {
20 Unknown = 0,
21
22 // GNM Graphics
23 GNM_SubmitCommandBuffers,
24 GNM_SubmitAndFlipCommandBuffers,
25 GNM_DrawIndex,
26 GNM_DrawIndexAuto,
27 GNM_Dispatch,
28 GNM_SetRenderTarget,
29 GNM_SetViewport,
30 GNM_SetScissor,
31 GNM_ClearRenderTarget,
32 GNM_WaitFlipDone,
33
34 // VideoOut
35 VideoOut_Open,
36 VideoOut_Close,
37 VideoOut_RegisterBuffers,
38 VideoOut_SubmitFlip,
39 VideoOut_GetFlipStatus,
40 VideoOut_SetFlipRate,
41
42 // Pad
43 Pad_Open,
44 Pad_Close,
45 Pad_Read,
46 Pad_ReadState,
47
48 // Audio
49 Audio_Open,
50 Audio_Close,
51 Audio_Output,
52
53 // System
54 Kernel_Usleep,
55 Kernel_Nanosleep,
56
57 MAX_SEMANTIC_ID
58};
59 
60// ═══════════════════════════════════════════════════════════════════════════
61// Function signature for dispatched calls
62// Using void* for maximum flexibility - actual casting happens in impl
63// ═══════════════════════════════════════════════════════════════════════════
64using DispatchFn = std::function<int64_t(void* obj, uint64_t* args, int argCount)>;
65 
66// ═══════════════════════════════════════════════════════════════════════════
67// Runtime Registry - maps function pointers to semantic implementations
68// ═══════════════════════════════════════════════════════════════════════════
69class RuntimeRegistry {
70public:
71 static RuntimeRegistry& Instance() {
72 static RuntimeRegistry instance;
73 return instance;
74 }
75
76 // Register a known function pointer -> semantic mapping
77 void RegisterFunction(void* fnPtr, SemanticID id, const std::string& name) {
78 std::lock_guard<std::mutex> lock(mutex_);
79 fnToSemantic_[fnPtr] = id;
80 semanticNames_[id] = name;
81 }
82
83 // Register a backend implementation for a semantic ID
84 void RegisterBackend(SemanticID id, DispatchFn impl) {
85 std::lock_guard<std::mutex> lock(mutex_);
86 backends_[id] = impl;
87 }
88
89 // Resolve a function pointer to its semantic ID
90 SemanticID Resolve(void* fnPtr) {
91 std::lock_guard<std::mutex> lock(mutex_);
92 auto it = fnToSemantic_.find(fnPtr);
93 if (it != fnToSemantic_.end()) {
94 return it->second;
95 }
96 return SemanticID::Unknown;
97 }
98
99 // Get backend implementation for a semantic ID
100 DispatchFn GetBackend(SemanticID id) {
101 std::lock_guard<std::mutex> lock(mutex_);
102 auto it = backends_.find(id);
103 if (it != backends_.end()) {
104 return it->second;
105 }
106 return nullptr;
107 }
108
109 // Get name for logging
110 const std::string& GetName(SemanticID id) {
111 static std::string unknown = "Unknown";
112 std::lock_guard<std::mutex> lock(mutex_);
113 auto it = semanticNames_.find(id);
114 if (it != semanticNames_.end()) {
115 return it->second;
116 }
117 return unknown;
118 }
119
120 // Log unknown function pointer for later analysis
121 void LogUnknown(void* obj, uint64_t offset, void* fnPtr) {
122 std::lock_guard<std::mutex> lock(mutex_);
123 unknownCalls_[fnPtr]++;
124
125 // Log first occurrence and every 1000th call
126 if (unknownCalls_[fnPtr] == 1 || unknownCalls_[fnPtr] % 1000 == 0) {
127 std::cout << "[DISPATCH] Unknown vtable call: obj=" << obj
128 << " offset=0x" << std::hex << offset
129 << " fn=" << fnPtr
130 << " count=" << std::dec << unknownCalls_[fnPtr]
131 << std::endl;
132 }
133 }
134
135 // Get statistics
136 void PrintStats() {
137 std::lock_guard<std::mutex> lock(mutex_);
138 std::cout << "\n[DISPATCH] === Runtime Dispatch Statistics ===" << std::endl;
139 std::cout << "[DISPATCH] Known functions: " << fnToSemantic_.size() << std::endl;
140 std::cout << "[DISPATCH] Unknown unique calls: " << unknownCalls_.size() << std::endl;
141
142 std::cout << "[DISPATCH] Top unknown calls:" << std::endl;
143 // Sort by count and show top 10
144 std::vector<std::pair<void*, uint64_t>> sorted(unknownCalls_.begin(), unknownCalls_.end());
145 std::sort(sorted.begin(), sorted.end(),
146 [](const auto& a, const auto& b) { return a.second > b.second; });
147
148 int shown = 0;
149 for (const auto& [ptr, count] : sorted) {
150 if (shown++ >= 10) break;
151 std::cout << " fn=" << ptr << " count=" << count << std::endl;
152 }
153 }
154 
155private:
156 RuntimeRegistry() = default;
157
158 std::mutex mutex_;
159 std::unordered_map<void*, SemanticID> fnToSemantic_;
160 std::unordered_map<SemanticID, std::string> semanticNames_;
161 std::unordered_map<SemanticID, DispatchFn> backends_;
162 std::unordered_map<void*, uint64_t> unknownCalls_;
163};
164 
165// ═══════════════════════════════════════════════════════════════════════════
166// Main dispatch function - called from decompiled code
167// ═══════════════════════════════════════════════════════════════════════════
168 
169// Dispatch for vtable call: call [obj + offset]
170inline int64_t ps4_vtable_dispatch(void* obj, uint64_t offset,
171 uint64_t a1 = 0, uint64_t a2 = 0,
172 uint64_t a3 = 0, uint64_t a4 = 0,
173 uint64_t a5 = 0, uint64_t a6 = 0) {
174 if (!obj) {
175 std::cerr << "[DISPATCH] NULL object in vtable call, offset=0x"
176 << std::hex << offset << std::endl;
177 return 0;
178 }
179
180 // Read function pointer from vtable
181 void* fnPtr = *reinterpret_cast<void**>(reinterpret_cast<uint8_t*>(obj) + offset);
182
183 auto& registry = RuntimeRegistry::Instance();
184 SemanticID id = registry.Resolve(fnPtr);
185
186 if (id != SemanticID::Unknown) {
187 auto backend = registry.GetBackend(id);
188 if (backend) {
189 uint64_t args[] = {a1, a2, a3, a4, a5, a6};
190 return backend(obj, args, 6);
191 }
192 }
193
194 // Unknown function - log it
195 registry.LogUnknown(obj, offset, fnPtr);
196 return 0;
197}
198 
199// Dispatch for indirect register call: call reg
200inline int64_t ps4_indirect_dispatch(void* fnPtr,
201 uint64_t a1 = 0, uint64_t a2 = 0,
202 uint64_t a3 = 0, uint64_t a4 = 0,
203 uint64_t a5 = 0, uint64_t a6 = 0) {
204 if (!fnPtr) {
205 std::cerr << "[DISPATCH] NULL function pointer in indirect call" << std::endl;
206 return 0;
207 }
208
209 auto& registry = RuntimeRegistry::Instance();
210 SemanticID id = registry.Resolve(fnPtr);
211
212 if (id != SemanticID::Unknown) {
213 auto backend = registry.GetBackend(id);
214 if (backend) {
215 uint64_t args[] = {a1, a2, a3, a4, a5, a6};
216 return backend(nullptr, args, 6);
217 }
218 }
219
220 // Unknown function - log it
221 registry.LogUnknown(nullptr, 0, fnPtr);
222 return 0;
223}
224 
225// ═══════════════════════════════════════════════════════════════════════════
226// Initialize the runtime dispatch system with known PS4 functions
227// ═══════════════════════════════════════════════════════════════════════════
228void InitializeDispatch();
229 
230} // namespace PS4Runtime
231 
232// C-compatible wrappers for use in decompiled code
233extern "C" {
234 int64_t ps4_vtable_dispatch_c(void* obj, uint64_t offset,
235 uint64_t a1, uint64_t a2, uint64_t a3,
236 uint64_t a4, uint64_t a5, uint64_t a6);
237
238 int64_t ps4_indirect_dispatch_c(void* fnPtr,
239 uint64_t a1, uint64_t a2, uint64_t a3,
240 uint64_t a4, uint64_t a5, uint64_t a6);
241}
242