From f95f00e6f248e15a5add69c336a3298a8ae4c9fa Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 8 May 2022 18:51:05 +0200 Subject: [PATCH] Rewrite of Function Patcher to match libfunctionpatcher --- Dockerfile | 6 +- source/FunctionAddressProvider.cpp | 65 +++++ source/FunctionAddressProvider.h | 85 ++++++ source/PatchedFunctionData.cpp | 194 +++++++++++++ source/PatchedFunctionData.h | 62 ++++ source/export.cpp | 83 ++++++ source/export.h | 6 + source/function_patcher.cpp | 447 ++++++----------------------- source/function_patcher.h | 22 +- source/main.cpp | 103 ++++++- source/{ => utils}/CThread.h | 21 +- source/utils/globals.cpp | 11 + source/utils/globals.h | 16 ++ source/utils/utils.h | 12 + 14 files changed, 741 insertions(+), 392 deletions(-) create mode 100644 source/FunctionAddressProvider.cpp create mode 100644 source/FunctionAddressProvider.h create mode 100644 source/PatchedFunctionData.cpp create mode 100644 source/PatchedFunctionData.h create mode 100644 source/export.cpp create mode 100644 source/export.h rename source/{ => utils}/CThread.h (89%) create mode 100644 source/utils/globals.cpp create mode 100644 source/utils/globals.h create mode 100644 source/utils/utils.h diff --git a/Dockerfile b/Dockerfile index d924d4a..addf72e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM wiiuenv/devkitppc:20211229 +FROM wiiuenv/devkitppc:20220507 COPY --from=wiiuenv/libkernel:20211031 /artifacts $DEVKITPRO -COPY --from=wiiuenv/libfunctionpatcher:20210924 /artifacts $DEVKITPRO -COPY --from=wiiuenv/wiiumodulesystem:20220123 /artifacts $DEVKITPRO +COPY --from=wiiuenv/libfunctionpatcher:20220507 /artifacts $DEVKITPRO +COPY --from=wiiuenv/wiiumodulesystem:20220507 /artifacts $DEVKITPRO WORKDIR project \ No newline at end of file diff --git a/source/FunctionAddressProvider.cpp b/source/FunctionAddressProvider.cpp new file mode 100644 index 0000000..30d86f3 --- /dev/null +++ b/source/FunctionAddressProvider.cpp @@ -0,0 +1,65 @@ +#include "FunctionAddressProvider.h" +#include "utils/logger.h" +#include +#include + +uint32_t FunctionAddressProvider::getEffectiveAddressOfFunction(function_replacement_library_type_t library, const char *functionName) { + uint32_t real_addr = 0; + OSDynLoad_Module rpl_handle = nullptr; + OSDynLoad_Error err = OS_DYNLOAD_OK; + int32_t rpl_handles_size = sizeof rpl_handles / sizeof rpl_handles[0]; + + for (int32_t i = 0; i < rpl_handles_size; i++) { + if (rpl_handles[i].library == library) { + if (rpl_handles[i].handle == nullptr) { + DEBUG_FUNCTION_LINE_VERBOSE("Lets acquire handle for rpl: %s", rpl_handles[i].rplname); + err = OSDynLoad_IsModuleLoaded((char *) rpl_handles[i].rplname, &rpl_handles[i].handle); + } + if (err != OS_DYNLOAD_OK || !rpl_handles[i].handle) { + DEBUG_FUNCTION_LINE_VERBOSE("%s is not loaded yet", rpl_handles[i].rplname, err, rpl_handles[i].handle); + return 0; + } + rpl_handle = rpl_handles[i].handle; + break; + } + } + + if (!rpl_handle) { + DEBUG_FUNCTION_LINE_ERR("Failed to find the RPL handle for %s", functionName); + return 0; + } + + OSDynLoad_FindExport(rpl_handle, 0, functionName, reinterpret_cast(&real_addr)); + + if (!real_addr) { + DEBUG_FUNCTION_LINE_VERBOSE("OSDynLoad_FindExport failed for %s", functionName); + return 0; + } + + if ((library == LIBRARY_NN_ACP) && (uint32_t) (*(volatile uint32_t *) (real_addr) &0x48000002) == 0x48000000) { + auto address_diff = (uint32_t) (*(volatile uint32_t *) (real_addr) &0x03FFFFFC); + if ((address_diff & 0x03000000) == 0x03000000) { + address_diff |= 0xFC000000; + } + real_addr += (int32_t) address_diff; + if ((uint32_t) (*(volatile uint32_t *) (real_addr) &0x48000002) == 0x48000000) { + DEBUG_FUNCTION_LINE_ERR("Error"); + return 0; + } + } + + return real_addr; +} + +void FunctionAddressProvider::resetHandles() { + int32_t rpl_handles_size = sizeof rpl_handles / sizeof rpl_handles[0]; + + for (int32_t i = 0; i < rpl_handles_size; i++) { + if (rpl_handles[i].handle != nullptr) { + DEBUG_FUNCTION_LINE_VERBOSE("Resetting handle for rpl: %s", rpl_handles[i].rplname); + OSDynLoad_Release(rpl_handles[i].handle); + } + + rpl_handles[i].handle = nullptr; + } +} diff --git a/source/FunctionAddressProvider.h b/source/FunctionAddressProvider.h new file mode 100644 index 0000000..e23ffef --- /dev/null +++ b/source/FunctionAddressProvider.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include + +typedef struct rpl_handling { + function_replacement_library_type_t library; + const char rplname[15]; + OSDynLoad_Module handle; +} rpl_handling; + +class FunctionAddressProvider { +public: + uint32_t getEffectiveAddressOfFunction(function_replacement_library_type_t library, const char *functionName); + void resetHandles(); + + rpl_handling rpl_handles[LIBRARY_OTHER] = { + {LIBRARY_AVM, "avm.rpl", nullptr}, + {LIBRARY_CAMERA, "camera.rpl", nullptr}, + {LIBRARY_COREINIT, "coreinit.rpl", nullptr}, + {LIBRARY_DC, "dc.rpl", nullptr}, + {LIBRARY_DMAE, "dmae.rpl", nullptr}, + {LIBRARY_DRMAPP, "drmapp.rpl", nullptr}, + {LIBRARY_ERREULA, "erreula.rpl", nullptr}, + {LIBRARY_GX2, "gx2.rpl", nullptr}, + {LIBRARY_H264, "h264.rpl", nullptr}, + {LIBRARY_LZMA920, "lzma920.rpl", nullptr}, + {LIBRARY_MIC, "mic.rpl", nullptr}, + {LIBRARY_NFC, "nfc.rpl", nullptr}, + {LIBRARY_NIO_PROF, "nio_prof.rpl", nullptr}, + {LIBRARY_NLIBCURL, "nlibcurl.rpl", nullptr}, + {LIBRARY_NLIBNSS, "nlibnss.rpl", nullptr}, + {LIBRARY_NLIBNSS2, "nlibnss2.rpl", nullptr}, + {LIBRARY_NN_AC, "nn_ac.rpl", nullptr}, + {LIBRARY_NN_ACP, "nn_acp.rpl", nullptr}, + {LIBRARY_NN_ACT, "nn_act.rpl", nullptr}, + {LIBRARY_NN_AOC, "nn_aoc.rpl", nullptr}, + {LIBRARY_NN_BOSS, "nn_boss.rpl", nullptr}, + {LIBRARY_NN_CCR, "nn_ccr.rpl", nullptr}, + {LIBRARY_NN_CMPT, "nn_cmpt.rpl", nullptr}, + {LIBRARY_NN_DLP, "nn_dlp.rpl", nullptr}, + {LIBRARY_NN_EC, "nn_ec.rpl", nullptr}, + {LIBRARY_NN_FP, "nn_fp.rpl", nullptr}, + {LIBRARY_NN_HAI, "nn_hai.rpl", nullptr}, + {LIBRARY_NN_HPAD, "nn_hpad.rpl", nullptr}, + {LIBRARY_NN_IDBE, "nn_idbe.rpl", nullptr}, + {LIBRARY_NN_NDM, "nn_ndm.rpl", nullptr}, + {LIBRARY_NN_NETS2, "nn_nets2.rpl", nullptr}, + {LIBRARY_NN_NFP, "nn_nfp.rpl", nullptr}, + {LIBRARY_NN_NIM, "nn_nim.rpl", nullptr}, + {LIBRARY_NN_OLV, "nn_olv.rpl", nullptr}, + {LIBRARY_NN_PDM, "nn_pdm.rpl", nullptr}, + {LIBRARY_NN_SAVE, "nn_save.rpl", nullptr}, + {LIBRARY_NN_SL, "nn_sl.rpl", nullptr}, + {LIBRARY_NN_SPM, "nn_spm.rpl", nullptr}, + {LIBRARY_NN_TEMP, "nn_temp.rpl", nullptr}, + {LIBRARY_NN_UDS, "nn_uds.rpl", nullptr}, + {LIBRARY_NN_VCTL, "nn_vctl.rpl", nullptr}, + {LIBRARY_NSYSCCR, "nsysccr.rpl", nullptr}, + {LIBRARY_NSYSHID, "nsyshid.rpl", nullptr}, + {LIBRARY_NSYSKBD, "nsyskbd.rpl", nullptr}, + {LIBRARY_NSYSNET, "nsysnet.rpl", nullptr}, + {LIBRARY_NSYSUHS, "nsysuhs.rpl", nullptr}, + {LIBRARY_NSYSUVD, "nsysuvd.rpl", nullptr}, + {LIBRARY_NTAG, "ntag.rpl", nullptr}, + {LIBRARY_PADSCORE, "padscore.rpl", nullptr}, + {LIBRARY_PROC_UI, "proc_ui.rpl", nullptr}, + {LIBRARY_SNDCORE2, "sndcore2.rpl", nullptr}, + {LIBRARY_SNDUSER2, "snduser2.rpl", nullptr}, + {LIBRARY_SND_CORE, "snd_core.rpl", nullptr}, + {LIBRARY_SND_USER, "snd_user.rpl", nullptr}, + {LIBRARY_SWKBD, "swkbd.rpl", nullptr}, + {LIBRARY_SYSAPP, "sysapp.rpl", nullptr}, + {LIBRARY_TCL, "tcl.rpl", nullptr}, + {LIBRARY_TVE, "tve.rpl", nullptr}, + {LIBRARY_UAC, "uac.rpl", nullptr}, + {LIBRARY_UAC_RPL, "uac_rpl.rpl", nullptr}, + {LIBRARY_USB_MIC, "usb_mic.rpl", nullptr}, + {LIBRARY_UVC, "uvc.rpl", nullptr}, + {LIBRARY_UVD, "uvd.rpl", nullptr}, + {LIBRARY_VPAD, "vpad.rpl", nullptr}, + {LIBRARY_VPADBASE, "vpadbase.rpl", nullptr}, + {LIBRARY_ZLIB125, "zlib125.rpl", nullptr}}; +}; diff --git a/source/PatchedFunctionData.cpp b/source/PatchedFunctionData.cpp new file mode 100644 index 0000000..9375f06 --- /dev/null +++ b/source/PatchedFunctionData.cpp @@ -0,0 +1,194 @@ +#include "PatchedFunctionData.h" +#include "utils/utils.h" + +std::optional> PatchedFunctionData::make_shared(std::shared_ptr functionAddressProvider, + function_replacement_data_t *replacementData, + MEMHeapHandle heapHandle) { + if (!replacementData) { + return {}; + } + + auto ptr = make_shared_nothrow(std::move(functionAddressProvider)); + if (!ptr) { + return {}; + } + + ptr->isPatched = false; + ptr->heapHandle = heapHandle; + ptr->library = replacementData->library; + ptr->targetProcess = replacementData->targetProcess; + ptr->replacementFunctionAddress = replacementData->replaceAddr; + ptr->realCallFunctionAddressPtr = replacementData->replaceCall; + + if (replacementData->library != LIBRARY_OTHER) { + ptr->functionName = replacementData->function_name; + } else { + ptr->realEffectiveFunctionAddress = replacementData->virtualAddr; + ptr->realPhysicalFunctionAddress = replacementData->physicalAddr; + } + + ptr->jumpToOriginal = (uint32_t *) MEMAllocFromExpHeapEx(ptr->heapHandle, 0x5 * sizeof(uint32_t), 4); + + if (ptr->replacementFunctionAddress > 0x03FFFFFC || ptr->targetProcess != FP_TARGET_PROCESS_ALL) { + ptr->jumpDataSize = 15; // We could predict the actual size and save some memory, but at the moment we don't need it. + ptr->jumpData = (uint32_t *) MEMAllocFromExpHeapEx(ptr->heapHandle, ptr->jumpDataSize * sizeof(uint32_t), 4); + + if (!ptr->jumpData) { + DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data"); + return {}; + } + } + + if (!ptr->jumpToOriginal) { + DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data"); + return {}; + } + + return ptr; +} + +bool PatchedFunctionData::updateFunctionAddresses() { + if (this->library == LIBRARY_OTHER) { + return true; + } + + if (!this->functionName) { + DEBUG_FUNCTION_LINE_ERR("Function name was empty. This should never happen."); + OSFatal("function name was empty"); + return false; + } + + auto real_address = functionAddressProvider->getEffectiveAddressOfFunction(library, this->functionName->c_str()); + if (!real_address) { + DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s, updating address not possible.", this->functionName->c_str()); + return false; + } + + this->realEffectiveFunctionAddress = real_address; + auto physicalFunctionAddress = (uint32_t) OSEffectiveToPhysical(real_address); + if (!physicalFunctionAddress) { + DEBUG_FUNCTION_LINE_ERR("Error. Something is wrong with the physical address"); + return false; + } + this->realPhysicalFunctionAddress = physicalFunctionAddress; + return true; +} + +void PatchedFunctionData::generateJumpToOriginal() { + if (!this->jumpToOriginal) { + DEBUG_FUNCTION_LINE_ERR("this->jumpToOriginal is not allocated"); + OSFatal("this->jumpToOriginal is not allocated"); + } + + uint32_t jumpToAddress = this->realEffectiveFunctionAddress + 4; + + this->jumpToOriginal[0] = this->replacedInstruction; + if (((uint32_t) jumpToAddress & 0x03FFFFFC) != (uint32_t) jumpToAddress) { + // We need to do a long jump + this->jumpToOriginal[1] = 0x3d600000 | ((jumpToAddress >> 16) & 0x0000FFFF); // lis r11 ,0x1234 + this->jumpToOriginal[2] = 0x616b0000 | (jumpToAddress & 0x0000ffff); // ori r11 ,r11 ,0x5678 + this->jumpToOriginal[3] = 0x7d6903a6; // mtspr CTR ,r11 + this->jumpToOriginal[4] = 0x4e800420; // bctr + } else { + this->jumpToOriginal[1] = 0x48000002 | (jumpToAddress & 0x03FFFFFC); + } + + DCFlushRange((void *) this->jumpToOriginal, sizeof(uint32_t) * 5); + ICInvalidateRange((void *) this->jumpToOriginal, sizeof(uint32_t) * 5); + + *(this->realCallFunctionAddressPtr) = (uint32_t) this->jumpToOriginal; + OSMemoryBarrier(); +} + +void PatchedFunctionData::generateReplacementJump() { + //setting jump back + this->replaceWithInstruction = 0x48000002 | (this->replacementFunctionAddress & 0x03FFFFFC); + + // If the jump is too big or we want only patch for certain processes we need a trampoline + if (this->replacementFunctionAddress > 0x03FFFFFC || this->targetProcess != FP_TARGET_PROCESS_ALL) { + if (!this->jumpData) { + DEBUG_FUNCTION_LINE_ERR("jumpData was not allocated"); + OSFatal("jumpData was not allocated"); + } + uint32_t offset = 0; + if (this->targetProcess != FP_TARGET_PROCESS_ALL) { + auto originalFunctionAddrWithOffset = this->realEffectiveFunctionAddress + 4; + bool shortBranchToOriginalPossible = ((uint32_t) originalFunctionAddrWithOffset & 0x03FFFFFC) == (uint32_t) originalFunctionAddrWithOffset; + // Only use patched function if OSGetUPID matches function_data->targetProcess + this->jumpData[offset++] = 0x3d600000 | (((uint32_t *) OSGetUPID)[0] & 0x0000FFFF); // lis r11 ,0x0 + this->jumpData[offset++] = 0x816b0000 | (((uint32_t *) OSGetUPID)[1] & 0x0000FFFF); // lwz r11 ,0x0(r11) + if (this->targetProcess == FP_TARGET_PROCESS_GAME_AND_MENU) { + this->jumpData[offset++] = 0x2c0b0000 | FP_TARGET_PROCESS_WII_U_MENU; // cmpwi r11 ,FP_TARGET_PROCESS_WII_U_MENU + this->jumpData[offset++] = 0x41820000 | (shortBranchToOriginalPossible ? 0x00000014 : 0x00000020); // beq myfunc + this->jumpData[offset++] = 0x2c0b0000 | FP_TARGET_PROCESS_GAME; // cmpwi r11 ,FP_TARGET_PROCESS_GAME + this->jumpData[offset++] = 0x41820000 | (shortBranchToOriginalPossible ? 0x0000000C : 0x00000018); // beq myfunc + } else { + this->jumpData[offset++] = 0x2c0b0000 | this->targetProcess; // cmpwi r11 ,function_data->targetProcess + this->jumpData[offset++] = 0x41820000 | (shortBranchToOriginalPossible ? 0x0000000C : 0x00000018); // beq myfunc + } + + this->jumpData[offset++] = this->replacedInstruction; + if (((uint32_t) originalFunctionAddrWithOffset & 0x03FFFFFC) != (uint32_t) originalFunctionAddrWithOffset) { + this->jumpData[offset++] = 0x3d600000 | (((this->realEffectiveFunctionAddress + 4) >> 16) & 0x0000FFFF); // lis r11 ,(real_addr + 4)@hi + this->jumpData[offset++] = 0x616b0000 | ((this->realEffectiveFunctionAddress + 4) & 0x0000ffff); // ori r11 ,(real_addr + 4)@lo + this->jumpData[offset++] = 0x7d6903a6; // mtspr CTR ,r11 + this->jumpData[offset++] = 0x4e800420; // bctr + } else { + this->jumpData[offset++] = 0x48000002 | (originalFunctionAddrWithOffset & 0x03FFFFFC); + } + } + // myfunc: + if (((uint32_t) this->replacementFunctionAddress & 0x03FFFFFC) != (uint32_t) this->replacementFunctionAddress) { + this->jumpData[offset++] = 0x3d600000 | (((this->replacementFunctionAddress) >> 16) & 0x0000FFFF); // lis r11 ,repl_addr@hi + this->jumpData[offset++] = 0x616b0000 | ((this->replacementFunctionAddress) & 0x0000ffff); // ori r11 ,r11 ,repl_addr@lo + this->jumpData[offset++] = 0x7d6903a6; // mtspr CTR ,r11 + this->jumpData[offset] = 0x4e800420; // bctr + } else { + this->jumpData[offset] = 0x48000002 | (replacementFunctionAddress & 0x03FFFFFC); + } + + if (offset >= this->jumpDataSize) { + DEBUG_FUNCTION_LINE_ERR("Tried to overflow buffer. offset: %08X vs array size: %08X", offset, this->jumpDataSize); + OSFatal("Wrote too much data"); + } + + // Make sure the trampoline itself is usable. + if (((uint32_t) this->jumpData & 0x03FFFFFC) != (uint32_t) this->jumpData) { + DEBUG_FUNCTION_LINE_ERR("Jump is impossible"); + OSFatal("Jump is impossible"); + } + + this->replaceWithInstruction = 0x48000002 | ((uint32_t) this->jumpData & 0x03FFFFFC); + + DCFlushRange((void *) this->jumpData, sizeof(uint32_t) * 15); + ICInvalidateRange((void *) this->jumpData, sizeof(uint32_t) * 15); + } + + DCFlushRange((void *) &replaceWithInstruction, 4); + ICInvalidateRange((void *) &replaceWithInstruction, 4); + + OSMemoryBarrier(); +} + +bool PatchedFunctionData::isDynamicFunction() const { + if (this->library == LIBRARY_OTHER) { + return false; + } + if ((this->realPhysicalFunctionAddress & 0x80000000) == 0x80000000 && (this->realPhysicalFunctionAddress & 0xFF000000) != 0xFF000000) { + if (this->targetProcess == FP_TARGET_PROCESS_GAME_AND_MENU || this->targetProcess == FP_TARGET_PROCESS_GAME || this->targetProcess == FP_TARGET_PROCESS_WII_U_MENU) { + return true; + } + } + return false; +} + +PatchedFunctionData::~PatchedFunctionData() { + if (this->jumpToOriginal) { + MEMFreeToExpHeap(this->heapHandle, this->jumpToOriginal); + this->jumpToOriginal = nullptr; + } + if (this->jumpData) { + MEMFreeToExpHeap(this->heapHandle, this->jumpData); + this->jumpData = nullptr; + } +} diff --git a/source/PatchedFunctionData.h b/source/PatchedFunctionData.h new file mode 100644 index 0000000..75ba2a7 --- /dev/null +++ b/source/PatchedFunctionData.h @@ -0,0 +1,62 @@ +#pragma once + +#include "FunctionAddressProvider.h" +#include "PatchedFunctionData.h" +#include "utils/logger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class PatchedFunctionData { + +public: + ~PatchedFunctionData(); + + explicit PatchedFunctionData(std::shared_ptr functionAddressProvider) : functionAddressProvider(std::move(functionAddressProvider)) { + } + + static std::optional> make_shared(std::shared_ptr functionAddressProvider, + function_replacement_data_t *replacementData, + MEMHeapHandle heapHandle); + + bool updateFunctionAddresses(); + + void generateJumpToOriginal(); + + void generateReplacementJump(); + + uint32_t getHandle() { + return (uint32_t) this; + } + + [[nodiscard]] bool isDynamicFunction() const; + + uint32_t *jumpToOriginal{}; + uint32_t *jumpData{}; + + uint32_t realEffectiveFunctionAddress{}; + uint32_t realPhysicalFunctionAddress{}; + + uint32_t *realCallFunctionAddressPtr{}; + + uint32_t replacementFunctionAddress{}; + + uint32_t replacedInstruction{}; + + uint32_t replaceWithInstruction{}; + uint32_t jumpDataSize = 15; + MEMHeapHandle heapHandle = nullptr; + + bool isPatched{}; + function_replacement_library_type_t library{}; + FunctionPatcherTargetProcess targetProcess{}; + std::optional functionName = {}; + std::shared_ptr functionAddressProvider; +}; diff --git a/source/export.cpp b/source/export.cpp new file mode 100644 index 0000000..18d41d4 --- /dev/null +++ b/source/export.cpp @@ -0,0 +1,83 @@ +#include "export.h" +#include "PatchedFunctionData.h" +#include "function_patcher.h" +#include "utils/globals.h" +#include +#include + +bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle) { + if (function_data->VERSION != FUNCTION_REPLACEMENT_DATA_STRUCT_VERSION) { + DEBUG_FUNCTION_LINE_ERR("Failed to patch function. struct version mismatch"); + return false; + } + + auto functionDataOpt = PatchedFunctionData::make_shared(gFunctionAddressProvider, function_data, gJumpHeapHandle); + + if (!functionDataOpt) { + return false; + } + + auto functionData = functionDataOpt.value(); + + std::lock_guard lock(gPatchedFunctionsMutex); + + if (!PatchFunction(functionData)) { + DEBUG_FUNCTION_LINE_ERR("Failed to patch function"); + return false; + } + + if (outHandle) { + *outHandle = functionData->getHandle(); + } + + gPatchedFunctions.push_back(std::move(functionData)); + + OSMemoryBarrier(); + + return true; +} + +bool FunctionPatcherRestoreFunction(PatchedFunctionHandle handle) { + std::lock_guard lock(gPatchedFunctionsMutex); + std::vector> toBeTempRestored; + bool found = false; + int32_t erasePosition = 0; + std::shared_ptr toRemoved; + for (auto &cur : gPatchedFunctions) { + if (cur->getHandle() == handle) { + toRemoved = cur; + found = true; + continue; + } + // Check if something else patched the same function afterwards. + if (found) { + if (cur->realPhysicalFunctionAddress == toRemoved->realPhysicalFunctionAddress) { + toBeTempRestored.push_back(cur); + } + } else { + erasePosition++; + } + } + if (!found) { + DEBUG_FUNCTION_LINE_ERR("Failed to find PatchedFunctionData by handle %08X", handle); + return false; + } + + // Restore function patches that were done after the patch we actually want to restore. + for (auto &cur : std::ranges::reverse_view(toBeTempRestored)) { + RestoreFunction(cur); + } + + // Restore the function we actually want to restore + RestoreFunction(toRemoved); + + gPatchedFunctions.erase(gPatchedFunctions.begin() + erasePosition); + + // Apply the other patches again + for (auto &cur : toBeTempRestored) { + PatchFunction(cur); + } + + OSMemoryBarrier(); + return true; +} \ No newline at end of file diff --git a/source/export.h b/source/export.h new file mode 100644 index 0000000..70a1e04 --- /dev/null +++ b/source/export.h @@ -0,0 +1,6 @@ +#pragma once +#include + +bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle); + +bool FunctionPatcherRestoreFunction(PatchedFunctionHandle handle); \ No newline at end of file diff --git a/source/function_patcher.cpp b/source/function_patcher.cpp index f4adeca..33fa16e 100644 --- a/source/function_patcher.cpp +++ b/source/function_patcher.cpp @@ -1,25 +1,23 @@ #include "function_patcher.h" -#include "CThread.h" +#include "FunctionAddressProvider.h" +#include "PatchedFunctionData.h" +#include "utils/CThread.h" #include "utils/logger.h" #include #include -#include #include #include +#include -void writeDataAndFlushIC(CThread *thread, void *arg) { - auto *data = (uint32_t *) arg; +static void writeDataAndFlushIC(CThread *thread, void *arg) { + auto *data = (PatchedFunctionData *) arg; - DCFlushRange(data, sizeof(uint32_t) * 3); - - uint32_t replace_instruction = data[0]; - uint32_t physical_address = data[1]; - uint32_t effective_address = data[2]; + uint32_t replace_instruction = data->replaceWithInstruction; + uint32_t physical_address = data->realPhysicalFunctionAddress; + uint32_t effective_address = data->realEffectiveFunctionAddress; DCFlushRange(&replace_instruction, 4); DCFlushRange(&physical_address, 4); - DEBUG_FUNCTION_LINE_VERBOSE("Write instruction %08X to %08X [%08X] on core %d", replace_instruction, effective_address, physical_address, OSGetThreadAffinity(OSGetCurrentThread()) / 2); - auto replace_instruction_physical = (uint32_t) &replace_instruction; if (replace_instruction_physical < 0x00800000 || replace_instruction_physical >= 0x01000000) { @@ -32,362 +30,99 @@ void writeDataAndFlushIC(CThread *thread, void *arg) { ICInvalidateRange((void *) (effective_address), 4); } -void FunctionPatcherRestoreDynamicFunctions(function_replacement_data_t *replacements, uint32_t size) { - for (uint32_t i = 0; i < size; i++) { - function_replacement_data_t *function_data = &replacements[i]; - if (function_data->VERSION != FUNCTION_REPLACEMENT_DATA_STRUCT_VERSION) { - OSFatal("Failed to patch function. struct version mismatch"); - } - - uint32_t targetAddrPhys = function_data->physicalAddr; - if (function_data->library != LIBRARY_OTHER) { - targetAddrPhys = (uint32_t) OSEffectiveToPhysical(function_data->realAddr); - } - if (isDynamicFunction(targetAddrPhys)) { - DEBUG_FUNCTION_LINE("Setting alreadyPatched to false for %s because it's a dynamic function", function_data->function_name); - function_data->alreadyPatched = 0; - } - } -} - -void FunctionPatcherPatchFunction(function_replacement_data_t *replacements, uint32_t size) { - for (uint32_t i = 0; i < size; i++) { - function_replacement_data_t *function_data = &replacements[i]; - if (function_data->VERSION != FUNCTION_REPLACEMENT_DATA_STRUCT_VERSION) { - OSFatal("Failed to patch function. struct version mismatch"); - } - /* Patch branches to it. */ - volatile uint32_t *space = function_data->replace_data; - - if (function_data->alreadyPatched == 1) { - DEBUG_FUNCTION_LINE_VERBOSE("Skipping %s, its already patched\n", function_data->function_name); - continue; - } - - DEBUG_FUNCTION_LINE_VERBOSE("Patching %s ...", function_data->function_name); - - uint32_t physical = function_data->physicalAddr; - auto repl_addr = (uint32_t) function_data->replaceAddr; - auto call_addr = (uint32_t) function_data->replaceCall; - - uint32_t real_addr = function_data->virtualAddr; - if (function_data->library != LIBRARY_OTHER) { - real_addr = getAddressOfFunction(function_data->function_name, function_data->library); - } - - if (!real_addr) { - DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s", function_data->function_name); - continue; - } - - DEBUG_FUNCTION_LINE_VERBOSE("%s is located at %08X!", function_data->function_name, real_addr); - - if (function_data->library != LIBRARY_OTHER) { - physical = (uint32_t) OSEffectiveToPhysical(real_addr); - } - - if (!physical) { - DEBUG_FUNCTION_LINE("Error. Something is wrong with the physical address"); - continue; - } - - DEBUG_FUNCTION_LINE_VERBOSE("%s physical is located at %08X!", function_data->function_name, physical); - - *(volatile uint32_t *) (call_addr) = (uint32_t) (space); - - auto targetAddr = (uint32_t) space; - if (targetAddr < 0x00800000 || targetAddr >= 0x01000000) { - targetAddr = (uint32_t) OSEffectiveToPhysical(targetAddr); - } else { - targetAddr = targetAddr + 0x30800000 - 0x00800000; - } - - KernelCopyData(targetAddr, physical, 4); - - ICInvalidateRange((void *) (space), 4); - DCFlushRange((void *) (space), 4); - - space++; - - // fill the restore instruction section - function_data->realAddr = real_addr; - function_data->restoreInstruction = space[-1]; - - DEBUG_FUNCTION_LINE_VERBOSE("function_data->realAddr = %08X!", function_data->realAddr); - DEBUG_FUNCTION_LINE_VERBOSE("function_data->restoreInstruction = %08X!", function_data->restoreInstruction); - - /* - 00808cfc 3d601234 lis r11 ,0x1234 - 00808d00 616b5678 ori r11 ,r11 ,0x5678 - 00808d04 7d6903a6 mtspr CTR ,r11 - 00808d08 4e800420 bctr - */ - - *space = 0x3d600000 | (((real_addr + 4) >> 16) & 0x0000FFFF); - space++; // lis r11 ,0x1234 - *space = 0x616b0000 | ((real_addr + 4) & 0x0000ffff); - space++; // ori r11 ,r11 ,0x5678 - *space = 0x7d6903a6; - space++; // mtspr CTR ,r11 - *space = 0x4e800420; - space++; // bctr - - //setting jump back - uint32_t replace_instr = 0x48000002 | (repl_addr & 0x03FFFFFC); - - // If the jump is too big or we want only patch for certain processes we need a trampoline - if (repl_addr > 0x03FFFFFC || function_data->targetProcess != FP_TARGET_PROCESS_ALL) { - auto repl_addr_test = (uint32_t) space; - if (function_data->targetProcess != FP_TARGET_PROCESS_ALL) { - // Only use patched function if OSGetUPID matches function_data->targetProcess - *space = 0x3d600000 | (((uint32_t *) OSGetUPID)[0] & 0x0000FFFF); - space++; // lis r11 ,0x0 - *space = 0x816b0000 | (((uint32_t *) OSGetUPID)[1] & 0x0000FFFF); - space++; // lwz r11 ,0x0(r11) - if (function_data->targetProcess == FP_TARGET_PROCESS_GAME_AND_MENU) { - *space = 0x2c0b0000 | FP_TARGET_PROCESS_WII_U_MENU; - space++; // cmpwi r11 ,FP_TARGET_PROCESS_WII_U_MENU - *space = 0x41820000 | 0x00000020; - space++; // beq myfunc - *space = 0x2c0b0000 | FP_TARGET_PROCESS_GAME; - space++; // cmpwi r11 ,FP_TARGET_PROCESS_GAME - *space = 0x41820000 | 0x00000018; - space++; // beq myfunc - } else { - *space = 0x2c0b0000 | function_data->targetProcess; - space++; // cmpwi r11 ,function_data->targetProcess - *space = 0x41820000 | 0x00000018; - space++; // beq myfunc - } - *space = 0x3d600000 | (((real_addr + 4) >> 16) & 0x0000FFFF); - space++; // lis r11 ,(real_addr + 4)@hi - *space = 0x616b0000 | ((real_addr + 4) & 0x0000ffff); - space++; // ori r11 ,(real_addr + 4)@lo - *space = 0x7d6903a6; - space++; // mtspr CTR ,r11 - *space = function_data->restoreInstruction; - space++; // - *space = 0x4e800420; - space++; // bctr - } - // myfunc: - *space = 0x3d600000 | (((repl_addr) >> 16) & 0x0000FFFF); - space++; // lis r11 ,repl_addr@hi - *space = 0x616b0000 | ((repl_addr) &0x0000ffff); - space++; // ori r11 ,r11 ,repl_addr@lo - *space = 0x7d6903a6; - space++; // mtspr CTR ,r11 - *space = 0x4e800420; - space++; // bctr - - // Make sure the trampoline itself is usable. - if ((repl_addr_test & 0x03FFFFFC) != repl_addr_test) { - OSFatal("Jump is impossible"); - } - - replace_instr = 0x48000002 | (repl_addr_test & 0x03FFFFFC); - } - - if (space > &function_data->replace_data[FUNCTION_PATCHER_METHOD_STORE_SIZE]) { - OSFatal("The replacement data is too long."); - } - - DCFlushRange((void *) function_data->replace_data, FUNCTION_PATCHER_METHOD_STORE_SIZE * 4); - ICInvalidateRange((void *) function_data->replace_data, FUNCTION_PATCHER_METHOD_STORE_SIZE * 4); - - uint32_t data[] = { - replace_instr, - physical, - real_addr}; - CThread::runOnAllCores(writeDataAndFlushIC, data); - - function_data->alreadyPatched = 1; - DEBUG_FUNCTION_LINE_VERBOSE("done with patching %s!", function_data->function_name); - } - DEBUG_FUNCTION_LINE_VERBOSE("Done with patching given functions!"); -} - -void FunctionPatcherRestoreFunctions(function_replacement_data_t *replacements, uint32_t size) { - DEBUG_FUNCTION_LINE("Restoring given functions!"); - for (uint32_t i = 0; i < size; i++) { - DEBUG_FUNCTION_LINE_VERBOSE("Restoring %s... ", replacements[i].function_name); - if (replacements[i].restoreInstruction == 0 || replacements[i].realAddr == 0) { - DEBUG_FUNCTION_LINE("I dont have the information for the restore =( skip"); - continue; - } - - auto targetAddrPhys = (uint32_t) replacements[i].physicalAddr; - - if (replacements[i].library != LIBRARY_OTHER) { - targetAddrPhys = (uint32_t) OSEffectiveToPhysical(replacements[i].realAddr); - } - - if (isDynamicFunction(targetAddrPhys) && - // Other processes than the wii u menu and game one seem to keep their rpl's loaded. - replacements[i].targetProcess != FP_TARGET_PROCESS_GAME_AND_MENU && - replacements[i].targetProcess != FP_TARGET_PROCESS_GAME && - replacements[i].targetProcess != FP_TARGET_PROCESS_WII_U_MENU) { - DEBUG_FUNCTION_LINE_VERBOSE("Its a dynamic function. We don't need to restore it!", replacements[i].function_name); - replacements[i].alreadyPatched = false; - } else { - DEBUG_FUNCTION_LINE_VERBOSE("\nRestoring %08X to %08X [%08X]", (uint32_t) replacements[i].restoreInstruction, replacements[i].realAddr, targetAddrPhys); - auto sourceAddr = (uint32_t) &replacements[i].restoreInstruction; - - auto sourceAddrPhys = (uint32_t) OSEffectiveToPhysical(sourceAddr); - - // These hardcoded values should be replaced with something more dynamic. - if (sourceAddrPhys == 0 && (sourceAddr >= 0x00800000 && sourceAddr < 0x01000000)) { - sourceAddrPhys = sourceAddr + (0x30800000 - 0x00800000); - } - - if (sourceAddrPhys == 0) { - OSFatal("Failed to get physical address"); - } - - KernelCopyData(targetAddrPhys, sourceAddrPhys, 4); - DEBUG_FUNCTION_LINE_VERBOSE("\nICInvalidateRange %08X", (void *) replacements[i].realAddr); - ICInvalidateRange((void *) replacements[i].realAddr, 4); - DEBUG_FUNCTION_LINE_VERBOSE("done"); - } - replacements[i].alreadyPatched = 0; // In case a - } - DEBUG_FUNCTION_LINE("Done with restoring given functions!"); -} - -bool isDynamicFunction(uint32_t physicalAddress) { - if ((physicalAddress & 0x80000000) == 0x80000000 && (physicalAddress & 0xFF000000) != 0xFF000000) { +bool PatchFunction(std::shared_ptr &patchedFunction) { + // The addresses of a function might change every time with run another application. + if (!patchedFunction->updateFunctionAddresses()) { return true; } - return false; + + if (patchedFunction->isPatched) { + return true; + } + + if (patchedFunction->functionName) { + DEBUG_FUNCTION_LINE("Patching function %s...", patchedFunction->functionName->c_str()); + } else { + DEBUG_FUNCTION_LINE("Patching function @ %08X", patchedFunction->realEffectiveFunctionAddress); + } + + volatile uint32_t replacedInstruction; + + auto targetAddress = (uint32_t) &replacedInstruction; + if (targetAddress < 0x00800000 || targetAddress >= 0x01000000) { + targetAddress = (uint32_t) OSEffectiveToPhysical(targetAddress); + } else { + targetAddress = targetAddress + 0x30800000 - 0x00800000; + } + + if (targetAddress == 0) { + DEBUG_FUNCTION_LINE_ERR("Failed to get physical address"); + OSFatal("Failed to get physical address"); + return false; + } + + // Save the instruction we will replace. + KernelCopyData(targetAddress, patchedFunction->realPhysicalFunctionAddress, 4); + DCFlushRange((void *) &replacedInstruction, 4); + patchedFunction->replacedInstruction = replacedInstruction; + + // Generate a jump to the original function so the unpatched function can still be called + patchedFunction->generateJumpToOriginal(); + + // Generate a code that is run when somebody calls the patched function. + // If the correct process calls this, it'll jump the function replacement, otherwise the original function will be called. + patchedFunction->generateReplacementJump(); + + // Write this->replaceWithInstruction to the first instruction of the function we want to replace. + CThread::runOnAllCores(writeDataAndFlushIC, patchedFunction.get()); + + // Set patch status + patchedFunction->isPatched = true; + + return true; } -rpl_handling rpl_handles[] __attribute__((section(".data"))) = { - {LIBRARY_AVM, "avm.rpl", nullptr}, - {LIBRARY_CAMERA, "camera.rpl", nullptr}, - {LIBRARY_COREINIT, "coreinit.rpl", nullptr}, - {LIBRARY_DC, "dc.rpl", nullptr}, - {LIBRARY_DMAE, "dmae.rpl", nullptr}, - {LIBRARY_DRMAPP, "drmapp.rpl", nullptr}, - {LIBRARY_ERREULA, "erreula.rpl", nullptr}, - {LIBRARY_GX2, "gx2.rpl", nullptr}, - {LIBRARY_H264, "h264.rpl", nullptr}, - {LIBRARY_LZMA920, "lzma920.rpl", nullptr}, - {LIBRARY_MIC, "mic.rpl", nullptr}, - {LIBRARY_NFC, "nfc.rpl", nullptr}, - {LIBRARY_NIO_PROF, "nio_prof.rpl", nullptr}, - {LIBRARY_NLIBCURL, "nlibcurl.rpl", nullptr}, - {LIBRARY_NLIBNSS, "nlibnss.rpl", nullptr}, - {LIBRARY_NLIBNSS2, "nlibnss2.rpl", nullptr}, - {LIBRARY_NN_AC, "nn_ac.rpl", nullptr}, - {LIBRARY_NN_ACP, "nn_acp.rpl", nullptr}, - {LIBRARY_NN_ACT, "nn_act.rpl", nullptr}, - {LIBRARY_NN_AOC, "nn_aoc.rpl", nullptr}, - {LIBRARY_NN_BOSS, "nn_boss.rpl", nullptr}, - {LIBRARY_NN_CCR, "nn_ccr.rpl", nullptr}, - {LIBRARY_NN_CMPT, "nn_cmpt.rpl", nullptr}, - {LIBRARY_NN_DLP, "nn_dlp.rpl", nullptr}, - {LIBRARY_NN_EC, "nn_ec.rpl", nullptr}, - {LIBRARY_NN_FP, "nn_fp.rpl", nullptr}, - {LIBRARY_NN_HAI, "nn_hai.rpl", nullptr}, - {LIBRARY_NN_HPAD, "nn_hpad.rpl", nullptr}, - {LIBRARY_NN_IDBE, "nn_idbe.rpl", nullptr}, - {LIBRARY_NN_NDM, "nn_ndm.rpl", nullptr}, - {LIBRARY_NN_NETS2, "nn_nets2.rpl", nullptr}, - {LIBRARY_NN_NFP, "nn_nfp.rpl", nullptr}, - {LIBRARY_NN_NIM, "nn_nim.rpl", nullptr}, - {LIBRARY_NN_OLV, "nn_olv.rpl", nullptr}, - {LIBRARY_NN_PDM, "nn_pdm.rpl", nullptr}, - {LIBRARY_NN_SAVE, "nn_save.rpl", nullptr}, - {LIBRARY_NN_SL, "nn_sl.rpl", nullptr}, - {LIBRARY_NN_SPM, "nn_spm.rpl", nullptr}, - {LIBRARY_NN_TEMP, "nn_temp.rpl", nullptr}, - {LIBRARY_NN_UDS, "nn_uds.rpl", nullptr}, - {LIBRARY_NN_VCTL, "nn_vctl.rpl", nullptr}, - {LIBRARY_NSYSCCR, "nsysccr.rpl", nullptr}, - {LIBRARY_NSYSHID, "nsyshid.rpl", nullptr}, - {LIBRARY_NSYSKBD, "nsyskbd.rpl", nullptr}, - {LIBRARY_NSYSNET, "nsysnet.rpl", nullptr}, - {LIBRARY_NSYSUHS, "nsysuhs.rpl", nullptr}, - {LIBRARY_NSYSUVD, "nsysuvd.rpl", nullptr}, - {LIBRARY_NTAG, "ntag.rpl", nullptr}, - {LIBRARY_PADSCORE, "padscore.rpl", nullptr}, - {LIBRARY_PROC_UI, "proc_ui.rpl", nullptr}, - {LIBRARY_SNDCORE2, "sndcore2.rpl", nullptr}, - {LIBRARY_SNDUSER2, "snduser2.rpl", nullptr}, - {LIBRARY_SND_CORE, "snd_core.rpl", nullptr}, - {LIBRARY_SND_USER, "snd_user.rpl", nullptr}, - {LIBRARY_SWKBD, "swkbd.rpl", nullptr}, - {LIBRARY_SYSAPP, "sysapp.rpl", nullptr}, - {LIBRARY_TCL, "tcl.rpl", nullptr}, - {LIBRARY_TVE, "tve.rpl", nullptr}, - {LIBRARY_UAC, "uac.rpl", nullptr}, - {LIBRARY_UAC_RPL, "uac_rpl.rpl", nullptr}, - {LIBRARY_USB_MIC, "usb_mic.rpl", nullptr}, - {LIBRARY_UVC, "uvc.rpl", nullptr}, - {LIBRARY_UVD, "uvd.rpl", nullptr}, - {LIBRARY_VPAD, "vpad.rpl", nullptr}, - {LIBRARY_VPADBASE, "vpadbase.rpl", nullptr}, - {LIBRARY_ZLIB125, "zlib125.rpl", nullptr}}; +bool RestoreFunction(std::shared_ptr &patchedFunction) { + if (!patchedFunction->isPatched) { + DEBUG_FUNCTION_LINE_VERBOSE("Skip restoring function because it's not patched"); + return true; + } + if (patchedFunction->replacedInstruction == 0 || patchedFunction->realEffectiveFunctionAddress == 0) { + DEBUG_FUNCTION_LINE_ERR("Failed to restore function, information is missing."); + return false; + } -uint32_t getAddressOfFunction(char *functionName, function_replacement_library_type_t library) { - uint32_t real_addr = 0; + auto targetAddrPhys = (uint32_t) patchedFunction->realPhysicalFunctionAddress; - OSDynLoad_Module rpl_handle = nullptr; + if (patchedFunction->library != LIBRARY_OTHER) { + targetAddrPhys = (uint32_t) OSEffectiveToPhysical(patchedFunction->realEffectiveFunctionAddress); + } - OSDynLoad_Error err = OS_DYNLOAD_OK; + if (patchedFunction->isDynamicFunction() && + // Other processes than the wii u menu and game one seem to keep their rpl's loaded. + patchedFunction->targetProcess != FP_TARGET_PROCESS_GAME_AND_MENU && + patchedFunction->targetProcess != FP_TARGET_PROCESS_GAME && + patchedFunction->targetProcess != FP_TARGET_PROCESS_WII_U_MENU) { + DEBUG_FUNCTION_LINE_VERBOSE("Its a dynamic function. We don't need to restore it!"); + } else { + DEBUG_FUNCTION_LINE_VERBOSE("Restoring %08X to %08X [%08X]", (uint32_t) patchedFunction->replacedInstruction, patchedFunction->realEffectiveFunctionAddress, targetAddrPhys); + auto sourceAddr = (uint32_t) &patchedFunction->replacedInstruction; - int32_t rpl_handles_size = sizeof rpl_handles / sizeof rpl_handles[0]; + auto sourceAddrPhys = (uint32_t) OSEffectiveToPhysical(sourceAddr); - for (int32_t i = 0; i < rpl_handles_size; i++) { - if (rpl_handles[i].library == library) { - if (rpl_handles[i].handle == nullptr) { - //DEBUG_FUNCTION_LINE("Lets acquire handle for rpl: %s", rpl_handles[i].rplname); - err = OSDynLoad_IsModuleLoaded((char *) rpl_handles[i].rplname, &rpl_handles[i].handle); - } - if (err != OS_DYNLOAD_OK || !rpl_handles[i].handle) { - DEBUG_FUNCTION_LINE_WRITE("%s failed to acquire %d %08X\n", rpl_handles[i].rplname, err, rpl_handles[i].handle); - return 0; - } - rpl_handle = rpl_handles[i].handle; - break; + // These hardcoded values should be replaced with something more dynamic. + if (sourceAddrPhys == 0 && (sourceAddr >= 0x00800000 && sourceAddr < 0x01000000)) { + sourceAddrPhys = sourceAddr + (0x30800000 - 0x00800000); } - } - if (!rpl_handle) { - DEBUG_FUNCTION_LINE("Failed to find the RPL handle for %s", functionName); - return 0; - } - - OSDynLoad_FindExport(rpl_handle, 0, functionName, reinterpret_cast(&real_addr)); - - if (!real_addr) { - DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s", functionName); - return 0; - } - - if ((library == LIBRARY_NN_ACP) && (uint32_t) (*(volatile uint32_t *) (real_addr) &0x48000002) == 0x48000000) { - auto address_diff = (uint32_t) (*(volatile uint32_t *) (real_addr) &0x03FFFFFC); - if ((address_diff & 0x03000000) == 0x03000000) { - address_diff |= 0xFC000000; + if (sourceAddrPhys == 0) { + OSFatal("Failed to get physical address"); } - real_addr += (int32_t) address_diff; - if ((uint32_t) (*(volatile uint32_t *) (real_addr) &0x48000002) == 0x48000000) { - return 0; - } - } - return real_addr; -} - - -void FunctionPatcherResetLibHandles() { - int32_t rpl_handles_size = sizeof rpl_handles / sizeof rpl_handles[0]; - - for (int32_t i = 0; i < rpl_handles_size; i++) { - if (rpl_handles[i].handle != nullptr) { - DEBUG_FUNCTION_LINE_VERBOSE("Resetting handle for rpl: %s", rpl_handles[i].rplname); - } - rpl_handles[i].handle = nullptr; - // Release handle? - } + KernelCopyData(targetAddrPhys, sourceAddrPhys, 4); + ICInvalidateRange((void *) patchedFunction->realEffectiveFunctionAddress, 4); + DCFlushRange((void *) patchedFunction->realEffectiveFunctionAddress, 4); + } + patchedFunction->isPatched = false; + return true; } diff --git a/source/function_patcher.h b/source/function_patcher.h index 2795432..3cede57 100644 --- a/source/function_patcher.h +++ b/source/function_patcher.h @@ -1,29 +1,17 @@ #pragma once +#include "PatchedFunctionData.h" #include #include +#include +#include #ifdef __cplusplus extern "C" { #endif -typedef struct rpl_handling { - function_replacement_library_type_t library; - const char rplname[15]; - OSDynLoad_Module handle; -} rpl_handling; - -void FunctionPatcherRestoreDynamicFunctions(function_replacement_data_t *replacements, uint32_t size); - -void FunctionPatcherPatchFunction(function_replacement_data_t *replacements, uint32_t size); - -void FunctionPatcherRestoreFunctions(function_replacement_data_t *replacements, uint32_t size); - -void FunctionPatcherResetLibHandles(); - -uint32_t getAddressOfFunction(char *functionName, function_replacement_library_type_t type); - -bool isDynamicFunction(uint32_t physicalAddress); +bool PatchFunction(std::shared_ptr &patchedFunction); +bool RestoreFunction(std::shared_ptr &patchedFunction); #ifdef __cplusplus } diff --git a/source/main.cpp b/source/main.cpp index 6df20d5..1eec86d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,21 +1,110 @@ +#include "FunctionAddressProvider.h" +#include "export.h" +#include "function_patcher.h" +#include "utils/globals.h" +#include "utils/logger.h" +#include "utils/utils.h" +#include #include -#include "function_patcher.h" -#include "utils/logger.h" - WUMS_MODULE_EXPORT_NAME("homebrew_functionpatcher"); -WUMS_MODULE_SKIP_INIT_FINI(); +WUMS_MODULE_INIT_BEFORE_RELOCATION_DONE_HOOK(); +WUMS_INITIALIZE() { + // We need the real MEMAllocFromDefaultHeapEx/MEMFreeToDefaultHeap function pointer to force-allocate memory on the default heap. + // Our custom heap doesn't work (yet) for threads and causes an app panic. + OSDynLoad_Module coreinitModule; + if (OSDynLoad_Acquire("coreinit", &coreinitModule) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("Failed to acquire coreinit.rpl"); + OSFatal("Failed to acquire coreinit.rpl"); + } + /* Memory allocation functions */ + uint32_t *allocPtr, *freePtr; + /* Memory allocation functions */ + if (OSDynLoad_FindExport(coreinitModule, true, "MEMAllocFromDefaultHeapEx", reinterpret_cast(&allocPtr)) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx"); + OSFatal("OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx"); + } + if (OSDynLoad_FindExport(coreinitModule, true, "MEMFreeToDefaultHeap", reinterpret_cast(&freePtr)) != OS_DYNLOAD_OK) { + DEBUG_FUNCTION_LINE_ERR("OSDynLoad_FindExport for MEMFreeToDefaultHeap"); + OSFatal("OSDynLoad_FindExport for MEMFreeToDefaultHeap"); + } + + gRealMEMAllocFromDefaultHeapEx = (void *(*) (uint32_t, int) ) * allocPtr; + gMEMFreeToDefaultHeap = (void (*)(void *)) * freePtr; + OSDynLoad_Release(coreinitModule); + + memset(gJumpHeapData, 0, JUMP_HEAP_DATA_SIZE); + gJumpHeapHandle = MEMCreateExpHeapEx((void *) (gJumpHeapData), JUMP_HEAP_DATA_SIZE, 1); + if (gJumpHeapHandle == nullptr) { + DEBUG_FUNCTION_LINE_ERR("Failed to create heap for jump data"); + OSFatal("Failed to create heap for jump data"); + } + + gFunctionAddressProvider = make_shared_nothrow(); + if (!gFunctionAddressProvider) { + DEBUG_FUNCTION_LINE_ERR("Failed to create gFunctionAddressProvider"); + OSFatal("Failed to create gFunctionAddressProvider"); + } +} + +void notify_callback(OSDynLoad_Module module, + void *userContext, + OSDynLoad_NotifyReason reason, + OSDynLoad_NotifyData *infos) { + if (reason == OS_DYNLOAD_NOTIFY_LOADED) { + std::lock_guard lock(gPatchedFunctionsMutex); + for (auto &cur : gPatchedFunctions) { + PatchFunction(cur); + } + } +} WUMS_APPLICATION_STARTS() { + uint32_t upid = OSGetUPID(); + if (upid != 2 && upid != 15) { + return; + } + initLogging(); - FunctionPatcherResetLibHandles(); + + std::lock_guard lock(gPatchedFunctionsMutex); + + DEBUG_FUNCTION_LINE_VERBOSE("Reset patch status"); + // Reset all dynamic functions + for (auto &cur : gPatchedFunctions) { + if (cur->isDynamicFunction()) { + if (cur->functionName) { + DEBUG_FUNCTION_LINE_VERBOSE("%s is dynamic, reset patched status", cur->functionName->c_str()); + } else { + DEBUG_FUNCTION_LINE_VERBOSE("is dynamic, reset patched status"); + } + cur->isPatched = false; + } else { + if (cur->functionName) { + DEBUG_FUNCTION_LINE_VERBOSE("Skip %s for targetProcess %d", cur->functionName->c_str(), cur->targetProcess); + } else { + DEBUG_FUNCTION_LINE_VERBOSE("Skip %08X for targetProcess %d", cur->realEffectiveFunctionAddress, cur->targetProcess); + } + } + } + + OSMemoryBarrier(); + + DEBUG_FUNCTION_LINE_VERBOSE("Patch all functions"); + for (auto &cur : gPatchedFunctions) { + PatchFunction(cur); + } + + OSDynLoad_AddNotifyCallback(notify_callback, nullptr); } WUMS_APPLICATION_REQUESTS_EXIT() { deinitLogging(); } +WUMS_APPLICATION_ENDS() { + gFunctionAddressProvider->resetHandles(); +} WUMS_EXPORT_FUNCTION(FunctionPatcherPatchFunction); -WUMS_EXPORT_FUNCTION(FunctionPatcherRestoreFunctions); -WUMS_EXPORT_FUNCTION(FunctionPatcherRestoreDynamicFunctions); +WUMS_EXPORT_FUNCTION(FunctionPatcherRestoreFunction); diff --git a/source/CThread.h b/source/utils/CThread.h similarity index 89% rename from source/CThread.h rename to source/utils/CThread.h index 44dc64e..6cf1dc6 100644 --- a/source/CThread.h +++ b/source/utils/CThread.h @@ -16,6 +16,8 @@ ****************************************************************************/ #pragma once +#include "globals.h" +#include "logger.h" #include #include #include @@ -26,14 +28,14 @@ public: typedef void (*Callback)(CThread *thread, void *arg); //! constructor - explicit CThread(int32_t iAttr, int32_t iPriority = 16, int32_t iStackSize = 0x8000, CThread::Callback callback = NULL, void *callbackArg = NULL) + explicit CThread(int32_t iAttr, int32_t iPriority = 16, int32_t iStackSize = 0x8000, CThread::Callback callback = nullptr, void *callbackArg = nullptr) : pThread(nullptr), pThreadStack(nullptr), pCallback(callback), pCallbackArg(callbackArg) { //! save attribute assignment iAttributes = iAttr; - //! allocate the thread - pThread = (OSThread *) memalign(0x10, sizeof(OSThread)); - //! allocate the stack - pThreadStack = (uint8_t *) memalign(0x20, iStackSize); + //! allocate the thread on the default Cafe OS heap + pThread = (OSThread *) gRealMEMAllocFromDefaultHeapEx(sizeof(OSThread), 0x10); + //! allocate the stack on the default Cafe OS heap + pThreadStack = (uint8_t *) gRealMEMAllocFromDefaultHeapEx(iStackSize, 0x20); //! create the thread if (pThread && pThreadStack) { // clang-format off @@ -53,7 +55,6 @@ public: static void runOnAllCores(CThread::Callback callback, void *callbackArg, int32_t iAttr = 0, int32_t iPriority = 16, int32_t iStackSize = 0x8000) { int32_t aff[] = {CThread::eAttributeAffCore2, CThread::eAttributeAffCore1, CThread::eAttributeAffCore0}; - for (int i : aff) { CThread thread(iAttr | i, iPriority, iStackSize, callback, callbackArg); thread.resumeThread(); @@ -80,7 +81,9 @@ public: //! Resume thread virtual void resumeThread() { if (!isThreadSuspended()) return; - if (pThread) OSResumeThread(pThread); + if (pThread) { + OSResumeThread(pThread); + } } //! Set thread priority @@ -122,10 +125,10 @@ public: } //! free the thread stack buffer if (pThreadStack) { - free(pThreadStack); + gMEMFreeToDefaultHeap(pThreadStack); } if (pThread) { - free(pThread); + gMEMFreeToDefaultHeap(pThread); } pThread = nullptr; pThreadStack = nullptr; diff --git a/source/utils/globals.cpp b/source/utils/globals.cpp new file mode 100644 index 0000000..409dfc7 --- /dev/null +++ b/source/utils/globals.cpp @@ -0,0 +1,11 @@ +#include "globals.h" + +char gJumpHeapData[JUMP_HEAP_DATA_SIZE] __attribute__((section(".data"))); +MEMHeapHandle gJumpHeapHandle __attribute__((section(".data"))); + +std::shared_ptr gFunctionAddressProvider; +std::mutex gPatchedFunctionsMutex; +std::vector> gPatchedFunctions; + +void *(*gRealMEMAllocFromDefaultHeapEx)(uint32_t size, int align); +void (*gMEMFreeToDefaultHeap)(void *ptr); \ No newline at end of file diff --git a/source/utils/globals.h b/source/utils/globals.h new file mode 100644 index 0000000..6642b18 --- /dev/null +++ b/source/utils/globals.h @@ -0,0 +1,16 @@ +#pragma once +#include "../PatchedFunctionData.h" +#include +#include +#include + +#define JUMP_HEAP_DATA_SIZE (32 * 1024) +extern char gJumpHeapData[]; +extern MEMHeapHandle gJumpHeapHandle; + +extern std::shared_ptr gFunctionAddressProvider; +extern std::mutex gPatchedFunctionsMutex; +extern std::vector> gPatchedFunctions; + +extern void *(*gRealMEMAllocFromDefaultHeapEx)(uint32_t size, int align); +extern void (*gMEMFreeToDefaultHeap)(void *ptr); \ No newline at end of file diff --git a/source/utils/utils.h b/source/utils/utils.h new file mode 100644 index 0000000..e945dd0 --- /dev/null +++ b/source/utils/utils.h @@ -0,0 +1,12 @@ +#pragma once +#include + +template +std::unique_ptr make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward(args)...))) { + return std::unique_ptr(new (std::nothrow) T(std::forward(args)...)); +} + +template +std::shared_ptr make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::forward(args)...))) { + return std::shared_ptr(new (std::nothrow) T(std::forward(args)...)); +}