1
0
mirror of synced 2024-11-12 00:40:47 +01:00

Bunch of driver work, and json-based patching

This commit is contained in:
Bottersnike 2022-06-14 13:30:36 +09:00
parent 331710e239
commit 463d94f1ee
41 changed files with 1348 additions and 469 deletions

View File

@ -53,3 +53,4 @@ dist:
@xcopy /E /H /C /R /Q /Y src\system "$(DIST_DIR)\system/*" @xcopy /E /H /C /R /Q /Y src\system "$(DIST_DIR)\system/*"
@xcopy /E /H /C /R /Q /Y src\tools "$(DIST_DIR)\tools/*" @xcopy /E /H /C /R /Q /Y src\tools "$(DIST_DIR)\tools/*"
@xcopy /E /H /C /R /Q /Y src\patches "$(DIST_DIR)\patches/*"

View File

@ -1,5 +1,7 @@
#include "com.h" #include "com.h"
#include "../lib/mice/mice.h"
com_hook_t* com_hook_list = NULL; com_hook_t* com_hook_list = NULL;
com_hook_t* new_com_hook(BYTE port) { com_hook_t* new_com_hook(BYTE port) {
com_hook_t* hook = (com_hook_t*)malloc(sizeof *hook); com_hook_t* hook = (com_hook_t*)malloc(sizeof *hook);
@ -19,9 +21,11 @@ com_hook_t* new_com_hook(BYTE port) {
return hook; return hook;
}; };
void hook_com(com_hook_t* hook) { void hook_com(com_hook_t* hook) {
hook->next = NULL; hook->next = NULL;
hook->virtual_handle = NULL; hook->virtual_handle = (LPHANDLE)malloc(sizeof(HANDLE));
*hook->virtual_handle = NULL;
if (com_hook_list == NULL) { if (com_hook_list == NULL) {
com_hook_list = hook; com_hook_list = hook;
return; return;
@ -33,97 +37,155 @@ void hook_com(com_hook_t* hook) {
}; };
BOOL WINAPI FakeGetCommState(HANDLE hFile, LPDCB lpDCB) { BOOL WINAPI FakeGetCommState(HANDLE hFile, LPDCB lpDCB) {
log_misc("comm", "GetCommState(0x%p, 0x%p)", hFile, lpDCB); log_misc(COMM_LOGGER, "GetCommState(0x%p, 0x%p)", hFile, lpDCB);
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->GetCommState != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->GetCommState == NULL) {
log_error(COMM_LOGGER, "GetCommState(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->GetCommState(hook->data, lpDCB); return hook->GetCommState(hook->data, lpDCB);
} }
hook = hook->next;
} }
return TrueGetCommState(hFile, lpDCB); return TrueGetCommState(hFile, lpDCB);
} }
BOOL WINAPI FakeSetCommState(HANDLE hFile, LPDCB lpDCB) { BOOL WINAPI FakeSetCommState(HANDLE hFile, LPDCB lpDCB) {
log_misc("comm", "SetCommState(0x%p, 0x%p)", hFile, lpDCB); log_misc(COMM_LOGGER, "SetCommState(0x%p, 0x%p)", hFile, lpDCB);
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->SetCommState != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->SetCommState == NULL) {
log_error(COMM_LOGGER, "SetCommState(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->SetCommState(hook->data, lpDCB); return hook->SetCommState(hook->data, lpDCB);
} }
hook = hook->next;
} }
return TrueSetCommState(hFile, lpDCB); return TrueSetCommState(hFile, lpDCB);
} }
BOOL WINAPI FakeGetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) { BOOL WINAPI FakeGetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) {
log_misc("comm", "GetCommTimeouts(0x%p, 0x%p)", hFile, lpCommTimeouts); log_misc(COMM_LOGGER, "GetCommTimeouts(0x%p, 0x%p)", hFile, lpCommTimeouts);
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->GetCommTimeouts != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->GetCommTimeouts == NULL) {
log_error(COMM_LOGGER, "GetCommTimeouts(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->GetCommTimeouts(hook->data, lpCommTimeouts); return hook->GetCommTimeouts(hook->data, lpCommTimeouts);
} }
hook = hook->next;
} }
return TrueGetCommTimeouts(hFile, lpCommTimeouts); return TrueGetCommTimeouts(hFile, lpCommTimeouts);
} }
BOOL WINAPI FakeSetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) { BOOL WINAPI FakeSetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) {
log_misc("comm", "SetCommTimeouts(0x%p, 0x%p)", hFile, lpCommTimeouts); log_misc(COMM_LOGGER, "SetCommTimeouts(0x%p, 0x%p)", hFile, lpCommTimeouts);
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->SetCommTimeouts != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->SetCommTimeouts == NULL) {
log_error(COMM_LOGGER, "SetCommTimeouts(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->SetCommTimeouts(hook->data, lpCommTimeouts); return hook->SetCommTimeouts(hook->data, lpCommTimeouts);
} }
hook = hook->next;
} }
return TrueSetCommTimeouts(hFile, lpCommTimeouts); return TrueSetCommTimeouts(hFile, lpCommTimeouts);
} }
BOOL WINAPI FakeSetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue) { BOOL WINAPI FakeSetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue) {
log_misc("comm", "SetupCom(0x%p, 0x%08x, 0x%08x)", hFile, dwInQueue, dwOutQueue); log_misc(COMM_LOGGER, "SetupCom(0x%p, 0x%08x, 0x%08x)", hFile, dwInQueue, dwOutQueue);
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->SetupComm != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->SetupComm == NULL) {
log_error(COMM_LOGGER, "SetupComm(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->SetupComm(hook->data, dwInQueue, dwOutQueue); return hook->SetupComm(hook->data, dwInQueue, dwOutQueue);
} }
hook = hook->next;
} }
return TrueSetupComm(hFile, dwInQueue, dwOutQueue); return TrueSetupComm(hFile, dwInQueue, dwOutQueue);
} }
BOOL WINAPI FakePurgeComm(HANDLE hFile, DWORD dwFlags) { BOOL WINAPI FakePurgeComm(HANDLE hFile, DWORD dwFlags) {
log_misc("comm", "PurgeComm(0x%p, 0x%08x)", hFile, dwFlags); log_misc(COMM_LOGGER, "PurgeComm(0x%p, 0x%08x)", hFile, dwFlags);
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->PurgeComm != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->PurgeComm == NULL) {
log_error(COMM_LOGGER, "PurgeComm(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->PurgeComm(hook->data, dwFlags); return hook->PurgeComm(hook->data, dwFlags);
} }
hook = hook->next;
} }
return TruePurgeComm(hFile, dwFlags); return TruePurgeComm(hFile, dwFlags);
} }
BOOL WINAPI FakeGetCommModemStatus(HANDLE hFile, LPDWORD lpModelStat) { BOOL WINAPI FakeGetCommModemStatus(HANDLE hFile, LPDWORD lpModelStat) {
log_misc("comm", "GetCommModemStatus(0x%p, 0x%p)", hFile, lpModelStat); log_misc(COMM_LOGGER, "GetCommModemStatus(0x%p, 0x%p)", hFile, lpModelStat);
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->GetCommModemStatus != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->GetCommModemStatus == NULL) {
log_error(COMM_LOGGER, "GetCommModemStatus(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->GetCommModemStatus(hook->data, lpModelStat); return hook->GetCommModemStatus(hook->data, lpModelStat);
} }
hook = hook->next;
} }
return TrueGetCommModemStatus(hFile, lpModelStat); return TrueGetCommModemStatus(hFile, lpModelStat);
} }
BOOL WINAPI FakeWaitCommEvent(HANDLE hFile, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped) { BOOL WINAPI FakeWaitCommEvent(HANDLE hFile, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped) {
log_misc("comm", "WaitCommEvent"); log_misc(COMM_LOGGER, "WaitCommEvent");
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->WaitCommEvent != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->WaitCommEvent == NULL) {
log_error(COMM_LOGGER, "WaitCommEvent(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->WaitCommEvent(hook->data, lpEvtMask, lpOverlapped); return hook->WaitCommEvent(hook->data, lpEvtMask, lpOverlapped);
} }
hook = hook->next;
} }
return TrueWaitCommEvent(hFile, lpEvtMask, lpOverlapped); return TrueWaitCommEvent(hFile, lpEvtMask, lpOverlapped);
} }
BOOL WINAPI FakeClearCommError(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat) { BOOL WINAPI FakeClearCommError(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat) {
log_misc("comm", "ClearCommError"); log_misc(COMM_LOGGER, "ClearCommError");
com_hook_t* hook = com_hook_list; com_hook_t* hook = com_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile && hook->ClearCommError != NULL) { if (*hook->virtual_handle == hFile) {
if (hook->ClearCommError == NULL) {
log_error(COMM_LOGGER, "ClearCommError(%ls) unimplemented", hook->wName);
return FALSE;
}
return hook->ClearCommError(hook->data, lpErrors, lpStat); return hook->ClearCommError(hook->data, lpErrors, lpStat);
} }
hook = hook->next;
} }
return TrueClearCommError(hFile, lpErrors, lpStat); return TrueClearCommError(hFile, lpErrors, lpStat);
} }

View File

@ -23,7 +23,7 @@ typedef BOOL(FnWaitCommEvent)(void* com, LPDWORD lpEvtMask, LPOVERLAPPED lpOverl
typedef BOOL(FnClearCommError)(void* com, LPDWORD lpErrors, LPCOMSTAT lpStat); typedef BOOL(FnClearCommError)(void* com, LPDWORD lpErrors, LPCOMSTAT lpStat);
typedef struct com_hook { typedef struct com_hook {
HANDLE virtual_handle; LPHANDLE virtual_handle;
WCHAR wName[7]; // max is COM255 WCHAR wName[7]; // max is COM255
BYTE com; BYTE com;

View File

@ -1,4 +1,5 @@
#include <Windows.h> #include <Windows.h>
#include <Winsock2.h>
#include <shlwapi.h> #include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib") #pragma comment(lib, "Shlwapi.lib")
@ -6,107 +7,52 @@
#include "../lib/mice/mice.h" #include "../lib/mice/mice.h"
#include "drivers/mx.h" #include "drivers/mx.h"
#include "files.h"
#include "com.h"
#include "micesetupapi.h" #include "micesetupapi.h"
#include "network.h"
#include "processes.h"
WCHAR exePath[MAX_PATH + 1]; WCHAR exePath[MAX_PATH + 1];
void enable_traces() { void enable_traces() {
if (wcscmp(exePath, L"Game.exe") == 0) { patches_t patches;
*(DWORD*)(0x008717a0) = 1; char error[256];
*(DWORD*)(0x00871728) = 1; if (!load_patches(&patches, "patches.json", error)) {
*(DWORD*)(0x00871cd8) = 1; log_error(BOOT_LOGGER, "Failed to load patches file: %s", error);
*(DWORD*)(0x00872660) = 1;
*(DWORD*)(0x0087297c) = 1; // LOG_EN_EEPROM
*(DWORD*)(0x00872980) = 1;
*(DWORD*)(0x00872988) = 1;
*(DWORD*)(0x00873540) = 1;
*(DWORD*)(0x00873538) = 1;
*(DWORD*)(0x0087f890) = 1;
*(DWORD*)(0x00882ce4) = 1;
*(DWORD*)(0x00882cec) = 1;
*(DWORD*)(0x00883018) = 1;
*(DWORD*)(0x00886ff8) = 1;
*(DWORD*)(0x0088b1b8) = 1;
*(DWORD*)(0x0088b1c0) = 1;
} else if (wcscmp(exePath, L"ALLNetProc_Win.exe") == 0) {
*(DWORD*)(0x004f48b0) = 1; // LOG_EN_DONGLE
*(DWORD*)(0x004f989c) = 1; // LOG_EN_PCP
*(DWORD*)(0x004f7d04) = 1; // LOG_EN_EEPROM
*(DWORD*)(0x004f7d08) = 1; // LOG_EN_BACKUP
*(DWORD*)(0x004f8118) = 1; // LOG_EN_RTC
*(DWORD*)(0x004f96dc) = 1; // LOG_EN_GFETCHER
*(DWORD*)(0x004f8338) = 1; // LOG_EN_NETWORK
*(DWORD*)(0x004f88c0) = 1; // LOG_EN_MASTER
*(DWORD*)(0x004f88b0) = 1; // LOG_EN_HM
*(DWORD*)(0x004f8330) = 1; // LOG_EN_SRAM
*(DWORD*)(0x004f9728) = 1; // LOG_EN_PLATFORM
//*(DWORD*)(0x004f9e7c) = 5; // LOG_EN_ASSERTS (handled by the bellow
// instead)
*(DWORD*)(0x004e884c) = 10; // ALPB_LOG_LEVEL (0=None, 1=Error,
// 2=Warning, 3=Most, 4=Debug)
} else if (wcscmp(exePath, L"ORIG_mxsegaboot.exe") == 0 || wcscmp(exePath, L"mxsegaboot.exe") == 0) {
// start of am
*(DWORD*)(0x0054fcd8) = 1; // LOG_EN_JVS_DRIVER
*(DWORD*)(0x00538810) = 1; // LOG_EN_DIPSW
*(DWORD*)(0x0054afc8) = 1; // LOG_EN_ATA
*(DWORD*)(0x00544b78) = 1; // LOG_EN_DONGLE
*(DWORD*)(0x00536c98) = 1; // LOG_EN_EEPROM
*(DWORD*)(0x00537834) = 1; // LOG_EN_GCATCHER
*(DWORD*)(0x00537d4c) = 1; // LOG_EN_GDELIVER
*(DWORD*)(0x0053828c) = 1; // LOG_EN_GFETCHER
*(DWORD*)(0x00547fd0) = 1; // LOG_EN_HWRESET
*(DWORD*)(0x00547fd8) = 1; // LOG_EN_PLATFORM
*(DWORD*)(0x00548268) = 1; // LOG_EN_HM
*(DWORD*)(0x00536f30) = 1; // LOG_EN_BACKUP
*(DWORD*)(0x00547fcc) = 1; // LOG_EN_CMOS
*(DWORD*)(0x005382d8) = 1; // LOG_EN_INSTALL
*(DWORD*)(0x00538818) = 1; // LOG_EN_JVS
*(DWORD*)(0x00544b68) = 1; // LOG_EN_JVSP
*(DWORD*)(0x0054b3d8) = 1; // LOG_EN_MASTER
*(DWORD*)(0x00548280) = 1; // LOG_EN_NETWORK
*(DWORD*)(0x00548050) = 1; // LOG_EN_RTC
*(DWORD*)(0x00536c94) = 1; // LOG_EN_SRAM
*(DWORD*)(0x005487f8) = 1; // LOG_EN_STORAGE
*(DWORD*)(0x00536c90) = 1; // LOG_EN_SYS
// end of am
*(DWORD*)(0x00536c70) = 1; // LOG_EN_PCPT
} else if (wcscmp(exePath, L"maimai_dump_.exe") == 0) {
*(DWORD*)(0x00c8ab7c) = 1; // LOG_EN_SRAM
*(DWORD*)(0x00f406f0) = 1; // LOG_EN_CABINET_DL
*(DWORD*)(0x00c88680) = 1; // LOG_EN_INSTALL
*(DWORD*)(0x00c8ab78) = 1; // LOG_EN_EEPROM
*(DWORD*)(0x00c88608) = 1; // LOG_EN_PLATFORM
*(DWORD*)(0x00c88dd0) = 1; // LOG_EN_STORAGE
*(DWORD*)(0x00ca25ec) = 1; // LOG_EN_PCP
*(DWORD*)(0x00c96ed0) = 1; // LOG_EN_HM
*(DWORD*)(0x00f406ec) = 1; // LOG_EN_HTTP
*(DWORD*)(0x00c89ca8) = 1; // LOG_EN_DIPSW
*(DWORD*)(0x00c96ed8) = 1; // LOG_EN_DONGLE
*(DWORD*)(0x00c9a83c) = 1; // LOG_EN_JVSP
*(DWORD*)(0x00c89730) = 1; // LOG_EN_NETWORK
*(DWORD*)(0x00c9a440) = 1; // LOG_EN_JVST_THREAD
*(DWORD*)(0x00c8ab80) = 1; // LOG_EN_JVS
*(DWORD*)(0x00c89fc8) = 1; // LOG_EN_MASTER
*(DWORD*)(0x00c88bb8) = 1; // LOG_EN_RTC
*(DWORD*)(0x00c9e420) = 1; // LOG_EN_JVST_DRIVER
*(DWORD*)(0x00c89320) = 1; // LOG_EN_BACKUP
*(DWORD*)(0x00c88bb4) = 1; // LOG_EN_HWRESET
*(DWORD*)(0x00c830e8) = 1; // LOG_EN
} else if (wcscmp(exePath, L"mxnetwork.exe") == 0) {
*(DWORD*)(0x004438e0) = 1; // LOG_EN_PCP
*(DWORD*)(0x004433f8) = 1; // LOG_EN_UTL
} else { } else {
log_warning("traces", "No known traces for %ls", exePath); char exePathC[MAX_PATH + 1];
WideCharToMultiByte(CP_ACP, 0, exePath, -1, exePathC, sizeof exePathC, NULL, NULL);
for (int i = 0; i < patches.nopatchsets; i++) {
patchset_t* patchset = patches.patchsets[i];
// Require the binary explicitly named
if (patchset->binary_name == NULL || strcmp(patchset->binary_name, exePathC) != 0) {
continue;
}
if (!patchset->apply) continue;
for (int j = 0; j < patchset->nopatches; j++) {
patch_t patch = patchset->patches[j];
if (memcmp(patch.from, (void*)patch.offset, patch.count) != 0) {
log_error(BOOT_LOGGER, "Patch %s[%d] failed! from-value missmatch", patchset->name, j);
continue;
}
memcpy((void*)patch.offset, patch.to, patch.count);
log_misc(BOOT_LOGGER, "Patched %d bytes at %08x", patch.count, patch.offset);
}
}
} }
free_patches(&patches);
} }
void prebind_hooks() { void prebind_hooks() {
hook_setupapi(); hook_setupapi();
hook_commio(); hook_commio();
hook_io(); hook_io();
hook_processes();
hook_network();
} }
void init_injection() { void init_injection() {
@ -114,8 +60,6 @@ void init_injection() {
setup_logging(); setup_logging();
log_info(BOOT_LOGGER, "Handover complete. Now executing within %ls", exePath); log_info(BOOT_LOGGER, "Handover complete. Now executing within %ls", exePath);
dmi_build_default();
enable_traces(); enable_traces();
setup_columba(); setup_columba();

View File

@ -4,6 +4,17 @@
#include "../lib/mice/mice.h" #include "../lib/mice/mice.h"
#include "files.h" #include "files.h"
// Much easier than pulling in winddk.h
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
typedef struct {
PHYSICAL_ADDRESS addr;
DWORD data_type;
DWORD bytes;
} columba_request;
#define DMI_HEADER_START 0x000f0000
#define DMI_TABLES_START 0x000f1000
BOOL columba_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, BOOL columba_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) {
switch (dwIoControlCode) { switch (dwIoControlCode) {
@ -12,15 +23,23 @@ BOOL columba_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nIn
"DeviceIoControl(<columba>, <read dmi>, 0x%p, 0x%x, -, " "DeviceIoControl(<columba>, <read dmi>, 0x%p, 0x%x, -, "
"0x%x, -, -)", "0x%x, -, -)",
lpInBuffer, nInBufferSize, nOutBufferSize); lpInBuffer, nInBufferSize, nOutBufferSize);
columba_request* request = (columba_request*)lpInBuffer;
log_info("columba", "Physical read: 0x%04x %ss at %08X", request->bytes,
request->data_type == 1 ? "byte"
: request->data_type == 2 ? "short"
: request->data_type == 4 ? "long"
: "void",
request->addr);
DWORD requested_size = request->data_type * request->bytes;
memset(lpOutBuffer, 0, nOutBufferSize); memset(lpOutBuffer, 0, nOutBufferSize);
if (((LPWORD)lpInBuffer)[0] == 0x0000) { if (request->addr.QuadPart == DMI_HEADER_START) {
DMI_HEADER dmi = { DMI_HEADER dmi = {
.Signature = {'_', 'D', 'M', 'I', '_'}, .Signature = {'_', 'D', 'M', 'I', '_'},
.Checksum = 0, .Checksum = 0,
.StructLength = dmi_size, .StructLength = dmi_size,
.StructAddr = 0xdeeabeef, .StructAddr = DMI_TABLES_START,
.NumberOfStructs = 0x20, .NumberOfStructs = 0x20,
.BCDRevision = 0, .BCDRevision = 0,
.Reserved = 0, .Reserved = 0,
@ -28,10 +47,13 @@ BOOL columba_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nIn
dmi.Checksum = dmi_calc_checksum((char*)&dmi, 15); dmi.Checksum = dmi_calc_checksum((char*)&dmi, 15);
memcpy(lpOutBuffer, &dmi, sizeof(DMI_HEADER)); memcpy(lpOutBuffer, &dmi, sizeof(DMI_HEADER));
if (lpBytesReturned) *lpBytesReturned = sizeof(DMI_HEADER); if (lpBytesReturned) *lpBytesReturned = requested_size;
} else { } else if (request->addr.QuadPart == DMI_TABLES_START) {
memcpy(lpOutBuffer, dmi_table, dmi_size); memcpy(lpOutBuffer, dmi_table, dmi_size);
if (lpBytesReturned) *lpBytesReturned = dmi_size; if (lpBytesReturned) *lpBytesReturned = 0x10000;
} else {
log_error("columna", "Request to unmapped memory location: %08x", request->addr);
return FALSE;
} }
break; break;
@ -40,10 +62,12 @@ BOOL columba_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nIn
return FALSE; return FALSE;
} }
return FALSE; return TRUE;
} }
void setup_columba() { void setup_columba() {
dmi_build_default();
file_hook_t* columba = new_file_hook(L"\\\\.\\columba"); file_hook_t* columba = new_file_hook(L"\\\\.\\columba");
columba->DeviceIoControl = &columba_DeviceIoControl; columba->DeviceIoControl = &columba_DeviceIoControl;

View File

@ -1,3 +1,4 @@
#include "../com.h"
#include "../files.h" #include "../files.h"
#include <initguid.h> #include <initguid.h>

View File

@ -3,16 +3,16 @@
#include "../lib/mice/mice.h" #include "../lib/mice/mice.h"
#include "mx.h" #include "mx.h"
BOOL mxjvs_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, BOOL JVS_SENSE = false;
DWORD nInBufferSize, LPVOID lpOutBuffer,
DWORD nOutBufferSize, LPDWORD lpBytesReturned, BOOL mxjvs_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
LPOVERLAPPED lpOverlapped) { DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) {
switch (dwIoControlCode) { switch (dwIoControlCode) {
case IOCTL_MXJVS_EXCHANGE: case IOCTL_MXJVS_EXCHANGE:
log_misc("mxjvs", log_error("mxjvs",
"DeviceIoControl(<mxjvs>, <exchange>, 0x%p, 0x%x, -, " "DeviceIoControl(<mxjvs>, <exchange>, 0x%p, 0x%x, -, "
"0x%x, -, -)", "0x%x, -, -)",
lpInBuffer, nInBufferSize, nOutBufferSize); lpInBuffer, nInBufferSize, nOutBufferSize);
// mxjvsDevice->exchange( // mxjvsDevice->exchange(
// lpbyte(lpInBuffer), nInBufferSize & 0xFFFF, // lpbyte(lpInBuffer), nInBufferSize & 0xFFFF,
@ -29,9 +29,38 @@ BOOL mxjvs_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer,
return TRUE; return TRUE;
} }
BOOL mxjvs_SetupComm(void* com, DWORD dwInQueue, DWORD dwOutQueue) { return TRUE; }
BOOL mxjvs_PurgeComm(void* com, DWORD dwFlags) { return TRUE; }
BOOL mxjvs_GetCommState(void* com, LPDCB lpDCB) { return TRUE; }
BOOL mxjvs_SetCommTimeouts(void* com, LPCOMMTIMEOUTS lpDCB) { return TRUE; }
BOOL mxjvs_GetCommModemStatus(void* com, LPDWORD lpModelStat) {
*lpModelStat = JVS_SENSE ? MS_DSR_ON : 0;
return TRUE;
}
BOOL mxjvs_SetCommState(void* com, LPDCB lpDCB) {
char PARITY[] = {'N', 'O', 'E', 'M', 'S'};
char* STOP[] = {"1", "1.5", "2"};
log_info("mxjvs", "Switching to %d baud (%d%c%s)", lpDCB->BaudRate, lpDCB->ByteSize, PARITY[lpDCB->Parity],
STOP[lpDCB->StopBits]);
return TRUE;
}
void setup_mxjvs() { void setup_mxjvs() {
file_hook_t* mxjvs = new_file_hook(L"\\\\.\\mxjvs"); file_hook_t* mxjvs = new_file_hook(L"\\\\.\\mxjvs");
mxjvs->DeviceIoControl = &mxjvs_DeviceIoControl; mxjvs->DeviceIoControl = &mxjvs_DeviceIoControl;
com_hook_t* jvscom = new_com_hook(-1);
jvscom->GetCommState = mxjvs_GetCommState;
jvscom->SetCommState = mxjvs_SetCommState;
jvscom->SetCommTimeouts = mxjvs_SetCommTimeouts;
jvscom->SetupComm = mxjvs_SetupComm;
jvscom->PurgeComm = mxjvs_PurgeComm;
jvscom->GetCommModemStatus = mxjvs_GetCommModemStatus;
hook_file(mxjvs); hook_file(mxjvs);
hook_com(jvscom);
free(jvscom->virtual_handle);
jvscom->virtual_handle = mxjvs->virtual_handle;
} }

View File

@ -208,15 +208,15 @@ BOOL mxsmbus_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nIn
case PCA9535_WRITE: case PCA9535_WRITE:
switch (i2c_packet->reg) { switch (i2c_packet->reg) {
case PCA9535_OUT1: case PCA9535_OUT1:
log_error("mxsmbus", "pca9535 OUT1: %02x %02x", i2c_packet->data[0], log_info("mxsmbus", "pca9535 OUT1: %02x %02x", i2c_packet->data[0],
i2c_packet->data[1]); i2c_packet->data[1]);
break; break;
case PCA9535_INV0: case PCA9535_INV0:
log_error("mxsmbus", "pca9535 INV0: %02x %02x", i2c_packet->data[0], log_info("mxsmbus", "pca9535 INV0: %02x %02x", i2c_packet->data[0],
i2c_packet->data[1]); i2c_packet->data[1]);
break; break;
case PCA9535_INV1: case PCA9535_INV1:
log_error("mxsmbus", "pca9535 INV1: %02x %02x", i2c_packet->data[0], log_info("mxsmbus", "pca9535 INV1: %02x %02x", i2c_packet->data[0],
i2c_packet->data[1]); i2c_packet->data[1]);
break; break;
default: default:

View File

@ -34,7 +34,7 @@ BOOL mxsram_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInB
switch (dwIoControlCode) { switch (dwIoControlCode) {
case IOCTL_MXSRAM_PING: // Get version case IOCTL_MXSRAM_PING: // Get version
log_misc("mxsram", log_info("mxsram",
"DeviceIoControl(<mxsram>, <ping>, 0x%p, 0x%x, -, 0x%x, " "DeviceIoControl(<mxsram>, <ping>, 0x%p, 0x%x, -, 0x%x, "
"-, -)", "-, -)",
lpInBuffer, nInBufferSize, nOutBufferSize); lpInBuffer, nInBufferSize, nOutBufferSize);
@ -43,7 +43,7 @@ BOOL mxsram_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInB
if (lpBytesReturned) *lpBytesReturned = 4; if (lpBytesReturned) *lpBytesReturned = 4;
break; break;
case IOCTL_DISK_GET_DRIVE_GEOMETRY: case IOCTL_DISK_GET_DRIVE_GEOMETRY:
log_misc("mxsram", log_info("mxsram",
"DeviceIoControl(<mxsram>, <get drive geom>, 0x%p, " "DeviceIoControl(<mxsram>, <get drive geom>, 0x%p, "
"0x%x, -, 0x%x, -, -)", "0x%x, -, 0x%x, -, -)",
lpInBuffer, nInBufferSize, nOutBufferSize); lpInBuffer, nInBufferSize, nOutBufferSize);
@ -58,7 +58,7 @@ BOOL mxsram_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInB
if (lpBytesReturned) *lpBytesReturned = sizeof(DISK_GEOMETRY); if (lpBytesReturned) *lpBytesReturned = sizeof(DISK_GEOMETRY);
break; break;
case IOCTL_MXSRAM_GET_SECTOR_SIZE: case IOCTL_MXSRAM_GET_SECTOR_SIZE:
log_misc("mxsram", log_info("mxsram",
"DeviceIoControl(<mxsram>, <get sector size>, 0x%p, " "DeviceIoControl(<mxsram>, <get sector size>, 0x%p, "
"0x%x, -, 0x%x, -, -)", "0x%x, -, 0x%x, -, -)",
lpInBuffer, nInBufferSize, nOutBufferSize); lpInBuffer, nInBufferSize, nOutBufferSize);
@ -118,6 +118,7 @@ void setup_mxsram() {
sram_restore(); sram_restore();
file_hook_t* mxsram = new_file_hook(L"\\\\.\\mxsram"); file_hook_t* mxsram = new_file_hook(L"\\\\.\\mxsram");
mxsram->DeviceIoControl = &mxsram_DeviceIoControl;
mxsram->SetFilePointer = &mxsram_SetFilePointer; mxsram->SetFilePointer = &mxsram_SetFilePointer;
mxsram->ReadFile = &mxsram_ReadFile; mxsram->ReadFile = &mxsram_ReadFile;
mxsram->WriteFile = &mxsram_WriteFile; mxsram->WriteFile = &mxsram_WriteFile;

View File

@ -32,10 +32,10 @@ BOOL mxsuperio_DeviceIoControl(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD n
if (lpBytesReturned) *lpBytesReturned = 4; if (lpBytesReturned) *lpBytesReturned = 4;
break; break;
case IOCTL_MXSUPERIO_READ: case IOCTL_MXSUPERIO_READ:
log_warning("mxsuperio", log_misc("mxsuperio",
"DeviceIoControl(<mxsuperio>, <read>, 0x%p, 0x%x, -, " "DeviceIoControl(<mxsuperio>, <read>, 0x%p, 0x%x, -, "
"0x%x, -, -)", "0x%x, -, -)",
lpInBuffer, nInBufferSize, nOutBufferSize); lpInBuffer, nInBufferSize, nOutBufferSize);
/* /*
BYTE chipId = 0|1 BYTE chipId = 0|1

View File

@ -19,7 +19,8 @@ file_hook_t* new_file_hook(LPCWSTR filename) {
} }
void hook_file(file_hook_t* hook) { void hook_file(file_hook_t* hook) {
hook->next = NULL; hook->next = NULL;
hook->virtual_handle = NULL; hook->virtual_handle = (LPHANDLE)malloc(sizeof(HANDLE));
*hook->virtual_handle = NULL;
if (file_hook_list == NULL) { if (file_hook_list == NULL) {
file_hook_list = hook; file_hook_list = hook;
return; return;
@ -30,8 +31,9 @@ void hook_file(file_hook_t* hook) {
hl->next = hook; hl->next = hook;
}; };
drive_redirect_t DRIVE_REDIRECT_TABLE[] = {{"Y:\\", ".\\dev\\Y\\"}, drive_redirect_t DRIVE_REDIRECT_TABLE[] = {
{"C:\\Documents and Settings\\AppUser\\temp\\", ".\\dev\\temp\\"}}; {.drive = "Y:\\", .path = ".\\dev\\Y\\"},
{.drive = "C:\\Documents and Settings\\AppUser\\temp\\", .path = ".\\dev\\temp\\"}};
char* redirect_path(char* path) { char* redirect_path(char* path) {
for (int i = 0; i < sizeof DRIVE_REDIRECT_TABLE / sizeof DRIVE_REDIRECT_TABLE[0]; i++) { for (int i = 0; i < sizeof DRIVE_REDIRECT_TABLE / sizeof DRIVE_REDIRECT_TABLE[0]; i++) {
@ -60,30 +62,17 @@ char* redirect_path(char* path) {
HANDLE WINAPI FakeCreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, HANDLE WINAPI FakeCreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
// for (int i = 0; i < (sizeof HOOKED_HANDLES) / (sizeof HANDLE_HOOK);
// i++) { HANDLE_HOOK hook = HOOKED_HANDLES[i]; if (lpFileName &&
// wcscmp(lpFileName, hook.wName) == 0) {
// log_warning(HOOKS_LOGGER, "CreateFileW intercepting driver file %ls",
// hook.wName); return hook.handle;
// }
// }
// HANDLE result = TrueCreateFileW(lpFileName, dwDesiredAccess,
// dwShareMode, lpSecurityAttributes, dwCreationDisposition,
// dwFlagsAndAttributes, hTemplateFile); log_misc(HOOKS_LOGGER,
// "CreateFileW(%ls) -> 0x%p", lpFileName, result);
HANDLE handle = NULL; HANDLE handle = NULL;
file_hook_t* hook = file_hook_list; file_hook_t* hook = file_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (wcscmp(lpFileName, hook->filename) == 0) { if (wcscmp(lpFileName, hook->filename) == 0) {
if (hook->virtual_handle == NULL) { if (*hook->virtual_handle == NULL) {
// TODO: Assign handles better! // TODO: Assign handles better!
hook->virtual_handle = fake_handle; *hook->virtual_handle = fake_handle;
((size_t)fake_handle)++; ((size_t)fake_handle)++;
} }
handle = hook->virtual_handle; handle = *hook->virtual_handle;
break; break;
} }
hook = hook->next; hook = hook->next;
@ -119,18 +108,23 @@ BOOL WINAPI FakeDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lp
file_hook_t* hook = file_hook_list; file_hook_t* hook = file_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hDevice) { if (*hook->virtual_handle == hDevice) {
if (hook->DeviceIoControl) { if (hook->DeviceIoControl) {
// TODO: Less jank // TODO: Less jank
if (lpOverlapped != NULL) SetEvent(lpOverlapped->hEvent); if (lpOverlapped != NULL) SetEvent(lpOverlapped->hEvent);
return hook->DeviceIoControl(dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, return hook->DeviceIoControl(dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize,
lpBytesReturned, lpOverlapped); lpBytesReturned, lpOverlapped);
} else {
log_error(HOOKS_LOGGER, "DeviceIoControl(%ls) unimplemented", hook->filename);
return FALSE;
} }
} }
hook = hook->next; hook = hook->next;
} }
// log_warning(HOOKS_LOGGER, "DeviceIoControl(0x%p, 0x%08x, -, -, -, -, 0, 0)", hDevice, dwIoControlCode);
return TrueDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, return TrueDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize,
lpBytesReturned, lpOverlapped); lpBytesReturned, lpOverlapped);
} }
@ -138,9 +132,12 @@ BOOL WINAPI FakeDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lp
DWORD WINAPI FakeSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) { DWORD WINAPI FakeSetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) {
file_hook_t* hook = file_hook_list; file_hook_t* hook = file_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile) { if (*hook->virtual_handle == hFile) {
if (hook->SetFilePointer) { if (hook->SetFilePointer) {
return hook->SetFilePointer(lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod); return hook->SetFilePointer(lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod);
} else {
log_error(HOOKS_LOGGER, "SetFilePointer(%ls) unimplemented", hook->filename);
return FALSE;
} }
} }
hook = hook->next; hook = hook->next;
@ -153,9 +150,12 @@ DWORD WINAPI FakeWriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesT
LPOVERLAPPED lpOverlapped) { LPOVERLAPPED lpOverlapped) {
file_hook_t* hook = file_hook_list; file_hook_t* hook = file_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile) { if (*hook->virtual_handle == hFile) {
if (hook->WriteFile) { if (hook->WriteFile) {
return hook->WriteFile(lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); return hook->WriteFile(lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
} else {
log_error(HOOKS_LOGGER, "WriteFile(%ls) unimplemented", hook->filename);
return FALSE;
} }
} }
hook = hook->next; hook = hook->next;
@ -168,9 +168,12 @@ BOOL WINAPI FakeReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRe
LPOVERLAPPED lpOverlapped) { LPOVERLAPPED lpOverlapped) {
file_hook_t* hook = file_hook_list; file_hook_t* hook = file_hook_list;
while (hook != NULL) { while (hook != NULL) {
if (hook->virtual_handle == hFile) { if (*hook->virtual_handle == hFile) {
if (hook->WriteFile) { if (hook->ReadFile) {
return hook->ReadFile(lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped); return hook->ReadFile(lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
} else {
log_error(HOOKS_LOGGER, "ReadFile(%ls) unimplemented", hook->filename);
return FALSE;
} }
} }
hook = hook->next; hook = hook->next;
@ -190,6 +193,11 @@ BOOL WINAPI FakeCloseHandle(HANDLE hObject) {
return TrueCloseHandle(hObject); return TrueCloseHandle(hObject);
} }
int WINAPIV Fake_stat64i32(const char* path, struct _stat64i32* buffer) {
path = redirect_path((char*)path);
return True_stat64i32(path, buffer);
};
void hook_io() { void hook_io() {
hook("Kernel32.dll", "DeviceIoControl", FakeDeviceIoControl, (void**)&TrueDeviceIoControl, 5); hook("Kernel32.dll", "DeviceIoControl", FakeDeviceIoControl, (void**)&TrueDeviceIoControl, 5);
@ -201,5 +209,5 @@ void hook_io() {
hook("Kernel32.dll", "WriteFile", FakeWriteFile, (void**)&TrueWriteFile, 6); hook("Kernel32.dll", "WriteFile", FakeWriteFile, (void**)&TrueWriteFile, 6);
hook("Kernel32.dll", "ReadFile", FakeReadFile, (void**)&TrueReadFile, 6); hook("Kernel32.dll", "ReadFile", FakeReadFile, (void**)&TrueReadFile, 6);
// hook("MSVCR90.DLL", "_stat64i32", Fake_stat64i32, &True_stat64i32, 5); hook("MSVCR90.DLL", "_stat64i32", Fake_stat64i32, (void**)&True_stat64i32, 5);
} }

View File

@ -35,6 +35,7 @@ typedef BOOL(FnWriteFile)(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);
typedef BOOL(FnReadFile)(LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, typedef BOOL(FnReadFile)(LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped); LPOVERLAPPED lpOverlapped);
static int(WINAPIV *True_stat64i32)(const char* path, struct _stat64i32* buffer);
#define _WriteFile (TrueWriteFile ? TrueWriteFile : WriteFile) #define _WriteFile (TrueWriteFile ? TrueWriteFile : WriteFile)
#define _ReadFile (TrueReadFile ? TrueReadFile : ReadFile) #define _ReadFile (TrueReadFile ? TrueReadFile : ReadFile)
@ -54,7 +55,7 @@ typedef struct file_hook {
FnWriteFile* WriteFile; FnWriteFile* WriteFile;
FnReadFile* ReadFile; FnReadFile* ReadFile;
HANDLE virtual_handle; LPHANDLE virtual_handle;
struct file_hook* next; struct file_hook* next;
} file_hook_t; } file_hook_t;

View File

@ -14,6 +14,8 @@ shared_library(
'setupapi.c', 'setupapi.c',
'com.c', 'com.c',
'comdevice.c', 'comdevice.c',
'processes.c',
'network.c',
], ],
link_with: [ link_with: [
dmi_lib, dmi_lib,

View File

@ -0,0 +1,59 @@
#include "network.h"
#include "../lib/mice/mice.h"
int WINAPI Fake_connect(SOCKET s, const SOCKADDR* name, int namelen) {
ULONG addr = _byteswap_ulong(((SOCKADDR_IN*)name)->sin_addr);
USHORT port = _byteswap_ushort(((SOCKADDR_IN*)name)->sin_port);
log_info("connect", "%hhu.%hhu.%hhu.%hhu:%hu", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff,
addr & 0xff, port);
return True_connect(s, name, namelen);
}
DWORD WINAPI FakeGetIfTable(PMIB_IFTABLE pIfTable, PULONG pdwSize, BOOL bOrder) {
DWORD ret = TrueGetIfTable(pIfTable, pdwSize, bOrder);
if (ret == NO_ERROR) {
for (size_t i = 0; i < pIfTable->dwNumEntries; i++) {
pIfTable->table[i].bPhysAddr[0] = 0x00;
pIfTable->table[i].bPhysAddr[1] = 0xD0;
pIfTable->table[i].bPhysAddr[2] = 0xF1;
}
}
return ret;
}
const char* INTERCEPT_DNS[] = {
"naominet.jp", // Startup
"ib.naominet.jp", // Billing
"aime.naominet.jp", // Aime (duh)
"tenporouter.loc", // Routers
"bbrouter.loc", "mobirouter.loc", "dslrouter.loc",
};
DNS_RECORDA dummy_record;
DNS_STATUS WINAPI FakeDnsQuery_A(PCSTR pszName, WORD wType, DWORD Options, PVOID pExtra, PDNS_RECORDA* ppQueryResults,
PVOID* pReserved) {
log_misc("dns", "DNS lookup for %s", pszName);
if (ppQueryResults) {
for (size_t i = 0; i < sizeof INTERCEPT_DNS / sizeof INTERCEPT_DNS[0]; i++) {
if (strcmp(pszName, INTERCEPT_DNS[i]) == 0) {
log_info("dns", "Replacing %s with 10.0.0.4", pszName);
// We only support replacing at most one address, but that's all we'll ever need to!
(*ppQueryResults) = &dummy_record;
(*ppQueryResults)->pNext = NULL;
(*ppQueryResults)->wType = DNS_TYPE_A;
(*ppQueryResults)->Data.A.IpAddress = 0x0400000a;
return ERROR_SUCCESS;
}
}
}
return TrueDnsQuery_A(pszName, wType, Options, pExtra, ppQueryResults, pReserved);
};
void hook_network() {
hook("Ws2_32.dll", "connect", Fake_connect, (void**)&True_connect, 5);
hook("Iphlpapi.dll", "GetIfTable", FakeGetIfTable, (void**)&TrueGetIfTable, 5);
hook("Dnsapi.dll", "DnsQuery_A", FakeDnsQuery_A, (void**)&TrueDnsQuery_A, 5);
}

View File

@ -0,0 +1,14 @@
#include <Winsock2.h>
#include <Windows.h>
#include <iphlpapi.h>
#include <windns.h>
static int(WINAPI* True_connect)(SOCKET s, const SOCKADDR* name, int namelen);
static DWORD(WINAPI* TrueGetIfTable)(PMIB_IFTABLE pIfTable, PULONG pdwSize, BOOL bOrder);
static DNS_STATUS(WINAPI* TrueDnsQuery_A)(PCSTR pszName, WORD wType, DWORD Options, PVOID pExtra,
PDNS_RECORDA* ppQueryResults, PVOID* pReserved);
void hook_network();

View File

@ -0,0 +1,27 @@
#include "processes.h"
#include "../lib/mice/mice.h"
const wchar_t* HOOK_BINARIES[] = {
L"app\\ALLNetProc.exe",
L"app\\CameraUploader.exe",
L"app\\GmSync.exe",
};
BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation) {
log_info("spawn", "CreateProcessW %ls %ls", lpApplicationName, lpCommandLine);
CHAR applicationName[MAX_PATH + 1];
WideCharToMultiByte(CP_ACP, 0, lpApplicationName, -1, applicationName, sizeof applicationName, NULL, NULL);
CHAR commandLine[MAX_PATH + 1];
WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, sizeof commandLine, NULL, NULL);
HANDLE child = start_and_inject(applicationName, commandLine, MICELIB);
return child != NULL;
}
void hook_processes() { hook("Kernel32.dll", "CreateProcessW", FakeCreateProcessW, (void**)&TrueCreateProcessW, 6); }

View File

@ -0,0 +1,11 @@
#include <Windows.h>
#include <stdbool.h>
#include <stdio.h>
static BOOL(WINAPI* TrueCreateProcessW)(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation);
void hook_processes();

View File

@ -68,7 +68,7 @@ BOOL WINAPI FakeSetupDiGetDeviceInterfaceDetailA(HDEVINFO DeviceInfoSet, PSP_DEV
FAKE_DEVICE* device = fake_devices; FAKE_DEVICE* device = fake_devices;
while (device != NULL) { while (device != NULL) {
if (device->handle == DeviceInfoSet && (ULONG_PTR)device->path == DeviceInterfaceData->Reserved) { if (device->handle == DeviceInfoSet && (ULONG_PTR)device->path == DeviceInterfaceData->Reserved) {
log_info("setupapi", "Yoinked SetupDiGetDeviceInterfaceDetailA"); log_info("setupapi", "Intercepted SetupDiGetDeviceInterfaceDetailA");
const WCHAR* res = (WCHAR*)DeviceInterfaceData->Reserved; const WCHAR* res = (WCHAR*)DeviceInterfaceData->Reserved;
int new_len = (wcslen(res) + 1) * (sizeof *res); int new_len = (wcslen(res) + 1) * (sizeof *res);

View File

@ -1,4 +0,0 @@
#include <Windows.h>
#include <stdbool.h>
HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject);

View File

@ -1,4 +1,5 @@
#include "locate.h" #include "locate.h"
#include "../lib/mice/mice.h"
const char* KNOWN_GAMES[] = { const char* KNOWN_GAMES[] = {
// Preferentially use a decrypted dump if present // Preferentially use a decrypted dump if present
@ -6,14 +7,6 @@ const char* KNOWN_GAMES[] = {
"maimai.exe", "maimai.exe",
}; };
#ifndef MICELIB
#ifdef MICE_WIN32
#define MICELIB "mice86.dll"
#else // MICE_WIN32
#define MICELIB "mice64.dll"
#endif // MICE_WIN32
#endif // MICELIB
bool locate_file(char* path, size_t len, const char* exe) { bool locate_file(char* path, size_t len, const char* exe) {
WIN32_FIND_DATA fdFile; WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL; HANDLE hFind = NULL;

View File

@ -2,7 +2,6 @@
#include <stdio.h> #include <stdio.h>
#include "../lib/mice/mice.h" #include "../lib/mice/mice.h"
#include "exe.h"
#include "locate.h" #include "locate.h"
const char* VERSION = "0.0-pre"; const char* VERSION = "0.0-pre";

View File

@ -4,7 +4,6 @@ executable(
win_subsystem: 'console', win_subsystem: 'console',
sources: [ sources: [
'locate.c', 'locate.c',
'exe.c',
'main.c', 'main.c',
rc, rc,
], ],

View File

@ -5,9 +5,9 @@ WORD dmi_size = 0;
size_t _dmi_max = 0; size_t _dmi_max = 0;
DMI_BIOS deafult_dmi_bios = { DMI_BIOS deafult_dmi_bios = {
.Type = 0x00, .Head.Type = 0x00,
.Length = 0x12, .Head.Length = 0x12,
.Handle = 0x0000, .Head.Handle = 0x0000,
.Vendor = 0x00, .Vendor = 0x00,
.Version = 0x00, .Version = 0x00,
.StartSegment = 0x0000, .StartSegment = 0x0000,
@ -17,9 +17,10 @@ DMI_BIOS deafult_dmi_bios = {
}; };
DMI_SYSTEM default_dmi_system = { DMI_SYSTEM default_dmi_system = {
.Type = 0x01, .Head.Type = 0x01,
.Length = 0x08, .Head.Length = 0x08,
.Handle = 0x0000, .Head.Handle = 0x0001,
// TODO: Are these used?
.Manufacturer = 0x00, .Manufacturer = 0x00,
.ProductName = 0x00, .ProductName = 0x00,
.Version = 0x00, .Version = 0x00,
@ -27,84 +28,82 @@ DMI_SYSTEM default_dmi_system = {
}; };
DMI_STRING deafult_dmi_string = { DMI_STRING deafult_dmi_string = {
.Type = 0x0b, .Head.Type = 0x0b,
.Length = 0x05, .Head.Length = 0x05,
.Handle = 0x0000, .Head.Handle = 0x0002,
.NoStrings = 0x00, .NoStrings = 0x00,
}; };
static void dmi_init(void) { static void dmi_init(void) {
if (dmi_table) free(dmi_table); if (dmi_table) free(dmi_table);
dmi_table = (LPBYTE)malloc(0xff); _dmi_max = 0xff;
dmi_size = 0; dmi_table = (LPBYTE)malloc(_dmi_max);
_dmi_max = 1024; memset(dmi_table, 0, _dmi_max);
dmi_size = 0;
} }
static void dmi_append(void* data, size_t size) { static void dmi_append(void* data, size_t size) {
if (!dmi_table) return; if (!dmi_table) return;
while (dmi_size + (size + 1) >= _dmi_max) { while (dmi_size + (size + 1) >= _dmi_max) {
LPBYTE new_table = (LPBYTE)realloc(dmi_table, _dmi_max += 0xff); LPBYTE new_table = (LPBYTE)realloc(dmi_table, _dmi_max += 0xff);
if (!new_table) return; if (!new_table) return;
dmi_table = new_table; dmi_table = new_table;
} }
memcpy(dmi_table + dmi_size, &data, size); memcpy(dmi_table + dmi_size, data, size);
dmi_size += size; dmi_size += size;
dmi_table[dmi_size++] = 0; dmi_table[dmi_size++] = 0;
dmi_table[dmi_size++] = 0; dmi_table[dmi_size++] = 0;
} }
static void dmi_append_with_strings(void* data, size_t size, int num_strings, ...) { static void dmi_append_with_strings(void* data, size_t size, int num_strings, ...) {
va_list args; va_list args;
va_start(args, num_strings); va_start(args, num_strings);
dmi_append(data, size); dmi_append(data, size);
dmi_size -= 2; dmi_size -= 2;
for (int i = 0; i < num_strings; i++) { for (int i = 0; i < num_strings; i++) {
char* str = va_arg(args, char*); char* str = va_arg(args, char*);
int len = strlen(str); int len = strlen(str);
// #pragma warning(disable : 4996) strcpy((char*)dmi_table + dmi_size, str);
strcpy((char*)dmi_table + dmi_size, str); dmi_size += len + 1;
dmi_size += len + 1; dmi_table[dmi_size - 1] = 0;
dmi_table[dmi_size - 1] = 0; }
} dmi_table[dmi_size++] = 0;
dmi_table[dmi_size++] = 0;
va_end(args); va_end(args);
} }
void dmi_build_default() { void dmi_build_default() {
dmi_init(); dmi_init();
dmi_append(&deafult_dmi_bios, sizeof deafult_dmi_bios); dmi_append(&deafult_dmi_bios, sizeof deafult_dmi_bios);
// Platform AAM: Board type one of "Supermicro"(=1) or "Advantech"(=2) // Platform AAM: Board type one of "Supermicro"(=1) or "Advantech"(=2)
// Platform AAL: Board type one of "NEC"(=0) or "AAL2"(=3) // Platform AAL: Board type one of "NEC"(=0) or "AAL2"(=3)
dmi_append_with_strings(&default_dmi_system, sizeof default_dmi_system, 1, "Supermicro"); dmi_append_with_strings(&default_dmi_system, sizeof default_dmi_system, 1, "Supermicro");
deafult_dmi_string.NoStrings = 3; deafult_dmi_string.NoStrings = 3;
dmi_append_with_strings(&deafult_dmi_string, sizeof deafult_dmi_string, dmi_append_with_strings(&deafult_dmi_string, sizeof deafult_dmi_string,
// OEM strings: // OEM strings:
// 0: ?? // 0: ??
// 1: ?? // 1: ??
// 2: Platform ID (AAL, AAM) // 2: Platform ID (AAL, AAM)
// AAL = Nvidia drivers // AAL = Nvidia drivers
// AAM = AMD drivers // AAM = AMD drivers
// *** = No dedicated drivers // *** = No dedicated drivers
// 3: ?? // 3: ??
// 4: Board type (AAL, NEC, AAL2, " ", AAM, Supermicro, Advantech) // 4: Board type (AAL, NEC, AAL2, " ", AAM, Supermicro, Advantech)
// If present -> makes board = 4 // If present -> makes board = 4
// AAL = 4 // AAL = 4
// AAM = 4 // AAM = 4
// Supermicro = 4 // Supermicro = 4
// Advantech = 4 // Advantech = 4
5, ".", ".", "AAM", ".", "AAL" 5, ".", ".", "AAM", ".", "AAL");
);
} }
BYTE dmi_calc_checksum(const char* buf, int len) { BYTE dmi_calc_checksum(const char* buf, int len) {
int sum = 0; int sum = 0;
int a; int a;
for (a = 0; a < len; a++) for (a = 0; a < len; a++) sum += buf[a];
sum += buf[a]; return (BYTE)(sum == 0);
return (BYTE)(sum == 0);
} }

View File

@ -7,7 +7,14 @@ extern WORD dmi_size;
// #define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) // #define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
#pragma pack(1) #pragma pack(1)
typedef struct DMI_HEADER_ { typedef struct {
BYTE Type;
BYTE Length;
WORD Handle;
} DMI_SECTION_HEADER;
#pragma pack(1)
typedef struct {
CHAR Signature[5]; CHAR Signature[5];
BYTE Checksum; BYTE Checksum;
WORD StructLength; WORD StructLength;
@ -18,10 +25,8 @@ typedef struct DMI_HEADER_ {
} DMI_HEADER; } DMI_HEADER;
#pragma pack(1) #pragma pack(1)
typedef struct DMI_BIOS_ { typedef struct {
BYTE Type; DMI_SECTION_HEADER Head;
BYTE Length;
WORD Handle;
BYTE Vendor; BYTE Vendor;
BYTE Version; BYTE Version;
WORD StartSegment; WORD StartSegment;
@ -31,10 +36,8 @@ typedef struct DMI_BIOS_ {
} DMI_BIOS; } DMI_BIOS;
#pragma pack(1) #pragma pack(1)
typedef struct DMI_SYSTEM_ { typedef struct {
BYTE Type; DMI_SECTION_HEADER Head;
BYTE Length;
WORD Handle;
BYTE Manufacturer; BYTE Manufacturer;
BYTE ProductName; BYTE ProductName;
BYTE Version; BYTE Version;
@ -42,17 +45,14 @@ typedef struct DMI_SYSTEM_ {
} DMI_SYSTEM; } DMI_SYSTEM;
#pragma pack(1) #pragma pack(1)
typedef struct DMI_STRING_ { typedef struct {
BYTE Type; DMI_SECTION_HEADER Head;
BYTE Length;
WORD Handle;
BYTE NoStrings; BYTE NoStrings;
} DMI_STRING; } DMI_STRING;
static void dmi_init(void); static void dmi_init(void);
static void dmi_append(void* data, size_t size); static void dmi_append(void* data, size_t size);
static void dmi_append_with_strings(void* data, size_t size, int num_strings, static void dmi_append_with_strings(void* data, size_t size, int num_strings, ...);
...);
void dmi_build_default(void); void dmi_build_default(void);
BYTE dmi_calc_checksum(const char* buf, int len); BYTE dmi_calc_checksum(const char* buf, int len);

View File

@ -1,8 +1,8 @@
subdir('util') # This is the only lib that should ever be link_with'd by another lib subdir('util') # This is the only lib that should ever be link_with'd by another lib
subdir('am')
subdir('mice')
subdir('dmi')
subdir('json') subdir('json')
subdir('am')
subdir('dmi')
subdir('mice')
fs = import('fs') fs = import('fs')
# Handle the fact we aren't distributing the libpcp source # Handle the fact we aren't distributing the libpcp source

View File

@ -1,6 +1,5 @@
#include "exe.h" #include "exe.h"
#include "log.h"
#include "../lib/mice/mice.h"
bool inject_debug_wait(HANDLE process) { bool inject_debug_wait(HANDLE process) {
BOOL present; BOOL present;

View File

@ -0,0 +1,12 @@
#include <Windows.h>
#include <stdbool.h>
HANDLE start_and_inject(LPCSTR path, LPSTR cmdline, LPCSTR inject);
#ifndef MICELIB
#ifdef MICE_WIN32
#define MICELIB "mice86.dll"
#else // MICE_WIN32
#define MICELIB "mice64.dll"
#endif // MICE_WIN32
#endif // MICELIB

View File

@ -146,6 +146,46 @@ int log_error(const char* caller, const char* format, ...) {
// return ret; // return ret;
// }; // };
char* trim_string(char* string) {
size_t len = strlen(string) - 1;
DWORD oldProt;
while (len > 0 && (string[len] == '\n' || string[len] == '\r')) {
// TODO: Reassess this. Suspect it may be causing issues.
// Make sure we can write! This is a terrible hack, but it does work.
VirtualProtect(string + len, 1, PAGE_EXECUTE_READWRITE, &oldProt);
string[len--] = '\0';
VirtualProtect(string + len + 1, 1, oldProt, &oldProt);
}
return string;
}
HANDLE WINAPI FakeRegisterEventSourceA(LPCSTR lpUNCServerName, LPCSTR lpSourceName) { return (HANDLE)0xDEADBEEF; }
BOOL WINAPI FakeReportEventA(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, PSID lpUserSid,
WORD wNumStrings, DWORD dwDataSize, LPCSTR* lpStrings, LPVOID lpRawData) {
switch (wType) {
case EVENTLOG_SUCCESS:
case EVENTLOG_AUDIT_SUCCESS:
for (int i = 0; i < wNumStrings; i++) log_misc("evtlog", trim_string((char*)lpStrings[i]));
break;
case EVENTLOG_AUDIT_FAILURE:
case EVENTLOG_ERROR_TYPE:
for (int i = 0; i < wNumStrings; i++) log_error("evtlog", trim_string((char*)lpStrings[i]));
break;
case EVENTLOG_WARNING_TYPE:
for (int i = 0; i < wNumStrings; i++) log_warning("evtlog", trim_string((char*)lpStrings[i]));
break;
case EVENTLOG_INFORMATION_TYPE:
default:
for (int i = 0; i < wNumStrings; i++) log_info("evtlog", trim_string((char*)lpStrings[i]));
break;
}
return TRUE;
};
BOOL WINAPI FakeDeregisterEventSource(HANDLE hEventLog) { return TRUE; }
static VOID(WINAPI* TrueOutputDebugStringA)(LPCSTR lpOutputString); static VOID(WINAPI* TrueOutputDebugStringA)(LPCSTR lpOutputString);
VOID WINAPI FakeOutputDebugStringA(LPCSTR lpOutputString) { log_info("debug", "%s", lpOutputString); } VOID WINAPI FakeOutputDebugStringA(LPCSTR lpOutputString) { log_info("debug", "%s", lpOutputString); }
@ -165,4 +205,8 @@ void setup_logging() {
if (LOG_FILE == NULL) if (LOG_FILE == NULL)
LOG_FILE = CreateFileA("log.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); LOG_FILE = CreateFileA("log.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
hook("Advapi32.dll", "RegisterEventSourceA", FakeRegisterEventSourceA, &TrueRegisterEventSourceA);
hook("Advapi32.dll", "ReportEventA", FakeReportEventA, &TrueReportEventA);
hook("Advapi32.dll", "DeregisterEventSource", FakeDeregisterEventSource, &TrueDeregisterEventSource);
} }

View File

@ -3,11 +3,17 @@
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#define LOG_MISC static HANDLE(WINAPI* TrueRegisterEventSourceA)(LPCSTR lpUNCServerName, LPCSTR lpSourceName);
static BOOL(WINAPI* TrueReportEventA)(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, PSID lpUserSid,
WORD wNumStrings, DWORD dwDataSize, LPCSTR* lpStrings, LPVOID lpRawData);
static BOOL(WINAPI* TrueDeregisterEventSource)(HANDLE hEventLog);
// #define LOG_MISC
#define LOG_INFO #define LOG_INFO
#define LOG_WARNING #define LOG_WARNING
#define LOG_ERROR #define LOG_ERROR
#define COMM_LOGGER "comm"
#define HOOKS_LOGGER "hooks" #define HOOKS_LOGGER "hooks"
#define BOOT_LOGGER "boot" #define BOOT_LOGGER "boot"

View File

@ -3,5 +3,10 @@ mice_lib = static_library(
sources: [ sources: [
'hook.c', 'hook.c',
'log.c', 'log.c',
'exe.c',
'patch.c',
], ],
link_with: [
json_lib,
]
) )

View File

@ -1,3 +1,5 @@
#include "hook.h" #include "hook.h"
#include "ioctl.h" #include "ioctl.h"
#include "log.h" #include "log.h"
#include "exe.h"
#include "patch.h"

View File

@ -0,0 +1,224 @@
#include "patch.h"
bool fetch(json_value* object, char* name, json_value** value) {
if (object->type != json_object) return false;
for (size_t j = 0; j < object->u.object.length; j++) {
json_object_entry* entry = &(object->u.object.values[j]);
if (strncmp(name, entry->name, entry->name_length) == 0) {
*value = entry->value;
return true;
}
}
return false;
}
bool fetch_string(json_value* object, char* name, char** value) {
json_value* fetched;
if (!fetch(object, name, &fetched)) return false;
if (fetched->type != json_string) return false;
*value = fetched->u.string.ptr;
return true;
}
bool fetch_int(json_value* object, char* name, size_t* value) {
json_value* fetched;
if (!fetch(object, name, &fetched)) return false;
if (fetched->type != json_integer) return false;
*value = fetched->u.integer;
return true;
}
bool fetch_bool(json_value* object, char* name, bool* value) {
json_value* fetched;
if (!fetch(object, name, &fetched)) return false;
if (fetched->type != json_boolean) return false;
*value = fetched->u.boolean;
return true;
}
void free_patches(patches_t* patches) {
if (patches->nopatchsets == 0) return;
for (int i = patches->nopatchsets - 1; i >= 0; i--) {
if (patches->patchsets[i] != NULL) {
for (int j = 0; j < patches->patchsets[i]->nopatches; j++) {
if (patches->patchsets[i]->patches[j].from != NULL) free(patches->patchsets[i]->patches[j].from);
if (patches->patchsets[i]->patches[j].to != NULL) free(patches->patchsets[i]->patches[j].to);
}
free(patches->patchsets[i]);
}
}
patches->nopatchsets = 0;
}
json_value* load_json_from_file(char* path, char* error) {
FILE* fp = fopen(path, "r");
if (fp == NULL) {
if (error != NULL) sprintf(error, "Failed to open patch file");
return NULL;
}
fseek(fp, 0L, SEEK_END);
size_t sz = ftell(fp);
rewind(fp);
char* json_buf = (char*)malloc(sz);
if (json_buf == NULL) {
if (error != NULL) sprintf(error, "Failed to allocate file buffer");
fclose(fp);
return NULL;
}
if (!(sz = fread(json_buf, 1, sz, fp))) {
if (error != NULL) sprintf(error, "Failed to read file");
fclose(fp);
return NULL;
};
fclose(fp);
json_settings settings = {0};
return json_parse_ex(&settings, json_buf, sz, error);
}
bool parse_patches(patches_t* patches, json_value** set_json, int set_count, char* error) {
patches->nopatchsets = set_count;
patches->patchsets = (patchset_t**)malloc(set_count * sizeof(patchset_t*));
for (int i = 0; i < set_count; i++) patches->patchsets[i] = NULL;
error[0] = '\0';
for (int i = 0; i < set_count; i++) {
patchset_t* patchset = (patchset_t*)malloc(sizeof(patchset_t));
patches->patchsets[i] = patchset;
patchset->name = NULL;
patchset->description = NULL;
patchset->binary_name = NULL;
patchset->apply = false;
patchset->nopatches = 0;
char *name, *description, *binary_name;
if (!fetch_string(set_json[i], "name", &name)) {
snprintf(error, json_error_max, "'name' missing for patch %d", i);
goto failed;
}
if (!fetch_string(set_json[i], "description", &description)) {
snprintf(error, json_error_max, "'description' missing for patch %d (%s)", i, name);
goto failed;
}
if (!fetch_string(set_json[i], "binary_name", &binary_name)) binary_name = NULL;
bool apply;
if (!fetch_bool(set_json[i], "apply", &apply)) {
snprintf(error, json_error_max, "'apply' missing for patch %d (%s)", i, name);
goto failed;
}
json_value* set_patches;
if (!fetch(set_json[i], "patches", &set_patches) || set_patches->type != json_array) {
char* patches_file_path;
if (fetch_string(set_json[i], "patches_file", &patches_file_path)) {
char load_error[json_error_max];
set_patches = load_json_from_file(patches_file_path, load_error);
if (set_patches == NULL) {
log_warning("patcher", "Failed to load '%s': %s", patches_file_path, load_error);
continue;
}
if (set_patches->type != json_array) {
log_warning("patcher", "Failed to load '%s': not an array", patches_file_path);
continue;
}
} else {
snprintf(error, json_error_max, "Neither 'patches' nor 'patchs_file' in patch %d (%s)", i, name);
goto failed;
}
}
int count = set_patches->u.array.length;
patchset = (patchset_t*)realloc(patchset, sizeof(patchset_t) + (sizeof(patch_t) * count));
patches->patchsets[i] = patchset;
patchset->name = name;
patchset->description = description;
patchset->binary_name = binary_name;
patchset->apply = apply;
patchset->nopatches = count;
memset(patchset->patches, 0, count * (sizeof(patch_t)));
for (int j = 0; j < count; j++) {
json_value* this_patch = set_patches->u.array.values[j];
size_t at;
char *from, *to;
if (!fetch_int(this_patch, "at", &at)) {
char* at_str;
if (!fetch_string(this_patch, "at", &at_str)) {
snprintf(error, json_error_max, "'at' missing for patch %s[%d]", name, j);
goto failed;
}
if (strlen(at_str) > 8) {
snprintf(error, json_error_max, "invalid 'at' value for patch %s[%d]", name, j);
goto failed;
}
hex_to_bin(at_str, (void*)&at, 8);
at = _byteswap_ulong(at); // Endianess!
}
if (!fetch_string(this_patch, "from", &from)) {
snprintf(error, json_error_max, "'from' missing for patch %s[%d]", name, j);
goto failed;
}
if (!fetch_string(this_patch, "to", &to)) {
snprintf(error, json_error_max, "'to' missing for patch %s[%d]", name, j);
goto failed;
}
char* patch_name;
if (!fetch_string(this_patch, "name", &patch_name)) patch_name = NULL;
patchset->patches[j].name = patch_name;
patchset->patches[j].offset = at;
size_t size = strlen(from);
if (size != strlen(to)) {
snprintf(error, json_error_max, "'from' and 'to' lengths differ in patch %s[%d]", name, j);
goto failed;
}
if (size % 2 != 0) {
snprintf(error, json_error_max, "invalid hex string in patch %s[%d]", name, j);
goto failed;
}
char* bin_from = patchset->patches[j].from = (char*)malloc(size / 2);
char* bin_to = patchset->patches[j].to = (char*)malloc(size / 2);
if (!hex_to_bin(from, bin_from, size)) {
snprintf(error, json_error_max, "invalid hex string in patch %s[%d].from", name, j);
goto failed;
};
if (!hex_to_bin(to, bin_to, size)) {
snprintf(error, json_error_max, "invalid hex string in patch %s[%d].to", name, j);
goto failed;
};
patchset->patches[j].count = size / 2;
}
}
return true;
failed:
free_patches(patches);
return false;
}
bool load_patches(patches_t* patches, char* path, char* error) {
patches->nopatchsets = 0;
json_value* parsed = load_json_from_file(path, error);
if (parsed == NULL) return false;
int loaded_patches = 0;
json_value** patches_json;
if (parsed->type == json_array) {
loaded_patches = parsed->u.array.length;
patches_json = parsed->u.array.values;
} else if (parsed->type == json_object) {
loaded_patches = 1;
patches_json = &parsed;
} else {
sprintf(error, "Patch file format error");
json_value_free(parsed);
return false;
}
if (!parse_patches(patches, patches_json, loaded_patches, error)) return false;
return true;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <stdbool.h>
#include <stdio.h>
#include "../json/json.h"
#include "../util/hex.h"
typedef struct {
size_t offset;
size_t count;
char* name;
unsigned char* from;
unsigned char* to;
} patch_t;
typedef struct {
char* name;
char* description;
char* binary_name;
bool apply;
size_t nopatches;
patch_t patches[];
} patchset_t;
typedef struct {
size_t nopatchsets;
patchset_t** patchsets;
} patches_t;
// Internal JSON helpers
bool fetch(json_value* object, char* name, json_value** value);
bool fetch_string(json_value* object, char* name, char** value);
bool fetch_int(json_value* object, char* name, size_t* value);
bool fetch_bool(json_value* object, char* name, bool* value);
bool parse_patches(patches_t* patches, json_value** set_json, int set_count, char* error);
// Main two exports
void free_patches(patches_t* patches);
bool load_patches(patches_t* patches, char* path, char* error);

View File

@ -1,167 +1,18 @@
#include "../lib/json/json.h" #include "../lib/mice/mice.h"
#include "../lib/util/hex.h"
#include "stdbool.h"
#include "stdio.h"
#include "string.h" #include "string.h"
typedef struct { void print_patches(patches_t* patches, char* filename) {
size_t offset;
size_t count;
unsigned char* from;
unsigned char* to;
} patch_t;
typedef struct {
char* name;
char* description;
bool apply;
size_t nopatches;
patch_t patches[];
} patchset_t;
typedef struct {
size_t nopatchsets;
patchset_t** patchsets;
} patches_t;
bool fetch(json_value* object, char* name, json_value** value) {
if (object->type != json_object) return false;
for (size_t j = 0; j < object->u.object.length; j++) {
json_object_entry* entry = &(object->u.object.values[j]);
if (strncmp(name, entry->name, entry->name_length) == 0) {
*value = entry->value;
return true;
}
}
return false;
}
bool fetch_string(json_value* object, char* name, char** value) {
json_value* fetched;
if (!fetch(object, name, &fetched)) return false;
if (fetched->type != json_string) return false;
*value = fetched->u.string.ptr;
return true;
}
bool fetch_int(json_value* object, char* name, size_t* value) {
json_value* fetched;
if (!fetch(object, name, &fetched)) return false;
if (fetched->type != json_integer) return false;
*value = fetched->u.integer;
return true;
}
bool fetch_bool(json_value* object, char* name, bool* value) {
json_value* fetched;
if (!fetch(object, name, &fetched)) return false;
if (fetched->type != json_boolean) return false;
*value = fetched->u.boolean;
return true;
}
void free_patches(patches_t* patches) {
if (patches->nopatchsets == 0) return;
for (int i = patches->nopatchsets - 1; i >= 0; i--) {
if (patches->patchsets[i] != NULL) {
for (int j = 0; j < patches->patchsets[i]->nopatches; j++) {
if (patches->patchsets[i]->patches[j].from != NULL) free(patches->patchsets[i]->patches[j].from);
if (patches->patchsets[i]->patches[j].to != NULL) free(patches->patchsets[i]->patches[j].to);
}
free(patches->patchsets[i]);
}
}
patches->nopatchsets = 0;
}
bool parse_patches(patches_t* patches, json_value** set_json, int set_count, char* error) {
patches->nopatchsets = set_count;
patches->patchsets = (patchset_t**)malloc(set_count * sizeof(patchset_t*));
for (int i = 0; i < set_count; i++) patches->patchsets[i] = NULL;
error[0] = '\0';
for (int i = 0; i < set_count; i++) {
char *name, *description;
if (!fetch_string(set_json[i], "name", &name)) {
snprintf(error, json_error_max, "'name' missing for patch %d", i);
goto failed;
}
if (!fetch_string(set_json[i], "description", &description)) {
snprintf(error, json_error_max, "'description' missing for patch %d (%s)", i, name);
goto failed;
}
bool apply;
if (!fetch_bool(set_json[i], "apply", &apply)) {
snprintf(error, json_error_max, "'apply' missing for patch %d (%s)", i, name);
goto failed;
}
json_value* set_patches;
if (!fetch(set_json[i], "patches", &set_patches) || set_patches->type != json_array) {
snprintf(error, json_error_max, "'patches' missing for patch %d (%s)", i, name);
goto failed;
}
int count = set_patches->u.array.length;
patchset_t* patchset = (patchset_t*)malloc(sizeof(patchset_t) + (sizeof(patch_t) * count));
patches->patchsets[i] = patchset;
patchset->name = name;
patchset->apply = apply;
patchset->description = description;
patchset->nopatches = count;
for (int j = 0; j < count; j++) {
json_value* this_patch = set_patches->u.array.values[j];
size_t at;
char *from, *to;
if (!fetch_int(this_patch, "at", &at)) {
snprintf(error, json_error_max, "'at' missing for patch %s[%d]", name, j);
goto failed;
}
if (!fetch_string(this_patch, "from", &from)) {
snprintf(error, json_error_max, "'from' missing for patch %s[%d]", name, j);
goto failed;
}
if (!fetch_string(this_patch, "to", &to)) {
snprintf(error, json_error_max, "'to' missing for patch %s[%d]", name, j);
goto failed;
}
patchset->patches[j].offset = at;
size_t size = strlen(from);
if (size != strlen(to)) {
snprintf(error, json_error_max, "'from' and 'to' lengths differ in patch %s[%d]", name, j);
goto failed;
}
if (size % 2 != 0) {
snprintf(error, json_error_max, "invalid hex string in patch %s[%d]", name, j);
goto failed;
}
char* bin_from = patchset->patches[j].from = (char*)malloc(size / 2);
char* bin_to = patchset->patches[j].to = (char*)malloc(size / 2);
if (!hex_to_bin(from, bin_from, size)) {
snprintf(error, json_error_max, "invalid hex string in patch %s[%d].from", name, j);
goto failed;
};
if (!hex_to_bin(to, bin_to, size)) {
snprintf(error, json_error_max, "invalid hex string in patch %s[%d].to", name, j);
goto failed;
};
patchset->patches[j].count = size / 2;
}
}
return true;
failed:
free_patches(patches);
return false;
}
void print_patches(patches_t* patches) {
for (int i = 0; i < patches->nopatchsets; i++) { for (int i = 0; i < patches->nopatchsets; i++) {
patchset_t* patchset = patches->patchsets[i]; patchset_t* patchset = patches->patchsets[i];
printf("Patch: %s (%s)\n", patchset->name, patchset->apply ? "applied" : "unapplied"); bool skip = patchset->name != NULL && strcmp(patchset->name, filename) != 0;
printf("Patch: %s (%s)\n", patchset->name, skip ? "skipped" : patchset->apply ? "applied" : "unapplied");
printf("- %s\n", patchset->description); printf("- %s\n", patchset->description);
for (int i = 0; i < patchset->nopatches; i++) { if (!skip) {
printf(":: %d bytes at %08x\n", patchset->patches[i].count, patchset->patches[i].offset); for (int i = 0; i < patchset->nopatches; i++) {
printf(":: %d bytes at %08x\n", patchset->patches[i].count, patchset->patches[i].offset);
}
} }
puts("======================"); puts("======================");
} }
@ -179,6 +30,11 @@ void apply_patches(patches_t* patches, char* filename) {
for (int i = 0; i < patches->nopatchsets; i++) { for (int i = 0; i < patches->nopatchsets; i++) {
patchset_t* patchset = patches->patchsets[i]; patchset_t* patchset = patches->patchsets[i];
if (patchset->name != NULL && strcmp(patchset->name, filename) != 0) {
continue;
}
for (int j = 0; j < patchset->nopatches; j++) { for (int j = 0; j < patchset->nopatches; j++) {
patch_t patch = patchset->patches[j]; patch_t patch = patchset->patches[j];
if (patch.offset + patch.count > sz) { if (patch.offset + patch.count > sz) {
@ -239,65 +95,18 @@ int main(int argc, char** argv) {
return -1; return -1;
} }
FILE* fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Failed to open '%s' (%d)\n", argv[1], errno);
return -1;
}
fseek(fp, 0L, SEEK_END);
size_t sz = ftell(fp);
rewind(fp);
char* json_buf = (char*)malloc(sz);
if (json_buf == NULL) {
fprintf(stderr, "Failed to allocate %d bytes\n", sz, errno);
fclose(fp);
return -1;
}
if (!(sz = fread(json_buf, 1, sz, fp))) {
fprintf(stderr, "Failed to read from '%s' (%d)\n", argv[1], errno);
fclose(fp);
return -1;
};
fclose(fp);
json_settings settings = {0};
char error[json_error_max]; char error[json_error_max];
json_value* parsed = json_parse_ex(&settings, json_buf, sz, error);
if (parsed == NULL) {
fprintf(stderr, "Parse error: %s\n", error);
return -1;
}
int loaded_patches = 0;
json_value** patches_json;
if (parsed->type == json_array) {
loaded_patches = parsed->u.array.length;
patches_json = parsed->u.array.values;
} else if (parsed->type == json_object) {
loaded_patches = 1;
patches_json = &parsed;
} else {
fprintf(stderr, "Patch file format error\n");
json_value_free(parsed);
return;
}
patches_t all_patches; patches_t all_patches;
if (!parse_patches(&all_patches, patches_json, loaded_patches, error)) { if (!load_patches(&all_patches, argv[1], error)) {
fprintf(stderr, "Patch file format error: %s\n", error); fprintf(stderr, "%s", error);
return; return -1;
} }
puts(""); puts("");
puts("Loaded patches:"); puts("Loaded patches:");
puts("---------------"); puts("---------------");
print_patches(&all_patches); print_patches(&all_patches, argv[2]);
apply_patches(&all_patches, argv[2]); apply_patches(&all_patches, argv[2]);
free_patches(&all_patches); free_patches(&all_patches);
return 0; return 0;
} }

View File

@ -8,7 +8,6 @@ micepatch = executable(
], ],
link_with: [ link_with: [
mice_lib, mice_lib,
json_lib,
], ],
link_args: [ link_args: [
'/MANIFEST:EMBED', '/MANIFEST:EMBED',

View File

@ -0,0 +1,93 @@
[
{
"name": "LOG_EN_DONGLE",
"at": "004f48b0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_PCP",
"at": "004f989c",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_EEPROM",
"at": "004f7d04",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_BACKUP",
"at": "004f7d08",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_RTC",
"at": "004f8118",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_GFETCHER",
"at": "004f96dc",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_NETWORK",
"at": "004f8338",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_MASTER",
"at": "004f88c0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_HM",
"at": "004f88b0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_SRAM",
"at": "004f8330",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_PLATFORM",
"at": "004f9728",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_ASSERTS (We use ALPB_LOG_LEVEL instead)",
"at": "004f9e7c",
"from": "00000000",
"to": "01000000",
"count": 0
},
{
"name": "ALPB_LOG_LEVEL (0=None, 1=Error, 2=Warning, 3=Most, 4=Debug)",
"at": "004e884c",
"from": "01000000",
"to": "0a000000",
"count": 4
}
]

View File

@ -0,0 +1,98 @@
[
{
"at": "008717a0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00871728",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00871cd8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00872660",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "0087297c",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00872980",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00872988",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00873540",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00873538",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "0087f890",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00882ce4",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00882cec",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00883018",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "00886ff8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "0088b1b8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"at": "0088b1c0",
"from": "00000000",
"to": "01000000",
"count": 4
}
]

View File

@ -0,0 +1,149 @@
[
{
"name": "LOG_EN_SRAM",
"at": "00c8ab7c",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_CABINET_DL",
"at": "00f406f0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_INSTALL",
"at": "00c88680",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_EEPROM",
"at": "00c8ab78",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_PLATFORM",
"at": "00c88608",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_STORAGE",
"at": "00c88dd0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_PCP",
"at": "00ca25ec",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_HM",
"at": "00c96ed0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_HTTP",
"at": "00f406ec",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_DIPSW",
"at": "00c89ca8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_DONGLE",
"at": "00c96ed8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_JVSP",
"at": "00c9a83c",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_NETWORK",
"at": "00c89730",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_JVST_THREAD",
"at": "00c9a440",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_JVS",
"at": "00c8ab80",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_MASTER",
"at": "00c89fc8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_RTC",
"at": "00c88bb8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_JVST_DRIVER",
"at": "00c9e420",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_BACKUP",
"at": "00c89320",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_HWRESET",
"at": "00c88bb4",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN",
"at": "00c830e8",
"from": "00000000",
"to": "01000000",
"count": 4
}
]

View File

@ -0,0 +1,16 @@
[
{
"name": "LOG_EN_PCP",
"at": "004438e0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_UTL",
"at": "004433f8",
"from": "00000000",
"to": "01000000",
"count": 4
}
]

View File

@ -0,0 +1,163 @@
[
{
"name": "LOG_EN_JVS_DRIVER",
"at": "0054fcd8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_DIPSW",
"at": "00538810",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_ATA",
"at": "0054afc8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_DONGLE",
"at": "00544b78",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_EEPROM",
"at": "00536c98",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_GCATCHER",
"at": "00537834",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_GDELIVER",
"at": "00537d4c",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_GFETCHER",
"at": "0053828c",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_HWRESET",
"at": "00547fd0",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_PLATFORM",
"at": "00547fd8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_HM",
"at": "00548268",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_BACKUP",
"at": "00536f30",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_CMOS",
"at": "00547fcc",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_INSTALL",
"at": "005382d8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_JVS",
"at": "00538818",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_JVSP",
"at": "00544b68",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_MASTER",
"at": "0054b3d8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_NETWORK",
"at": "00548280",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_RTC",
"at": "00548050",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_SRAM",
"at": "00536c94",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_STORAGE",
"at": "005487f8",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_SYS",
"at": "00536c90",
"from": "00000000",
"to": "01000000",
"count": 4
},
{
"name": "LOG_EN_PCPT",
"at": "00536c70",
"from": "00000000",
"to": "01000000",
"count": 4
}
]

51
src/patches/patches.json Normal file
View File

@ -0,0 +1,51 @@
[
{
"name": "maimai Finale logs",
"description": "Enable logging facilities",
"binary_name": "maimai_dump_.exe",
"apply": true,
"patches_file": "maimai_dump_.patch.json"
},
{
"name": "mxnetwork logs",
"description": "Enable logging facilities",
"binary_name": "mxnetwork.exe",
"apply": true,
"patches_file": "mxnetwork.patch.json"
},
{
"name": "maimai 1.00 logs",
"description": "Enable logging facilities",
"binary_name": "Game.exe",
"apply": true,
"patches_file": "Game.patch.json"
},
{
"name": "ALLNetProc logs",
"description": "Enable logging facilities",
"binary_name": "ALLNetProc.exe",
"apply": true,
"patches_file": "ALLNetProc.patch.json"
},
{
"name": "ALLNetProc logs",
"description": "Enable logging facilities (alt filename)",
"binary_name": "ALLNetProc_win.exe",
"apply": true,
"patches_file": "ALLNetProc.patch.json"
},
{
"name": "mxsegaboot logs",
"description": "Enable logging facilities",
"binary_name": "mxsegaboot.exe",
"apply": true,
"patches_file": "mxsegaboot.patch.json"
},
{
"name": "mxsegaboot logs",
"description": "Enable logging facilities (alt filename)",
"binary_name": "ORIG_mxsegaboot.exe",
"apply": true,
"patches_file": "mxsegaboot.patch.json"
}
]