From a2b3bb128bc2b87e70840f73ae75d20551699486 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Mon, 12 Feb 2024 00:44:38 -0500 Subject: [PATCH] Bug Fixes & Low-Level Improvements --- .gitea/workflows/build.yml | 4 +- docs/CHANGELOG.md | 1 + example-mods/build.sh | 1 + launcher/src/patchelf.cpp | 3 + libreborn/CMakeLists.txt | 4 +- libreborn/include/libreborn/elf.h | 22 --- libreborn/include/libreborn/libreborn.h | 1 - libreborn/include/libreborn/patch.h | 2 + libreborn/src/patch/code-block.cpp | 46 +++++ libreborn/src/patch/instruction.cpp | 50 ++++++ libreborn/src/patch/patch-internal.h | 26 +++ libreborn/src/patch/patch.c | 214 ------------------------ libreborn/src/patch/patch.cpp | 163 ++++++++++++++++++ libreborn/src/patch/segments.cpp | 50 ++++++ libreborn/src/util/elf.c | 28 ---- mods/src/compat/bcm_host.c | 3 +- mods/src/compat/compat-internal.h | 5 + mods/src/compat/compat.c | 5 + mods/src/compat/egl.c | 3 +- mods/src/compat/x11.c | 3 +- mods/src/death/death.cpp | 8 +- mods/src/init/init.c | 1 + mods/src/misc/misc.c | 1 - mods/src/options/options.cpp | 7 +- mods/src/server/server.cpp | 4 +- scripts/simulate-ci.sh | 3 + scripts/test.sh | 3 + symbols/src/misc/Strings.def | 5 +- 28 files changed, 378 insertions(+), 288 deletions(-) delete mode 100644 libreborn/include/libreborn/elf.h create mode 100644 libreborn/src/patch/code-block.cpp create mode 100644 libreborn/src/patch/instruction.cpp create mode 100644 libreborn/src/patch/patch-internal.h delete mode 100644 libreborn/src/patch/patch.c create mode 100644 libreborn/src/patch/patch.cpp create mode 100644 libreborn/src/patch/segments.cpp delete mode 100644 libreborn/src/util/elf.c create mode 100644 mods/src/compat/compat-internal.h diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index d8db7e42d..b9aed6af1 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -87,9 +87,7 @@ jobs: ./out/client/host/usr/bin/minecraft-pi-reborn-client --copy-sdk # Build Example Mods - name: Build Example Mods - run: | - cd example-mods - ./build.sh + run: ./example-mods/build.sh - name: Upload Artifacts uses: actions/upload-artifact@v3 with: diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 574b7c43c..17fd1d33a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -21,6 +21,7 @@ * Fix Item Dropping When Killing Players From The Server Console * Fix Furnace Visual Bug When Using Lava Bucket As Fuel * Add Splash Text To Start Screen +* `overwrite_calls` Now Scans VTables **2.5.3** * Add `Replace Block Highlight With Outline` Feature Flag (Enabled By Default) diff --git a/example-mods/build.sh b/example-mods/build.sh index 2fc61e649..8d3553d0d 100755 --- a/example-mods/build.sh +++ b/example-mods/build.sh @@ -3,6 +3,7 @@ set -e # Create Output Directory +cd "$(dirname "$0")" ROOT="$(pwd)" OUT="${ROOT}/out" rm -rf "${OUT}" diff --git a/launcher/src/patchelf.cpp b/launcher/src/patchelf.cpp index 8af919fd0..4441d314f 100644 --- a/launcher/src/patchelf.cpp +++ b/launcher/src/patchelf.cpp @@ -3,6 +3,9 @@ #include +#include +#include + #include #include "patchelf.h" diff --git a/libreborn/CMakeLists.txt b/libreborn/CMakeLists.txt index 208a4f348..3d560042e 100644 --- a/libreborn/CMakeLists.txt +++ b/libreborn/CMakeLists.txt @@ -5,7 +5,7 @@ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/libreborn") configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/libreborn/config.h" ESCAPE_QUOTES @ONLY) # Util -add_library(reborn-util SHARED src/util/elf.c src/util/exec.c src/util/string.c src/util/util.c src/util/log.c src/util/cp437.cpp) +add_library(reborn-util SHARED src/util/exec.c src/util/string.c src/util/util.c src/util/log.c src/util/cp437.cpp) target_include_directories( reborn-util PUBLIC @@ -25,7 +25,7 @@ endif() # Patch if(BUILD_ARM_COMPONENTS) - add_library(reborn-patch SHARED src/patch/patch.c) + add_library(reborn-patch SHARED src/patch/patch.cpp src/patch/segments.cpp src/patch/code-block.cpp src/patch/instruction.cpp) target_link_libraries(reborn-patch dl pthread reborn-util) target_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE) # Install diff --git a/libreborn/include/libreborn/elf.h b/libreborn/include/libreborn/elf.h deleted file mode 100644 index da46b0cbb..000000000 --- a/libreborn/include/libreborn/elf.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "log.h" -#include "exec.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Find And Iterate Over All Segments In Current Binary -typedef void (*segment_callback_t)(ElfW(Addr) section, ElfW(Word) size, void *data); -void iterate_segments(segment_callback_t callback, void *data); - -#ifdef __cplusplus -} -#endif diff --git a/libreborn/include/libreborn/libreborn.h b/libreborn/include/libreborn/libreborn.h index dcb514df0..4ef7cba70 100644 --- a/libreborn/include/libreborn/libreborn.h +++ b/libreborn/include/libreborn/libreborn.h @@ -5,6 +5,5 @@ #include "util.h" #include "string.h" #include "exec.h" -#include "elf.h" #include "home.h" #include "patch.h" diff --git a/libreborn/include/libreborn/patch.h b/libreborn/include/libreborn/patch.h index 13ef6a05f..38b6d827e 100644 --- a/libreborn/include/libreborn/patch.h +++ b/libreborn/include/libreborn/patch.h @@ -8,6 +8,8 @@ extern "C" { #ifdef REBORN_HAS_PATCH_CODE +void reborn_init_patch(); + void _overwrite_call(const char *file, int line, void *start, void *target); #define overwrite_call(start, target) _overwrite_call(__FILE__, __LINE__, start, target); diff --git a/libreborn/src/patch/code-block.cpp b/libreborn/src/patch/code-block.cpp new file mode 100644 index 000000000..ebc339660 --- /dev/null +++ b/libreborn/src/patch/code-block.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include "patch-internal.h" + +// Limit To 512 overwrite_calls() Uses +#define CODE_BLOCK_SIZE 4096 +static unsigned char *code_block = NULL; +#define CODE_SIZE 8 +static int code_block_remaining = CODE_BLOCK_SIZE; + +// Create Long Overwrite At Current Position +static void long_overwrite(void *start, void *target) { + unsigned char patch_data[4] = {0x04, 0xf0, 0x1f, 0xe5}; // "ldr pc, [pc, #-0x4]" + _patch(NULL, -1, start, patch_data); + _patch_address(NULL, -1, (void *) (((unsigned char *) start) + 4), target); +} +void *update_code_block(void *target) { + // BL Instructions can only access a limited portion of memory. + // So this allocates memory closer to the original instruction, + // that when run, will jump into the actual target. + if (code_block == NULL) { + code_block = (unsigned char *) mmap((void *) 0x200000, CODE_BLOCK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (code_block == MAP_FAILED) { + ERR("Unable To Allocate Code Block: %s", strerror(errno)); + } + DEBUG("Code Block Allocated At: 0x%08x", (uint32_t) code_block); + // Store Segment + segment_data data; + data.start = code_block; + data.end = (void *) (((uintptr_t) code_block) + CODE_BLOCK_SIZE); + data.is_executable = true; + data.is_writable = true; + add_segment(data); + } + if (code_block_remaining < CODE_SIZE) { + ERR("Maximum Amount Of overwrite_calls() Uses Reached"); + } + long_overwrite(code_block, target); + // Return + return code_block; +} +void increment_code_block() { + code_block = code_block + CODE_SIZE; + code_block_remaining = code_block_remaining - CODE_SIZE; +} diff --git a/libreborn/src/patch/instruction.cpp b/libreborn/src/patch/instruction.cpp new file mode 100644 index 000000000..cb5d2235a --- /dev/null +++ b/libreborn/src/patch/instruction.cpp @@ -0,0 +1,50 @@ +#include +#include "patch-internal.h" + +// Generate A BL Instruction +#define INSTRUCTION_RANGE 32000000 +static uint64_t nice_diff_64(uint64_t a, uint64_t b) { + if (a > b) { + return a - b; + } else { + return b - a; + } +} +uint32_t generate_bl_instruction(void *from, void *to, int use_b_instruction) { + // Check Instruction Range + if (nice_diff_64((uint64_t) to, (uint64_t) from) > INSTRUCTION_RANGE) { + IMPOSSIBLE(); + } + + // Create New Instruction + uint32_t instruction; + unsigned char *instruction_array = (unsigned char *) &instruction; + instruction_array[3] = use_b_instruction ? B_INSTRUCTION : BL_INSTRUCTION; + + // Determine PC + unsigned char *pc = ((unsigned char *) from) + 8; + int32_t offset = (int32_t) to - (int32_t) pc; + int32_t target = offset >> 2; + + // Set Instruction Offset + unsigned char *target_array = (unsigned char *) ⌖ + instruction_array[0] = target_array[0]; + instruction_array[1] = target_array[1]; + instruction_array[2] = target_array[2]; + + // Return + return instruction; +} + +// Extract Target Address From B(L) Instruction +void *extract_from_bl_instruction(unsigned char *from) { + // Calculate PC + unsigned char *pc = ((unsigned char *) from) + 8; + + // Extract Offset From Instruction + int32_t target = *(int32_t *) from; + target = (target << 8) >> 8; + int32_t offset = target << 2; + // Add PC To Offset + return (void *) (pc + offset); +} diff --git a/libreborn/src/patch/patch-internal.h b/libreborn/src/patch/patch-internal.h new file mode 100644 index 000000000..11f60472f --- /dev/null +++ b/libreborn/src/patch/patch-internal.h @@ -0,0 +1,26 @@ +#pragma once + +#ifndef __arm__ +#error "Patching Code Is ARM Only" +#endif + +#include + +// Segments +struct segment_data { + void *start; + void *end; + bool is_executable; + bool is_writable; +}; +__attribute__((visibility("internal"))) segment_data &get_data_for_addr(void *addr); +__attribute__((visibility("internal"))) void add_segment(segment_data data); + +// Code Block +__attribute__((visibility("internal"))) void *update_code_block(void *target); +__attribute__((visibility("internal"))) void increment_code_block(); + +// BL Instruction Magic Number +#define BL_INSTRUCTION 0xeb +#define B_INSTRUCTION 0xea +__attribute__((visibility("internal"))) uint32_t generate_bl_instruction(void *from, void *to, int use_b_instruction); diff --git a/libreborn/src/patch/patch.c b/libreborn/src/patch/patch.c deleted file mode 100644 index 488cc4014..000000000 --- a/libreborn/src/patch/patch.c +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef __arm__ -#error "Patching Code Is ARM Only" -#endif - -// BL Instruction Magic Number -#define BL_INSTRUCTION 0xeb -#define B_INSTRUCTION 0xea - -// Generate A BL Instruction -static uint32_t generate_bl_instruction(void *from, void *to, int use_b_instruction) { - if (abs(((int32_t) to) - ((int32_t) from)) > 32000000) { - IMPOSSIBLE(); - } - - uint32_t instruction; - unsigned char *instruction_array = (unsigned char *) &instruction; - - instruction_array[3] = use_b_instruction ? B_INSTRUCTION : BL_INSTRUCTION; - - unsigned char *pc = ((unsigned char *) from) + 8; - int32_t offset = (int32_t) to - (int32_t) pc; - int32_t target = offset >> 2; - - unsigned char *target_array = (unsigned char *) ⌖ - instruction_array[0] = target_array[0]; - instruction_array[1] = target_array[1]; - instruction_array[2] = target_array[2]; - - return instruction; -} - -// Run For Every .text Section -static int _overwrite_calls_within_internal(const char *file, int line, void *from, void *to, void *target, void *replacement) { - int found = 0; - for (uint32_t i = (uint32_t) from; i < (uint32_t) to; i = i + 4) { - unsigned char *addr = (unsigned char *) i; - int use_b_instruction = addr[3] == B_INSTRUCTION; - // Check If Instruction is B Or BL - if (addr[3] == BL_INSTRUCTION || use_b_instruction) { - uint32_t check_instruction = generate_bl_instruction(addr, target, use_b_instruction); - unsigned char *check_instruction_array = (unsigned char *) &check_instruction; - // Check If Instruction Calls Target - if (addr[0] == check_instruction_array[0] && addr[1] == check_instruction_array[1] && addr[2] == check_instruction_array[2]) { - // Patch Instruction - uint32_t new_instruction = generate_bl_instruction(addr, replacement, use_b_instruction); - _patch(file, line, addr, (unsigned char *) &new_instruction); - found++; - } - } - } - return found; -} -struct overwrite_data { - const char *file; - int line; - void *target; - void *replacement; - int found; -}; -static void overwrite_calls_callback(ElfW(Addr) section_addr, ElfW(Word) size, void *data) { - struct overwrite_data *args = (struct overwrite_data *) data; - void *section = (void *) section_addr; - args->found += _overwrite_calls_within_internal(args->file, args->line, section, (void *) (section_addr + size), args->target, args->replacement); -} - -// Limit To 512 overwrite_calls() Uses -#define CODE_BLOCK_SIZE 4096 -static unsigned char *code_block = NULL; -#define CODE_SIZE 8 -static int code_block_remaining = CODE_BLOCK_SIZE; - -static void _long_overwrite(void *start, void *target) { - unsigned char patch_data[4] = {0x04, 0xf0, 0x1f, 0xe5}; // "ldr pc, [pc, #-0x4]" - - _patch(NULL, -1, start, patch_data); - _patch_address(NULL, -1, (void *) (((unsigned char *) start) + 4), target); -} -static void update_code_block(void *target) { - // BL Instructions Can Only Access A Limited Portion of Memory, So This Allocates Memory Closer To The Original Instruction, That When Run, Will Jump Into The Actual Target - if (code_block == NULL) { - code_block = mmap((void *) 0x200000, CODE_BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (code_block == MAP_FAILED) { - ERR("Unable To Allocate Code Block: %s", strerror(errno)); - } - DEBUG("Code Block Allocated At: 0x%08x", (uint32_t) code_block); - } - if (code_block_remaining < CODE_SIZE) { - ERR("Maximum Amount Of overwrite_calls() Uses Reached"); - } - _long_overwrite(code_block, target); -} -static void increment_code_block() { - code_block = code_block + CODE_SIZE; - code_block_remaining = code_block_remaining - CODE_SIZE; -} - -// Overwrite Specific B(L) Instruction -static void _overwrite_call_internal(const char *file, int line, void *start, void *target, int use_b_instruction) { - // Add New Target To Code Block - update_code_block(target); - - // Patch - uint32_t new_instruction = generate_bl_instruction(start, code_block, use_b_instruction); - _patch(file, line, start, (unsigned char *) &new_instruction); - - // Increment Code Block Position - increment_code_block(); -} -void _overwrite_call(const char *file, int line, void *start, void *target) { - int use_b_instruction = ((unsigned char *) start)[3] == B_INSTRUCTION; - _overwrite_call_internal(file, line, start, target, use_b_instruction); -} - -// Overwrite All B(L) Intrusctions That Target The Specified Address -#define NO_CALLSITE_ERROR "(%s:%i) Unable To Find Callsites For 0x%08x" -void _overwrite_calls(const char *file, int line, void *start, void *target) { - // Add New Target To Code Block - update_code_block(target); - - struct overwrite_data data; - data.file = file; - data.line = line; - data.target = start; - data.replacement = code_block; - data.found = 0; - - iterate_segments(overwrite_calls_callback, &data); - - // Increment Code Block Position - increment_code_block(); - - // Check - if (data.found < 1) { - ERR(NO_CALLSITE_ERROR, file, line, (uint32_t) start); - } -} -void _overwrite_calls_within(const char *file, int line, void *from /* inclusive */, void *to /* exclusive */, void *target, void *replacement) { - // Add New Target To Code Block - update_code_block(replacement); - - // Patch - int found = _overwrite_calls_within_internal(file, line, from, to, target, code_block); - // Check - if (found < 1) { - ERR(NO_CALLSITE_ERROR, file, line, (uint32_t) target); - } - - // Increment Code Block Position - increment_code_block(); -} - -// Extract Target Address From B(L) Instruction -void *extract_from_bl_instruction(unsigned char *from) { - unsigned char *pc = ((unsigned char *) from) + 8; - - int32_t target = *(int32_t *) from; - target = (target << 8) >> 8; - - int32_t offset = target << 2; - - return (void *) (pc + offset); -} - -// Overwrite Function -void _overwrite(const char *file, int line, void *start, void *target) { - _overwrite_call_internal(file, line, start, target, 1); -} - -// Print Patch Debug Data -#define PATCH_PRINTF(file, line, start, str) if (file != NULL) DEBUG("(%s:%i): Patching (0x%08x) - " str ": 0x%02x 0x%02x 0x%02x 0x%02x", file, line, (uint32_t) start, data[0], data[1], data[2], data[3]); - -// Patch Instruction -void _patch(const char *file, int line, void *start, unsigned char patch[4]) { - if (((uint32_t) start) % 4 != 0) { - ERR("Invalid Address"); - } - - size_t page_size = sysconf(_SC_PAGESIZE); - uintptr_t end = ((uintptr_t) start) + 4; - uintptr_t page_start = ((uintptr_t) start) & -page_size; - // Allow Writing To Code Memory - mprotect((void *) page_start, end - page_start, PROT_READ | PROT_WRITE | PROT_EXEC); // PROT_EXEC Is Needed Because Other Code In The Page May Be Being Executed - - unsigned char *data = (unsigned char *) start; - - PATCH_PRINTF(file, line, start, "original"); - - memcpy(data, patch, 4); - - PATCH_PRINTF(file, line, start, "result"); - - // Reset Code Memory Permissions - mprotect((void *) page_start, end - page_start, PROT_READ | PROT_EXEC); - - // Clear ARM Instruction Cache - __clear_cache(start, (void *) end); -} - -// Patch Address -void _patch_address(const char *file, int line, void *start, void *target) { - uint32_t addr = (uint32_t) target; - unsigned char *patch_data = (unsigned char *) &addr; - _patch(file, line, start, patch_data); -} diff --git a/libreborn/src/patch/patch.cpp b/libreborn/src/patch/patch.cpp new file mode 100644 index 000000000..c60a08c0b --- /dev/null +++ b/libreborn/src/patch/patch.cpp @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "patch-internal.h" + +// Overwrite Specific B(L) Instruction +static void _overwrite_call_internal(const char *file, int line, void *start, void *target, int use_b_instruction) { + // Add New Target To Code Block + void *code_block = update_code_block(target); + + // Patch + uint32_t new_instruction = generate_bl_instruction(start, code_block, use_b_instruction); + _patch(file, line, start, (unsigned char *) &new_instruction); + + // Increment Code Block Position + increment_code_block(); +} +void _overwrite_call(const char *file, int line, void *start, void *target) { + int use_b_instruction = ((unsigned char *) start)[3] == B_INSTRUCTION; + _overwrite_call_internal(file, line, start, target, use_b_instruction); +} + +// .rodata Information +#define RODATA_START 0x1020c8 +#define RODATA_END 0x11665c +// Search And Patch VTables Containing Function +static int _patch_vtables(const char *file, int line, void *target, void *replacement) { + int found = 0; + for (uintptr_t i = RODATA_START; i < RODATA_END; i = i + 4) { + uint32_t *addr = (uint32_t *) i; + if (*addr == (uintptr_t) target) { + // Found VTable Entry + _patch_address(file, line, addr, replacement); + found++; + } + } + return found; +} + +// Patch Calls Within Range +static int _overwrite_calls_within_internal(const char *file, int line, void *from, void *to, void *target, void *replacement) { + int found = 0; + for (uintptr_t i = (uintptr_t) from; i < (uintptr_t) to; i = i + 4) { + unsigned char *addr = (unsigned char *) i; + int use_b_instruction = addr[3] == B_INSTRUCTION; + // Check If Instruction is B Or BL + if (addr[3] == BL_INSTRUCTION || use_b_instruction) { + uint32_t check_instruction = generate_bl_instruction(addr, target, use_b_instruction); + unsigned char *check_instruction_array = (unsigned char *) &check_instruction; + // Check If Instruction Calls Target + if (addr[0] == check_instruction_array[0] && addr[1] == check_instruction_array[1] && addr[2] == check_instruction_array[2]) { + // Patch Instruction + uint32_t new_instruction = generate_bl_instruction(addr, replacement, use_b_instruction); + _patch(file, line, addr, (unsigned char *) &new_instruction); + found++; + } + } + } + return found; +} + +// .text Information +#define TEXT_START 0xde60 +#define TEXT_END 0x1020c0 +// Overwrite All B(L) Intrusctions That Target The Specified Address +#define NO_CALLSITE_ERROR "(%s:%i) Unable To Find Callsites For %p" +void _overwrite_calls(const char *file, int line, void *start, void *target) { + // Add New Target To Code Block + void *code_block = update_code_block(target); + + // Patch Code + int found = _overwrite_calls_within_internal(file, line, (void *) TEXT_START, (void *) TEXT_END, start, code_block); + // Patch VTables + found += _patch_vtables(file, line, start, code_block); + + // Increment Code Block Position + increment_code_block(); + + // Check + if (found < 1) { + ERR(NO_CALLSITE_ERROR, file, line, start); + } +} +void _overwrite_calls_within(const char *file, int line, void *from /* inclusive */, void *to /* exclusive */, void *target, void *replacement) { + // Add New Target To Code Block + void *code_block = update_code_block(replacement); + + // Patch + int found = _overwrite_calls_within_internal(file, line, from, to, target, code_block); + // Check + if (found < 1) { + ERR(NO_CALLSITE_ERROR, file, line, target); + } + + // Increment Code Block Position + increment_code_block(); +} + +// Overwrite Function +void _overwrite(const char *file, int line, void *start, void *target) { + // Replace the function's start with a call + // to the replacement function. + _overwrite_call_internal(file, line, start, target, 1); +} + +// Print Patch Debug Data +#define PATCH_PRINTF(file, line, start, str) if (file != NULL) DEBUG("(%s:%i): Patching (%p) - " str ": %02x %02x %02x %02x", file, line, start, data[0], data[1], data[2], data[3]); + +// Patch Instruction +static void safe_mprotect(void *addr, size_t len, int prot) { + long page_size = sysconf(_SC_PAGESIZE); + long diff = ((uintptr_t) addr) % page_size; + void *aligned_addr = (void *) (((uintptr_t) addr) - diff); + size_t aligned_len = len + diff; + int ret = mprotect(aligned_addr, aligned_len, prot); + if (ret == -1) { + ERR("Unable To Set Permissions: %p: %s", addr, strerror(errno)); + } +} +void _patch(const char *file, int line, void *start, unsigned char patch[4]) { + if (((uint32_t) start) % 4 != 0) { + ERR("Invalid Address"); + } + + // Get Current Permissions + segment_data &segment_data = get_data_for_addr(start); + int prot = PROT_READ; + if (segment_data.is_executable) { + prot |= PROT_EXEC; + } + if (segment_data.is_writable) { + prot |= PROT_WRITE; + } + + // Allow Writing To Code Memory + uint32_t size = 4; + safe_mprotect(start, size, prot | PROT_WRITE); + + // Patch + unsigned char *data = (unsigned char *) start; + PATCH_PRINTF(file, line, start, "original"); + memcpy(data, patch, 4); + PATCH_PRINTF(file, line, start, "result"); + + // Reset Code Memory Permissions + safe_mprotect(start, size, prot); + + // Clear ARM Instruction Cache + __clear_cache(start, (void *) (((uintptr_t) start) + size)); +} + +// Patch Address +void _patch_address(const char *file, int line, void *start, void *target) { + uint32_t addr = (uint32_t) target; + unsigned char *patch_data = (unsigned char *) &addr; + _patch(file, line, start, patch_data); +} diff --git a/libreborn/src/patch/segments.cpp b/libreborn/src/patch/segments.cpp new file mode 100644 index 000000000..0f100f514 --- /dev/null +++ b/libreborn/src/patch/segments.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include +#include + +#include +#include "patch-internal.h" + +// Track Segments +static std::vector &get_segments() { + static std::vector data; + return data; +} + +// Functions +segment_data &get_data_for_addr(void *addr) { + for (segment_data &data : get_segments()) { + if (addr >= data.start && addr < data.end) { + return data; + } + } + ERR("Address Not Part Of Main Program: %p", addr); +} +void add_segment(segment_data data) { + get_segments().push_back(data); +} + +// Init +void reborn_init_patch() { + dl_iterate_phdr([](struct dl_phdr_info *info, __attribute__((unused)) size_t size, __attribute__((unused)) void *user_data) { + // Only Search Current Program + if (strcmp(info->dlpi_name, "") == 0) { + for (int i = 0; i < info->dlpi_phnum; i++) { + // Only Loaded Segemnts + if (info->dlpi_phdr[i].p_type == PT_LOAD) { + // Store + segment_data data; + data.start = (void *) (info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); + data.end = (void *) (((uintptr_t) data.start) + info->dlpi_phdr[i].p_memsz); + data.is_executable = info->dlpi_phdr[i].p_flags & PF_X; + data.is_writable = info->dlpi_phdr[i].p_flags & PF_W; + add_segment(data); + } + } + } + // Return + return 0; + }, nullptr); +} diff --git a/libreborn/src/util/elf.c b/libreborn/src/util/elf.c deleted file mode 100644 index f60b273b6..000000000 --- a/libreborn/src/util/elf.c +++ /dev/null @@ -1,28 +0,0 @@ -#include - -// Find And Iterate Over All Segments In Current Binary -typedef struct { - segment_callback_t callback; - void *data; -} dl_iterate_callback_data; -static int dl_iterate_callback(struct dl_phdr_info *info, __attribute__((unused)) size_t size, void *data) { - dl_iterate_callback_data *callback_data = (dl_iterate_callback_data *) data; - // Only Search Current Program - if (strcmp(info->dlpi_name, "") == 0) { - for (int i = 0; i < info->dlpi_phnum; i++) { - // Only Executable Segemnts - if (info->dlpi_phdr[i].p_type == PT_LOAD && (info->dlpi_phdr[i].p_flags & PF_X) != 0) { - // Callback - (*callback_data->callback)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr, info->dlpi_phdr[i].p_memsz, callback_data->data); - } - } - } - return 0; -} -void iterate_segments(segment_callback_t callback, void *data) { - dl_iterate_callback_data callback_data = { - .callback = callback, - .data = data - }; - dl_iterate_phdr(dl_iterate_callback, (void *) &callback_data); -} diff --git a/mods/src/compat/bcm_host.c b/mods/src/compat/bcm_host.c index 954c4272e..d236abd0a 100644 --- a/mods/src/compat/bcm_host.c +++ b/mods/src/compat/bcm_host.c @@ -1,4 +1,5 @@ #include +#include "compat-internal.h" // Do Nothing Function static void do_nothing() { @@ -6,7 +7,7 @@ static void do_nothing() { } // Patch bcm_host Calls -__attribute__((constructor)) static void patch_bcm_host_calls() { +void _patch_bcm_host_calls() { // Disable bcm_host Calls overwrite_call((void *) 0xdfec, (void *) do_nothing); // bcm_host_init overwrite_call((void *) 0x12418, (void *) do_nothing); // bcm_host_deinit diff --git a/mods/src/compat/compat-internal.h b/mods/src/compat/compat-internal.h new file mode 100644 index 000000000..11107b23e --- /dev/null +++ b/mods/src/compat/compat-internal.h @@ -0,0 +1,5 @@ +#pragma once + +__attribute__((visibility("internal"))) void _patch_egl_calls(); +__attribute__((visibility("internal"))) void _patch_x11_calls(); +__attribute__((visibility("internal"))) void _patch_bcm_host_calls(); diff --git a/mods/src/compat/compat.c b/mods/src/compat/compat.c index fd016cb28..cff6a39c1 100644 --- a/mods/src/compat/compat.c +++ b/mods/src/compat/compat.c @@ -5,6 +5,7 @@ #include #include #include +#include "compat-internal.h" #include @@ -120,6 +121,10 @@ void init_compat() { act_sigterm.sa_flags = SA_RESTART; act_sigterm.sa_handler = &exit_handler; sigaction(SIGTERM, &act_sigterm, NULL); + // Patches + _patch_egl_calls(); + _patch_x11_calls(); + _patch_bcm_host_calls(); } // Store Exit Requests diff --git a/mods/src/compat/egl.c b/mods/src/compat/egl.c index 42fbf0483..5c9d01c38 100644 --- a/mods/src/compat/egl.c +++ b/mods/src/compat/egl.c @@ -2,6 +2,7 @@ #include #include +#include "compat-internal.h" // Functions That Have Their Return Values Used static EGLSurface eglCreateWindowSurface_injection(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLConfig config, __attribute__((unused)) NativeWindowType native_window, __attribute__((unused)) EGLint const *attrib_list) { @@ -20,7 +21,7 @@ static EGLBoolean eglSwapBuffers_injection(__attribute__((unused)) EGLDisplay di } // Patch EGL Calls -__attribute__((constructor)) static void patch_egl_calls() { +void _patch_egl_calls() { // Disable EGL Calls unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" patch((void *) 0x1250c, nop_patch); // eglTerminate diff --git a/mods/src/compat/x11.c b/mods/src/compat/x11.c index 27dbb73f9..ab87d308d 100644 --- a/mods/src/compat/x11.c +++ b/mods/src/compat/x11.c @@ -2,6 +2,7 @@ #include #include +#include "compat-internal.h" // Functions That Have Their Return Values Used static int XTranslateCoordinates_injection(__attribute__((unused)) void *display, __attribute__((unused)) XID src_w, __attribute__((unused)) XID dest_w, int src_x, int src_y, int *dest_x_return, int *dest_y_return, __attribute__((unused)) XID *child_return) { @@ -21,7 +22,7 @@ static int XGetWindowAttributes_injection(__attribute__((unused)) void *display, } // Patch X11 Calls -__attribute__((constructor)) static void patch_x11_calls() { +void _patch_x11_calls() { // Disable X11 Calls overwrite_call((void *) 0x132a4, (void *) XGetWindowAttributes_injection); // XGetWindowAttributes overwrite_call((void *) 0x132d4, (void *) XTranslateCoordinates_injection); // XTranslateCoordinates diff --git a/mods/src/death/death.cpp b/mods/src/death/death.cpp index 606ece2f4..2b88ba643 100644 --- a/mods/src/death/death.cpp +++ b/mods/src/death/death.cpp @@ -130,13 +130,13 @@ void init_death() { patch_address(LocalPlayer_die_vtable_addr, (void *) LocalPlayer_die_injection); patch_address(ServerPlayer_actuallyHurt_vtable_addr, (void *) ServerPlayer_actuallyHurt_injection); patch_address(LocalPlayer_actuallyHurt_vtable_addr, (void *) LocalPlayer_actuallyHurt_injection); - patch_address(Mob_hurt_vtable_addr, (void *) Mob_hurt_injection); overwrite_calls((void *) Mob_hurt_non_virtual, (void *) Mob_hurt_injection); } + // Fix TNT - // This changes PrimedTnt_explode from Level_explode(NULL, x, y, z, 3.1f) to Level_explode(this, x, y, z, 3.1f) - unsigned char cpy_r1_r0_patch[4] = {0x00, 0x10, 0xa0, 0xe1}; // "cpy r1,r0" + // This changes PrimedTnt_explode from Level::explode(NULL, x, y, z, 3.1f) to Level::explode(this, x, y, z, 3.1f) + unsigned char cpy_r1_r0_patch[4] = {0x00, 0x10, 0xa0, 0xe1}; // "cpy r1, r0" patch((void *) 0x87998, cpy_r1_r0_patch); - unsigned char ldr_r0_24_patch[4] = {0x24, 0x00, 0x90, 0xe5}; // "ldr r0,[r0,#0x24]" + unsigned char ldr_r0_24_patch[4] = {0x24, 0x00, 0x90, 0xe5}; // "ldr r0, [r0, #0x24]" patch((void *) 0x8799c, ldr_r0_24_patch); } diff --git a/mods/src/init/init.c b/mods/src/init/init.c index 94b549189..2d2979411 100644 --- a/mods/src/init/init.c +++ b/mods/src/init/init.c @@ -5,6 +5,7 @@ __attribute__((constructor)) static void init(int argc, char *argv[]) { media_ensure_loaded(); + reborn_init_patch(); run_tests(); init_symbols(); init_version(); diff --git a/mods/src/misc/misc.c b/mods/src/misc/misc.c index aac9da410..64504ad10 100644 --- a/mods/src/misc/misc.c +++ b/mods/src/misc/misc.c @@ -799,7 +799,6 @@ void init_misc() { // Animation overwrite_calls((void *) ContainerMenu_constructor, (void *) ContainerMenu_injection); overwrite_calls((void *) ContainerMenu_destructor_complete_non_virtual, (void *) ContainerMenu_destructor_injection); - patch_address(ContainerMenu_destructor_complete_vtable_addr, (void *) ContainerMenu_destructor_injection); } patch_address((void *) 0x115b48, (void *) ChestTileEntity_shouldSave_injection); diff --git a/mods/src/options/options.cpp b/mods/src/options/options.cpp index 8e4019bd2..2661a2c01 100644 --- a/mods/src/options/options.cpp +++ b/mods/src/options/options.cpp @@ -215,9 +215,7 @@ void _init_options_cpp() { // Replace "feedback_vibration" Loading/Saving With "gfx_ao" { // Replace String - static const char *new_feedback_vibration_options_txt_name = "gfx_ao"; - patch_address((void *) Strings_feedback_vibration_options_txt_name_1_pointer, (void *) &new_feedback_vibration_options_txt_name); - patch_address((void *) Strings_feedback_vibration_options_txt_name_2_pointer, (void *) &new_feedback_vibration_options_txt_name); + patch_address((void *) Strings_feedback_vibration_options_txt_name_pointer, (void *) "gfx_ao"); // Loading unsigned char offset = (unsigned char) offsetof(Options, ambient_occlusion); unsigned char gfx_ao_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET" @@ -230,8 +228,7 @@ void _init_options_cpp() { // Replace "gfx_lowquality" Loading With "gfx_anaglyph" { // Replace String - static const char *new_gfx_lowquality_options_txt_name = "gfx_anaglyph"; - patch_address((void *) Strings_gfx_lowquality_options_txt_name_pointer, (void *) &new_gfx_lowquality_options_txt_name); + patch_address((void *) Strings_gfx_lowquality_options_txt_name_pointer, (void *) "gfx_anaglyph"); // Loading unsigned char offset = (unsigned char) offsetof(Options, anaglyph_3d); unsigned char gfx_anaglyph_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET" diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp index 31917ccbf..9af3d2eb9 100644 --- a/mods/src/server/server.cpp +++ b/mods/src/server/server.cpp @@ -433,7 +433,7 @@ static bool is_ip_in_blacklist(const char *ip) { } // Ban Players -static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) unsigned char *rakpeer, const char *ip) { +static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) RakNet_RakPeer *rakpeer, const char *ip) { // Check List bool ret = is_ip_in_blacklist(ip); if (is_whitelist()) { @@ -576,7 +576,7 @@ static void server_init() { unsigned char max_players_patch[4] = {get_max_players(), 0x30, 0xa0, 0xe3}; // "mov r3, #MAX_PLAYERS" patch((void *) 0x166d0, max_players_patch); // Custom Banned IP List - overwrite((void *) RakNet_RakPeer_IsBanned_non_virtual, (void *) RakNet_RakPeer_IsBanned_injection); + patch_address(RakNet_RakPeer_IsBanned_vtable_addr, (void *) RakNet_RakPeer_IsBanned_injection); // Show The MineCon Icon Next To MOTD In Server List if (get_server_properties().get_bool("show-minecon-badge", DEFAULT_SHOW_MINECON_BADGE)) { diff --git a/scripts/simulate-ci.sh b/scripts/simulate-ci.sh index c16e0c875..77c194a10 100755 --- a/scripts/simulate-ci.sh +++ b/scripts/simulate-ci.sh @@ -2,5 +2,8 @@ set -e +# Change Directory +cd "$(dirname "$0")/../" + # Run act push -W '.gitea/workflows/build.yml' diff --git a/scripts/test.sh b/scripts/test.sh index 8f208fa5c..57299fb41 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -2,6 +2,9 @@ set -e +# Change Directory +cd "$(dirname "$0")/../" + # Variables MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')" ARCH='host' diff --git a/symbols/src/misc/Strings.def b/symbols/src/misc/Strings.def index 10898c00e..34d29536c 100644 --- a/symbols/src/misc/Strings.def +++ b/symbols/src/misc/Strings.def @@ -4,9 +4,8 @@ static-property char *default_username = 0x18fd4; // StevePi static-property char *minecraft_pi_version = 0x39d94; // v0.1.1 alpha static-property char *options_txt_path = 0x19bc8; // options.txt static-property char *options_txt_fopen_mode_when_loading = 0x19d24; // w -static-property char **feedback_vibration_options_txt_name_1 = 0x198a0; // feedback_vibration -static-property char **feedback_vibration_options_txt_name_2 = 0x194bc; // feedback_vibration -static-property char **gfx_lowquality_options_txt_name = 0x194c4; // gfx_lowquality +static-property char *feedback_vibration_options_txt_name = 0x135d70; // feedback_vibration +static-property char *gfx_lowquality_options_txt_name = 0x135d88; // gfx_lowquality static-property char *classic_create_button_text = 0x39bec; // Create static-property-array char creative_mode_description = 0x104492; // Unlimited resources and flying static-property-array char survival_mode_description = 0x104470; // Mobs, health and gather resources