#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 WUMS_MODULE_EXPORT_NAME("homebrew_functionpatcher"); WUMS_MODULE_INIT_BEFORE_RELOCATION_DONE_HOOK(); void UpdateFunctionPointer() { // 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"); } gMEMAllocFromDefaultHeapExForThreads = (void *(*) (uint32_t, int) ) * allocPtr; gMEMFreeToDefaultHeapForThreads = (void (*)(void *)) * freePtr; OSDynLoad_Release(coreinitModule); } WUMS_INITIALIZE() { UpdateFunctionPointer(); 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; } // Now we can update the pointer with the "real" functions gMEMAllocFromDefaultHeapExForThreads = MEMAllocFromDefaultHeapEx; gMEMFreeToDefaultHeapForThreads = MEMFreeToDefaultHeap; initLogging(); 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(FunctionPatcherRestoreFunction);