Bug Fixes & Low-Level Improvements
Some checks failed
CI / Build (ARM64, Client) (push) Waiting to run
CI / Build (ARM64, Server) (push) Waiting to run
CI / Build (ARMHF, Client) (push) Waiting to run
CI / Build (ARMHF, Server) (push) Waiting to run
CI / Test (Client) (push) Waiting to run
CI / Test (Server) (push) Waiting to run
CI / Build Example Mods (push) Waiting to run
CI / Release (push) Blocked by required conditions
CI / Build (AMD64, Server) (push) Has been cancelled
CI / Build (AMD64, Client) (push) Has been cancelled
Some checks failed
CI / Build (ARM64, Client) (push) Waiting to run
CI / Build (ARM64, Server) (push) Waiting to run
CI / Build (ARMHF, Client) (push) Waiting to run
CI / Build (ARMHF, Server) (push) Waiting to run
CI / Test (Client) (push) Waiting to run
CI / Test (Server) (push) Waiting to run
CI / Build Example Mods (push) Waiting to run
CI / Release (push) Blocked by required conditions
CI / Build (AMD64, Server) (push) Has been cancelled
CI / Build (AMD64, Client) (push) Has been cancelled
This commit is contained in:
parent
180ba9dcaf
commit
a2b3bb128b
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -3,6 +3,7 @@
|
||||
set -e
|
||||
|
||||
# Create Output Directory
|
||||
cd "$(dirname "$0")"
|
||||
ROOT="$(pwd)"
|
||||
OUT="${ROOT}/out"
|
||||
rm -rf "${OUT}"
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
#include <LIEF/ELF.hpp>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include "patchelf.h"
|
||||
|
@ -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
|
||||
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
|
||||
#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
|
@ -5,6 +5,5 @@
|
||||
#include "util.h"
|
||||
#include "string.h"
|
||||
#include "exec.h"
|
||||
#include "elf.h"
|
||||
#include "home.h"
|
||||
#include "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);
|
||||
|
||||
|
46
libreborn/src/patch/code-block.cpp
Normal file
46
libreborn/src/patch/code-block.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#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;
|
||||
}
|
50
libreborn/src/patch/instruction.cpp
Normal file
50
libreborn/src/patch/instruction.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
#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);
|
||||
}
|
26
libreborn/src/patch/patch-internal.h
Normal file
26
libreborn/src/patch/patch-internal.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __arm__
|
||||
#error "Patching Code Is ARM Only"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// 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);
|
@ -1,214 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#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);
|
||||
}
|
163
libreborn/src/patch/patch.cpp
Normal file
163
libreborn/src/patch/patch.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <cstdint>
|
||||
#include <cerrno>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#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);
|
||||
}
|
50
libreborn/src/patch/segments.cpp
Normal file
50
libreborn/src/patch/segments.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include <vector>
|
||||
|
||||
#include <elf.h>
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include "patch-internal.h"
|
||||
|
||||
// Track Segments
|
||||
static std::vector<segment_data> &get_segments() {
|
||||
static std::vector<segment_data> 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);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#include <libreborn/elf.h>
|
||||
|
||||
// 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);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
#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
|
||||
|
5
mods/src/compat/compat-internal.h
Normal file
5
mods/src/compat/compat-internal.h
Normal file
@ -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();
|
@ -5,6 +5,7 @@
|
||||
#include <mods/compat/compat.h>
|
||||
#include <mods/screenshot/screenshot.h>
|
||||
#include <mods/init/init.h>
|
||||
#include "compat-internal.h"
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
@ -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
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <media-layer/core.h>
|
||||
#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
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <media-layer/core.h>
|
||||
#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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)) {
|
||||
|
@ -2,5 +2,8 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Change Directory
|
||||
cd "$(dirname "$0")/../"
|
||||
|
||||
# Run
|
||||
act push -W '.gitea/workflows/build.yml'
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Change Directory
|
||||
cd "$(dirname "$0")/../"
|
||||
|
||||
# Variables
|
||||
MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
|
||||
ARCH='host'
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user