mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-31 12:35:51 +00:00
Return the main program's handle, the first one sceKernelGetModuleList lists and one sceKernelGetModuleInfo accepts. reSOT (PPSA99023) imports it from libkernel and passes the handle to sceKernelGetModuleInfo to find its own code segment; before this its eboot failed to load on the unresolved import. The PS5 3.20 export lists have it in libkernel as well as libkernel_sys, and on Windows a libkernel import never reaches libkernel_sys, so the throwing libkernel_sys stub moves to libkernel as pselect did. Co-Authored-By: Claude Opus 5.5 <[email protected]>
219 lines
13 KiB
C++
219 lines
13 KiB
C++
#include "prx/libkernel/include/Api.hpp"
|
|
#include "SceTypes.hpp"
|
|
#include "prx/libkernel/KernelErrors.hpp"
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
#include <source_location>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
static void Require(bool value, const std::source_location& location = std::source_location::current()) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "module info check failed at line %u\n", location.line());
|
|
std::abort();
|
|
}
|
|
}
|
|
template<typename TFunction>
|
|
static bool ThrowsInvalidArgument(TFunction function) {
|
|
try {
|
|
function();
|
|
} catch (const std::invalid_argument&) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
static ModuleInfoEx Query(const void* address, int expected) {
|
|
ModuleInfoEx info{};
|
|
info.st_size = sizeof(ModuleInfoEx);
|
|
Require(sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(address), 1, &info) == expected);
|
|
return info;
|
|
}
|
|
int main(int argc, char** argv) {
|
|
Require(argc == 2);
|
|
void* module = dlopen_nid_postfix(argv[1], 2);
|
|
Require(module != nullptr);
|
|
const void* add = dlsym_nid_postfix(module, "GuestModuleAdd");
|
|
Require(add != nullptr);
|
|
const auto info = Query(add, 0);
|
|
Require(info.st_size == 0);
|
|
Require(info.id == static_cast<KernelModule>(reinterpret_cast<std::intptr_t>(module)));
|
|
Require(std::string(info.name) == std::string(argv[1]).substr(std::string(argv[1]).find_last_of("/\\") + 1));
|
|
Require(info.segment_count > 0 && info.segment_count <= 4 && info.ref_count == 1);
|
|
bool executable = false;
|
|
for (std::uint32_t i = 0; i < info.segment_count; ++i) {
|
|
const auto& segment = info.segments[i];
|
|
if (reinterpret_cast<std::uintptr_t>(add) >= segment.address && reinterpret_cast<std::uintptr_t>(add) - segment.address < segment.size)
|
|
executable = (segment.prot & 5) == 5;
|
|
}
|
|
Require(executable);
|
|
#if defined(_WIN32) && defined(__SEH__)
|
|
Require(info.eh_frame_addr == 0 && info.eh_frame_size == 0);
|
|
#else
|
|
Require(info.eh_frame_addr != 0 && info.eh_frame_size != 0);
|
|
#endif
|
|
#ifndef _WIN32
|
|
Require(info.eh_frame_hdr_addr != 0 && info.eh_frame_hdr_size != 0);
|
|
#endif
|
|
Require(info.init_proc_addr == 0 || info.init_proc_addr >= info.segments[0].address);
|
|
const auto self = Query(reinterpret_cast<const void*>(&Require), 0);
|
|
Require(self.id != info.id && Query(reinterpret_cast<const void*>(&Query), 0).id == self.id);
|
|
void* second = dlopen_nid_postfix(argv[1], 2);
|
|
Require(second && Query(add, 0).id == info.id);
|
|
Require(dlclose_nid_postfix(second) == 0);
|
|
std::vector<KernelModule> handles(512, -1);
|
|
std::size_t count = 0;
|
|
Require(sceKernelGetModuleList(handles.data(), handles.size(), &count) == 0);
|
|
Require(count >= 2 && count < handles.size() && handles[count] == -1);
|
|
handles.resize(count);
|
|
Require(handles.front() == self.id);
|
|
const auto mainHandle = sceKernelGetExecutableModuleHandle();
|
|
Require(mainHandle == self.id);
|
|
ModuleInfo executableInfo{};
|
|
executableInfo.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo(mainHandle, &executableInfo) == 0 && std::string(executableInfo.name) == self.name);
|
|
bool holdsOwnCode = false;
|
|
for (std::uint32_t i = 0; i < executableInfo.segment_count; ++i) {
|
|
const auto code = reinterpret_cast<std::uintptr_t>(&Query);
|
|
holdsOwnCode = holdsOwnCode || (code >= executableInfo.segments[i].address && code - executableInfo.segments[i].address < executableInfo.segments[i].size);
|
|
}
|
|
Require(holdsOwnCode);
|
|
const auto kernel = Query(reinterpret_cast<const void*>(&sceKernelGetModuleList), 0).id;
|
|
Require(std::count(handles.begin(), handles.end(), kernel) == 1);
|
|
Require(std::count(handles.begin(), handles.end(), info.id) == 0);
|
|
auto sorted = handles;
|
|
std::sort(sorted.begin(), sorted.end());
|
|
Require(std::adjacent_find(sorted.begin(), sorted.end()) == sorted.end());
|
|
std::vector<KernelModule> shorter(count, -1);
|
|
std::size_t untouched = 99;
|
|
Require(sceKernelGetModuleList(shorter.data(), count - 1, &untouched) == SCE_KERNEL_ERROR_ENOMEM);
|
|
Require(untouched == 99 && shorter.back() == -1 && std::equal(handles.begin(), handles.end() - 1, shorter.begin()));
|
|
Require(sceKernelGetModuleList(nullptr, 0, &untouched) == SCE_KERNEL_ERROR_EFAULT);
|
|
std::vector<KernelModule> written(count, -1);
|
|
Require(sceKernelGetModuleList(written.data(), written.size(), nullptr) == SCE_KERNEL_ERROR_EFAULT);
|
|
Require(written == handles);
|
|
const auto kernelEx = Query(reinterpret_cast<const void*>(&sceKernelGetModuleInfo), 0);
|
|
ModuleInfo kernelInfo{};
|
|
kernelInfo.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo(kernel, &kernelInfo) == 0);
|
|
Require(kernelInfo.st_size == sizeof(ModuleInfo) && std::string(kernelInfo.name) == kernelEx.name);
|
|
Require(kernelInfo.segment_count == kernelEx.segment_count && kernelInfo.segment_count > 0);
|
|
for (std::uint32_t i = 0; i < kernelInfo.segment_count; ++i)
|
|
Require(kernelInfo.segments[i].address == kernelEx.segments[i].address && kernelInfo.segments[i].size == kernelEx.segments[i].size && kernelInfo.segments[i].prot == kernelEx.segments[i].prot);
|
|
Require(std::all_of(std::begin(kernelInfo.fingerprint), std::end(kernelInfo.fingerprint), [](std::uint8_t byte) { return byte == 0; }));
|
|
for (const auto handle : handles) {
|
|
ModuleInfo listed{};
|
|
listed.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo(handle, &listed) == 0 && listed.name[0] != '\0');
|
|
if (handle == self.id) Require(std::string(listed.name) == self.name);
|
|
}
|
|
ModuleInfo program{};
|
|
program.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo(self.id, &program) == 0 && program.segment_count > 0);
|
|
void* textAddress = nullptr;
|
|
std::size_t textSize = 0;
|
|
Require(sceKernelGetLibkernelTextLocation_nid_postfix(&textAddress, &textSize) == 0);
|
|
Require(reinterpret_cast<std::uintptr_t>(textAddress) == program.segments[0].address);
|
|
Require(textSize == program.segments[0].size);
|
|
Require(ThrowsInvalidArgument([&] { sceKernelGetLibkernelTextLocation_nid_postfix(nullptr, &textSize); }));
|
|
Require(ThrowsInvalidArgument([&] { sceKernelGetLibkernelTextLocation_nid_postfix(&textAddress, nullptr); }));
|
|
InternalSegmentInfo segment{};
|
|
Require(sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(&segment) == 0);
|
|
Require(segment.address == program.segments[0].address && segment.size == program.segments[0].size);
|
|
Require(ThrowsInvalidArgument([] { sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(nullptr); }));
|
|
for (const std::uint64_t size : {0x158, 0x1a8, 0x1b0, 0x200, 0}) {
|
|
ModuleInfo sized{};
|
|
sized.st_size = size;
|
|
sized.name[0] = 'x';
|
|
Require(sceKernelGetModuleInfo(kernel, &sized) == SCE_KERNEL_ERROR_EINVAL && sized.st_size == size && sized.name[0] == 'x');
|
|
}
|
|
Require(sceKernelGetModuleInfo(kernel, nullptr) == SCE_KERNEL_ERROR_EFAULT);
|
|
ModuleInfo unknown{};
|
|
unknown.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo(info.id, &unknown) == SCE_KERNEL_ERROR_ESRCH);
|
|
Require(sceKernelGetModuleInfo(-1, &unknown) == SCE_KERNEL_ERROR_ESRCH && unknown.name[0] == '\0');
|
|
std::vector<KernelModule> titleHandles(handles.size() + 1, -1);
|
|
std::size_t titleCount = 0;
|
|
Require(sceKernelGetModuleList2(titleHandles.data(), titleHandles.size(), &titleCount) == 0);
|
|
Require(titleCount >= 1 && titleCount < count && titleHandles[titleCount] == -1);
|
|
titleHandles.resize(titleCount);
|
|
Require(titleHandles.front() == self.id);
|
|
for (const auto handle : titleHandles) {
|
|
Require(std::count(handles.begin(), handles.end(), handle) == 1);
|
|
ModuleInfo title{}, listed{};
|
|
title.st_size = listed.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo2(handle, &title) == 0 && sceKernelGetModuleInfo(handle, &listed) == 0);
|
|
Require(std::string(title.name) == listed.name && title.segment_count == listed.segment_count);
|
|
}
|
|
std::size_t titleUntouched = 99;
|
|
Require(sceKernelGetModuleList2(titleHandles.data(), titleCount - 1, &titleUntouched) == SCE_KERNEL_ERROR_ENOMEM && titleUntouched == 99);
|
|
Require(sceKernelGetModuleList2(nullptr, 0, &titleUntouched) == SCE_KERNEL_ERROR_EFAULT);
|
|
Require(sceKernelGetModuleList2(titleHandles.data(), titleHandles.size(), nullptr) == SCE_KERNEL_ERROR_EFAULT);
|
|
for (const auto handle : handles) {
|
|
if (std::count(titleHandles.begin(), titleHandles.end(), handle) != 0) continue;
|
|
ModuleInfo system{};
|
|
system.st_size = sizeof(ModuleInfo);
|
|
system.name[0] = 'x';
|
|
Require(sceKernelGetModuleInfo2(handle, &system) == SCE_KERNEL_ERROR_EPERM && system.name[0] == 'x');
|
|
Require(sceKernelGetModuleInfo2(handle, nullptr) == SCE_KERNEL_ERROR_EFAULT);
|
|
system.st_size = 0x158;
|
|
Require(sceKernelGetModuleInfo2(handle, &system) == SCE_KERNEL_ERROR_EINVAL);
|
|
}
|
|
ModuleInfo absent{};
|
|
absent.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo2(-1, &absent) == SCE_KERNEL_ERROR_ESRCH && absent.name[0] == 0);
|
|
int local = 0;
|
|
Query(&local, SCE_KERNEL_ERROR_ESRCH);
|
|
Require(sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(add), 2, nullptr) == SCE_KERNEL_ERROR_EFAULT);
|
|
ModuleInfoEx invalid{};
|
|
invalid.st_size = sizeof(ModuleInfoEx);
|
|
for (const int flags : {-1, 0, 3, 4, 8}) {
|
|
std::memset(&invalid, 0xbb, sizeof(invalid));
|
|
Require(sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(add), flags, &invalid) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(std::all_of(reinterpret_cast<const unsigned char*>(&invalid), reinterpret_cast<const unsigned char*>(&invalid + 1), [](unsigned char value) { return value == 0; }));
|
|
}
|
|
Require(ThrowsInvalidArgument([&] { sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(add), 0, nullptr); }));
|
|
std::vector<KernelModule> everyModule(64);
|
|
std::vector<KernelModule> titleOnlyModules(64);
|
|
std::size_t everyCount = 0;
|
|
std::size_t titleOnlyCount = 0;
|
|
Require(sceKernelGetModuleList(everyModule.data(), everyModule.size(), &everyCount) == 0);
|
|
Require(sceKernelGetModuleList2(titleOnlyModules.data(), titleOnlyModules.size(), &titleOnlyCount) == 0);
|
|
const auto systemModule = std::find_if(everyModule.begin(), everyModule.begin() + static_cast<std::ptrdiff_t>(everyCount),
|
|
[&](KernelModule handle) { return std::find(titleOnlyModules.begin(), titleOnlyModules.begin() + static_cast<std::ptrdiff_t>(titleOnlyCount), handle) == titleOnlyModules.begin() + static_cast<std::ptrdiff_t>(titleOnlyCount); });
|
|
Require(systemModule != everyModule.begin() + static_cast<std::ptrdiff_t>(everyCount));
|
|
ModuleInfo systemInfo{};
|
|
systemInfo.st_size = sizeof(ModuleInfo);
|
|
Require(sceKernelGetModuleInfo(*systemModule, &systemInfo) == 0 && systemInfo.segment_count > 0);
|
|
const auto systemAddress = static_cast<std::uintptr_t>(systemInfo.segments[0].address);
|
|
ModuleInfoEx anyModule{};
|
|
Require(sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(&Query), 2, &anyModule) == 0 && anyModule.segment_count > 0 && anyModule.st_size == 0);
|
|
Require(sceKernelGetModuleInfoFromAddr(systemAddress, 1, &anyModule) == 0 && anyModule.id == *systemModule);
|
|
std::memset(&anyModule, 0xbb, sizeof(anyModule));
|
|
Require(sceKernelGetModuleInfoFromAddr(systemAddress, 2, &anyModule) == SCE_KERNEL_ERROR_ESRCH && anyModule.segment_count == 0 && anyModule.name[0] == 0);
|
|
Require(sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(add), 1, &anyModule) == 0 && anyModule.id == info.id);
|
|
ModuleInfoEx unset{};
|
|
unset.st_size = 0xf0000;
|
|
Require(sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(add), 1, &unset) == 0 && unset.st_size == 0);
|
|
const auto again = Query(add, 0);
|
|
Require(again.eh_frame_hdr_addr == info.eh_frame_hdr_addr && again.eh_frame_hdr_size == info.eh_frame_hdr_size);
|
|
Require(again.eh_frame_addr == info.eh_frame_addr && again.eh_frame_size == info.eh_frame_size);
|
|
Require(dlclose_nid_postfix(module) == 0);
|
|
void* reloaded = dlopen_nid_postfix(argv[1], 2);
|
|
Require(reloaded != nullptr);
|
|
const void* reloadedAdd = dlsym_nid_postfix(reloaded, "GuestModuleAdd");
|
|
Require(reloadedAdd != nullptr);
|
|
const auto fresh = Query(reloadedAdd, 0);
|
|
Require(fresh.eh_frame_size == info.eh_frame_size && fresh.eh_frame_hdr_size == info.eh_frame_hdr_size);
|
|
if (info.eh_frame_addr)
|
|
Require(fresh.eh_frame_addr - fresh.segments[0].address == info.eh_frame_addr - info.segments[0].address);
|
|
else Require(fresh.eh_frame_addr == 0);
|
|
Require(dlclose_nid_postfix(reloaded) == 0);
|
|
Require(sceKernelGetExecutableModuleHandle() == self.id);
|
|
}
|