Patch Callsites Instead Of Reverting Patches

This commit is contained in:
TheBrokenRail 2020-11-09 19:49:41 -05:00
parent 55feb34251
commit 50af254e2a
7 changed files with 161 additions and 124 deletions

View File

@ -13,7 +13,7 @@ Minecraft: Pi Edition was compiled with an old version of GCC, so when interacti
## ``libcore.so`` API
Header files and the shared library can be download from [Jenkins](https://jenkins.thebrokenrail.com/job/minecraft-pi-docker/job/master/lastSuccessfulBuild/artifact/out/lib).
### ``void *overwrite(void *start, void *target)``
### ``void overwrite(void *start, void *target)``
This method replaces a function with another function.
#### Parameters
@ -21,7 +21,7 @@ This method replaces a function with another function.
- **target:** The function you are replacing it with.
#### Return Value
The original contents of the function.
None
#### Warning
This should never be used on functions that are only 1 byte long because it overwrites 2 bytes.
@ -37,16 +37,19 @@ __attribute__((constructor)) static void init() {
}
```
### ``void revert_overwrite(void *start, void *original)``
This allows you to revert ``overwrite()``. This can be used to call the original version of a function.
### ``void overwrite_calls(void *start, void *original)``
This allows you to overwrite all calls of a function rather than the function itself. This allows you to call the original function. However, this does not effect VTables.
#### Parameters
- **start:** The function that was overwritten.
- **original:** The return value of ``overwrite()``.
- **start:** The function call to overwrite;
- **target:** The function call you are replacing it with.
#### Return Value
None
#### Warning
This method can only be safely used 512 times in total.
#### Example
```c
typedef int (*func_t)(int a, int b);
@ -54,15 +57,13 @@ static func_t func = (func_t) 0xabcde;
static void *func_original = NULL;
static int func_injection(int a, int b) {
revert_overwrite((void *) func, func_original);
(*func)(a, b);
revert_overwrite((void *) func, func_original);
return a + 4;
}
__attribute__((constructor)) static void init() {
func_original = overwrite((void *) func, func_injection);
overwrite_calls((void *) func, func_injection);
}
```

View File

@ -30,10 +30,11 @@ extern "C" {
#define INFO(msg, ...) fprintf(stderr, "[INFO]: " msg "\n", __VA_ARGS__);
#define ERR(msg, ...) fprintf(stderr, "[ERR]: " msg "\n", __VA_ARGS__); exit(1);
void *_overwrite(const char *file, int line, void *start, void *target);
#define overwrite(start, target) _overwrite(__FILE__, __LINE__, start, target);
void _overwrite_calls(const char *file, int line, void *start, void *target);
#define overwrite_calls(start, target) _overwrite_calls(__FILE__, __LINE__, start, target);
void revert_overwrite(void *start, void *original);
void _overwrite(const char *file, int line, void *start, void *target);
#define overwrite(start, target) _overwrite(__FILE__, __LINE__, start, target);
void _patch(const char *file, int line, void *start, unsigned char patch[]);
#define patch(start, patch) _patch(__FILE__, __LINE__, start, patch);

View File

@ -1,55 +1,132 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
#include <elf.h>
#include <libcore/libcore.h>
#define PATCH_PRINTF(file, line, start, str) if (file != NULL) fprintf(stderr, "[PATCH]: (%s:%i) Patching (0x%04x) - "str": 0x%02x 0x%02x 0x%02x 0x%02x\n", file, line, (uint32_t) start, data[0], data[1], data[2], data[3]);
// Find And Iterate Over All .text Sections In Current Binary
typedef void (*text_section_callback)(void *section, Elf32_Word size, void *data);
static void iterate_text_section(text_section_callback callback, void *data) {
// Load Main Binary
char *real_path = realpath("/proc/self/exe", NULL);
FILE *file_obj = fopen(real_path, "rb");
free(real_path);
#define ORIGINAL_SIZE 8
// Get File Size
fseek(file_obj, 0L, SEEK_END);
long int size = ftell(file_obj);
fseek(file_obj, 0L, SEEK_SET);
void *_overwrite(const char *file, int line, void *start, void *target) {
void *original = malloc(ORIGINAL_SIZE);
memcpy(original, start, ORIGINAL_SIZE);
// Map File To Pointer
unsigned char *file_map = mmap(0, size, PROT_READ, MAP_PRIVATE, fileno(file_obj), 0);
int thumb = ((uint32_t) start) & 1;
unsigned char *patch_data;
if (thumb) {
unsigned char patch_data_temp[4] = {0xdf, 0xf8, 0x00, 0xf0};
patch_data = patch_data_temp;
} else {
unsigned char patch_data_temp[4] = {0x04, 0xf0, 0x1f, 0xe5};
patch_data = patch_data_temp;
// Parse ELF
Elf32_Ehdr *elf_header = (Elf32_Ehdr *) file_map;
Elf32_Shdr *elf_section_headers = (Elf32_Shdr *) (file_map + elf_header->e_shoff);
int elf_section_header_count = elf_header->e_shnum;
// Locate Section Names
Elf32_Shdr elf_strtab = elf_section_headers[elf_header->e_shstrndx];
unsigned char *elf_strtab_p = file_map + elf_strtab.sh_offset;
// Iterate Sections
for (int i = 0; i < elf_section_header_count; ++i) {
Elf32_Shdr header = elf_section_headers[i];
char *name = (char *) (elf_strtab_p + header.sh_name);
if (strcmp(name, ".text") == 0) {
(*callback)((void *) header.sh_addr, header.sh_size, data);
}
}
// Unmap And Close File
munmap(file_map, size);
fclose(file_obj);
}
// BL Instruction Magic Number
#define BL_INSTRUCTION 0xeb
// Generate A BL Instruction
static uint32_t generate_bl_instruction(void *from, void *to) {
uint32_t instruction;
unsigned char *instruction_array = (unsigned char *) &instruction;
instruction_array[3] = 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 *) &target;
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
struct overwrite_data {
const char *file;
int line;
void *target;
void *replacement;
};
static void overwrite_calls_callback(void *section, Elf32_Word size, void *data) {
struct overwrite_data args = *(struct overwrite_data *) data;
for (uint32_t i = 0; i < size; i = i + 4) {
unsigned char *addr = ((unsigned char *) section) + i;
if (addr[3] == BL_INSTRUCTION) {
uint32_t check_instruction = generate_bl_instruction(addr, args.target);
unsigned char *check_instruction_array = (unsigned char *) &check_instruction;
if (addr[0] == check_instruction_array[0] && addr[1] == check_instruction_array[1] && addr[2] == check_instruction_array[2]) {
uint32_t new_instruction = generate_bl_instruction(addr, args.replacement);
_patch(args.file, args.line, addr, (unsigned char *) &new_instruction);
}
}
}
}
// Limit To 512 overwrite_calls() Uses
#define CODE_BLOCK_SIZE 4096
static unsigned char *code_block = NULL;
// Overwrite Function Calls
void _overwrite_calls(const char *file, int line, void *start, 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);
INFO("Code Block Allocated At: 0x%08x", (uint32_t) code_block);
}
_overwrite(NULL, -1, code_block, target);
struct overwrite_data data;
data.file = file;
data.line = line;
data.target = start;
data.replacement = code_block;
iterate_text_section(overwrite_calls_callback, &data);
code_block = code_block + 8;
}
// Overwrite Function
void _overwrite(const char *file, int line, void *start, void *target) {
unsigned char patch_data[4] = {0x04, 0xf0, 0x1f, 0xe5};
_patch(file, line, start, patch_data);
_patch_address(file, line, start + 4, target);
return original;
}
void revert_overwrite(void *start, void *original) {
unsigned char *data = (unsigned char *) start;
int thumb = ((uint32_t) start) & 1;
if (thumb) {
data--;
}
// Store Current Value In Temp
void *temp = malloc(ORIGINAL_SIZE);
memcpy(temp, data, ORIGINAL_SIZE);
// Insert Original Value
_patch(NULL, -1, start, original);
_patch(NULL, -1, start + 4, original + 4);
// Complete Memory Swap
memcpy(original, temp, ORIGINAL_SIZE);
free(temp);
}
// Print Patch Debug Data
#define PATCH_PRINTF(file, line, start, str) if (file != NULL) fprintf(stderr, "[PATCH]: (%s:%i) Patching (0x%08x) - "str": 0x%02x 0x%02x 0x%02x 0x%02x\n", 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[]) {
size_t page_size = sysconf(_SC_PAGESIZE);
uintptr_t end = ((uintptr_t) start) + 4;
@ -57,10 +134,6 @@ void _patch(const char *file, int line, void *start, unsigned char patch[]) {
mprotect((void *) page_start, end - page_start, PROT_READ | PROT_WRITE);
unsigned char *data = (unsigned char *) start;
int thumb = ((uint32_t) start) & 1;
if (thumb) {
data--;
}
PATCH_PRINTF(file, line, start, "original");
@ -74,6 +147,7 @@ void _patch(const char *file, int line, void *start, unsigned char patch[]) {
__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[4] = {addr & 0xff, (addr >> 8) & 0xff, (addr >> 16) & 0xff, (addr >> 24) & 0xff};

View File

@ -20,16 +20,13 @@ typedef void (*releaseUsingItem_t)(unsigned char *game_mode, unsigned char *play
typedef void (*Minecraft_tickInput_t)(unsigned char *minecraft);
static Minecraft_tickInput_t Minecraft_tickInput = (Minecraft_tickInput_t) 0x15ffc;
static void *Minecraft_tickInput_original = NULL;
typedef int (*Player_isUsingItem_t)(unsigned char *player);
static Player_isUsingItem_t Player_isUsingItem = (Player_isUsingItem_t) 0x8f15c;
static void Minecraft_tickInput_injection(unsigned char *minecraft) {
// Call Original Method
revert_overwrite((void *) Minecraft_tickInput, Minecraft_tickInput_original);
(*Minecraft_tickInput)(minecraft);
revert_overwrite((void *) Minecraft_tickInput, Minecraft_tickInput_original);
// GameMode Is Offset From minecraft By 0x160
// Player Is Offset From minecraft By 0x18c
@ -48,29 +45,23 @@ static void Minecraft_tickInput_injection(unsigned char *minecraft) {
typedef void (*Gui_tickItemDrop_t)(unsigned char *);
static Gui_tickItemDrop_t Gui_tickItemDrop = (Gui_tickItemDrop_t) 0x27778;
static void *Gui_tickItemDrop_original = NULL;
#include <SDL/SDL_events.h>
static void Gui_tickItemDrop_injection(unsigned char *this) {
if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) {
// Call Original Method
revert_overwrite((void *) Gui_tickItemDrop, Gui_tickItemDrop_original);
(*Gui_tickItemDrop)(this);
revert_overwrite((void *) Gui_tickItemDrop, Gui_tickItemDrop_original);
}
}
typedef void (*Gui_handleClick_t)(unsigned char *this, int32_t param_2, int32_t param_3, int32_t param_4);
static Gui_handleClick_t Gui_handleClick = (Gui_handleClick_t) 0x2599c;
static void *Gui_handleClick_original = NULL;
static void Gui_handleClick_injection(unsigned char *this, int32_t param_2, int32_t param_3, int32_t param_4) {
if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) {
// Call Original Method
revert_overwrite((void *) Gui_handleClick, Gui_handleClick_original);
(*Gui_handleClick)(this, param_2, param_3, param_4);
revert_overwrite((void *) Gui_handleClick, Gui_handleClick_original);
}
}
@ -102,15 +93,12 @@ static void set_is_survival(int new_is_survival) {
typedef void (*Minecraft_setIsCreativeMode_t)(unsigned char *, int32_t);
static Minecraft_setIsCreativeMode_t Minecraft_setIsCreativeMode = (Minecraft_setIsCreativeMode_t) 0x16ec4;
static void *Minecraft_setIsCreativeMode_original = NULL;
static void Minecraft_setIsCreativeMode_injection(unsigned char *this, int32_t new_game_mode) {
set_is_survival(!new_game_mode);
// Call Original Method
revert_overwrite((void *) Minecraft_setIsCreativeMode, Minecraft_setIsCreativeMode_original);
(*Minecraft_setIsCreativeMode)(this, new_game_mode);
revert_overwrite((void *) Minecraft_setIsCreativeMode, Minecraft_setIsCreativeMode_original);
}
static char *get_username() {
@ -121,15 +109,12 @@ static char *get_username() {
return username;
}
typedef void (*Minecraft_init_t)(unsigned char *);
typedef void (*Minecraft_init_t)(unsigned char *this);
static Minecraft_init_t Minecraft_init = (Minecraft_init_t) 0x1700c;
static void *Minecraft_init_original = NULL;
static void Minecraft_init_injection(unsigned char *this) {
// Call Original Method
revert_overwrite((void *) Minecraft_init, Minecraft_init_original);
(*Minecraft_init)(this);
revert_overwrite((void *) Minecraft_init, Minecraft_init_original);
// Enable Fancy Graphics
*(this + 83) = 1;
@ -205,7 +190,7 @@ __attribute__((constructor)) static void init() {
// Dynamic Game Mode Switching
set_is_survival(1);
Minecraft_setIsCreativeMode_original = overwrite((void *) Minecraft_setIsCreativeMode, Minecraft_setIsCreativeMode_injection);
overwrite_calls((void *) Minecraft_setIsCreativeMode, Minecraft_setIsCreativeMode_injection);
// Get Default Game Mode
if (!is_server) {
@ -218,13 +203,13 @@ __attribute__((constructor)) static void init() {
}
// Disable Item Dropping Using The Cursor When Cursor Is Hidden
Gui_tickItemDrop_original = overwrite((void *) Gui_tickItemDrop, Gui_tickItemDrop_injection);
overwrite_calls((void *) Gui_tickItemDrop, Gui_tickItemDrop_injection);
// Disable Opening Inventory Using The Cursor When Cursor Is Hidden
Gui_handleClick_original = overwrite((void *) Gui_handleClick, Gui_handleClick_injection);
overwrite_calls((void *) Gui_handleClick, Gui_handleClick_injection);
if (extra_has_feature("Fix Bow & Arrow")) {
// Fix Bow
Minecraft_tickInput_original = overwrite((void *) Minecraft_tickInput, Minecraft_tickInput_injection);
overwrite_calls((void *) Minecraft_tickInput, Minecraft_tickInput_injection);
}
if (extra_has_feature("Fix Attacking")) {
@ -257,7 +242,7 @@ __attribute__((constructor)) static void init() {
if (extra_has_feature("Fancy Graphics")) {
// Enable Fancy Graphics
Minecraft_init_original = overwrite((void *) Minecraft_init, Minecraft_init_injection);
overwrite_calls((void *) Minecraft_init, Minecraft_init_injection);
}
// Allow Connecting To Non-Pi Servers

View File

@ -54,16 +54,13 @@ extern "C" {
typedef void (*Screen_updateEvents_t)(unsigned char *screen);
static Screen_updateEvents_t Screen_updateEvents = (Screen_updateEvents_t) 0x28eb8;
static void *Screen_updateEvents_original = NULL;
typedef void (*Screen_keyboardNewChar_t)(unsigned char *screen, char key);
typedef void (*Screen_keyPressed_t)(unsigned char *screen, int32_t key);
static void Screen_updateEvents_injection(unsigned char *screen) {
// Call Original
revert_overwrite((void *) Screen_updateEvents, Screen_updateEvents_original);
(*Screen_updateEvents)(screen);
revert_overwrite((void *) Screen_updateEvents, Screen_updateEvents_original);
if (*(char *)(screen + 4) == '\0') {
uint32_t vtable = *((uint32_t *) screen);
@ -88,7 +85,6 @@ extern "C" {
typedef int32_t (*FillingContainer_addItem_t)(unsigned char *filling_container, unsigned char *item_instance);
static FillingContainer_addItem_t FillingContainer_addItem = (FillingContainer_addItem_t) 0x92aa0;
static void *FillingContainer_addItem_original = NULL;
static void inventory_add_item(unsigned char *inventory, unsigned char *item, bool is_tile) {
unsigned char *item_instance = (unsigned char *) ::operator new(0xc);
@ -115,9 +111,7 @@ extern "C" {
static int32_t FillingContainer_addItem_injection(unsigned char *filling_container, unsigned char *item_instance) {
// Call Original
revert_overwrite((void *) FillingContainer_addItem, FillingContainer_addItem_original);
int32_t ret = (*FillingContainer_addItem)(filling_container, item_instance);
revert_overwrite((void *) FillingContainer_addItem, FillingContainer_addItem_original);
// Add After Sign
if (*(int32_t *) (item_instance + 0x4) == *(int32_t *) (*item_sign + 0x4)) {
@ -152,12 +146,12 @@ extern "C" {
if (extra_has_feature("Fix Sign Placement")) {
// Fix Signs
patch_address((void *) 0x106460, (void *) LocalPlayer_openTextEdit);
Screen_updateEvents_original = overwrite((void *) Screen_updateEvents, (void *) Screen_updateEvents_injection);
patch_address((void *) 0x10531c, (void *) Screen_updateEvents_injection);
}
if (extra_has_feature("Expand Creative Inventory")) {
// Add Extra Items To Creative Inventory
FillingContainer_addItem_original = overwrite((void *) FillingContainer_addItem, (void *) FillingContainer_addItem_injection);
overwrite_calls((void *) FillingContainer_addItem, (void *) FillingContainer_addItem_injection);
}
}
}

View File

@ -62,7 +62,6 @@ static void *read_stdin_thread(__attribute__((unused)) void *data) {
typedef void (*Minecraft_update_t)(unsigned char *minecraft);
static Minecraft_update_t Minecraft_update = (Minecraft_update_t) 0x16b74;
static void *Minecraft_update_original = NULL;
struct LevelSettings {
unsigned long seed;
@ -273,16 +272,13 @@ static void list_callback(unsigned char *minecraft, std::string username, unsign
typedef void (*Level_saveLevelData_t)(unsigned char *level);
static Level_saveLevelData_t Level_saveLevelData = (Level_saveLevelData_t) 0xa2e94;
static void *Level_saveLevelData_original = NULL;
static void Level_saveLevelData_injection(unsigned char *level) {
// Print Log Message
INFO("%s", "Saving Game");
// Call Original Method
revert_overwrite((void *) Level_saveLevelData, Level_saveLevelData_original);
(*Level_saveLevelData)(level);
revert_overwrite((void *) Level_saveLevelData, Level_saveLevelData_original);
// Save Player Data
playerdata_save(level);
@ -384,9 +380,7 @@ static void Minecraft_update_injection(unsigned char *minecraft) {
print_progress(minecraft);
// Call Original Method
revert_overwrite((void *) Minecraft_update, Minecraft_update_original);
(*Minecraft_update)(minecraft);
revert_overwrite((void *) Minecraft_update, Minecraft_update_original);
// Handle Commands
handle_commands(minecraft);
@ -397,31 +391,19 @@ static void Minecraft_update_injection(unsigned char *minecraft) {
typedef void (*Gui_addMessage_t)(unsigned char *gui, std::string const& text);
static Gui_addMessage_t Gui_addMessage = (Gui_addMessage_t) 0x27820;
static void *Gui_addMessage_original = NULL;
static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) {
// Print Log Message
fprintf(stderr, "[CHAT]: %s\n", text.c_str());
// Call Original Method
revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original);
(*Gui_addMessage)(gui, text);
revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original);
}
typedef bool (*RakNet_RakPeer_IsBanned_t)(unsigned char *rakpeer, const char *ip);
static RakNet_RakPeer_IsBanned_t RakNet_RakPeer_IsBanned = (RakNet_RakPeer_IsBanned_t) 0xda3b4;
static void *RakNet_RakPeer_IsBanned_original = NULL;
static bool RakNet_RakPeer_IsBanned_injection(unsigned char *rakpeer, const char *ip) {
// Call Original
revert_overwrite((void *) RakNet_RakPeer_IsBanned, RakNet_RakPeer_IsBanned_original);
bool ret = (*RakNet_RakPeer_IsBanned)(rakpeer, ip);
revert_overwrite((void *) RakNet_RakPeer_IsBanned, RakNet_RakPeer_IsBanned_original);
if (ret) {
return true;
} else {
static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) unsigned char *rakpeer, const char *ip) {
// Check banned-ips.txt
std::string banned_ips_file_path = get_banned_ips_file();
std::ifstream banned_ips_file(banned_ips_file_path);
@ -448,7 +430,6 @@ static bool RakNet_RakPeer_IsBanned_injection(unsigned char *rakpeer, const char
} else {
ERR("%s", "Unable To Read banned-ips.txt");
}
}
}
const char *server_get_motd() {
@ -525,13 +506,13 @@ void server_init() {
unsigned char player_patch[4] = {0x00, 0x20, 0xa0, 0xe3};
patch((void *) 0x1685c, player_patch);
// Start World On Launch
Minecraft_update_original = overwrite((void *) Minecraft_update, (void *) Minecraft_update_injection);
overwrite_calls((void *) Minecraft_update, (void *) Minecraft_update_injection);
// Print Log On Game Save
Level_saveLevelData_original = overwrite((void *) Level_saveLevelData, (void *) Level_saveLevelData_injection);
overwrite_calls((void *) Level_saveLevelData, (void *) Level_saveLevelData_injection);
// Exit handler
signal(SIGINT, exit_handler);
// Print Chat To Log
Gui_addMessage_original = overwrite((void *) Gui_addMessage, (void *) Gui_addMessage_injection);
overwrite_calls((void *) Gui_addMessage, (void *) Gui_addMessage_injection);
// Allow All IPs To Join
unsigned char allow_all_ip_patch[4] = {0x00, 0xf0, 0x20, 0xe3};
patch((void *) 0xe1f6c, allow_all_ip_patch);
@ -539,7 +520,7 @@ void server_init() {
unsigned char max_players_patch[4] = {server_get_max_players(), 0x30, 0xa0, 0xe3};
patch((void *) 0x166d0, max_players_patch);
// Custom Banned IP List
RakNet_RakPeer_IsBanned_original = overwrite((void *) RakNet_RakPeer_IsBanned, (void *) RakNet_RakPeer_IsBanned_injection);
overwrite((void *) RakNet_RakPeer_IsBanned, (void *) RakNet_RakPeer_IsBanned_injection);
// Load Player Data
playerdata_init();

View File

@ -1,6 +1,7 @@
#include <sys/time.h>
#include <time.h>
// Replace gettimeofday() With clock_gettime()
int gettimeofday(struct timeval *tv, __attribute__((unused)) void *tz) {
struct timespec tp;
int ret = clock_gettime(CLOCK_MONOTONIC, &tp);