mirror of
https://github.com/wiiu-env/FunctionPatcherModule.git
synced 2026-03-23 10:34:13 -05:00
Compare commits
No commits in common. "main" and "FunctionPatcherModule-20220904-161521" have entirely different histories.
main
...
FunctionPa
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
|
|
@ -1,10 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "docker"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
38
.github/workflows/ci.yml
vendored
38
.github/workflows/ci.yml
vendored
|
|
@ -3,28 +3,21 @@ name: CI-Release
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
clang-format:
|
clang-format:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: clang-format
|
- name: clang-format
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source
|
||||||
build-binary:
|
build-binary:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format
|
needs: clang-format
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: create version.h
|
|
||||||
run: |
|
|
||||||
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
|
||||||
cat <<EOF > ./source/version.h
|
|
||||||
#pragma once
|
|
||||||
#define MODULE_VERSION_EXTRA " (nightly-$git_hash)"
|
|
||||||
EOF
|
|
||||||
- name: build binary
|
- name: build binary
|
||||||
run: |
|
run: |
|
||||||
docker build . -t builder
|
docker build . -t builder
|
||||||
|
|
@ -48,12 +41,25 @@ jobs:
|
||||||
- name: zip artifact
|
- name: zip artifact
|
||||||
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms
|
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: "softprops/action-gh-release@v2"
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||||
|
release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: true
|
prerelease: true
|
||||||
generate_release_notes: true
|
body: |
|
||||||
name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
Not a stable release:
|
||||||
files: |
|
${{ github.event.head_commit.message }}
|
||||||
./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
- name: Upload Release Asset
|
||||||
|
id: upload-release-asset
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||||
|
asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
||||||
|
asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
15
.github/workflows/pr.yml
vendored
15
.github/workflows/pr.yml
vendored
|
|
@ -6,15 +6,15 @@ jobs:
|
||||||
clang-format:
|
clang-format:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: clang-format
|
- name: clang-format
|
||||||
run: |
|
run: |
|
||||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source
|
||||||
check-build-with-logging:
|
check-build-with-logging:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format
|
needs: clang-format
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: build binary with logging
|
- name: build binary with logging
|
||||||
run: |
|
run: |
|
||||||
docker build . -t builder
|
docker build . -t builder
|
||||||
|
|
@ -25,14 +25,7 @@ jobs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: clang-format
|
needs: clang-format
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- name: create version.h
|
|
||||||
run: |
|
|
||||||
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")
|
|
||||||
cat <<EOF > ./source/version.h
|
|
||||||
#pragma once
|
|
||||||
#define MODULE_VERSION_EXTRA " (nightly-$git_hash)"
|
|
||||||
EOF
|
|
||||||
- name: build binary
|
- name: build binary
|
||||||
run: |
|
run: |
|
||||||
docker build . -t builder
|
docker build . -t builder
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,4 +8,3 @@ build/
|
||||||
cmake-build-debug/
|
cmake-build-debug/
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
*.wms
|
*.wms
|
||||||
*.zip
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
FROM ghcr.io/wiiu-env/devkitppc:20240505
|
FROM wiiuenv/devkitppc:20220806
|
||||||
|
|
||||||
COPY --from=ghcr.io/wiiu-env/libkernel:20230621 /artifacts $DEVKITPRO
|
COPY --from=wiiuenv/libkernel:20220904 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20230621 /artifacts $DEVKITPRO
|
COPY --from=wiiuenv/libfunctionpatcher:20220904 /artifacts $DEVKITPRO
|
||||||
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20240424 /artifacts $DEVKITPRO
|
COPY --from=wiiuenv/wiiumodulesystem:20220904 /artifacts $DEVKITPRO
|
||||||
|
|
||||||
WORKDIR project
|
WORKDIR project
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -108,7 +108,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
all: $(BUILD)
|
all: $(BUILD)
|
||||||
|
|
||||||
$(BUILD):
|
$(BUILD):
|
||||||
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
|
@[ -d $@ ] || mkdir -p $@
|
||||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,4 @@ docker run -it --rm -v ${PWD}:/project functionpatchermodule-builder make clean
|
||||||
|
|
||||||
## Format the code via docker
|
## Format the code via docker
|
||||||
|
|
||||||
`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source -i`
|
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source -i`
|
||||||
|
|
@ -7,18 +7,19 @@ uint32_t FunctionAddressProvider::getEffectiveAddressOfFunction(function_replace
|
||||||
uint32_t real_addr = 0;
|
uint32_t real_addr = 0;
|
||||||
OSDynLoad_Module rpl_handle = nullptr;
|
OSDynLoad_Module rpl_handle = nullptr;
|
||||||
OSDynLoad_Error err = OS_DYNLOAD_OK;
|
OSDynLoad_Error err = OS_DYNLOAD_OK;
|
||||||
|
int32_t rpl_handles_size = sizeof rpl_handles / sizeof rpl_handles[0];
|
||||||
|
|
||||||
for (auto &rplHandle : rpl_handles) {
|
for (int32_t i = 0; i < rpl_handles_size; i++) {
|
||||||
if (rplHandle.library == library) {
|
if (rpl_handles[i].library == library) {
|
||||||
if (rplHandle.handle == nullptr) {
|
if (rpl_handles[i].handle == nullptr) {
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Lets check if rpl is loaded: %s", rplHandle.rplname);
|
DEBUG_FUNCTION_LINE_VERBOSE("Lets acquire handle for rpl: %s", rpl_handles[i].rplname);
|
||||||
err = OSDynLoad_IsModuleLoaded((char *) rplHandle.rplname, &rplHandle.handle);
|
err = OSDynLoad_IsModuleLoaded((char *) rpl_handles[i].rplname, &rpl_handles[i].handle);
|
||||||
}
|
}
|
||||||
if (err != OS_DYNLOAD_OK || !rplHandle.handle) {
|
if (err != OS_DYNLOAD_OK || !rpl_handles[i].handle) {
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("%s is not loaded yet", rplHandle.rplname, err, rplHandle.handle);
|
DEBUG_FUNCTION_LINE_VERBOSE("%s is not loaded yet", rpl_handles[i].rplname, err, rpl_handles[i].handle);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
rpl_handle = rplHandle.handle;
|
rpl_handle = rpl_handles[i].handle;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +29,7 @@ uint32_t FunctionAddressProvider::getEffectiveAddressOfFunction(function_replace
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSDynLoad_FindExport(rpl_handle, OS_DYNLOAD_EXPORT_FUNC, functionName, reinterpret_cast<void **>(&real_addr));
|
OSDynLoad_FindExport(rpl_handle, 0, functionName, reinterpret_cast<void **>(&real_addr));
|
||||||
|
|
||||||
if (!real_addr) {
|
if (!real_addr) {
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("OSDynLoad_FindExport failed for %s", functionName);
|
DEBUG_FUNCTION_LINE_VERBOSE("OSDynLoad_FindExport failed for %s", functionName);
|
||||||
|
|
@ -49,30 +50,14 @@ uint32_t FunctionAddressProvider::getEffectiveAddressOfFunction(function_replace
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionAddressProvider::resetHandles() {
|
void FunctionAddressProvider::resetHandles() {
|
||||||
for (auto &rplHandle : rpl_handles) {
|
int32_t rpl_handles_size = sizeof rpl_handles / sizeof rpl_handles[0];
|
||||||
if (rplHandle.handle != nullptr) {
|
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Resetting handle for rpl: %s", rplHandle.rplname);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
rplHandle.handle = nullptr;
|
rpl_handles[i].handle = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function_replacement_library_type_t FunctionAddressProvider::getTypeForHandle(OSDynLoad_Module handle) {
|
|
||||||
for (auto &rplHandle : rpl_handles) {
|
|
||||||
if (rplHandle.handle == handle) {
|
|
||||||
return rplHandle.library;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LIBRARY_OTHER;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FunctionAddressProvider::resetHandle(OSDynLoad_Module handle) {
|
|
||||||
for (auto &rplHandle : rpl_handles) {
|
|
||||||
if (rplHandle.handle == handle) {
|
|
||||||
rplHandle.handle = nullptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include <coreinit/dynload.h>
|
#include <coreinit/dynload.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <function_patcher/fpatching_defines.h>
|
#include <function_patcher/fpatching_defines.h>
|
||||||
#include <list>
|
|
||||||
|
|
||||||
typedef struct rpl_handling {
|
typedef struct rpl_handling {
|
||||||
function_replacement_library_type_t library;
|
function_replacement_library_type_t library;
|
||||||
|
|
@ -16,11 +15,7 @@ public:
|
||||||
uint32_t getEffectiveAddressOfFunction(function_replacement_library_type_t library, const char *functionName);
|
uint32_t getEffectiveAddressOfFunction(function_replacement_library_type_t library, const char *functionName);
|
||||||
void resetHandles();
|
void resetHandles();
|
||||||
|
|
||||||
function_replacement_library_type_t getTypeForHandle(OSDynLoad_Module toReset);
|
rpl_handling rpl_handles[LIBRARY_OTHER] = {
|
||||||
|
|
||||||
bool resetHandle(OSDynLoad_Module handle);
|
|
||||||
|
|
||||||
std::list<rpl_handling> rpl_handles = {
|
|
||||||
{LIBRARY_AVM, "avm.rpl", nullptr},
|
{LIBRARY_AVM, "avm.rpl", nullptr},
|
||||||
{LIBRARY_CAMERA, "camera.rpl", nullptr},
|
{LIBRARY_CAMERA, "camera.rpl", nullptr},
|
||||||
{LIBRARY_COREINIT, "coreinit.rpl", nullptr},
|
{LIBRARY_COREINIT, "coreinit.rpl", nullptr},
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,9 @@
|
||||||
#include "PatchedFunctionData.h"
|
#include "PatchedFunctionData.h"
|
||||||
#include "utils/KernelFindExport.h"
|
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
#include <coreinit/mcp.h>
|
|
||||||
#include <coreinit/title.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
std::optional<std::shared_ptr<PatchedFunctionData>> PatchedFunctionData::make_shared_v3(std::shared_ptr<FunctionAddressProvider> functionAddressProvider,
|
std::optional<std::shared_ptr<PatchedFunctionData>> PatchedFunctionData::make_shared(std::shared_ptr<FunctionAddressProvider> functionAddressProvider,
|
||||||
function_replacement_data_v3_t *replacementData,
|
function_replacement_data_t *replacementData,
|
||||||
MEMHeapHandle heapHandle) {
|
MEMHeapHandle heapHandle) {
|
||||||
if (!replacementData) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ptr = make_shared_nothrow<PatchedFunctionData>(std::move(functionAddressProvider));
|
|
||||||
if (!ptr) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to alloc PatchedFunctionData");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr->isPatched = false;
|
|
||||||
ptr->heapHandle = heapHandle;
|
|
||||||
ptr->replacementFunctionAddress = replacementData->replaceAddr;
|
|
||||||
ptr->realCallFunctionAddressPtr = replacementData->replaceCall;
|
|
||||||
ptr->targetProcess = replacementData->targetProcess;
|
|
||||||
ptr->type = replacementData->type;
|
|
||||||
|
|
||||||
switch (replacementData->type) {
|
|
||||||
case FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME:
|
|
||||||
case FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS: {
|
|
||||||
ptr->library = {};
|
|
||||||
for (uint32_t i = 0; i < replacementData->ReplaceInRPX.targetTitleIdsCount; i++) {
|
|
||||||
ptr->titleIds.insert(replacementData->ReplaceInRPX.targetTitleIds[i]);
|
|
||||||
}
|
|
||||||
ptr->titleVersionMin = replacementData->ReplaceInRPX.versionMin;
|
|
||||||
ptr->titleVersionMax = replacementData->ReplaceInRPX.versionMax;
|
|
||||||
ptr->executableName = replacementData->ReplaceInRPX.executableName;
|
|
||||||
if (replacementData->type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) {
|
|
||||||
ptr->textOffset = replacementData->ReplaceInRPX.textOffset;
|
|
||||||
} else if (replacementData->type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME) {
|
|
||||||
ptr->functionName = replacementData->ReplaceInRPX.functionName;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FUNCTION_PATCHER_REPLACE_BY_LIB_OR_ADDRESS: {
|
|
||||||
ptr->library = replacementData->ReplaceInRPL.library;
|
|
||||||
if (replacementData->ReplaceInRPL.library != LIBRARY_OTHER) {
|
|
||||||
ptr->functionName = replacementData->ReplaceInRPL.function_name;
|
|
||||||
} else {
|
|
||||||
ptr->realEffectiveFunctionAddress = replacementData->virtualAddr;
|
|
||||||
ptr->realPhysicalFunctionAddress = replacementData->physicalAddr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ptr->allocateDataForJumps()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::shared_ptr<PatchedFunctionData>> PatchedFunctionData::make_shared_v2(std::shared_ptr<FunctionAddressProvider> functionAddressProvider,
|
|
||||||
function_replacement_data_v2_t *replacementData,
|
|
||||||
MEMHeapHandle heapHandle) {
|
|
||||||
if (!replacementData) {
|
if (!replacementData) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +13,6 @@ std::optional<std::shared_ptr<PatchedFunctionData>> PatchedFunctionData::make_sh
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr->type = FUNCTION_PATCHER_REPLACE_BY_LIB_OR_ADDRESS;
|
|
||||||
ptr->isPatched = false;
|
ptr->isPatched = false;
|
||||||
ptr->heapHandle = heapHandle;
|
ptr->heapHandle = heapHandle;
|
||||||
ptr->library = replacementData->library;
|
ptr->library = replacementData->library;
|
||||||
|
|
@ -88,136 +27,47 @@ std::optional<std::shared_ptr<PatchedFunctionData>> PatchedFunctionData::make_sh
|
||||||
ptr->realPhysicalFunctionAddress = replacementData->physicalAddr;
|
ptr->realPhysicalFunctionAddress = replacementData->physicalAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ptr->allocateDataForJumps()) {
|
ptr->jumpToOriginal = (uint32_t *) MEMAllocFromExpHeapEx(ptr->heapHandle, 0x5 * sizeof(uint32_t), 4);
|
||||||
|
|
||||||
|
if (ptr->replacementFunctionAddress > 0x01FFFFFC || 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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PatchedFunctionData::updateFunctionAddresses() {
|
||||||
bool PatchedFunctionData::allocateDataForJumps() {
|
if (this->library == LIBRARY_OTHER) {
|
||||||
if (this->jumpData != nullptr && this->jumpToOriginal != nullptr) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this->replacementFunctionAddress > 0x01FFFFFC || this->targetProcess != FP_TARGET_PROCESS_ALL) {
|
|
||||||
this->jumpDataSize = 15; // We could predict the actual size and save some memory, but at the moment we don't need it.
|
|
||||||
this->jumpData = (uint32_t *) MEMAllocFromExpHeapEx(this->heapHandle, this->jumpDataSize * sizeof(uint32_t), 4);
|
|
||||||
|
|
||||||
if (!this->jumpData) {
|
if (!this->functionName) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data");
|
DEBUG_FUNCTION_LINE_ERR("Function name was empty. This should never happen.");
|
||||||
return false;
|
OSFatal("function name was empty");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->jumpToOriginal = (uint32_t *) MEMAllocFromExpHeapEx(this->heapHandle, 0x5 * sizeof(uint32_t), 4);
|
|
||||||
|
|
||||||
if (!this->jumpToOriginal) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to alloc jump data");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PatchedFunctionData::getAddressForExecutable(uint32_t *outAddress) const {
|
|
||||||
if (!outAddress) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!executableName.has_value()) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t result = 0;
|
|
||||||
if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) {
|
|
||||||
int num_rpls = OSDynLoad_GetNumberOfRPLs();
|
|
||||||
if (num_rpls == 0) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("OSDynLoad_GetNumberOfRPLs failed. Missing patches?");
|
|
||||||
OSFatal("OSDynLoad_GetNumberOfRPLs failed. This shouldn't happen. Missing patches?");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<OSDynLoad_NotifyData> rpls;
|
|
||||||
rpls.resize(num_rpls);
|
|
||||||
|
|
||||||
bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data());
|
|
||||||
if (!ret) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("OSDynLoad_GetRPLInfo failed. Missing patches?");
|
|
||||||
OSFatal("OSDynLoad_GetNumberOfRPLs failed. This shouldn't happen. Missing patches?");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool found = false;
|
|
||||||
for (auto &rpl : rpls) {
|
|
||||||
if (std::string_view(rpl.name).ends_with(executableName.value())) {
|
|
||||||
result = rpl.textAddr + textOffset;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
if (executableName->ends_with(".rpx")) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Can't patch function. \"%s\" is not loaded.", executableName->c_str());
|
|
||||||
} else {
|
|
||||||
DEBUG_FUNCTION_LINE_WARN("Can't patch function. \"%s\" is not loaded.", executableName->c_str());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME) {
|
|
||||||
if (!this->functionName) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Function name was empty. This should never happen.");
|
|
||||||
OSFatal("Function name was empty. This should never happen. Check logs for more information.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
result = KernelFindExport(executableName.value(), functionName.value());
|
|
||||||
if (result == 0) {
|
|
||||||
DEBUG_FUNCTION_LINE_WARN("Failed to find function \"%s\" in \"%s\".", functionName->c_str(), executableName->c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Unexpected function patching type. %d", type);
|
|
||||||
OSFatal("Unexpected function patching type.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*outAddress = result;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PatchedFunctionData::updateFunctionAddresses() {
|
|
||||||
uint32_t real_address;
|
|
||||||
if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME || type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) {
|
|
||||||
if (!getAddressForExecutable(&real_address)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!this->library) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("library name was empty. This should never happen.");
|
|
||||||
OSFatal("library was empty. This should never happen. Check logs for more information.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this->library == LIBRARY_OTHER) {
|
|
||||||
// Use the provided physical/effective address!
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->functionName) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Function name was empty. This should never happen.");
|
|
||||||
OSFatal("Function name was empty. This should never happen. Check logs for more information.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
real_address = functionAddressProvider->getEffectiveAddressOfFunction(library.value(), 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;
|
this->realEffectiveFunctionAddress = real_address;
|
||||||
auto physicalFunctionAddress = (uint32_t) OSEffectiveToPhysical(real_address);
|
auto physicalFunctionAddress = (uint32_t) OSEffectiveToPhysical(real_address);
|
||||||
if (!physicalFunctionAddress) {
|
if (!physicalFunctionAddress) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Error. Something is wrong with the physical address");
|
DEBUG_FUNCTION_LINE_ERR("Error. Something is wrong with the physical address");
|
||||||
OSFatal("Error. Something is wrong with the physical address");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this->realPhysicalFunctionAddress = physicalFunctionAddress;
|
this->realPhysicalFunctionAddress = physicalFunctionAddress;
|
||||||
|
|
@ -227,20 +77,20 @@ bool PatchedFunctionData::updateFunctionAddresses() {
|
||||||
void PatchedFunctionData::generateJumpToOriginal() {
|
void PatchedFunctionData::generateJumpToOriginal() {
|
||||||
if (!this->jumpToOriginal) {
|
if (!this->jumpToOriginal) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("this->jumpToOriginal is not allocated");
|
DEBUG_FUNCTION_LINE_ERR("this->jumpToOriginal is not allocated");
|
||||||
OSFatal("FunctionPatcherModule: this->jumpToOriginal is not allocated");
|
OSFatal("this->jumpToOriginal is not allocated");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t jumpToAddress = this->realEffectiveFunctionAddress + 4;
|
uint32_t jumpToAddress = this->realEffectiveFunctionAddress + 4;
|
||||||
|
|
||||||
|
this->jumpToOriginal[0] = this->replacedInstruction;
|
||||||
|
|
||||||
if (((uint32_t) jumpToAddress & 0x01FFFFFC) != (uint32_t) jumpToAddress) {
|
if (((uint32_t) jumpToAddress & 0x01FFFFFC) != (uint32_t) jumpToAddress) {
|
||||||
// We need to do a long jump
|
// We need to do a long jump
|
||||||
this->jumpToOriginal[0] = 0x3d600000 | ((jumpToAddress >> 16) & 0x0000FFFF); // lis r11 ,0x1234
|
this->jumpToOriginal[1] = 0x3d600000 | ((jumpToAddress >> 16) & 0x0000FFFF); // lis r11 ,0x1234
|
||||||
this->jumpToOriginal[1] = 0x616b0000 | (jumpToAddress & 0x0000ffff); // ori r11 ,r11 ,0x5678
|
this->jumpToOriginal[2] = 0x616b0000 | (jumpToAddress & 0x0000ffff); // ori r11 ,r11 ,0x5678
|
||||||
this->jumpToOriginal[2] = 0x7d6903a6; // mtspr CTR ,r11
|
this->jumpToOriginal[3] = 0x7d6903a6; // mtspr CTR ,r11
|
||||||
this->jumpToOriginal[3] = this->replacedInstruction;
|
this->jumpToOriginal[4] = 0x4e800420; // bctr
|
||||||
this->jumpToOriginal[4] = 0x4e800420; // bctr
|
|
||||||
} else {
|
} else {
|
||||||
this->jumpToOriginal[0] = this->replacedInstruction;
|
|
||||||
this->jumpToOriginal[1] = 0x48000002 | (jumpToAddress & 0x01FFFFFC);
|
this->jumpToOriginal[1] = 0x48000002 | (jumpToAddress & 0x01FFFFFC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +109,7 @@ void PatchedFunctionData::generateReplacementJump() {
|
||||||
if (this->replacementFunctionAddress > 0x01FFFFFC || this->targetProcess != FP_TARGET_PROCESS_ALL) {
|
if (this->replacementFunctionAddress > 0x01FFFFFC || this->targetProcess != FP_TARGET_PROCESS_ALL) {
|
||||||
if (!this->jumpData) {
|
if (!this->jumpData) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("jumpData was not allocated");
|
DEBUG_FUNCTION_LINE_ERR("jumpData was not allocated");
|
||||||
OSFatal("FunctionPatcherModule: jumpData was not allocated");
|
OSFatal("jumpData was not allocated");
|
||||||
}
|
}
|
||||||
uint32_t offset = 0;
|
uint32_t offset = 0;
|
||||||
if (this->targetProcess != FP_TARGET_PROCESS_ALL) {
|
if (this->targetProcess != FP_TARGET_PROCESS_ALL) {
|
||||||
|
|
@ -300,13 +150,13 @@ void PatchedFunctionData::generateReplacementJump() {
|
||||||
|
|
||||||
if (offset >= this->jumpDataSize) {
|
if (offset >= this->jumpDataSize) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Tried to overflow buffer. offset: %08X vs array size: %08X", offset, this->jumpDataSize);
|
DEBUG_FUNCTION_LINE_ERR("Tried to overflow buffer. offset: %08X vs array size: %08X", offset, this->jumpDataSize);
|
||||||
OSFatal("FunctionPatcherModule: Wrote too much data");
|
OSFatal("Wrote too much data");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the trampoline itself is usable.
|
// Make sure the trampoline itself is usable.
|
||||||
if (((uint32_t) this->jumpData & 0x01FFFFFC) != (uint32_t) this->jumpData) {
|
if (((uint32_t) this->jumpData & 0x01FFFFFC) != (uint32_t) this->jumpData) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Jump is impossible");
|
DEBUG_FUNCTION_LINE_ERR("Jump is impossible");
|
||||||
OSFatal("FunctionPatcherModule: Jump is impossible");
|
OSFatal("Jump is impossible");
|
||||||
}
|
}
|
||||||
|
|
||||||
this->replaceWithInstruction = 0x48000002 | ((uint32_t) this->jumpData & 0x01FFFFFC);
|
this->replaceWithInstruction = 0x48000002 | ((uint32_t) this->jumpData & 0x01FFFFFC);
|
||||||
|
|
@ -321,6 +171,18 @@ void PatchedFunctionData::generateReplacementJump() {
|
||||||
OSMemoryBarrier();
|
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() {
|
PatchedFunctionData::~PatchedFunctionData() {
|
||||||
if (this->jumpToOriginal) {
|
if (this->jumpToOriginal) {
|
||||||
MEMFreeToExpHeap(this->heapHandle, this->jumpToOriginal);
|
MEMFreeToExpHeap(this->heapHandle, this->jumpToOriginal);
|
||||||
|
|
@ -331,35 +193,3 @@ PatchedFunctionData::~PatchedFunctionData() {
|
||||||
this->jumpData = nullptr;
|
this->jumpData = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PatchedFunctionData::shouldBePatched() const {
|
|
||||||
if (type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_NAME || type == FUNCTION_PATCHER_REPLACE_FOR_EXECUTABLE_BY_ADDRESS) {
|
|
||||||
uint64_t curTitleId = OSGetTitleID();
|
|
||||||
if (!this->titleIds.contains(curTitleId)) {
|
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Skip function patch. Patch is not for title %016llX", curTitleId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto mcpHandle = MCP_Open();
|
|
||||||
MCPTitleListType titleInfo;
|
|
||||||
int32_t res = -1;
|
|
||||||
if ((curTitleId & 0x0000000F00000000) == 0) {
|
|
||||||
res = MCP_GetTitleInfo(mcpHandle, curTitleId | 0x0000000E00000000, &titleInfo);
|
|
||||||
}
|
|
||||||
if (res != 0) {
|
|
||||||
res = MCP_GetTitleInfo(mcpHandle, curTitleId, &titleInfo);
|
|
||||||
}
|
|
||||||
MCP_Close(mcpHandle);
|
|
||||||
if (res != 0) {
|
|
||||||
DEBUG_FUNCTION_LINE_WARN("Failed to get title version of %016llX.", curTitleId);
|
|
||||||
OSFatal("Failed to get title version. This should not happen.\n"
|
|
||||||
"Please report this with a crash log.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
MCP_Close(mcpHandle);
|
|
||||||
if (titleInfo.titleVersion < titleVersionMin || titleInfo.titleVersion > titleVersionMax) {
|
|
||||||
DEBUG_FUNCTION_LINE("Skipping function patch. Title version does not match: Expected >= %d && <= %d. Real version: %d", titleVersionMin, titleVersionMax, titleInfo.titleVersion);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
#include <function_patcher/fpatching_defines.h>
|
#include <function_patcher/fpatching_defines.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
@ -23,16 +22,9 @@ public:
|
||||||
explicit PatchedFunctionData(std::shared_ptr<FunctionAddressProvider> functionAddressProvider) : functionAddressProvider(std::move(functionAddressProvider)) {
|
explicit PatchedFunctionData(std::shared_ptr<FunctionAddressProvider> functionAddressProvider) : functionAddressProvider(std::move(functionAddressProvider)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<std::shared_ptr<PatchedFunctionData>> make_shared_v2(std::shared_ptr<FunctionAddressProvider> functionAddressProvider,
|
static std::optional<std::shared_ptr<PatchedFunctionData>> make_shared(std::shared_ptr<FunctionAddressProvider> functionAddressProvider,
|
||||||
function_replacement_data_v2_t *replacementData,
|
function_replacement_data_t *replacementData,
|
||||||
MEMHeapHandle heapHandle);
|
MEMHeapHandle heapHandle);
|
||||||
static std::optional<std::shared_ptr<PatchedFunctionData>> make_shared_v3(std::shared_ptr<FunctionAddressProvider> functionAddressProvider,
|
|
||||||
function_replacement_data_v3_t *replacementData,
|
|
||||||
MEMHeapHandle heapHandle);
|
|
||||||
|
|
||||||
bool allocateDataForJumps();
|
|
||||||
|
|
||||||
bool getAddressForExecutable(uint32_t *outAddress) const;
|
|
||||||
|
|
||||||
bool updateFunctionAddresses();
|
bool updateFunctionAddresses();
|
||||||
|
|
||||||
|
|
@ -40,38 +32,31 @@ public:
|
||||||
|
|
||||||
void generateReplacementJump();
|
void generateReplacementJump();
|
||||||
|
|
||||||
[[nodiscard]] bool shouldBePatched() const;
|
|
||||||
|
|
||||||
uint32_t getHandle() {
|
uint32_t getHandle() {
|
||||||
return (uint32_t) this;
|
return (uint32_t) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t *jumpToOriginal = {};
|
[[nodiscard]] bool isDynamicFunction() const;
|
||||||
uint32_t *jumpData = {};
|
|
||||||
|
|
||||||
uint32_t realEffectiveFunctionAddress = {};
|
uint32_t *jumpToOriginal{};
|
||||||
uint32_t realPhysicalFunctionAddress = {};
|
uint32_t *jumpData{};
|
||||||
|
|
||||||
uint32_t *realCallFunctionAddressPtr = {};
|
uint32_t realEffectiveFunctionAddress{};
|
||||||
|
uint32_t realPhysicalFunctionAddress{};
|
||||||
|
|
||||||
uint32_t replacementFunctionAddress = {};
|
uint32_t *realCallFunctionAddressPtr{};
|
||||||
|
|
||||||
uint32_t replacedInstruction = {};
|
uint32_t replacementFunctionAddress{};
|
||||||
|
|
||||||
uint32_t replaceWithInstruction = {};
|
uint32_t replacedInstruction{};
|
||||||
uint32_t jumpDataSize = 15;
|
|
||||||
MEMHeapHandle heapHandle = nullptr;
|
|
||||||
|
|
||||||
FunctionPatcherFunctionType type = {};
|
uint32_t replaceWithInstruction{};
|
||||||
std::set<uint64_t> titleIds;
|
uint32_t jumpDataSize = 15;
|
||||||
uint16_t titleVersionMin = 0;
|
MEMHeapHandle heapHandle = nullptr;
|
||||||
uint16_t titleVersionMax = 0xFFFF;
|
|
||||||
std::optional<std::string> executableName = {};
|
|
||||||
uint32_t textOffset = 0;
|
|
||||||
|
|
||||||
bool isPatched = {};
|
bool isPatched{};
|
||||||
std::optional<function_replacement_library_type_t> library = {};
|
function_replacement_library_type_t library{};
|
||||||
FunctionPatcherTargetProcess targetProcess = {};
|
FunctionPatcherTargetProcess targetProcess{};
|
||||||
std::optional<std::string> functionName = {};
|
std::optional<std::string> functionName = {};
|
||||||
std::shared_ptr<FunctionAddressProvider> functionAddressProvider = {};
|
std::shared_ptr<FunctionAddressProvider> functionAddressProvider;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,84 +4,54 @@
|
||||||
#include "utils/globals.h"
|
#include "utils/globals.h"
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <wums/exports.h>
|
|
||||||
|
|
||||||
WUT_CHECK_OFFSET(function_replacement_data_v2_t, 0x00, VERSION);
|
bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle) {
|
||||||
WUT_CHECK_OFFSET(function_replacement_data_v3_t, 0x00, version);
|
if (function_data->VERSION != FUNCTION_REPLACEMENT_DATA_STRUCT_VERSION) {
|
||||||
|
|
||||||
FunctionPatcherStatus FPAddFunctionPatch(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle, bool *outHasBeenPatched) {
|
|
||||||
if (function_data == nullptr) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("function_data was NULL");
|
|
||||||
return FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function_data->version < 2 || function_data->version > 3) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to patch function. struct version mismatch");
|
DEBUG_FUNCTION_LINE_ERR("Failed to patch function. struct version mismatch");
|
||||||
return FUNCTION_PATCHER_RESULT_UNSUPPORTED_STRUCT_VERSION;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::shared_ptr<PatchedFunctionData>> functionDataOpt;
|
auto functionDataOpt = PatchedFunctionData::make_shared(gFunctionAddressProvider, function_data, gJumpHeapHandle);
|
||||||
if (function_data->version == 2) {
|
|
||||||
functionDataOpt = PatchedFunctionData::make_shared_v2(gFunctionAddressProvider, (function_replacement_data_v2_t *) function_data, gJumpHeapHandle);
|
|
||||||
} else if (function_data->version == 3) {
|
|
||||||
functionDataOpt = PatchedFunctionData::make_shared_v3(gFunctionAddressProvider, (function_replacement_data_v3_t *) function_data, gJumpHeapHandle);
|
|
||||||
} else {
|
|
||||||
// Should never happen.
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Unknown function_replacement_data_t struct version");
|
|
||||||
OSFatal("Unknown function patching struct version. Update FunctionPatcherModule/Aroma.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!functionDataOpt) {
|
if (!functionDataOpt) {
|
||||||
return FUNCTION_PATCHER_RESULT_UNKNOWN_ERROR;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &functionData = functionDataOpt.value();
|
auto functionData = functionDataOpt.value();
|
||||||
|
|
||||||
// PatchFunction calls OSFatal on fatal errors.
|
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
||||||
// If this function returns false the target function was not patched
|
|
||||||
// Usually this means the target RPL is not (yet) loaded.
|
if (!PatchFunction(functionData)) {
|
||||||
auto patchResult = PatchFunction(functionData);
|
DEBUG_FUNCTION_LINE_ERR("Failed to patch function");
|
||||||
if (outHasBeenPatched) {
|
return false;
|
||||||
*outHasBeenPatched = patchResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outHandle) {
|
if (outHandle) {
|
||||||
*outHandle = functionData->getHandle();
|
*outHandle = functionData->getHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
gPatchedFunctions.push_back(std::move(functionData));
|
||||||
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
|
||||||
gPatchedFunctions.push_back(std::move(functionData));
|
|
||||||
|
|
||||||
OSMemoryBarrier();
|
OSMemoryBarrier();
|
||||||
}
|
|
||||||
|
|
||||||
return FUNCTION_PATCHER_RESULT_SUCCESS;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionPatcherPatchFunction(function_replacement_data_t *function_data, PatchedFunctionHandle *outHandle) {
|
bool FunctionPatcherRestoreFunction(PatchedFunctionHandle handle) {
|
||||||
return FPAddFunctionPatch(function_data, outHandle, nullptr) == FUNCTION_PATCHER_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionPatcherStatus FPRemoveFunctionPatch(PatchedFunctionHandle handle) {
|
|
||||||
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
||||||
std::vector<std::shared_ptr<PatchedFunctionData>> toBeTempRestored;
|
std::vector<std::shared_ptr<PatchedFunctionData>> toBeTempRestored;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
int32_t erasePosition = 0;
|
int32_t erasePosition = 0;
|
||||||
std::shared_ptr<PatchedFunctionData> toBeRemoved;
|
std::shared_ptr<PatchedFunctionData> toRemoved;
|
||||||
for (auto &cur : gPatchedFunctions) {
|
for (auto &cur : gPatchedFunctions) {
|
||||||
if (cur->getHandle() == handle) {
|
if (cur->getHandle() == handle) {
|
||||||
toBeRemoved = cur;
|
toRemoved = cur;
|
||||||
found = true;
|
found = true;
|
||||||
if (!cur->isPatched) {
|
|
||||||
// Early return if the function is not patched.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Check if something else patched the same function afterwards.
|
// Check if something else patched the same function afterwards.
|
||||||
if (found) {
|
if (found) {
|
||||||
if (cur->realPhysicalFunctionAddress == toBeRemoved->realPhysicalFunctionAddress) {
|
if (cur->realPhysicalFunctionAddress == toRemoved->realPhysicalFunctionAddress) {
|
||||||
toBeTempRestored.push_back(cur);
|
toBeTempRestored.push_back(cur);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -90,59 +60,24 @@ FunctionPatcherStatus FPRemoveFunctionPatch(PatchedFunctionHandle handle) {
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to find PatchedFunctionData by handle %08X", handle);
|
DEBUG_FUNCTION_LINE_ERR("Failed to find PatchedFunctionData by handle %08X", handle);
|
||||||
return FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toBeRemoved->isPatched) {
|
// Restore function patches that were done after the patch we actually want to restore.
|
||||||
// Restore function patches that were done after the patch we actually want to restore.
|
for (auto &cur : std::ranges::reverse_view(toBeTempRestored)) {
|
||||||
for (auto &cur : std::ranges::reverse_view(toBeTempRestored)) {
|
RestoreFunction(cur);
|
||||||
RestoreFunction(cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore the function we actually want to restore
|
|
||||||
RestoreFunction(toBeRemoved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore the function we actually want to restore
|
||||||
|
RestoreFunction(toRemoved);
|
||||||
|
|
||||||
gPatchedFunctions.erase(gPatchedFunctions.begin() + erasePosition);
|
gPatchedFunctions.erase(gPatchedFunctions.begin() + erasePosition);
|
||||||
|
|
||||||
if (toBeRemoved->isPatched) {
|
// Apply the other patches again
|
||||||
// Apply the other patches again
|
for (auto &cur : toBeTempRestored) {
|
||||||
for (auto &cur : toBeTempRestored) {
|
PatchFunction(cur);
|
||||||
PatchFunction(cur);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OSMemoryBarrier();
|
OSMemoryBarrier();
|
||||||
return FUNCTION_PATCHER_RESULT_SUCCESS;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionPatcherRestoreFunction(PatchedFunctionHandle handle) {
|
|
||||||
return FPRemoveFunctionPatch(handle) == FUNCTION_PATCHER_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionPatcherStatus FPGetVersion(FunctionPatcherAPIVersion *outVersion) {
|
|
||||||
if (outVersion == nullptr) {
|
|
||||||
return FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
*outVersion = 2;
|
|
||||||
return FUNCTION_PATCHER_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionPatcherStatus FPIsFunctionPatched(PatchedFunctionHandle handle, bool *outIsFunctionPatched) {
|
|
||||||
if (outIsFunctionPatched == nullptr) {
|
|
||||||
return FUNCTION_PATCHER_RESULT_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
|
||||||
for (auto &cur : gPatchedFunctions) {
|
|
||||||
if (cur->getHandle() == handle) {
|
|
||||||
*outIsFunctionPatched = cur->isPatched;
|
|
||||||
return FUNCTION_PATCHER_RESULT_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FUNCTION_PATCHER_RESULT_PATCH_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
WUMS_EXPORT_FUNCTION(FPGetVersion);
|
|
||||||
WUMS_EXPORT_FUNCTION(FPAddFunctionPatch);
|
|
||||||
WUMS_EXPORT_FUNCTION(FPRemoveFunctionPatch);
|
|
||||||
WUMS_EXPORT_FUNCTION(FPIsFunctionPatched);
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "PatchedFunctionData.h"
|
#include "PatchedFunctionData.h"
|
||||||
#include "utils/CThread.h"
|
#include "utils/CThread.h"
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
#include "utils/utils.h"
|
|
||||||
#include <coreinit/cache.h>
|
#include <coreinit/cache.h>
|
||||||
#include <coreinit/debug.h>
|
#include <coreinit/debug.h>
|
||||||
#include <coreinit/memorymap.h>
|
#include <coreinit/memorymap.h>
|
||||||
|
|
@ -32,17 +31,13 @@ static void writeDataAndFlushIC(CThread *thread, void *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PatchFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
|
bool PatchFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
|
||||||
if (patchedFunction->isPatched) {
|
// The addresses of a function might change every time with run another application.
|
||||||
|
if (!patchedFunction->updateFunctionAddresses()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!patchedFunction->shouldBePatched()) {
|
if (patchedFunction->isPatched) {
|
||||||
return false;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
// The addresses of a function might change every time with run another application.
|
|
||||||
if (!patchedFunction->updateFunctionAddresses()) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patchedFunction->functionName) {
|
if (patchedFunction->functionName) {
|
||||||
|
|
@ -51,12 +46,26 @@ bool PatchFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
|
||||||
DEBUG_FUNCTION_LINE("Patching function @ %08X", patchedFunction->realEffectiveFunctionAddress);
|
DEBUG_FUNCTION_LINE("Patching function @ %08X", patchedFunction->realEffectiveFunctionAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ReadFromPhysicalAddress(patchedFunction->realPhysicalFunctionAddress, &patchedFunction->replacedInstruction)) {
|
volatile uint32_t replacedInstruction;
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to read instruction.");
|
|
||||||
OSFatal("FunctionPatcherModule: Failed to read instruction.");
|
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;
|
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
|
// Generate a jump to the original function so the unpatched function can still be called
|
||||||
patchedFunction->generateJumpToOriginal();
|
patchedFunction->generateJumpToOriginal();
|
||||||
|
|
||||||
|
|
@ -89,36 +98,31 @@ bool RestoreFunction(std::shared_ptr<PatchedFunctionData> &patchedFunction) {
|
||||||
targetAddrPhys = (uint32_t) OSEffectiveToPhysical(patchedFunction->realEffectiveFunctionAddress);
|
targetAddrPhys = (uint32_t) OSEffectiveToPhysical(patchedFunction->realEffectiveFunctionAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if patched instruction is still loaded.
|
if (patchedFunction->isDynamicFunction() &&
|
||||||
uint32_t currentInstruction;
|
// Other processes than the wii u menu and game one seem to keep their rpl's loaded.
|
||||||
if (!ReadFromPhysicalAddress(patchedFunction->realPhysicalFunctionAddress, ¤tInstruction)) {
|
patchedFunction->targetProcess != FP_TARGET_PROCESS_GAME_AND_MENU &&
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to read instruction.");
|
patchedFunction->targetProcess != FP_TARGET_PROCESS_GAME &&
|
||||||
return false;
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
ICInvalidateRange((void *) patchedFunction->realEffectiveFunctionAddress, 4);
|
||||||
|
DCFlushRange((void *) patchedFunction->realEffectiveFunctionAddress, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentInstruction != patchedFunction->replaceWithInstruction) {
|
|
||||||
DEBUG_FUNCTION_LINE_WARN("Instruction is different than expected. Skip restoring. Expected: %08X Real: %08X", currentInstruction, patchedFunction->replaceWithInstruction);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Restoring %08X to %08X [%08X]", (uint32_t) patchedFunction->replacedInstruction, patchedFunction->realEffectiveFunctionAddress, targetAddrPhys);
|
|
||||||
auto sourceAddr = (uint32_t) &patchedFunction->replacedInstruction;
|
|
||||||
|
|
||||||
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("FunctionPatcherModule: Failed to get physical address");
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelCopyData(targetAddrPhys, sourceAddrPhys, 4);
|
|
||||||
ICInvalidateRange((void *) patchedFunction->realEffectiveFunctionAddress, 4);
|
|
||||||
DCFlushRange((void *) patchedFunction->realEffectiveFunctionAddress, 4);
|
|
||||||
|
|
||||||
patchedFunction->isPatched = false;
|
patchedFunction->isPatched = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
149
source/main.cpp
149
source/main.cpp
|
|
@ -6,14 +6,10 @@
|
||||||
#include "utils/utils.h"
|
#include "utils/utils.h"
|
||||||
#include <coreinit/memdefaultheap.h>
|
#include <coreinit/memdefaultheap.h>
|
||||||
#include <coreinit/memexpheap.h>
|
#include <coreinit/memexpheap.h>
|
||||||
#include <kernel/kernel.h>
|
|
||||||
#include <ranges>
|
|
||||||
#include <set>
|
|
||||||
#include <wums.h>
|
#include <wums.h>
|
||||||
|
|
||||||
WUMS_MODULE_EXPORT_NAME("homebrew_functionpatcher");
|
WUMS_MODULE_EXPORT_NAME("homebrew_functionpatcher");
|
||||||
WUMS_MODULE_INIT_BEFORE_RELOCATION_DONE_HOOK();
|
WUMS_MODULE_INIT_BEFORE_RELOCATION_DONE_HOOK();
|
||||||
WUMS_DEPENDS_ON(homebrew_kernel);
|
|
||||||
|
|
||||||
void UpdateFunctionPointer() {
|
void UpdateFunctionPointer() {
|
||||||
// We need the real MEMAllocFromDefaultHeapEx/MEMFreeToDefaultHeap function pointer to force-allocate memory on the default heap.
|
// We need the real MEMAllocFromDefaultHeapEx/MEMFreeToDefaultHeap function pointer to force-allocate memory on the default heap.
|
||||||
|
|
@ -21,17 +17,18 @@ void UpdateFunctionPointer() {
|
||||||
OSDynLoad_Module coreinitModule;
|
OSDynLoad_Module coreinitModule;
|
||||||
if (OSDynLoad_Acquire("coreinit", &coreinitModule) != OS_DYNLOAD_OK) {
|
if (OSDynLoad_Acquire("coreinit", &coreinitModule) != OS_DYNLOAD_OK) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to acquire coreinit.rpl");
|
DEBUG_FUNCTION_LINE_ERR("Failed to acquire coreinit.rpl");
|
||||||
OSFatal("FunctionPatcherModule: Failed to acquire coreinit.rpl");
|
OSFatal("Failed to acquire coreinit.rpl");
|
||||||
}
|
}
|
||||||
/* Memory allocation functions */
|
/* Memory allocation functions */
|
||||||
uint32_t *allocPtr, *freePtr;
|
uint32_t *allocPtr, *freePtr;
|
||||||
if (OSDynLoad_FindExport(coreinitModule, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromDefaultHeapEx", reinterpret_cast<void **>(&allocPtr)) != OS_DYNLOAD_OK) {
|
/* Memory allocation functions */
|
||||||
|
if (OSDynLoad_FindExport(coreinitModule, true, "MEMAllocFromDefaultHeapEx", reinterpret_cast<void **>(&allocPtr)) != OS_DYNLOAD_OK) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx");
|
DEBUG_FUNCTION_LINE_ERR("OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx");
|
||||||
OSFatal("FunctionPatcherModule: OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx");
|
OSFatal("OSDynLoad_FindExport for MEMAllocFromDefaultHeapEx");
|
||||||
}
|
}
|
||||||
if (OSDynLoad_FindExport(coreinitModule, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToDefaultHeap", reinterpret_cast<void **>(&freePtr)) != OS_DYNLOAD_OK) {
|
if (OSDynLoad_FindExport(coreinitModule, true, "MEMFreeToDefaultHeap", reinterpret_cast<void **>(&freePtr)) != OS_DYNLOAD_OK) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("OSDynLoad_FindExport for MEMFreeToDefaultHeap");
|
DEBUG_FUNCTION_LINE_ERR("OSDynLoad_FindExport for MEMFreeToDefaultHeap");
|
||||||
OSFatal("FunctionPatcherModule: OSDynLoad_FindExport for MEMFreeToDefaultHeap");
|
OSFatal("OSDynLoad_FindExport for MEMFreeToDefaultHeap");
|
||||||
}
|
}
|
||||||
|
|
||||||
gMEMAllocFromDefaultHeapExForThreads = (void *(*) (uint32_t, int) ) * allocPtr;
|
gMEMAllocFromDefaultHeapExForThreads = (void *(*) (uint32_t, int) ) * allocPtr;
|
||||||
|
|
@ -40,87 +37,25 @@ void UpdateFunctionPointer() {
|
||||||
OSDynLoad_Release(coreinitModule);
|
OSDynLoad_Release(coreinitModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckIfPatchedFunctionsAreStillInMemory() {
|
uint32_t gDoFunctionResets;
|
||||||
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
|
||||||
// Check if rpl has been unloaded by comparing the instruction.
|
|
||||||
std::set<uint32_t> physicalAddressesUnchanged;
|
|
||||||
std::set<uint32_t> physicalAddressesChanged;
|
|
||||||
// Restore function patches that were done after the patch we actually want to restore.
|
|
||||||
for (auto &cur : std::ranges::reverse_view(gPatchedFunctions)) {
|
|
||||||
if (!cur->isPatched || physicalAddressesUnchanged.contains(cur->realPhysicalFunctionAddress)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (physicalAddressesChanged.contains(cur->realPhysicalFunctionAddress)) {
|
|
||||||
cur->isPatched = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if patched instruction is still loaded.
|
|
||||||
uint32_t currentInstruction;
|
|
||||||
if (!ReadFromPhysicalAddress(cur->realPhysicalFunctionAddress, ¤tInstruction)) {
|
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to read instruction.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentInstruction == cur->replaceWithInstruction) {
|
|
||||||
physicalAddressesUnchanged.insert(cur->realPhysicalFunctionAddress);
|
|
||||||
} else {
|
|
||||||
cur->isPatched = false;
|
|
||||||
physicalAddressesChanged.insert(cur->realPhysicalFunctionAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PatchInstruction(void *instr, uint32_t original, uint32_t replacement) {
|
|
||||||
uint32_t current = *(uint32_t *) instr;
|
|
||||||
if (current != original) {
|
|
||||||
return current == replacement;
|
|
||||||
}
|
|
||||||
|
|
||||||
KernelCopyData(OSEffectiveToPhysical((uint32_t) instr), OSEffectiveToPhysical((uint32_t) &replacement), sizeof(replacement));
|
|
||||||
|
|
||||||
DCFlushRange(instr, 4);
|
|
||||||
ICInvalidateRange(instr, 4);
|
|
||||||
|
|
||||||
current = *(uint32_t *) instr;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PatchDynLoadFunctions() {
|
|
||||||
uint32_t *patch1 = ((uint32_t *) &OSDynLoad_GetNumberOfRPLs) + 6;
|
|
||||||
uint32_t *patch2 = ((uint32_t *) &OSDynLoad_GetRPLInfo) + 22;
|
|
||||||
|
|
||||||
if (!PatchInstruction(patch1, 0x41820038 /* beq +38 */, 0x60000000 /*nop*/)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!PatchInstruction(patch2, 0x41820100 /* beq +100 */, 0x60000000 /*nop*/)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
WUMS_INITIALIZE() {
|
WUMS_INITIALIZE() {
|
||||||
UpdateFunctionPointer();
|
UpdateFunctionPointer();
|
||||||
initLogging();
|
|
||||||
|
|
||||||
if (!PatchDynLoadFunctions()) {
|
// don't reset the patch status on the first launch.
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to patch OSDynLoad_GetRPLInfo or OSDynLoad_GetNumberOfRPLs");
|
gDoFunctionResets = false;
|
||||||
OSFatal("Failed to patch OSDynLoad_GetRPLInfo or OSDynLoad_GetNumberOfRPLs");
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(gJumpHeapData, 0, JUMP_HEAP_DATA_SIZE);
|
memset(gJumpHeapData, 0, JUMP_HEAP_DATA_SIZE);
|
||||||
gJumpHeapHandle = MEMCreateExpHeapEx((void *) (gJumpHeapData), JUMP_HEAP_DATA_SIZE, 1);
|
gJumpHeapHandle = MEMCreateExpHeapEx((void *) (gJumpHeapData), JUMP_HEAP_DATA_SIZE, 1);
|
||||||
if (gJumpHeapHandle == nullptr) {
|
if (gJumpHeapHandle == nullptr) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to create heap for jump data");
|
DEBUG_FUNCTION_LINE_ERR("Failed to create heap for jump data");
|
||||||
OSFatal("FunctionPatcherModule: Failed to create heap for jump data");
|
OSFatal("Failed to create heap for jump data");
|
||||||
}
|
}
|
||||||
|
|
||||||
gFunctionAddressProvider = make_shared_nothrow<FunctionAddressProvider>();
|
gFunctionAddressProvider = make_shared_nothrow<FunctionAddressProvider>();
|
||||||
if (!gFunctionAddressProvider) {
|
if (!gFunctionAddressProvider) {
|
||||||
DEBUG_FUNCTION_LINE_ERR("Failed to create gFunctionAddressProvider");
|
DEBUG_FUNCTION_LINE_ERR("Failed to create gFunctionAddressProvider");
|
||||||
OSFatal("FunctionPatcherModule: Failed to create gFunctionAddressProvider");
|
OSFatal("Failed to create gFunctionAddressProvider");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,18 +68,6 @@ void notify_callback(OSDynLoad_Module module,
|
||||||
for (auto &cur : gPatchedFunctions) {
|
for (auto &cur : gPatchedFunctions) {
|
||||||
PatchFunction(cur);
|
PatchFunction(cur);
|
||||||
}
|
}
|
||||||
} else if (reason == OS_DYNLOAD_NOTIFY_UNLOADED) {
|
|
||||||
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
|
||||||
auto library = gFunctionAddressProvider->getTypeForHandle(module);
|
|
||||||
if (library != LIBRARY_OTHER) {
|
|
||||||
for (auto &cur : gPatchedFunctions) {
|
|
||||||
if (cur->type == FUNCTION_PATCHER_REPLACE_BY_LIB_OR_ADDRESS && cur->library.has_value() && cur->library == library) {
|
|
||||||
cur->isPatched = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gFunctionAddressProvider->resetHandle(module);
|
|
||||||
CheckIfPatchedFunctionsAreStillInMemory();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,25 +77,51 @@ WUMS_APPLICATION_STARTS() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OSReport("Running FunctionPatcherModule " MODULE_VERSION_FULL "\n");
|
|
||||||
|
|
||||||
// Now we can update the pointer with the "real" functions
|
// Now we can update the pointer with the "real" functions
|
||||||
gMEMAllocFromDefaultHeapExForThreads = MEMAllocFromDefaultHeapEx;
|
gMEMAllocFromDefaultHeapExForThreads = MEMAllocFromDefaultHeapEx;
|
||||||
gMEMFreeToDefaultHeapForThreads = MEMFreeToDefaultHeap;
|
gMEMFreeToDefaultHeapForThreads = MEMFreeToDefaultHeap;
|
||||||
|
|
||||||
initLogging();
|
initLogging();
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
|
||||||
// reset function patch status if the rpl they were patching has been unloaded from memory.
|
|
||||||
CheckIfPatchedFunctionsAreStillInMemory();
|
|
||||||
DEBUG_FUNCTION_LINE_VERBOSE("Patch all functions");
|
|
||||||
for (auto &cur : gPatchedFunctions) {
|
|
||||||
PatchFunction(cur);
|
|
||||||
}
|
|
||||||
|
|
||||||
OSMemoryBarrier();
|
std::lock_guard<std::mutex> lock(gPatchedFunctionsMutex);
|
||||||
OSDynLoad_AddNotifyCallback(notify_callback, nullptr);
|
|
||||||
|
// Avoid resetting the patch status of function on the first start.
|
||||||
|
// WUMS_INITIALIZE & WUMS_APPLICATION_STARTS are called during the same application => the .rpl won't get reloaded.
|
||||||
|
// If the .rpl won't get reloaded, old patches will still be present. This can be an issue if a module patches a
|
||||||
|
// dynamic function in WUMS_INITIALIZE, which is called right before the first time this function will be called.
|
||||||
|
// This reset code would mark it as unpatched, while the code is actually still patched, leading to patching an
|
||||||
|
// already patched function.
|
||||||
|
// To avoid this issues, the need to skip the reset status part the first time.
|
||||||
|
if (gDoFunctionResets) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
gDoFunctionResets = true;
|
||||||
|
|
||||||
|
OSMemoryBarrier();
|
||||||
|
|
||||||
|
DEBUG_FUNCTION_LINE_VERBOSE("Patch all functions");
|
||||||
|
for (auto &cur : gPatchedFunctions) {
|
||||||
|
PatchFunction(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
OSDynLoad_AddNotifyCallback(notify_callback, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
WUMS_APPLICATION_REQUESTS_EXIT() {
|
WUMS_APPLICATION_REQUESTS_EXIT() {
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,10 @@ public:
|
||||||
typedef void (*Callback)(CThread *thread, void *arg);
|
typedef void (*Callback)(CThread *thread, void *arg);
|
||||||
|
|
||||||
//! constructor
|
//! constructor
|
||||||
explicit CThread(int32_t iAttr, int32_t iPriority = 16, int32_t stacksize = 0x8000, CThread::Callback callback = nullptr, void *callbackArg = nullptr)
|
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) {
|
: pThread(nullptr), pThreadStack(nullptr), pCallback(callback), pCallbackArg(callbackArg) {
|
||||||
//! save attribute assignment
|
//! save attribute assignment
|
||||||
iAttributes = iAttr;
|
iAttributes = iAttr;
|
||||||
iStackSize = stacksize;
|
|
||||||
//! allocate the thread on the default Cafe OS heap
|
//! allocate the thread on the default Cafe OS heap
|
||||||
pThread = (OSThread *) gMEMAllocFromDefaultHeapExForThreads(sizeof(OSThread), 0x10);
|
pThread = (OSThread *) gMEMAllocFromDefaultHeapExForThreads(sizeof(OSThread), 0x10);
|
||||||
//! allocate the stack on the default Cafe OS heap
|
//! allocate the stack on the default Cafe OS heap
|
||||||
|
|
@ -124,15 +123,11 @@ public:
|
||||||
}
|
}
|
||||||
OSJoinThread(pThread, nullptr);
|
OSJoinThread(pThread, nullptr);
|
||||||
}
|
}
|
||||||
// Some games (e.g. Minecraft) expect the default heap to be empty.
|
|
||||||
// Make sure to clean up the memory after using it
|
|
||||||
//! free the thread stack buffer
|
//! free the thread stack buffer
|
||||||
if (pThreadStack) {
|
if (pThreadStack) {
|
||||||
memset(pThreadStack, 0, iStackSize);
|
|
||||||
gMEMFreeToDefaultHeapForThreads(pThreadStack);
|
gMEMFreeToDefaultHeapForThreads(pThreadStack);
|
||||||
}
|
}
|
||||||
if (pThread) {
|
if (pThread) {
|
||||||
memset(pThread, 0, sizeof(OSThread));
|
|
||||||
gMEMFreeToDefaultHeapForThreads(pThread);
|
gMEMFreeToDefaultHeapForThreads(pThread);
|
||||||
}
|
}
|
||||||
pThread = nullptr;
|
pThread = nullptr;
|
||||||
|
|
@ -156,7 +151,6 @@ private:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t iStackSize;
|
|
||||||
int32_t iAttributes;
|
int32_t iAttributes;
|
||||||
OSThread *pThread;
|
OSThread *pThread;
|
||||||
uint8_t *pThreadStack;
|
uint8_t *pThreadStack;
|
||||||
|
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
#include "KernelFindExport.h"
|
|
||||||
#include <coreinit/cache.h>
|
|
||||||
#include <elf.h>
|
|
||||||
#include <kernel/kernel.h>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#define KernelGetLoadedRPL ((LOADED_RPL * (*) (uint32_t))(0xfff13524))
|
|
||||||
#define KernelGetRAMPID ((int32_t(*)(uint32_t *))(0xfff10ea0))
|
|
||||||
#define KernelSetRAMPID ((uint32_t(*)(uint32_t, uint32_t))(0xfff10cc0))
|
|
||||||
|
|
||||||
#define ELF_ST_TYPE(i) ((i) &0xf)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Based on the findClosestSymbol implementation. See:
|
|
||||||
* https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/kernel/cafe_kernel_loader.cpp#L251
|
|
||||||
*/
|
|
||||||
uint32_t FindExportKernel(const char *rplName, const char *functionName) {
|
|
||||||
if (rplName == nullptr || functionName == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
uint32_t result = 0;
|
|
||||||
uint32_t currentRamPID;
|
|
||||||
auto err = KernelGetRAMPID(¤tRamPID);
|
|
||||||
if (err != -1) {
|
|
||||||
// Switch to loader address space view.
|
|
||||||
KernelSetRAMPID(err, 2);
|
|
||||||
for (auto rpl = KernelGetLoadedRPL(0); rpl != nullptr; rpl = rpl->nextLoadedRpl) {
|
|
||||||
if (std::string_view(rpl->moduleNameBuffer) != rplName) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t textSectionIndex = 0xFFFFFFFF;
|
|
||||||
for (auto i = 0u; i < rpl->elfHeader.shnum; ++i) {
|
|
||||||
auto sectionAddress = rpl->sectionAddressBuffer[i];
|
|
||||||
if (!sectionAddress) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto sectionHeader = (ElfSectionHeader *) (((uint32_t) (rpl->sectionHeaderBuffer)) + rpl->elfHeader.shentsize * i);
|
|
||||||
|
|
||||||
if (auto shstrndx = rpl->elfHeader.shstrndx) {
|
|
||||||
auto shStrSection = (char *) (rpl->sectionAddressBuffer[shstrndx]);
|
|
||||||
auto sectionName = (shStrSection + sectionHeader->name);
|
|
||||||
if (std::string_view(sectionName) == ".text") {
|
|
||||||
textSectionIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto i = 0u; i < rpl->elfHeader.shnum; ++i) {
|
|
||||||
auto sectionAddress = rpl->sectionAddressBuffer[i];
|
|
||||||
if (!sectionAddress) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto sectionHeader = (ElfSectionHeader *) (((uint32_t) (rpl->sectionHeaderBuffer)) + rpl->elfHeader.shentsize * i);
|
|
||||||
|
|
||||||
if (sectionHeader->type == SHT_SYMTAB) {
|
|
||||||
auto strTab = rpl->sectionAddressBuffer[sectionHeader->link];
|
|
||||||
auto symTabEntSize = sectionHeader->entsize ? static_cast<uint32_t>(sectionHeader->entsize) : sizeof(ElfSymbol);
|
|
||||||
auto numSymbols = sectionHeader->size / symTabEntSize;
|
|
||||||
bool found = false;
|
|
||||||
for (auto j = 0u; j < numSymbols; ++j) {
|
|
||||||
auto symbol = (ElfSymbol *) (sectionAddress + j * symTabEntSize);
|
|
||||||
if (symbol->shndx == textSectionIndex && ELF_ST_TYPE(symbol->info) == STT_FUNC) {
|
|
||||||
auto symbolName = (const char *) (strTab + symbol->name);
|
|
||||||
if (std::string_view(symbolName) == functionName) {
|
|
||||||
result = symbol->value;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Switch back to "old" space address view
|
|
||||||
KernelSetRAMPID(err, currentRamPID);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" uint32_t SC_0x51(const char *rplname, const char *functionName);
|
|
||||||
|
|
||||||
uint32_t KernelFindExport(const std::string_view &rplName, const std::string_view &functionName) {
|
|
||||||
KernelPatchSyscall(0x51, (uint32_t) &FindExportKernel);
|
|
||||||
OSMemoryBarrier();
|
|
||||||
if (rplName.ends_with(".rpx") || rplName.ends_with(".rpl")) {
|
|
||||||
auto pureRPLName = std::string(rplName).substr(0, rplName.length() - 4);
|
|
||||||
return SC_0x51(pureRPLName.c_str(), functionName.data());
|
|
||||||
}
|
|
||||||
return SC_0x51(rplName.data(), functionName.data());
|
|
||||||
}
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <wut.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/loader/cafe_loader_rpl.h#L150
|
|
||||||
*/
|
|
||||||
struct ElfHeader {
|
|
||||||
uint8_t magic[4]; // File identification.
|
|
||||||
uint8_t fileClass; // File class.
|
|
||||||
uint8_t encoding; // Data encoding.
|
|
||||||
uint8_t elfVersion; // File version.
|
|
||||||
uint8_t abi; // OS/ABI identification.
|
|
||||||
uint8_t abiVersion; // OS/ABI version.
|
|
||||||
uint8_t pad[7];
|
|
||||||
|
|
||||||
uint16_t type; // Type of file (ET_*)
|
|
||||||
uint16_t machine; // Required architecture for this file (EM_*)
|
|
||||||
uint32_t version; // Must be equal to 1
|
|
||||||
uint32_t entry; // Address to jump to in order to start program
|
|
||||||
uint32_t phoff; // Program header table's file offset, in bytes
|
|
||||||
uint32_t shoff; // Section header table's file offset, in bytes
|
|
||||||
uint32_t flags; // Processor-specific flags
|
|
||||||
uint16_t ehsize; // Size of ELF header, in bytes
|
|
||||||
uint16_t phentsize; // Size of an entry in the program header table
|
|
||||||
uint16_t phnum; // Number of entries in the program header table
|
|
||||||
uint16_t shentsize; // Size of an entry in the section header table
|
|
||||||
uint16_t shnum; // Number of entries in the section header table
|
|
||||||
uint16_t shstrndx; // Sect hdr table index of sect name string table
|
|
||||||
};
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x00, magic);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x04, fileClass);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x05, encoding);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x06, elfVersion);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x07, abi);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x08, abiVersion);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x10, type);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x12, machine);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x14, version);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x18, entry);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x1C, phoff);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x20, shoff);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x24, flags);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x28, ehsize);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x2A, phentsize);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x2C, phnum);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x2E, shentsize);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x30, shnum);
|
|
||||||
WUT_CHECK_OFFSET(ElfHeader, 0x32, shstrndx);
|
|
||||||
WUT_CHECK_SIZE(ElfHeader, 0x34);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/loader/cafe_loader_rpl.h#L216
|
|
||||||
*/
|
|
||||||
struct ElfSectionHeader {
|
|
||||||
//! Section name (index into string table)
|
|
||||||
uint32_t name;
|
|
||||||
|
|
||||||
//! Section type (SHT_*)
|
|
||||||
uint32_t type;
|
|
||||||
|
|
||||||
//! Section flags (SHF_*)
|
|
||||||
uint32_t flags;
|
|
||||||
|
|
||||||
//! Address where section is to be loaded
|
|
||||||
uint32_t addr;
|
|
||||||
|
|
||||||
//! File offset of section data, in bytes
|
|
||||||
uint32_t offset;
|
|
||||||
|
|
||||||
//! Size of section, in bytes
|
|
||||||
uint32_t size;
|
|
||||||
|
|
||||||
//! Section type-specific header table index link
|
|
||||||
uint32_t link;
|
|
||||||
|
|
||||||
//! Section type-specific extra information
|
|
||||||
uint32_t info;
|
|
||||||
|
|
||||||
//! Section address alignment
|
|
||||||
uint32_t addralign;
|
|
||||||
|
|
||||||
//! Size of records contained within the section
|
|
||||||
uint32_t entsize;
|
|
||||||
};
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x00, name);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x04, type);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x08, flags);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x0C, addr);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x10, offset);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x14, size);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x18, link);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x1C, info);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x20, addralign);
|
|
||||||
WUT_CHECK_OFFSET(ElfSectionHeader, 0x24, entsize);
|
|
||||||
WUT_CHECK_SIZE(ElfSectionHeader, 0x28);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* https://github.com/decaf-emu/decaf-emu/blob/e8c9af3057a7d94f6e970406eb1ba1c37c87b4d1/src/libdecaf/src/cafe/loader/cafe_loader_rpl.h#L260
|
|
||||||
*/
|
|
||||||
struct ElfSymbol {
|
|
||||||
//! Symbol name (index into string table)
|
|
||||||
uint32_t name;
|
|
||||||
|
|
||||||
//! Value or address associated with the symbol
|
|
||||||
uint32_t value;
|
|
||||||
|
|
||||||
//! Size of the symbol
|
|
||||||
uint32_t size;
|
|
||||||
|
|
||||||
//! Symbol's type and binding attributes
|
|
||||||
uint8_t info;
|
|
||||||
|
|
||||||
//! Must be zero; reserved
|
|
||||||
uint8_t other;
|
|
||||||
|
|
||||||
//! Which section (header table index) it's defined in (SHN_*)
|
|
||||||
uint16_t shndx;
|
|
||||||
};
|
|
||||||
WUT_CHECK_OFFSET(ElfSymbol, 0x00, name);
|
|
||||||
WUT_CHECK_OFFSET(ElfSymbol, 0x04, value);
|
|
||||||
WUT_CHECK_OFFSET(ElfSymbol, 0x08, size);
|
|
||||||
WUT_CHECK_OFFSET(ElfSymbol, 0x0C, info);
|
|
||||||
WUT_CHECK_OFFSET(ElfSymbol, 0x0D, other);
|
|
||||||
WUT_CHECK_OFFSET(ElfSymbol, 0x0E, shndx);
|
|
||||||
WUT_CHECK_SIZE(ElfSymbol, 0x10);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* https://github.com/decaf-emu/decaf-emu/blob/6feb1be1db3938e6da2d4a65fc0a7a8599fc8dd6/src/libdecaf/src/cafe/loader/cafe_loader_loaded_rpl.h#L26
|
|
||||||
*/
|
|
||||||
struct LOADED_RPL {
|
|
||||||
WUT_UNKNOWN_BYTES(0x08);
|
|
||||||
char *moduleNameBuffer;
|
|
||||||
WUT_UNKNOWN_BYTES(0x10);
|
|
||||||
ElfHeader elfHeader;
|
|
||||||
void *sectionHeaderBuffer;
|
|
||||||
WUT_UNKNOWN_BYTES(0xA0);
|
|
||||||
uint32_t *sectionAddressBuffer;
|
|
||||||
WUT_UNKNOWN_BYTES(0x1C);
|
|
||||||
LOADED_RPL *nextLoadedRpl;
|
|
||||||
};
|
|
||||||
WUT_CHECK_OFFSET(LOADED_RPL, 0x08, moduleNameBuffer);
|
|
||||||
WUT_CHECK_OFFSET(LOADED_RPL, 0x1C, elfHeader);
|
|
||||||
WUT_CHECK_OFFSET(LOADED_RPL, 0x50, sectionHeaderBuffer);
|
|
||||||
WUT_CHECK_OFFSET(LOADED_RPL, 0xF4, sectionAddressBuffer);
|
|
||||||
WUT_CHECK_OFFSET(LOADED_RPL, 0x114, nextLoadedRpl);
|
|
||||||
WUT_CHECK_SIZE(LOADED_RPL, 0x118);
|
|
||||||
|
|
||||||
uint32_t KernelFindExport(const std::string_view &rplName, const std::string_view &functioName);
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "../PatchedFunctionData.h"
|
#include "../PatchedFunctionData.h"
|
||||||
#include "version.h"
|
|
||||||
#include <coreinit/memheap.h>
|
#include <coreinit/memheap.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define MODULE_VERSION "v0.2.3"
|
|
||||||
#define MODULE_VERSION_FULL MODULE_VERSION MODULE_VERSION_EXTRA
|
|
||||||
|
|
||||||
#define JUMP_HEAP_DATA_SIZE (32 * 1024)
|
#define JUMP_HEAP_DATA_SIZE (32 * 1024)
|
||||||
extern char gJumpHeapData[];
|
extern char gJumpHeapData[];
|
||||||
extern MEMHeapHandle gJumpHeapHandle;
|
extern MEMHeapHandle gJumpHeapHandle;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ extern "C" {
|
||||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
|
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
||||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "## WARN## ", "", FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
|
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
|
||||||
|
|
||||||
|
|
@ -53,7 +52,6 @@ extern "C" {
|
||||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
||||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "## WARN## ", "\n", FMT, ##ARGS)
|
|
||||||
|
|
||||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);
|
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
.global SC_0x51
|
|
||||||
SC_0x51:
|
|
||||||
li %r0, 0x5100
|
|
||||||
sc
|
|
||||||
blr
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#include <coreinit/cache.h>
|
|
||||||
#include <coreinit/memorymap.h>
|
|
||||||
#include <kernel/kernel.h>
|
|
||||||
|
|
||||||
bool ReadFromPhysicalAddress(uint32_t srcPhys, uint32_t *out) {
|
|
||||||
if (!out) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Check if patched instruction is still loaded.
|
|
||||||
volatile uint32_t currentInstruction;
|
|
||||||
|
|
||||||
auto currentInstructionAddress = (uint32_t) ¤tInstruction;
|
|
||||||
uint32_t currentInstructionAddressPhys;
|
|
||||||
if (currentInstructionAddress < 0x00800000 || currentInstructionAddress >= 0x01000000) {
|
|
||||||
currentInstructionAddressPhys = (uint32_t) OSEffectiveToPhysical(currentInstructionAddress);
|
|
||||||
} else {
|
|
||||||
currentInstructionAddressPhys = currentInstructionAddress + 0x30800000 - 0x00800000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentInstructionAddressPhys == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Save the instruction we will replace.
|
|
||||||
KernelCopyData(currentInstructionAddressPhys, srcPhys, 4);
|
|
||||||
DCFlushRange((void *) ¤tInstruction, 4);
|
|
||||||
*out = currentInstruction;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -10,5 +10,3 @@ template<class T, class... Args>
|
||||||
std::shared_ptr<T> make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(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)...));
|
return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadFromPhysicalAddress(uint32_t srcPhys, uint32_t *out);
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#define MODULE_VERSION_EXTRA ""
|
|
||||||
Loading…
Reference in New Issue
Block a user