#include "dbg/dbg.hpp" #include "hijacker.hpp" #include "hijacker/hijacker.hpp" #include "util.hpp" #include #include #include // this always seems to be the case static constexpr uintptr_t ENTRYPOINT_OFFSET = 0x70; static constexpr int SYSTEM_SERVICE_HANDLE = 38; struct LoopBuilder { static constexpr size_t LOOB_BUILDER_SIZE = 39; static constexpr size_t LOOP_BUILDER_TARGET_OFFSET = 2; uint8_t data[LOOB_BUILDER_SIZE]; void setTarget(uintptr_t addr) { *reinterpret_cast(data + LOOP_BUILDER_TARGET_OFFSET) = addr; } }; static inline constexpr LoopBuilder SLEEP_LOOP{ // // 48 b8 xx xx xx xx xx xx xx xx 48 c7 c7 40 42 0f 00 ff d0 eb eb //loop: // MOV RAX, _nanosleep 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MOV RDI, 1000000000 // 1 second 0x48, 0xc7, 0xc7, 0x00, 0xca, 0x9a, 0x3b, // MOV RSI, 0 0x48, 0xc7, 0xc6, 0x00, 0x00, 0x00, 0x00, // PUSH RDI 0x57, // PUSH RSI 0x56, // CALL RAX 0xff, 0xd0, // JMP loop 0xeb, 0xe2 }; namespace { // shellcode to hack into your ps5 and steal your nudes extern const uint8_t SHELLCODE[282]; } struct Args { struct Result { int32_t state; int32_t err; } result; uintptr_t sceSystemServiceGetAppStatus; uintptr_t sceSystemServiceAddLocalProcess; uintptr_t socketpair; uintptr_t usleep; uintptr_t errno; Args(Hijacker &hijacker) : result({0, 0}), socketpair(hijacker.getLibKernelFunctionAddress(nid::socketpair)), usleep(hijacker.getLibKernelFunctionAddress(nid::usleep)), errno(hijacker.getLibKernelFunctionAddress(nid::_errno)) { UniquePtr libSceSystemService = hijacker.getLib(SYSTEM_SERVICE_HANDLE); SharedLib *lib = libSceSystemService.get(); sceSystemServiceGetAppStatus = hijacker.getFunctionAddress(lib, nid::sceSystemServiceGetAppStatus); sceSystemServiceAddLocalProcess = hijacker.getFunctionAddress(lib, nid::sceSystemServiceAddLocalProcess); } }; static int32_t getResult(const Hijacker &hijacker, ProcessPointer &state, dbg::IdArray &pids, int pid) { // we are done when a new process has spawned or the hijacked process has died auto frame = hijacker.getTrapFrame(); if (frame == nullptr) { return -2; } int32_t res = *state; if (res != 0) { return res; } auto ids = dbg::getAllPids(); return ids.contains(pid) && ids.length() > pids.length(); } UniquePtr Spawner::spawn() { LoopBuilder loop = SLEEP_LOOP; const int lastPid = pids[0]; // this should never miss because the pid lock is currently // held by the kernel when creating the new process // if spawning fails it's because the redis server can only do this once // we'll end up obtaining the previously spawned one // may be a good idea to rename the process int id = dbg::getAllPids()[0]; if (id == lastPid) { // catastrophic failure puts("catastrophic failure\n"); printf("lastPid %d id %d\n", lastPid, id); return nullptr; } UniquePtr info = new dbg::ProcessInfo(id); printf("spawned %s\n", info->name().c_str()); // TODO: time from here until the stack pointer is set UniquePtr spawned = nullptr; while (spawned == nullptr) { spawned = Hijacker::getHijacker(id); usleep(10); // NOLINT(*) } uintptr_t base = 0; while (base == 0) { base = spawned->getLibKernelBase(); } loop.setTarget(base + nanosleepOffset); base = spawned->imagebase(); // force the entrypoint to an infinite loop so that it doesn't start until we're ready dbg::write(id, base + ENTRYPOINT_OFFSET, loop.data, sizeof(loop.data)); return spawned; } UniquePtr Spawner::bootstrap(Hijacker &hijacker) { uintptr_t entry = hijacker.textAllocator.allocate(sizeof(SHELLCODE)); hijacker.write(entry, SHELLCODE); uintptr_t argbuf = hijacker.dataAllocator.allocate(sizeof(Args)); Args args{hijacker}; dbg::write(pid, argbuf, &args, sizeof(args)); ProcessPointer pstate = {pid, argbuf}; ScopedSuspender suspender{&hijacker}; auto frame = hijacker.getTrapFrame(); if (frame == nullptr) { return nullptr; } UniquePtr backup{new TrapFrame(*frame.get())}; frame->setRdi(argbuf) .setRip(entry) .setRsp(frame->getRsp()) .flush(); // wait for the new process to spawn hijacker.resume(); int32_t state = 0; do { // NOLINT(cppcoreguidelines-avoid-do-while) state = getResult(hijacker, pstate, pids, pid); } while (state == 0); if (state != 1) [[unlikely]] { if (state == -2) { // process died return nullptr; } ProcessPointer pres{pid, argbuf}; Args::Result res = *pres; if (res.state == -1) { printf("spawn failed err: %s\n", strerror(res.err)); } else if (res.state > 1) { printf("spawn failed %d\n", res.state); } else { printf("spawn failed state: %llx err: 0x%llx\n", (unsigned long long) res.state, (unsigned long long) res.err); } return nullptr; } hijacker.suspend(); hijacker.getTrapFrame()->setFrame(backup.get()) .flush(); return spawn(); } // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) namespace { // see shellcode/spawner.c for source const uint8_t SHELLCODE[]{ 0x53, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0xfb, 0x48, 0x8d, 0x7c, 0x24, 0x40, 0xff, 0x53, 0x08, 0x85, 0xc0, 0x0f, 0x88, 0xa2, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x4c, 0x24, 0x10, 0xbf, 0x01, 0x00, 0x00, 0x00, 0xbe, 0x01, 0x00, 0x00, 0x00, 0x31, 0xd2, 0xc7, 0x44, 0x24, 0x10, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x18, 0x01, 0x00, 0x00, 0x00, 0xc7, 0x44, 0x24, 0x1c, 0xff, 0xff, 0xff, 0xff, 0x48, 0xc7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00, 0x48, 0xc7, 0x44, 0x24, 0x28, 0x00, 0x00, 0x00, 0x00, 0xff, 0x53, 0x18, 0x83, 0xf8, 0xff, 0x74, 0x77, 0x48, 0xb8, 0x2f, 0x61, 0x70, 0x70, 0x30, 0x2f, 0x65, 0x62, 0x48, 0x8d, 0x74, 0x24, 0x30, 0x48, 0x8d, 0x54, 0x24, 0x08, 0x48, 0x8d, 0x4c, 0x24, 0x10, 0x48, 0x89, 0x44, 0x24, 0x30, 0x48, 0xb8, 0x6f, 0x6f, 0x74, 0x2e, 0x62, 0x69, 0x6e, 0x00, 0x48, 0x89, 0x44, 0x24, 0x38, 0x48, 0xc7, 0x44, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x7c, 0x24, 0x40, 0xff, 0x53, 0x10, 0x85, 0xc0, 0x78, 0x56, 0xc7, 0x03, 0x01, 0x00, 0x00, 0x00, 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, 0xbf, 0x40, 0x42, 0x0f, 0x00, 0xff, 0x53, 0x20, 0xeb, 0xf6, 0xc7, 0x03, 0x02, 0x00, 0x00, 0x00, 0x89, 0x43, 0x04, 0x66, 0x66, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x40, 0x42, 0x0f, 0x00, 0xff, 0x53, 0x20, 0xeb, 0xf6, 0xff, 0x53, 0x28, 0x8b, 0x00, 0xc7, 0x03, 0x03, 0x00, 0x00, 0x00, 0x89, 0x43, 0x04, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x40, 0x42, 0x0f, 0x00, 0xff, 0x53, 0x20, 0xeb, 0xf6, 0xc7, 0x03, 0x02, 0x00, 0x00, 0x00, 0x89, 0x43, 0x04, 0x66, 0x66, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x40, 0x42, 0x0f, 0x00, 0xff, 0x53, 0x20, 0xeb, 0xf6 }; } // anonymous namespace // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)