Seregon/ShadPKG

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

C++/47.3 KB/No license
ps4MEL/ps4_kernel.cpp
ShadPKG / ps4MEL / ps4_kernel.cpp
1/*
2 * ╔═══════════════════════════════════════════════════════════════════════════╗
3 * ║ PS4 KERNEL STUBS - IMPLEMENTATION ║
4 * ╠═══════════════════════════════════════════════════════════════════════════╣
5 * ║ Emulates sceKernel* syscalls with logging for debugging. ║
6 * ╚═══════════════════════════════════════════════════════════════════════════╝
7 */
8 
9#include "ps4_kernel.h"
10#include "ps4_memory.h"
11#include "ps4_tls.h"
12#include <chrono>
13#include <cstring>
14#include <ctime>
15#include <fcntl.h>
16#include <iomanip>
17#include <iostream>
18#include <map>
19#include <mutex>
20#include <sys/mman.h>
21#include <sys/stat.h>
22#include <unistd.h>
23 
24// VFS path prefix
25static const char *g_vfs_root = nullptr;
26 
27/*
28 * ┌─────────────────────────────────────────────────────────────────┐
29 * │ LOGGING HELPERS │
30 * └─────────────────────────────────────────────────────────────────┘
31 */
32#define LOG_STUB(name) \
33 std::cout << "[PS4Kernel] " << name << "() called" << std::endl
34 
35#define LOG_STUB_ARGS(name, args) \
36 std::cout << "[PS4Kernel] " << name << "(" << args << ") called" << std::endl
37 
38/*
39 * ┌─────────────────────────────────────────────────────────────────┐
40 * │ FILE DESCRIPTOR TABLE │
41 * └─────────────────────────────────────────────────────────────────┘
42 */
43static std::map<int32_t, int> g_fd_map; // PS4 fd -> host fd
44static int32_t g_next_fd = 3; // Start after stdin/stdout/stderr
45static std::mutex g_fd_mutex;
46 
47static int32_t AllocateFd(int hostFd) {
48 std::lock_guard<std::mutex> lock(g_fd_mutex);
49 int32_t ps4Fd = g_next_fd++;
50 g_fd_map[ps4Fd] = hostFd;
51 return ps4Fd;
52}
53 
54static int GetHostFd(int32_t ps4Fd) {
55 std::lock_guard<std::mutex> lock(g_fd_mutex);
56 auto it = g_fd_map.find(ps4Fd);
57 if (it == g_fd_map.end())
58 return -1;
59 return it->second;
60}
61 
62static void FreeFd(int32_t ps4Fd) {
63 std::lock_guard<std::mutex> lock(g_fd_mutex);
64 g_fd_map.erase(ps4Fd);
65}
66 
67/*
68 * ┌─────────────────────────────────────────────────────────────────┐
69 * │ VFS PATH TRANSLATION │
70 * │ │
71 * │ /app0/ → assets/ │
72 * │ /data/ → savedata/ │
73 * │ /host0/ → current directory │
74 * └─────────────────────────────────────────────────────────────────┘
75 */
76static std::string TranslatePath(const char *ps4Path) {
77 if (!ps4Path)
78 return "";
79 
80 std::string path(ps4Path);
81 std::string basePath = g_vfs_root ? g_vfs_root : "assets";
82 
83 // Remove leading slash if present
84 if (!path.empty() && path[0] == '/') {
85 // Check for known prefixes
86 if (path.rfind("/app0/", 0) == 0) {
87 return basePath + "/" + path.substr(6);
88 }
89 if (path.rfind("/data/", 0) == 0) {
90 return "savedata/" + path.substr(6);
91 }
92 if (path.rfind("/host0/", 0) == 0) {
93 return path.substr(7);
94 }
95 // Default: strip leading slash
96 return basePath + path;
97 }
98 
99 return basePath + "/" + path;
100}
101 
102extern "C" {
103 
104/*
105 * ┌─────────────────────────────────────────────────────────────────┐
106 * │ MEMORY SYSCALLS │
107 * └─────────────────────────────────────────────────────────────────┘
108 */
109 
110uint64_t sceKernelGetDirectMemorySize() {
111 LOG_STUB("sceKernelGetDirectMemorySize");
112 return ORBIS_KERNEL_TOTAL_MEM;
113}
114 
115int32_t sceKernelAllocateDirectMemory(int64_t searchStart, int64_t searchEnd,
116 uint64_t len, uint64_t alignment,
117 int32_t memoryType,
118 int64_t *physAddrOut) {
119 LOG_STUB_ARGS("sceKernelAllocateDirectMemory",
120 "len=" << len << ", alignment=" << alignment);
121 
122 // Use our existing PS4 memory system
123 static int64_t nextPhysAddr = 0x100000; // Start at 1MB
124 
125 // Align the address
126 if (alignment > 0) {
127 nextPhysAddr = (nextPhysAddr + alignment - 1) & ~(alignment - 1);
128 }
129 
130 if (physAddrOut) {
131 *physAddrOut = nextPhysAddr;
132 }
133 
134 nextPhysAddr += len;
135 return ORBIS_OK;
136}
137 
138int32_t sceKernelMapDirectMemory(void **addr, uint64_t len, int32_t prot,
139 int32_t flags, int64_t physAddr,
140 uint64_t alignment) {
141 LOG_STUB_ARGS("sceKernelMapDirectMemory",
142 "len=" << len << ", physAddr=0x" << std::hex << physAddr);
143 
144 // Allocate memory using standard mmap
145 void *ptr = mmap(nullptr, len, PROT_READ | PROT_WRITE,
146 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
147 
148 if (ptr == MAP_FAILED) {
149 return ORBIS_KERNEL_ERROR_ENOMEM;
150 }
151 
152 if (addr) {
153 *addr = ptr;
154 }
155 
156 return ORBIS_OK;
157}
158 
159int32_t sceKernelMapFlexibleMemory(void **addrInOut, uint64_t len, int32_t prot,
160 int32_t flags) {
161 LOG_STUB_ARGS("sceKernelMapFlexibleMemory", "len=" << len);
162 
163 void *ptr = mmap(nullptr, len, PROT_READ | PROT_WRITE,
164 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
165 
166 if (ptr == MAP_FAILED) {
167 return ORBIS_KERNEL_ERROR_ENOMEM;
168 }
169 
170 if (addrInOut) {
171 *addrInOut = ptr;
172 }
173 
174 return ORBIS_OK;
175}
176 
177int32_t sceKernelMunmap(void *addr, uint64_t len) {
178 LOG_STUB_ARGS("sceKernelMunmap", "addr=" << addr << ", len=" << len);
179 
180 if (munmap(addr, len) != 0) {
181 return ORBIS_KERNEL_ERROR_EINVAL;
182 }
183 return ORBIS_OK;
184}
185 
186int32_t sceKernelMprotect(const void *addr, uint64_t size, int32_t prot) {
187 LOG_STUB_ARGS("sceKernelMprotect", "addr=" << addr << ", size=" << size);
188 
189 int hostProt = 0;
190 if (prot & ORBIS_KERNEL_PROT_CPU_READ)
191 hostProt |= PROT_READ;
192 if (prot & ORBIS_KERNEL_PROT_CPU_WRITE)
193 hostProt |= PROT_WRITE;
194 
195 if (mprotect(const_cast<void *>(addr), size, hostProt) != 0) {
196 return ORBIS_KERNEL_ERROR_EINVAL;
197 }
198 return ORBIS_OK;
199}
200 
201/*
202 * ┌─────────────────────────────────────────────────────────────────┐
203 * │ FILE SYSCALLS │
204 * └─────────────────────────────────────────────────────────────────┘
205 */
206 
207int32_t sceKernelOpen(const char *path, int32_t flags, uint16_t mode) {
208 std::string hostPath = TranslatePath(path);
209 LOG_STUB_ARGS("sceKernelOpen",
210 "path=\"" << path << "\" -> \"" << hostPath << "\"");
211 
212 // Convert PS4 flags to POSIX
213 int hostFlags = 0;
214 if (flags & 0x0001)
215 hostFlags |= O_WRONLY;
216 if (flags & 0x0002)
217 hostFlags |= O_RDWR;
218 if (flags & 0x0200)
219 hostFlags |= O_CREAT;
220 if (flags & 0x0400)
221 hostFlags |= O_TRUNC;
222 if (flags & 0x0008)
223 hostFlags |= O_APPEND;
224 if ((flags & 0x0003) == 0)
225 hostFlags |= O_RDONLY;
226 
227 int hostFd = open(hostPath.c_str(), hostFlags, mode);
228 if (hostFd < 0) {
229 std::cerr << "[PS4Kernel] sceKernelOpen FAILED: " << hostPath << std::endl;
230 return ORBIS_KERNEL_ERROR_ENOENT;
231 }
232 
233 return AllocateFd(hostFd);
234}
235 
236int32_t sceKernelClose(int32_t fd) {
237 LOG_STUB_ARGS("sceKernelClose", "fd=" << fd);
238 
239 int hostFd = GetHostFd(fd);
240 if (hostFd < 0)
241 return ORBIS_KERNEL_ERROR_EINVAL;
242 
243 close(hostFd);
244 FreeFd(fd);
245 return ORBIS_OK;
246}
247 
248int64_t sceKernelRead(int32_t fd, void *buf, uint64_t nbytes) {
249 int hostFd = GetHostFd(fd);
250 if (hostFd < 0)
251 return ORBIS_KERNEL_ERROR_EINVAL;
252 
253 ssize_t result = read(hostFd, buf, nbytes);
254 if (result < 0)
255 return ORBIS_KERNEL_ERROR_EIO;
256 return result;
257}
258 
259int64_t sceKernelWrite(int32_t fd, const void *buf, uint64_t nbytes) {
260 int hostFd = GetHostFd(fd);
261 if (hostFd < 0)
262 return ORBIS_KERNEL_ERROR_EINVAL;
263 
264 ssize_t result = write(hostFd, buf, nbytes);
265 if (result < 0)
266 return ORBIS_KERNEL_ERROR_EIO;
267 return result;
268}
269 
270int64_t sceKernelLseek(int32_t fd, int64_t offset, int32_t whence) {
271 int hostFd = GetHostFd(fd);
272 if (hostFd < 0)
273 return ORBIS_KERNEL_ERROR_EINVAL;
274 
275 off_t result = lseek(hostFd, offset, whence);
276 if (result < 0)
277 return ORBIS_KERNEL_ERROR_EINVAL;
278 return result;
279}
280 
281int32_t sceKernelStat(const char *path, OrbisKernelStat *sb) {
282 std::string hostPath = TranslatePath(path);
283 LOG_STUB_ARGS("sceKernelStat", "path=\"" << hostPath << "\"");
284 
285 struct stat hostStat;
286 if (stat(hostPath.c_str(), &hostStat) != 0) {
287 return ORBIS_KERNEL_ERROR_ENOENT;
288 }
289 
290 memset(sb, 0, sizeof(OrbisKernelStat));
291 sb->st_size = hostStat.st_size;
292 sb->st_mode = hostStat.st_mode;
293 return ORBIS_OK;
294}
295 
296int32_t sceKernelFstat(int32_t fd, OrbisKernelStat *sb) {
297 int hostFd = GetHostFd(fd);
298 if (hostFd < 0)
299 return ORBIS_KERNEL_ERROR_EINVAL;
300 
301 struct stat hostStat;
302 if (fstat(hostFd, &hostStat) != 0) {
303 return ORBIS_KERNEL_ERROR_EIO;
304 }
305 
306 memset(sb, 0, sizeof(OrbisKernelStat));
307 sb->st_size = hostStat.st_size;
308 sb->st_mode = hostStat.st_mode;
309 return ORBIS_OK;
310}
311 
312int32_t sceKernelMkdir(const char *path, uint16_t mode) {
313 std::string hostPath = TranslatePath(path);
314 LOG_STUB_ARGS("sceKernelMkdir", "path=\"" << hostPath << "\"");
315 
316 if (mkdir(hostPath.c_str(), mode) != 0) {
317 return ORBIS_KERNEL_ERROR_EEXIST;
318 }
319 return ORBIS_OK;
320}
321 
322/*
323 * ┌─────────────────────────────────────────────────────────────────┐
324 * │ TIME SYSCALLS │
325 * └─────────────────────────────────────────────────────────────────┘
326 */
327 
328uint64_t sceKernelGetProcessTime() {
329 auto now = std::chrono::steady_clock::now();
330 auto duration = now.time_since_epoch();
331 return std::chrono::duration_cast<std::chrono::microseconds>(duration)
332 .count();
333}
334 
335uint64_t sceKernelGetProcessTimeCounter() { return sceKernelGetProcessTime(); }
336 
337uint64_t sceKernelGetProcessTimeCounterFrequency() {
338 return 1000000; // 1 MHz (microsecond precision)
339}
340 
341int32_t sceKernelGettimeofday(OrbisKernelTimeval *tv) {
342 if (!tv)
343 return ORBIS_KERNEL_ERROR_EINVAL;
344 
345 auto now = std::chrono::system_clock::now();
346 auto duration = now.time_since_epoch();
347 auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
348 auto micros =
349 std::chrono::duration_cast<std::chrono::microseconds>(duration - seconds);
350 
351 tv->tv_sec = seconds.count();
352 tv->tv_usec = micros.count();
353 return ORBIS_OK;
354}
355 
356int32_t sceKernelClockGettime(int32_t clockId, OrbisKernelTimespec *tp) {
357 if (!tp)
358 return ORBIS_KERNEL_ERROR_EINVAL;
359 
360 auto now = std::chrono::steady_clock::now();
361 auto duration = now.time_since_epoch();
362 auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
363 auto nanos =
364 std::chrono::duration_cast<std::chrono::nanoseconds>(duration - seconds);
365 
366 tp->tv_sec = seconds.count();
367 tp->tv_nsec = nanos.count();
368 return ORBIS_OK;
369}
370 
371int32_t sceKernelUsleep(uint32_t microseconds) {
372 usleep(microseconds);
373 return ORBIS_OK;
374}
375 
376int32_t sceKernelNanosleep(const OrbisKernelTimespec *rqtp,
377 OrbisKernelTimespec *rmtp) {
378 if (!rqtp)
379 return ORBIS_KERNEL_ERROR_EINVAL;
380 
381 struct timespec req = {.tv_sec = static_cast<time_t>(rqtp->tv_sec),
382 .tv_nsec = static_cast<long>(rqtp->tv_nsec)};
383 struct timespec rem;
384 
385 nanosleep(&req, &rem);
386 
387 if (rmtp) {
388 rmtp->tv_sec = rem.tv_sec;
389 rmtp->tv_nsec = rem.tv_nsec;
390 }
391 return ORBIS_OK;
392}
393 
394/*
395 * ┌─────────────────────────────────────────────────────────────────┐
396 * │ PROCESS SYSCALLS │
397 * └─────────────────────────────────────────────────────────────────┘
398 */
399 
400int32_t sceKernelGetCurrentCpu() {
401 return 0; // Always return CPU 0
402}
403 
404int32_t sceKernelGetProcessId() {
405 return 1; // Fake PID
406}
407 
408/*
409 * ┌─────────────────────────────────────────────────────────────────┐
410 * │ ERROR HANDLING │
411 * └─────────────────────────────────────────────────────────────────┘
412 */
413 
414int32_t *__Error() { return PS4Emu::GetErrno(); }
415 
416} // extern "C"
417 
418/*
419 * ┌─────────────────────────────────────────────────────────────────┐
420 * │ INITIALIZATION │
421 * └─────────────────────────────────────────────────────────────────┘
422 */
423namespace PS4Emu {
424 
425void SetVfsRoot(const char *root) { g_vfs_root = root; }
426 
427} // namespace PS4Emu
428