Rewrite of Function Patcher to match libfunctionpatcher

This commit is contained in:
Maschell 2022-05-08 18:51:05 +02:00
parent a6eb4eeb88
commit f95f00e6f2
14 changed files with 741 additions and 392 deletions

View File

@ -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

View File

@ -0,0 +1,65 @@
#include "FunctionAddressProvider.h"
#include "utils/logger.h"
#include <coreinit/dynload.h>
#include <function_patcher/fpatching_defines.h>
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<void **>(&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;
}
}

View File

@ -0,0 +1,85 @@
#pragma once
#include <coreinit/dynload.h>
#include <cstdint>
#include <function_patcher/fpatching_defines.h>
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}};
};

View File

@ -0,0 +1,194 @@
#include "PatchedFunctionData.h"
#include "utils/utils.h"
std::optional<std::shared_ptr<PatchedFunctionData>> PatchedFunctionData::make_shared(std::shared_ptr<FunctionAddressProvider> functionAddressProvider,
function_replacement_data_t *replacementData,
MEMHeapHandle heapHandle) {
if (!replacementData) {
return {};
}
auto ptr = make_shared_nothrow<PatchedFunctionData>(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;
}
}

View File

@ -0,0 +1,62 @@
#pragma once
#include "FunctionAddressProvider.h"
#include "PatchedFunctionData.h"
#include "utils/logger.h"
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/memexpheap.h>
#include <coreinit/memorymap.h>
#include <cstdint>
#include <function_patcher/fpatching_defines.h>
#include <memory>
#include <optional>
#include <string>
#include <utility>
class PatchedFunctionData {
public:
~PatchedFunctionData();
explicit PatchedFunctionData(std::shared_ptr<FunctionAddressProvider> functionAddressProvider) : functionAddressProvider(std::move(functionAddressProvider)) {
}
static std::optional<std::shared_ptr<PatchedFunctionData>> make_shared(std::shared_ptr<FunctionAddressProvider> 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<std::string> functionName = {};
std::shared_ptr<FunctionAddressProvider> functionAddressProvider;
};

83
source/export.cpp Normal file
View File

@ -0,0 +1,83 @@
#include "export.h"
#include "PatchedFunctionData.h"
#include "function_patcher.h"
#include "utils/globals.h"
#include <ranges>
#include <vector>
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<std::mutex> 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<std::mutex> lock(gPatchedFunctionsMutex);
std::vector<std::shared_ptr<PatchedFunctionData>> toBeTempRestored;
bool found = false;
int32_t erasePosition = 0;
std::shared_ptr<PatchedFunctionData> 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;
}

6
source/export.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <function_patcher/fpatching_defines.h>
bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle);
bool FunctionPatcherRestoreFunction(PatchedFunctionHandle handle);

View File

@ -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 <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/dynload.h>
#include <coreinit/memorymap.h>
#include <kernel/kernel.h>
#include <memory>
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<PatchedFunctionData> &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<PatchedFunctionData> &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<void **>(&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;
}

View File

@ -1,29 +1,17 @@
#pragma once
#include "PatchedFunctionData.h"
#include <coreinit/dynload.h>
#include <function_patcher/fpatching_defines.h>
#include <memory>
#include <vector>
#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<PatchedFunctionData> &patchedFunction);
bool RestoreFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction);
#ifdef __cplusplus
}

View File

@ -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 <coreinit/memexpheap.h>
#include <wums.h>
#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<void **>(&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<void **>(&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<FunctionAddressProvider>();
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<std::mutex> 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<std::mutex> 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);

View File

@ -16,6 +16,8 @@
****************************************************************************/
#pragma once
#include "globals.h"
#include "logger.h"
#include <coreinit/thread.h>
#include <cstdint>
#include <malloc.h>
@ -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;

11
source/utils/globals.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "globals.h"
char gJumpHeapData[JUMP_HEAP_DATA_SIZE] __attribute__((section(".data")));
MEMHeapHandle gJumpHeapHandle __attribute__((section(".data")));
std::shared_ptr<FunctionAddressProvider> gFunctionAddressProvider;
std::mutex gPatchedFunctionsMutex;
std::vector<std::shared_ptr<PatchedFunctionData>> gPatchedFunctions;
void *(*gRealMEMAllocFromDefaultHeapEx)(uint32_t size, int align);
void (*gMEMFreeToDefaultHeap)(void *ptr);

16
source/utils/globals.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "../PatchedFunctionData.h"
#include <coreinit/memheap.h>
#include <memory>
#include <vector>
#define JUMP_HEAP_DATA_SIZE (32 * 1024)
extern char gJumpHeapData[];
extern MEMHeapHandle gJumpHeapHandle;
extern std::shared_ptr<FunctionAddressProvider> gFunctionAddressProvider;
extern std::mutex gPatchedFunctionsMutex;
extern std::vector<std::shared_ptr<PatchedFunctionData>> gPatchedFunctions;
extern void *(*gRealMEMAllocFromDefaultHeapEx)(uint32_t size, int align);
extern void (*gMEMFreeToDefaultHeap)(void *ptr);

12
source/utils/utils.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <memory>
template<class T, class... Args>
std::unique_ptr<T> make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
}
template<class T, class... Args>
std::shared_ptr<T> make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
}