Files
lindbergh-loader_lindbergh-…/src/lindbergh/hook.c
T
2022-12-29 10:15:27 +00:00

466 lines
11 KiB
C

#include <dlfcn.h>
#include <errno.h>
#include <linux/sockios.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ucontext.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "hook.h"
#include "baseboard.h"
#include "config.h"
#include "driveboard.h"
#include "eeprom.h"
#include "jvs.h"
#include "rideboard.h"
#include "securityboard.h"
#define HOOK_FILE_NAME "/dev/zero"
#define BASEBOARD 0
#define EEPROM 1
#define SERIAL0 2
#define SERIAL1 3
#define CPUINFO 0
#define OSRELEASE 1
int hooks[5] = {-1, -1, -1, -1};
FILE *fileHooks[2] = {NULL, NULL};
int fileRead[2] = {0, 0};
uint16_t basePortAddress = 0xFFFF;
static void handleSegfault(int signal, siginfo_t *info, void *ptr)
{
ucontext_t *ctx = ptr;
uint8_t *code = (uint8_t *)ctx->uc_mcontext.gregs[REG_EIP];
switch (*code)
{
case 0xED:
{
uint16_t port = ctx->uc_mcontext.gregs[REG_EDX] & 0xFFFF;
// The first port called is usually random, but everything after that
// is a constant offset, so this is a hack to fix that.
// When run as sudo it works fine!?
if (basePortAddress == 0xFFFF)
basePortAddress = port;
if (port > 0x38)
port = port - basePortAddress;
securityBoardIn(port, (uint32_t *)&(ctx->uc_mcontext.gregs[REG_EAX]));
ctx->uc_mcontext.gregs[REG_EIP]++;
return;
}
break;
case 0xE7: // OUT IMMIDIATE
{
ctx->uc_mcontext.gregs[REG_EIP] += 2;
return;
}
break;
case 0xE6: // OUT IMMIDIATE
{
ctx->uc_mcontext.gregs[REG_EIP] += 2;
return;
}
break;
case 0xEE: // OUT
{
uint16_t port = ctx->uc_mcontext.gregs[REG_EDX] & 0xFFFF;
uint8_t data = ctx->uc_mcontext.gregs[REG_EAX] & 0xFF;
ctx->uc_mcontext.gregs[REG_EIP]++;
return;
}
break;
case 0xEF: // OUT
{
uint16_t port = ctx->uc_mcontext.gregs[REG_EDX] & 0xFFFF;
ctx->uc_mcontext.gregs[REG_EIP]++;
return;
}
break;
default:
printf("Warning: Skipping SEGFAULT %X\n", *code);
ctx->uc_mcontext.gregs[REG_EIP]++;
// abort();
}
}
void __attribute__((constructor)) hook_init()
{
printf("SEGA Lindbergh Loader\nRobert Dilley 2022\nNot for public consumption\n\n");
// Implement SIGSEGV handler
struct sigaction act;
act.sa_sigaction = handleSegfault;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, NULL);
initConfig();
if (initEeprom() != 0)
exit(1);
if (initBaseboard() != 0)
exit(1);
if (initJVS() != 0)
exit(1);
if (initSecurityBoard() != 0)
exit(1);
if (getConfig()->emulateDriveboard)
{
if (initDriveboard() != 0)
exit(1);
}
if (getConfig()->emulateRideboard)
{
if (initRideboard() != 0)
exit(1);
}
securityBoardSetDipResolution(getConfig()->width, getConfig()->height);
printf("Loader init success\n");
}
int open(const char *pathname, int flags)
{
int (*_open)(const char *pathname, int flags) = dlsym(RTLD_NEXT, "open");
// printf("Open %s\n", pathname);
if (strcmp(pathname, "/dev/lbb") == 0)
{
hooks[BASEBOARD] = _open(HOOK_FILE_NAME, flags);
return hooks[BASEBOARD];
}
if (strcmp(pathname, "/dev/i2c/0") == 0)
{
hooks[EEPROM] = _open(HOOK_FILE_NAME, flags);
return hooks[EEPROM];
}
if (strcmp(pathname, "/dev/ttyS0") == 0 || strcmp(pathname, "/dev/tts/0") == 0)
{
hooks[SERIAL0] = _open(HOOK_FILE_NAME, flags);
return hooks[SERIAL0];
}
if (strcmp(pathname, "/dev/ttyS1") == 0 || strcmp(pathname, "/dev/tts/1") == 0)
{
hooks[SERIAL1] = _open(HOOK_FILE_NAME, flags);
return hooks[SERIAL1];
}
if (strncmp(pathname, "/tmp/", 5) == 0)
{
mkdir("tmp", 0777);
return _open(pathname + 1, flags);
}
return _open(pathname, flags);
}
FILE *fopen(const char *restrict pathname, const char *restrict mode)
{
FILE *(*_fopen)(const char *restrict pathname, const char *restrict mode) = dlsym(RTLD_NEXT, "fopen");
// printf("fopen %s\n", pathname);
if (strcmp(pathname, "/root/lindbergrc") == 0)
{
return _fopen("lindbergrc", mode);
}
if (strcmp(pathname, "/proc/cpuinfo") == 0)
{
fileRead[CPUINFO] = 0;
fileHooks[CPUINFO] = _fopen(HOOK_FILE_NAME, mode);
return fileHooks[CPUINFO];
}
return _fopen(pathname, mode);
}
FILE *fopen64(const char *pathname, const char *mode)
{
FILE *(*_fopen64)(const char *restrict pathname, const char *restrict mode) = dlsym(RTLD_NEXT, "fopen64");
// printf("fopen64 %s\n", pathname);
if (strcmp(pathname, "/proc/sys/kernel/osrelease") == 0)
{
EmulatorConfig *config = getConfig();
config->game = SEGABOOT_2_6;
fileRead[OSRELEASE] = 0;
fileHooks[OSRELEASE] = _fopen64(HOOK_FILE_NAME, mode);
return fileHooks[OSRELEASE];
}
return _fopen64(pathname, mode);
}
int openat(int dirfd, const char *pathname, int flags)
{
int (*_openat)(int dirfd, const char *pathname, int flags) = dlsym(RTLD_NEXT, "openat");
// printf("openat %s\n", pathname);
if (strcmp(pathname, "/dev/ttyS0") == 0 || strcmp(pathname, "/dev/ttyS1") == 0 || strcmp(pathname, "/dev/tts/0") == 0 || strcmp(pathname, "/dev/tts/1") == 0)
{
return open(pathname, flags);
}
return _openat(dirfd, pathname, flags);
}
int close(int fd)
{
int (*_close)(int fd) = dlsym(RTLD_NEXT, "close");
for (int i = 0; i < (sizeof hooks / sizeof hooks[0]); i++)
{
if (hooks[i] == fd)
{
hooks[i] = -1;
return 0;
}
}
return _close(fd);
}
char *fgets(char *str, int n, FILE *stream)
{
char *(*_fgets)(char *str, int n, FILE *stream) = dlsym(RTLD_NEXT, "fgets");
if (stream == fileHooks[OSRELEASE])
{
char *contents = "mvl";
strcpy(str, contents);
return str;
}
// This currently doesn't work
if (stream == fileHooks[CPUINFO])
{
char contents[4][256];
strcpy(contents[0], "processor : 0");
strcpy(contents[1], "vendor_id : GenuineIntel");
strcpy(contents[2], "model : 142");
strcpy(contents[3], "model name : Intel(R) Pentium(R) CPU 3.00GHz");
if (getConfig()->lindberghColour == RED)
strcpy(contents[3], "model name : Intel(R) Celeron(R) CPU 3.00GHz");
if (fileRead[CPUINFO] == 4)
return NULL;
strcpy(str, contents[fileRead[CPUINFO]++]);
return str;
}
return _fgets(str, n, stream);
}
ssize_t read(int fd, void *buf, size_t count)
{
int (*_read)(int fd, void *buf, size_t count) = dlsym(RTLD_NEXT, "read");
if (fd == hooks[BASEBOARD])
{
return baseboardRead(fd, buf, count);
}
if (fd == hooks[SERIAL0] && getConfig()->emulateRideboard)
{
return rideboardRead(fd, buf, count);
}
if (fd == hooks[SERIAL0] && getConfig()->emulateDriveboard)
{
return driveboardRead(fd, buf, count);
}
return _read(fd, buf, count);
}
ssize_t write(int fd, const void *buf, size_t count)
{
int (*_write)(int fd, const void *buf, size_t count) = dlsym(RTLD_NEXT, "write");
if (fd == hooks[BASEBOARD])
{
return baseboardWrite(fd, buf, count);
}
if (fd == hooks[SERIAL0] && getConfig()->emulateRideboard)
{
return rideboardWrite(fd, buf, count);
}
if (fd == hooks[SERIAL0] && getConfig()->emulateDriveboard)
{
return driveboardWrite(fd, buf, count);
}
return _write(fd, buf, count);
}
int ioctl(int fd, unsigned int request, void *data)
{
int (*_ioctl)(int fd, int request, void *data) = dlsym(RTLD_NEXT, "ioctl");
// Attempt to stop access to the ethernet ports
if ((request == SIOCSIFADDR) || (request == SIOCSIFFLAGS) || (request == SIOCSIFHWADDR) || (request == SIOCSIFHWBROADCAST) || (request == SIOCDELRT) || (request == SIOCADDRT) || (request == SIOCSIFNETMASK))
{
errno = ENXIO;
return -1;
}
if (fd == hooks[EEPROM])
{
return eepromIoctl(fd, request, data);
}
if (fd == hooks[BASEBOARD])
{
return baseboardIoctl(fd, request, data);
}
// Just accept any IOCTL on serial ports and ignore it
if (fd == hooks[SERIAL0] || fd == hooks[SERIAL1])
{
return 0;
}
return _ioctl(fd, request, data);
}
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout)
{
int (*_select)(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout) = dlsym(RTLD_NEXT, "select");
if (readfds != NULL && FD_ISSET(hooks[BASEBOARD], readfds))
{
return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout);
}
if (writefds != NULL && FD_ISSET(hooks[BASEBOARD], writefds))
{
return baseboardSelect(nfds, readfds, writefds, exceptfds, timeout);
}
return _select(nfds, readfds, writefds, exceptfds, timeout);
}
int system(const char *command)
{
int (*_system)(const char *command) = dlsym(RTLD_NEXT, "system");
if (strcmp(command, "lsmod | grep basebd > /dev/null") == 0)
return 0;
if (strcmp(command, "cd /tmp/segaboot > /dev/null") == 0)
return system("cd tmp/segaboot > /dev/null");
if (strcmp(command, "mkdir /tmp/segaboot > /dev/null") == 0)
return system("mkdir tmp/segaboot > /dev/null");
if (strcmp(command, "lspci | grep \"Multimedia audio controller: %Creative\" > /dev/null") == 0)
return 0;
if (strcmp(command, "lsmod | grep ctaud") == 0)
return 0;
if (strcmp(command, "lspci | grep MPC8272 > /dev/null") == 0)
return 0;
if (strcmp(command, "uname -r | grep mvl") == 0)
{
EmulatorConfig *config = getConfig();
config->game = SEGABOOT_2_4;
return 0;
}
if (strstr(command, "hwclock") != NULL)
return 0;
return _system(command);
}
int iopl(int level)
{
return 0;
}
/**
* Hook for the only function provided by kswapapi.so
* @param p No idea this gets discarded
*/
void kswap_collect(void *p)
{
return;
}
/**
* Hook for function used by Primevil
* @param base The number to raise to the exponent
* @param exp The exponent to raise the number to
* @return The result of raising the number to the exponent
*/
float powf(float base, float exponent)
{
return (float)pow((double)base, (double)exponent);
}
/*
int sem_wait(sem_t *sem)
{
int (*original_sem_wait)(sem_t * sem) = dlsym(RTLD_NEXT, "sem_wait");
return 0;
}
*/
/**
* Hook function used by Harley Davidson to change IPs to localhost
* Currently does nothing.
*/
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
int (*_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = dlsym(RTLD_NEXT, "connect");
struct sockaddr_in *in_pointer = (struct sockaddr_in *)addr;
// Change the IP to connect to to 127.0.0.1
// in_pointer->sin_addr.s_addr = inet_addr("127.0.0.1");
char *some_addr = inet_ntoa(in_pointer->sin_addr);
printf("Connecting to %s\n", some_addr);
return _connect(sockfd, addr, addrlen);
}