mirror of
https://github.com/etaHEN/etaHEN.git
synced 2026-01-15 04:23:20 +08:00
988 lines
28 KiB
C++
988 lines
28 KiB
C++
#include <stddef.h>
|
|
#define _MMAP_DECLARED
|
|
#include "dbg/dbg.hpp"
|
|
#include "elfldr.hpp"
|
|
#include "kernel/proc.hpp"
|
|
#include "kernel/rtld.hpp"
|
|
#include "sysmodules.hpp"
|
|
#include "util.hpp"
|
|
#include <netinet/in.h>
|
|
#include <ps5/kernel.h>
|
|
#include <sys/elf_common.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
|
|
extern "C" {
|
|
#include <sys/_stdint.h>
|
|
#include <stdint.h>
|
|
#include <sys/elf64.h>
|
|
#include <ps5/payload.h>
|
|
|
|
int puts(const char *);
|
|
int usleep(unsigned int useconds);
|
|
uintptr_t mmap(uintptr_t, size_t, int, int, int, off_t);
|
|
int munmap(uintptr_t addr, uint64_t len);
|
|
int sceKernelJitCreateSharedMemory(uintptr_t addr, size_t length, uint64_t flags, int *p_fd);
|
|
int *__error();
|
|
int sceSysmoduleLoadModuleInternal(uint32_t);
|
|
int sceSysmoduleLoadModuleByNameInternal(const char *fname, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
|
|
int sceKernelDlsym(int handle, const char* symbol, void** addrp);
|
|
extern const int _master_sock;
|
|
extern const int _victim_sock;
|
|
extern const int _rw_pipe[2];
|
|
extern const uint64_t _pipe_addr;
|
|
}
|
|
|
|
#include "nid_resolver/resolver.h"
|
|
|
|
#ifndef IPV6_2292PKTOPTIONS
|
|
#define IPV6_2292PKTOPTIONS 25 // NOLINT(*)
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
constexpr size_t NUM_PRELOADED_MODULES = 3;
|
|
constexpr int LIBKERNEL_HANDLE = 0x2001;
|
|
constexpr int LIBC_HANDLE = 2;
|
|
//constexpr int LIBSYSMODULE_HANDLE = 0X11;
|
|
constexpr size_t _PAGE_SIZE = 0x4000; // PAGE_SIZE is a c macro set to 0x100000
|
|
constexpr size_t PAGE_ALIGN_MASK = _PAGE_SIZE - 1;
|
|
constexpr size_t STACK_ALIGN = 0x10;
|
|
|
|
constexpr uint32_t MAP_SHARED = 0x1;
|
|
constexpr uint32_t MAP_PRIVATE = 0x2;
|
|
constexpr uint32_t MAP_FIXED = 0x10;
|
|
constexpr uint32_t MAP_ANONYMOUS = 0x1000;
|
|
constexpr uintptr_t MAP_FAILED = ~0ULL;
|
|
|
|
};
|
|
|
|
Elf::~Elf() noexcept {
|
|
// tracer detaches on destruction and the loaded elf runs
|
|
} // must be defined after SymbolLookupTable
|
|
|
|
Elf::Elf(Hijacker *hijacker, uint8_t *data) noexcept :
|
|
Elf64_Ehdr(*reinterpret_cast<Elf64_Ehdr *>(data)), tracer(hijacker->getPid()),
|
|
phdrs(reinterpret_cast<Elf64_Phdr*>(data + e_phoff)), strtab(),
|
|
strtabLength(), symtab(), symtabLength(), relatbl(), relaLength(),
|
|
plt(), pltLength(), hijacker(hijacker), textOffset(), imagebase(),
|
|
data(data), resolver(nullptr), mappedMemory(nullptr), jitFd(-1) {
|
|
// TODO check the elf magic stupid
|
|
//hexdump(data, sizeof(Elf64_Ehdr));
|
|
}
|
|
|
|
bool loadLibraries(Hijacker &hijacker, dbg::Tracer &tracer, const Array<String> &paths, ManagedResolver &resolver) noexcept;
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
extern "C" int _write(int, const void*, size_t);
|
|
|
|
void log_to_file(const char *msg) {
|
|
int fd = open("/data/prelf1.log", O_WRONLY | O_CREAT | O_APPEND, 0777);
|
|
if (fd < 0) {
|
|
return;
|
|
}
|
|
_write(fd, msg, __builtin_strlen(msg));
|
|
close(fd);
|
|
printf("[ELF_LOADER] %s", msg);
|
|
}
|
|
|
|
bool Elf::parseDynamicTable() noexcept {
|
|
const Elf64_Dyn *__restrict dyntbl = nullptr;
|
|
for (size_t i = 0; i < e_phnum; i++) {
|
|
const Elf64_Phdr *__restrict phdr = phdrs + i;
|
|
if (phdr->p_type == PT_DYNAMIC) {
|
|
dyntbl = reinterpret_cast<Elf64_Dyn*>(data + phdr->p_offset);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dyntbl == nullptr) [[unlikely]] {
|
|
log_to_file("dynamic table not found\n");
|
|
return false;
|
|
}
|
|
|
|
List<const Elf64_Dyn *> neededLibs{};
|
|
|
|
uint8_t *const image = data + textOffset;
|
|
|
|
for (const Elf64_Dyn *dyn = dyntbl; dyn->d_tag != DT_NULL; dyn++) {
|
|
switch (dyn->d_tag) {
|
|
case DT_NEEDED:
|
|
neededLibs.push_front(dyn);
|
|
break;
|
|
case DT_RELA:
|
|
relatbl = reinterpret_cast<Elf64_Rela *>(image + dyn->d_un.d_ptr);
|
|
break;
|
|
case DT_RELASZ:
|
|
relaLength = dyn->d_un.d_val / sizeof(Elf64_Rela);
|
|
break;
|
|
case DT_JMPREL:
|
|
plt = reinterpret_cast<Elf64_Rela *>(image + dyn->d_un.d_ptr);
|
|
break;
|
|
case DT_PLTRELSZ:
|
|
pltLength = dyn->d_un.d_val / sizeof(Elf64_Rela);
|
|
break;
|
|
case DT_SYMTAB:
|
|
symtab = reinterpret_cast<Elf64_Sym *>(image + dyn->d_un.d_ptr);
|
|
break;
|
|
case DT_STRTAB:
|
|
strtab = reinterpret_cast<const char *>(image + dyn->d_un.d_ptr);
|
|
break;
|
|
case DT_STRSZ:
|
|
strtabLength = dyn->d_un.d_val;
|
|
break;
|
|
case DT_HASH:
|
|
symtabLength = reinterpret_cast<uint32_t *>(image + dyn->d_un.d_ptr)[1];
|
|
break;
|
|
|
|
// don't care for now
|
|
case DT_INIT_ARRAY:
|
|
case DT_INIT_ARRAYSZ:
|
|
case DT_FINI_ARRAY:
|
|
case DT_FINI_ARRAYSZ:
|
|
case DT_PREINIT_ARRAY:
|
|
case DT_PREINIT_ARRAYSZ:
|
|
case DT_PLTREL:
|
|
// don't care
|
|
case DT_RELACOUNT:
|
|
case DT_GNU_HASH:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
log_to_file("after switch\n");
|
|
|
|
if (strtab == nullptr) [[unlikely]] {
|
|
log_to_file("strtab not found\n");
|
|
|
|
}
|
|
|
|
if (strtabLength == 0 && strtab != nullptr) [[unlikely]] {
|
|
log_to_file("strtab size not found but strtab exists\n");
|
|
return false;
|
|
}
|
|
|
|
if (symtabLength == 0) [[unlikely]] {
|
|
log_to_file("symtab size not found\n");
|
|
}
|
|
|
|
if (symtab == nullptr) [[unlikely]] {
|
|
log_to_file("symtab not found\n");
|
|
}
|
|
|
|
if (relatbl == nullptr) [[unlikely]] {
|
|
log_to_file("rela table not found\n");
|
|
}
|
|
|
|
if (plt == nullptr) [[unlikely]] {
|
|
log_to_file("plt table not found\n");
|
|
}
|
|
|
|
if (symtab == nullptr || strtab == nullptr) [[unlikely]] {
|
|
// don't need to proceed
|
|
log_to_file("symtab or strtab not found\n");
|
|
return true;
|
|
}
|
|
|
|
Array<String> names{neededLibs.length()};
|
|
|
|
int preLoadedHandles[NUM_PRELOADED_MODULES];
|
|
int handleCount = 0;
|
|
size_t i = 0;
|
|
for (const Elf64_Dyn *lib : neededLibs) {
|
|
StringView filename = strtab + lib->d_un.d_val;
|
|
if (!filename.endswith(".so"_sv)) [[unlikely]] {
|
|
__builtin_printf("unexpected library 0x%llx %s\n", (unsigned long long)lib->d_un.d_val, filename.c_str());
|
|
log_to_file("unexpected library\n");
|
|
return false;
|
|
}
|
|
// I really do not want to implement a hashmap
|
|
if (filename.startswith("libkernel"_sv)) {
|
|
*(preLoadedHandles + handleCount++) = LIBKERNEL_HANDLE;
|
|
continue;
|
|
}
|
|
if (filename == "libSceLibcInternal.so"_sv || filename == "libc.so"_sv) {
|
|
*(preLoadedHandles + handleCount++) = LIBC_HANDLE;
|
|
continue;
|
|
}
|
|
//if (filename == "libSceSysmodule.so"_sv) {
|
|
// *(preLoadedHandles + handleCount++) = LIBSYSMODULE_HANDLE;
|
|
// continue;
|
|
//}
|
|
|
|
names[i++] = StringView{filename.c_str(), filename.length() - 3};
|
|
}
|
|
|
|
log_to_file("after loop\n");
|
|
|
|
// remove unset values
|
|
names.shrink(i);
|
|
log_to_file("after shrink\n");
|
|
|
|
resolver = new ManagedResolver{};
|
|
|
|
resolver->reserve_library_memory(handleCount + names.length());
|
|
|
|
log_to_file("after reserve\n");
|
|
|
|
log_to_file("filling symbol tables\n");
|
|
for (auto i = 0; i < handleCount; i++) {
|
|
auto ptr = hijacker->getLib(preLoadedHandles[i]);
|
|
if (ptr == nullptr) [[unlikely]] {
|
|
printf("failed to get lib for 0x%x\n", (unsigned int) preLoadedHandles[i]);
|
|
log_to_file("failed to get lib\n");
|
|
return false;
|
|
}
|
|
if (resolver->add_library_metadata(ptr->imagebase(), ptr->getMetaDataAddress()) != 0) {
|
|
printf("failed to add library metadata for 0x%x\n", (unsigned int) preLoadedHandles[i]);
|
|
log_to_file("failed to add library metadata\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (names.length() > 0) {
|
|
log_to_file("loading libraries\n");
|
|
if (!loadLibraries(*hijacker, tracer, names, *resolver)) {
|
|
__builtin_printf("failed to load libraries\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
log_to_file("finished process dynamic table\n");
|
|
return true;
|
|
}
|
|
|
|
class TracedMemory {
|
|
const dbg::Tracer *tracer;
|
|
uintptr_t mem;
|
|
size_t length;
|
|
|
|
public:
|
|
TracedMemory(const dbg::Tracer *tracer, uintptr_t mem, size_t length) noexcept :
|
|
tracer(tracer), mem(mem), length(length) {}
|
|
TracedMemory(const TracedMemory&) = delete;
|
|
TracedMemory &operator=(const TracedMemory&) = delete;
|
|
TracedMemory(TracedMemory &&rhs) noexcept : tracer(rhs.tracer), mem(rhs.mem), length(rhs.length) {
|
|
rhs.tracer = nullptr;
|
|
}
|
|
TracedMemory &operator=(TracedMemory &&rhs) noexcept {
|
|
if (tracer != nullptr) {
|
|
tracer->munmap(mem, length);
|
|
}
|
|
tracer = rhs.tracer;
|
|
mem = rhs.mem;
|
|
length = rhs.length;
|
|
rhs.tracer = nullptr;
|
|
return *this;
|
|
}
|
|
~TracedMemory() noexcept {
|
|
if (tracer != nullptr) {
|
|
tracer->munmap(mem, length);
|
|
tracer = nullptr;
|
|
}
|
|
}
|
|
operator uintptr_t() const noexcept {
|
|
return mem;
|
|
}
|
|
};
|
|
|
|
static inline size_t pageAlign(size_t length) {
|
|
return (length + PAGE_ALIGN_MASK) & ~PAGE_ALIGN_MASK;
|
|
}
|
|
|
|
static constexpr int PROT_READ = 1;
|
|
static constexpr int PROT_WRITE = 2;
|
|
static constexpr int PROT_EXEC = 4;
|
|
static constexpr int PROT_GPU_READ = 0x10;
|
|
static constexpr int PROT_GPU_WRITE = 0x20;
|
|
|
|
static bool loadLibrariesInplace(Hijacker &hijacker, const Array<String> &names, ManagedResolver &resolver) noexcept {
|
|
for (const auto &name : names) {
|
|
const auto id = SYSMODULES[name];
|
|
int handle = id != 0 ? sceSysmoduleLoadModuleInternal(id) :
|
|
sceSysmoduleLoadModuleByNameInternal(name.c_str(), 0, 0, 0, 0, 0);
|
|
if (handle == -1) {
|
|
printf("failed to get lib handle for %s\n", name.c_str());
|
|
log_to_file("failed to get lib handle\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
log_to_file("after loadLibrariesInplace\n");
|
|
|
|
const auto nlibs = names.length();
|
|
for (size_t i = 0; i < nlibs; i++) {
|
|
auto ptr = hijacker.getLib(names[i]);
|
|
if (ptr == nullptr) [[unlikely]] {
|
|
printf("failed to get lib handle for %s\n", names[i].c_str());
|
|
log_to_file("failed to get lib handle\n");
|
|
return false;
|
|
}
|
|
if (resolver.add_library_metadata(ptr->imagebase(), ptr->getMetaDataAddress()) != 0) {
|
|
printf("failed to add library metadata for %s\n", names[i].c_str());
|
|
log_to_file("failed to add library metadata\n");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool loadLibraries(Hijacker &hijacker, dbg::Tracer &tracer, const Array<String> &names, ManagedResolver &resolver) noexcept {
|
|
if (hijacker.getPid() == getpid()) {
|
|
return loadLibrariesInplace(hijacker, names, resolver);
|
|
}
|
|
log_to_file("before loadLibraries\n");
|
|
|
|
static constexpr uint32_t INTERNAL_MASK = 0x80000000;
|
|
|
|
const size_t nlibs = names.length();
|
|
String fulltbl{};
|
|
UniquePtr<uintptr_t[]> positions{new uintptr_t[nlibs]};
|
|
{
|
|
log_to_file("before loop\n");
|
|
size_t i = 0;
|
|
size_t tblSize = 0;
|
|
for (const String &path : names) {
|
|
tblSize += path.length() + 1;
|
|
}
|
|
fulltbl.reserve(tblSize);
|
|
for (const String &path : names) {
|
|
const StringView name{path};
|
|
auto id = SYSMODULES[name];
|
|
if (id != 0) {
|
|
positions[i++] = id;
|
|
} else {
|
|
positions[i++] = fulltbl.length();
|
|
fulltbl += path;
|
|
fulltbl += '\0';
|
|
}
|
|
}
|
|
}
|
|
log_to_file("after loop w\n");
|
|
const uintptr_t strtab = hijacker.getDataAllocator().allocate(fulltbl.length() + 1);
|
|
|
|
hijacker.write(strtab, fulltbl.c_str(), fulltbl.length() + 1); // include the null terminator
|
|
log_to_file("after write\n");
|
|
#if 1
|
|
UniquePtr<SharedLib> lib; // Declare lib here without initializing.
|
|
int number_of_tries = 0;
|
|
int handle = -1;
|
|
do{
|
|
lib = hijacker.getLib("libSceSysmodule.sprx");
|
|
if(lib == nullptr) printf("[1] libSceSysmodule.sprx is NULL\n");
|
|
number_of_tries++;
|
|
if(number_of_tries > 150) {
|
|
log_to_file("libSceSysmodule.sprx not loaded\n");
|
|
return false;
|
|
}
|
|
|
|
if(lib != nullptr) {
|
|
printf("[2-] libSceSysmodule.sprx is not NULL\n");
|
|
break;
|
|
}
|
|
tracer.dynlib_load_prx("/system/common/lib/libSceSysmodule.sprx", &handle);
|
|
if(handle > 0){
|
|
lib = hijacker.getLib(handle);
|
|
if(lib == nullptr) {
|
|
printf("getlib(handle) is NULL\n");
|
|
}
|
|
else
|
|
{
|
|
printf("getlib(handle) is not NULL\n");
|
|
}
|
|
}
|
|
printf("libSceSysmodule.sprx: %p, try %i, handle %x\n", lib.get(), number_of_tries, handle);
|
|
|
|
usleep(100000);
|
|
}while (lib == nullptr);
|
|
|
|
|
|
if (lib == nullptr) [[unlikely]] {
|
|
log_to_file("libSceSysmodule.sprx not loaded\n");
|
|
return false;
|
|
}
|
|
else {
|
|
log_to_file("libSceSysmodule.sprx loaded\n");
|
|
}
|
|
|
|
// int sceSysmoduleLoadModuleInternal(uint32_t id);
|
|
const auto sceSysmoduleLoadModuleInternal = hijacker.getFunctionAddress(lib.get(), nid::sceSysmoduleLoadModuleInternal);
|
|
if (sceSysmoduleLoadModuleInternal == 0) [[unlikely]] {
|
|
log_to_file("sceSysmoduleLoadModuleInternal not found");
|
|
return false;
|
|
}
|
|
|
|
// int sceSysmoduleLoadModuleByNameInternal(const char *fname, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
|
|
const auto sceSysmoduleLoadModuleByNameInternal = hijacker.getFunctionAddress(lib.get(), nid::sceSysmoduleLoadModuleByNameInternal);
|
|
if (sceSysmoduleLoadModuleByNameInternal == 0) [[unlikely]] {
|
|
log_to_file("sceSysmoduleLoadModuleByNameInternal not found");
|
|
return false;
|
|
}
|
|
|
|
const auto sceSysmoduleLoadModule = hijacker.getFunctionAddress(lib.get(), nid::sceSysmoduleLoadModule);
|
|
if (sceSysmoduleLoadModule == 0) [[unlikely]] {
|
|
log_to_file("sceSysmoduleLoadModule not found");
|
|
return false;
|
|
}
|
|
#else
|
|
int handle = -1;
|
|
int ret = tracer.dynlib_load_prx("libSceSysmodule.sprx", &handle);
|
|
if (ret != 0 || handle == -1) [[unlikely]] {
|
|
printf("failed to get lib handle for libSceSysmodule.sprx, ret %i\n", ret);
|
|
int ret = tracer.dynlib_load_prx("/system/common/lib/libSceSysmodule.sprx", &handle);
|
|
if (ret != 0 || handle == -1) [[unlikely]] {
|
|
printf("failed to get lib handle for libSceSysmodule.sprx, ret %i\n", ret);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
printf("handle: 0x%X ret %i\n", handle, ret);
|
|
|
|
int (*sceSysmoduleLoadModuleInternal)(uint32_t) = nullptr;
|
|
int (*sceSysmoduleLoadModuleByNameInternal)(const char *fname, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) = nullptr;
|
|
int(*sceSysmoduleLoadModule)(uint16_t) = nullptr;
|
|
if (tracer.dynlib_dlsym(handle, "sceSysmoduleLoadModuleInternal", (void**)&sceSysmoduleLoadModuleInternal) == -1) {
|
|
log_to_file("failed to get sceSysmoduleLoadModuleInternal\n");
|
|
return false;
|
|
}
|
|
if (tracer.dynlib_dlsym(handle, "sceSysmoduleLoadModuleByNameInternal", (void**)&sceSysmoduleLoadModuleByNameInternal) == -1) {
|
|
log_to_file("failed to get sceSysmoduleLoadModuleByNameInternal\n");
|
|
return false;
|
|
}
|
|
if (tracer.dynlib_dlsym(handle, "sceSysmoduleLoadModule", (void**)&sceSysmoduleLoadModule) == -1) {
|
|
log_to_file("failed to get sceSysmoduleLoadModule\n");
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
for (size_t i = 0; i < nlibs; i++) {
|
|
int handle = -1;
|
|
if (positions[i] & INTERNAL_MASK) {
|
|
handle = tracer.call<int>((uintptr_t)sceSysmoduleLoadModuleInternal, static_cast<uint32_t>(positions[i]));
|
|
printf("id: 0x%x, handle %i\n", (unsigned int)positions[i], handle);
|
|
String prx_path("/system/common/lib/");
|
|
prx_path += names[i];
|
|
prx_path += ".sprx";
|
|
tracer.dynlib_load_prx(prx_path.c_str(), &handle);
|
|
printf("[2] path: %s, handle %i\n", prx_path.c_str(), handle);
|
|
auto ptr = hijacker.getLib(handle);
|
|
printf("failed to get lib handle for 0x%x, handle %i\n", (unsigned int)positions[i], handle);
|
|
if (resolver.add_library_metadata(ptr->imagebase(), ptr->getMetaDataAddress()) != 0) {
|
|
printf("failed to add library metadata for %s\n", names[i].c_str());
|
|
return false;
|
|
}
|
|
continue;
|
|
} else {
|
|
if(names[i] == "libSceMsgDialog"){
|
|
tracer.dynlib_load_prx("/system/common/lib/libSceMsgDialog.native.sprx", &handle);
|
|
printf("[2] handle %i\n", handle);
|
|
auto ptr = hijacker.getLib(handle);
|
|
if (ptr == nullptr) [[unlikely]] {
|
|
printf("failed to get lib handle for 0x%x, handle %i\n", (unsigned int)positions[i], handle);
|
|
if (resolver.add_library_metadata(ptr->imagebase(), ptr->getMetaDataAddress()) != 0) {
|
|
printf("failed to add library metadata for %s\n", names[i].c_str());
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
handle = tracer.call<int>((uintptr_t)sceSysmoduleLoadModuleByNameInternal, strtab + positions[i], 0, 0, 0, 0, 0);
|
|
|
|
printf("handle %i\n", handle);
|
|
//printf("str: %s\n", (const char*)(strtab + positions[i]));
|
|
}
|
|
|
|
if (handle == -1) [[unlikely]] {
|
|
printf("failed to get lib handle for %s\n", names[i].c_str());
|
|
return false;
|
|
}
|
|
|
|
auto ptr = names[i] == "libSceMsgDialog" ? hijacker.getLib("libSceMsgDialog.native") : hijacker.getLib(names[i]);
|
|
if (ptr == nullptr) [[unlikely]] {
|
|
printf("failed to get lib handle for %s\n", names[i].c_str());
|
|
return false;
|
|
}
|
|
|
|
if (resolver.add_library_metadata(ptr->imagebase(), ptr->getMetaDataAddress()) != 0) {
|
|
printf("failed to add library metadata for %s\n", names[i].c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
puts("finished loading libraries");
|
|
return true;
|
|
}
|
|
|
|
static inline bool isLoadable(const Elf64_Phdr *__restrict phdr) {
|
|
return phdr->p_type == PT_LOAD || phdr->p_type == PT_GNU_EH_FRAME;
|
|
}
|
|
|
|
static int jitshm_create(uintptr_t addr, size_t length, uint64_t flags) noexcept {
|
|
int fd = -1;
|
|
if (sceKernelJitCreateSharedMemory(addr, length, flags, &fd) != 0) {
|
|
perror("sceKernelJitCreateSharedMemory");
|
|
return -1;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
static inline int toMmapProt(const Elf64_Phdr *__restrict phdr) noexcept {
|
|
int res = 0;
|
|
if (phdr->p_flags & PF_X) [[unlikely]] {
|
|
res |= PROT_EXEC;
|
|
}
|
|
if (phdr->p_flags & PF_R) [[likely]] {
|
|
res |= PROT_READ | PROT_GPU_READ;
|
|
}
|
|
if (phdr->p_flags & PF_W) {
|
|
res |= PROT_WRITE | PROT_GPU_WRITE;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool Elf::processProgramHeaders() noexcept {
|
|
size_t textLength = 0;
|
|
size_t totalSize = 0;
|
|
size_t numLoadable = 0;
|
|
const Elf64_Phdr *__restrict text = nullptr;
|
|
for (auto i = 0; i < e_phnum; i++) {
|
|
const auto *__restrict phdr = phdrs + i;
|
|
if (isLoadable(phdr)) {
|
|
numLoadable++;
|
|
totalSize += pageAlign(phdr->p_memsz);
|
|
if (phdr->p_flags & PF_X) [[unlikely]] {
|
|
text = phdr;
|
|
textLength = pageAlign(phdr->p_memsz);
|
|
textOffset = phdr->p_offset;
|
|
}
|
|
}
|
|
}
|
|
if (textOffset == 0) {
|
|
puts("no executable section found");
|
|
return false;
|
|
}
|
|
|
|
const bool inplace = hijacker->getPid() == getpid();
|
|
|
|
if (inplace) {
|
|
mappedMemory = {numLoadable};
|
|
}
|
|
|
|
uintptr_t mem = inplace ?
|
|
mmap(0, totalSize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0) :
|
|
tracer.mmap(0, totalSize, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
|
|
if (mem == MAP_FAILED) [[unlikely]] {
|
|
tracer.perror("mmap Elf::processProgramHeaders");
|
|
return false;
|
|
}
|
|
|
|
if (inplace) {
|
|
munmap(mem, totalSize);
|
|
} else {
|
|
tracer.munmap(mem, totalSize);
|
|
}
|
|
|
|
int fd = inplace ? jitshm_create(0, textLength, PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GPU_READ) :
|
|
tracer.jitshm_create(0, textLength, PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GPU_READ);
|
|
if (fd < 0) [[unlikely]] {
|
|
tracer.perror("mmap Elf::processProgramHeaders Tracer::jitshm_create");
|
|
return false;
|
|
}
|
|
|
|
if (inplace) {
|
|
jitFd = fd;
|
|
}
|
|
|
|
const auto prot = toMmapProt(text);
|
|
imagebase = inplace ? mmap(mem, textLength, prot, MAP_FIXED | MAP_SHARED, fd, 0) :
|
|
tracer.mmap(mem, textLength, prot, MAP_FIXED | MAP_SHARED, fd, 0);
|
|
if (imagebase == MAP_FAILED) [[unlikely]] {
|
|
tracer.perror("mmap Elf::processProgramHeaders Tracer::mmap text");
|
|
return false;
|
|
}
|
|
|
|
if (imagebase != mem) {
|
|
puts("mmap Elf::processProgramHeaders did not give the requested address");
|
|
return false;
|
|
}
|
|
|
|
size_t memIndex = 0;
|
|
if (inplace) {
|
|
mappedMemory[memIndex++] = {imagebase, textLength};
|
|
}
|
|
|
|
for (int i = 0; i < e_phnum; i++) {
|
|
const auto *__restrict phdr = phdrs + i;
|
|
if (phdr->p_flags & PF_X) [[unlikely]] {
|
|
continue;
|
|
}
|
|
|
|
if (!isLoadable(phdr)) {
|
|
continue;
|
|
}
|
|
|
|
const auto addr = phdr->p_paddr + imagebase;
|
|
const auto sz = pageAlign(phdr->p_memsz);
|
|
const auto prot = toMmapProt(phdr);
|
|
auto result = inplace ? mmap(addr, sz, prot, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0) :
|
|
tracer.mmap(addr, sz, prot, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
if (result == MAP_FAILED) [[unlikely]] {
|
|
tracer.perror("mmap Elf::processProgramHeaders Tracer::mmap data");
|
|
return false;
|
|
}
|
|
if (result != addr) {
|
|
puts("mmap Elf::processProgramHeaders mmap did not give the requested address");
|
|
return false;
|
|
}
|
|
if (inplace) {
|
|
mappedMemory[memIndex++] = {addr, sz};
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
struct KernelRWArgs {
|
|
struct Result {
|
|
int state;
|
|
int err;
|
|
} result;
|
|
uintptr_t files;
|
|
uintptr_t usleep;
|
|
uintptr_t socket;
|
|
uintptr_t pipe;
|
|
uintptr_t setsockopt;
|
|
uintptr_t errno;
|
|
|
|
KernelRWArgs(Hijacker& hijacker) :
|
|
result({0, 0}), usleep(hijacker.getLibKernelFunctionAddress(nid::usleep)),
|
|
socket(hijacker.getLibKernelFunctionAddress(nid::socket)), pipe(hijacker.getLibKernelFunctionAddress(nid::pipe)),
|
|
setsockopt(hijacker.getLibKernelFunctionAddress(nid::setsockopt)), errno(hijacker.getLibKernelFunctionAddress(nid::_errno)) {
|
|
auto &alloc = hijacker.getDataAllocator();
|
|
files = alloc.allocate(sizeof(int[4]));
|
|
}
|
|
};
|
|
|
|
static int rwpipe[2]; // NOLINT(*)
|
|
static int rwpair[2]; // NOLINT(*)
|
|
|
|
typedef int dlsym_t(int, const char*, void*);
|
|
|
|
static struct InternalPayloadArgs {
|
|
struct payload_args args;
|
|
int payloadout;
|
|
} gResult; // NOLINT(*)
|
|
|
|
static uintptr_t setupKernelRWInplace(const Hijacker& hijacker) {
|
|
rwpipe[0] = _rw_pipe[0];
|
|
rwpipe[1] = _rw_pipe[1];
|
|
rwpair[0] = _master_sock;
|
|
rwpair[1] = _victim_sock;
|
|
gResult.args = {
|
|
.sys_dynlib_dlsym = reinterpret_cast<dlsym_t*>(hijacker.getLibKernelFunctionAddress(nid::sceKernelDlsym)), // NOLINT(*)
|
|
.rwpipe = rwpipe,
|
|
.rwpair = rwpair,
|
|
.kpipe_addr = static_cast<intptr_t>(_pipe_addr),
|
|
.kdata_base_addr = static_cast<intptr_t>(kernel_base),
|
|
.payloadout = &gResult.payloadout
|
|
};
|
|
return reinterpret_cast<uintptr_t>(&gResult);
|
|
}
|
|
|
|
uintptr_t Elf::setupKernelRW() noexcept {
|
|
if (hijacker->getPid() == getpid()) {
|
|
return setupKernelRWInplace(*hijacker);
|
|
}
|
|
|
|
int files[4];
|
|
files[0] = tracer.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
|
files[1] = tracer.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
|
|
|
printf("master socket: %d\n", files[0]);
|
|
printf("victim socket: %d\n", files[1]);
|
|
|
|
if (files[0] == -1 || files[1] == -1) [[unlikely]] {
|
|
tracer.perror("Elf::setupKernelRW socket");
|
|
return 0;
|
|
}
|
|
|
|
if (tracer.pipe(files + 2) < 0) [[unlikely]] {
|
|
tracer.perror("Elf::setupKernelRW pipe");
|
|
return 0;
|
|
}
|
|
|
|
printf("rw pipes: %d, %d\n", files[2], files[3]);
|
|
|
|
unsigned int buf[]{20, IPPROTO_IPV6, IPV6_TCLASS, 0, 0, 0}; // NOLINT(*)
|
|
|
|
if (tracer.setsockopt(files[0], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, (void*)buf, sizeof(buf)) == -1) {
|
|
tracer.perror("Elf::setupKernelRW setsockopt master");
|
|
}
|
|
|
|
__builtin_memset(buf, 0, sizeof(buf));
|
|
|
|
if (tracer.setsockopt(files[1], IPPROTO_IPV6, IPV6_PKTINFO, (void*)buf, sizeof(buf) - sizeof(int)) == -1) {
|
|
tracer.perror("Elf::setupKernelRW setsockopt victim");
|
|
}
|
|
|
|
if (!createReadWriteSockets(hijacker->getProc(), files)) {
|
|
puts("failed to create kernelrw sockets");
|
|
return 0;
|
|
}
|
|
|
|
auto newtbl = hijacker->getProc()->getFdTbl();
|
|
const uintptr_t pipeaddr = kread<uintptr_t>(newtbl.getFile(files[2]));
|
|
|
|
auto regs = tracer.getRegisters();
|
|
regs.rsp(regs.rsp() - sizeof(files));
|
|
const uintptr_t newFiles = regs.rsp();
|
|
hijacker->write(newFiles, files, sizeof(files));
|
|
regs.rsp(regs.rsp() - sizeof(int) - sizeof(payload_args));
|
|
const auto rsp = regs.rsp();
|
|
tracer.setRegisters(regs);
|
|
|
|
// NOLINTBEGIN(performance-no-int-to-ptr)
|
|
struct payload_args result = {
|
|
.sys_dynlib_dlsym = reinterpret_cast<dlsym_t *>(hijacker->getLibKernelFunctionAddress(nid::sceKernelDlsym)),
|
|
.rwpipe = reinterpret_cast<int *>(newFiles) + 2,
|
|
.rwpair = reinterpret_cast<int *>(newFiles),
|
|
.kpipe_addr = static_cast<intptr_t>(pipeaddr),
|
|
.kdata_base_addr = static_cast<intptr_t>(kernel_base),
|
|
.payloadout = reinterpret_cast<int*>(rsp + sizeof(struct payload_args))
|
|
};
|
|
// NOLINTEND(performance-no-int-to-ptr)
|
|
|
|
|
|
hijacker->write(rsp, &result, sizeof(result));
|
|
return rsp;
|
|
}
|
|
|
|
bool Elf::load() noexcept {
|
|
for (size_t i = 0; i < e_phnum; i++) {
|
|
const Elf64_Phdr *__restrict phdr = phdrs + i;
|
|
|
|
if (!isLoadable(phdr)) {
|
|
continue;
|
|
}
|
|
|
|
const uintptr_t vaddr = phdr->p_paddr + imagebase;
|
|
|
|
if (vaddr == 0) {
|
|
continue;
|
|
}
|
|
|
|
int j = 0;
|
|
while (!hijacker->write(vaddr, data + phdr->p_offset, phdr->p_filesz)) {
|
|
printf("failed to write section data for phdr with paddr 0x%08lx\n", phdr->p_paddr);
|
|
if (j++ > 10) { // NOLINT(cppcoreguidelines-avoid-magic-numbers)
|
|
// TODO: find out why I did this
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Elf::launch() noexcept {
|
|
puts("processing program headers");
|
|
log_to_file( "processing program headers\n");
|
|
|
|
if (!processProgramHeaders()) [[unlikely]] {
|
|
log_to_file( "failed to process program headers\n");
|
|
return false;
|
|
}
|
|
|
|
puts("processing dynamic table");
|
|
log_to_file( "processing dynamic table\n");
|
|
|
|
if (!parseDynamicTable()) [[unlikely]] {
|
|
log_to_file( "failed to process dynamic table\n");
|
|
return false;
|
|
}
|
|
puts("processing relocations");
|
|
log_to_file( "processing relocations\n");
|
|
|
|
if (!processRelocations()) [[unlikely]] {
|
|
log_to_file( "failed to process relocations\n");
|
|
return false;
|
|
}
|
|
puts("processing plt relocations");
|
|
log_to_file( "processing plt relocation\n");
|
|
|
|
if (!processPltRelocations()) [[unlikely]] {
|
|
log_to_file( "failed to process plt relocations\n");
|
|
return false;
|
|
}
|
|
|
|
puts("setting up kernel rw");
|
|
log_to_file( "setting up kernel rw\n");
|
|
|
|
uintptr_t args = setupKernelRW();
|
|
if (args == 0) [[unlikely]] {
|
|
log_to_file( "failed to setup kernel rw\n");
|
|
return false;
|
|
}
|
|
|
|
puts("loading into memory");
|
|
log_to_file( "loading into memory\n");
|
|
|
|
if (!load()) [[unlikely]] {
|
|
log_to_file( "failed to load\n");
|
|
return false;
|
|
}
|
|
|
|
puts("starting");
|
|
log_to_file( "starting\n");
|
|
|
|
|
|
return start(args);
|
|
}
|
|
|
|
static void correctRsp(dbg::Registers ®s) noexcept {
|
|
constexpr auto mask = ~(STACK_ALIGN - 1);
|
|
regs.rsp((regs.rsp() & mask) - sizeof(mask));
|
|
}
|
|
|
|
bool Elf::start(uintptr_t args) noexcept {
|
|
printf("imagebase: 0x%08lx\n", imagebase);
|
|
if (hijacker->getPid() == getpid()) {
|
|
auto fun = reinterpret_cast<int(*)(uintptr_t)>(imagebase + e_entry); // NOLINT(*)
|
|
bool res = fun(args) == 0;
|
|
for (const auto &mem : mappedMemory) {
|
|
munmap(mem.mem, mem.len);
|
|
}
|
|
close(jitFd);
|
|
return res;
|
|
}
|
|
(void) args;
|
|
dbg::Registers regs = tracer.getRegisters();
|
|
correctRsp(regs);
|
|
regs.rdi(args);
|
|
regs.rip(imagebase + e_entry);
|
|
tracer.setRegisters(regs);
|
|
// it will run on detatch
|
|
puts("great success");
|
|
return true;
|
|
}
|
|
|
|
uintptr_t Elf::getSymbolAddress(const Elf64_Rela *__restrict rel) const noexcept {
|
|
if (symtab == nullptr || strtab == nullptr) [[unlikely]] {
|
|
return true;
|
|
}
|
|
|
|
const Elf64_Sym *__restrict sym = symtab + ELF64_R_SYM(rel->r_info);
|
|
if (sym->st_value != 0) {
|
|
// the symbol exists in our elf
|
|
// this can only occur if you're loading a library instead of an executable
|
|
// this was a mistake and I'm an idiot but it may be useful in the future
|
|
return imagebase + sym->st_value;
|
|
}
|
|
|
|
if (ELF64_ST_BIND(sym->st_info) == STB_WEAK) {
|
|
return -1;
|
|
}
|
|
|
|
const auto libsym = resolver->lookup_symbol(strtab + sym->st_name);
|
|
|
|
if (libsym) {
|
|
return libsym;
|
|
}
|
|
printf("symbol lookup for %s failed\n", strtab + sym->st_name);
|
|
return 0;
|
|
}
|
|
|
|
bool Elf::processRelocations() noexcept {
|
|
if (relatbl == nullptr) [[unlikely]] {
|
|
return true;
|
|
}
|
|
uint8_t *const image = data + textOffset;
|
|
const size_t length = relaLength;
|
|
for (size_t i = 0; i < length; i++) {
|
|
const Elf64_Rela *__restrict rel = relatbl + i;
|
|
switch (ELF64_R_TYPE(rel->r_info)) {
|
|
case R_X86_64_64: {
|
|
// symbol + addend
|
|
auto libsym = getSymbolAddress(rel);
|
|
if (libsym == 0) [[unlikely]] {
|
|
return false;
|
|
}
|
|
*reinterpret_cast<uintptr_t*>(image + rel->r_offset) = libsym + rel->r_addend;
|
|
break;
|
|
}
|
|
case R_X86_64_GLOB_DAT: {
|
|
// symbol
|
|
auto libsym = getSymbolAddress(rel);
|
|
if (libsym == 0) [[unlikely]] {
|
|
return false;
|
|
}
|
|
if (libsym == static_cast<uintptr_t>(-1)) [[unlikely]] {
|
|
continue;
|
|
}
|
|
*reinterpret_cast<uintptr_t*>(image + rel->r_offset) = libsym;
|
|
break;
|
|
}
|
|
case R_X86_64_RELATIVE: {
|
|
// imagebase + addend
|
|
*reinterpret_cast<uintptr_t*>(image + rel->r_offset) = imagebase + rel->r_addend;
|
|
break;
|
|
}
|
|
case R_X86_64_JMP_SLOT: {
|
|
// edge case where the dynamic relocation sections are merged
|
|
auto libsym = getSymbolAddress(rel);
|
|
if (libsym == 0) {
|
|
const Elf64_Sym *sym = symtab + ELF64_R_SYM(rel->r_info);
|
|
const char *name = strtab + sym->st_name;
|
|
__builtin_printf("failed to find library symbol %s\n", name);
|
|
return false;
|
|
}
|
|
*reinterpret_cast<uintptr_t*>(image + rel->r_offset) = libsym;
|
|
break;
|
|
}
|
|
default:
|
|
const Elf64_Sym *sym = symtab + ELF64_R_SYM(rel->r_info);
|
|
const char *name = strtab + sym->st_name;
|
|
unsigned int type = ELF64_R_TYPE(rel->r_info);
|
|
__builtin_printf("unexpected relocation type %u for symbol %s\n", type, name);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Elf::processPltRelocations() noexcept {
|
|
if (plt == nullptr) [[unlikely]] {
|
|
return true;
|
|
}
|
|
uint8_t *const image = data + textOffset;
|
|
const size_t length = pltLength;
|
|
for (size_t i = 0; i < length; i++) {
|
|
const Elf64_Rela *__restrict rel = plt + i;
|
|
if ((ELF64_R_TYPE(rel->r_info)) != R_X86_64_JMP_SLOT) [[unlikely]] {
|
|
const Elf64_Sym *sym = symtab + ELF64_R_SYM(rel->r_info);
|
|
const char *name = strtab + sym->st_name;
|
|
unsigned int type = ELF64_R_TYPE(rel->r_info);
|
|
__builtin_printf("unexpected relocation type %u for symbol %s\n", type, name);
|
|
return false;
|
|
}
|
|
auto libsym = getSymbolAddress(rel);
|
|
if (libsym == 0) {
|
|
const Elf64_Sym *sym = symtab + ELF64_R_SYM(rel->r_info);
|
|
const char *name = strtab + sym->st_name;
|
|
__builtin_printf("failed to find library symbol %s\n", name);
|
|
return false;
|
|
}
|
|
*reinterpret_cast<uintptr_t*>(image + rel->r_offset) = libsym;
|
|
}
|
|
|
|
return true;
|
|
}
|