A tool for deriving PKG packet encryption keys for ps4 written in c++
| 1 | /* |
| 2 | * ╔═══════════════════════════════════════════════════════════════════════════╗ |
| 3 | * ║ PS4 PTHREAD STUBS - IMPLEMENTATION ║ |
| 4 | * ╠═══════════════════════════════════════════════════════════════════════════╣ |
| 5 | * ║ Real pthread wrappers for PS4 threading primitives. ║ |
| 6 | * ╚═══════════════════════════════════════════════════════════════════════════╝ |
| 7 | */ |
| 8 | |
| 9 | #include "ps4_pthread.h" |
| 10 | #include "ps4_kernel.h" |
| 11 | #include "ps4_tls.h" |
| 12 | #include <condition_variable> |
| 13 | #include <cstring> |
| 14 | #include <iostream> |
| 15 | #include <map> |
| 16 | #include <mutex> |
| 17 | #include <shared_mutex> |
| 18 | #include <thread> |
| 19 | |
| 20 | /* |
| 21 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 22 | * │ INTERNAL STRUCTURES │ |
| 23 | * └─────────────────────────────────────────────────────────────────┘ |
| 24 | */ |
| 25 | |
| 26 | struct PS4Thread { |
| 27 | std::thread native; |
| 28 | void *(*entry)(void *); |
| 29 | void *arg; |
| 30 | void *retval; |
| 31 | char name[32]; |
| 32 | bool detached; |
| 33 | PS4Emu::Tcb *tcb; |
| 34 | }; |
| 35 | |
| 36 | struct PS4Mutex { |
| 37 | std::recursive_mutex native; |
| 38 | char name[32]; |
| 39 | int32_t type; |
| 40 | }; |
| 41 | |
| 42 | struct PS4Cond { |
| 43 | std::condition_variable_any native; |
| 44 | char name[32]; |
| 45 | }; |
| 46 | |
| 47 | struct PS4Rwlock { |
| 48 | std::shared_mutex native; |
| 49 | char name[32]; |
| 50 | }; |
| 51 | |
| 52 | /* |
| 53 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 54 | * │ THREAD REGISTRY │ |
| 55 | * └─────────────────────────────────────────────────────────────────┘ |
| 56 | */ |
| 57 | |
| 58 | static std::map<ScePthread, PS4Thread *> g_threads; |
| 59 | static std::mutex g_threads_lock; |
| 60 | static thread_local PS4Thread *g_current_thread = nullptr; |
| 61 | |
| 62 | // Thread-specific data |
| 63 | static std::map<uint32_t, std::map<std::thread::id, void *>> g_tsd; |
| 64 | static std::map<uint32_t, void (*)(void *)> g_tsd_destructors; |
| 65 | static std::mutex g_tsd_lock; |
| 66 | static uint32_t g_next_key = 1; |
| 67 | |
| 68 | /* |
| 69 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 70 | * │ THREAD WRAPPER │ |
| 71 | * └─────────────────────────────────────────────────────────────────┘ |
| 72 | */ |
| 73 | |
| 74 | static void ThreadWrapper(PS4Thread *thrd) { |
| 75 | // Set up TLS for this thread |
| 76 | thrd->tcb = PS4Emu::CreateTcb(); |
| 77 | PS4Emu::SetTcbBase(thrd->tcb); |
| 78 | g_current_thread = thrd; |
| 79 | |
| 80 | std::cout << "[PS4Thread] Thread '" << thrd->name << "' started" << std::endl; |
| 81 | |
| 82 | // Call the actual entry point |
| 83 | thrd->retval = thrd->entry(thrd->arg); |
| 84 | |
| 85 | std::cout << "[PS4Thread] Thread '" << thrd->name << "' exited" << std::endl; |
| 86 | |
| 87 | // Cleanup TLS |
| 88 | PS4Emu::DestroyTcb(thrd->tcb); |
| 89 | } |
| 90 | |
| 91 | extern "C" { |
| 92 | |
| 93 | /* |
| 94 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 95 | * │ THREAD LIFECYCLE │ |
| 96 | * └─────────────────────────────────────────────────────────────────┘ |
| 97 | */ |
| 98 | |
| 99 | int32_t scePthreadCreate(ScePthread *thread, const ScePthreadAttr *attr, |
| 100 | void *(*entry)(void *), void *arg, const char *name) { |
| 101 | std::cout << "[PS4Thread] scePthreadCreate(\"" << (name ? name : "unnamed") |
| 102 | << "\")" << std::endl; |
| 103 | |
| 104 | PS4Thread *thrd = new PS4Thread(); |
| 105 | thrd->entry = entry; |
| 106 | thrd->arg = arg; |
| 107 | thrd->retval = nullptr; |
| 108 | thrd->detached = false; |
| 109 | thrd->tcb = nullptr; |
| 110 | strncpy(thrd->name, name ? name : "thread", sizeof(thrd->name) - 1); |
| 111 | |
| 112 | // Start the thread |
| 113 | thrd->native = std::thread(ThreadWrapper, thrd); |
| 114 | |
| 115 | { |
| 116 | std::lock_guard<std::mutex> lock(g_threads_lock); |
| 117 | g_threads[thrd] = thrd; |
| 118 | } |
| 119 | |
| 120 | *thread = thrd; |
| 121 | return ORBIS_OK; |
| 122 | } |
| 123 | |
| 124 | int32_t scePthreadJoin(ScePthread thread, void **retval) { |
| 125 | PS4Thread *thrd = static_cast<PS4Thread *>(thread); |
| 126 | if (!thrd) |
| 127 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 128 | |
| 129 | std::cout << "[PS4Thread] scePthreadJoin(\"" << thrd->name << "\")" |
| 130 | << std::endl; |
| 131 | |
| 132 | if (thrd->native.joinable()) { |
| 133 | thrd->native.join(); |
| 134 | } |
| 135 | |
| 136 | if (retval) { |
| 137 | *retval = thrd->retval; |
| 138 | } |
| 139 | |
| 140 | { |
| 141 | std::lock_guard<std::mutex> lock(g_threads_lock); |
| 142 | g_threads.erase(thrd); |
| 143 | } |
| 144 | |
| 145 | delete thrd; |
| 146 | return ORBIS_OK; |
| 147 | } |
| 148 | |
| 149 | void scePthreadExit(void *retval) { |
| 150 | std::cout << "[PS4Thread] scePthreadExit" << std::endl; |
| 151 | if (g_current_thread) { |
| 152 | g_current_thread->retval = retval; |
| 153 | } |
| 154 | // Note: Can't actually exit std::thread from within |
| 155 | } |
| 156 | |
| 157 | ScePthread scePthreadSelf() { return g_current_thread; } |
| 158 | |
| 159 | int32_t scePthreadCancel(ScePthread thread) { |
| 160 | // std::thread doesn't support cancellation |
| 161 | std::cout << "[PS4Thread] scePthreadCancel (not implemented)" << std::endl; |
| 162 | return ORBIS_OK; |
| 163 | } |
| 164 | |
| 165 | int32_t scePthreadDetach(ScePthread thread) { |
| 166 | PS4Thread *thrd = static_cast<PS4Thread *>(thread); |
| 167 | if (!thrd) |
| 168 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 169 | |
| 170 | if (thrd->native.joinable()) { |
| 171 | thrd->native.detach(); |
| 172 | } |
| 173 | thrd->detached = true; |
| 174 | return ORBIS_OK; |
| 175 | } |
| 176 | |
| 177 | /* |
| 178 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 179 | * │ THREAD ATTRIBUTES │ |
| 180 | * └─────────────────────────────────────────────────────────────────┘ |
| 181 | */ |
| 182 | |
| 183 | int32_t scePthreadAttrInit(ScePthreadAttr *attr) { |
| 184 | *attr = new uint8_t[64](); |
| 185 | return ORBIS_OK; |
| 186 | } |
| 187 | |
| 188 | int32_t scePthreadAttrDestroy(ScePthreadAttr *attr) { |
| 189 | delete[] static_cast<uint8_t *>(*attr); |
| 190 | *attr = nullptr; |
| 191 | return ORBIS_OK; |
| 192 | } |
| 193 | |
| 194 | int32_t scePthreadAttrSetdetachstate(ScePthreadAttr *attr, int32_t state) { |
| 195 | return ORBIS_OK; |
| 196 | } |
| 197 | |
| 198 | int32_t scePthreadAttrSetstacksize(ScePthreadAttr *attr, uint64_t size) { |
| 199 | return ORBIS_OK; |
| 200 | } |
| 201 | |
| 202 | int32_t scePthreadAttrSetschedparam(ScePthreadAttr *attr, const void *param) { |
| 203 | return ORBIS_OK; |
| 204 | } |
| 205 | |
| 206 | /* |
| 207 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 208 | * │ MUTEX │ |
| 209 | * └─────────────────────────────────────────────────────────────────┘ |
| 210 | */ |
| 211 | |
| 212 | int32_t scePthreadMutexInit(ScePthreadMutex *mutex, |
| 213 | const ScePthreadMutexattr *attr, const char *name) { |
| 214 | PS4Mutex *mtx = new PS4Mutex(); |
| 215 | mtx->type = SCE_PTHREAD_MUTEX_NORMAL; |
| 216 | strncpy(mtx->name, name ? name : "mutex", sizeof(mtx->name) - 1); |
| 217 | *mutex = mtx; |
| 218 | return ORBIS_OK; |
| 219 | } |
| 220 | |
| 221 | int32_t scePthreadMutexDestroy(ScePthreadMutex *mutex) { |
| 222 | PS4Mutex *mtx = static_cast<PS4Mutex *>(*mutex); |
| 223 | if (mtx) { |
| 224 | delete mtx; |
| 225 | *mutex = nullptr; |
| 226 | } |
| 227 | return ORBIS_OK; |
| 228 | } |
| 229 | |
| 230 | int32_t scePthreadMutexLock(ScePthreadMutex *mutex) { |
| 231 | PS4Mutex *mtx = static_cast<PS4Mutex *>(*mutex); |
| 232 | if (!mtx) |
| 233 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 234 | mtx->native.lock(); |
| 235 | return ORBIS_OK; |
| 236 | } |
| 237 | |
| 238 | int32_t scePthreadMutexTrylock(ScePthreadMutex *mutex) { |
| 239 | PS4Mutex *mtx = static_cast<PS4Mutex *>(*mutex); |
| 240 | if (!mtx) |
| 241 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 242 | return mtx->native.try_lock() ? ORBIS_OK : ORBIS_KERNEL_ERROR_EBUSY; |
| 243 | } |
| 244 | |
| 245 | int32_t scePthreadMutexUnlock(ScePthreadMutex *mutex) { |
| 246 | PS4Mutex *mtx = static_cast<PS4Mutex *>(*mutex); |
| 247 | if (!mtx) |
| 248 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 249 | mtx->native.unlock(); |
| 250 | return ORBIS_OK; |
| 251 | } |
| 252 | |
| 253 | int32_t scePthreadMutexattrInit(ScePthreadMutexattr *attr) { |
| 254 | *attr = new int32_t(SCE_PTHREAD_MUTEX_NORMAL); |
| 255 | return ORBIS_OK; |
| 256 | } |
| 257 | |
| 258 | int32_t scePthreadMutexattrDestroy(ScePthreadMutexattr *attr) { |
| 259 | delete static_cast<int32_t *>(*attr); |
| 260 | *attr = nullptr; |
| 261 | return ORBIS_OK; |
| 262 | } |
| 263 | |
| 264 | int32_t scePthreadMutexattrSettype(ScePthreadMutexattr *attr, int32_t type) { |
| 265 | *static_cast<int32_t *>(*attr) = type; |
| 266 | return ORBIS_OK; |
| 267 | } |
| 268 | |
| 269 | /* |
| 270 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 271 | * │ CONDITION VARIABLE │ |
| 272 | * └─────────────────────────────────────────────────────────────────┘ |
| 273 | */ |
| 274 | |
| 275 | int32_t scePthreadCondInit(ScePthreadCond *cond, const ScePthreadCondattr *attr, |
| 276 | const char *name) { |
| 277 | PS4Cond *cv = new PS4Cond(); |
| 278 | strncpy(cv->name, name ? name : "cond", sizeof(cv->name) - 1); |
| 279 | *cond = cv; |
| 280 | return ORBIS_OK; |
| 281 | } |
| 282 | |
| 283 | int32_t scePthreadCondDestroy(ScePthreadCond *cond) { |
| 284 | PS4Cond *cv = static_cast<PS4Cond *>(*cond); |
| 285 | if (cv) { |
| 286 | delete cv; |
| 287 | *cond = nullptr; |
| 288 | } |
| 289 | return ORBIS_OK; |
| 290 | } |
| 291 | |
| 292 | int32_t scePthreadCondSignal(ScePthreadCond *cond) { |
| 293 | PS4Cond *cv = static_cast<PS4Cond *>(*cond); |
| 294 | if (!cv) |
| 295 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 296 | cv->native.notify_one(); |
| 297 | return ORBIS_OK; |
| 298 | } |
| 299 | |
| 300 | int32_t scePthreadCondBroadcast(ScePthreadCond *cond) { |
| 301 | PS4Cond *cv = static_cast<PS4Cond *>(*cond); |
| 302 | if (!cv) |
| 303 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 304 | cv->native.notify_all(); |
| 305 | return ORBIS_OK; |
| 306 | } |
| 307 | |
| 308 | int32_t scePthreadCondWait(ScePthreadCond *cond, ScePthreadMutex *mutex) { |
| 309 | PS4Cond *cv = static_cast<PS4Cond *>(*cond); |
| 310 | PS4Mutex *mtx = static_cast<PS4Mutex *>(*mutex); |
| 311 | if (!cv || !mtx) |
| 312 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 313 | cv->native.wait(mtx->native); |
| 314 | return ORBIS_OK; |
| 315 | } |
| 316 | |
| 317 | int32_t scePthreadCondTimedwait(ScePthreadCond *cond, ScePthreadMutex *mutex, |
| 318 | uint32_t usec) { |
| 319 | PS4Cond *cv = static_cast<PS4Cond *>(*cond); |
| 320 | PS4Mutex *mtx = static_cast<PS4Mutex *>(*mutex); |
| 321 | if (!cv || !mtx) |
| 322 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 323 | |
| 324 | auto status = |
| 325 | cv->native.wait_for(mtx->native, std::chrono::microseconds(usec)); |
| 326 | return status == std::cv_status::timeout ? ORBIS_KERNEL_ERROR_ETIMEDOUT |
| 327 | : ORBIS_OK; |
| 328 | } |
| 329 | |
| 330 | /* |
| 331 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 332 | * │ READ-WRITE LOCK │ |
| 333 | * └─────────────────────────────────────────────────────────────────┘ |
| 334 | */ |
| 335 | |
| 336 | int32_t scePthreadRwlockInit(ScePthreadRwlock *rwlock, |
| 337 | const ScePthreadRwlockattr *attr, |
| 338 | const char *name) { |
| 339 | PS4Rwlock *rw = new PS4Rwlock(); |
| 340 | strncpy(rw->name, name ? name : "rwlock", sizeof(rw->name) - 1); |
| 341 | *rwlock = rw; |
| 342 | return ORBIS_OK; |
| 343 | } |
| 344 | |
| 345 | int32_t scePthreadRwlockDestroy(ScePthreadRwlock *rwlock) { |
| 346 | PS4Rwlock *rw = static_cast<PS4Rwlock *>(*rwlock); |
| 347 | if (rw) { |
| 348 | delete rw; |
| 349 | *rwlock = nullptr; |
| 350 | } |
| 351 | return ORBIS_OK; |
| 352 | } |
| 353 | |
| 354 | int32_t scePthreadRwlockRdlock(ScePthreadRwlock *rwlock) { |
| 355 | PS4Rwlock *rw = static_cast<PS4Rwlock *>(*rwlock); |
| 356 | if (!rw) |
| 357 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 358 | rw->native.lock_shared(); |
| 359 | return ORBIS_OK; |
| 360 | } |
| 361 | |
| 362 | int32_t scePthreadRwlockWrlock(ScePthreadRwlock *rwlock) { |
| 363 | PS4Rwlock *rw = static_cast<PS4Rwlock *>(*rwlock); |
| 364 | if (!rw) |
| 365 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 366 | rw->native.lock(); |
| 367 | return ORBIS_OK; |
| 368 | } |
| 369 | |
| 370 | int32_t scePthreadRwlockUnlock(ScePthreadRwlock *rwlock) { |
| 371 | PS4Rwlock *rw = static_cast<PS4Rwlock *>(*rwlock); |
| 372 | if (!rw) |
| 373 | return ORBIS_KERNEL_ERROR_EINVAL; |
| 374 | // Note: shared_mutex doesn't track read vs write lock |
| 375 | // This is a simplification |
| 376 | rw->native.unlock(); |
| 377 | return ORBIS_OK; |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 382 | * │ THREAD-SPECIFIC DATA │ |
| 383 | * └─────────────────────────────────────────────────────────────────┘ |
| 384 | */ |
| 385 | |
| 386 | int32_t scePthreadKeyCreate(uint32_t *key, void (*destructor)(void *)) { |
| 387 | std::lock_guard<std::mutex> lock(g_tsd_lock); |
| 388 | uint32_t k = g_next_key++; |
| 389 | g_tsd_destructors[k] = destructor; |
| 390 | *key = k; |
| 391 | return ORBIS_OK; |
| 392 | } |
| 393 | |
| 394 | int32_t scePthreadKeyDelete(uint32_t key) { |
| 395 | std::lock_guard<std::mutex> lock(g_tsd_lock); |
| 396 | g_tsd.erase(key); |
| 397 | g_tsd_destructors.erase(key); |
| 398 | return ORBIS_OK; |
| 399 | } |
| 400 | |
| 401 | void *scePthreadGetspecific(uint32_t key) { |
| 402 | std::lock_guard<std::mutex> lock(g_tsd_lock); |
| 403 | auto it = g_tsd.find(key); |
| 404 | if (it == g_tsd.end()) |
| 405 | return nullptr; |
| 406 | auto tid = std::this_thread::get_id(); |
| 407 | auto it2 = it->second.find(tid); |
| 408 | return it2 != it->second.end() ? it2->second : nullptr; |
| 409 | } |
| 410 | |
| 411 | int32_t scePthreadSetspecific(uint32_t key, const void *value) { |
| 412 | std::lock_guard<std::mutex> lock(g_tsd_lock); |
| 413 | g_tsd[key][std::this_thread::get_id()] = const_cast<void *>(value); |
| 414 | return ORBIS_OK; |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | * ┌─────────────────────────────────────────────────────────────────┐ |
| 419 | * │ ONCE / YIELD │ |
| 420 | * └─────────────────────────────────────────────────────────────────┘ |
| 421 | */ |
| 422 | |
| 423 | int32_t scePthreadOnce(void *once_control, void (*init_routine)()) { |
| 424 | static std::mutex once_mutex; |
| 425 | static std::map<void *, bool> once_done; |
| 426 | |
| 427 | std::lock_guard<std::mutex> lock(once_mutex); |
| 428 | if (!once_done[once_control]) { |
| 429 | init_routine(); |
| 430 | once_done[once_control] = true; |
| 431 | } |
| 432 | return ORBIS_OK; |
| 433 | } |
| 434 | |
| 435 | int32_t scePthreadYield() { |
| 436 | std::this_thread::yield(); |
| 437 | return ORBIS_OK; |
| 438 | } |
| 439 | |
| 440 | } // extern "C" |
| 441 |