|
/* |
|
* preload.c - Minecraft Pi Dynamic Patches. |
|
* |
|
* Copyright 2020 Alvarito050506 <[email protected]> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; version 2 of the License. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
* MA 02110-1301, USA. |
|
* |
|
* |
|
*/ |
|
|
|
#define _GNU_SOURCE /* Required for RTLD_NEXT */ |
|
|
|
/* Lots of standard includes and libraries, for types and function prototypes. */ |
|
#include <stdio.h> |
|
#include <dlfcn.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <sys/mman.h> |
|
#include <stdint.h> |
|
#include <libgen.h> |
|
#include <fcntl.h> |
|
#include <sys/types.h> |
|
#include <sys/user.h> |
|
#include <sys/socket.h> |
|
#include <sys/ptrace.h> |
|
#include <unistd.h> |
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
|
#include <GL/gl.h> |
|
#include <SDL2/SDL.h> |
|
#include <errno.h> |
|
|
|
/* Standard/POSIX. */ |
|
static FILE* (*old_fopen)(const char* pathname, const char* mode) = NULL; |
|
ssize_t (*old_sendto)(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen); |
|
ssize_t (*old_recv)(int sockfd, void *buf, size_t len, int flags); |
|
ssize_t (*old_send)(int sockfd, const void* buf, size_t len, int flags); |
|
ssize_t (*old_recvfrom)(int sockfd, void *buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen); |
|
int (*old_open)(const char *pathname, int flags); |
|
|
|
/* OpenGL. */ |
|
void (*old_glShadeModel)(GLenum mode); |
|
void (*old_glFogfv)(GLenum pname, const GLfloat* params); |
|
|
|
/* SDL. */ |
|
int (*old_SDL_PollEvent)(SDL_Event* event); |
|
void (*old_SDL_WM_SetCaption)(const char* title, const char* icon) = NULL; |
|
|
|
/* Please ignore the variable names. */ |
|
void *addr_0, *addr_1, *addr_2; |
|
int game_mode = 1; |
|
int ex_game_mode = 1; |
|
char level_dat_name[512]; |
|
int reading = 1; |
|
int connected = 1; |
|
char buffer[64]; |
|
int i = 0; |
|
int api_fd = 0; |
|
|
|
int api_setup(); |
|
int api_kill(); |
|
|
|
/* To be optimized. */ |
|
void* search(char* data, int from) |
|
{ |
|
void* i; |
|
|
|
i = (void*)from; |
|
while (i < (void*)0x20000) |
|
{ |
|
if (memcmp(i, data, 4) == 0 && i != (void*)data) |
|
{ |
|
return i; |
|
} |
|
i += 1; |
|
} |
|
return NULL; |
|
} |
|
|
|
void unprotect(uint32_t addr) |
|
{ |
|
mprotect((void*)(addr - (addr % 4096)), 4096, PROT_READ|PROT_WRITE|PROT_EXEC); |
|
} |
|
|
|
/* [Work In Progress] Dinamyclly change the game mode. */ |
|
FILE* fopen(const char* filename, const char* mode) |
|
{ |
|
if (strcmp(basename((char*)filename), "player.dat") == 0) |
|
{ |
|
FILE* level_dat_file; |
|
char* andseq; |
|
|
|
level_dat_name[0] = 0; |
|
sprintf(level_dat_name, "%s/level.dat", dirname((char*)filename)); |
|
level_dat_file = fopen(level_dat_name, "rb"); |
|
fseek(level_dat_file, 0x16, SEEK_SET); |
|
fread(&game_mode, 1, 1, level_dat_file); |
|
|
|
if (ex_game_mode == 1) |
|
{ |
|
andseq = "\x00\x2d\x10\x00"; |
|
} else |
|
{ |
|
andseq = "\x60\x2f\x10\x00"; |
|
} |
|
|
|
/* To be optimized. */ |
|
addr_0 = search(andseq, 0xde60); |
|
unprotect((uint32_t)(addr_0)); |
|
addr_1 = search(andseq, (int)addr_0 + 4); |
|
unprotect((uint32_t)(addr_1)); |
|
|
|
/* Don't replace this with loops or memory/string functions, it will not work. */ |
|
if (game_mode == 0) |
|
{ |
|
((char*)addr_0)[0] = '\x60'; |
|
((char*)addr_0)[1] = '\x2f'; |
|
((char*)addr_0)[2] = '\x10'; |
|
((char*)addr_0)[3] = '\x00'; |
|
((char*)addr_1)[0] = '\x60'; |
|
((char*)addr_1)[1] = '\x2f'; |
|
((char*)addr_1)[2] = '\x10'; |
|
((char*)addr_1)[3] = '\x00'; |
|
} else if (game_mode == 1) |
|
{ |
|
((char*)addr_0)[0] = '\x00'; |
|
((char*)addr_0)[1] = '\x2d'; |
|
((char*)addr_0)[2] = '\x10'; |
|
((char*)addr_0)[3] = '\x00'; |
|
((char*)addr_1)[0] = '\x00'; |
|
((char*)addr_1)[1] = '\x2d'; |
|
((char*)addr_1)[2] = '\x10'; |
|
((char*)addr_1)[3] = '\x00'; |
|
} |
|
ex_game_mode = (int)game_mode; |
|
fclose(level_dat_file); |
|
} else if (strcmp(basename((char*)filename), "entities.dat") == 0 && strcmp(basename((char*)mode), "rb") == 0) |
|
{ |
|
api_kill(); |
|
} |
|
|
|
return old_fopen(filename, mode); |
|
} |
|
|
|
/* Fancy stuff: Change the window name. */ |
|
void SDL_WM_SetCaption(const char* title, const char* icon) |
|
{ |
|
return old_SDL_WM_SetCaption("Minecraft - Pi Edition - Patched", "/usr/share/pixmaps/minecraft-pi.png"); |
|
} |
|
|
|
/* "Smooth" shading. */ |
|
void glShadeModel(GLenum mode) |
|
{ |
|
if (connected == 1) |
|
{ |
|
api_kill(); |
|
api_setup(); |
|
} |
|
return old_glShadeModel(GL_SMOOTH); |
|
} |
|
|
|
/* Fancy stuff: Green fog. */ |
|
void glFogfv(GLenum pname, const GLfloat* params) |
|
{ |
|
GLfloat fog[] = {0.0, 1.0, 0.0, 1.0}; |
|
GLfloat start = 0.0; |
|
|
|
if (pname == GL_FOG_COLOR) |
|
{ |
|
return old_glFogfv(GL_FOG_COLOR, fog); |
|
} else if (pname == GL_FOG_START) |
|
{ |
|
return old_glFogfv(GL_FOG_START, &start); |
|
} else |
|
{ |
|
return old_glFogfv(pname, params); |
|
} |
|
} |
|
|
|
/* Extends port search: 19132-19139. */ |
|
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen) |
|
{ |
|
struct sockaddr_in* addr = (struct sockaddr_in*)dest_addr; |
|
int i = 19136; |
|
if (addr->sin_addr.s_addr == -1 && ntohs(addr->sin_port) == 19135) |
|
{ |
|
while (i < 19139) |
|
{ |
|
addr->sin_port = htons(i); |
|
if (old_sendto(sockfd, buf, len, flags, dest_addr, addrlen) < 0) |
|
{ |
|
printf("sendto failed with exit errno %i\n", errno); |
|
} |
|
i++; |
|
} |
|
addr->sin_port = htons(19135); |
|
} |
|
return old_sendto(sockfd, buf, len, flags, dest_addr, addrlen); |
|
} |
|
|
|
/* Failed, thanks to the lack of debug symbols. Supposed to change the slot number to 2. */ |
|
int _ZN3Gui11getNumSlotsEv() |
|
{ |
|
return 2; |
|
} |
|
|
|
/* [Work In Progress] T-Chat. Stores the message in `buffer` and then prints it to `stdout`. */ |
|
int SDL_PollEvent(SDL_Event* event) |
|
{ |
|
int rt = old_SDL_PollEvent(event); |
|
|
|
/* MCPI uses custom events, and the Mojang folks decided that storing the keycode in the (optional) window ID was a good idea. */ |
|
char chr = (char)event->user.windowID; |
|
|
|
if (event->type == 65538) |
|
{ |
|
/* |
|
* 0x0d == CR/LF? |
|
* 0x1b == Esc. |
|
* 0x00 == Ignore, other "special" keys. |
|
* |
|
*/ |
|
if (reading == 0 && chr != 0x00 && chr != 0x0d && chr != 0x1b && i < 64) |
|
{ |
|
buffer[i] = chr; |
|
event->user.windowID = 0x00; /* Say "ignore" to MCPI. */ |
|
i++; |
|
} else if (reading == 1 && chr == 't') |
|
{ |
|
reading = 0; |
|
event->user.windowID = 0x00; /* Say "ignore" to MCPI. */ |
|
} else if (reading == 0 && (chr == 0x0d || chr == 0x1b)) |
|
{ |
|
if (chr == 0x0d) |
|
{ |
|
write(api_fd, "chat.post(", strlen("chat.post(")); |
|
write(api_fd, buffer, strlen(buffer)); |
|
write(api_fd, ")\n", strlen(")\n")); |
|
} |
|
event->user.windowID = 0x00; /* Say "ignore" to MCPI. */ |
|
reading = 1; |
|
buffer[i] = 0x00; |
|
i = 0; |
|
memset(buffer, 0x00, 63); /* Clear the buffer. */ |
|
} |
|
} |
|
return rt; |
|
} |
|
|
|
/* Setup API connection. */ |
|
int api_setup() |
|
{ |
|
int rt = 0; |
|
struct sockaddr_in api_addr; |
|
|
|
api_addr.sin_family = AF_INET; |
|
api_addr.sin_port = htons(4711); |
|
rt = inet_pton(AF_INET, "127.0.0.1", &api_addr.sin_addr); |
|
api_fd = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
if (api_fd < -1) |
|
{ |
|
connected = 1; |
|
return -1; |
|
} |
|
rt = connect(api_fd, (struct sockaddr*)&api_addr, sizeof(api_addr)); |
|
if (rt < 0) |
|
{ |
|
connected = 1; |
|
return -1; |
|
} |
|
connected = 0; |
|
return 0; |
|
} |
|
|
|
/* Kill API connection. */ |
|
int api_kill() |
|
{ |
|
if (connected == 0) |
|
{ |
|
shutdown(api_fd, SHUT_RDWR); |
|
close(api_fd); |
|
connected = 1; |
|
} |
|
return 0; |
|
} |
|
|
|
/* Constructor, gets called before MCPI itself. */ |
|
void __attribute__((constructor)) init() |
|
{ |
|
old_fopen = dlsym(RTLD_NEXT, "fopen"); |
|
old_sendto = dlsym(RTLD_NEXT, "sendto"); |
|
|
|
old_glShadeModel = dlsym(RTLD_NEXT, "glShadeModel"); |
|
old_glFogfv = dlsym(RTLD_NEXT, "glFogfv"); |
|
|
|
old_SDL_WM_SetCaption = dlsym(RTLD_NEXT, "SDL_WM_SetCaption"); |
|
old_SDL_PollEvent = dlsym(RTLD_NEXT, "SDL_PollEvent"); |
|
return; |
|
} |
|
|
|
/* Constructor, gets called when the library is unloaded from memory, and closes the API connection. */ |
|
void __attribute__((destructor)) destroy() |
|
{ |
|
api_kill(); |
|
return; |
|
} |