commit edcf2c69b1471a111b990e3ceaf9127c2985ebb8 Author: Maschell Date: Sat Jun 6 22:15:47 2020 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba57c28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.cbp +*.elf +*.layout +*.rpx +build/ +*.save-failed +.idea/ +cmake-build-debug/ +CMakeLists.txt +*.wms diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3952bda --- /dev/null +++ b/Makefile @@ -0,0 +1,142 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wums/share/wums_rules + +WUMS_ROOT := $(DEVKITPRO)/wums +WUT_ROOT := $(DEVKITPRO)/wut +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := FunctionPatcherModule +BUILD := build +SOURCES := source +DATA := data +INCLUDES := source + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -g -Wall -O0 -ffunction-sections\ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ + +CXXFLAGS := $(CFLAGS) -std=c++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(WUMSSPECS) -Wl,-Map,$(notdir $*.map) + +LIBS := -lwums -lwut -lkernel + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) + + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wms + +$(OUTPUT).wms : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o: %.s + @echo $(notdir $<) + @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/source/CThread.h b/source/CThread.h new file mode 100644 index 0000000..0d4822a --- /dev/null +++ b/source/CThread.h @@ -0,0 +1,154 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#pragma once + +#include +#include +#include +#include + +class CThread { +public: + typedef void (*Callback)(CThread *thread, void *arg); + + //! constructor + CThread(int32_t iAttr, int32_t iPriority = 16, int32_t iStackSize = 0x8000, CThread::Callback callback = NULL, void *callbackArg = NULL) + : pThread(NULL), pThreadStack(NULL), 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); + //! create the thread + if (pThread && pThreadStack) { + OSCreateThread(pThread, (int (*)(int, const char **)) &CThread::threadCallback, 1, (char *) this, (void *) (pThreadStack + iStackSize), iStackSize, iPriority, iAttributes); + } + } + + //! destructor + virtual ~CThread() { + shutdownThread(); + } + + static CThread *create(CThread::Callback callback, void *callbackArg, int32_t iAttr = eAttributeNone, int32_t iPriority = 16, int32_t iStackSize = 0x8000) { + return (new CThread(iAttr, iPriority, iStackSize, callback, callbackArg)); + } + + 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 (uint32_t i = 0; i < (sizeof(aff) / sizeof(aff[0])); i++) { + CThread *thread = CThread::create(callback, callbackArg, iAttr | aff[i], iPriority, iStackSize); + thread->resumeThread(); + delete thread; + } + } + + //! Get thread ID + virtual void *getThread() const { + return pThread; + } + + //! Thread entry function + virtual void executeThread(void) { + if (pCallback) + pCallback(this, pCallbackArg); + } + + //! Suspend thread + virtual void suspendThread(void) { + if (isThreadSuspended()) return; + if (pThread) OSSuspendThread(pThread); + } + + //! Resume thread + virtual void resumeThread(void) { + if (!isThreadSuspended()) return; + if (pThread) OSResumeThread(pThread); + } + + //! Set thread priority + virtual void setThreadPriority(int32_t prio) { + if (pThread) OSSetThreadPriority(pThread, prio); + } + + //! Check if thread is suspended + virtual bool isThreadSuspended(void) const { + if (pThread) return OSIsThreadSuspended(pThread); + return false; + } + + //! Check if thread is terminated + virtual bool isThreadTerminated(void) const { + if (pThread) return OSIsThreadTerminated(pThread); + return false; + } + + //! Check if thread is running + virtual bool isThreadRunning(void) const { + return !isThreadSuspended() && !isThreadRunning(); + } + + //! Gets the thread affinity. + virtual uint16_t getThreadAffinity(void) const { + if (pThread) return OSGetThreadAffinity(pThread); + return 0; + } + + //! Shutdown thread + virtual void shutdownThread(void) { + //! wait for thread to finish + if (pThread && !(iAttributes & eAttributeDetach)) { + while (isThreadSuspended()) { + resumeThread(); + } + OSJoinThread(pThread, NULL); + } + //! free the thread stack buffer + if (pThreadStack) { + free(pThreadStack); + } + if (pThread) { + free(pThread); + } + pThread = NULL; + pThreadStack = NULL; + } + + //! Thread attributes + enum eCThreadAttributes { + eAttributeNone = 0x07, + eAttributeAffCore0 = 0x01, + eAttributeAffCore1 = 0x02, + eAttributeAffCore2 = 0x04, + eAttributeDetach = 0x08, + eAttributePinnedAff = 0x10 + }; +private: + static int32_t threadCallback(int32_t argc, void *arg) { + //! After call to start() continue with the internal function + ((CThread *) arg)->executeThread(); + return 0; + } + + int32_t iAttributes; + OSThread *pThread; + uint8_t *pThreadStack; + Callback pCallback; + void *pCallbackArg; +}; \ No newline at end of file diff --git a/source/defines.h b/source/defines.h new file mode 100644 index 0000000..6809e14 --- /dev/null +++ b/source/defines.h @@ -0,0 +1,125 @@ +#pragma once + +typedef enum function_replacement_library_type_t { + LIBRARY_AVM, + LIBRARY_CAMERA, + LIBRARY_COREINIT, + LIBRARY_DC, + LIBRARY_DMAE, + LIBRARY_DRMAPP, + LIBRARY_ERREULA, + LIBRARY_GX2, + LIBRARY_H264, + LIBRARY_LZMA920, + LIBRARY_MIC, + LIBRARY_NFC, + LIBRARY_NIO_PROF, + LIBRARY_NLIBCURL, + LIBRARY_NLIBNSS, + LIBRARY_NLIBNSS2, + LIBRARY_NN_AC, + LIBRARY_NN_ACP, + LIBRARY_NN_ACT, + LIBRARY_NN_AOC, + LIBRARY_NN_BOSS, + LIBRARY_NN_CCR, + LIBRARY_NN_CMPT, + LIBRARY_NN_DLP, + LIBRARY_NN_EC, + LIBRARY_NN_FP, + LIBRARY_NN_HAI, + LIBRARY_NN_HPAD, + LIBRARY_NN_IDBE, + LIBRARY_NN_NDM, + LIBRARY_NN_NETS2, + LIBRARY_NN_NFP, + LIBRARY_NN_NIM, + LIBRARY_NN_OLV, + LIBRARY_NN_PDM, + LIBRARY_NN_SAVE, + LIBRARY_NN_SL, + LIBRARY_NN_SPM, + LIBRARY_NN_TEMP, + LIBRARY_NN_UDS, + LIBRARY_NN_VCTL, + LIBRARY_NSYSCCR, + LIBRARY_NSYSHID, + LIBRARY_NSYSKBD, + LIBRARY_NSYSNET, + LIBRARY_NSYSUHS, + LIBRARY_NSYSUVD, + LIBRARY_NTAG, + LIBRARY_PADSCORE, + LIBRARY_PROC_UI, + LIBRARY_SND_CORE, + LIBRARY_SND_USER, + LIBRARY_SNDCORE2, + LIBRARY_SNDUSER2, + LIBRARY_SWKBD, + LIBRARY_SYSAPP, + LIBRARY_TCL, + LIBRARY_TVE, + LIBRARY_UAC, + LIBRARY_UAC_RPL, + LIBRARY_USB_MIC, + LIBRARY_UVC, + LIBRARY_UVD, + LIBRARY_VPAD, + LIBRARY_VPADBASE, + LIBRARY_ZLIB125, + LIBRARY_OTHER, +} function_replacement_library_type_t; + +#define MAXIMUM_FUNCTION_NAME_LENGTH 100 +#define FUNCTION_PATCHER_METHOD_STORE_SIZE 20 + +#define STATIC_FUNCTION 0 +#define DYNAMIC_FUNCTION 1 + +typedef struct function_replacement_data_t { + uint32_t physicalAddr; /* [needs to be filled] */ + uint32_t virtualAddr; /* [needs to be filled] */ + uint32_t replaceAddr; /* [needs to be filled] Address of our replacement function */ + uint32_t replaceCall; /* [needs to be filled] Address to access the real_function */ + function_replacement_library_type_t library; /* [needs to be filled] rpl where the function we want to replace is. */ + char function_name[MAXIMUM_FUNCTION_NAME_LENGTH]; /* [needs to be filled] name of the function we want to replace */ + uint32_t realAddr; /* [will be filled] Address of the real function we want to replace. */ + volatile uint32_t replace_data [FUNCTION_PATCHER_METHOD_STORE_SIZE]; /* [will be filled] Space for us to store some jump instructions */ + uint32_t restoreInstruction; /* [will be filled] Copy of the instruction we replaced to jump to our code. */ + uint8_t functionType; /* [will be filled] */ + uint8_t alreadyPatched; /* [will be filled] */ +} function_replacement_data_t; + +#define REPLACE_FUNCTION(x, lib, function_name) \ + { \ + 0, \ + 0, \ + (uint32_t) my_ ## x, \ + (uint32_t) &real_ ## x \ + lib, \ + # function_name, \ + 0, \ + {}, \ + 0, \ + functionType, \ + 0 \ + } + +#define REPLACE_FUNCTION_VIA_ADDRESS(x, physicalAddress, effectiveAddress) \ + { \ + physicalAddress, \ + effectiveAddress, \ + (uint32_t) my_ ## x, \ + (uint32_t) &real_ ## x \ + LIBRARY_OTHER, \ + # x, \ + 0, \ + {}, \ + 0, \ + functionType, \ + 0 \ + } + +#define DECL_FUNCTION(res, name, ...) \ + res (* real_ ## name)(__VA_ARGS__) __attribute__((section(".data"))); \ + res my_ ## name(__VA_ARGS__) \ No newline at end of file diff --git a/source/function_patcher.cpp b/source/function_patcher.cpp new file mode 100644 index 0000000..aee1063 --- /dev/null +++ b/source/function_patcher.cpp @@ -0,0 +1,336 @@ + +#include +#include +#include +#include +#include +#include "defines.h" +#include "function_patcher.h" +#include "logger.h" +#include "CThread.h" + +#define DEBUG_LOG_DYN 1 + +void writeDataAndFlushIC(CThread *thread, void *arg) { + uint32_t *data = (uint32_t *) arg; + uint16_t core = OSGetThreadAffinity(OSGetCurrentThread()); + + DCFlushRange(data, sizeof(uint32_t) * 3); + + uint32_t replace_instruction = data[0]; + uint32_t physical_address = data[1]; + uint32_t effective_address = data[2]; + DCFlushRange(&replace_instruction, 4); + DCFlushRange(&physical_address, 4); + + DEBUG_FUNCTION_LINE("Write instruction %08X to %08X [%08X] on core %d", replace_instruction, effective_address, physical_address, core / 2); + + uint32_t replace_instruction_physical = (uint32_t) &replace_instruction; + + if (replace_instruction_physical < 0x00800000 || replace_instruction_physical >= 0x01000000) { + replace_instruction_physical = OSEffectiveToPhysical(replace_instruction_physical); + } else { + replace_instruction_physical = replace_instruction_physical + 0x30800000 - 0x00800000; + } + + KernelCopyData(physical_address, replace_instruction_physical, 4); + ICInvalidateRange((void *) (effective_address), 4); +} + + +void FunctionPatcherPatchFunction(function_replacement_data_t *replacements, uint32_t size) { + uint32_t skip_instr = 1; + uint32_t my_instr_len = 4; + uint32_t instr_len = my_instr_len + skip_instr + 15; + uint32_t flush_len = 4 * instr_len; + + for (uint32_t i = 0; i < size; i++) { + function_replacement_data_t *function_data = &replacements[i]; + /* Patch branches to it. */ + volatile uint32_t *space = function_data->replace_data; + + DEBUG_FUNCTION_LINE_WRITE("Patching %s ...", function_data->function_name); + + if (function_data->library == LIBRARY_OTHER) { + WHBLogWritef("Oh, using straight PA/VA"); + if (function_data->alreadyPatched == 1) { + DEBUG_FUNCTION_LINE("Skipping %s, its already patched", function_data->function_name); + continue; + } + } else { + if (function_data->functionType == STATIC_FUNCTION && function_data->alreadyPatched == 1) { + if (isDynamicFunction((uint32_t) OSEffectiveToPhysical(function_data->realAddr))) { + DEBUG_FUNCTION_LINE("INFO: The function %s is a dynamic function.", function_data->function_name); + function_data->functionType = DYNAMIC_FUNCTION; + } else { + WHBLogWritef("Skipping %s, its already patched", function_data->function_name); + continue; + } + } + } + + uint32_t physical = function_data->physicalAddr; + uint32_t repl_addr = (uint32_t) function_data->replaceAddr; + uint32_t 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) { + WHBLogWritef(""); + DEBUG_FUNCTION_LINE("OSDynLoad_FindExport failed for %s", function_data->function_name); + continue; + } + + if (DEBUG_LOG_DYN) { + DEBUG_FUNCTION_LINE("%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) { + WHBLogWritef("Error. Something is wrong with the physical address"); + continue; + } + + if (DEBUG_LOG_DYN) { + DEBUG_FUNCTION_LINE("%s physical is located at %08X!", function_data->function_name, physical); + } + + *(volatile uint32_t *) (call_addr) = (uint32_t) (space); + + uint32_t 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++; + + //Only works if skip_instr == 1 + if (skip_instr == 1) { + // fill the restore instruction section + function_data->realAddr = real_addr; + function_data->restoreInstruction = space[-1]; + if (DEBUG_LOG_DYN) { + DEBUG_FUNCTION_LINE("function_data->realAddr = %08X!", function_data->realAddr); + } + if (DEBUG_LOG_DYN) { + DEBUG_FUNCTION_LINE("function_data->restoreInstruction = %08X!", function_data->restoreInstruction); + } + } else { + WHBLogWritef("Error. Can't save %s for restoring!", function_data->function_name); + } + + /* + 00808cfc 3d601234 lis r11 ,0x1234 + 00808d00 616b5678 ori r11 ,r11 ,0x5678 + 00808d04 7d6903a6 mtspr CTR ,r11 + 00808d08 4e800420 bctr + */ + + *space = 0x3d600000 | (((real_addr + (skip_instr * 4)) >> 16) & 0x0000FFFF); + space++; // lis r11 ,0x1234 + *space = 0x616b0000 | ((real_addr + (skip_instr * 4)) & 0x0000ffff); + space++; // ori r11 ,r11 ,0x5678 + *space = 0x7d6903a6; + space++; // mtspr CTR ,r11 + *space = 0x4e800420; + space++; + + + uint32_t repl_addr_test = (uint32_t) space; + /* + // Only use patched function if OSGetUPID is 2 (wii u menu) or 15 (game) + *space = 0x3d600000 | (((uint32_t*) OSGetUPID)[0] & 0x0000FFFF); space++; // lis r11 ,0x0 + *space = 0x816b0000 | (((uint32_t*) OSGetUPID)[1] & 0x0000FFFF); space++; // lwz r11 ,0x0(r11) + *space = 0x2c0b0000 | 0x00000002; space++; // cmpwi r11 ,0x2 + *space = 0x41820000 | 0x00000020; space++; // beq myfunc + *space = 0x2c0b0000 | 0x0000000F; space++; // cmpwi r11 ,0xF + *space = 0x41820000 | 0x00000018; space++; // beq myfunc + *space = 0x3d600000 | (((real_addr + (skip_instr * 4)) >> 16) & 0x0000FFFF); space++; // lis r11 ,0x1234 + *space = 0x616b0000 | ((real_addr + (skip_instr * 4)) & 0x0000ffff); space++; // ori r11 ,r11 ,0x5678 + *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 ,0x1234 + *space = 0x616b0000 | ((repl_addr) & 0x0000ffff); + space++; // ori r11 ,r11 ,0x5678 + *space = 0x7d6903a6; + space++; // mtspr CTR ,r11 + *space = 0x4e800420; + space++; // bctr + + DCFlushRange((void *) (((uint32_t) space) - flush_len), flush_len); + ICInvalidateRange((void *) (((uint32_t) space) - flush_len), flush_len); + + if ((repl_addr_test & 0x03fffffc) != repl_addr_test) { + OSFatal("Jump is impossible"); + } + + //setting jump back + uint32_t replace_instr = 0x48000002 | (repl_addr_test & 0x03fffffc); + + uint32_t data[] = { + replace_instr, + physical, + real_addr + }; + CThread::runOnAllCores(writeDataAndFlushIC, data); + + function_data->alreadyPatched = 1; + DEBUG_FUNCTION_LINE("done with patching %s!", function_data->function_name); + + } + DEBUG_FUNCTION_LINE("Done with patching given functions!"); + +} + +bool isDynamicFunction(uint32_t physicalAddress) { + if ((physicalAddress & 0x80000000) == 0x80000000) { + return 1; + } + return 0; +} + +rpl_handling rpl_handles[] __attribute__((section(".data"))) = { + {LIBRARY_AVM, "avm.rpl", 0}, + {LIBRARY_CAMERA, "camera.rpl", 0}, + {LIBRARY_COREINIT, "coreinit.rpl", 0}, + {LIBRARY_DC, "dc.rpl", 0}, + {LIBRARY_DMAE, "dmae.rpl", 0}, + {LIBRARY_DRMAPP, "drmapp.rpl", 0}, + {LIBRARY_ERREULA, "erreula.rpl", 0}, + {LIBRARY_GX2, "gx2.rpl", 0}, + {LIBRARY_H264, "h264.rpl", 0}, + {LIBRARY_LZMA920, "lzma920.rpl", 0}, + {LIBRARY_MIC, "mic.rpl", 0}, + {LIBRARY_NFC, "nfc.rpl", 0}, + {LIBRARY_NIO_PROF, "nio_prof.rpl", 0}, + {LIBRARY_NLIBCURL, "nlibcurl.rpl", 0}, + {LIBRARY_NLIBNSS, "nlibnss.rpl", 0}, + {LIBRARY_NLIBNSS2, "nlibnss2.rpl", 0}, + {LIBRARY_NN_AC, "nn_ac.rpl", 0}, + {LIBRARY_NN_ACP, "nn_acp.rpl", 0}, + {LIBRARY_NN_ACT, "nn_act.rpl", 0}, + {LIBRARY_NN_AOC, "nn_aoc.rpl", 0}, + {LIBRARY_NN_BOSS, "nn_boss.rpl", 0}, + {LIBRARY_NN_CCR, "nn_ccr.rpl", 0}, + {LIBRARY_NN_CMPT, "nn_cmpt.rpl", 0}, + {LIBRARY_NN_DLP, "nn_dlp.rpl", 0}, + {LIBRARY_NN_EC, "nn_ec.rpl", 0}, + {LIBRARY_NN_FP, "nn_fp.rpl", 0}, + {LIBRARY_NN_HAI, "nn_hai.rpl", 0}, + {LIBRARY_NN_HPAD, "nn_hpad.rpl", 0}, + {LIBRARY_NN_IDBE, "nn_idbe.rpl", 0}, + {LIBRARY_NN_NDM, "nn_ndm.rpl", 0}, + {LIBRARY_NN_NETS2, "nn_nets2.rpl", 0}, + {LIBRARY_NN_NFP, "nn_nfp.rpl", 0}, + {LIBRARY_NN_NIM, "nn_nim.rpl", 0}, + {LIBRARY_NN_OLV, "nn_olv.rpl", 0}, + {LIBRARY_NN_PDM, "nn_pdm.rpl", 0}, + {LIBRARY_NN_SAVE, "nn_save.rpl", 0}, + {LIBRARY_NN_SL, "nn_sl.rpl", 0}, + {LIBRARY_NN_SPM, "nn_spm.rpl", 0}, + {LIBRARY_NN_TEMP, "nn_temp.rpl", 0}, + {LIBRARY_NN_UDS, "nn_uds.rpl", 0}, + {LIBRARY_NN_VCTL, "nn_vctl.rpl", 0}, + {LIBRARY_NSYSCCR, "nsysccr.rpl", 0}, + {LIBRARY_NSYSHID, "nsyshid.rpl", 0}, + {LIBRARY_NSYSKBD, "nsyskbd.rpl", 0}, + {LIBRARY_NSYSNET, "nsysnet.rpl", 0}, + {LIBRARY_NSYSUHS, "nsysuhs.rpl", 0}, + {LIBRARY_NSYSUVD, "nsysuvd.rpl", 0}, + {LIBRARY_NTAG, "ntag.rpl", 0}, + {LIBRARY_PADSCORE, "padscore.rpl", 0}, + {LIBRARY_PROC_UI, "proc_ui.rpl", 0}, + {LIBRARY_SNDCORE2, "sndcore2.rpl", 0}, + {LIBRARY_SNDUSER2, "snduser2.rpl", 0}, + {LIBRARY_SND_CORE, "snd_core.rpl", 0}, + {LIBRARY_SND_USER, "snd_user.rpl", 0}, + {LIBRARY_SWKBD, "swkbd.rpl", 0}, + {LIBRARY_SYSAPP, "sysapp.rpl", 0}, + {LIBRARY_TCL, "tcl.rpl", 0}, + {LIBRARY_TVE, "tve.rpl", 0}, + {LIBRARY_UAC, "uac.rpl", 0}, + {LIBRARY_UAC_RPL, "uac_rpl.rpl", 0}, + {LIBRARY_USB_MIC, "usb_mic.rpl", 0}, + {LIBRARY_UVC, "uvc.rpl", 0}, + {LIBRARY_UVD, "uvd.rpl", 0}, + {LIBRARY_VPAD, "vpad.rpl", 0}, + {LIBRARY_VPADBASE, "vpadbase.rpl", 0}, + {LIBRARY_ZLIB125, "zlib125.rpl", 0} +}; + +uint32_t getAddressOfFunction(char *functionName, function_replacement_library_type_t library) { + uint32_t real_addr = 0; + + OSDynLoad_Module rpl_handle = 0; + + 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 == 0) { + DEBUG_FUNCTION_LINE("Lets acquire handle for rpl: %s", rpl_handles[i].rplname); + OSDynLoad_Acquire((char *) rpl_handles[i].rplname, &rpl_handles[i].handle); + } + if (rpl_handles[i].handle == 0) { + WHBLogWritef("%s failed to acquire", rpl_handles[i].rplname); + return 0; + } + rpl_handle = rpl_handles[i].handle; + break; + } + } + + 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) { + uint32_t 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) { + 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 != 0) { + DEBUG_FUNCTION_LINE("Resetting handle for rpl: %s", rpl_handles[i].rplname); + } + rpl_handles[i].handle = 0; + // Release handle? + } +} diff --git a/source/function_patcher.h b/source/function_patcher.h new file mode 100644 index 0000000..41b823c --- /dev/null +++ b/source/function_patcher.h @@ -0,0 +1,26 @@ +#pragma once +#include "defines.h" +#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 FunctionPatcherPatchFunction(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); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/source/logger.h b/source/logger.h new file mode 100644 index 0000000..ceb3111 --- /dev/null +++ b/source/logger.h @@ -0,0 +1,30 @@ +#ifndef __LOGGER_H_ +#define __LOGGER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \ + OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \ + WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0); + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \ + WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \ + } while (0); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..739ddcc --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,14 @@ +#include +#include +#include "function_patcher.h" +WUMS_MODULE_EXPORT_NAME("homebrew_functionpatcher"); + +WUMS_INITIALIZE(){ + WHBLogUdpInit(); +} + +WUMS_APPLICATION_STARTS() { + FunctionPatcherResetLibHandles(); +} + +WUMS_EXPORT_FUNCTION(FunctionPatcherPatchFunction);