diff --git a/Makefile b/Makefile index deb50bd..19933e3 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 3eceba3..0810314 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,34 @@ - Splatoon (All regions, v272) - [ENLBufferPwn](https://github.com/PabloMK7/ENLBufferPwn) fix - - ENL nullptr deref fix + OOB read \ No newline at end of file + - 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` \ No newline at end of file diff --git a/source/config.cpp b/source/config.cpp new file mode 100644 index 0000000..87c6a1d --- /dev/null +++ b/source/config.cpp @@ -0,0 +1,104 @@ +#include "config.h" +#include "globals.h" +#include "patches.h" +#include "utils/logger.h" +#include +#include +#include +#include +#include + +#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; +} diff --git a/source/config.h b/source/config.h new file mode 100644 index 0000000..a060ebe --- /dev/null +++ b/source/config.h @@ -0,0 +1,2 @@ +#pragma once +void readStorage(); \ No newline at end of file diff --git a/source/globals.cpp b/source/globals.cpp new file mode 100644 index 0000000..00212a1 --- /dev/null +++ b/source/globals.cpp @@ -0,0 +1,2 @@ +bool gActivateMK8Patches = true; +bool gActivateSplatoonPatches = true; \ No newline at end of file diff --git a/source/globals.h b/source/globals.h new file mode 100644 index 0000000..f372861 --- /dev/null +++ b/source/globals.h @@ -0,0 +1,4 @@ +#pragma once + +extern bool gActivateMK8Patches; +extern bool gActivateSplatoonPatches; \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 030df99..0003e8b 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,56 +1,87 @@ -#include -#include - +#include "config.h" +#include "globals.h" #include "patches.h" +#include "utils/logger.h" +#include +#include +#include +#include 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 gRPLInfo; +std::vector mk8Patches; +std::vector splatoonPatches; + +void RemovePatches(std::vector &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(); +} \ No newline at end of file diff --git a/source/patcher/patcher.cpp b/source/patcher/patcher.cpp deleted file mode 100644 index 8d530d1..0000000 --- a/source/patcher/patcher.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "patcher.h" - -#include "utils/logger.h" - -#include -#include - -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; -} diff --git a/source/patcher/patcher.h b/source/patcher/patcher.h deleted file mode 100644 index ed89dc5..0000000 --- a/source/patcher/patcher.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -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); diff --git a/source/patcher/rplinfo.cpp b/source/patcher/rplinfo.cpp deleted file mode 100644 index a1d864a..0000000 --- a/source/patcher/rplinfo.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright 2022 Pretendo Network contributors - Copyright 2022 Ash Logan - - 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 - -#include -#include - -std::optional> TryGetRPLInfo() { - int num_rpls = OSDynLoad_GetNumberOfRPLs(); - if (num_rpls == 0) { - return std::nullopt; - } - - DEBUG_FUNCTION_LINE("num_rpls: %d", num_rpls); - - std::vector 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; -} diff --git a/source/patcher/rplinfo.h b/source/patcher/rplinfo.h deleted file mode 100644 index 52eeabc..0000000 --- a/source/patcher/rplinfo.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2022 Pretendo Network contributors - Copyright 2022 Ash Logan - - 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 -#include -#include -#include - -#include - -using rplinfo = std::vector; - -std::optional TryGetRPLInfo(); -bool PatchDynLoadFunctions(); - -constexpr inline std::optional 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; -} diff --git a/source/patches.cpp b/source/patches.cpp index 5c4aa9b..78597a8 100644 --- a/source/patches.cpp +++ b/source/patches.cpp @@ -1,10 +1,13 @@ #include "patches.h" +#include "utils/logger.h" +#include +#include // ========================================================================================== - 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 &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 &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; } -} \ No newline at end of file + 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; +} diff --git a/source/patches.h b/source/patches.h index a3ba4d2..046493c 100644 --- a/source/patches.h +++ b/source/patches.h @@ -1,48 +1,30 @@ #pragma once -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include - #include #include - -#undef DECL_FUNCTION -#include - -enum EPatchType { - PATCH_ENL_BUFFER_RCE, - PATCH_ENL_ID_TOKEN_RCE, -}; - -struct GamePatches { - const char *mTitleName; - uint64_t mRegionalTIDs[3]; - std::vector mPatchTypes; - void (*mPatchFunc)(EPatchType type); -}; +#include +#include +#include +#include +#include +#include +#include +#include +#include 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 &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 &functionPatches); // ========================================================================================== - -extern std::optional gRPLInfo; -static std::vector sGamePatchList = { - {"Mario Kart 8 ", {MARIO_KART_8_TID}, {MARIO_KART_8_PATCHES}, MARIO_KART_8_ApplyPatch}, - {"Splatoon", {SPLATOON_TID}, {SPLATOON_PATCHES}, SPLATOON_ApplyPatch}, -}; \ No newline at end of file diff --git a/source/utils/logger.c b/source/utils/logger.c new file mode 100644 index 0000000..f700806 --- /dev/null +++ b/source/utils/logger.c @@ -0,0 +1,36 @@ +#ifdef DEBUG +#include +#include +#include +#include + +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 +} \ No newline at end of file diff --git a/source/utils/logger.h b/source/utils/logger.h index b0fdbc6..a2c86a9 100644 --- a/source/utils/logger.h +++ b/source/utils/logger.h @@ -1,29 +1,69 @@ #pragma once +#include +#include +#include + #ifdef __cplusplus extern "C" { #endif -#include -#include +#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 }