mirror of
https://github.com/wiiu-env/FunctionPatcherModule.git
synced 2024-11-17 15:29:23 +01:00
initial commit
This commit is contained in:
commit
edcf2c69b1
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
*.cbp
|
||||||
|
*.elf
|
||||||
|
*.layout
|
||||||
|
*.rpx
|
||||||
|
build/
|
||||||
|
*.save-failed
|
||||||
|
.idea/
|
||||||
|
cmake-build-debug/
|
||||||
|
CMakeLists.txt
|
||||||
|
*.wms
|
142
Makefile
Normal file
142
Makefile
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/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
|
||||||
|
#-------------------------------------------------------------------------------
|
154
source/CThread.h
Normal file
154
source/CThread.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
****************************************************************************/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <coreinit/thread.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
125
source/defines.h
Normal file
125
source/defines.h
Normal file
@ -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__)
|
336
source/function_patcher.cpp
Normal file
336
source/function_patcher.cpp
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
|
||||||
|
#include <coreinit/dynload.h>
|
||||||
|
#include <coreinit/cache.h>
|
||||||
|
#include <coreinit/debug.h>
|
||||||
|
#include <coreinit/memorymap.h>
|
||||||
|
#include <kernel/kernel.h>
|
||||||
|
#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<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) {
|
||||||
|
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?
|
||||||
|
}
|
||||||
|
}
|
26
source/function_patcher.h
Normal file
26
source/function_patcher.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "defines.h"
|
||||||
|
#include <coreinit/dynload.h>
|
||||||
|
|
||||||
|
#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
|
30
source/logger.h
Normal file
30
source/logger.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef __LOGGER_H_
|
||||||
|
#define __LOGGER_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <whb/log.h>
|
||||||
|
|
||||||
|
#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
|
14
source/main.cpp
Normal file
14
source/main.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include <wums.h>
|
||||||
|
#include <whb/log_udp.h>
|
||||||
|
#include "function_patcher.h"
|
||||||
|
WUMS_MODULE_EXPORT_NAME("homebrew_functionpatcher");
|
||||||
|
|
||||||
|
WUMS_INITIALIZE(){
|
||||||
|
WHBLogUdpInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
WUMS_APPLICATION_STARTS() {
|
||||||
|
FunctionPatcherResetLibHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
WUMS_EXPORT_FUNCTION(FunctionPatcherPatchFunction);
|
Loading…
Reference in New Issue
Block a user