Add framework for in-game patches

This commit is contained in:
Ash Logan 2022-09-26 21:56:56 +10:00
parent e76aec4a67
commit e86bb6cf15
13 changed files with 268 additions and 74 deletions

Binary file not shown.

View File

@ -21,7 +21,7 @@ WUMS_ROOT := $(DEVKITPRO)/wums
#-------------------------------------------------------------------------------
TARGET := Inkay-pretendo
BUILD := build
SOURCES := src src/utils
SOURCES := src src/utils src/patcher src/patcher/patches
DATA := data
INCLUDES := src
@ -33,7 +33,7 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
CXXFLAGS := $(CFLAGS)
CXXFLAGS := $(CFLAGS) -std=c++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)

View File

@ -29,6 +29,7 @@
#include "wut_extra.h"
#include <utils/logger.h>
#include "url_patches.h"
#include "patcher/ingame.h"
/**
Mandatory plugin information.
@ -48,22 +49,6 @@ bool prevSkipValue = false;
#include <kernel/kernel.h>
#include <mocha/mocha.h>
//#ifdef OLD_WUPS
extern "C" {
OSDynLoad_Error
OSDynLoad_IsModuleLoaded(char const *name,
OSDynLoad_Module *outModule);
}
//#endif
const char original_url[] = "discovery.olv.nintendo.net/v1/endpoint";
const char new_url[] = "discovery.olv.pretendo.cc/v1/endpoint";
_Static_assert(sizeof(original_url) > sizeof(new_url),
"new_url too long! Must be less than 38chars.");
// We'll keep a handle to nn_olv, just to ensure it doesn't get unloaded
static OSDynLoad_Module olv_handle;
//thanks @Gary#4139 :p
static void write_string(uint32_t addr, const char* str)
{
@ -209,69 +194,14 @@ WUPS_CONFIG_CLOSED() {
prevSkipValue = skipPatches;
}
bool checkForOlvLibs() {
OSDynLoad_Module olv_handle = 0;
OSDynLoad_Error dret;
dret = OSDynLoad_IsModuleLoaded("nn_olv", &olv_handle);
if (dret == OS_DYNLOAD_OK && olv_handle != 0) {
return true;
}
dret = OSDynLoad_IsModuleLoaded("nn_olv2", &olv_handle);
if (dret == OS_DYNLOAD_OK && olv_handle != 0) {
return true;
}
return false;
}
bool replace(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz) {
for (uint32_t addr = start; addr < start + size - original_val_sz; addr++) {
int ret = memcmp(original_val, (void*)addr, original_val_sz);
if (ret == 0) {
DEBUG_FUNCTION_LINE("found str @%08x: %s", addr, (const char*)addr);
KernelCopyData(OSEffectiveToPhysical(addr), OSEffectiveToPhysical((uint32_t)new_val), new_val_sz);
DEBUG_FUNCTION_LINE("new str @%08x: %s", addr, (const char*)addr);
return true;
}
}
return false;
}
ON_APPLICATION_START() {
WHBLogUdpInit();
DEBUG_FUNCTION_LINE("Inkay: hewwo!\n");
auto olvLoaded = checkForOlvLibs();
if (!olvLoaded) {
DEBUG_FUNCTION_LINE("Inkay: no olv, quitting for now\n");
return;
}
if (!skipPatches) {
OSDynLoad_Acquire("nn_olv", &olv_handle);
DEBUG_FUNCTION_LINE("Inkay: olv! %08x\n", olv_handle);
//wish there was a better way than "blow through MEM2"
uint32_t base_addr, size;
if (OSGetMemBound(OS_MEM2, &base_addr, &size)) {
DEBUG_FUNCTION_LINE("Inkay: OSGetMemBound failed!");
return;
}
replace(base_addr, size, original_url, sizeof(original_url), new_url, sizeof(new_url));
}
else {
DEBUG_FUNCTION_LINE("Inkay: Miiverse patches skipped.");
}
if (!skipPatches) RunPatcher();
}
ON_APPLICATION_ENDS() {
DEBUG_FUNCTION_LINE("Inkay: shutting down...\n");
OSDynLoad_Release(olv_handle);
}

56
src/patcher/ingame.cpp Normal file
View File

@ -0,0 +1,56 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "ingame.h"
#include "rplinfo.h"
#include "utils/logger.h"
#include "utils/asm.h"
#include <coreinit/title.h>
#include "patches/nn_olv.h"
void RunPatcher() {
auto orplinfo = TryGetRPLInfo();
if (!orplinfo) {
DEBUG_FUNCTION_LINE("Couldn't get RPL info - trying patches");
bool bret = PatchDynLoadFunctions();
if (!bret) {
DEBUG_FUNCTION_LINE("OSDynLoad patches failed! Quitting");
return;
}
//try again
orplinfo = TryGetRPLInfo();
if (!orplinfo) {
DEBUG_FUNCTION_LINE("Still couldn't get RPL info - quitting");
return;
}
}
auto rpls = *orplinfo;
/*for (const auto& rpl : rpls) {
DEBUG_FUNCTION_LINE("rpl[0]: [%s] %08x %08x %08x|%08x %08x %08x|%08x %08x %08x",
rpl.name, rpl.textAddr, rpl.textOffset, rpl.textSize,
rpl.dataAddr, rpl.dataOffset, rpl.dataSize,
rpl.readAddr, rpl.readOffset, rpl.readSize
);
}*/
uint64_t titleId = OSGetTitleID();
auto titleVer = __OSGetTitleVersion();
// "always" patches
Patch_nn_olv(titleVer, titleId, rpls);
}

19
src/patcher/ingame.h Normal file
View File

@ -0,0 +1,19 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef INKAY_INGAME_H
#define INKAY_INGAME_H
void RunPatcher();
#endif //INKAY_INGAME_H

20
src/patcher/patcher.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "patcher.h"
#include "utils/logger.h"
#include <kernel/kernel.h>
#include <coreinit/memorymap.h>
bool replace_string(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz) {
for (uint32_t addr = start; addr < start + size - original_val_sz; addr++) {
int ret = memcmp(original_val, (void*)addr, original_val_sz);
if (ret == 0) {
DEBUG_FUNCTION_LINE("found str @%08x: %s", addr, (const char*)addr);
KernelCopyData(OSEffectiveToPhysical(addr), OSEffectiveToPhysical((uint32_t)new_val), new_val_sz);
DEBUG_FUNCTION_LINE("new str @%08x: %s", addr, (const char*)addr);
return true;
}
}
return false;
}

9
src/patcher/patcher.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef INKAY_PATCHER_H
#define INKAY_PATCHER_H
#include <cstdint>
#include <cstdlib>
bool replace_string(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz);
#endif //INKAY_PATCHER_H

View File

@ -0,0 +1,30 @@
#include "nn_olv.h"
#include "utils/logger.h"
#include "patcher/patcher.h"
#include <cstring>
const char original_url[] = "discovery.olv.nintendo.net/v1/endpoint";
const char new_url[] = "discovery.olv.pretendo.cc/v1/endpoint";
_Static_assert(sizeof(original_url) > sizeof(new_url),
"new_url too long! Must be less than 38chars.");
std::optional<OSDynLoad_NotifyData> checkForOlvLibs(const rplinfo& rpls) {
auto res = FindRPL(rpls, "nn_olv.rpl");
if (res) return res;
res = FindRPL(rpls, "nn_olv2.rpl");
if (res) return res;
return std::nullopt;
}
void Patch_nn_olv(uint32_t titleVer, uint64_t titleId, const rplinfo& rpls) {
auto olv_rpl = checkForOlvLibs(rpls);
if (!olv_rpl) {
DEBUG_FUNCTION_LINE("olv not loaded?");
return;
}
replace_string(olv_rpl->dataAddr, olv_rpl->dataSize, original_url, sizeof(original_url), new_url, sizeof(new_url));
}

View File

@ -0,0 +1,8 @@
#ifndef _TPATCH_NN_OLV_H
#define _TPATCH_NN_OLV_H
#include "patcher/rplinfo.h"
void Patch_nn_olv(uint32_t titleVer, uint64_t titleId, const rplinfo& rpls);
#endif //_TPATCH_NN_OLV_H

70
src/patcher/rplinfo.cpp Normal file
View File

@ -0,0 +1,70 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "rplinfo.h"
#include "utils/logger.h"
#include <kernel/kernel.h>
#include <coreinit/cache.h>
#include <coreinit/memorymap.h>
std::optional<std::vector<OSDynLoad_NotifyData>> TryGetRPLInfo() {
int num_rpls = OSDynLoad_GetNumberOfRPLs();
if (num_rpls == 0) {
return std::nullopt;
}
DEBUG_FUNCTION_LINE("num_rpls: %d", num_rpls);
std::vector<OSDynLoad_NotifyData> rpls;
rpls.resize(num_rpls);
bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data());
if (!ret) {
return std::nullopt;
}
return rpls;
}
bool PatchInstruction(void* instr, uint32_t original, uint32_t replacement) {
uint32_t current = *(uint32_t*)instr;
DEBUG_FUNCTION_LINE("current instr %08x", current);
if (current != original) return current == replacement;
KernelCopyData(OSEffectiveToPhysical((uint32_t)instr), OSEffectiveToPhysical((uint32_t)&replacement), sizeof(replacement));
//Only works on AROMA! WUPS 0.1's KernelCopyData is uncached, needs DCInvalidate here instead
DCFlushRange(instr, 4);
ICInvalidateRange(instr, 4);
current = *(uint32_t*)instr;
DEBUG_FUNCTION_LINE("patched instr %08x", current);
return true;
}
bool PatchDynLoadFunctions() {
uint32_t *patch1 = ((uint32_t *) &OSDynLoad_GetNumberOfRPLs) + 6;
uint32_t *patch2 = ((uint32_t *) &OSDynLoad_GetRPLInfo) + 22;
if (!PatchInstruction(patch1, 0x41820038 /* beq +38 */, 0x60000000 /*nop*/)) {
return false;
}
if (!PatchInstruction(patch2, 0x41820100 /* beq +100 */, 0x60000000 /*nop*/)) {
return false;
}
return true;
}

35
src/patcher/rplinfo.h Normal file
View File

@ -0,0 +1,35 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef INKAY_RPLINFO_H
#define INKAY_RPLINFO_H
#include <optional>
#include <vector>
#include <string>
#include <algorithm>
#include <coreinit/dynload.h>
using rplinfo = std::vector<OSDynLoad_NotifyData>;
std::optional<rplinfo> TryGetRPLInfo();
bool PatchDynLoadFunctions();
constexpr inline std::optional<OSDynLoad_NotifyData> FindRPL(const rplinfo& rpls, const std::string& name) {
auto res = std::find_if(rpls.cbegin(), rpls.cend(), [&](const OSDynLoad_NotifyData& data){ return std::string(data.name).ends_with(name); });
if (res == rpls.cend()) return std::nullopt;
return *res;
}
#endif //INKAY_RPLINFO_H

5
src/utils/asm.S Normal file
View File

@ -0,0 +1,5 @@
.global __OSGetTitleVersion
__OSGetTitleVersion:
li %r0, 0x7000
sc
blr

12
src/utils/asm.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef INKAY_ASM_H
#define INKAY_ASM_H
#include <stdint.h>
extern "C" {
uint32_t __OSGetTitleVersion();
}
#endif //INKAY_ASM_H