Simplify patches by using the latest libfunctionpatcher, add a config menu, make logging optional, always log blocked attacks.

This commit is contained in:
Maschell 2023-01-08 13:46:24 +01:00
parent 8743a2e061
commit cf9620d7d3
15 changed files with 465 additions and 301 deletions

View file

@ -21,7 +21,7 @@ WUMS_ROOT := $(DEVKITPRO)/wums
#-------------------------------------------------------------------------------
TARGET := rce_patches
BUILD := build
SOURCES := source source/patcher
SOURCES := source source/utils
DATA := data
INCLUDES := source
@ -36,7 +36,17 @@ CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
CXXFLAGS := $(CFLAGS) -std=gnu++20
ASFLAGS := $(ARCH)
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libkernel.ld $(WUPSSPECS)
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
ifeq ($(DEBUG),1)
CXXFLAGS += -DDEBUG -g
CFLAGS += -DDEBUG -g
endif
ifeq ($(DEBUG),VERBOSE)
CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
endif
LIBS := -lfunctionpatcher -lkernel -lwups -lwut

View file

@ -8,3 +8,33 @@
- Splatoon (All regions, v272)
- [ENLBufferPwn](https://github.com/PabloMK7/ENLBufferPwn) fix
- ENL nullptr deref fix + OOB read
## Buildflags
### Logging
Building via `make` only logs errors (via OSReport). To enable logging via the [LoggingModule](https://github.com/wiiu-env/LoggingModule) set `DEBUG` to `1` or `VERBOSE`.
`make` Logs errors only (via OSReport).
`make DEBUG=1` Enables information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
`make DEBUG=VERBOSE` Enables verbose information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present, it'll fallback to UDP (Port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging.
## Building using the Dockerfile
It's possible to use a docker image for building. This way you don't need anything installed on your host system.
```
# Build docker image (only needed once)
docker build . -t rcepatches-builder
# make
docker run -it --rm -v ${PWD}:/project rcepatches-builder make
# make clean
docker run -it --rm -v ${PWD}:/project rcepatches-builder make clean
```
## Format the code via docker
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source -i`

104
source/config.cpp Normal file
View file

@ -0,0 +1,104 @@
#include "config.h"
#include "globals.h"
#include "patches.h"
#include "utils/logger.h"
#include <coreinit/title.h>
#include <string>
#include <sysapp/launch.h>
#include <wups.h>
#include <wups/config/WUPSConfigItemBoolean.h>
#define MK8PATCHES_CONFIG_ID "mk8patches"
#define SPLATOONPATCHES_CONFIG_ID "splatoonpatches"
#define LOAD_BOOL_FROM_CONFIG(config_name, __variable__) \
if ((storageRes = WUPS_GetBool(nullptr, config_name, &__variable__)) == WUPS_STORAGE_ERROR_NOT_FOUND) { \
if (WUPS_StoreBool(nullptr, config_name, __variable__) != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to store bool"); \
} \
} else if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) { \
DEBUG_FUNCTION_LINE_WARN("Failed to get bool %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes); \
} \
while (0)
#define PROCESS_BOOL_ITEM_CHANGED(__config__name, __variable__) \
if (std::string_view(item->configId) == __config__name) { \
DEBUG_FUNCTION_LINE_VERBOSE("New value in %s: %d", __config__name, newValue); \
__variable__ = newValue; \
WUPS_StoreInt(nullptr, __config__name, __variable__); \
return; \
} \
while (0)
bool prevActivateMK8Patches = true;
bool prevActivateSplatoonPatches = true;
void readStorage() {
// Open storage to read values
WUPSStorageError storageRes = WUPS_OpenStorage();
if (storageRes != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to open storage %s (%d)", WUPS_GetStorageStatusStr(storageRes), storageRes);
} else {
LOAD_BOOL_FROM_CONFIG(MK8PATCHES_CONFIG_ID, gActivateMK8Patches);
LOAD_BOOL_FROM_CONFIG(SPLATOONPATCHES_CONFIG_ID, gActivateSplatoonPatches);
prevActivateMK8Patches = gActivateMK8Patches;
prevActivateSplatoonPatches = gActivateSplatoonPatches;
// Close storage
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to close storage");
}
}
}
void boolItemChanged(ConfigItemBoolean *item, bool newValue) {
PROCESS_BOOL_ITEM_CHANGED(MK8PATCHES_CONFIG_ID, gActivateMK8Patches);
PROCESS_BOOL_ITEM_CHANGED(SPLATOONPATCHES_CONFIG_ID, gActivateSplatoonPatches);
}
WUPS_GET_CONFIG() {
// We open the storage, so we can persist the configuration the user did.
if (WUPS_OpenStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to open storage");
return 0;
}
WUPSConfigHandle config;
WUPSConfig_CreateHandled(&config, "RCE Patches");
WUPSConfigCategoryHandle cat;
WUPSConfig_AddCategoryByNameHandled(config, "Games", &cat);
WUPSConfigItemBoolean_AddToCategoryHandled(config, cat, MK8PATCHES_CONFIG_ID, "Patch exploits in Mario Kart 8", gActivateMK8Patches, &boolItemChanged);
WUPSConfigItemBoolean_AddToCategoryHandled(config, cat, SPLATOONPATCHES_CONFIG_ID, "Patch exploits in Splatoon", gActivateSplatoonPatches, &boolItemChanged);
return config;
}
WUPS_CONFIG_CLOSED() {
// Save all changes
if (WUPS_CloseStorage() != WUPS_STORAGE_ERROR_SUCCESS) {
DEBUG_FUNCTION_LINE("Failed to close storage");
}
if (prevActivateMK8Patches != gActivateMK8Patches) {
if (OSGetTitleID() == MARIO_KART_8_TID_J ||
OSGetTitleID() == MARIO_KART_8_TID_E ||
OSGetTitleID() == MARIO_KART_8_TID_U) {
_SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr);
}
}
if (prevActivateSplatoonPatches != gActivateSplatoonPatches) {
if (OSGetTitleID() == SPLATOON_TID_J ||
OSGetTitleID() == SPLATOON_TID_E ||
OSGetTitleID() == SPLATOON_TID_U) {
_SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr);
}
}
prevActivateMK8Patches = gActivateMK8Patches;
prevActivateSplatoonPatches = gActivateSplatoonPatches;
}

2
source/config.h Normal file
View file

@ -0,0 +1,2 @@
#pragma once
void readStorage();

2
source/globals.cpp Normal file
View file

@ -0,0 +1,2 @@
bool gActivateMK8Patches = true;
bool gActivateSplatoonPatches = true;

4
source/globals.h Normal file
View file

@ -0,0 +1,4 @@
#pragma once
extern bool gActivateMK8Patches;
extern bool gActivateSplatoonPatches;

View file

@ -1,56 +1,87 @@
#include <string.h>
#include <wups.h>
#include "config.h"
#include "globals.h"
#include "patches.h"
#include "utils/logger.h"
#include <cstring>
#include <function_patcher/function_patching.h>
#include <vector>
#include <wups.h>
WUPS_PLUGIN_NAME("rce_patches");
WUPS_PLUGIN_DESCRIPTION("Patches security issues in WiiU games that could be triggered remotely");
WUPS_PLUGIN_VERSION("v1.0");
WUPS_PLUGIN_AUTHOR("Rambo6Glaz");
WUPS_PLUGIN_AUTHOR("Rambo6Glaz, Maschell");
WUPS_PLUGIN_LICENSE("");
WUPS_USE_STORAGE("rce_patches"); // Unique id for the storage api
std::optional<rplinfo> gRPLInfo;
std::vector<PatchData> mk8Patches;
std::vector<PatchData> splatoonPatches;
void RemovePatches(std::vector<PatchData> &patchHandles) {
for (auto &patch : patchHandles) {
if (FunctionPatcher_RemoveFunctionPatch(patch.handle) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_WARN("Failed to remove function patch %08X", patch.handle);
}
}
patchHandles.clear();
}
INITIALIZE_PLUGIN() {
DEBUG_FUNCTION_LINE("Patch functions");
if (FunctionPatcher_InitLibrary() != FUNCTION_PATCHER_RESULT_SUCCESS) {
OSFatal("rce_patches: FunctionPatcher_InitLibrary failed");
}
readStorage();
}
DEINITIALIZE_PLUGIN() {
RemovePatches(mk8Patches);
RemovePatches(splatoonPatches);
}
ON_APPLICATION_START() {
initLogging();
// If this is not a supported title, no need to do anything
uint64_t titleId = OSGetTitleID();
GamePatches *gamePatch = nullptr;
for (auto &patch : sGamePatchList) {
for (int i = 0; i < 3; i++) {
if (patch.mRegionalTIDs[i] == titleId) {
gamePatch = &patch;
break;
}
if (!gActivateMK8Patches && !mk8Patches.empty()) {
DEBUG_FUNCTION_LINE("Remove MK8 patches");
RemovePatches(mk8Patches);
} else if (gActivateMK8Patches && mk8Patches.empty()) {
DEBUG_FUNCTION_LINE("Add MK8 patches");
if (!MARIO_KART_8_AddPatches(mk8Patches)) {
DEBUG_FUNCTION_LINE_ERR("Failed to add Mario Kart patches");
RemovePatches(mk8Patches);
}
}
if (!gamePatch)
return;
// Init logging
if (!WHBLogModuleInit()) {
WHBLogCafeInit();
WHBLogUdpInit();
if (!gActivateSplatoonPatches && !splatoonPatches.empty()) {
DEBUG_FUNCTION_LINE("Remove Splatoon patches");
RemovePatches(splatoonPatches);
} else if (gActivateSplatoonPatches && splatoonPatches.empty()) {
DEBUG_FUNCTION_LINE("Add Splatoon patches");
if (!SPLATOON_AddPatches(splatoonPatches)) {
DEBUG_FUNCTION_LINE_ERR("Failed to add Splatoon patches");
RemovePatches(splatoonPatches);
}
}
WHBLogPrintf("rce_patches: applying patches for %s...", gamePatch->mTitleName);
// Patch the dynload functions so GetRPLInfo works
if (!PatchDynLoadFunctions()) {
WHBLogPrintf("rce_patches: Failed to patch dynload functions");
return;
for (auto &patch : mk8Patches) {
bool isPatched = false;
if (FunctionPatcher_IsFunctionPatched(patch.handle, &isPatched) == FUNCTION_PATCHER_RESULT_SUCCESS && isPatched) {
DEBUG_FUNCTION_LINE("MK8: Function %s (handle: %08X) has been patched", patch.name.c_str(), patch.handle);
} else {
DEBUG_FUNCTION_LINE_ERR("Splatoon: Function %s (handle: %08X) has NOT been patched", patch.name.c_str(), patch.handle);
}
}
// Get the RPLInfo
gRPLInfo = TryGetRPLInfo();
if (!gRPLInfo) {
WHBLogPrintf("rce_patches: Failed to get RPL info");
return;
}
// For each patch type, call apply patch func (terrible design lol)
for (auto &patch : gamePatch->mPatchTypes) {
gamePatch->mPatchFunc(patch);
for (auto &patch : splatoonPatches) {
bool isPatched = false;
if (FunctionPatcher_IsFunctionPatched(patch.handle, &isPatched) == FUNCTION_PATCHER_RESULT_SUCCESS && isPatched) {
DEBUG_FUNCTION_LINE("Splatoon: Function %s (handle: %08X) has been patched", patch.name.c_str(), patch.handle);
} else {
DEBUG_FUNCTION_LINE_ERR("Splatoon: Function %s (handle: %08X) has NOT been patched", patch.name.c_str(), patch.handle);
}
}
}
ON_APPLICATION_ENDS() {
deinitLogging();
}

View file

@ -1,20 +0,0 @@
#include "patcher.h"
#include "utils/logger.h"
#include <coreinit/memorymap.h>
#include <kernel/kernel.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;
}

View file

@ -1,6 +0,0 @@
#pragma once
#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);

View file

@ -1,71 +0,0 @@
/* 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;
}

View file

@ -1,32 +0,0 @@
/* 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.
*/
#pragma once
#include <algorithm>
#include <optional>
#include <string>
#include <vector>
#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;
}

View file

@ -1,10 +1,13 @@
#include "patches.h"
#include "utils/logger.h"
#include <function_patcher/function_patching.h>
#include <set>
// ==========================================================================================
DECL_FUNCTION(bool, enl_ParseIdentificationToken, void *identifiationInfo, sead_String *identificationToken) {
// Fix for RCE (stack overflow if identification buffer was bigger than 16)
if (strnlen(identificationToken->mBuffer, 16) == 16) {
DEBUG_FUNCTION_LINE_INFO("Avoided bufferoverlow in enl_ParseIdentificationToken!");
identificationToken->mBuffer[15] = '\0';
return real_enl_ParseIdentificationToken(identifiationInfo, identificationToken);
}
@ -12,9 +15,11 @@ DECL_FUNCTION(bool, enl_ParseIdentificationToken, void *identifiationInfo, sead_
return real_enl_ParseIdentificationToken(identifiationInfo, identificationToken);
}
enl_ContentTransporter *(*real_enl_TransportManager_getContentTransporter)(void *_this, unsigned char &id);
DECL_FUNCTION(void, enl_TransportManager_updateReceiveBuffer_, void *_this, signed char const &bufferId, uint8_t *data, uint32_t size) {
DECL_FUNCTION(enl_ContentTransporter *, enl_TransportManager_getContentTransporter, void *_this, unsigned char &id) {
return real_enl_TransportManager_getContentTransporter(_this, id);
}
DECL_FUNCTION(void, enl_TransportManager_updateReceiveBuffer_, void *_this, signed char const &bufferId, uint8_t *data, uint32_t size) {
// Check for end record in the data, if there is not, drop packet
bool hasEndRecord = false;
@ -29,107 +34,134 @@ DECL_FUNCTION(void, enl_TransportManager_updateReceiveBuffer_, void *_this, sign
enl_ContentTransporter *contentTransp = real_enl_TransportManager_getContentTransporter(_this, record->mContentTransporterID);
// Actual fix for the ENL nullptr deref crash (lmao)
if (!contentTransp)
if (!contentTransp) {
DEBUG_FUNCTION_LINE_INFO("Avoided ENL nullptr deref crash in enl_TransportManager_updateReceiveBuffer_!");
return;
}
if (record->mContentLength > 0x440)
if (record->mContentLength > 0x440) {
DEBUG_FUNCTION_LINE_INFO("record->mContentLength was over 0x440 in enl_TransportManager_updateReceiveBuffer_!");
return;
}
pData += sizeof(enl_RecordHeader);
pData += record->mContentLength;
}
if (!hasEndRecord)
if (!hasEndRecord) {
return;
}
return real_enl_TransportManager_updateReceiveBuffer_(_this, bufferId, data, size);
}
DECL_FUNCTION(void, enl_Buffer_set, enl_Buffer *_this, uint8_t const *data, size_t size) {
// Fix for the RCE
if (!_this->mData || !size || size > _this->mCapacity)
if (!_this->mData || !size || size > _this->mCapacity) {
DEBUG_FUNCTION_LINE_INFO("Avoided overflow in enl_Buffer_set!");
return;
}
memcpy(_this->mData, data, size);
_this->mSize = size;
}
// ==========================================================================================
void MARIO_KART_8_ApplyPatch(EPatchType type) {
auto turbo_rpx = FindRPL(*gRPLInfo, "Turbo.rpx");
if (!turbo_rpx) {
WHBLogPrintf("rce_patches: Couldn't find Turbo.rpx ...");
return;
bool MARIO_KART_8_AddPatches(std::vector<PatchData> &functionPatches) {
uint64_t titleIds[] = {MARIO_KART_8_TID};
function_replacement_data_t repl = REPLACE_FUNCTION_OF_EXECUTABLE_BY_ADDRESS_WITH_VERSION(
enl_ParseIdentificationToken,
titleIds, sizeof(titleIds) / sizeof(titleIds[0]),
"Turbo.rpx",
0x8E3930, // Address of 'enl::PiaUtil::ParseIdentificationToken'
64, 64);
PatchedFunctionHandle handle = 0;
if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add \"enl_ParseIdentificationToken\" patch");
return false;
}
functionPatches.emplace_back("enl::PiaUtil::ParseIdentificationToken", handle);
if (type == PATCH_ENL_ID_TOKEN_RCE) {
// Address of 'enl::PiaUtil::ParseIdentificationToken'
uint32_t addr_func = turbo_rpx->textAddr + 0x8E3930;
function_replacement_data_t repl = REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(
enl_ParseIdentificationToken,
OSEffectiveToPhysical(addr_func),
addr_func,
FP_TARGET_PROCESS_GAME_AND_MENU);
FunctionPatcherPatchFunction(&repl, nullptr);
WHBLogPrintf("rce_patches: Patched Mario Kart 8 (PATCH_ENL_ID_TOKEN_RCE)");
function_replacement_data_t repl1 = REPLACE_FUNCTION_OF_EXECUTABLE_BY_ADDRESS_WITH_VERSION(
enl_TransportManager_getContentTransporter,
titleIds, sizeof(titleIds) / sizeof(titleIds[0]),
"Turbo.rpx",
0x8D7678, // Address of 'enl::TransportManager::getContentTransporter'
64, 64);
if (FunctionPatcher_AddFunctionPatch(&repl1, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add \"enl_TransportManager_getContentTransporter\" patch");
return false;
}
functionPatches.emplace_back("enl::TransportManager::getContentTransporter", handle);
if (type == PATCH_ENL_BUFFER_RCE) {
real_enl_TransportManager_getContentTransporter = (enl_ContentTransporter * (*) (void *, unsigned char &) )(turbo_rpx->textAddr + 0x8D7678);
// Address of 'enl::TransportManager::updateReceiveBuffer_'
uint32_t addr_func = turbo_rpx->textAddr + 0x8D772C;
function_replacement_data_t repl = REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(
enl_TransportManager_updateReceiveBuffer_,
OSEffectiveToPhysical(addr_func),
addr_func,
FP_TARGET_PROCESS_GAME_AND_MENU);
FunctionPatcherPatchFunction(&repl, nullptr);
addr_func = turbo_rpx->textAddr + 0x8CF228;
repl = REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(
enl_Buffer_set,
OSEffectiveToPhysical(addr_func),
addr_func,
FP_TARGET_PROCESS_GAME_AND_MENU);
FunctionPatcherPatchFunction(&repl, nullptr);
WHBLogPrintf("rce_patches: Patched Mario Kart 8 (PATCH_ENL_BUFFER_RCE)");
function_replacement_data_t repl2 = REPLACE_FUNCTION_OF_EXECUTABLE_BY_ADDRESS_WITH_VERSION(
enl_TransportManager_updateReceiveBuffer_,
titleIds, sizeof(titleIds) / sizeof(titleIds[0]),
"Turbo.rpx",
0x8D772C, // Address of 'enl::TransportManager::updateReceiveBuffer_'
64, 64);
if (FunctionPatcher_AddFunctionPatch(&repl2, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add \"enl_TransportManager_updateReceiveBuffer_\" patch");
return false;
}
functionPatches.emplace_back("enl::TransportManager::updateReceiveBuffer_", handle);
function_replacement_data_t repl3 = REPLACE_FUNCTION_OF_EXECUTABLE_BY_ADDRESS_WITH_VERSION(
enl_Buffer_set,
titleIds, sizeof(titleIds) / sizeof(titleIds[0]),
"Turbo.rpx",
0x8CF228,
64, 64);
if (FunctionPatcher_AddFunctionPatch(&repl3, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add \"enl_Buffer_set\" patch");
return false;
}
functionPatches.emplace_back("enl:Buffer::set", handle);
WHBLogPrintf("rce_patches: Patched Mario Kart 8 (PATCH_ENL_BUFFER_RCE)");
return true;
}
// ==========================================================================================
void SPLATOON_ApplyPatch(EPatchType type) {
auto gambit_rpx = FindRPL(*gRPLInfo, "Gambit.rpx");
if (!gambit_rpx) {
WHBLogPrintf("rce_patches: Couldn't find Gambit.rpx ...");
return;
bool SPLATOON_AddPatches(std::vector<PatchData> &functionPatches) {
uint64_t titleIds[] = {SPLATOON_TID};
function_replacement_data_t repl = REPLACE_FUNCTION_OF_EXECUTABLE_BY_ADDRESS_WITH_VERSION(
enl_TransportManager_getContentTransporter,
titleIds, sizeof(titleIds) / sizeof(titleIds[0]),
"Gambit.rpx",
0xB4108C, // Address of 'enl::TransportManager::getContentTransporter'
272, 272);
PatchedFunctionHandle handle;
if (FunctionPatcher_AddFunctionPatch(&repl, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add \"enl_TransportManager_getContentTransporter\" patch");
return false;
}
functionPatches.emplace_back("enl::PiaUtil::ParseIdentificationToken", handle);
if (type == PATCH_ENL_BUFFER_RCE) {
real_enl_TransportManager_getContentTransporter = (enl_ContentTransporter * (*) (void *, unsigned char &) )(gambit_rpx->textAddr + 0xB4108C);
// Address of 'enl::TransportManager::updateReceiveBuffer_'
uint32_t addr_func = gambit_rpx->textAddr + 0xB41140;
function_replacement_data_t repl = REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(
enl_TransportManager_updateReceiveBuffer_,
OSEffectiveToPhysical(addr_func),
addr_func,
FP_TARGET_PROCESS_GAME_AND_MENU);
FunctionPatcherPatchFunction(&repl, nullptr);
// Address of 'enl:Buffer::set'
addr_func = gambit_rpx->textAddr + 0xB4D178;
repl = REPLACE_FUNCTION_VIA_ADDRESS_FOR_PROCESS(
enl_Buffer_set,
OSEffectiveToPhysical(addr_func),
addr_func,
FP_TARGET_PROCESS_GAME_AND_MENU);
FunctionPatcherPatchFunction(&repl, nullptr);
WHBLogPrintf("rce_patches: Patched Splatoon (PATCH_ENL_BUFFER_RCE)");
function_replacement_data_t repl1 = REPLACE_FUNCTION_OF_EXECUTABLE_BY_ADDRESS_WITH_VERSION(
enl_TransportManager_updateReceiveBuffer_,
titleIds, sizeof(titleIds) / sizeof(titleIds[0]),
"Gambit.rpx",
0xB41140, // Address of 'enl::TransportManager::updateReceiveBuffer_'
272, 272);
if (FunctionPatcher_AddFunctionPatch(&repl1, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add \"enl_TransportManager_updateReceiveBuffer_\" patch");
return false;
}
functionPatches.emplace_back("enl::TransportManager::updateReceiveBuffer_", handle);
function_replacement_data_t repl2 = REPLACE_FUNCTION_OF_EXECUTABLE_BY_ADDRESS_WITH_VERSION(
enl_Buffer_set,
titleIds, sizeof(titleIds) / sizeof(titleIds[0]),
"Gambit.rpx",
0xB4D178, // Address of 'enl:Buffer::set'
272, 272);
if (FunctionPatcher_AddFunctionPatch(&repl2, &handle, nullptr) != FUNCTION_PATCHER_RESULT_SUCCESS) {
DEBUG_FUNCTION_LINE_ERR("Failed to add \"enl_Buffer_set\" patch");
return false;
}
functionPatches.emplace_back("enl:Buffer::set", handle);
return true;
}

View file

@ -1,48 +1,30 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <vector>
#include <whb/log.h>
#include <whb/log_cafe.h>
#include <whb/log_module.h>
#include <whb/log_udp.h>
#include <coreinit/title.h>
#include <patcher/patcher.h>
#include <patcher/rplinfo.h>
#include <kernel/kernel.h>
#include <coreinit/cache.h>
#include <coreinit/memorymap.h>
#undef DECL_FUNCTION
#include <function_patcher/function_patching.h>
enum EPatchType {
PATCH_ENL_BUFFER_RCE,
PATCH_ENL_ID_TOKEN_RCE,
};
struct GamePatches {
const char *mTitleName;
uint64_t mRegionalTIDs[3];
std::vector<EPatchType> mPatchTypes;
void (*mPatchFunc)(EPatchType type);
};
#include <coreinit/title.h>
#include <cstdint>
#include <cstring>
#include <function_patcher/fpatching_defines.h>
#include <kernel/kernel.h>
#include <set>
#include <string>
#include <utility>
#include <vector>
struct sead_String {
char *mBuffer;
uint32_t vtable;
};
WUT_CHECK_OFFSET(sead_String, 0x00, mBuffer);
WUT_CHECK_OFFSET(sead_String, 0x04, vtable);
struct __attribute__((__packed__)) enl_RecordHeader {
uint8_t mContentTransporterID;
uint16_t mContentLength;
};
WUT_CHECK_OFFSET(enl_RecordHeader, 0x00, mContentTransporterID);
WUT_CHECK_OFFSET(enl_RecordHeader, 0x01, mContentLength);
struct enl_Buffer {
uint8_t *mData;
@ -50,6 +32,10 @@ struct enl_Buffer {
size_t mSize;
bool isAllocated;
};
WUT_CHECK_OFFSET(enl_Buffer, 0x00, mData);
WUT_CHECK_OFFSET(enl_Buffer, 0x04, mCapacity);
WUT_CHECK_OFFSET(enl_Buffer, 0x08, mSize);
WUT_CHECK_OFFSET(enl_Buffer, 0x0C, isAllocated);
struct enl_ContentTransporter {
struct ContentTransporterVtbl {
@ -67,19 +53,41 @@ struct enl_ContentTransporter {
unsigned char (*getContentID)(enl_ContentTransporter *_this);
// ...
};
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x00, a);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x04, b);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x08, f08);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x0C, init);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x10, _f10);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x14, getSendBuffer);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x18, _f18);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x1C, getSendBufferSize);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x20, _f20);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x24, isNeedSend);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x28, _f28);
WUT_CHECK_OFFSET(ContentTransporterVtbl, 0x2C, getContentID);
ContentTransporterVtbl *vtable;
};
WUT_CHECK_OFFSET(enl_ContentTransporter, 0x00, vtable);
class PatchData {
public:
PatchData(std::string name, PatchedFunctionHandle handle) : name(std::move(name)), handle(handle) {
}
std::string name;
PatchedFunctionHandle handle;
};
// ==========================================================================================
#define MARIO_KART_8_TID_J 0x000500001010EB00
#define MARIO_KART_8_TID_U 0x000500001010EC00
#define MARIO_KART_8_TID_E 0x000500001010ED00
#define MARIO_KART_8_TID_J 0x000500001010EB00
#define MARIO_KART_8_TID_U 0x000500001010EC00
#define MARIO_KART_8_TID_E 0x000500001010ED00
#define MARIO_KART_8_TID MARIO_KART_8_TID_J, MARIO_KART_8_TID_U, MARIO_KART_8_TID_E
#define MARIO_KART_8_PATCHES PATCH_ENL_BUFFER_RCE, PATCH_ENL_ID_TOKEN_RCE
#define MARIO_KART_8_TID MARIO_KART_8_TID_J, MARIO_KART_8_TID_U, MARIO_KART_8_TID_E
void MARIO_KART_8_ApplyPatch(EPatchType type);
bool MARIO_KART_8_AddPatches(std::vector<PatchData> &functionPatches);
// ==========================================================================================
@ -90,12 +98,6 @@ void MARIO_KART_8_ApplyPatch(EPatchType type);
#define SPLATOON_TID SPLATOON_TID_J, SPLATOON_TID_U, SPLATOON_TID_E
#define SPLATOON_PATCHES PATCH_ENL_BUFFER_RCE
void SPLATOON_ApplyPatch(EPatchType type);
bool SPLATOON_AddPatches(std::vector<PatchData> &functionPatches);
// ==========================================================================================
extern std::optional<rplinfo> gRPLInfo;
static std::vector<GamePatches> sGamePatchList = {
{"Mario Kart 8 ", {MARIO_KART_8_TID}, {MARIO_KART_8_PATCHES}, MARIO_KART_8_ApplyPatch},
{"Splatoon", {SPLATOON_TID}, {SPLATOON_PATCHES}, SPLATOON_ApplyPatch},
};

36
source/utils/logger.c Normal file
View file

@ -0,0 +1,36 @@
#ifdef DEBUG
#include <stdint.h>
#include <whb/log_cafe.h>
#include <whb/log_module.h>
#include <whb/log_udp.h>
uint32_t moduleLogInit = false;
uint32_t cafeLogInit = false;
uint32_t udpLogInit = false;
#endif // DEBUG
void initLogging() {
#ifdef DEBUG
if (!(moduleLogInit = WHBLogModuleInit())) {
cafeLogInit = WHBLogCafeInit();
udpLogInit = WHBLogUdpInit();
}
#endif // DEBUG
}
void deinitLogging() {
#ifdef DEBUG
if (moduleLogInit) {
WHBLogModuleDeinit();
moduleLogInit = false;
}
if (cafeLogInit) {
WHBLogCafeDeinit();
cafeLogInit = false;
}
if (udpLogInit) {
WHBLogUdpDeinit();
udpLogInit = false;
}
#endif // DEBUG
}

View file

@ -1,29 +1,69 @@
#pragma once
#include <coreinit/debug.h>
#include <string.h>
#include <whb/log.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <whb/log.h>
#define LOG_APP_TYPE "P"
#define LOG_APP_NAME "rce_patches"
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...) \
do { \
OSFatal_printf("[%s]%s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", FMT, ##ARGS)
#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS)
#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \
do { \
LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \
} while (0)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
do { \
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0);
#ifdef DEBUG
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \
do { \
WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0);
#ifdef VERBOSE_DEBUG
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "", "", FMT, ##ARGS);
#else
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0)
#endif
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
#else
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##INFO ## ", "\n", FMT, ##ARGS)
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);
#endif
void initLogging();
void deinitLogging();
#ifdef __cplusplus
}