mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
238 lines
12 KiB
C++
238 lines
12 KiB
C++
#include "prx/libc/include/LegacySignals.hpp"
|
|
#include "prx/libc/include/GuestErrno.hpp"
|
|
#include <nid/NidCompute.hpp>
|
|
#include <array>
|
|
#include <bit>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#include <link.h>
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
void Require(bool value, const char* message) {
|
|
if (value) return;
|
|
std::fprintf(stderr, "sigvec modules: %s\n", message);
|
|
std::abort();
|
|
}
|
|
|
|
class Module {
|
|
public:
|
|
explicit Module(const std::filesystem::path& path) {
|
|
#ifdef _WIN32
|
|
handle = LoadLibraryExW(path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
if (handle == nullptr) std::fprintf(stderr, "LoadLibraryExW(%ls) failed: %lu\n", path.c_str(), GetLastError());
|
|
#else
|
|
handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
if (handle == nullptr) std::fprintf(stderr, "%s\n", dlerror());
|
|
#endif
|
|
Require(handle != nullptr, "load actual patched module");
|
|
#ifdef _WIN32
|
|
HMODULE pinned = nullptr;
|
|
Require(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
|
reinterpret_cast<LPCWSTR>(handle), &pinned) != 0 && pinned == handle, "pin actual module until process exit");
|
|
#endif
|
|
}
|
|
|
|
~Module() {
|
|
#ifdef _WIN32
|
|
FreeLibrary(handle);
|
|
#else
|
|
dlclose(handle);
|
|
#endif
|
|
}
|
|
|
|
Module(const Module&) = delete;
|
|
Module& operator=(const Module&) = delete;
|
|
|
|
void* Find(const char* name) const {
|
|
#ifdef _WIN32
|
|
auto* result = reinterpret_cast<void*>(GetProcAddress(handle, name));
|
|
#else
|
|
auto* result = dlsym(handle, name);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
void* Symbol(const char* name) const {
|
|
auto* result = Find(name);
|
|
Require(result != nullptr, "lookup actual native export");
|
|
return result;
|
|
}
|
|
|
|
void RequireOwner(void* address) const {
|
|
#ifdef _WIN32
|
|
HMODULE owner = nullptr;
|
|
Require(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
|
reinterpret_cast<LPCWSTR>(address), &owner) != 0 && owner == handle, "NID resolved outside requested DLL");
|
|
#else
|
|
link_map* image = nullptr;
|
|
Dl_info info{};
|
|
Require(dlinfo(handle, RTLD_DI_LINKMAP, &image) == 0 && image != nullptr, "read requested module identity");
|
|
Require(dladdr(address, &info) != 0 && info.dli_fbase == reinterpret_cast<void*>(image->l_addr), "NID resolved through an ELF dependency");
|
|
#endif
|
|
}
|
|
|
|
template<typename T>
|
|
T Function(const char* name) const {
|
|
return reinterpret_cast<T>(Symbol(Nid::ComputeNid(name, "").c_str()));
|
|
}
|
|
|
|
private:
|
|
#ifdef _WIN32
|
|
HMODULE handle = nullptr;
|
|
#else
|
|
void* handle = nullptr;
|
|
#endif
|
|
};
|
|
|
|
using Sigvec = decltype(&sigvec_nid_postfix);
|
|
GuestSigaction recorded{};
|
|
GuestSigaction answer{0x12345678, 0x13, {{0x87654321, 0x11111111, 0x22222222, 0x33333333}}};
|
|
int calls = 0;
|
|
int result = 0;
|
|
bool sawReplacement = false;
|
|
bool sawPrevious = false;
|
|
int requestedSignal = 0;
|
|
|
|
int APS5_VABI RecordAction(int signal, const GuestSigaction* replacement, GuestSigaction* previous) {
|
|
++calls;
|
|
requestedSignal = signal;
|
|
sawReplacement = replacement != nullptr;
|
|
sawPrevious = previous != nullptr;
|
|
if (replacement != nullptr) recorded = *replacement;
|
|
if (result == 0 && previous != nullptr) *previous = answer;
|
|
return result;
|
|
}
|
|
|
|
bool Equal(const GuestSignalVector& first, const GuestSignalVector& second) {
|
|
return first.handler == second.handler && first.mask == second.mask && first.flags == second.flags;
|
|
}
|
|
|
|
void CheckSharedDisposition(const std::array<Sigvec, 2>& exports, const Module& libc) {
|
|
const auto error = libc.Function<decltype(&__error_nid_postfix)>("__error");
|
|
GuestSignalVector original{};
|
|
Require(exports[0](30, nullptr, &original) == 0, "save actual kernel disposition");
|
|
const GuestSignalVector first{1, std::bit_cast<std::int32_t>(0x80000005u), 3};
|
|
const GuestSignalVector second{1, -123, 2};
|
|
GuestSignalVector queried{};
|
|
Require(exports[1](30, &first, nullptr) == 0 && exports[0](30, nullptr, &queried) == 0 && Equal(queried, first), "Internal install is not shared with libc");
|
|
Require(exports[0](30, &second, nullptr) == 0 && exports[1](30, nullptr, &queried) == 0 && Equal(queried, second), "libc install is not shared with Internal");
|
|
for (const auto function : exports) {
|
|
queried = first;
|
|
Require(function(0, &second, &queried) == -1 && *error() == 22 && Equal(queried, first), "actual backend failure changed module output");
|
|
}
|
|
Require(exports[0](30, &original, nullptr) == 0, "restore actual kernel disposition");
|
|
}
|
|
|
|
void CheckConversions(const std::array<Sigvec, 2>& exports) {
|
|
const GuestSignalVector expected{answer.handler, std::bit_cast<std::int32_t>(answer.mask.bits[0]), answer.flags ^ 2};
|
|
for (const auto function : exports) {
|
|
for (const int flags : {0, 1, 2, 3, -1}) {
|
|
const GuestSignalVector replacement{0x34567890, -1, flags};
|
|
GuestSignalVector previous{};
|
|
result = 0;
|
|
const int before = calls;
|
|
Require(function(30, &replacement, &previous) == 0 && calls == before + 1, "module wrapper did not call shared core once");
|
|
Require(requestedSignal == 30 && sawReplacement && sawPrevious, "forwarded argument identity");
|
|
Require(recorded.handler == replacement.handler && recorded.flags == (flags ^ 2), "firmware handler/flag conversion");
|
|
Require(recorded.mask.bits[0] == 0xffffffffu && recorded.mask.bits[1] == 0 && recorded.mask.bits[2] == 0 && recorded.mask.bits[3] == 0, "firmware low mask conversion");
|
|
Require(Equal(previous, expected), "firmware previous vector conversion");
|
|
GuestSignalVector alias = replacement;
|
|
Require(function(30, &alias, &alias) == 0 && Equal(alias, expected), "module-owned alias conversion");
|
|
Require(recorded.handler == replacement.handler && recorded.flags == (flags ^ 2) && recorded.mask.bits[0] == 0xffffffffu, "alias input changed before forwarding");
|
|
}
|
|
result = 0;
|
|
Require(function(30, nullptr, nullptr) == 0 && !sawReplacement && !sawPrevious, "both null pointers were not forwarded");
|
|
GuestSignalVector previous{};
|
|
Require(function(30, nullptr, &previous) == 0 && !sawReplacement && sawPrevious && Equal(previous, expected), "null replacement query");
|
|
const GuestSignalVector replacement{0x34567890, 7, 0};
|
|
Require(function(30, &replacement, nullptr) == 0 && sawReplacement && !sawPrevious, "null previous output");
|
|
for (const int failure : {-7, -1, 1}) {
|
|
result = failure;
|
|
previous = replacement;
|
|
Require(function(17, &replacement, &previous) == failure && Equal(previous, replacement), "forward failure changed output or result");
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckGuestOutput(const std::array<Sigvec, 2>& exports, const Module& kernel, const Module& libc) {
|
|
const auto allocate = kernel.Function<decltype(&sceKernelAllocateDirectMemory)>("sceKernelAllocateDirectMemory");
|
|
const auto map = kernel.Function<decltype(&sceKernelMapDirectMemory)>("sceKernelMapDirectMemory");
|
|
const auto unmap = kernel.Function<decltype(&sceKernelMunmap)>("sceKernelMunmap");
|
|
const auto release = kernel.Function<decltype(&sceKernelReleaseDirectMemory)>("sceKernelReleaseDirectMemory");
|
|
constexpr std::size_t page = 0x4000;
|
|
std::int64_t physical = 0;
|
|
Require(allocate(0, 0x7fffffffffll, page * 2, 0, 0, &physical) == 0, "allocate real guest module output");
|
|
void* mapping = nullptr;
|
|
Require(map(&mapping, page * 2, 3, 0, physical, 0) == 0 && mapping != nullptr, "map real guest module output");
|
|
auto* output = reinterpret_cast<GuestSignalVector*>(static_cast<unsigned char*>(mapping) + page - 8);
|
|
const GuestSignalVector expected{answer.handler, std::bit_cast<std::int32_t>(answer.mask.bits[0]), answer.flags ^ 2};
|
|
result = 0;
|
|
for (const auto function : exports) Require(function(30, nullptr, output) == 0 && Equal(*output, expected), "owned NID guest-backed output");
|
|
#ifdef _WIN32
|
|
const auto protect = kernel.Function<decltype(&sceKernelMprotect)>("sceKernelMprotect");
|
|
const auto error = libc.Function<decltype(&__error_nid_postfix)>("__error");
|
|
const GuestSignalVector sentinel{0x23456789, 5, 2};
|
|
*output = sentinel;
|
|
Require(protect(static_cast<unsigned char*>(mapping) + page, page, 1) == 0, "protect module output tail");
|
|
for (const auto function : exports) {
|
|
const int before = calls;
|
|
Require(function(30, &sentinel, output) == -1 && *error() == 14 && calls == before, "rejected module output mutated backend");
|
|
Require(Equal(*output, sentinel), "rejected module output partially written");
|
|
}
|
|
Require(protect(static_cast<unsigned char*>(mapping) + page, page, 3) == 0, "restore module output protection");
|
|
#else
|
|
static_cast<void>(libc);
|
|
#endif
|
|
Require(unmap(reinterpret_cast<std::uint64_t>(mapping), page * 2) == 0, "unmap module output");
|
|
Require(release(physical, page * 2) == 0, "release module output");
|
|
}
|
|
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
Require(argc == 3 || (argc == 4 && std::string(argv[3]) == "libc-first"), "expected patched module directory, exception probe and optional load order");
|
|
const auto directory = std::filesystem::absolute(argv[1]);
|
|
const Module first(directory / (argc == 4 ? "libc.prx" : "libSceLibcInternal.prx"));
|
|
const Module libc(directory / "libc.prx");
|
|
const Module kernel(directory / "libkernel.prx");
|
|
const Module internal(directory / "libSceLibcInternal.prx");
|
|
auto* libcAddress = libc.Symbol("yhxKO9LYc8A");
|
|
auto* internalAddress = internal.Symbol("yhxKO9LYc8A");
|
|
libc.RequireOwner(libcAddress);
|
|
internal.RequireOwner(internalAddress);
|
|
Require(libcAddress != internalAddress, "both modules resolve to the same wrapper");
|
|
std::array<Sigvec, 2> exports{reinterpret_cast<Sigvec>(libcAddress), reinterpret_cast<Sigvec>(internalAddress)};
|
|
for (const auto function : exports) Require(function(30, nullptr, nullptr) == 0, "cold kernel registration missing");
|
|
CheckSharedDisposition(exports, libc);
|
|
auto* registrationAddress = libc.Symbol("RegisterSigvec_nid_no_patch");
|
|
libc.RequireOwner(registrationAddress);
|
|
auto* coreAddress = libc.Symbol("LegacySigvec_nid_no_patch");
|
|
libc.RequireOwner(coreAddress);
|
|
#ifdef _WIN32
|
|
Require(internal.Find("LegacySigvec_nid_no_patch") == nullptr, "Internal duplicated the private core");
|
|
#else
|
|
Require(internal.Find("LegacySigvec_nid_no_patch") == coreAddress, "Internal does not use libc private core");
|
|
#endif
|
|
const auto registration = reinterpret_cast<decltype(&RegisterSigvec_nid_no_patch)>(registrationAddress);
|
|
const Module exceptionProbe(directory / argv[2]);
|
|
auto* exceptionAddress = exceptionProbe.Symbol("SigvecMissingBackend_nid_no_patch");
|
|
exceptionProbe.RequireOwner(exceptionAddress);
|
|
const auto checkMissingBackend = reinterpret_cast<bool (*)(Sigvec, Sigvec, const void*)>(exceptionAddress);
|
|
registration(nullptr);
|
|
Require(checkMissingBackend(exports[0], exports[1], coreAddress), "module wrapper has duplicate or stale callback state");
|
|
registration(RecordAction);
|
|
CheckConversions(exports);
|
|
CheckGuestOutput(exports, kernel, libc);
|
|
registration(kernel.Function<LegacySigaction_nid_no_patch>("sigaction"));
|
|
for (const auto function : exports) Require(function(30, nullptr, nullptr) == 0, "actual kernel registration restore");
|
|
std::printf("sigvec NID ownership, shared callback/core, firmware conversions and guest output passed (%s first)\n", argc == 4 ? "libc" : "Internal");
|
|
}
|