diff --git a/Makefile b/Makefile index 013728d..f63df3d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,7 @@ BUILD_DIR := build +BUILD_DIR_32 := $(BUILD_DIR)\build32 +BUILD_DIR_64 := $(BUILD_DIR)\build64 +COMMIT := $(shell git rev-parse --short HEAD) DIST_DIR := dist BUILD_DRIVE := M: @@ -8,19 +11,33 @@ MICE := "$(BUILD_DIR)/src\mice.exe" # VCVARS := "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars32.bat" # For windows XP: -VCVARS := "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars32.bat" -vcvars_ver=14.16 +VCVARS_32 := "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars32.bat" -vcvars_ver=14.16 +VCVARS_64 := "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" -vcvars_ver=14.16 .ONESHELL: .PHONY: all all: mice dist +# all: mice mice64 dist +.PHONY: mice mice: -@subst $(BUILD_DRIVE) . @cd /D $(BUILD_DRIVE) \ - & $(VCVARS) \ - & meson.py setup --cross cross-32.ini $(BUILD_DRIVE)\$(BUILD_DIR) \ - & meson.py compile -C $(BUILD_DRIVE)\$(BUILD_DIR) + & $(VCVARS_32) \ + & meson.py setup --cross cross-32.ini $(BUILD_DRIVE)\$(BUILD_DIR_32) \ + & meson.py configure -Dmice_version=$(COMMIT) $(BUILD_DRIVE)\$(BUILD_DIR_32) \ + & meson.py compile -C $(BUILD_DRIVE)\$(BUILD_DIR_32) + @subst $(BUILD_DRIVE) /D + +.PHONY: mice64 +mice64: + -@subst $(BUILD_DRIVE) . + @cd /D $(BUILD_DRIVE) \ + & $(VCVARS_64) \ + & meson.py setup --cross cross-64.ini $(BUILD_DRIVE)\$(BUILD_DIR_64) \ + & meson.py configure -Dmice_version=$(COMMIT) $(BUILD_DRIVE)\$(BUILD_DIR_64) -Dwin64=true \ + & meson.py compile -C $(BUILD_DRIVE)\$(BUILD_DIR_64) @subst $(BUILD_DRIVE) /D .PHONY: clean @@ -39,21 +56,26 @@ dist: @-mkdir $(DIST_DIR)\Execute\Z > NUL 2>&1 @-mkdir $(DIST_DIR)\Execute\S > NUL 2>&1 - @copy /Y "$(BUILD_DIR)/src/micetools/micekeychip\micekeychip.exe" "$(DIST_DIR)/micekeychip.exe" - @copy /Y "$(BUILD_DIR)/src/micetools/micemaster\micemaster.exe" "$(DIST_DIR)/micemaster.exe" - @copy /Y "$(BUILD_DIR)/src/micetools/lib/libpcp\libpcp.lib" "$(DIST_DIR)/libpcp.lib" + @copy /Y "$(BUILD_DIR_32)/src/micetools/micekeychip\micekeychip.exe" "$(DIST_DIR)/micekeychip.exe" + @copy /Y "$(BUILD_DIR_32)/src/micetools/micemaster\micemaster.exe" "$(DIST_DIR)/micemaster.exe" + @copy /Y "$(BUILD_DIR_32)/src/micetools/lib/libpcp\libpcp.lib" "$(DIST_DIR)/libpcp.lib" - @copy /Y "$(BUILD_DIR)/src/micetools/launcher\mice.exe" "$(DIST_DIR)/mice.exe" -# @copy /Y "$(BUILD_DIR)/src/micetools/launcher\mice.pdb" "$(DIST_DIR)/mice.pdb" - @copy /Y "$(BUILD_DIR)/src/micetools/dll\mice.pdb" "$(DIST_DIR)/mice.pdb" - @copy /Y "$(BUILD_DIR)/src/micetools/dll\mice.dll" "$(DIST_DIR)/mice.dll" + @copy /Y "$(BUILD_DIR_32)/src/micetools/launcher\mice.exe" "$(DIST_DIR)/mice.exe" +# @copy /Y "$(BUILD_DIR_32)/src/micetools/launcher\mice.pdb" "$(DIST_DIR)/mice.pdb" + @copy /Y "$(BUILD_DIR_32)/src/micetools/dll\mice.pdb" "$(DIST_DIR)/mice.pdb" + @copy /Y "$(BUILD_DIR_32)/src/micetools/dll\mice.dll" "$(DIST_DIR)/mice.dll" - @copy /Y "$(BUILD_DIR)/src/micetools/miceboot\miceprestartup.exe" "$(DIST_DIR)/Execute/miceprestartup.exe" - @copy /Y "$(BUILD_DIR)/src/micetools/miceboot\micestartup.exe" "$(DIST_DIR)/Execute/micestartup.exe" - @copy /Y "$(BUILD_DIR)/src/micetools/miceboot\mxmaster.exe" "$(DIST_DIR)/Execute/S/mxmaster.exe" +# @copy /Y "$(BUILD_DIR_64)/src/micetools/launcher\mice64.exe" "$(DIST_DIR)/mice64.exe" +# @copy /Y "$(BUILD_DIR_64)/src/micetools/launcher\mice64.pdb" "$(DIST_DIR)/mice64.pdb" +# @copy /Y "$(BUILD_DIR_64)/src/micetools/dll\mice64.pdb" "$(DIST_DIR)/mice64.pdb" +# @copy /Y "$(BUILD_DIR_64)/src/micetools/dll\mice64.dll" "$(DIST_DIR)/mice64.dll" - @copy /Y "$(BUILD_DIR)/src/micetools/util\*.exe" "$(DIST_DIR)/util/" - @copy /Y "$(BUILD_DIR)/src/micetools/util\*.pdb" "$(DIST_DIR)/util/" + @copy /Y "$(BUILD_DIR_32)/src/micetools/miceboot\miceprestartup.exe" "$(DIST_DIR)/Execute/miceprestartup.exe" + @copy /Y "$(BUILD_DIR_32)/src/micetools/miceboot\micestartup.exe" "$(DIST_DIR)/Execute/micestartup.exe" + @copy /Y "$(BUILD_DIR_32)/src/micetools/miceboot\mxmaster.exe" "$(DIST_DIR)/Execute/S/mxmaster.exe" + + @copy /Y "$(BUILD_DIR_32)/src/micetools/util\*.exe" "$(DIST_DIR)/util/" + @copy /Y "$(BUILD_DIR_32)/src/micetools/util\*.pdb" "$(DIST_DIR)/util/" @xcopy /E /H /C /R /Q /Y src\system "$(DIST_DIR)\system/*" @xcopy /E /H /C /R /Q /Y src\patches "$(DIST_DIR)\patches/*" diff --git a/cross-32.ini b/cross-32.ini index 4721fac..782bedf 100644 --- a/cross-32.ini +++ b/cross-32.ini @@ -9,4 +9,3 @@ system = 'windows' cpu_family = 'x86' cpu = 'i686' endian = 'little' - diff --git a/cross-64.ini b/cross-64.ini new file mode 100644 index 0000000..26aacdf --- /dev/null +++ b/cross-64.ini @@ -0,0 +1,11 @@ +[binaries] +c = 'cl' +cpp = 'cl' +strip = 'cl' +ar = ['lib', '/LTCG'] + +[host_machine] +system = 'windows' +cpu_family = 'x86_64' +cpu = 'x86_64' +endian = 'little' diff --git a/meson.build b/meson.build index 06c7531..db2a844 100644 --- a/meson.build +++ b/meson.build @@ -5,17 +5,38 @@ project('micetools', 'c', default_options: [ 'warning_level=3', ]) -winxp = true -subsystem = 'console,5.01' +add_global_arguments('-DMICE_VERSION=' + '"' + get_option('mice_version') + '"', language : 'c') + +if get_option('win64') + add_project_arguments( + '/D_AMD64_', + language: 'c', + ) + winxp = false + subsystem = 'console' +else + add_project_arguments( + '/D_X86_', + language: 'c', + ) + winxp = true + subsystem = 'console,5.01' +endif if (host_machine.cpu_family() == 'x86') add_project_arguments('-DMICE_WIN32', language: 'c') endif openssl_inc = include_directories('openssl-1.0.1/include') -openssl_lib = meson.get_compiler('c').find_library('libeay32', dirs: [ - join_paths(meson.source_root(), 'openssl-1.0.1/lib') -], required: true) +if get_option('win64') + openssl_lib = meson.get_compiler('c').find_library('libeay64', dirs: [ + join_paths(meson.source_root(), 'openssl-1.0.1/lib') + ], required: true) +else + openssl_lib = meson.get_compiler('c').find_library('libeay32', dirs: [ + join_paths(meson.source_root(), 'openssl-1.0.1/lib') + ], required: true) +endif assert(openssl_lib.found(), 'Please download openssl!') freeglut_lib = meson.get_compiler('c').find_library('freeglut', dirs: [ @@ -27,9 +48,13 @@ add_project_link_arguments( '/DYNAMICBASE', '/OPT:REF', '/LTCG', - + '/IGNORE:4099', # no PDBs language: 'c' ) +add_project_link_arguments( + '/IGNORE:4099', # no PDBs + language: 'cpp' +) add_project_arguments( '/DWIN32_LEAN_AND_MEAN', # Strip out headers we don't really need '/D_WIN32_WINNT=_WIN32_WINNT_WINXP', # hahahahaha I hate it @@ -54,6 +79,9 @@ add_project_arguments( '/we4057', # ... differs in levels of indirection (slightly) from ... '/we4024', # ... different types for formal and actual paramter ... '/we4013', # ... undefined; assuming extern returning int + '/we4431', # missing type specifier - int assumed. + + '/W4', language: 'c', ) diff --git a/meson_options.txt b/meson_options.txt index ce9a913..5af572a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,10 @@ -option('vsenv', type: 'boolean', value: false) +option( + 'win64', + type: 'boolean', + value: false, + description: 'Set this flag when targeting Win64 rather than Win32 (default)' +) +option( + 'mice_version', + type: 'string', +) diff --git a/openssl-1.0.1/lib/libeay32.lib b/openssl-1.0.1/lib/libeay32.lib index ca90f87..0c6eebe 100644 Binary files a/openssl-1.0.1/lib/libeay32.lib and b/openssl-1.0.1/lib/libeay32.lib differ diff --git a/openssl-1.0.1/lib/libeay64.lib b/openssl-1.0.1/lib/libeay64.lib new file mode 100644 index 0000000..c8964d1 Binary files /dev/null and b/openssl-1.0.1/lib/libeay64.lib differ diff --git a/openssl-1.0.1/lib/ssleay32.lib b/openssl-1.0.1/lib/ssleay32.lib index 4f8ceac..84d5bbb 100644 Binary files a/openssl-1.0.1/lib/ssleay32.lib and b/openssl-1.0.1/lib/ssleay32.lib differ diff --git a/openssl-1.0.1/lib/ssleay64.lib b/openssl-1.0.1/lib/ssleay64.lib new file mode 100644 index 0000000..84aebd6 Binary files /dev/null and b/openssl-1.0.1/lib/ssleay64.lib differ diff --git a/src/meson.build b/src/meson.build index 7b62cea..cc2ef70 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,4 +1,5 @@ inih = subproject('inih_dep') +detours = subproject('detours') cimgui = subproject('cimgui_dep', default_options: [ 'win32=enabled', 'dx9=enabled', diff --git a/src/micetools/amBackupStructs.h b/src/micetools/amBackupStructs.h index 22d9a93..f6748ad 100644 --- a/src/micetools/amBackupStructs.h +++ b/src/micetools/amBackupStructs.h @@ -20,8 +20,11 @@ typedef struct { } ERROR_LOG_BODY; typedef struct { + // 0 = Common, 1 = Individual uint8_t ChuteType; + // 0 = Common, 1 = Individual uint8_t ServiceType; + // 0 = Normal, 1 = Freeplay uint8_t Operation; uint8_t CoinRate[2]; uint8_t BonusAdder; diff --git a/src/micetools/dll/comdevice.c b/src/micetools/dll/comdevice.c index d2e7831..7a0df24 100644 --- a/src/micetools/dll/comdevice.c +++ b/src/micetools/dll/comdevice.c @@ -4,6 +4,7 @@ com_device_t* GetComDevice(HANDLE hFile) { open_hook_t* pHData = GetDataForHandle(hFile, HDATA_FILE); + if (pHData == NULL || pHData->hook == NULL || pHData->hook->com_hook == NULL) return NULL; return pHData->hook->com_hook->com_device; } @@ -13,18 +14,23 @@ BOOL DevGetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) { return TR BOOL DevSetCommTimeouts(HANDLE hFile, LPCOMMTIMEOUTS lpCommTimeouts) { return TRUE; } BOOL DevSetupComm(HANDLE hFile, DWORD dwInQueue, DWORD dwOutQueue) { return TRUE; } BOOL DevPurgeComm(HANDLE hFile, DWORD dwFlags) { - if (dwFlags & PURGE_RXCLEAR) ringbuf_purge(&(GetComDevice(hFile)->out)); + com_device_t* dev = GetComDevice(hFile); + if (dev == NULL) return FALSE; + if (dwFlags & PURGE_RXCLEAR) ringbuf_purge(&(dev->out)); return TRUE; } BOOL DevGetCommModemStatus(HANDLE hFile, LPDWORD lpModemStatus) { if (!lpModemStatus) return FALSE; com_device_t* dev = GetComDevice(hFile); - if (!dev) return false; + if (dev == NULL) return FALSE; *lpModemStatus = dev->modemStatus; return TRUE; } BOOL DevWaitCommEvent(HANDLE hFile, LPDWORD lpEvtMask, LPOVERLAPPED lpOverlapped) { - WaitForSingleObject(GetComDevice(hFile)->dataOutReady, INFINITE); + com_device_t* dev = GetComDevice(hFile); + if (dev == NULL) return FALSE; + + WaitForSingleObject(dev->dataOutReady, INFINITE); if (lpOverlapped != NULL) SetEvent(lpOverlapped->hEvent); return TRUE; } @@ -39,8 +45,12 @@ BOOL DevClearCommError(HANDLE hFile, LPDWORD lpErrors, LPCOMSTAT lpStat) { lpStat->fEof = FALSE; lpStat->fTxim = FALSE; lpStat->fReserved = 0; - lpStat->cbInQue = ringbuf_available(&(GetComDevice(hFile)->out)); - lpStat->cbOutQue = ringbuf_available(&(GetComDevice(hFile)->in)); + + com_device_t* dev = GetComDevice(hFile); + if (dev == NULL) return FALSE; + + lpStat->cbInQue = ringbuf_available(&dev->out); + lpStat->cbOutQue = ringbuf_available(&dev->in); } return TRUE; } @@ -49,22 +59,28 @@ BOOL DevWriteFile(file_context_t* ctx, LPCVOID lpBuffer, DWORD nNumberOfBytesToW LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) { if (nNumberOfBytesToWrite > 0xffff) return FALSE; com_device_t* dev = GetComDevice(ctx->m_Handle); + if (dev == NULL) return FALSE; + // Ignore overflow - ringbuf_write(&(dev->in), lpBuffer, nNumberOfBytesToWrite & 0xffff); - SetEvent(dev->dataInReady); + ringbuf_write(&dev->in, lpBuffer, nNumberOfBytesToWrite & 0xffff); + if (!SetEvent(dev->dataInReady)) + log_error(plfComm, "Failed to signal data in: %d", GetLastError()); + if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nNumberOfBytesToWrite; return TRUE; } BOOL DevReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) { if (nNumberOfBytesToRead > 0xffff) return FALSE; - com_device_t* comdev = GetComDevice(ctx->m_Handle); + com_device_t* dev = GetComDevice(ctx->m_Handle); + if (dev == NULL) return FALSE; + // Make sure we have at least one byte to return - // while (!ringbuf_available(&(comdev->out))) { - // WaitForSingleObject(comdev->event, INFINITE); + // while (!ringbuf_available(&(dev->out))) { + // WaitForSingleObject(dev->event, INFINITE); // } - short read = ringbuf_read(&(comdev->out), lpBuffer, nNumberOfBytesToRead & 0xffff); + short read = ringbuf_read(&dev->out, lpBuffer, nNumberOfBytesToRead & 0xffff); if (lpNumberOfBytesRead) *lpNumberOfBytesRead = read; if (lpOverlapped) { @@ -75,10 +91,7 @@ BOOL DevReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRea } short comdev_read_blocking(com_device_t* com, unsigned char* buffer, short bytes) { - while (comdev_available(com) < bytes) { - WaitForSingleObject(com->dataInReady, INFINITE); - SwitchToThread(); - } + while (comdev_available(com) < bytes) WaitForSingleObject(com->dataInReady, INFINITE); return ringbuf_read(&com->in, buffer, bytes); } short comdev_read(com_device_t* com, LPVOID buffer, short bytes) { @@ -94,6 +107,7 @@ BYTE comdev_peek(com_device_t* com) { return com->in.buffer[com->in.read]; } // Read data from a com device, unescaping as we go void comio_read(com_device_t* com, LPVOID data, BYTE len) { + LPBYTE lpbData = (LPBYTE)data; BYTE one_byte; for (; len; len--) { comdev_read_blocking(com, &one_byte, 1); @@ -101,14 +115,15 @@ void comio_read(com_device_t* com, LPVOID data, BYTE len) { comdev_read_blocking(com, &one_byte, 1); one_byte++; } - *(((LPBYTE)data)++) = one_byte; + *(lpbData++) = one_byte; } } // Write data to a com device, escaping as we go void comio_write(com_device_t* com, LPCVOID data, BYTE len) { + LPBYTE lpbData = (LPBYTE)data; BYTE one_byte; for (; len; len--) { - one_byte = *(((LPBYTE)data)++); + one_byte = *(lpbData++); if (one_byte == COMIO_MARK || one_byte == COMIO_SYNC) { BYTE mark = COMIO_MARK; comdev_write(com, &mark, 1); @@ -120,11 +135,7 @@ void comio_write(com_device_t* com, LPCVOID data, BYTE len) { unsigned char comio_next_req(com_device_t* com, comio_recv_head_t* head, LPVOID data) { BYTE one_byte; do { - if (comdev_available(com) < (sizeof *head + 1)) { - SwitchToThread(); - continue; - } - comdev_read(com, &one_byte, 1); + comdev_read_blocking(com, &one_byte, 1); if (one_byte != COMIO_SYNC) { log_error(plfComm, "Garbage on JVS: %02x", one_byte); continue; @@ -165,25 +176,31 @@ void comio_reply(com_device_t* com, comio_recv_head_t* req, BYTE status, BYTE le comio_write(com, &one_byte, 1); } -BOOL attach_com_device(BYTE port, FnComDeviceThread* thread) { +BOOL attach_com_device(BYTE port, PVIRTUAL_SERIAL_DEVICE lpDevice) { if (port < 1 || port > NUM_COM_PORTS) { log_error(plfComm, "Requested COM%hhu but that is out of range!", port); return FALSE; } com_device_t* com = com_devices[port - 1]; - if (com->thread != INVALID_HANDLE_VALUE) { - if (port == RESERVED_JVS_COM_PORT) return FALSE; + if (com->thread != NULL) { + if (port == RESERVED_JVS_COM_PORT) { + log_warning(plfComm, "Refusing to attach to reserved port COM%hhu", port); + return FALSE; + } // No need to change what's assigned! - if (com->thread_worker == thread) return TRUE; - + if (com->lpDevice == lpDevice) return TRUE; log_warning(plfComm, "COM%hhu is already attached!", port); TerminateThread(com->thread, (DWORD)-1); } - com->thread = CreateThread(NULL, 0, thread, com, 0, NULL); - com->thread_worker = thread; + com->thread = CreateThread(NULL, 0, lpDevice->m_Entrypoint, com, 0, NULL); + if (com->thread == NULL) { + log_error(plfComm, "Attach to COM%hhu failed: %d", port, GetLastError()); + return FALSE; + } + com->lpDevice = lpDevice; return TRUE; } @@ -196,15 +213,15 @@ void detach_com_device(BYTE port) { if (!com->thread) return; TerminateThread(com->thread, (DWORD)-1); - com->thread = INVALID_HANDLE_VALUE; + com->thread = NULL; } void detach_all_com_devices(void) { for (int i = 0; i < NUM_COM_PORTS; i++) { if (i == RESERVED_JVS_COM_PORT - 1) continue; - if (com_devices[i]->thread != INVALID_HANDLE_VALUE) { + if (com_devices[i]->thread != NULL) { TerminateThread(com_devices[i]->thread, (DWORD)-1); - com_devices[i]->thread = INVALID_HANDLE_VALUE; + com_devices[i]->thread = NULL; } } } @@ -219,6 +236,8 @@ com_device_t* new_com_device(BYTE port) { file->altFilename = com->wDosName; com_device->com = com; com_device->file = file; + com_device->dataInReady = CreateEventA(NULL, FALSE, FALSE, NULL); + com_device->dataOutReady = CreateEventW(NULL, TRUE, FALSE, NULL); com->GetCommState = DevGetCommState; com->SetCommState = DevSetCommState; @@ -235,8 +254,7 @@ com_device_t* new_com_device(BYTE port) { ringbuf_purge(&com_device->in); ringbuf_purge(&com_device->out); - com_device->dataOutReady = CreateEventW(NULL, TRUE, FALSE, com_device->com->wName); - com_device->thread = INVALID_HANDLE_VALUE; + com_device->thread = NULL; hook_file(file); @@ -244,7 +262,7 @@ com_device_t* new_com_device(BYTE port) { } void init_com_devices(void) { - for (BYTE i = 0; i < NUM_COM_PORTS; i++) { + for (BYTE i = 1; i < NUM_COM_PORTS; i++) { com_devices[i] = new_com_device(i + 1); } } diff --git a/src/micetools/dll/comdevice.h b/src/micetools/dll/comdevice.h index 9c749ca..3211c58 100644 --- a/src/micetools/dll/comdevice.h +++ b/src/micetools/dll/comdevice.h @@ -8,6 +8,11 @@ typedef struct com_device com_device_t; typedef DWORD(WINAPI FnComDeviceThread)(com_device_t* com); +typedef struct VIRTUAL_SERIAL_DEVICE { + LPCSTR m_Name; + FnComDeviceThread* m_Entrypoint; +} VIRTUAL_SERIAL_DEVICE, *PVIRTUAL_SERIAL_DEVICE; + struct com_device { com_hook_t* com; file_hook_t* file; @@ -18,7 +23,7 @@ struct com_device { HANDLE dataInReady; HANDLE dataOutReady; HANDLE thread; - FnComDeviceThread* thread_worker; + PVIRTUAL_SERIAL_DEVICE lpDevice; }; com_device_t* com_devices[NUM_COM_PORTS]; @@ -60,7 +65,7 @@ unsigned char comio_next_req(com_device_t* com, comio_recv_head_t* head, LPVOID void comio_reply(com_device_t* com, comio_recv_head_t* req, BYTE status, BYTE len, LPCVOID data); com_device_t* new_com_device(BYTE port); -BOOL attach_com_device(BYTE port, FnComDeviceThread* thread); +BOOL attach_com_device(BYTE port, PVIRTUAL_SERIAL_DEVICE thread); void detach_com_device(BYTE port); void detach_all_com_devices(void); void init_com_devices(void); diff --git a/src/micetools/dll/common.h b/src/micetools/dll/common.h index a75d3e8..743b55e 100644 --- a/src/micetools/dll/common.h +++ b/src/micetools/dll/common.h @@ -23,8 +23,15 @@ #include "../lib/mice/mice.h" #include "./util/_util.h" +#include "micefs.h" -void mice_got_game_id(char game_id[4]); +#define size2int(x) ((int)((x) & 0x7fffffff)) +#define size2uint(x) ((unsigned int)((x) & 0xffffffff)) + +void _MiceGotGameId(char game_id[4]); +BOOL MiceGuessGameId(char* lpGameId); extern WCHAR exeName[MAX_PATH + 1]; -extern DWORD imageOffset; +extern size_t imageOffset; + +extern BOOL g_bIsInDllMain; diff --git a/src/micetools/dll/devices/_devices.c b/src/micetools/dll/devices/_devices.c index 0f18263..520c6b0 100644 --- a/src/micetools/dll/devices/_devices.c +++ b/src/micetools/dll/devices/_devices.c @@ -6,46 +6,52 @@ #include "smb_pca9535.h" typedef struct _device_list { - const char* m_Name; - FnComDeviceThread* m_Thread; + VIRTUAL_SERIAL_DEVICE m_Device; struct _device_list* m_Next; } device_list_t; device_list_t device_list = { .m_Next = NULL }; -#define _start_device_n(n) \ - if (strcmp(MiceConfig.devices.com##n, name) == 0) attach_com_device(n, thread) - -inline void start_device(const char* name, FnComDeviceThread* thread) { - _start_device_n(1); - _start_device_n(2); - _start_device_n(3); - _start_device_n(5); - _start_device_n(6); - _start_device_n(7); - _start_device_n(8); +static DWORD WINAPI dummy_comdev_thread(com_device_t* dev) { + BYTE buffer[1]; + while (1) { + comdev_read_blocking(dev, buffer, 1); + log_warning(plfComm, "Idle data on port %ls: %02x", dev->com->wName, buffer[0]); + } } -#define _stop_if_unregistered(n) \ - if (MiceConfig.devices.com##n[0] == '\0') detach_com_device(n) +static VIRTUAL_SERIAL_DEVICE dummySerialDevice = { + .m_Name = "Port idler", + .m_Entrypoint = dummy_comdev_thread, +}; -inline void stop_old_devices() { - _stop_if_unregistered(1); - _stop_if_unregistered(2); - _stop_if_unregistered(3); - _stop_if_unregistered(5); - _stop_if_unregistered(6); - _stop_if_unregistered(7); - _stop_if_unregistered(8); +inline void start_virtual_device(BYTE bPort, LPCSTR szName) { + if (szName[0] != '\0') { + device_list_t* device = &device_list; + while (device->m_Next) { + device = device->m_Next; + if (strcmp(szName, device->m_Device.m_Name) == 0) { + attach_com_device(bPort, &device->m_Device); + return; + } + } + + log_error(plfComm, "Device %s requested by unknown! (COM%hhu)", szName, bPort); + } + + if (MiceConfig.devices.add_idlers) + attach_com_device(bPort, &dummySerialDevice); + else + detach_com_device(bPort); } void start_devices() { - stop_old_devices(); - - device_list_t* device = &device_list; - while (device->m_Next) { - device = device->m_Next; - start_device(device->m_Name, device->m_Thread); - } + // start_virtual_device(1, MiceConfig.devices.com1); + start_virtual_device(2, MiceConfig.devices.com2); + start_virtual_device(3, MiceConfig.devices.com3); + start_virtual_device(5, MiceConfig.devices.com5); + start_virtual_device(6, MiceConfig.devices.com6); + start_virtual_device(7, MiceConfig.devices.com7); + start_virtual_device(8, MiceConfig.devices.com8); } void register_device(const char* name, FnComDeviceThread* thread) { @@ -54,8 +60,8 @@ void register_device(const char* name, FnComDeviceThread* thread) { device_list_t* us = tail->m_Next = malloc(sizeof(device_list_t)); - us->m_Name = name; - us->m_Thread = thread; + us->m_Device.m_Name = name; + us->m_Device.m_Entrypoint = thread; us->m_Next = NULL; } @@ -64,8 +70,8 @@ void install_devices() { install_touch_bd(); install_aime_bd(); install_servo_15069(); - - start_devices(); + install_gacchu_guts_card(); + install_gacchu_guts_screen(); smbus_install(/* 0x20 */ PCA9535_ADDRESS, &smbus_PCA9535_write, &smbus_PCA9535_read); diff --git a/src/micetools/dll/devices/_devices.h b/src/micetools/dll/devices/_devices.h index 23c803f..80c65dc 100644 --- a/src/micetools/dll/devices/_devices.h +++ b/src/micetools/dll/devices/_devices.h @@ -7,6 +7,8 @@ void install_led_bd(); void install_touch_bd(); void install_aime_bd(); void install_servo_15069(); +void install_gacchu_guts_card(); +void install_gacchu_guts_screen(); smbus_callback_t smbus_N2_write; smbus_callback_t smbus_N2_read; diff --git a/src/micetools/dll/devices/meson.build b/src/micetools/dll/devices/meson.build index 18e0a96..ab4197b 100644 --- a/src/micetools/dll/devices/meson.build +++ b/src/micetools/dll/devices/meson.build @@ -8,6 +8,8 @@ devices_files = files( 'ser_maitouch.c', 'ser_tn32msec.c', 'ser_servo_838-15069.c', + 'ser_gacchu_guts_card.c', + 'ser_gacchu_guts_screen.c', # SMBus devices 'smb_pca9535.c', 'smb_ds2460.c', diff --git a/src/micetools/dll/devices/ser_gacchu_guts_card.c b/src/micetools/dll/devices/ser_gacchu_guts_card.c new file mode 100644 index 0000000..f35afe2 --- /dev/null +++ b/src/micetools/dll/devices/ser_gacchu_guts_card.c @@ -0,0 +1,31 @@ +/** + * Commands in: + * + * - Length prefix (ie 2) + * - Command (ie "C2" or "Z8") + * - 0x03 + * + */ + +#include + +#include "_devices.h" + +static DWORD WINAPI ser_gacchu_guts_card(com_device_t* com) { + puts("GG CARD"); + BYTE nBytes; + BYTE buffer[256]; + BYTE tail; + while (1) { + comdev_read_blocking(com, &nBytes, 1); + comdev_read_blocking(com, buffer, nBytes); + comdev_read_blocking(com, &tail, 1); + + printf("Got Gacchu command: %.*s\n", nBytes, buffer); + BYTE response[1]; + response[0] = '\0'; + // comdev_write(com, response, 1); + } +} + +void install_gacchu_guts_card() { register_device("gacchu_guts_card", ser_gacchu_guts_card); } diff --git a/src/micetools/dll/devices/ser_gacchu_guts_screen.c b/src/micetools/dll/devices/ser_gacchu_guts_screen.c new file mode 100644 index 0000000..acc7b30 --- /dev/null +++ b/src/micetools/dll/devices/ser_gacchu_guts_screen.c @@ -0,0 +1,187 @@ +#include + +#include "_devices.h" + +/* +Packet format: + String packets: + [0]: commandNum > 16 ? 0x81 : 0x01 + [...]: string. does not contain null. + [n]: commandNum > 16 ? 0x8d : 0x0d + Binary packets: + [0]: 0x55 + [1]: + [2]: + [3]: + [4]: + [5]: + [6]: + [7]: Sum of ([1] through [6]) + 0xAA + +Responses: + Type(0) packets: + [0]: 0x80 | (if 0x40 set, arg2 = 0x80, else arg2 = 0) + [1]: arg0 & 0x7f + [2]: (arg0 >> 7) & 0x7f + [3]: arg1 & 0x7f + [4]: (arg1 >> 7) & 0x7f + + Type(2) packets: + [0]: 0x40 | (if 0x02 set, arg2 = 0, else arg2 = 0x80) + [1]: arg0 & 0x3f + [2]: (arg0 >> 6) & 0x3f + [3]: arg1 & 0x3f + [4]: (arg1 >> 6) & 0x3f + [5]: ??? + + Binary packets: + // String packets (this seems.. like an impossible contradiction!?): + // [0]: 0x01 + // [1]: 0x0d + + // [0]: 0x55 + // [1]: 0x54 + // [...?] -> Buffer length reset to 2. Do these matter? + // [9] 0x55 + // [10] 0x54 + + // --- OR --- + + [0]: 0x55 + [1]: 0x54 + [2]: if low two bits set (& 3), [7] contains arg2, else arg2=0 + [3]: arg0 & 0xff + [4]: arg0 >> 8 + [5]: arg1 & 0xff + [6]: arg1 >> 8 + [7]: arg2 + [8]: + [9]: Sum of ([1] through [8]) + 0xAA + + --- OR --- + Whatever (005c1edb) is doing + From the looks of it, 0x41 is some sort of continuation byte, + every 10 bytes. +*/ + +/* + +FQF[02x: arg0] = 11 (set frequency?) +FQF00 = 12 (get frequency?) +.5A[02x: touch panel effect][02x: ??] = 16 (set config?) + +*/ + +/* + +First command received: + = Screen command 8 + []Z[] + 01 + 5A + 0D + +Second command sent: + = Screen command 16 + = ".5A0001" + But the last four digits are replaced with "%02X%02X" + + [].5AFF00[] + 01 + 2E 35 41 46 46 30 30 + 0D + +*/ + +static inline void response_ok(com_device_t* lpDev, char* szResponse) { + int nBytes = szResponse ? strlen(szResponse) : 0; + if (nBytes > 8) nBytes = 8; + + BYTE buffer[10]; + buffer[0] = 0x01; + if (nBytes) memcpy(&buffer[1], szResponse, nBytes); + buffer[nBytes + 1] = 0x0d; + comdev_write(lpDev, buffer, (nBytes + 2) & 0xffff); +} + +static inline void response_two_short(com_device_t* lpDev, SHORT arg0, SHORT arg1) { + BYTE buffer[5]; + buffer[0] = 0x80; + buffer[1] = arg0 & 0x7f; + buffer[2] = (arg0 >> 7) & 0x7f; + buffer[3] = arg1 & 0x7f; + buffer[4] = (arg1 >> 7) & 0x7f; + comdev_write(lpDev, buffer, 5); +} + +static BYTE command_do[4] = { + 0xd0, + 0xce, + 0xb8, + 0xb1, +}; + +static DWORD WINAPI ser_gacchu_guts_screen(com_device_t* dev) { + BYTE bHead; + BYTE buffer[256]; + puts("Gacchu screen start"); + while (1) { + comdev_read_blocking(dev, &bHead, 1); + if (bHead == 0x55) { + log_error(plfGacchuGuts, "Screen binary!"); + comdev_read_blocking(dev, buffer, 7); + continue; + } + if (bHead != 0x01 && bHead != 0x81) { + log_error(plfGacchuGuts, "Screen babble: %02x", bHead); + continue; + } + WORD index = 0; + BYTE tail = bHead == 0x01 ? 0x0D : 0x8D; + do { + comdev_read_blocking(dev, &buffer[index++], 1); + } while (index < _countof(buffer) && buffer[index - 1] != tail); + + // comdev_read_blocking(dev, buffer, nBytes); + // comdev_read_blocking(dev, &tail, 1); + + // Done and working (hopefully!) + if (index == 2 && memcmp(buffer, "Z", 1) == 0) { + response_ok(dev, "0"); + } + // Not working :( + else if (index == 6 && memcmp(buffer, "FQF", 3) == 0) { + BYTE value = 0; + _snscanf_s((const char*)&buffer[3], 2, "%02hhX", &value); + if (value == 0) { + log_game(plfGacchuGuts, "Got FQF00 command. What now...?"); + response_ok(dev, " "); + } else { + log_game(plfGacchuGuts, "Got FQF[%02x] command. What now...?", value); + response_ok(dev, "0"); // Meant to do " " vs "0" I think? + } + } + + else if (index == 8 && memcmp(buffer, ".5A", 3) == 0) { + log_game(plfGacchuGuts, "Got .5A command. What now...?"); + /* + uVar9 = (arg0 * 1024) / (0x4000 - 1) + */ + + // At a guess, this is getting the display resolution. + response_two_short(dev, 1024, 768); + } + + else if (index == 5 && memcmp(buffer, command_do, 4) == 0) { + log_game(plfGacchuGuts, "Got D0 command. What now...?"); + response_ok(dev, "0"); // Probably wrong + } else { + log_error(plfGacchuGuts, "%ls: Got Gacchu screen: %02x(%d):[%02x]%.*s\n", + dev->com->wName, bHead, index, buffer[0], index - 1, buffer); + log_error(plfGacchuGuts, "%ls: Got Gacchu bytes: %02x %02x %02x %02x %02x\n", + dev->com->wName, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); + } + } +} + +void install_gacchu_guts_screen() { register_device("gacchu_guts_screen", ser_gacchu_guts_screen); } diff --git a/src/micetools/dll/devices/ser_led_bd.c b/src/micetools/dll/devices/ser_led_bd.c index 6a0d21e..baa98bf 100644 --- a/src/micetools/dll/devices/ser_led_bd.c +++ b/src/micetools/dll/devices/ser_led_bd.c @@ -81,12 +81,8 @@ static DWORD WINAPI led_bd_thread(com_device_t* dev) { while (1) { rs232c_recv_head_t head; - if (comdev_available(dev) < sizeof head) { - // Sleep(100); - continue; - } - comdev_read(dev, (unsigned char*)&head, sizeof head); - comdev_read(dev, extra, head.length); + comdev_read_blocking(dev, (unsigned char*)&head, sizeof head); + comdev_read_blocking(dev, extra, head.length); // log_info(plfMaiLED, "Bound %02x->%02x", head.src, head.dst); diff --git a/src/micetools/dll/devices/ser_maitouch.c b/src/micetools/dll/devices/ser_maitouch.c index 3926840..dd79492 100644 --- a/src/micetools/dll/devices/ser_maitouch.c +++ b/src/micetools/dll/devices/ser_maitouch.c @@ -1,11 +1,7 @@ +#include "../hooks/gui.h" #include "_devices.h" -static BYTE read_one(com_device_t* dev) { - while (!comdev_available(dev)) Sleep(50); - BYTE data; - comdev_read(dev, &data, 1); - return data; -} +static BYTE read_one(com_device_t* dev) {} const BYTE TOUCH_ID_LUT[] = "ABCD\0EFGH\0IJKL\0MNOPQRSTU\0VWXY\0"; static BYTE get_touch_id(BYTE id) { @@ -15,26 +11,130 @@ static BYTE get_touch_id(BYTE id) { return 0xff; } -BOOL touch_is_enabled = false; +// High byte: index, Low byte: bitflag +#define BUTTON_A1 0x0101 +#define BUTTON_B1 0x0102 +#define BUTTON_A2 0x0104 +#define BUTTON_B2 0x0108 +#define BUTTON_A3 0x0201 +#define BUTTON_B3 0x0202 +#define BUTTON_A4 0x0204 +#define BUTTON_B4 0x0208 +#define BUTTON_A5 0x0301 +#define BUTTON_B5 0x0302 +#define BUTTON_A6 0x0304 +#define BUTTON_B6 0x0308 +#define BUTTON_A7 0x0401 +#define BUTTON_B7 0x0402 +#define BUTTON_A8 0x0404 +#define BUTTON_B8 0x0408 +#define BUTTON_C 0x0410 + +WORD getSensorInRegion(float mX, float mY, float rX, float rY, float rW, float rH) { + // Out of region + if (mX < rX || mY < rY || mX > rX + rW || mY > rY + rH) return 0; + + // i is now a position in a region from (-1,-1) to (1,1) + float iX = ((mX - rX) / (rW / 2)) - 1; + float iY = ((mY - rY) / (rH / 2)) - 1; + + float r2 = iX * iX + iY * iY; + + // Bound to a unit circle + if (r2 > 1) return 0; + + // Centre button + if (r2 < (121.0f / 540.0f) * (121.0f / 540.0f)) return BUTTON_C; + + WORD button = 0; + + // Four quadrants + if (iX > 0 && iY < 0) button = -iY > iX ? BUTTON_A1 : BUTTON_A2; + if (iX > 0 && iY > 0) button = iX > iY ? BUTTON_A3 : BUTTON_A4; + if (iX < 0 && iY > 0) button = iY > -iX ? BUTTON_A5 : BUTTON_A6; + if (iX < 0 && iY < 0) button = -iX > -iY ? BUTTON_A7 : BUTTON_A8; + + // Inner ring of buttons + if (r2 < (298.0f / 540.0f) * (298.0f / 540.0f)) + button = (button & 0xff00) | ((button & 0xff) << 1); + + return button; +} + +static BYTE g_ActiveResponse[14]; +static void populateActiveResponse() { + // Placeholders + g_ActiveResponse[1] = '@'; + g_ActiveResponse[2] = '@'; + g_ActiveResponse[3] = '@'; + g_ActiveResponse[4] = '@'; + // + g_ActiveResponse[7] = '@'; + g_ActiveResponse[8] = '@'; + g_ActiveResponse[9] = '@'; + g_ActiveResponse[10] = '@'; + + if (!GetAsyncKeyState(VK_LBUTTON)) return; + + POINT pCursor; + GetCursorPos(&pCursor); + if (!mainWindow) return; + if (!ScreenToClient(mainWindow, &pCursor)) return; + + RECT winRect; + if (!GetWindowRect(mainWindow, &winRect)) return; + DWORD dwStyle = GetWindowLongW(mainWindow, GWL_STYLE); + UnadjustWindowRect(&winRect, dwStyle, FALSE); + + int w = winRect.right - winRect.left; + int h = winRect.bottom - winRect.top; + + float x = (float)pCursor.x / (float)(w); + float y = (float)pCursor.y / (float)(h); + + // If the window is square, assume `1P_ONLY` is set + WORD button; + if (w == h) { + button = getSensorInRegion(x, y, 0.0f, 0.0f, 1.0f, 1.0f); + if (button) g_ActiveResponse[button >> 8] |= button & 0xff; + } else { + // 1P + button = getSensorInRegion(x, y, 0.0f, 0.4375f, 0.5f, 0.5625f); + if (button) g_ActiveResponse[button >> 8] |= button & 0xff; + // 2P + button = getSensorInRegion(x, y, 0.5f, 0.4375f, 0.5f, 0.5625f); + if (button) g_ActiveResponse[(button >> 8) + 6] |= button & 0xff; + } +} + +BOOL touch_is_enabled = TRUE; // Default on is important! BYTE thresh = 0x00; // Lazy caching of single value DWORD WINAPI touch_bd_thread(com_device_t* dev) { log_info(plfMaiTouch, "%ls woke up", dev->com->wName); + // Constant values + g_ActiveResponse[0] = '('; + g_ActiveResponse[5] = '@'; + g_ActiveResponse[6] = '@'; + g_ActiveResponse[11] = '@'; + g_ActiveResponse[12] = '@'; + g_ActiveResponse[13] = ')'; + while (1) { if (touch_is_enabled && !comdev_available(dev)) { - // Active mode! - comdev_write(dev, (unsigned char*)"(@@@@@@@@@@@@)", 14); - Sleep(100); + populateActiveResponse(); + comdev_write(dev, g_ActiveResponse, 14); + Sleep(5); // 200Hz should be plenty continue; } - while (read_one(dev) != '{') continue; - while (comdev_available(dev) < 5) { - log_info(plfMaiTouch, "<. .>"); - Sleep(50); - } + BYTE startWait; + do { + comdev_read_blocking(dev, &startWait, 1); + } while (startWait != '{'); + BYTE command[5]; - comdev_read(dev, command, 5); + comdev_read_blocking(dev, command, 5); BYTE response[6]; memcpy(response, "( )", 6); diff --git a/src/micetools/dll/devices/ser_servo_838-15069.c b/src/micetools/dll/devices/ser_servo_838-15069.c index 7a069ac..c7385f7 100644 --- a/src/micetools/dll/devices/ser_servo_838-15069.c +++ b/src/micetools/dll/devices/ser_servo_838-15069.c @@ -4,6 +4,53 @@ #define SERVO_OK 0 #define SERVO_NG 8 +/* + +From AndyGeezer: + +SUD = Sega Universal Drive + +-- sample init code -- + + +ff00007f <-- reset sud +81307f4e <-- set init values/reset sud +fc00205c <-- init sud + +fd00007d +fd00007d <-- poll until not returning 0x44 +fd00007d + +fa001f65 <-- change echo status to return +83600467 <-- set global power +86010205 <-- set friction + +move the wheel left/right to centre here.. + + +8800040c <-- set go to target point power +f0000070 <-- set target point + +84010005 <-- direction off +80000000 <-- motor off + + +8b2000ff <-- centering power +862f00ff <-- friction power +800001FF <-- motor on +fd00007d <-- poll + + + +80 = power + +examples: +80 00 01 = Constantly on +80 01 01 = Run once +80 00 00 = Motor off + +*/ + static DWORD WINAPI ser_servo_15069_thread(com_device_t* dev) { log_game(plfServo15069, "%ls woke up", dev->com->wName); diff --git a/src/micetools/dll/devices/ser_tn32msec.c b/src/micetools/dll/devices/ser_tn32msec.c index 71bd2c0..5360074 100644 --- a/src/micetools/dll/devices/ser_tn32msec.c +++ b/src/micetools/dll/devices/ser_tn32msec.c @@ -43,7 +43,14 @@ static LPCSTR GetCardIdentifier(DWORD player) { player == 0 ? MiceConfig.aime.player1_cardfile : MiceConfig.aime.player2_cardfile; LPSTR cardIdentifier = player == 0 ? cardIdentifier1P : cardIdentifier2P; - if (_PathFileExistsA(cardFile)) { + const size_t cSize = strlen(cardFile) + 1; + wchar_t* wc = malloc(cSize); + if (!wc) return NULL; + MultiByteToWideChar(0, 0, cardFile, -1, wc, cSize + 1); + BOOL exists = FileExistsW(wc); + free(wc); + + if (exists) { log_info(plfAime, "Reading P%d card from %s", player + 1, cardFile); HANDLE hFile = @@ -127,6 +134,13 @@ static CardType TN32GetCardInfo(LPCSTR lpIdentifier, PACCESS_CODE lpAccessCode, &lpFelicaId->m_ID[6], &lpFelicaId->m_ID[7], &nChars) == 8 && nChars == 23) return CardType_FeliCa; + } else if (len == 23) { + if (sscanf_s(lpIdentifier, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%n", + &lpFelicaId->m_ID[0], &lpFelicaId->m_ID[1], &lpFelicaId->m_ID[2], + &lpFelicaId->m_ID[3], &lpFelicaId->m_ID[4], &lpFelicaId->m_ID[5], + &lpFelicaId->m_ID[6], &lpFelicaId->m_ID[7], &nChars) == 8 && + nChars == 23) + return CardType_FeliCa; } return CardType_Unknown; } @@ -160,8 +174,6 @@ static void MifarePopulate(PNFC_CARD lpCard, PACCESS_CODE lpAccessCode) { DWORD nSerial = AccessCodeToSerial(lpAccessCode); lpCard->m_Mifare.m_Uid = nSerial; - printf("Serial: %d\n", nSerial); - // Block 0: Manufacture data (only used by BNG) // Block 1: Game ID information (kCARD_DETECT_KEY_B_READ_1) @@ -536,8 +548,6 @@ DWORD WINAPI aime_bd_thread(com_device_t* dev) { break; } } - - Sleep(50); } } diff --git a/src/micetools/dll/devices/smb_at24c64an.c b/src/micetools/dll/devices/smb_at24c64an.c index defe5d8..ef30098 100644 --- a/src/micetools/dll/devices/smb_at24c64an.c +++ b/src/micetools/dll/devices/smb_at24c64an.c @@ -2,7 +2,7 @@ #include "../../maiBackupStructs.h" #include "_devices.h" -#define EEPROM_PATH L"dev/eeprom.bin" +#define EEPROM_PATH MiceIpcRelativePath("eeprom.bin") #include "../../sysconf.h" @@ -52,17 +52,21 @@ void set_eeprom_static_config() { } void build_eeprom() { // return; - log_info(plfEeprom, "Building default EEPROM file"); + log_misc(plfEeprom, "Building default EEPROM file"); memset(EEPROM_DATA, 0xff, EEPROM_SIZE); set_eeprom_static_config(); + BOOL freeplay = TRUE; AM_SYSDATAwH_CREDIT Credit = { .m_Config = { + .ChuteType = 0, .ServiceType = 1, - .CreditRate = 1, + .Operation = freeplay ? 1 : 0, .CoinRate = { 1, 1 }, + .BonusAdder = 0, + .CreditRate = 1, .Cost = { 1, 1, 1, 1, 1, 1, 1, 1 }, }, }; @@ -86,29 +90,17 @@ void build_eeprom() { // return; fix_crc(DevConfig); memcpy(&EEPROM_DATA[AM_SYSDATAwH_ALPB_DEV_CONFIG_REG], &DevConfig, sizeof DevConfig); memcpy(&EEPROM_DATA[AM_SYSDATAwH_ALPB_DEV_CONFIG_DUP], &DevConfig, sizeof DevConfig); - - // eeprom_dump(); } -AM_SYSDATAwH_HISTORY AmHistory = { - // By making the game ID "____" we ensure games overwrite this, letting us see what they write. - // This is currently the best way I have found to retrieve the game ID - .m_GameId = { '_', '_', '_', '_' }, -}; -/** Clobber EEPROM with our user settings */ +/* Clobber EEPROM with our user settings */ void eeprom_fixup() { set_eeprom_static_config(); set_eeprom_network_config(); - - // AmHistory.m_Region = MiceConfig.sysconf.region & (1 | 2 | 4 | 8); - // fix_crc(AmHistory); - // memcpy(&EEPROM_DATA[AM_SYSDATAwH_HISTORY_REG], &AmHistory, sizeof AmHistory); - // memcpy(&EEPROM_DATA[AM_SYSDATAwH_HISTORY_DUP], &AmHistory, sizeof AmHistory); } void ensure_valid_eeprom() { if (!EEPROM_DATA) { - BOOL isNew = !FileExists(EEPROM_PATH); + BOOL isNew = !FileExistsA(EEPROM_PATH); EEPROM_DATA = open_mapped_file(EEPROM_PATH, EEPROM_SIZE, &EEPROM_FILE, &EEPROM_FILE_MAPPING); @@ -140,14 +132,14 @@ void eeprom_write(WORD addr, BYTE* data, BYTE length) { if (addr == AM_SYSDATAwH_HISTORY_REG && length == sizeof(AM_SYSDATAwH_HISTORY)) { PAM_SYSDATAwH_HISTORY pHistory = (PAM_SYSDATAwH_HISTORY)data; - mice_got_game_id(pHistory->m_GameId); + _MiceGotGameId(pHistory->m_GameId); } else if (addr >= 0x1000 && length > 8) { char game_id[4]; game_id[0] = data[7]; game_id[1] = data[6]; game_id[2] = data[5]; game_id[3] = data[4]; - mice_got_game_id(game_id); + _MiceGotGameId(game_id); } memcpy(EEPROM_DATA + addr, data, length); diff --git a/src/micetools/dll/amvStub/amv.c b/src/micetools/dll/dllStub/amv.c similarity index 72% rename from src/micetools/dll/amvStub/amv.c rename to src/micetools/dll/dllStub/amv.c index b9db2e2..1df4b62 100644 --- a/src/micetools/dll/amvStub/amv.c +++ b/src/micetools/dll/dllStub/amv.c @@ -2,6 +2,31 @@ DWORD NvidiaDebugLevel = 0; +/* ============= AMD ============= */ +__declspec(dllexport) int amvGetAvailableDriverEx_Amd(LPVOID pGpuHandle, DWORD dwGpuHandleSize) { + return 0; +} +__declspec(dllexport) int amvGetAvailableDriver_Amd() { + return amvGetAvailableDriverEx_Amd(NULL, 4); +} +__declspec(dllexport) int amvSetCustomDisplaySettingEx_Amd(LPVOID pGpuHandle, + DWORD dwGpuHandleSize, + LPWORD NV_SetCustomDisplaySetting) { + return 0; +} +__declspec(dllexport) int amvSetCustomDisplaySetting_Amd(LPVOID pGpuHandle) { + return amvSetCustomDisplaySettingEx_Amd(NULL, 4, pGpuHandle); +} + +__declspec(dllexport) int amvSetDisplayModeEx_Amd(LPVOID pGpuHandle, DWORD dwGpuHandleSize, + BYTE displayMode) { + return 0; +} +__declspec(dllexport) int amvSetDisplayMode_Amd(BYTE displayMode) { + return amvSetDisplayModeEx_Amd(NULL, 4, displayMode); +} + +/* ============= Nvidia ============= */ __declspec(dllexport) int amvFinalize_Nvidia() { return 0; } __declspec(dllexport) int amvGetAvailableDriverEx_Nvidia(LPVOID pGpuHandle, DWORD dwGpuHandleSize) { return 0; diff --git a/src/micetools/dll/dllStub/ati.c b/src/micetools/dll/dllStub/ati.c new file mode 100644 index 0000000..d7a892a --- /dev/null +++ b/src/micetools/dll/dllStub/ati.c @@ -0,0 +1,7 @@ +#include + +// TODO: ATIDL_GetGamma +// TODO: ATIDL_SetGamma + +__declspec(dllexport) int ATIDL_GetGamma(int a, void* p) { return 0; } +__declspec(dllexport) int ATIDL_SetGamma(int a, void* p) { return 0; } diff --git a/src/micetools/dll/dllmain.c b/src/micetools/dll/dllmain.c index 906c8df..47c21ef 100644 --- a/src/micetools/dll/dllmain.c +++ b/src/micetools/dll/dllmain.c @@ -1,3 +1,4 @@ +#include #include #include "../lib/util/pid.h" @@ -8,54 +9,12 @@ WCHAR exeName[MAX_PATH + 1]; char exeNameC[MAX_PATH + 1]; -DWORD imageOffset; - -#define WIN32_EXE_BASE 0x00400000 - -DWORD GetImageBase(void) { - WCHAR sModulePath[MAX_PATH]; - GetModuleFileNameW(NULL, sModulePath, MAX_PATH); - - HANDLE hObject = - _CreateFileW(sModulePath, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hObject == INVALID_HANDLE_VALUE) { - log_error(plfBoot, "Failed to open %ls %03x", sModulePath, GetLastError()); - return 0; - } - - IMAGE_DOS_HEADER dosHeader = { 0 }; - DWORD nRead; - _SetFilePointer(hObject, 0, NULL, FILE_BEGIN); - _ReadFile(hObject, &dosHeader, sizeof dosHeader, &nRead, NULL); - if (nRead != sizeof dosHeader) { - log_error(plfBoot, "Failed to read DOS header %03x", GetLastError()); - return 0; - } - if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) { - log_error(plfBoot, "Invalid DOS magic number: %04x", dosHeader.e_magic); - return 0; - } - - IMAGE_NT_HEADERS32 ntHeaders32 = { 0 }; - _SetFilePointer(hObject, dosHeader.e_lfanew, NULL, FILE_BEGIN); - _ReadFile(hObject, &ntHeaders32, sizeof ntHeaders32, &nRead, NULL); - if (nRead != sizeof ntHeaders32) { - log_error(plfBoot, "Failed to read NT header %03x", GetLastError()); - return 0; - } - if (ntHeaders32.Signature != IMAGE_NT_SIGNATURE) { - log_error(plfBoot, "Invalid NT signature: %08x", ntHeaders32.Signature); - return 0; - } - - _CloseHandle(hObject); - return ntHeaders32.OptionalHeader.ImageBase; -} +size_t imageOffset; void apply_patches(HMODULE hModule) { void* baseAddress = (void*)hModule; - DWORD imageBase = GetImageBase(); + DWORD imageBase = MiceGetImageBase(); if (imageBase == 0) { log_error(plfBoot, "Failed to locate image base. Patches will not be applied."); return; @@ -69,22 +28,22 @@ void apply_patches(HMODULE hModule) { patch_t* patch = patch_list->next; if (patch == NULL) { - log_warning(plfBoot, "No patches to apply. Did you forgot an amiDebug patch file?"); + log_info(plfBoot, "No patches to apply. Skipping."); + return; } while (patch) { DWORD oldProt; - VirtualProtect((void*)((DWORD)patch->address + imageOffset), patch->count, - PAGE_EXECUTE_READWRITE, &oldProt); + VirtualProtect((void*)(patch->address + imageOffset), patch->count, PAGE_EXECUTE_READWRITE, + &oldProt); - if (memcmp(patch->match, (void*)((DWORD)patch->address + imageOffset), patch->count) != 0) { + if (memcmp(patch->match, (void*)(patch->address + imageOffset), patch->count) != 0) { log_error(plfBoot, "Patch %s failed! from-value missmatch", patch->name); - VirtualProtect((void*)((DWORD)patch->address + imageOffset), patch->count, oldProt, - &oldProt); + VirtualProtect((void*)(patch->address + imageOffset), patch->count, oldProt, &oldProt); patch = patch->next; continue; } - memcpy((void*)((DWORD)patch->address + imageOffset), patch->replace, patch->count); + memcpy((void*)(patch->address + imageOffset), patch->replace, patch->count); log_misc(plfBoot, "Patched %d bytes at %08x (%s)", patch->count, patch->address, patch->name); @@ -111,39 +70,48 @@ void init_injection(HMODULE hModule) { log_info(plfBoot, "Handover complete. Now executing within %ls", exeName); // Start Mice FS - BOOL success = TRUE; - success &= MiceFSInit(); - if (success) success &= MiceFSAddRingedgeLayers(FALSE); - if (!success) { + if (!MiceFSInit()) { log_error(plfFile, "Failed to initialise Mice FS stack: %d", GetLastError()); exit(1); } - if (MiceConfig.mice.original && MiceConfig.mice.original[0] == '\0') { - CHAR szCurrentDir[MAX_PATH + 1]; - GetCurrentDirectoryA(sizeof szCurrentDir, szCurrentDir); - MiceFSAddLayer(RING_MOUNT_ORIGINAL, szCurrentDir); - MiceFSAddLayer(RING_MOUNT_GAME, szCurrentDir); + BOOL success = TRUE; + success &= MiceFSAddDevLayers(); + + CHAR szWorkPath[MAX_PATH + 1]; + if (!MiceConfig.mice.original || MiceConfig.mice.original[0] == '\0') { + _GetCurrentDirectoryA(sizeof szWorkPath, szWorkPath); + success &= MiceFSAddLayer(RING_MOUNT_ORIGINAL, szWorkPath); + success &= MiceFSAddLayer(RING_MOUNT_GAME, szWorkPath); } else { - MiceFSAddLayer(RING_MOUNT_ORIGINAL, MiceConfig.mice.original); - MiceFSAddLayer(RING_MOUNT_GAME, MiceConfig.mice.original); + success &= MiceFSAddLayer(RING_MOUNT_ORIGINAL, MiceConfig.mice.original); + success &= MiceFSAddLayer(RING_MOUNT_GAME, MiceConfig.mice.original); } if (MiceConfig.mice.patch && MiceConfig.mice.patch[0] != '\0') { - MiceFSAddLayer(RING_MOUNT_PATCH, MiceConfig.mice.patch); - MiceFSAddLayer(RING_MOUNT_GAME, MiceConfig.mice.patch); + success &= MiceFSAddLayer(RING_MOUNT_PATCH, MiceConfig.mice.patch); + success &= MiceFSAddLayer(RING_MOUNT_GAME, MiceConfig.mice.patch); } if (MiceConfig.mice.extend && MiceConfig.mice.extend[0] != '\0') { - MiceFSAddLayer(RING_MOUNT_EXTEND_VOL, MiceConfig.mice.extend); - MiceFSAddLayer(RING_MOUNT_GAME, MiceConfig.mice.extend); + success &= MiceFSAddLayer(RING_MOUNT_EXTEND_VOL, MiceConfig.mice.extend); + success &= MiceFSAddLayer(RING_MOUNT_GAME, MiceConfig.mice.extend); } if (MiceConfig.mice.extend2 && MiceConfig.mice.extend2[0] != '\0') { MiceFSAddLayer(RING_MOUNT_EXTEND2_VOL, MiceConfig.mice.extend2); MiceFSAddLayer(RING_MOUNT_GAME, MiceConfig.mice.extend2); } - if (success) success &= MiceFSAddDevLayers(); + success &= MiceFSAddRingedgeLayers(FALSE); - MiceFSSetCwd(RING_MOUNT_GAME "\\"); + if (!success) { + log_error(plfFile, "Failed to add Mice FS layers: %d", GetLastError()); + exit(1); + } + + szWorkPath[0] = '\0'; + strcat_s(szWorkPath, sizeof szWorkPath, RING_MOUNT_GAME "\\"); + if (_miceIpcData && _miceIpcData->m_LauncherIsReady) + strcat_s(szWorkPath, sizeof szWorkPath, _miceIpcData->m_PathPrefix); + MiceFSSetCwd(szWorkPath); // Setup default COM devices init_com_devices(); @@ -158,7 +126,10 @@ void init_injection(HMODULE hModule) { // MX SuperIO: Communicate with the HW monitor chip if (MiceConfig.drivers.mxsuperio) setup_mxsuperio(); // MX JVS: Interacting with JVS-based devices - if (MiceConfig.drivers.mxjvs) setup_mxjvs(); + if (MiceConfig.drivers.mxjvs) { + setup_mxjvs(); + load_keybind_config(); + } // MX HW Reset: Forcibly reboot the machine if (MiceConfig.drivers.mxhwreset) setup_mxhwreset(); // MX SMBus: Communicate over the LPC bus. This contains the EEPROM, and PCA9535 @@ -177,23 +148,9 @@ void init_injection(HMODULE hModule) { // Must be the last thing called! prebind_hooks(); - // TODO: Figure out why we're needing to call this manually (medium priority) - if (wcscmp(exeName, L"ALLNetProc.exe") == 0) { - log_warning(plfBoot, "Making explicit call to OPENSSL_add_all_algorithms_noconf"); - - DWORD imageBase = GetImageBase(); - imageOffset = (DWORD)hModule - imageBase; - - // OPENSSL_add_all_algorithms_noconf (old exe) - // ((void (*)(void))(0x00459770 + imageOffset))(); - - // OPENSSL_add_all_algorithms_noconf (pink exe) - // ((void (*)(void))(0x00463480 + imageOffset))(); - - // TODO: Find finale offset - } - setup_hooks(); + + start_devices(); } void tea_hook_test(char* fmt, ...) { @@ -203,24 +160,63 @@ void tea_hook_test(char* fmt, ...) { va_end(argp); } +BOOL g_bIsInDllMain = TRUE; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if (ul_reason_for_call != DLL_PROCESS_ATTACH) return TRUE; + g_bIsInDllMain = TRUE; GetModuleFileNameW(NULL, exeName, MAX_PATH); wcscpy_s(exeName, MAX_PATH + 1, PathFindFileNameW(exeName)); + if (!MiceIpcSetup(FALSE)) { + fprintf(stderr, "Failed to initialise IPC from within micelib. Bailing hard!"); + TerminateProcess(GetCurrentProcess(), 0); + return FALSE; + } + HMODULE exeModule = GetModuleHandleA(NULL); init_injection(exeModule); - if (wcscmp(exeName, L"InitialD8_GLW_RE_SBZZ_dumped_.exe") == 0) { - CreateHook32((void*)(0x00407850), &tea_hook_test); - // *((DWORD*)(0x00407850)) = (DWORD)(&logcb); + _MiceGotGameId(_miceIpcData->m_GameId); + + // InitialD 8 + if (false) { + DWORD dword = 0; + MiceHookPatchAtImage(exeModule, (PVOID)0x0126867c, &dword, 4); + + BYTE movEax1[] = { 0xb8, 0x01, 0x00, 0x00, 0x00 }; + // Bypass cgGcIsProfileSupported + MiceHookNopAtImage(exeModule, (PVOID)0xb4c9ac, 6); + MiceHookPatchAtImage(exeModule, (PVOID)0xb4c9ac, movEax1, sizeof movEax1); + MiceHookNopAtImage(exeModule, (PVOID)0xb4ca9f, 6); + MiceHookPatchAtImage(exeModule, (PVOID)0xb4ca9f, movEax1, sizeof movEax1); + + // change glProgramEnvParameters4fvNV to EXT version that works with any GPUs + static const char* arb = "glProgramEnvParameters4fvEXT"; + MiceHookPatchAtImage(exeModule, (PVOID)0xB70E26, &arb, 4); + // force volatile texture (driver bug(?) check) presence + MiceHookNopAtImage(exeModule, (PVOID)(0xB6BC20 + 0x5667), 2); + + DWORD profile; + BOOL amd = TRUE; + if (amd) + profile = 6151; // CG_PROFILE_FP40 + else + profile = 7000; // CG_PROFILE_ARBFP1 + + // Vertex shader is compiled with profile 6150 + // Pixel shader is compiled with profile 7000 + + // Change shader profile for glareGenerator + MiceHookPatchAtImage(exeModule, (PVOID)0x7D06A8, &profile, 4); + + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + void* original = (void*)0x00407850; + DetourAttach(&original, tea_hook_test); + DetourTransactionCommit(); } - // if (wcscmp(exeName, L"RingGame.exe") == 0) { - // log_warning(plfBoot, "Bodge hook goo!"); - // CreateHook32((void*)(0x005f2580), &tea_hook_test); - // } - + g_bIsInDllMain = FALSE; return TRUE; } diff --git a/src/micetools/dll/drivers/columba.c b/src/micetools/dll/drivers/columba.c index 09e992e..0302075 100644 --- a/src/micetools/dll/drivers/columba.c +++ b/src/micetools/dll/drivers/columba.c @@ -1,5 +1,5 @@ #include "../lib/am/amOemstring.h" -#include "../lib/dmi/dmi.h" +#include "../lib/mice/dmi.h" #include "mx.h" #define DMI_HEADER_START 0x000f0000 diff --git a/src/micetools/dll/drivers/jvs.h b/src/micetools/dll/drivers/jvs.h index 4631bc3..6a90cf7 100644 --- a/src/micetools/dll/drivers/jvs.h +++ b/src/micetools/dll/drivers/jvs.h @@ -4,35 +4,42 @@ #define JVS_NODE_BROADCAST 0xff #define JVS_VERSION_CMD 0x13 // A real board reports 1.1, but we support 1.3 -#define JVS_VERSION_JVS 0x20 +#define JVS_VERSION_JVS 0x30 // My board is 0x20, but SBWU needs 0x30 #define JVS_VERSION_COMM 0x10 -#define JVS_STATUS_OK 0x01 -#define JVS_STATUS_UKCOM 0x02 -#define JVS_STATUS_SUM 0x03 -#define JVS_STATUS_OVERFLOW 0x04 -#define JVS_STATUS_UNKNOWN 0xff // Not a spec-compliant status, but there's none for this! +typedef enum JVS_STATUS { + JVS_STATUS_OK = 0x01, + JVS_STATUS_UKCOM = 0x02, + JVS_STATUS_SUM = 0x03, + JVS_STATUS_OVERFLOW = 0x04, + JVS_STATUS_UNKNOWN = 0xfe, // Not a spec-compliant status, but there's none for this! + JVS_STATUS_SILENCE = 0xff, // Pseudo-status to indicate that the response is not to be sent +} JVS_STATUS; -#define JVS_REPORT_OK 0x01 -#define JVS_REPORT_PARAM_NODATA 0x02 -#define JVS_REPORT_PARAM_INVALID 0x03 -#define JVS_REPORT_BUSY 0x04 +typedef enum JVS_REPORT { + JVS_REPORT_OK = 0x01, + JVS_REPORT_PARAM_NODATA = 0x02, + JVS_REPORT_PARAM_INVALID = 0x03, + JVS_REPORT_BUSY = 0x04, +} JVS_REPORT; -#define JVS_FEATURE_PAD 0x00 -#define JVS_FEATURE_EOF 0x00 -#define JVS_FEATURE_PLAYERS 0x01 -#define JVS_FEATURE_COINS 0x02 -#define JVS_FEATURE_ANALOG 0x03 -#define JVS_FEATURE_ROTARY 0x04 -#define JVS_FEATURE_KEYCODE 0x05 -#define JVS_FEATURE_SCREEN_POS 0x06 -#define JVS_FEATURE_MISC_SWITCH 0x07 -#define JVS_FEATURE_CARDS 0x10 -#define JVS_FEATURE_MEDAL_HOPPER 0x11 -#define JVS_FEATURE_GPIO 0x12 -#define JVS_FEATURE_ANALOG_OUT 0x13 -#define JVS_FEATURE_CHAR_OUT 0x14 -#define JVS_FEATURE_BACKUP 0x15 +typedef enum JVS_FEATURE { + JVS_FEATURE_PAD = 0x00, + JVS_FEATURE_EOF = 0x00, + JVS_FEATURE_PLAYERS = 0x01, + JVS_FEATURE_COINS = 0x02, + JVS_FEATURE_ANALOG = 0x03, + JVS_FEATURE_ROTARY = 0x04, + JVS_FEATURE_KEYCODE = 0x05, + JVS_FEATURE_SCREEN_POS = 0x06, + JVS_FEATURE_MISC_SWITCH = 0x07, + JVS_FEATURE_CARDS = 0x10, + JVS_FEATURE_MEDAL_HOPPER = 0x11, + JVS_FEATURE_GPIO = 0x12, + JVS_FEATURE_ANALOG_OUT = 0x13, + JVS_FEATURE_CHAR_OUT = 0x14, + JVS_FEATURE_BACKUP = 0x15, +} JVS_FEATURE; #define JVS_CHARTYPE_UNKNOWN 0x00 #define JVS_CHARTYPE_NUMBER 0x01 diff --git a/src/micetools/dll/drivers/jvs_boards/jvs.h b/src/micetools/dll/drivers/jvs_boards/jvs.h new file mode 100644 index 0000000..987369a --- /dev/null +++ b/src/micetools/dll/drivers/jvs_boards/jvs.h @@ -0,0 +1,74 @@ +#include + +#include "../../input.h" +#include "../jvs.h" + +#define JVS_IO_MAX 31 + +#define MICE_JVS_MAX 256 +// Every single byte could be masked +#define MICE_JVS_MASKED (MICE_JVS_MAX * 2) + +#define new(type, instance) _##type##_vftable.ctor(&instance) + +typedef struct MICE_JVS MICE_JVS, *PMICE_JVS; +typedef struct MICE_JVS_vftable MICE_JVS_vftable; + +struct MICE_JVS_vftable { + void (*ctor)(PMICE_JVS this); + JVS_STATUS (*transact)(PMICE_JVS this, BYTE command); + void (*readAllButtons)(PMICE_JVS this); +}; + +extern MICE_JVS_vftable _MiceJvsBase_vftable; +extern MICE_JVS_vftable _MiceJvs837_14572_vftable; +extern MICE_JVS_vftable _MiceJvs837_14895_vftable; + +struct MICE_JVS { + MICE_JVS_vftable* vftable; + + LPBYTE m_lpInData; + DWORD m_nInIdx; + LPBYTE m_lpOutData; + DWORD m_nOutIdx; + + BYTE m_Address; + + LPCSTR m_BoardId; + BYTE m_CommandVersion; + BYTE m_JvsRevision; + BYTE m_VersionComm; + + BOOL m_SenseOut; + + BYTE m_Players; + BYTE m_ButtonsPerPlayer; + BYTE m_Coins; + BYTE m_NumButtons; + LPBOOL m_ButtonStates; + PMICE_BUTTON_BINDING m_Bindings; + + DWORD m_CoinCounts[2]; +}; +#pragma pack(push, 1) +typedef struct JVS_PACKET { + BYTE m_Sync; + BYTE m_Dst; + BYTE m_nBytes; + BYTE m_Data[MICE_JVS_MAX - 3]; +} JVS_PACKET, *PJVS_PACKET; +#pragma pack(pop) + +static inline BYTE MiceJvsRead(PMICE_JVS this) { + if (this->m_lpInData == NULL) return 0xff; + if (this->m_nInIdx < MICE_JVS_MAX) return this->m_lpInData[this->m_nInIdx++]; + return 0xff; +} + +#include +static inline void MiceJvsWrite(PMICE_JVS this, BYTE data) { + if (this->m_lpOutData == NULL) return; + if (this->m_nOutIdx < MICE_JVS_MAX) this->m_lpOutData[this->m_nOutIdx++] = data; +} + +extern MICE_JVS _MiceJvsBoards[JVS_IO_MAX]; diff --git a/src/micetools/dll/drivers/jvs_boards/jvs_837_14572.c b/src/micetools/dll/drivers/jvs_boards/jvs_837_14572.c new file mode 100644 index 0000000..fc47b9f --- /dev/null +++ b/src/micetools/dll/drivers/jvs_boards/jvs_837_14572.c @@ -0,0 +1,148 @@ +#include + +#include "../../common.h" +#include "jvs.h" + +#define PLAYER_COUNT 2 +#define COIN_COUNTERS 2 + +#define SWITCH_BYTES 2 +#define PLAYER_SWITCH_COUNT 13 // Must be less than 8*SWITCH_BYTES !! + +BOOL coin_solenoid = false; + +static const char JVS_837_14572_ID[] = "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"; + +static void Ctor(PMICE_JVS this) { + _MiceJvsBase_vftable.ctor(this); + this->vftable = &_MiceJvs837_14572_vftable; + this->vftable->readAllButtons = _MiceJvsBase_vftable.readAllButtons; + + this->m_BoardId = JVS_837_14572_ID; + this->m_CommandVersion = 0x13; + this->m_JvsRevision = 0x20; + this->m_VersionComm = 0x10; + + // Coin + Test + 13 switches per player + this->m_ButtonsPerPlayer = PLAYER_SWITCH_COUNT; + this->m_Coins = COIN_COUNTERS; + this->m_Players = PLAYER_COUNT; + + this->m_NumButtons = this->m_Coins + 1 + (this->m_ButtonsPerPlayer * this->m_Players); + this->m_ButtonStates = malloc(this->m_NumButtons * sizeof *this->m_ButtonStates); + ZeroMemory(this->m_ButtonStates, this->m_NumButtons * sizeof *this->m_ButtonStates); + this->m_Bindings = malloc(this->m_NumButtons * sizeof *this->m_Bindings); + ZeroMemory(this->m_Bindings, this->m_NumButtons * sizeof *this->m_Bindings); + + ZeroMemory(this->m_CoinCounts, sizeof this->m_CoinCounts); +} + +static JVS_STATUS Transact(PMICE_JVS this, BYTE command) { + switch (command) { + case JVS_CMD_READ_SW: + BYTE players = MiceJvsRead(this); + BYTE switch_bytes = MiceJvsRead(this); + if (players > this->m_Players || switch_bytes != SWITCH_BYTES) { + MiceJvsWrite(this, JVS_REPORT_PARAM_INVALID); + return JVS_STATUS_OK; + } + MiceJvsWrite(this, JVS_REPORT_OK); + + this->vftable->readAllButtons(this); + + // Test button + MiceJvsWrite(this, this->m_ButtonStates[this->m_Coins] ? 0x80 : 0x00); + + for (int player = 0; player < players; player++) { + unsigned short buttons = 0x0000; + for (int j = 0; j < this->m_ButtonsPerPlayer; j++) { + buttons <<= 1; + if (this->m_ButtonStates[this->m_Coins + 1 + + (this->m_ButtonsPerPlayer * player) + j]) { + buttons |= 1; + } + } + buttons <<= (16 - this->m_ButtonsPerPlayer); + + for (int i = switch_bytes - 1; i >= 0; i--) + MiceJvsWrite(this, (buttons >> (i * 8)) & 0xff); + } + return JVS_STATUS_OK; + + case JVS_CMD_READ_COIN: + MiceJvsWrite(this, JVS_REPORT_OK); + unsigned char coin_count; + coin_count = MiceJvsRead(this); + + MiceInputPollDevices(); + for (int i = 0; i < this->m_Coins; i++) + if (MiceInputGetButtonState(&this->m_Bindings[i])) this->m_CoinCounts[i]++; + + for (unsigned char slot = 0; slot < this->m_Coins; slot++) { + MiceJvsWrite(this, (this->m_CoinCounts[slot] >> 8) & 0xff); + MiceJvsWrite(this, this->m_CoinCounts[slot] & 0xff); + } + return JVS_STATUS_OK; + + case JVS_CMD_READ_ANALOGS: + MiceJvsWrite(this, JVS_REPORT_OK); + // TODO: Actually emulate these (super low priority) + unsigned char analog_count; + analog_count = MiceJvsRead(this); + for (int i = analog_count; i > 0; i--) { + MiceJvsWrite(this, 0xde); + MiceJvsWrite(this, 0xad); + } + return JVS_STATUS_OK; + + case JVS_CMD_WRITE_GPIO1: + MiceJvsWrite(this, JVS_REPORT_OK); + unsigned char gpio_bytes; + gpio_bytes = MiceJvsRead(this); + for (int i = 0; i < gpio_bytes; i++) { + unsigned char gpio_value; + gpio_value = MiceJvsRead(this); + + if (i == 0) { + if (!!(gpio_value & 0x80) != coin_solenoid) { + coin_solenoid = !!(gpio_value & 0x80); + log_info(plfMxJvs, "Coin solenoid: %s", + coin_solenoid ? "Locked" : "Unlocked"); + } + } + + // log_warning(plfMxJvs, "Unhandled GPIO write: *(%d) = %02x", i, gpio_value); + } + return JVS_STATUS_OK; + case JVS_CMD_WRITE_GPIO2: + case JVS_CMD_WRITE_GPIO3: + MiceJvsWrite(this, JVS_REPORT_OK); + unsigned char gpioByteIndex; + unsigned char gpioByteData; + gpioByteIndex = MiceJvsRead(this); + gpioByteData = MiceJvsRead(this); + log_warning(plfMxJvs, "GPIO%d Unhandled: [%02x]=%02x", + (command - JVS_CMD_WRITE_GPIO2) + 2, gpioByteIndex, gpioByteData); + return JVS_STATUS_OK; + + case JVS_CMD_COIN_DECREASE: + MiceJvsWrite(this, JVS_REPORT_OK); + + unsigned char slot; + slot = MiceJvsRead(this); + unsigned char cAmount[2]; + cAmount[0] = MiceJvsRead(this); + cAmount[1] = MiceJvsRead(this); + unsigned short sAmount = cAmount[0] << 8 | cAmount[1]; + // board->coin_counts[slot] -= sAmount; + return JVS_STATUS_OK; + + default: + return _MiceJvsBase_vftable.transact(this, command); + } +} + +MICE_JVS_vftable _MiceJvs837_14572_vftable = { + .ctor = Ctor, + .transact = Transact, +}; diff --git a/src/micetools/dll/drivers/jvs_boards/jvs_837_14895.c b/src/micetools/dll/drivers/jvs_boards/jvs_837_14895.c new file mode 100644 index 0000000..e24116f --- /dev/null +++ b/src/micetools/dll/drivers/jvs_boards/jvs_837_14895.c @@ -0,0 +1,169 @@ +#include + +#include "../../common.h" +#include "jvs.h" + +#define PLAYER_COUNT 1 +#define COIN_COUNTERS 2 +#define SWITCH_BYTES 2 + +BOOL coin_solenoid = false; + +static const char JVS_837_14895_ID[] = ";;837-14895;;;FE MODE;"; + +static void Ctor(PMICE_JVS this) { + _MiceJvsBase_vftable.ctor(this); + this->vftable = &_MiceJvs837_14895_vftable; + this->vftable->readAllButtons = _MiceJvsBase_vftable.readAllButtons; + + this->m_BoardId = JVS_837_14895_ID; + this->m_CommandVersion = 0x13; + this->m_JvsRevision = 0x30; + this->m_VersionComm = 0x10; + + // Eeh whatever I'll do this eventually + this->m_Players = 0; + this->m_ButtonsPerPlayer = 0; + this->m_Coins = 0; + this->m_NumButtons = 0; +} + +// TODO: Urgent: Restore buttons + +static JVS_STATUS Transact(PMICE_JVS this, BYTE command) { + switch (command) { + case JVS_CMD_GET_FEATURES: + MiceJvsWrite(this, JVS_REPORT_OK); + + MiceJvsWrite(this, JVS_FEATURE_PLAYERS); + MiceJvsWrite(this, PLAYER_COUNT); + MiceJvsWrite(this, 22); // bits per player + MiceJvsWrite(this, JVS_FEATURE_PAD); + + // We need at least 15 general outputs + MiceJvsWrite(this, JVS_FEATURE_GPIO); + MiceJvsWrite(this, 16); + MiceJvsWrite(this, JVS_FEATURE_PAD); + MiceJvsWrite(this, JVS_FEATURE_PAD); + + // Toss in two coin counters for good luck + MiceJvsWrite(this, JVS_FEATURE_COINS); + MiceJvsWrite(this, COIN_COUNTERS); + MiceJvsWrite(this, JVS_FEATURE_PAD); + MiceJvsWrite(this, JVS_FEATURE_PAD); + + MiceJvsWrite(this, JVS_FEATURE_EOF); + return JVS_STATUS_OK; + + case JVS_CMD_READ_SW: + unsigned char players; + unsigned char switch_bytes; + players = MiceJvsRead(this); + switch_bytes = MiceJvsRead(this); + if (players > PLAYER_COUNT || switch_bytes != 3) { + MiceJvsWrite(this, JVS_REPORT_PARAM_INVALID); + return JVS_STATUS_OK; + } + + // update_jvs_buttons(board); + + // MiceJvsWrite(this, JVS_REPORT_OK); + // MiceJvsWrite(this, board->last_sysbuttons); + // for (int player = 0; player < players; player++) { + // for (int i = switch_bytes - 1; i >= 0; i--) { + // MiceJvsWrite(this, (board->last_buttons[player] >> (i * 8)) & 0xff); + // } + // } + MiceJvsWrite(this, JVS_REPORT_OK); + MiceJvsWrite(this, 0); + for (int player = 0; player < players; player++) { + for (int i = switch_bytes - 1; i >= 0; i--) { + MiceJvsWrite(this, (0x00 >> (i * 8)) & 0xff); + } + } + return JVS_STATUS_OK; + + case JVS_CMD_READ_COIN: + MiceJvsWrite(this, JVS_REPORT_OK); + unsigned char coin_count; + coin_count = MiceJvsRead(this); + + // int coin1 = jvsKeybindings[board->num].buttons[0]; + // int coin2 = jvsKeybindings[board->num].buttons[1]; + + // if (coin1 && GetAsyncKeyState(coin1) & 1) board->coin_counts[0]++; + // if (coin2 && GetAsyncKeyState(coin2) & 1) board->coin_counts[1]++; + + // for (unsigned char slot = 0; slot < coin_count; slot++) { + // MiceJvsWrite(this, board->coin_counts[slot] >> 8); + // MiceJvsWrite(this, board->coin_counts[slot] & 0xff); + // } + + for (unsigned char slot = 0; slot < coin_count; slot++) { + MiceJvsWrite(this, 0); + MiceJvsWrite(this, 0); + } + return JVS_STATUS_OK; + + case JVS_CMD_READ_ANALOGS: + MiceJvsWrite(this, JVS_REPORT_OK); + // TODO: Actually emulate these (super low priority) + unsigned char analog_count; + analog_count = MiceJvsRead(this); + for (int i = analog_count; i > 0; i--) { + MiceJvsWrite(this, 0xde); + MiceJvsWrite(this, 0xad); + } + return JVS_STATUS_OK; + + case JVS_CMD_WRITE_GPIO1: + MiceJvsWrite(this, JVS_REPORT_OK); + unsigned char gpio_bytes; + gpio_bytes = MiceJvsRead(this); + for (int i = 0; i < gpio_bytes; i++) { + unsigned char gpio_value; + gpio_value = MiceJvsRead(this); + + if (i == 0) { + if (!!(gpio_value & 0x80) != coin_solenoid) { + coin_solenoid = !!(gpio_value & 0x80); + log_info(plfMxJvs, "Coin solenoid: %s", + coin_solenoid ? "Locked" : "Unlocked"); + } + } + + // log_warning(plfMxJvs, "Unhandled GPIO write: *(%d) = %02x", i, gpio_value); + } + return JVS_STATUS_OK; + case JVS_CMD_WRITE_GPIO2: + case JVS_CMD_WRITE_GPIO3: + MiceJvsWrite(this, JVS_REPORT_OK); + unsigned char gpioByteIndex; + unsigned char gpioByteData; + gpioByteIndex = MiceJvsRead(this); + gpioByteData = MiceJvsRead(this); + log_warning(plfMxJvs, "GPIO%d Unhandled: [%02x]=%02x", + (command - JVS_CMD_WRITE_GPIO2) + 2, gpioByteIndex, gpioByteData); + return JVS_STATUS_OK; + + case JVS_CMD_COIN_DECREASE: + MiceJvsWrite(this, JVS_REPORT_OK); + + unsigned char slot; + slot = MiceJvsRead(this); + unsigned char cAmount[2]; + cAmount[0] = MiceJvsRead(this); + cAmount[1] = MiceJvsRead(this); + unsigned short sAmount = cAmount[0] << 8 | cAmount[1]; + // board->coin_counts[slot] -= sAmount; + return JVS_STATUS_OK; + + default: + return _MiceJvsBase_vftable.transact(this, command); + } +} + +MICE_JVS_vftable _MiceJvs837_14895_vftable = { + .ctor = Ctor, + .transact = Transact, +}; diff --git a/src/micetools/dll/drivers/jvs_boards/jvs_base.c b/src/micetools/dll/drivers/jvs_boards/jvs_base.c new file mode 100644 index 0000000..94b2d38 --- /dev/null +++ b/src/micetools/dll/drivers/jvs_boards/jvs_base.c @@ -0,0 +1,115 @@ +#include + +#include "../../common.h" +#include "jvs.h" + +static void Ctor(PMICE_JVS this) { + this->vftable = &_MiceJvsBase_vftable; + + this->m_nInIdx = 0; + this->m_lpInData = NULL; + this->m_nOutIdx = 0; + this->m_lpOutData = NULL; + + this->m_BoardId = NULL; + + this->m_Address = 0xff; + + // Sane, but impractical, defaults. Boards should set better values. + this->m_CommandVersion = 0x10; + this->m_JvsRevision = 0x20; + this->m_VersionComm = 0x10; +} + +static JVS_STATUS Transact(PMICE_JVS this, BYTE command) { + switch (command) { + case JVS_CMD_RESET: + if (MiceJvsRead(this) != JVS_CMD_RESET_ASSERT) return JVS_STATUS_UKCOM; + + this->m_Address = 0xff; + this->m_SenseOut = TRUE; + return JVS_STATUS_SILENCE; + case JVS_CMD_CHANGE_COMMS: + return JVS_STATUS_SILENCE; + + case JVS_CMD_ASSIGN_ADDR: + MiceJvsWrite(this, JVS_REPORT_OK); + this->m_SenseOut = FALSE; + // we don't bother remembering the address because at the moment we only simulate + // a single board in the JVS chain. + MiceJvsRead(this); + return JVS_STATUS_OK; + + case JVS_CMD_READ_ID: + MiceJvsWrite(this, JVS_REPORT_OK); + if (this->m_BoardId == NULL) + MiceJvsWrite(this, 0x00); + else + for (size_t i = 0; i < strlen(this->m_BoardId); i++) + MiceJvsWrite(this, this->m_BoardId[i]); + return JVS_STATUS_OK; + case JVS_CMD_GET_CMD_VERSION: + MiceJvsWrite(this, JVS_REPORT_OK); + MiceJvsWrite(this, this->m_CommandVersion); + return JVS_STATUS_OK; + case JVS_CMD_GET_JVS_VERSION: + MiceJvsWrite(this, JVS_REPORT_OK); + MiceJvsWrite(this, this->m_JvsRevision); + return JVS_STATUS_OK; + case JVS_CMD_GET_COMM_VERSION: + MiceJvsWrite(this, JVS_REPORT_OK); + MiceJvsWrite(this, this->m_VersionComm); + return JVS_STATUS_OK; + + case JVS_CMD_GET_FEATURES: + MiceJvsWrite(this, JVS_REPORT_OK); + + MiceJvsWrite(this, JVS_FEATURE_PLAYERS); + MiceJvsWrite(this, this->m_Players); + MiceJvsWrite(this, this->m_ButtonsPerPlayer); + MiceJvsWrite(this, JVS_FEATURE_PAD); + MiceJvsWrite(this, JVS_FEATURE_COINS); + MiceJvsWrite(this, this->m_Coins); + MiceJvsWrite(this, JVS_FEATURE_PAD); + MiceJvsWrite(this, JVS_FEATURE_PAD); + + // TODO: Make this based on `this` + MiceJvsWrite(this, JVS_FEATURE_ANALOG); + MiceJvsWrite(this, 8); // 8 ADC channels + MiceJvsWrite(this, JVS_FEATURE_PAD); // ?? (was "10" prior) + MiceJvsWrite(this, JVS_FEATURE_PAD); + + // TODO: Make this based on `this` + MiceJvsWrite(this, JVS_FEATURE_GPIO); + MiceJvsWrite(this, 6); // 6 ports + MiceJvsWrite(this, JVS_FEATURE_PAD); + MiceJvsWrite(this, JVS_FEATURE_PAD); + + MiceJvsWrite(this, JVS_FEATURE_EOF); + return JVS_STATUS_OK; + + case JVS_CMD_RECEIVE_MAIN_ID: + unsigned char tempRead = 0xff; + while (tempRead != 0) tempRead = MiceJvsRead(this); + // TODO: Do we need to report here? + return JVS_STATUS_OK; + + default: + log_error(plfMxJvs, "Unknown command: 0x%02x", command); + return JVS_STATUS_UKCOM; + } +} + +static void ReadAllButtons(PMICE_JVS this) { + MiceInputPollDevices(); + + for (DWORD i = 0; i < this->m_NumButtons; i++) { + this->m_ButtonStates[i] = MiceInputGetButtonState(&(this->m_Bindings[i])); + } +} + +MICE_JVS_vftable _MiceJvsBase_vftable = { + .ctor = Ctor, + .transact = Transact, + .readAllButtons = ReadAllButtons, +}; diff --git a/src/micetools/dll/drivers/meson.build b/src/micetools/dll/drivers/meson.build index 351d16f..c87f0cf 100644 --- a/src/micetools/dll/drivers/meson.build +++ b/src/micetools/dll/drivers/meson.build @@ -7,4 +7,8 @@ drivers_files = files( 'mxsram.c', 'mxsuperio.c', 'htsysmnt.c', + + 'jvs_boards/jvs_base.c', + 'jvs_boards/jvs_837_14572.c', + 'jvs_boards/jvs_837_14895.c', ) \ No newline at end of file diff --git a/src/micetools/dll/drivers/mxjvs.c b/src/micetools/dll/drivers/mxjvs.c index 6293d21..03ab91f 100644 --- a/src/micetools/dll/drivers/mxjvs.c +++ b/src/micetools/dll/drivers/mxjvs.c @@ -2,290 +2,22 @@ #include "../comdevice.h" #include "../common.h" +#include "../input.h" #include "../key_config.h" -#include "jvs.h" +#include "jvs_boards/jvs.h" #include "mx.h" -BOOL JVS_SENSE = false; -BOOL coin_solenoid = false; -BOOL test_btn = false; +#define JVS_SENSE (MiceConfig.keys.board_count == 0 ? FALSE : _MiceJvsBoards[0].m_SenseOut) -#define PLAYER_COUNT 2 -#define COIN_COUNTERS 2 -#define SWITCH_BYTES 2 - -// const char JVS_837_14572_ID[] = "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-14572 ;Ver1.00;98/10"; -const char JVS_837_14572_ID[] = "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"; - -typedef struct _jvs_board { - int num; - unsigned short coin_counts[COIN_COUNTERS]; - - unsigned char (*handler)(struct _jvs_board* board, unsigned char* inData, short inCount, - unsigned char* outData, unsigned char* outCount); - const char* id; - - unsigned char last_sysbuttons; - unsigned short last_buttons[PLAYER_COUNT]; -} jvs_board_t; -#define JVS_FLAG_NONE 0 -#define JVS_FLAG_NC 1 -#define JVS_FLAG_INVERT 2 - -jvs_board_t jvs_boards[JVS_IO_MAX] = { 0 }; - -short jvs_unpad(unsigned char* paddedData, short length, unsigned char* unpaddedData) { - short index = 0; - bool escape = false; - for (short i = 0; i < length; i++) { - if (escape) { - unpaddedData[index++] = paddedData[i] + 1; - escape = false; - } else if (paddedData[i] == JVS_MARK) - escape = true; - else - unpaddedData[index++] = paddedData[i]; +JVS_STATUS MiceJvsWrapper(PMICE_JVS this, DWORD nMaxRead) { + JVS_STATUS status = JVS_STATUS_OK; + // -1, because we don't want to treat the checksum as a command! + while (this->m_nInIdx < nMaxRead - 1) { + JVS_STATUS subStatus = this->vftable->transact(this, MiceJvsRead(this)); + if (subStatus > status) status = subStatus; } - return index; -} -short jvs_pad(unsigned char* unpaddedData, short length, unsigned char* paddedData, - short paddedMax) { - short index = 0; - for (short i = 0; i < length; i++) { - if (i > paddedMax) return -1; - - if (i != 0 && (unpaddedData[i] == JVS_MARK || unpaddedData[i] == JVS_SYNC)) { - paddedData[index++] = JVS_MARK; - paddedData[index++] = unpaddedData[i] - 1; - } else { - paddedData[index++] = unpaddedData[i]; - } - } - return index; -} - -void update_jvs_buttons(jvs_board_t* board) { - unsigned char sysButtons = 0x00; - if (GetAsyncKeyState(jvsKeybindings[board->num].test) < 0) sysButtons |= 0x80; - board->last_sysbuttons = sysButtons; - - for (int player = 0; player < PLAYER_COUNT; player++) { - unsigned short buttons = 0x0000; - for (int i = 0; i < SWITCH_BYTES * 8; i++) { - buttons <<= 1; - if (i < 14) { - int scancode = jvsKeybindings[board->num].buttons[(i + 1) * 2 + player]; - bool invert = jvsKeybindings[board->num].invert[(i + 1) * 2 + player]; - - if (scancode == 0) continue; - - if (invert) - buttons |= (GetAsyncKeyState(scancode) >= 0); // << i; - else - buttons |= (GetAsyncKeyState(scancode) < 0); // << i; - } - - // 0x80 == push 3 - - // buttons |= 1; - // buttons |= 0x80; - } - board->last_buttons[player] = buttons; - } -} - -unsigned char jvs_exchange(jvs_board_t* board, unsigned char* inData, short inCount, - unsigned char* response, unsigned char* outCount) { - short jvsIndex = 0; - unsigned char respIndex = 0; - -#define jvs_read(x) \ - if (jvsIndex >= inCount) { \ - return JVS_STATUS_OVERFLOW; \ - } else { \ - (x) = inData[jvsIndex++]; \ - } -#define jvs_write(x) response[respIndex++] = (x); - - static bool flipflop = false; - - while (jvsIndex < inCount) { - unsigned char cmd; - jvs_read(cmd); - - // log_info(plfMxJvs, "CMD: %02x", cmd); - - switch (cmd) { - case JVS_CMD_RESET: - unsigned char reset_assert; - jvs_read(reset_assert); - if (reset_assert != JVS_CMD_RESET_ASSERT) return JVS_STATUS_UKCOM; - - JVS_SENSE = true; - // Special case - *outCount = 0; - return JVS_STATUS_OK; - case JVS_CMD_CHANGE_COMMS: - // Special case - *outCount = 0; - return JVS_STATUS_OK; - - case JVS_CMD_ASSIGN_ADDR: - jvs_write(JVS_REPORT_OK); - JVS_SENSE = false; - // we don't bother remembering the address because at the moment we only simulate - // a single board in the JVS chain. - unsigned char _; - jvs_read(_); - break; - - case JVS_CMD_READ_ID: - jvs_write(JVS_REPORT_OK); - for (size_t i = 0; i < strlen(board->id); i++) jvs_write(board->id[i]); - break; - case JVS_CMD_GET_CMD_VERSION: - jvs_write(JVS_REPORT_OK); - jvs_write(JVS_VERSION_CMD); - break; - case JVS_CMD_GET_JVS_VERSION: - jvs_write(JVS_REPORT_OK); - jvs_write(JVS_VERSION_JVS); - break; - case JVS_CMD_GET_COMM_VERSION: - jvs_write(JVS_REPORT_OK); - jvs_write(JVS_VERSION_COMM); - break; - - case JVS_CMD_GET_FEATURES: - jvs_write(JVS_REPORT_OK); - - jvs_write(JVS_FEATURE_PLAYERS); - jvs_write(PLAYER_COUNT); - jvs_write(13); // bits per player - jvs_write(JVS_FEATURE_PAD); - jvs_write(JVS_FEATURE_COINS); - jvs_write(COIN_COUNTERS); - jvs_write(JVS_FEATURE_PAD); - jvs_write(JVS_FEATURE_PAD); - jvs_write(JVS_FEATURE_ANALOG); - jvs_write(8); // 8 ADC channels - jvs_write(JVS_FEATURE_PAD); // ?? (was "10" prior) - jvs_write(JVS_FEATURE_PAD); - jvs_write(JVS_FEATURE_GPIO); - jvs_write(6); // 6 ports - jvs_write(JVS_FEATURE_PAD); - jvs_write(JVS_FEATURE_PAD); - - jvs_write(JVS_FEATURE_EOF); - - break; - case JVS_CMD_RECEIVE_MAIN_ID: - unsigned char tempRead = 0xff; - while (tempRead != 0) jvs_read(tempRead); - // TODO: Do we need to report here? - break; - - case JVS_CMD_READ_SW: - unsigned char players; - unsigned char switch_bytes; - jvs_read(players); - jvs_read(switch_bytes); - if (players > PLAYER_COUNT || switch_bytes != 2) { - jvs_write(JVS_REPORT_PARAM_INVALID); - break; - } - update_jvs_buttons(board); - - jvs_write(JVS_REPORT_OK); - jvs_write(board->last_sysbuttons); - for (int player = 0; player < players; player++) { - for (int i = switch_bytes - 1; i >= 0; i--) { - jvs_write((board->last_buttons[player] >> (i * 8)) & 0xff); - } - } - break; - - case JVS_CMD_READ_COIN: - jvs_write(JVS_REPORT_OK); - unsigned char coin_count; - jvs_read(coin_count); - - int coin1 = jvsKeybindings[board->num].buttons[0]; - int coin2 = jvsKeybindings[board->num].buttons[1]; - - if (coin1 && GetAsyncKeyState(coin1) & 1) board->coin_counts[0]++; - if (coin2 && GetAsyncKeyState(coin2) & 1) board->coin_counts[1]++; - - for (unsigned char slot = 0; slot < coin_count; slot++) { - jvs_write(board->coin_counts[slot] >> 8); - jvs_write(board->coin_counts[slot] & 0xff); - } - break; - - case JVS_CMD_READ_ANALOGS: - jvs_write(JVS_REPORT_OK); - // TODO: Actually emulate these (super low priority) - unsigned char analog_count; - jvs_read(analog_count); - for (int i = analog_count; i > 0; i--) { - jvs_write(0xde); - jvs_write(0xad); - } - break; - - case JVS_CMD_WRITE_GPIO1: - jvs_write(JVS_REPORT_OK); - unsigned char gpio_bytes; - jvs_read(gpio_bytes); - for (int i = 0; i < gpio_bytes; i++) { - unsigned char gpio_value; - jvs_read(gpio_value); - - if (i == 0) { - if (!!(gpio_value & 0x80) != coin_solenoid) { - coin_solenoid = !!(gpio_value & 0x80); - log_info(plfMxJvs, "Coin solenoid: %s", - coin_solenoid ? "Locked" : "Unlocked"); - } - } - - // log_warning(plfMxJvs, "Unhandled GPIO write: *(%d) = %02x", i, gpio_value); - } - break; - case JVS_CMD_WRITE_GPIO2: - case JVS_CMD_WRITE_GPIO3: - jvs_write(JVS_REPORT_OK); - unsigned char gpioByteIndex; - unsigned char gpioByteData; - jvs_read(gpioByteIndex); - jvs_read(gpioByteData); - log_warning(plfMxJvs, "GPIO%d Unhandled: [%02x]=%02x", - (cmd - JVS_CMD_WRITE_GPIO2) + 2, gpioByteIndex, gpioByteData); - break; - - case JVS_CMD_COIN_DECREASE: - jvs_write(JVS_REPORT_OK); - - unsigned char slot; - jvs_read(slot); - unsigned char cAmount[2]; - jvs_read(cAmount[0]); - jvs_read(cAmount[1]); - unsigned short sAmount = cAmount[0] << 8 | cAmount[1]; - board->coin_counts[slot] -= sAmount; - break; - - default: - log_error(plfMxJvs, "Unknown command: 0x%02x", cmd); - return JVS_STATUS_UKCOM; - } - } -#undef jvs_read -#undef jvs_write - - *outCount = respIndex; - return JVS_STATUS_OK; + return status; } void jvs_send_status(unsigned char status, unsigned char* outData, LPDWORD outCount) { @@ -306,78 +38,116 @@ void jvs_send_status(unsigned char status, unsigned char* outData, LPDWORD outCo } } -void mxjvs_handle(unsigned char* paddedIn, short inCount, unsigned char* outData, short maxOut, - LPDWORD outCount) { - *outCount = 0; - - unsigned char* inData = malloc(inCount); - inCount = jvs_unpad(paddedIn, inCount, inData); +BYTE jvsInUnmasked[MICE_JVS_MAX]; +BYTE jvsOutUnmasked[MICE_JVS_MAX]; +BYTE jvsOutMasked[MICE_JVS_MASKED]; +void mxjvs_handle(unsigned char* lpMaskedIn, short nMaskedCount, unsigned char* outData, + short maxOut, LPDWORD outCount) { + SHORT nIn = 0; + BOOL wasMark = FALSE; + for (int i = 0; i < nMaskedCount; i++) { + if (lpMaskedIn[i] == JVS_MARK) { + wasMark = TRUE; + continue; + } + if (wasMark) + jvsInUnmasked[nIn++] = lpMaskedIn[i] + 1; + else + jvsInUnmasked[nIn++] = lpMaskedIn[i]; + } + PJVS_PACKET lpPacket = (PJVS_PACKET)jvsInUnmasked; // JVS frame is 4 bytes in total - if (inCount < 4) { - log_error(plfMxJvs, "inCount impossibly small: %d", inCount); + if (nIn < 4) { + log_error(plfMxJvs, "inCount impossibly small: %d", nIn); jvs_send_status(JVS_STATUS_UNKNOWN, outData, outCount); - free(inData); return; } - // This isn't a JVS packet - if (inData[0] != JVS_SYNC) { - log_error(plfMxJvs, "SYNC missing. Saw 0x%02x", inData[0]); + // This isn't a JVS packet. We should scan for SYNC, but I don't think we'll ever need to + if (lpPacket->m_Sync != JVS_SYNC) { + log_error(plfMxJvs, "SYNC missing. Saw 0x%02x", lpPacket->m_Sync); jvs_send_status(JVS_STATUS_UNKNOWN, outData, outCount); - free(inData); return; } // Validate the checksum before proceeding unsigned char sum = 0; bool escape = false; - for (int i = 1; i < inCount - 1; i++) sum += inData[i]; - if (sum != inData[inCount - 1]) { + for (int i = 1; i < nIn - 1; i++) sum += jvsInUnmasked[i]; + if (sum != jvsInUnmasked[nIn - 1]) { log_error(plfMxJvs, "Checksum failed. Computed 0x%02x, expected 0x%02x", sum, - inData[inCount - 1]); + jvsInUnmasked[nIn - 1]); jvs_send_status(JVS_STATUS_SUM, outData, outCount); return; } - unsigned char destination = inData[1]; - // Not broadcast, not master-bound, and within bounds - if (destination != 0xff && (destination == 0 || destination > MiceConfig.keys.board_count)) - return; + DWORD nBoardCount = MiceConfig.keys.board_count; + if (nBoardCount > _countof(_MiceJvsBoards)) nBoardCount = _countof(_MiceJvsBoards); - unsigned char* response = malloc(maxOut); - unsigned char packetSize = 0; - unsigned char status = JVS_STATUS_UNKNOWN; - if (destination == 0xff) { - for (int i = 0; i < MiceConfig.keys.board_count; i++) { - jvs_board_t* board = &jvs_boards[i]; - status = board->handler(board, inData + 3, inData[2] - 1, response + 4, &packetSize); - if (packetSize != 0) break; + // Not broadcast, not master-bound, and within bounds + if (lpPacket->m_Dst != 0xff && (lpPacket->m_Dst == 0 || lpPacket->m_Dst > nBoardCount)) { + log_warning(plfMxJvs, "JVS bound for %02x ignored", lpPacket->m_Dst); + *outCount = 0; + return; + } + + JVS_STATUS status = JVS_STATUS_UNKNOWN; + DWORD nBytesWrote = 0; + if (lpPacket->m_Dst == 0xff) { + for (DWORD i = 0; i < nBoardCount; i++) { + PMICE_JVS lpMiceJvs = &_MiceJvsBoards[i]; + + lpMiceJvs->m_lpInData = jvsInUnmasked; + lpMiceJvs->m_lpOutData = jvsOutUnmasked; + lpMiceJvs->m_nInIdx = 3; + lpMiceJvs->m_nOutIdx = 4; + + status = MiceJvsWrapper(lpMiceJvs, nIn); + if (lpMiceJvs->m_nOutIdx > 4) { + nBytesWrote = lpMiceJvs->m_nOutIdx - 4; + break; + } } } else { - jvs_board_t* board = &jvs_boards[MiceConfig.keys.board_count - destination]; - status = board->handler(board, inData + 3, inData[2] - 1, response + 4, &packetSize); + PMICE_JVS lpMiceJvs = &_MiceJvsBoards[nBoardCount - lpPacket->m_Dst]; + + lpMiceJvs->m_lpInData = jvsInUnmasked; + lpMiceJvs->m_lpOutData = jvsOutUnmasked; + lpMiceJvs->m_nInIdx = 3; + lpMiceJvs->m_nOutIdx = 4; + + status = MiceJvsWrapper(lpMiceJvs, nIn); + nBytesWrote = lpMiceJvs->m_nOutIdx - 4; + } + + if (status == JVS_STATUS_SILENCE) { + *outCount = 0; + return; } - free(inData); if (status == JVS_STATUS_OK) { - if (packetSize == 0) return; - - response[0] = JVS_SYNC; - response[1] = JVS_NODE_MASTER; - response[2] = packetSize + 2; - response[3] = status; + ((PJVS_PACKET)&jvsOutUnmasked)->m_Dst = JVS_NODE_MASTER; + ((PJVS_PACKET)&jvsOutUnmasked)->m_nBytes = (nBytesWrote + 2) & 0xff; + jvsOutUnmasked[3] = status; sum = 0; - for (int i = 1; i < packetSize + 4; i++) sum += response[i]; - response[packetSize + 4] = sum; + for (DWORD i = 1; i < nBytesWrote + 4; i++) sum += jvsOutUnmasked[i]; + jvsOutUnmasked[nBytesWrote + 4] = sum; - short paddedLength = jvs_pad(response, packetSize + 5, outData, maxOut); - *outCount = (paddedLength == -1) ? 0 : paddedLength; + DWORD nPadded = 1; + jvsOutMasked[0] = JVS_SYNC; + for (DWORD i = 1; i <= nBytesWrote + 4; i++) { + if (i == JVS_MARK || i == JVS_SYNC) { + jvsOutMasked[nPadded++] = JVS_MARK; + jvsOutMasked[nPadded++] = jvsOutUnmasked[i] - 1; + } else + jvsOutMasked[nPadded++] = jvsOutUnmasked[i]; + } - free(response); + *outCount = nPadded; + memcpy_s(outData, maxOut, jvsOutMasked, nPadded); } else { - log_error(plfMxJvs, "JVS board %d returned status %d", destination, status); - free(response); + log_error(plfMxJvs, "JVS board %d returned status %d", lpPacket->m_Dst, status); jvs_send_status(status, outData, outCount); } } @@ -410,7 +180,7 @@ 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; + *lpModelStat = JVS_SENSE ? 0 : MS_DSR_ON; return TRUE; } @@ -423,10 +193,12 @@ BOOL mxjvs_SetCommState(void* com, LPDCB lpDCB) { } void init_jvs_boards() { - for (int i = 0; i < _countof(jvs_boards); i++) { - jvs_boards[i].num = i; - jvs_boards[i].id = JVS_837_14572_ID; - jvs_boards[i].handler = &jvs_exchange; + // new (MiceJvs837_14895, _MiceJvsBoards[0]); + new (MiceJvs837_14572, _MiceJvsBoards[0]); + + // Init all the others + for (int i = 1; i < _countof(_MiceJvsBoards); i++) { + new (MiceJvs837_14572, _MiceJvsBoards[i]); } } @@ -455,11 +227,16 @@ DWORD __stdcall mxjvs_comdev_thread(com_device_t* com) { short packetSize = (bufferPtr - inBuffer) & 0xFFFF; mxjvs_handle(inBuffer, packetSize, outBuffer, _countof(outBuffer), &bytesReturned); - com->modemStatus = !JVS_SENSE ? MS_DSR_ON : 0; + com->modemStatus = JVS_SENSE ? 0 : MS_DSR_ON; comdev_write(com, outBuffer, bytesReturned & 0xFFFF); } } +VIRTUAL_SERIAL_DEVICE jvsSerialDevice = { + .m_Name = "JVS", + .m_Entrypoint = mxjvs_comdev_thread, +}; + void setup_mxjvs() { init_jvs_boards(); @@ -479,6 +256,5 @@ void setup_mxjvs() { mxjvs->com_hook = jvscom; - // TODO: Looks like some things might use JVS on COM4. We should setup a comdevice for this! - attach_com_device(4, mxjvs_comdev_thread); + attach_com_device(4, &jvsSerialDevice); } diff --git a/src/micetools/dll/drivers/mxparallel.c b/src/micetools/dll/drivers/mxparallel.c index 3041716..4ea7c0f 100644 --- a/src/micetools/dll/drivers/mxparallel.c +++ b/src/micetools/dll/drivers/mxparallel.c @@ -167,19 +167,19 @@ void init_nv_storage() { void dump_nv_storage() { FILE* fp; - if (fopen_s(&fp, "dev/kc/nvram.bin", "wb")) { + if (fopen_s(&fp, MiceIpcRelativePath("keychip\\nvram.bin"), "wb")) { log_error(plfMxParallel, "Failed to open nvram.bin"); return; } fwrite(nvram, 1, sizeof nvram, fp); fclose(fp); - if (fopen_s(&fp, "dev/kc/eeprom.bin", "wb")) { + if (fopen_s(&fp, MiceIpcRelativePath("keychip\\eeprom.bin"), "wb")) { log_error(plfMxParallel, "Failed to open eeprom.bin"); return; } fwrite(eeprom, 1, sizeof eeprom, fp); fclose(fp); - if (fopen_s(&fp, "dev/kc/flash.bin", "wb")) { + if (fopen_s(&fp, MiceIpcRelativePath("keychip\\flash.bin"), "wb")) { log_error(plfMxParallel, "Failed to open flash.bin"); return; } diff --git a/src/micetools/dll/drivers/mxsmbus.c b/src/micetools/dll/drivers/mxsmbus.c index b1ce05b..8b6827d 100644 --- a/src/micetools/dll/drivers/mxsmbus.c +++ b/src/micetools/dll/drivers/mxsmbus.c @@ -1,5 +1,5 @@ #include "../hooks/setupapi_.h" -#include "../lib/dmi/dmi.h" +#include "../../lib/mice/dmi.h" #include "mx.h" #include "smbus.h" diff --git a/src/micetools/dll/drivers/mxsram.c b/src/micetools/dll/drivers/mxsram.c index 8542045..c96a02e 100644 --- a/src/micetools/dll/drivers/mxsram.c +++ b/src/micetools/dll/drivers/mxsram.c @@ -2,8 +2,8 @@ #include "../../maiBackupStructs.h" #include "mx.h" -#define SRAM_PATH L"dev/sram.bin" -#define SRAM_SIZE 1024 * 2084 +// The size of RingEdge[2], which is larger than RingWide needs +#define SRAM_SIZE_MAX 1024 * 2084 LPBYTE SRAM_DATA = NULL; HANDLE SRAM_FILE = INVALID_HANDLE_VALUE; HANDLE SRAM_FILE_MAPPING = INVALID_HANDLE_VALUE; @@ -20,9 +20,9 @@ HANDLE SRAM_FILE_MAPPING = INVALID_HANDLE_VALUE; } while (0) void build_sram() { - log_info(plfMxSram, "Building default SRAM file"); + log_warning(plfMxSram, "Building default SRAM file"); - memset(SRAM_DATA, 0xff, SRAM_SIZE); + memset(SRAM_DATA, 0xff, SRAM_SIZE_MAX); // TODO: Do we want to re-enable this with actual values #if false @@ -52,12 +52,12 @@ void build_sram() { void ensure_valid_sram() { if (!SRAM_DATA) { - BOOL isNew = !FileExists(SRAM_PATH); + BOOL isNew = !FileExistsA(SRAM_PATH); - SRAM_DATA = open_mapped_file(SRAM_PATH, SRAM_SIZE, &SRAM_FILE, &SRAM_FILE_MAPPING); + SRAM_DATA = open_mapped_file(SRAM_PATH, SRAM_SIZE_MAX, &SRAM_FILE, &SRAM_FILE_MAPPING); if (SRAM_DATA == NULL) { log_error(plfMxSram, "SRAM will be memory-backed and not syncronised!"); - SRAM_DATA = malloc(SRAM_SIZE); + SRAM_DATA = malloc(SRAM_SIZE_MAX); } if (isNew) build_sram(); @@ -68,7 +68,7 @@ BOOL WINAPI mxsram_DeviceIoControl(file_context_t* ctx, DWORD dwIoControlCode, L DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { DWORD SRAM_VERSION = 0x01000001; - DWORD SRAM_SECTOR_SIZE = 4; // Max is 0x800 + DWORD SRAM_SECTOR_SIZE = MiceConfig.sysconf.platform == MICE_PLATFORM_RINGEDGE2 ? 4 : 512; switch (dwIoControlCode) { case IOCTL_MXSRAM_PING: // Get version @@ -88,6 +88,8 @@ BOOL WINAPI mxsram_DeviceIoControl(file_context_t* ctx, DWORD dwIoControlCode, L PDISK_GEOMETRY out = (PDISK_GEOMETRY)lpOutBuffer; memset(out, 0, sizeof *out); + // I'm unsure how these values change specifically for RW, RE1, RE2 + // I'm also unsure (ie highly doubtful) it truly matters. out->Cylinders.QuadPart = 256; out->MediaType = FixedMedia; out->TracksPerCylinder = 2; @@ -119,8 +121,8 @@ BOOL mxsram_WriteFile(file_context_t* ctx, LPCVOID lpBuffer, DWORD nNumberOfByte LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) { log_misc(plfMxSram, "sram write 0x%04x bytes at 0x%04x", nNumberOfBytesToWrite, ctx->m_Pointer.LowPart); - if (ctx->m_Pointer.LowPart + nNumberOfBytesToWrite >= SRAM_SIZE) { - nNumberOfBytesToWrite = SRAM_SIZE - ctx->m_Pointer.LowPart; + if (ctx->m_Pointer.LowPart + nNumberOfBytesToWrite >= SRAM_SIZE_MAX) { + nNumberOfBytesToWrite = SRAM_SIZE_MAX - ctx->m_Pointer.LowPart; } ensure_valid_sram(); memcpy(SRAM_DATA + ctx->m_Pointer.LowPart, lpBuffer, nNumberOfBytesToWrite); @@ -132,8 +134,8 @@ BOOL mxsram_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesT LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) { log_misc(plfMxSram, "sram read 0x%04x bytes at 0x%04x", nNumberOfBytesToRead, ctx->m_Pointer.LowPart); - if (ctx->m_Pointer.LowPart + nNumberOfBytesToRead >= SRAM_SIZE) { - nNumberOfBytesToRead = SRAM_SIZE - ctx->m_Pointer.LowPart; + if (ctx->m_Pointer.LowPart + nNumberOfBytesToRead >= SRAM_SIZE_MAX) { + nNumberOfBytesToRead = SRAM_SIZE_MAX - ctx->m_Pointer.LowPart; } ensure_valid_sram(); memcpy((LPVOID)lpBuffer, SRAM_DATA + ctx->m_Pointer.LowPart, nNumberOfBytesToRead); diff --git a/src/micetools/dll/games.c b/src/micetools/dll/games.c index 73dacf1..36ff92a 100644 --- a/src/micetools/dll/games.c +++ b/src/micetools/dll/games.c @@ -1,14 +1,166 @@ #include "common.h" #include "devices/_devices.h" +#include "drivers/jvs_boards/jvs.h" +#include "input.h" #include "key_config.h" #define IS_GAME(match) (strncmp(game_id, #match, 4) == 0) -void mice_got_game_id(char game_id[4]) { - static char last_game_id[4] = { '_', '_', '_', '_' }; +MICE_BUTTON_BINDING maimaiDefaultBindings[31] = { + // Coins + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = '1' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = '2' }, + // Test + { .m_Type = MICE_BB_TYPE_UNBOUND }, + // 1P + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'E' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'W' }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'D' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'C' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'X' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'Z' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'A' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = 'Q' }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + // 2P + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD9 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD8 }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD6 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD3 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD2 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD1 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD4 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = TRUE, .m_AsyncKey.m_Key = VK_NUMPAD7 }, + { .m_Type = MICE_BB_TYPE_UNBOUND }, +}; +MICE_BUTTON_BINDING apmDefaultBindings[31] = { + // Coins + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_F1 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_F2 }, + // Test + { .m_Type = MICE_BB_TYPE_UNBOUND }, + // 1P + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_RETURN }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = '*' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_UP }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_DOWN }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_LEFT }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_RIGHT }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, + .m_Invert = FALSE, + .m_AsyncKey.m_Key = '1' }, // 1P Light + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, + .m_Invert = FALSE, + .m_AsyncKey.m_Key = '2' }, // 1P Middle + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, + .m_Invert = FALSE, + .m_AsyncKey.m_Key = '3' }, // 1P Heavy + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = '4' }, // 1P Exs + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = '5' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = '6' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, + .m_Invert = FALSE, + .m_AsyncKey.m_Key = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, + .m_Invert = FALSE, + .m_AsyncKey.m_Key = MICE_BB_TYPE_UNBOUND }, - if (memcmp(game_id, last_game_id, 4) == 0) return; - memcpy(last_game_id, game_id, 4); + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_BACK }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = '-' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_NUMPAD8 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_NUMPAD2 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_NUMPAD4 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = VK_NUMPAD6 }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = 'Q' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = 'W' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = 'E' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = 'R' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = 'T' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, .m_Invert = FALSE, .m_AsyncKey.m_Key = 'Y' }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, + .m_Invert = FALSE, + .m_AsyncKey.m_Key = MICE_BB_TYPE_UNBOUND }, + { .m_Type = MICE_BB_TYPE_GET_ASYNC_KEY, + .m_Invert = FALSE, + .m_AsyncKey.m_Key = MICE_BB_TYPE_UNBOUND }, +}; + +void _MiceSetupMaimai() { + MiceConfig.devices.com1 = ""; + MiceConfig.devices.com2 = "aime_tn32msec"; + MiceConfig.devices.com3 = "maitouch"; + MiceConfig.devices.com5 = ""; + MiceConfig.devices.com6 = "mailed"; + MiceConfig.devices.com7 = ""; + MiceConfig.devices.com8 = "mailed"; + + // Maimai is going to keep checking COM5 and COM7; we don't care. + MiceConfig.devices.add_idlers = false; + + MiceConfig.sysconf.dipsw = 0b01111000; + MiceConfig.window.nosize = true; + + if (MiceConfig.keys.board_count == 0) MiceConfig.keys.board_count = 1; + + free(_MiceJvsBoards[0].m_Bindings); + _MiceJvsBoards[0].m_Bindings = malloc(sizeof maimaiDefaultBindings); + memcpy(_MiceJvsBoards[0].m_Bindings, maimaiDefaultBindings, sizeof maimaiDefaultBindings); + _MiceJvsBoards[0].m_NumButtons = _countof(maimaiDefaultBindings); + _MiceJvsBoards[0].m_Coins = 2; + _MiceJvsBoards[0].m_Players = 2; + _MiceJvsBoards[0].m_ButtonsPerPlayer = 14; + free(_MiceJvsBoards[0].m_ButtonStates); + _MiceJvsBoards[0].m_ButtonStates = + malloc(_countof(maimaiDefaultBindings) * sizeof *_MiceJvsBoards[0].m_ButtonStates); + + // Maimai is going to check if these exist. They do, now. + make_dirs(MiceIpcRelativePath("dev\\w\\maimai")); + make_dirs(MiceIpcRelativePath("dev\\v\\maimai")); + make_dirs(MiceIpcRelativePath("dev\\y\\maimai_deliver\\maimai")); +} +void _MiceSetupApm() { + MiceConfig.devices.com1 = ""; + MiceConfig.devices.com2 = ""; + MiceConfig.devices.com3 = ""; + MiceConfig.devices.com5 = ""; + MiceConfig.devices.com6 = ""; + MiceConfig.devices.com7 = ""; + MiceConfig.devices.com8 = ""; + + MiceConfig.sysconf.dipsw = 0b01000000; // 720p + + if (MiceConfig.keys.board_count == 0) MiceConfig.keys.board_count = 1; + + free(_MiceJvsBoards[0].m_Bindings); + _MiceJvsBoards[0].m_Bindings = malloc(sizeof apmDefaultBindings); + memcpy(_MiceJvsBoards[0].m_Bindings, apmDefaultBindings, sizeof apmDefaultBindings); + _MiceJvsBoards[0].m_NumButtons = _countof(apmDefaultBindings); + _MiceJvsBoards[0].m_Coins = 2; + _MiceJvsBoards[0].m_Players = 2; + _MiceJvsBoards[0].m_ButtonsPerPlayer = 14; + free(_MiceJvsBoards[0].m_ButtonStates); + _MiceJvsBoards[0].m_ButtonStates = + malloc(_countof(apmDefaultBindings) * sizeof *_MiceJvsBoards[0].m_ButtonStates); +} + +void _MiceGotGameId(char game_id[4]) { + static bool gotGameId = false; + if (gotGameId) return; + if (game_id[0] != 'S') return; + if (game_id[1] < 'A' || game_id[1] > 'Z') return; + if (game_id[2] < 'A' || game_id[2] > 'Z') return; + if (game_id[3] < 'A' || game_id[3] > 'Z') return; + gotGameId = true; if (!MiceConfig.devices.do_auto) return; @@ -18,79 +170,47 @@ void mice_got_game_id(char game_id[4]) { IS_GAME(SDDZ /* maimai MiLK [PLUS] */) || IS_GAME(SDEY /* maimai FiNALE */) || IS_GAME(SBYC /* maimai china */)) { log_info(plfBoot, "Detect game MaiMai"); - - MiceConfig.devices.com1 = ""; - MiceConfig.devices.com2 = "aime_tn32msec"; - MiceConfig.devices.com3 = "maitouch"; - MiceConfig.devices.com5 = ""; - MiceConfig.devices.com6 = "mailed"; - MiceConfig.devices.com7 = ""; - MiceConfig.devices.com8 = "mailed"; - - if (MiceConfig.keys.board_count == 0) MiceConfig.keys.board_count = 1; - - if (!jvsKeybindings[0].notdefault) { - // Zero and invert all buttons - memset(&jvsKeybindings[0].buttons[3 * 2], 0, 12 * 2); - memset(&jvsKeybindings[0].invert[3 * 2], 1, 12 * 2); - - // Setup default keybinds - jvsKeybindings[0].buttons[6 * 2] = 'A'; // 1P1 - jvsKeybindings[0].buttons[5 * 2] = 'Q'; // 1P2 - jvsKeybindings[0].buttons[8 * 2] = 'W'; // 1P3 - jvsKeybindings[0].buttons[9 * 2] = 'E'; // 1P4 - jvsKeybindings[0].buttons[10 * 2] = 'D'; // 1P5 - jvsKeybindings[0].buttons[11 * 2] = 'C'; // 1P6 - jvsKeybindings[0].buttons[12 * 2] = 'X'; // 1P7 - jvsKeybindings[0].buttons[13 * 2] = 'Z'; // 1P8 - - jvsKeybindings[0].buttons[6 * 2 + 1] = VK_NUMPAD4; // 2P1 - jvsKeybindings[0].buttons[5 * 2 + 1] = VK_NUMPAD7; // 2P2 - jvsKeybindings[0].buttons[8 * 2 + 1] = VK_NUMPAD8; // 2P3 - jvsKeybindings[0].buttons[9 * 2 + 1] = VK_NUMPAD9; // 2P4 - jvsKeybindings[0].buttons[10 * 2 + 1] = VK_NUMPAD6; // 2P5 - jvsKeybindings[0].buttons[11 * 2 + 1] = VK_NUMPAD3; // 2P6 - jvsKeybindings[0].buttons[12 * 2 + 1] = VK_NUMPAD2; // 2P7 - jvsKeybindings[0].buttons[13 * 2 + 1] = VK_NUMPAD1; // 2P8 - } - } else if (IS_GAME(SBYG /* APM2 */)) { + _MiceSetupMaimai(); + } else if (IS_GAME(SBYG /* APM2 */) || IS_GAME(SDCM /* UNIB */)) { log_info(plfBoot, "Detect game APM2"); - MiceConfig.devices.com1 = ""; - MiceConfig.devices.com2 = ""; - MiceConfig.devices.com3 = ""; - MiceConfig.devices.com5 = ""; - MiceConfig.devices.com6 = ""; - MiceConfig.devices.com7 = ""; - MiceConfig.devices.com8 = ""; + _MiceSetupApm(); - if (MiceConfig.keys.board_count == 0) MiceConfig.keys.board_count = 1; + // MiceConfig.devices.com1 = ""; + // MiceConfig.devices.com2 = ""; + // MiceConfig.devices.com3 = ""; + // MiceConfig.devices.com5 = ""; + // MiceConfig.devices.com6 = ""; + // MiceConfig.devices.com7 = ""; + // MiceConfig.devices.com8 = ""; - if (!jvsKeybindings[0].notdefault) { - puts("!!"); + // if (MiceConfig.keys.board_count == 0) MiceConfig.keys.board_count = 1; - // Zero all buttons - memset(&jvsKeybindings[0].buttons[3 * 2], 0, 12 * 2); - memset(&jvsKeybindings[0].invert[3 * 2], 0, 12 * 2); + // if (!jvsKeybindings[0].notdefault) { + // puts("!!"); - jvsKeybindings[0].buttons[0 * 2] = VK_BACK; - jvsKeybindings[0].buttons[1 * 2] = VK_RETURN; + // // Zero all buttons + // memset(&jvsKeybindings[0].buttons[3 * 2], 0, 12 * 2); + // memset(&jvsKeybindings[0].invert[3 * 2], 0, 12 * 2); - jvsKeybindings[0].buttons[3 * 2] = VK_UP; - jvsKeybindings[0].buttons[4 * 2] = VK_DOWN; - jvsKeybindings[0].buttons[5 * 2] = VK_LEFT; - jvsKeybindings[0].buttons[6 * 2] = VK_RIGHT; + // jvsKeybindings[0].buttons[0 * 2] = VK_BACK; + // jvsKeybindings[0].buttons[1 * 2] = VK_RETURN; - jvsKeybindings[0].buttons[7 * 2] = 'Q'; - jvsKeybindings[0].buttons[8 * 2] = 'W'; - jvsKeybindings[0].buttons[9 * 2] = 'E'; - jvsKeybindings[0].buttons[10 * 2] = 'A'; - jvsKeybindings[0].buttons[11 * 2] = 'S'; - jvsKeybindings[0].buttons[12 * 2] = 'D'; + // jvsKeybindings[0].buttons[3 * 2] = VK_UP; + // jvsKeybindings[0].buttons[4 * 2] = VK_DOWN; + // jvsKeybindings[0].buttons[5 * 2] = VK_LEFT; + // jvsKeybindings[0].buttons[6 * 2] = VK_RIGHT; - // 2P is unbound by default, as a (nice) keymap with both - // will probably also involve changing 1P too. - } + // jvsKeybindings[0].buttons[7 * 2] = 'Q'; + // jvsKeybindings[0].buttons[8 * 2] = 'W'; + // jvsKeybindings[0].buttons[9 * 2] = 'E'; + // jvsKeybindings[0].buttons[10 * 2] = 'A'; + // jvsKeybindings[0].buttons[11 * 2] = 'S'; + // jvsKeybindings[0].buttons[12 * 2] = 'D'; + + // // 2P is unbound by default, as a (nice) keymap with both + // // will probably also involve changing 1P too. + // } } else if (IS_GAME(SBVH /* Sega & Sonic All-Stars Racing Arcade */)) { log_info(plfBoot, "Detect game Sega & Sonic All-Stars Racing Arcade"); @@ -101,6 +221,15 @@ void mice_got_game_id(char game_id[4]) { MiceConfig.devices.com6 = ""; MiceConfig.devices.com7 = ""; MiceConfig.devices.com8 = ""; + } else if (IS_GAME(SBWU)) { + log_info(plfBoot, "Detect game Gacchu Guts"); + MiceConfig.devices.com1 = "gacchu_guts_card"; + MiceConfig.devices.com2 = "gacchu_guts_screen"; + MiceConfig.devices.com3 = "gacchu_guts_screen"; + MiceConfig.devices.com5 = ""; + MiceConfig.devices.com6 = ""; + MiceConfig.devices.com7 = ""; + MiceConfig.devices.com8 = ""; } else { if (game_id[0] != '\0') { log_warning(plfBoot, "Unknown game ID: %.4s", game_id); @@ -109,6 +238,8 @@ void mice_got_game_id(char game_id[4]) { return; } - save_current_config(); + MiceConfig.devices.do_auto = false; + + save_current_config(true); start_devices(); } \ No newline at end of file diff --git a/src/micetools/dll/gui/gui.c b/src/micetools/dll/gui/gui.c index b6a6c02..cd7608e 100644 --- a/src/micetools/dll/gui/gui.c +++ b/src/micetools/dll/gui/gui.c @@ -3,6 +3,7 @@ #define CIMGUI_DEFINE_ENUMS_AND_STRUCTS #endif #include "../devices/smb_at24c64an.h" +#include "../drivers/jvs_boards/jvs.h" #include "../key_config.h" #include "cimgui.h" #include "imgui/backends/GL/freeglut.h" @@ -15,15 +16,20 @@ const ImVec4 DISABLED_COL = { .7f, .7f, .7f, 1.0f }; const ImVec4 WARN_COL = { 1.0f, 1.0f, .1f, 1.0f }; const ImVec4 DANGER_COL = { 1.0f, .1f, .1f, 1.0f }; -struct { +typedef struct JVS_BUTTON_NAME { const char* name; int index; int pinNum; -} JVS_BUTTON_NAMES[JVS_BUTTON_PAIR_COUNT] = { +} JVS_BUTTON_NAME, *PJVS_BUTTON_NAME; +JVS_BUTTON_NAME JVS_BUTTON_NAMES[JVS_BUTTON_PAIR_COUNT] = { { "Coin", 45 }, { "Start", 17 }, { "Service", 41 }, { "UP", 23 }, { "DOWN", 25 }, { "LEFT", 21 }, { "RIGHT", 19 }, { "Push 1", 27 }, { "Push 2", 29 }, { "Push 3", 31 }, { "Push 4", 33 }, { "Push 5", 35 }, { "Push 6", 37 }, { "Push 7", 39 }, { "Push 8", 47 }, }; +JVS_BUTTON_NAME JVS_BUTTON_NAMES_MAIMAI[14] = { + { "Coin" }, { "Button 1" }, { "Button 2" }, { "Button 3" }, { "Button 4" }, + { "Button 5" }, { "Button 6" }, { "Button 7" }, { "Button 8" }, +}; const char* RESOLUTION_NAMES[] = { "Unspecified", "640x480", "1024x600", "1024x768", @@ -176,11 +182,10 @@ void hud_eeprom(ImGuiKey open_key) { if (igIsKeyPressed_Bool(open_key, false)) showEeprom = !showEeprom; // TODO: Less hacky :) - extern BYTE EEPROM_DATA[0x2000]; + extern LPBYTE EEPROM_DATA; editor.Open = showEeprom; - if (showEeprom) - MemoryEditor_DrawWindow(&editor, "EEPROM Editor", EEPROM_DATA, sizeof EEPROM_DATA, 0x000); + if (showEeprom) MemoryEditor_DrawWindow(&editor, "EEPROM Editor", EEPROM_DATA, 0x2000, 0x000); } void hud_sram(ImGuiKey open_key) { static MemoryEditor editor; @@ -239,7 +244,7 @@ bool AddSettingBool(const char* name, const char* help, bool* value) { igNextColumn(); igPopID(); - if (changed) save_current_config(); + if (changed) save_current_config(false); return changed; } @@ -257,7 +262,7 @@ bool AddSettingString(const char* name, const char* help, char** value) { int newLen = strlen(buffer) + 1; *value = realloc(*value, newLen); memcpy(*value, buffer, newLen); - save_current_config(); + save_current_config(false); } igNextColumn(); @@ -283,7 +288,7 @@ bool AddSettingIPv4(const char* name, const char* help, unsigned int* value) { if (sscanf(buffer, "%hhu.%hhu.%hhu.%hhu%n", &a, &b, &c, &d, &n) == 4 && n == strlen(buffer)) { *value = (a << 24) | (b << 16) | (c << 8) | d; - save_current_config(); + save_current_config(false); } else { changed = false; } @@ -349,6 +354,8 @@ bool igDipsw(const char* label, bool* value) { return pressed; } +#define SERIAL_HELP \ + "This value was automatically generated for you and uniquely identifies your computer" void tab_settings_system() { igColumns(2, "SettingsSystem", true); igTextUnformatted("Name", NULL); @@ -374,7 +381,7 @@ void tab_settings_system() { if (igRadioButton_Bool("##JPN", MiceConfig.sysconf.region == 1)) { MiceConfig.sysconf.region = 1; staticChanged = true; - save_current_config(); + save_current_config(false); } igSameLine(0.0, -1.0); igSeparatorEx(ImGuiSeparatorFlags_Vertical); @@ -384,7 +391,7 @@ void tab_settings_system() { if (igRadioButton_Bool("##USA", MiceConfig.sysconf.region == 2)) { MiceConfig.sysconf.region = 2; staticChanged = true; - save_current_config(); + save_current_config(false); } igSameLine(0.0, -1.0); igSeparatorEx(ImGuiSeparatorFlags_Vertical); @@ -394,7 +401,7 @@ void tab_settings_system() { if (igRadioButton_Bool("##EXP", MiceConfig.sysconf.region == 4)) { MiceConfig.sysconf.region = 4; staticChanged = true; - save_current_config(); + save_current_config(false); } igSameLine(0.0, -1.0); igSeparatorEx(ImGuiSeparatorFlags_Vertical); @@ -404,16 +411,29 @@ void tab_settings_system() { if (igRadioButton_Bool("##", MiceConfig.sysconf.region == 8)) { MiceConfig.sysconf.region = 8; staticChanged = true; - save_current_config(); + save_current_config(false); } igNextColumn(); igPopID(); } staticChanged |= AddSettingBool("Rental", NULL, &MiceConfig.sysconf.rental); - staticChanged |= - AddSettingString("Serial", "Main board serial number", &MiceConfig.sysconf.serial); - if (staticChanged) build_eeprom(); + + // The 'M' prefix isn't technically foolproof, but it's good enough for a simple help message + staticChanged |= AddSettingString( + "PCB serial number", + (MiceConfig.sysconf.serial && MiceConfig.sysconf.serial[0] == 'M' ? SERIAL_HELP : NULL), + &MiceConfig.sysconf.serial); + if (staticChanged) { + build_eeprom(); + save_current_config(false); + } + if (AddSettingString( + "Keychip serial number", + (MiceConfig.sysconf.keyid && MiceConfig.sysconf.keyid[0] == 'M' ? SERIAL_HELP : NULL), + &MiceConfig.sysconf.keyid)) { + save_current_config(false); + } igSeparator(); @@ -428,7 +448,7 @@ void tab_settings_system() { vec0)) { MiceConfig.sysconf.dipsw = ((MiceConfig.sysconf.dipsw & 0b1'000'1111) | (i << 4)) & 0xff; - save_current_config(); + save_current_config(false); } if (is_selected) igSetItemDefaultFocus(); } @@ -445,7 +465,7 @@ void tab_settings_system() { vec0)) { MiceConfig.sysconf.dipsw = ((MiceConfig.sysconf.dipsw & 0b1111'0'111) | (i << 3)) & 0xff; - save_current_config(); + save_current_config(false); } if (is_selected) igSetItemDefaultFocus(); } @@ -461,7 +481,7 @@ void tab_settings_system() { if (i != 0) igSameLine(0.0, 2); if (igDipsw("", &val)) { MiceConfig.sysconf.dipsw = (MiceConfig.sysconf.dipsw & ~(1 << i)) | (val << i); - save_current_config(); + save_current_config(false); } igPopID(); } @@ -503,7 +523,7 @@ void tab_settings_window() { igEndDisabled(); igEndDisabled(); - if (changed) save_current_config(); + if (changed) save_current_config(false); igEndColumns(); } @@ -518,6 +538,11 @@ void tab_settings_network() { igSeparator(); bool networkChanged = false; + networkChanged |= + AddSettingIPv4("Real Upstream DNS", + "When not set to 0.0.0.0, used in place of any system configured servers.", + &MiceConfig.network.upstream_dns_server); + igSeparator(); networkChanged |= AddSettingIPv4("IP Address", NULL, &MiceConfig.network.ip_address); networkChanged |= AddSettingIPv4("Subnet Mask", NULL, &MiceConfig.network.subnet_mask); networkChanged |= AddSettingIPv4("Gateway", NULL, &MiceConfig.network.gateway); @@ -538,7 +563,7 @@ void tab_settings_network() { changed |= AddSettingIPv4("bbrouter.loc", NULL, &MiceConfig.network.bbrouter_loc); changed |= AddSettingIPv4("mobirouter.loc", NULL, &MiceConfig.network.mobirouter_loc); changed |= AddSettingIPv4("dslrouter.loc", NULL, &MiceConfig.network.dslrouter_loc); - if (changed) save_current_config(); + if (changed) save_current_config(false); igSeparator(); { @@ -557,7 +582,7 @@ void tab_settings_network() { NULL, NULL)) { _snscanf_s(buffer, _countof(buffer), "%02x", &scan); MiceConfig.network.mac = (MiceConfig.network.mac & 0x00FFFF) | ((scan & 0xff) << 16); - save_current_config(); + save_current_config(false); } igSameLine(0.0, 0); igTextUnformatted(":", NULL); @@ -568,7 +593,7 @@ void tab_settings_network() { NULL, NULL)) { _snscanf_s(buffer, _countof(buffer), "%02x", &scan); MiceConfig.network.mac = (MiceConfig.network.mac & 0xFF00FF) | ((scan & 0xff) << 8); - save_current_config(); + save_current_config(false); } igSameLine(0.0, 0); igTextUnformatted(":", NULL); @@ -579,7 +604,7 @@ void tab_settings_network() { NULL, NULL)) { _snscanf_s(buffer, _countof(buffer), "%02x", &scan); MiceConfig.network.mac = (MiceConfig.network.mac & 0xFFFF00) | (scan & 0xff); - save_current_config(); + save_current_config(false); } igPopStyleVar(1); @@ -641,7 +666,7 @@ void tab_settings_adavanced() { igEndColumns(); - if (changed) save_current_config(); + if (changed) save_current_config(false); } void tab_main_settings() { @@ -691,90 +716,102 @@ bool igKeyBindPopup(const char* name, int* boundKey) { } return false; } +bool igKeyBindPopup_New(const char* name, PMICE_BUTTON_BINDING binding) { + if (igBeginPopupModal(name, NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + igText("Press any button"); + if (igButton("Cancel", vec0)) { + igCloseCurrentPopup(); + igEndPopup(); + return true; + } -void AddSettingButton(int board, int id) { + if (MiceInputGetNewBinding(binding)) { + igCloseCurrentPopup(); + igEndPopup(); + return true; + } + + igEndPopup(); + } + return false; +} + +void AddSettingButton(PMICE_JVS board, int player, int button) { char keyName[32]; static int currentlyBinding; - char pinInfo[32]; - snprintf(pinInfo, _countof(pinInfo), "CN3, Pins %d/%d", JVS_BUTTON_NAMES[id].pinNum, - JVS_BUTTON_NAMES[id].pinNum + 1); - AddSetting(JVS_BUTTON_NAMES[id].name, pinInfo); - if (jvsKeybindings[board].buttons[id * 2] == 0) { - igTextColored(DISABLED_COL, "None"); - } else { - GetKeyNameTextA(MapVirtualKey(jvsKeybindings[board].buttons[id * 2], MAPVK_VK_TO_VSC) << 16, - keyName, _countof(keyName)); - igTextUnformatted(keyName, NULL); + int index = player * board->m_ButtonsPerPlayer + button + board->m_Coins + 1; + + PMICE_BUTTON_BINDING bind = &(board->m_Bindings[index]); + + switch (bind->m_Type) { + case MICE_BB_TYPE_UNBOUND: + igTextColored(DISABLED_COL, "None"); + break; + case MICE_BB_TYPE_DI_JOY: + switch (bind->m_DIJoy.m_ButtonMajor) { + case MICE_BUTTON_MAJOR_AXIS: + igTextColored(DISABLED_COL, "Axis"); + break; + case MICE_BUTTON_MAJOR_DPAD: + igText("D-Pad %s", bind->m_DIJoy.m_ButtonMinor == MICE_DPAD_UP ? "Up" + : bind->m_DIJoy.m_ButtonMinor == MICE_DPAD_DOWN ? "Down" + : bind->m_DIJoy.m_ButtonMinor == MICE_DPAD_LEFT ? "Left" + : bind->m_DIJoy.m_ButtonMinor == MICE_DPAD_RIGHT ? "Right" + : "??"); + break; + case MICE_BUTTON_MAJOR_BUTTON: + igText("Button %d", bind->m_DIJoy.m_ButtonMinor + 1); + break; + } + break; + case MICE_BB_TYPE_GET_ASYNC_KEY: + GetKeyNameTextA(MapVirtualKey(bind->m_AsyncKey.m_Key, MAPVK_VK_TO_VSC) << 16, keyName, + _countof(keyName)); + igTextUnformatted(keyName, NULL); + break; } igNextColumn(); + char name[16]; - char name2[16]; char clear[16]; - char clear2[16]; char invertName[16]; - char invertName2[16]; - snprintf(name, _countof(name), "Bind##Bind%d", id); - snprintf(name2, _countof(name2), "Bind##Bind2p%d", id); - snprintf(clear, _countof(clear), "Clear##Bind%d", id); - snprintf(clear2, _countof(clear2), "Clear##Bind2p%d", id); - snprintf(invertName, _countof(invertName), "##Invert%d", id); - snprintf(invertName2, _countof(invertName2), "##Invert2p%d", id); + snprintf(name, _countof(name), "Bind##Bind%d", index); + snprintf(clear, _countof(clear), "Clear##Bind%d", index); + snprintf(invertName, _countof(invertName), "##Invert%d", index); if (igButton(name, vec0)) { - currentlyBinding = 1; + MiceInputGetNewBinding(NULL); igOpenPopup_Str(name, ImGuiPopupFlags_None); } - if (jvsKeybindings[board].buttons[id * 2]) { + + if (bind->m_Type != MICE_BB_TYPE_UNBOUND) { igSameLine(0, -1); if (igButton(clear, vec0)) { - jvsKeybindings[board].buttons[id * 2] = 0; - save_current_config(); + bind->m_Type = MICE_BB_TYPE_UNBOUND; + save_current_config(false); } } igNextColumn(); - igBeginDisabled(!jvsKeybindings[board].buttons[id * 2]); - if (igCheckbox(invertName, &jvsKeybindings[board].invert[id * 2])) save_current_config(); + igBeginDisabled(bind->m_Type == MICE_BB_TYPE_UNBOUND); + if (igCheckbox(invertName, (bool*)&bind->m_Invert)) save_current_config(false); igEndDisabled(); igNextColumn(); - if (jvsKeybindings[board].buttons[id * 2 + 1] == 0) { - igTextColored(DISABLED_COL, "None"); - } else { - GetKeyNameTextA(MapVirtualKey(jvsKeybindings[board].buttons[id * 2 + 1], MAPVK_VK_TO_VSC) - << 16, - keyName, _countof(keyName)); - igTextUnformatted(keyName, NULL); - } + if (igKeyBindPopup_New(name, bind)) save_current_config(false); +} - igNextColumn(); - if (igButton(name2, vec0)) { - currentlyBinding = 2; - igOpenPopup_Str(name, ImGuiPopupFlags_None); - } - if (jvsKeybindings[board].buttons[id * 2 + 1]) { - igSameLine(0, -1); - if (igButton(clear2, vec0)) { - jvsKeybindings[board].buttons[id * 2 + 1] = 0; - save_current_config(); +void AddButtonsForBoard(PMICE_JVS board) { + for (DWORD button = 0; button < board->m_ButtonsPerPlayer; button++) { + char pinInfo[32]; + snprintf(pinInfo, _countof(pinInfo), "CN3, Pins %d/%d", JVS_BUTTON_NAMES[button].pinNum, + JVS_BUTTON_NAMES[button].pinNum + 1); + AddSetting(JVS_BUTTON_NAMES[button].name, pinInfo); + + for (DWORD player = 0; player < board->m_Players; player++) { + AddSettingButton(board, player, button); } } - igNextColumn(); - igBeginDisabled(!jvsKeybindings[board].buttons[id * 2 + 1]); - if (igCheckbox(invertName2, &jvsKeybindings[board].invert[id * 2 + 1])) save_current_config(); - igEndDisabled(); - - igNextColumn(); - - int boundKey; - if (igKeyBindPopup(name, &boundKey)) { - if (boundKey != -1) { - jvsKeybindings[board].buttons[id * 2 + (currentlyBinding - 1)] = boundKey; - jvsKeybindings[board].notdefault = 1; - save_current_config(); - } - currentlyBinding = 0; - } } void tab_jvs_board(int num) { @@ -804,7 +841,7 @@ void tab_jvs_board(int num) { igSeparator(); - for (int i = 0; i < _countof(JVS_BUTTON_NAMES); i++) AddSettingButton(num, i); + AddButtonsForBoard(&_MiceJvsBoards[0]); igSeparator(); AddSetting("Test", NULL); @@ -822,14 +859,14 @@ void tab_jvs_board(int num) { igSameLine(0, -1); if (igButton("Clear##ClearJvsTest", vec0)) { jvsKeybindings[num].test = 0; - save_current_config(); + save_current_config(false); } } int boundKey; if (igKeyBindPopup("BindJvsTest", &boundKey)) if (boundKey != -1) { jvsKeybindings[num].test = boundKey; - save_current_config(); + save_current_config(false); } igEndColumns(); @@ -865,14 +902,14 @@ void tab_system_buttons() { igSameLine(0, -1); if (igButton("Clear##ClearTest", vec0)) { MiceConfig.keys.test = 0; - save_current_config(); + save_current_config(false); } } igNextColumn(); if (igKeyBindPopup("BindSysTest", &boundKey)) if (boundKey != -1) { MiceConfig.keys.test = boundKey; - save_current_config(); + save_current_config(false); } igTextUnformatted("System Service", NULL); @@ -891,14 +928,14 @@ void tab_system_buttons() { igSameLine(0, -1); if (igButton("Clear##ClearService", vec0)) { MiceConfig.keys.service = 0; - save_current_config(); + save_current_config(false); } } igNextColumn(); if (igKeyBindPopup("BindSysService", &boundKey)) if (boundKey != -1) { MiceConfig.keys.service = boundKey; - save_current_config(); + save_current_config(false); } } @@ -907,7 +944,7 @@ void tab_main_keybinds() { ImGuiInputTextFlags_None)) { if (MiceConfig.keys.board_count < 0) MiceConfig.keys.board_count = 0; if (MiceConfig.keys.board_count > JVS_IO_MAX) MiceConfig.keys.board_count = JVS_IO_MAX; - save_current_config(); + save_current_config(false); } if (igBeginTabBar("JVSBoards", 0)) { @@ -929,7 +966,8 @@ void tab_main_keybinds() { void tab_main_aime_cards() { igText( - "Specify the cards to be inserted when using the TN32MSEC AiMe reader module.\nCards can be " + "Specify the cards to be inserted when using the TN32MSEC AiMe reader module.\nCards can " + "be " "specified as either an NFC ID or an access code.\nIf a card file is present, it will be " "used preferentially."); @@ -944,7 +982,7 @@ void tab_main_aime_cards() { igEndColumns(); - if (changed) save_current_config(); + if (changed) save_current_config(false); } void hud_control(ImGuiKey open_key) { diff --git a/src/micetools/dll/hooks/drive/disks.c b/src/micetools/dll/hooks/drive/disks.c index baae292..bd995dd 100644 --- a/src/micetools/dll/hooks/drive/disks.c +++ b/src/micetools/dll/hooks/drive/disks.c @@ -1,47 +1,132 @@ #include "drive.h" -physical_disk_t SSD = { - .m_SerialNumber = 0x00144DB0, +/* ================ BOOT SSD ================ */ + +// 60GB SSD used by maimai and Project Diva +physical_disk_t SSD_60GB = { + .m_SerialNumber = 0x4d696365, .m_BusType = BusTypeAta, .m_BootPartition = 1, .m_HasSegaboot = TRUE, .m_BlockSize = BLOCK_SIZE_HDD, + // Number of physical sectors. 59.9885 GiB + .m_TotalSize = 125805015, // = (sum of parts) + (n * 63) + .m_DiskType = DiskType_HardDisk, + .m_IsFormatted = true, + .m_Partitions = { + // 1.5GB boot partition + { .m_Size = 0x300B85, .m_Filesystem = MBR_FS_NTFS }, + // 1.5GB recovery partition + { .m_Size = 0x300BC4, .m_Filesystem = MBR_FS_NTFS }, + }, + .m_Extended = { + // 512MB OS update + { 0x102d83, MBR_FS_FAT16, SPD_OS, .m_ReadFunc = &ReadFunc_OSLogFiles, .m_WriteFunc = &WriteFunc_OS }, + // 2GB patch0 + { 0x403947, MBR_FS_FAT16, SPD_Patch0, .m_ReadFunc = &ReadFunc_Patch0, .m_WriteFunc = &WriteFunc_Patch0 }, + // 2GB patch1 + { 0x403947, MBR_FS_FAT16, SPD_Patch1, .m_ReadFunc = NULL }, + // 40GB appdata + { 0x48ed459, MBR_FS_NTFS, SPD_AppData, .m_ReadFunc = NULL }, + // 16GB original0 + { 0x20014aa, MBR_FS_FAT16, SPD_Original0, .m_ReadFunc = &ReadFunc_Original0, .m_WriteFunc = &WriteFunc_Original0 }, + { 0 }, + }, +}; +// 30GB SSD used by all other RingEdge and RingEdge2 games +physical_disk_t SSD_30GB = { + .m_SerialNumber = 0x4d696365, + + .m_BusType = BusTypeAta, + .m_BootPartition = 1, + .m_HasSegaboot = TRUE, + .m_BlockSize = BLOCK_SIZE_HDD, + // Number of physical sectors. 29.9902 GiB + // TODO: Recapture these images! My numbers miss-match with Nibs', and neither decrypt + // My size: 62894080 (lack 395: Failed reading the last chunk of sectors and gave up) + // Nibs's size: 62894471 (lack 4 : Ignored locked sectors) + // The following number is theorhetical + .m_TotalSize = 62894475, // = (sum of parts) + (n * 63) + .m_DiskType = DiskType_HardDisk, + .m_IsFormatted = true, + .m_Partitions = { + // 1GB boot partition + { .m_Size = 0x201c84, .m_Filesystem = MBR_FS_NTFS }, + // 1GB recovery partition + { .m_Size = 0x201cc3, .m_Filesystem = MBR_FS_NTFS }, + }, + .m_Extended = { + // 512MB OS update + { 0x102d83, MBR_FS_FAT16, SPD_OS, .m_ReadFunc = &ReadFunc_OSLogFiles, .m_WriteFunc = &WriteFunc_OS }, + // 2GB patch0 + { 0x403947, MBR_FS_FAT16, SPD_Patch0, .m_ReadFunc = &ReadFunc_Patch0, .m_WriteFunc = &WriteFunc_Patch0 }, + // 2GB patch1 + { 0x403947, MBR_FS_FAT16, SPD_Patch1, .m_ReadFunc = NULL }, + // 15.5GB appdata + { 0x1eead23, MBR_FS_NTFS, SPD_AppData, .m_ReadFunc = NULL }, + // 8GB original0 + { 0x1002996, MBR_FS_FAT16, SPD_Original0, .m_ReadFunc = &ReadFunc_Original0, .m_WriteFunc = &WriteFunc_Original0 }, + { 0 }, + }, +}; +// 7.5GB SSD used by RingWide games (we're calling that 8GB for naming convenience) +physical_disk_t SSD_8GB = { + .m_SerialNumber = 0x4d696365, + + .m_BusType = BusTypeAta, + .m_BootPartition = 1, + .m_HasSegaboot = TRUE, + .m_BlockSize = BLOCK_SIZE_HDD, + // Number of physical sectors. (Allocated) + (Disk slack) + .m_TotalSize = 15711570 + 17070, + .m_DiskType = DiskType_HardDisk, + .m_IsFormatted = true, + .m_Partitions = { + // 1GB boot partition + { .m_Size = 0x201c84, .m_Filesystem = MBR_FS_NTFS }, + // 1GB recovery partition + { .m_Size = 0x201cc3, .m_Filesystem = MBR_FS_NTFS }, + }, + .m_Extended = { + // 512MB OS update + { 0x102d83, MBR_FS_FAT16, SPD_OS, .m_ReadFunc = &ReadFunc_OSLogFiles, .m_WriteFunc = &WriteFunc_OS }, + // 1GB patch0 + { 0x1f6041, MBR_FS_FAT16, SPD_Patch0, .m_ReadFunc = &ReadFunc_Patch0, .m_WriteFunc = &WriteFunc_Patch0 }, + // 1GB patch1 + { 0x1f6041, MBR_FS_FAT16, SPD_Patch1, .m_ReadFunc = NULL }, + // 337MB appdata + { 0xa8a2c, MBR_FS_NTFS, SPD_AppData, .m_ReadFunc = NULL }, + // 2.7GB original0 + { 0x560a60, MBR_FS_FAT16, SPD_Original0, .m_ReadFunc = &ReadFunc_Original0, .m_WriteFunc = &WriteFunc_Original0 }, + { 0 }, + }, +}; + +/* ================ EXTRA HDD ================ */ +// TODO: Get real sizes for this +physical_disk_t APM_HDD = { + .m_BusType = BusTypeUsb, + .m_HasSegaboot = false, + .m_BlockSize = BLOCK_SIZE_HDD, .m_TotalSize = 64 * 1024 * 1024 * (1024 / BLOCK_SIZE_HDD), .m_DiskType = DiskType_HardDisk, .m_IsFormatted = true, .m_Partitions = { - // 1.5GB boot partitions + // ~64GB big block { - .m_Size = 0x300B85, - .m_Filesystem = MBR_FS_NTFS, - }, - // 1.5GB recovery partitions - { - .m_Size = 0x300BC4, + .m_Size = 0x801eb80, .m_Filesystem = MBR_FS_NTFS, + .m_Volume = { + .m_Name = "APM", + .m_MountPoint = 'I', // the APM loader mounts this for us + } }, }, - .m_Extended = { - { 0x102d83, MBR_FS_FAT16, SPD_OS, .m_ReadFunc = &ReadFunc_OSLogFiles, .m_WriteFunc = &WriteFunc_OS }, // 512MB OS update - { 0x403947, MBR_FS_FAT16, SPD_Patch0, .m_ReadFunc = &ReadFunc_Patch0, .m_WriteFunc = &WriteFunc_Patch0 }, // 2GB patch0 - { 0x403947, MBR_FS_FAT16, SPD_Patch1, .m_ReadFunc = NULL }, // 2GB patch1 - { 0x48ed459, MBR_FS_NTFS, SPD_AppData, .m_ReadFunc = NULL }, // 40GB something - { - // 16GB partition for the game - // The real value here should be "0x20014aa," - 0x20014aa, // 16GB, FiNALE - - // Instead, we're going to just allocate ~8GB, the exact size of SDCQ - // 0xeafc00, // ~8GB. Lol. Lmao. - MBR_FS_FAT16, - SPD_Original0, - .m_ReadFunc = &ReadFunc_Original0, - .m_WriteFunc = &WriteFunc_Original0, - }, - { 0 }, - }, + .m_Extended = {{ 0 }}, }; + +/* ================ ASSORTED USBS ================ */ physical_disk_t UPDATE_USB = { .m_BusType = BusTypeUsb, .m_VID = "13FE", @@ -124,6 +209,8 @@ physical_disk_t MAI_USB_DONGLE = { }, .m_Extended = {{ 0 }}, }; + +/* ================ DVD DRIVES ================ */ physical_disk_t ALPHA_DVD = { .m_BusType = BusTypeScsi, .m_DeviceType = DeviceTypeCdRom, @@ -142,35 +229,20 @@ physical_disk_t ALPHA_DVD = { } }, }; -physical_disk_t APM_HDD = { - .m_BusType = BusTypeUsb, - .m_HasSegaboot = false, - .m_BlockSize = BLOCK_SIZE_HDD, - .m_TotalSize = 64 * 1024 * 1024 * (1024 / BLOCK_SIZE_HDD), - .m_DiskType = DiskType_HardDisk, - .m_IsFormatted = true, - .m_Partitions = { - // ~64GB big block - { - .m_Size = 0x801eb80, - .m_Filesystem = MBR_FS_NTFS, - .m_Volume = { - .m_Name = "APM", - .m_MountPoint = 'I', // the APM loader mounts this for us - } - }, - }, - .m_Extended = {{ 0 }}, -}; - -physical_disk_t* PHYSICAL_DISKS[] = { - &SSD, - // &UPDATE_USB, - // &DOWNLOAD_USB, - // &APM_HDD, - - // &MAI_USB_DONGLE, - - // &LOG_USB, - // &ALPHA_DVD, +virtual_disk_t AVAILABLE_DISKS[] = { + // IMPORTANT: Only one of these can be selected! + { &SSD_60GB, "60GB Boot SSD", TRUE }, + { &SSD_30GB, "30GB Boot SSD", FALSE }, + { &SSD_8GB, "7.5GB Boot SSD", FALSE }, + // Extra HDD + { &APM_HDD, "APM Secondary HDD", FALSE }, + // USBs + { &UPDATE_USB, "Game update USB (SEGA_INS)", FALSE }, + { &DOWNLOAD_USB, "Game download USB (SEGA_DL)", FALSE }, + { &LOG_USB, "Debug log USB", FALSE }, + { &MAI_USB_DONGLE, "maimai bootstrap USB", FALSE }, + // DVDs + { &ALPHA_DVD, "Alpha DVD", FALSE }, + + { NULL, NULL, FALSE }, }; diff --git a/src/micetools/dll/hooks/drive/disks.h b/src/micetools/dll/hooks/drive/disks.h index 4308d0c..08fb533 100644 --- a/src/micetools/dll/hooks/drive/disks.h +++ b/src/micetools/dll/hooks/drive/disks.h @@ -7,9 +7,10 @@ typedef struct _physical_disk physical_disk_t; typedef struct _disk_partition disk_partition_t; typedef struct _disk_volume disk_volume_t; typedef struct _disk_raw disk_raw_t; +typedef struct _virtual_disk virtual_disk_t; #define MAX_DISKS 32 -extern physical_disk_t* PHYSICAL_DISKS[MAX_DISKS]; +extern virtual_disk_t AVAILABLE_DISKS[MAX_DISKS]; typedef BOOL(mice_partition_read_function_t)(DWORD nOffset, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, @@ -80,3 +81,10 @@ struct _physical_disk { disk_partition_t m_Partitions[4]; disk_partition_t m_Extended[]; }; + +struct _virtual_disk { + physical_disk_t* m_Disk; + LPCSTR m_Name; + BOOL m_Installed; + BOOL m_Attached; +}; diff --git a/src/micetools/dll/hooks/drive/drive.c b/src/micetools/dll/hooks/drive/drive.c index e0254e4..aadeffc 100644 --- a/src/micetools/dll/hooks/drive/drive.c +++ b/src/micetools/dll/hooks/drive/drive.c @@ -7,6 +7,8 @@ #include "../../util/_util.h" #include "../files.h" +static DWORD _AttachedPhysicalDisks = 0; + /* First 512 bytes of SPD_Original0: LOADER::LoadBootIDHeader @@ -46,10 +48,17 @@ BYTE Original0BootIDHeader[512] = { 0x2F, 0x99, 0xC8, 0x54, 0xD2, 0xDB, 0x52, 0x49, 0xD6, 0xB6, 0x07, 0x1A, 0xBA, 0x9A, 0x85, 0xBB, }; +physical_disk_t* _GetDisk0() { + for (int i = 0; i < MAX_DISKS; i++) { + if (AVAILABLE_DISKS[i].m_Installed) return AVAILABLE_DISKS[i].m_Disk; + } + return NULL; +} + BOOL ReadFunc_Original0(DWORD nOffset, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead) { *lpNumberOfBytesRead = 0; - if (!_PathFileExistsA(ORIGINAL0_PATH)) { + if (!FileExistsA(ORIGINAL0_PATH)) { log_error(plfDrive, "Failed to open %s (does not exist)", ORIGINAL0_PATH); return FALSE; } @@ -60,8 +69,11 @@ BOOL ReadFunc_Original0(DWORD nOffset, LPVOID lpBuffer, DWORD nNumberOfBytesToRe log_error(plfDrive, "Failed to open %s", ORIGINAL0_PATH); return FALSE; } + physical_disk_t* lpSsd = _GetDisk0(); + if (lpSsd == NULL) return FALSE; + LARGE_INTEGER seekTo; - seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)SSD.m_BlockSize; + seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)lpSsd->m_BlockSize; _SetFilePointerEx(hFile, seekTo, NULL, FILE_BEGIN); BOOL ret = _ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, NULL); if (!ret) log_error(plfDrive, "Failed to read to %s: %03x", ORIGINAL0_PATH, GetLastError()); @@ -78,8 +90,11 @@ BOOL WriteFunc_Original0(DWORD nOffset, LPCVOID lpBuffer, DWORD nNumberOfBytesTo log_error(plfDrive, "Failed to open %s", ORIGINAL0_PATH); return FALSE; } + physical_disk_t* lpSsd = _GetDisk0(); + if (lpSsd == NULL) return FALSE; + LARGE_INTEGER seekTo; - seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)SSD.m_BlockSize; + seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)lpSsd->m_BlockSize; _SetFilePointerEx(hFile, seekTo, NULL, FILE_BEGIN); BOOL ret = _WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, NULL); if (!ret) log_error(plfDrive, "Failed to write to %s: %03x", ORIGINAL0_PATH, GetLastError()); @@ -90,7 +105,7 @@ BOOL WriteFunc_Original0(DWORD nOffset, LPCVOID lpBuffer, DWORD nNumberOfBytesTo BOOL ReadFunc_Patch0(DWORD nOffset, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead) { *lpNumberOfBytesRead = 0; - if (!_PathFileExistsA(PATCH0_PATH)) { + if (!FileExistsA(PATCH0_PATH)) { log_error(plfDrive, "Failed to open %s (does not exist)", PATCH0_PATH); return FALSE; } @@ -101,8 +116,11 @@ BOOL ReadFunc_Patch0(DWORD nOffset, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, log_error(plfDrive, "Failed to open %s", PATCH0_PATH); return FALSE; } + physical_disk_t* lpSsd = _GetDisk0(); + if (lpSsd == NULL) return FALSE; + LARGE_INTEGER seekTo; - seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)SSD.m_BlockSize; + seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)lpSsd->m_BlockSize; _SetFilePointerEx(hFile, seekTo, NULL, FILE_BEGIN); BOOL ret = _ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, NULL); if (!ret) log_error(plfDrive, "Failed to read to %s: %03x", PATCH0_PATH, GetLastError()); @@ -119,8 +137,11 @@ BOOL WriteFunc_Patch0(DWORD nOffset, LPCVOID lpBuffer, DWORD nNumberOfBytesToWri log_error(plfDrive, "Failed to open %s", PATCH0_PATH); return FALSE; } + physical_disk_t* lpSsd = _GetDisk0(); + if (lpSsd == NULL) return FALSE; + LARGE_INTEGER seekTo; - seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)SSD.m_BlockSize; + seekTo.QuadPart = (LONGLONG)nOffset * (LONGLONG)lpSsd->m_BlockSize; _SetFilePointerEx(hFile, seekTo, NULL, FILE_BEGIN); BOOL ret = _WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, NULL); if (!ret) log_error(plfDrive, "Failed to write to %s: %03x", PATCH0_PATH, GetLastError()); @@ -217,6 +238,7 @@ BOOL WINAPI x_drive_DeviceIoControl(file_context_t* ctx, DWORD dwIoControlCode, } } +extern physical_disk_t ALPHA_DVD; // Bit hacky, but good enough for now BOOL WINAPI q_drive_DeviceIoControl(file_context_t* ctx, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { @@ -379,14 +401,17 @@ disk_volume_t* getVolumeByPath(LPCSTR lpRootPathName, DWORD match) { return FALSE; } - for (size_t disk = 0; disk < MAX_DISKS && PHYSICAL_DISKS[disk] != NULL; disk++) { + for (size_t disk = 0; disk < MAX_DISKS && AVAILABLE_DISKS[disk].m_Disk != NULL; disk++) { + if (!AVAILABLE_DISKS[disk].m_Installed) continue; + for (size_t part = 0; part < 4; part++) { - disk_volume_t* volume = &(PHYSICAL_DISKS[disk]->m_Partitions[part].m_Volume); + disk_volume_t* volume = &(AVAILABLE_DISKS[disk].m_Disk->m_Partitions[part].m_Volume); if (matchVolume(volume, lpRootPathName, match)) return volume; } - for (size_t part = 0; PHYSICAL_DISKS[disk]->m_Extended[part].m_PartitionNumber; part++) { - disk_volume_t* volume = &(PHYSICAL_DISKS[disk]->m_Extended[part].m_Volume); + for (size_t part = 0; AVAILABLE_DISKS[disk].m_Disk->m_Extended[part].m_PartitionNumber; + part++) { + disk_volume_t* volume = &(AVAILABLE_DISKS[disk].m_Disk->m_Extended[part].m_Volume); if (matchVolume(volume, lpRootPathName, match)) return volume; } } @@ -646,10 +671,41 @@ void init_pd(physical_disk_t* pd) { hook_file(hook); } +BOOL attach_disk(virtual_disk_t* lpDisk) { + if (lpDisk->m_Disk->m_DriveNumber != (DWORD)-1) return FALSE; + if (lpDisk->m_Attached) return TRUE; + lpDisk->m_Attached = TRUE; + + lpDisk->m_Disk->m_DriveNumber = _AttachedPhysicalDisks++; + lpDisk->m_Disk->m_DeviceName[0] = '\0'; + lpDisk->m_Disk->m_DosDeviceName[0] = '\0'; + init_pd(lpDisk->m_Disk); + return TRUE; +} +BOOL detach_disk(virtual_disk_t* lpDisk) { + if (lpDisk->m_Disk->m_DriveNumber == (DWORD)-1) return FALSE; + if (!lpDisk->m_Attached) return TRUE; + + wchar_t hookPath[21]; + swprintf_s(hookPath, _countof(hookPath), L"\\\\.\\%ls", lpDisk->m_Disk->m_DosDeviceName); + + file_hook_t* hook = unhook_file(hookPath); + if (hook == NULL) return FALSE; + + lpDisk->m_Disk->m_DriveNumber = (DWORD)-1; + lpDisk->m_Attached = FALSE; + free((void*)hook->filename); + free(hook); + return TRUE; +} + void init_all_pd() { - for (int i = 0; i < MAX_DISKS && PHYSICAL_DISKS[i] != NULL; i++) { - PHYSICAL_DISKS[i]->m_DriveNumber = i; - init_pd(PHYSICAL_DISKS[i]); + for (int i = 0; i < MAX_DISKS && AVAILABLE_DISKS[i].m_Disk != NULL; i++) { + AVAILABLE_DISKS[i].m_Disk->m_DriveNumber = (DWORD)-1; + AVAILABLE_DISKS[i].m_Attached = FALSE; + } + for (int i = 0; i < MAX_DISKS && AVAILABLE_DISKS[i].m_Disk != NULL; i++) { + if (AVAILABLE_DISKS[i].m_Installed) attach_disk(&AVAILABLE_DISKS[i]); } } @@ -704,11 +760,6 @@ void hook_drives() { q_drive->DeviceIoControl = &q_drive_DeviceIoControl; q_drive->ReadFile = &q_drive_ReadFile; hook_file(q_drive); - // TODO: ewwwwwwwwwwwwwwwwww - file_hook_t* q_drive_lower = new_file_hook(L"\\\\.\\q:"); - q_drive_lower->DeviceIoControl = &q_drive_DeviceIoControl; - q_drive_lower->ReadFile = &q_drive_ReadFile; - hook_file(q_drive_lower); hook("Kernel32.dll", "FindFirstVolumeW", &FakeFindFirstVolumeW, NULL); hook("Kernel32.dll", "FindNextVolumeW", &FakeFindNextVolumeW, NULL); @@ -734,14 +785,15 @@ void hook_drives() { hook("Winmm.dll", "mciSendStringA", &Fake_mciSendStringA, NULL); - make_dirs(DISK_PATH); + make_dirs(DISK_PATH)); SegaBootRecordDefault.crc = amiCrc32RCalc(sizeof SegaBootRecordDefault - 4, &SegaBootRecordDefault.version, 0); HANDLE hFile; DWORD numberOfBytesRead; - if (_PathFileExistsA(SBR0_PATH)) { + + if (FileExistsA(SBR0_PATH)) { hFile = _CreateFileA(SBR0_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { @@ -755,7 +807,7 @@ void hook_drives() { // memcpy(&SegaBootRecord0, &SegaBootRecordDefault, sizeof SegaBootRecord0); } - if (_PathFileExistsA(SBR1_PATH)) { + if (FileExistsA(SBR1_PATH)) { hFile = _CreateFileA(SBR1_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { diff --git a/src/micetools/dll/hooks/drive/drive.h b/src/micetools/dll/hooks/drive/drive.h index b8927ca..8ed8e79 100644 --- a/src/micetools/dll/hooks/drive/drive.h +++ b/src/micetools/dll/hooks/drive/drive.h @@ -15,17 +15,12 @@ #include "disks.h" #include "irb.h" -#define DISK_PATH "dev/disk/" -#define SPD_PATH (DISK_PATH "spd.bin") -#define SBR0_PATH (DISK_PATH "sbr0.bin") -#define SBR1_PATH (DISK_PATH "sbr1.bin") -#define ORIGINAL0_PATH (DISK_PATH "original0.bin") -#define PATCH0_PATH (DISK_PATH "patch0.bin") - -extern physical_disk_t SSD; -extern physical_disk_t UPDATE_USB; -extern physical_disk_t LOG_USB; -extern physical_disk_t ALPHA_DVD; +#define DISK_PATH MiceIpcRelativePath("disk\\" +#define SPD_PATH DISK_PATH "spd.bin") +#define SBR0_PATH DISK_PATH "sbr0.bin") +#define SBR1_PATH DISK_PATH "sbr1.bin") +#define ORIGINAL0_PATH DISK_PATH "original0.bin") +#define PATCH0_PATH DISK_PATH "patch0.bin") extern sbr_t SegaBootRecordDefault; extern sbr_t SegaBootRecord0; @@ -35,6 +30,9 @@ extern sbr_t SegaBootRecord1; void init_volume(disk_volume_t* vol); void init_pd(physical_disk_t* pd); void init_all_pd(); +BOOL attach_disk(virtual_disk_t* lpDisk); +BOOL detach_disk(virtual_disk_t* lpDisk); +physical_disk_t* _GetDisk0(); mice_partition_read_function_t ReadFunc_Original0; mice_partition_write_function_t WriteFunc_Original0; diff --git a/src/micetools/dll/hooks/drive/hooks.c b/src/micetools/dll/hooks/drive/hooks.c index cc7b339..02af5c2 100644 --- a/src/micetools/dll/hooks/drive/hooks.c +++ b/src/micetools/dll/hooks/drive/hooks.c @@ -9,12 +9,17 @@ disk_volume_t* incrementFindIndex(HANDLE hFindVolume) { if (find_index == NULL) return NULL; while (1) { - if (find_index->disk >= MAX_DISKS || PHYSICAL_DISKS[find_index->disk] == NULL) { + if (find_index->disk >= MAX_DISKS || AVAILABLE_DISKS[find_index->disk].m_Disk == NULL) { return NULL; } + if (!AVAILABLE_DISKS[find_index->disk].m_Installed) { + find_index->disk++; + find_index->partition = 0; + continue; + } if (find_index->partition > 3) { - if (PHYSICAL_DISKS[find_index->disk] + if (AVAILABLE_DISKS[find_index->disk].m_Disk ->m_Extended[find_index->partition - 4] .m_PartitionNumber == 0) { find_index->disk++; @@ -22,18 +27,18 @@ disk_volume_t* incrementFindIndex(HANDLE hFindVolume) { continue; } - return &(PHYSICAL_DISKS[find_index->disk] + return &(AVAILABLE_DISKS[find_index->disk].m_Disk ->m_Extended[(find_index->partition++) - 4] .m_Volume); } - if (PHYSICAL_DISKS[find_index->disk] + if (AVAILABLE_DISKS[find_index->disk].m_Disk ->m_Partitions[find_index->partition] .m_PartitionNumber == 0) { find_index->partition = 4; continue; } - return &(PHYSICAL_DISKS[find_index->disk]->m_Partitions[find_index->partition++].m_Volume); + return &(AVAILABLE_DISKS[find_index->disk].m_Disk->m_Partitions[find_index->partition++].m_Volume); } } @@ -333,7 +338,7 @@ BOOL WINAPI FakeGetDiskFreeSpaceExA(LPCSTR lpDirectoryName, // return FALSE; // } - // We're going to be remapping the drive to ./dev/, so the free bytes are whatever the current + // We're going to be remapping the drive to ./mice/dev/, so the free bytes are whatever the current // real drive has free. No point claiming we have more than we do! return TrueGetDiskFreeSpaceExA(NULL, lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); diff --git a/src/micetools/dll/hooks/drive/physicalDrive.c b/src/micetools/dll/hooks/drive/physicalDrive.c index 87da2bf..7a192b0 100644 --- a/src/micetools/dll/hooks/drive/physicalDrive.c +++ b/src/micetools/dll/hooks/drive/physicalDrive.c @@ -72,8 +72,11 @@ BOOL WINAPI pd_DeviceIoControl(file_context_t* ctx, DWORD dwIoControlCode, LPVOI log_error(plfDrive, "Unimeplemented ATA %02X command: %02x", command, data); return FALSE; case IOCTL_DISK_GET_LENGTH_INFO: + physical_disk_t* lpSsd = _GetDisk0(); + if (lpSsd == NULL) return FALSE; + PGET_LENGTH_INFORMATION pLi = (PGET_LENGTH_INFORMATION)lpOutBuffer; - pLi->Length.QuadPart = SSD.m_TotalSize * (long long)SSD.m_BlockSize; + pLi->Length.QuadPart = lpSsd->m_TotalSize * (long long)lpSsd->m_BlockSize; *lpBytesReturned = sizeof *pLi; return TRUE; } @@ -81,6 +84,8 @@ BOOL WINAPI pd_DeviceIoControl(file_context_t* ctx, DWORD dwIoControlCode, LPVOI return FALSE; } +// TODO: File-backed +BYTE SEGABOOT_DATA_CACHE[512 * 60] = { 0xff }; BOOL pd_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) { physical_disk_t* pd = (physical_disk_t*)ctx->m_HookData; @@ -126,7 +131,7 @@ BOOL pd_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRea } if (ptrLBA <= MBR_LBA_GAP) { // Read within the 63 extra tracks - log_error(plfDrive, "Read failed"); + log_error(plfDrive, "Read failed (%08x)", ptrLBA); return FALSE; } @@ -139,7 +144,8 @@ BOOL pd_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRea DWORD readOffset = ptrLBA - pd->m_Partitions[i].m_PhysicalLBA; if (pd->m_Partitions[i].m_ReadFunc == NULL) { - log_error(plfDrive, "Attempted read in %d/%d at block offset %08x; No read function", + log_error(plfDrive, + "Attempted read in %d/%d at block offset %08x; No read function", pd->m_DriveNumber, pd->m_Partitions[i].m_PartitionNumber, readOffset); return FALSE; } @@ -230,12 +236,21 @@ BOOL pd_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRea *lpNumberOfBytesRead = sizeof SegaBootRecord1; return TRUE; } + + if (ptrLBA >= headerLBA && ptrLBA < headerLBA + EXT_HEADER_GAP) { + DWORD nDataOffset = (ptrLBA - headerLBA - 4) * pd->m_BlockSize; + if (nNumberOfBytesToRead > sizeof SEGABOOT_DATA_CACHE - nDataOffset) { + nNumberOfBytesToRead = sizeof SEGABOOT_DATA_CACHE - nDataOffset; + } + memcpy(lpBuffer, SEGABOOT_DATA_CACHE, nNumberOfBytesToRead); + *lpNumberOfBytesRead = nNumberOfBytesToRead; + return TRUE; + } } - if (ptrLBA >= pd->m_Extended[i].m_PhysicalLBA - EXT_HEADER_GAP && - ptrLBA < pd->m_Extended[i].m_PhysicalLBA) { + if (ptrLBA >= headerLBA && ptrLBA < headerLBA + EXT_HEADER_GAP) { // Read within the 63 extra tracks - log_error(plfDrive, "Read failed"); + log_error(plfDrive, "Read failed (%08x)", ptrLBA); return FALSE; } @@ -244,7 +259,8 @@ BOOL pd_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRea DWORD readOffset = ptrLBA - pd->m_Extended[i].m_PhysicalLBA; if (pd->m_Extended[i].m_ReadFunc == NULL) { - log_error(plfDrive, "Attempted read in %d/%d at block offset %08x; No read function", + log_error(plfDrive, + "Attempted read in %d/%d at block offset %08x; No read function", pd->m_DriveNumber, pd->m_Extended[i].m_PartitionNumber, readOffset); return FALSE; } @@ -262,7 +278,7 @@ BOOL pd_ReadFile(file_context_t* ctx, LPVOID lpBuffer, DWORD nNumberOfBytesToRea } } - log_error(plfDrive, "Read failed"); + log_error(plfDrive, "Read failed (%08x)", ptrLBA); return FALSE; } diff --git a/src/micetools/dll/hooks/files.c b/src/micetools/dll/hooks/files.c index 45cb22c..db142af 100644 --- a/src/micetools/dll/hooks/files.c +++ b/src/micetools/dll/hooks/files.c @@ -3,13 +3,15 @@ #include "../util/_util.h" +static const bool ALLOW_FILE_PASSTHROUGH = true; + // TODO: This should be part of MiceFS I think file_hook_t* MiceFSLocateHookW(_In_ LPCWSTR lpFileName); file_hook_t* MiceFSLocateHookW(LPCWSTR lpFileName) { file_hook_t* file_hook = file_hook_list; while (file_hook != NULL) { - if (wcscmp(lpFileName, file_hook->filename) == 0 || - (file_hook->altFilename != NULL && wcscmp(lpFileName, file_hook->altFilename) == 0)) { + if (_wcsicmp(lpFileName, file_hook->filename) == 0 || + (file_hook->altFilename != NULL && _wcsicmp(lpFileName, file_hook->altFilename) == 0)) { return file_hook; } file_hook = file_hook->next; @@ -19,6 +21,7 @@ file_hook_t* MiceFSLocateHookW(LPCWSTR lpFileName) { HANDLE open_hook(file_hook_t* file_hook) { open_hook_t* opened = (open_hook_t*)malloc(sizeof(open_hook_t)); + if (opened == NULL) return INVALID_HANDLE_VALUE; ZeroMemory(opened, sizeof *opened); opened->hook = file_hook; @@ -51,7 +54,25 @@ void hook_file(file_hook_t* hook) { file_hook_t* hl = file_hook_list; while (hl->next != NULL) hl = hl->next; hl->next = hook; -}; +} +file_hook_t* unhook_file(LPCWSTR lpFilename) { + file_hook_t* hl = file_hook_list; + if (hl == NULL) return NULL; + file_hook_t* previous = NULL; + do { + if (_wcsicmp(hl->filename, lpFilename) == 0 || _wcsicmp(hl->altFilename, lpFilename) == 0) { + if (previous == NULL) + file_hook_list = hl->next; + else + previous->next = hl->next; + return hl; + } + + previous = hl; + hl = hl->next; + } while (hl != NULL); + return NULL; +} struct buffer_file { LPBYTE buffer; @@ -116,23 +137,35 @@ HANDLE WINAPI FakeCreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD d LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { + size_t len = wcslen(lpFileName); + if (wcscmp(&lpFileName[len - 4], L".dbg") == 0 || wcscmp(&lpFileName[len - 4], L".pdb") == 0) { + return TrueCreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, + dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + } + file_hook_t* found_fh = MiceFSLocateHookW(lpFileName); if (found_fh != NULL) { HANDLE handle = open_hook(found_fh); log_info(plfHooks, "CreateFileW(%ls) -> 0x%p", lpFileName, handle); return handle; } + if (!ALLOW_FILE_PASSTHROUGH) { + SetLastError(ERROR_FILE_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } HANDLE handle; MiceFSRedirectPathW(lpFileName, &lpFileName); handle = TrueCreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - if (handle == INVALID_HANDLE_VALUE) - log_warning(plfHooks, "CreateFileW(%ls) failed", lpFileName); - else - log_misc(plfHooks, "CreateFileW(%ls) -> 0x%p", lpFileName, handle); - + if (handle == INVALID_HANDLE_VALUE) { + if (!(_wcsicmp(lpFileName, L"C:\\windows\\system32\\atipblag.dat") == 0 || + wcsncmp(lpFileName, L"\\\\?\\hid#", 8) == 0)) + log_warning(plfHooks, "CreateFileW(%ls) failed", lpFileName); + } else { + log_info(plfHooks, "CreateFileW(%ls) -> 0x%p", lpFileName, handle); + } return handle; } HANDLE WINAPI FakeCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, @@ -148,35 +181,31 @@ HANDLE WINAPI FakeCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dw log_info(plfHooks, "CreateFileA(%s) -> 0x%p", lpFileName, handle); return handle; } + if (!ALLOW_FILE_PASSTHROUGH) { + SetLastError(ERROR_FILE_NOT_FOUND); + return INVALID_HANDLE_VALUE; + } MiceFSRedirectPathA(lpFileName, &lpFileName); HANDLE handle = TrueCreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); - if (handle == INVALID_HANDLE_VALUE) - log_warning(plfHooks, "CreateFileA(%s) failed", lpFileName); - else + if (handle == INVALID_HANDLE_VALUE) { + if (_stricmp(lpFileName, "C:\\windows\\system32\\atipblag.dat") != 0) + log_warning(plfHooks, "CreateFileA(%s) failed", lpFileName); + } else log_misc(plfHooks, "CreateFileA(%s) -> 0x%p", lpFileName, handle); return handle; } -BOOL WINAPI FakePathFileExistsA(LPCSTR pszPath) { - MiceFSRedirectPathA(pszPath, &pszPath); - BOOL ret = TruePathFileExistsA(pszPath); - log_misc(plfHooks, "PathFileExistsA(%s) = %d", pszPath, ret); - return ret; -} -BOOL WINAPI FakePathFileExistsW(LPCWSTR pszPath) { - MiceFSRedirectPathW(pszPath, &pszPath); - BOOL ret = TruePathFileExistsW(pszPath); - log_misc(plfHooks, "PathFileExistsW(%ls) = %d", pszPath, ret); - return ret; -} - BOOL WINAPI FakeDeleteFileA(LPCSTR pszPath) { + if (!ALLOW_FILE_PASSTHROUGH) return FALSE; + MiceFSRedirectPathA(pszPath, &pszPath); return TrueDeleteFileA(pszPath); } BOOL WINAPI FakeDeleteFileW(LPCWSTR pszPath) { + if (!ALLOW_FILE_PASSTHROUGH) return FALSE; + MiceFSRedirectPathW(pszPath, &pszPath); return TrueDeleteFileW(pszPath); } @@ -186,9 +215,6 @@ BOOL WINAPI FakeDeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lp LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped) { open_hook_t* pHData = GetDataForHandle(hDevice, HDATA_FILE); if (pHData == NULL) { - // log_trace(plfHooks, "DeviceIoControl(0x%p, 0x%08x, 0x%p, 0x%x, -, 0x%x, 0, 0)", hDevice, - // dwIoControlCode, lpInBuffer, nInBufferSize, nOutBufferSize); - return TrueDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlapped); } @@ -326,7 +352,6 @@ BOOL WINAPI FakeCloseHandle(HANDLE hObject) { return TrueCloseHandle(hObject); } - void FileTimeToTimet(__time64_t* t, LPFILETIME pft) { ULARGE_INTEGER time_value; time_value.HighPart = pft->dwHighDateTime; @@ -405,8 +430,7 @@ void hook_io() { hook("Kernel32.dll", "ReadFile", FakeReadFile, (void**)&TrueReadFile); hook("Kernel32.dll", "GetFileSizeEx", FakeGetFileSizeEx, (void**)&TrueGetFileSizeEx); - hook("Shlwapi.dll", "PathFileExistsA", FakePathFileExistsA, (void**)&TruePathFileExistsA); - hook("Shlwapi.dll", "PathFileExistsW", FakePathFileExistsW, (void**)&TruePathFileExistsW); + // PathIsDirectory, PathFileExists, etc just call GetFileAttributes under the hood hook("Kernel32.dll", "DeleteFileA", FakeDeleteFileA, (void**)&TrueDeleteFileA); hook("Kernel32.dll", "DeleteFileW", FakeDeleteFileW, (void**)&TrueDeleteFileW); hook("Kernel32.dll", "GetCurrentDirectoryA", FakeGetCurrentDirectoryA, @@ -420,10 +444,8 @@ void hook_io() { (void**)&TrueGetFileAttributesA); hook("Kernel32.dll", "GetFileAttributesW", FakeGetFileAttributesW, (void**)&TrueGetFileAttributesW); - hook("Kernel32.dll", "CreateDirectoryA", FakeCreateDirectoryA, - (void**)&TrueCreateDirectoryA); - hook("Kernel32.dll", "CreateDirectoryW", FakeCreateDirectoryW, - (void**)&TrueCreateDirectoryW); + hook("Kernel32.dll", "CreateDirectoryA", FakeCreateDirectoryA, (void**)&TrueCreateDirectoryA); + hook("Kernel32.dll", "CreateDirectoryW", FakeCreateDirectoryW, (void**)&TrueCreateDirectoryW); hook("MSVCR90.DLL", "_stat64i32", Fake_stat64i32, (void**)&True_stat64i32); } diff --git a/src/micetools/dll/hooks/files.h b/src/micetools/dll/hooks/files.h index 2a52971..87c856a 100644 --- a/src/micetools/dll/hooks/files.h +++ b/src/micetools/dll/hooks/files.h @@ -47,8 +47,6 @@ _MICE_FILES BOOL(WINAPI* TrueReadFile)(HANDLE hFile, LPVOID lpBuffer, DWORD nNum LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); _MICE_FILES BOOL(WINAPI* TrueGetFileSizeEx)(HANDLE hFile, PLARGE_INTEGER lpFileSize); _MICE_FILES BOOL(WINAPI* TrueCloseHandle)(HANDLE hObject); -_MICE_FILES BOOL(WINAPI* TruePathFileExistsA)(LPCSTR pszPath); -_MICE_FILES BOOL(WINAPI* TruePathFileExistsW)(LPCWSTR pszPath); _MICE_FILES BOOL(WINAPI* TrueDeleteFileA)(LPCSTR lpFileName); _MICE_FILES BOOL(WINAPI* TrueDeleteFileW)(LPCWSTR lpFileName); _MICE_FILES HANDLE(WINAPI* TrueFindFirstFileA)(LPCSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData); @@ -93,7 +91,8 @@ static int(WINAPIV* True_stat64i32)(const char* path, _stat64i32_t* buffer); #define _CreateFileA (TrueCreateFileA ? TrueCreateFileA : CreateFileA) #define _SetFilePointer (TrueSetFilePointer ? TrueSetFilePointer : SetFilePointer) #define _SetFilePointerEx (TrueSetFilePointerEx ? TrueSetFilePointerEx : SetFilePointerEx) -#define _PathFileExistsA (TruePathFileExistsA ? TruePathFileExistsA : PathFileExistsA) +#define _GetFileAttributesW (TrueGetFileAttributesW ? TrueGetFileAttributesW : GetFileAttributesW) +// GetFileAttributesA internally calls (hooked)GetFileAttributesW #define _GetCurrentDirectoryA \ (TrueGetCurrentDirectoryA ? TrueGetCurrentDirectoryA : GetCurrentDirectoryA) #define _GetCurrentDirectoryW \ @@ -137,3 +136,4 @@ file_hook_t* new_file_hook(LPCWSTR filename); void hook_file(file_hook_t* hook); void hook_io(); void hook_file_with_buffer(LPCWSTR filename, LPBYTE buffer, DWORD nBytes, DWORD access); +file_hook_t* unhook_file(LPCWSTR lpFilename); diff --git a/src/micetools/dll/hooks/gui.c b/src/micetools/dll/hooks/gui.c index 95d82d5..081afcd 100644 --- a/src/micetools/dll/hooks/gui.c +++ b/src/micetools/dll/hooks/gui.c @@ -1,8 +1,12 @@ #include "gui.h" +#include + +#include "../input.h" + extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -HWND mainWindow; +HWND mainWindow = NULL; static unsigned int hookType; @@ -22,41 +26,61 @@ HWND GetProcessWindow() { return window; } -BOOL UnFrameWindow(HWND hwnd) { +static const LONG frameFlags = WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; +LONG UnFrameWindow(HWND hwnd) { SetLastError(ERROR_SUCCESS); LONG style = GetWindowLongW(hwnd, GWL_STYLE); - if (GetLastError() != ERROR_SUCCESS) return FALSE; + if (GetLastError() != ERROR_SUCCESS) return style; RECT rect; - if (!GetClientRect(hwnd, &rect)) return FALSE; + if (!GetClientRect(hwnd, &rect)) return style; - style &= ~(WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU); - if (!AdjustWindowRect(&rect, style, FALSE)) return FALSE; + // Nothing to style + if ((style & frameFlags) == 0) return style; - SetWindowLongW(hwnd, GWL_STYLE, style); + LONG newStyle = style & ~(frameFlags); + if (!AdjustWindowRect(&rect, newStyle, FALSE)) return style; + + SetWindowLongW(hwnd, GWL_STYLE, newStyle); if (!SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_NOMOVE)) - return FALSE; - return TRUE; + return style; + return newStyle; } -BOOL FrameWindow(HWND hwnd) { +LONG FrameWindow(HWND hwnd) { SetLastError(ERROR_SUCCESS); LONG style = GetWindowLongW(hwnd, GWL_STYLE); - if (GetLastError() != ERROR_SUCCESS) return FALSE; + if (GetLastError() != ERROR_SUCCESS) return style; + + // Nothing to do + if ((style & frameFlags) == frameFlags) return style; RECT rect; - if (!GetClientRect(hwnd, &rect)) return FALSE; + if (!GetClientRect(hwnd, &rect)) return style; - style |= WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU; - if (!AdjustWindowRect(&rect, style, FALSE)) return FALSE; + LONG newStyle = style | frameFlags; + if (!AdjustWindowRect(&rect, newStyle, FALSE)) return style; - SetWindowLongW(hwnd, GWL_STYLE, style); + SetWindowLongW(hwnd, GWL_STYLE, newStyle); if (!SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_NOMOVE)) - return FALSE; - return TRUE; + return style; + return newStyle; +} + +BOOL UnadjustWindowRect(LPRECT prc, DWORD dwStyle, BOOL fMenu) { + RECT rc; + SetRectEmpty(&rc); + BOOL fRc = AdjustWindowRect(&rc, dwStyle, fMenu); + if (fRc) { + prc->left -= rc.left; + prc->top -= rc.top; + prc->right -= rc.right; + prc->bottom -= rc.bottom; + } + return fRc; } BOOL GetD3D9Device(void** pTable, size_t Size) { @@ -155,12 +179,19 @@ void post_win_create(HWND hWnd) { if (hookType == UI_HOOK_DX9) { void* d3d9Device[119]; if (GetD3D9Device(d3d9Device, sizeof(d3d9Device))) { - *((PVOID*)&TrueEndScene) = CreateHook32((PVOID)d3d9Device[42], (PVOID)hkEndScene); + TrueEndScene = d3d9Device[42]; + + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach((PVOID*)&TrueEndScene, hkEndScene); + DetourTransactionCommit(); } - if (hWnd && !SetWindowSubclass(hWnd, WndProc, (int)&WndProc, (DWORD_PTR)NULL)) { + if (hWnd && !SetWindowSubclass(hWnd, WndProc, (UINT_PTR)&WndProc, (DWORD_PTR)NULL)) { log_error(plfGUI, "failed to SetWindowSubclass(%d)", GetLastError()); } + } else { + MiceInputInit(hWnd); } } @@ -175,6 +206,8 @@ BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMoni } void SetupWindowPosition(LPRECT lpRect, DWORD dwStyle) { + UnadjustWindowRect(lpRect, dwStyle, FALSE); + int x = lpRect->left; int y = lpRect->top; int w = lpRect->right - x; @@ -191,6 +224,17 @@ void SetupWindowPosition(LPRECT lpRect, DWORD dwStyle) { if (MiceConfig.window.centre) { x = ((monitorRect.right - monitorRect.left) - w) / 2; y = ((monitorRect.bottom - monitorRect.top) - h) / 2; + + // if ((dwStyle & frameFlags) == frameFlags) { + // RECT adjustedRect; + // memcpy(&adjustedRect, lpRect, sizeof *lpRect); + // UnadjustWindowRect(&adjustedRect, dwStyle, FALSE); + // // We're going to only adjust y, on the assumption x is unchanged, and even if it is + // // it'll be symmetic. y has the titlebar to worry about. + // int outerH = adjustedRect.bottom - adjustedRect.top; + + // y += (outerH - h) / 2; + // } } else { x = MiceConfig.window.x; y = MiceConfig.window.y; @@ -284,12 +328,16 @@ HRESULT STDMETHODCALLTYPE FakeCreateDevice(IDirect3D9* this, UINT Adapter, D3DDE Adapter = MiceConfig.window.adaptor; RECT winRect; - GetWindowRect(hFocusWindow, &winRect); - DWORD dwStyle = GetWindowLongW(hFocusWindow, GWL_STYLE); - SetupWindowPosition(&winRect, dwStyle); + if (!GetWindowRect(hFocusWindow, &winRect)) { + log_error(plfGUI, "GetWindowRect failed: %d", GetLastError()); + } else { + DWORD dwStyle = GetWindowLongW(hFocusWindow, GWL_STYLE); + SetupWindowPosition(&winRect, dwStyle); - SetWindowPos(hFocusWindow, HWND_TOP, winRect.left, winRect.top, winRect.right - winRect.left, - winRect.bottom - winRect.top, 0); + // I have no idea why, but this is causing crashes??? + SetWindowPos(hFocusWindow, HWND_TOP, winRect.left, winRect.top, + winRect.right - winRect.left, winRect.bottom - winRect.top, 0); + } HRESULT res = TrueCreateDevice(this, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); @@ -313,6 +361,9 @@ HRESULT STDMETHODCALLTYPE FakeCreateDevice(IDirect3D9* this, UINT Adapter, D3DDE } } log_info(plfD3D9, "Device created: %d", res); + + MiceInputInit(hFocusWindow); + return res; } @@ -337,12 +388,12 @@ IDirect3D9* WINAPI FakeDirect3DCreate9(UINT SDKVersion) { hookType = UI_HOOK_DX9; TrueCreateDevice = pD3D->lpVtbl->CreateDevice; - DWORD patch = (DWORD)&FakeCreateDevice; - patch_at(&pD3D->lpVtbl->CreateDevice, (char*)&patch, 4); + size_t patch = (size_t)&FakeCreateDevice; + MiceHookPatchAt(&pD3D->lpVtbl->CreateDevice, (char*)&patch, sizeof patch); TrueEnumAdapterModes = pD3D->lpVtbl->EnumAdapterModes; - patch = (DWORD)&FakeEnumAdapterModes; - patch_at(&pD3D->lpVtbl->EnumAdapterModes, (char*)&patch, 4); + patch = (size_t)&FakeEnumAdapterModes; + MiceHookPatchAt(&pD3D->lpVtbl->EnumAdapterModes, (char*)&patch, sizeof patch); return pD3D; }; @@ -423,7 +474,7 @@ void hook_gui() { hook("User32.dll", "ChangeDisplaySettingsExA", FakeChangeDisplaySettingsExA, NULL); hook("User32.dll", "SetCursor", FakeSetCursor, (void**)&TrueSetCursor); - if (PathFileExistsA("FREEGLUT.DLL")) { + if (PathFileExistsA("FREEGLUT.DLL") && FALSE) { // Hooked as a way to identify use of GLUT hook("FREEGLUT.DLL", "glutInitDisplayMode", Fake_glutInitDisplayMode, (void**)&True_glutInitDisplayMode); diff --git a/src/micetools/dll/hooks/gui.h b/src/micetools/dll/hooks/gui.h index aa7c7b2..eb42da2 100644 --- a/src/micetools/dll/hooks/gui.h +++ b/src/micetools/dll/hooks/gui.h @@ -43,5 +43,6 @@ void register_gui_hook(FnEndScene* end_scene); void hook_gui(); void setup_hud_gui(); +BOOL UnadjustWindowRect(LPRECT prc, DWORD dwStyle, BOOL fMenu); extern HWND mainWindow; extern DWORD changeCursorState; diff --git a/src/micetools/dll/hooks/logging.c b/src/micetools/dll/hooks/logging.c index 39ae84d..915100a 100644 --- a/src/micetools/dll/hooks/logging.c +++ b/src/micetools/dll/hooks/logging.c @@ -89,7 +89,7 @@ BOOL WINAPI FakeReportEventA(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD case EVENTLOG_INFORMATION_TYPE: default: for (int i = 0; i < wNumStrings; i++) - log_info(plfEvtlog, trim_string((char*)lpStrings[i])); + log_game(plfEvtlog, trim_string((char*)lpStrings[i])); break; } return TRUE; @@ -97,16 +97,17 @@ BOOL WINAPI FakeReportEventA(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD BOOL WINAPI FakeDeregisterEventSource(HANDLE hEventLog) { return TRUE; } -// static VOID(WINAPI* TrueOutputDebugStringA)(LPCSTR lpOutputString); -// VOID WINAPI FakeOutputDebugStringA(LPCSTR lpOutputString) { log_info("debug", "%s", -// lpOutputString); } +VOID WINAPI FakeOutputDebugStringA(LPCSTR lpOutputString) { + log_game(plfDebugLog, "%s", lpOutputString); +} void hook_logging() { // hook("MSVCR90.DLL", "printf", Fakeprintf, (void**)&Trueprintf); - // hook("MSVCR90.DLL", "fprintf", Fakefprintf, (void**)&Truefprintf); - // hook("MSVCR90.DLL", "fprintf_s", Fakefprintf_s, (void**)&Truefprintf_s); + hook("MSVCR90.DLL", "fprintf", Fakefprintf, (void**)&Truefprintf); + // hook("MSVCR90.DLL", "fprintf_s"\z, Fakefprintf_s, (void**)&Truefprintf_s); // hook("MSVCR90.DLL", "vfprintf_s", Fakevfprintf_s, (void**)&Truevfprintf_s); + hook("Kernel32.dll", "OutputDebugStringA", FakeOutputDebugStringA, NULL); hook("Advapi32.dll", "RegisterEventSourceA", FakeRegisterEventSourceA, (void**)&TrueRegisterEventSourceA); hook("Advapi32.dll", "ReportEventA", FakeReportEventA, (void**)&TrueReportEventA); diff --git a/src/micetools/dll/hooks/network.c b/src/micetools/dll/hooks/network.c index 2fcf27b..d00df4e 100644 --- a/src/micetools/dll/hooks/network.c +++ b/src/micetools/dll/hooks/network.c @@ -1,11 +1,73 @@ #include "network.h" +#pragma comment(lib, "ws2_32.lib") +#pragma comment(lib, "Dnsapi.lib") + +#define IP(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define NAOMINET IP(14, 128, 27, 15) +#define IB_NAOMINET IP(14, 128, 27, 9) +#define AIME_NAOMINET IP(112, 137, 187, 91) int WINAPI Fake_connect(SOCKET s, const SOCKADDR* name, int namelen) { ULONG addr = _byteswap_ulong(((SOCKADDR_IN*)name)->sin_addr.S_un.S_addr); USHORT port = _byteswap_ushort(((SOCKADDR_IN*)name)->sin_port); // Poorly exclude nxauth and mxgcatcher. - // TODO: better - if (port != 40190 && port != 40110 && port != 40102 && port != 40103) { + + if (addr == NAOMINET || addr == IB_NAOMINET || addr == AIME_NAOMINET) { + log_error( + plfNetwork, + "DANGER!! Game is attempting to connect to SEGA services! Check your configuration!"); + return -1; + } + + if (addr == IP(127, 0, 0, 1)) { + switch (port) { + case 40100: + case 40101: + log_info(plfNetwork, "connect(master)"); + break; + case 40102: + case 40103: + log_info(plfNetwork, "connect(installer)"); + break; + case 40104: + case 40105: + log_info(plfNetwork, "connect(network)"); + break; + case 40106: + case 40107: + log_info(plfNetwork, "connect(keychip)"); + break; + case 40108: + case 40112: + log_info(plfNetwork, "connect(gdeliver)"); + break; + case 40110: + case 40113: + log_info(plfNetwork, "connect(gcatcher)"); + break; + case 40114: + case 40115: + log_info(plfNetwork, "connect(storage)"); + break; + case 40190: + case 40191: + case 40192: + case 40193: + // nxAuth + break; + case 40194: + case 40195: + log_info(plfNetwork, "connect(nxmount)"); + break; + case 40196: + case 40197: + log_info(plfNetwork, "connect(nxrelay)"); + break; + default: + log_info(plfNetwork, "connect(localhost:%hu)", port); + break; + } + } else { log_info(plfNetwork, "connect(%hhu.%hhu.%hhu.%hhu:%hu)", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff, port); } @@ -94,11 +156,40 @@ dns INTERCEPT_DNS[] = { DNS_RECORDA dummy_record; +static BOOL _DoDnsLookup(PCSTR pszName, DWORD* lpResolved) { + DNS_STATUS status; + PDNS_RECORDA pDnsRecord = NULL; + + if (MiceConfig.network.upstream_dns_server) { + IP4_ARRAY upstreamDns; + upstreamDns.AddrCount = 1; + upstreamDns.AddrArray[0] = _byteswap_ulong(MiceConfig.network.upstream_dns_server); + status = TrueDnsQuery_A(pszName, DNS_TYPE_A, DNS_QUERY_BYPASS_CACHE, &upstreamDns, + &pDnsRecord, NULL); + } else { + status = TrueDnsQuery_A(pszName, DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL); + } + + if (status != 0) { + log_error(plfNetwork, "DNS(%s):FAILED", pszName); + return FALSE; + } + + if (pDnsRecord == NULL || pDnsRecord->wType != DNS_TYPE_A) { + log_error(plfNetwork, "DNS(%s):NXRECORD", pszName); + return FALSE; + } + if (lpResolved) *lpResolved = _byteswap_ulong(pDnsRecord->Data.A.IpAddress); + DnsRecordListFree(pDnsRecord, DnsFreeRecordListDeep); + return TRUE; +} + DNS_STATUS WINAPI FakeDnsQuery_A(PCSTR pszName, WORD wType, DWORD Options, PVOID pExtra, PDNS_RECORDA* ppQueryResults, PVOID* pReserved) { if (ppQueryResults) { for (size_t i = 0; i < sizeof INTERCEPT_DNS / sizeof INTERCEPT_DNS[0]; i++) { - if (strcmp(pszName, INTERCEPT_DNS[i].name) == 0) { + if (INTERCEPT_DNS[i].address && *INTERCEPT_DNS[i].address && + strcmp(pszName, INTERCEPT_DNS[i].name) == 0) { printf("%08x\n", MiceConfig.network.naominet_jp); log_info(plfNetwork, "DNS Replacing %s with %08x", pszName, @@ -115,21 +206,42 @@ DNS_STATUS WINAPI FakeDnsQuery_A(PCSTR pszName, WORD wType, DWORD Options, PVOID } } log_info(plfNetwork, "DNS passthrough for %s", pszName); + + if (MiceConfig.network.upstream_dns_server) { + IP4_ARRAY upstreamDns; + upstreamDns.AddrCount = 1; + upstreamDns.AddrArray[0] = _byteswap_ulong(MiceConfig.network.upstream_dns_server); + return TrueDnsQuery_A(pszName, wType, DNS_QUERY_BYPASS_CACHE, &upstreamDns, ppQueryResults, + pReserved); + } return TrueDnsQuery_A(pszName, wType, Options, pExtra, ppQueryResults, pReserved); -}; +} INT WSAAPI FakeWSAStringToAddressA(LPSTR AddressString, INT AddressFamily, LPWSAPROTOCOL_INFOA lpProtocolInfo, LPSOCKADDR lpAddress, LPINT lpAddressLength) { log_misc(plfNetwork, "WSA DNS lookup for %s", AddressString); - for (size_t i = 0; i < sizeof INTERCEPT_DNS / sizeof INTERCEPT_DNS[0]; i++) { - if (strcmp(AddressString, INTERCEPT_DNS[i].name) == 0) { - log_info(plfNetwork, "WSA DNS Replacing %s with %08x", AddressString, - *INTERCEPT_DNS[i].address); + if (AddressFamily == AF_INET) { + for (size_t i = 0; i < sizeof INTERCEPT_DNS / sizeof INTERCEPT_DNS[0]; i++) { + if (INTERCEPT_DNS[i].address && *INTERCEPT_DNS[i].address && + strcmp(AddressString, INTERCEPT_DNS[i].name) == 0) { + log_info(plfNetwork, "WSA DNS Replacing %s with %08x", AddressString, + *INTERCEPT_DNS[i].address); + + lpAddress->sa_family = AF_INET; + PULONG addr = &(((SOCKADDR_IN*)lpAddress)->sin_addr.S_un.S_addr); + *addr = _byteswap_ulong(*INTERCEPT_DNS[i].address); + return ERROR_SUCCESS; + } + } + + DWORD nResolved; + if (_DoDnsLookup(AddressString, &nResolved)) { + log_info(plfNetwork, "WSA DNS Replacing %s with %08x", AddressString, nResolved); lpAddress->sa_family = AF_INET; PULONG addr = &(((SOCKADDR_IN*)lpAddress)->sin_addr.S_un.S_addr); - *addr = _byteswap_ulong(*INTERCEPT_DNS[i].address); + *addr = _byteswap_ulong(nResolved); return ERROR_SUCCESS; } } @@ -140,11 +252,13 @@ INT WSAAPI FakeWSAStringToAddressA(LPSTR AddressString, INT AddressFamily, INT WINAPI Fake_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, const ADDRINFOA* pHints, PADDRINFOA* ppResult) { + char szNewAddress[16]; + if (!pNodeName) return True_getaddrinfo(pNodeName, pServiceName, pHints, ppResult); for (size_t i = 0; i < sizeof INTERCEPT_DNS / sizeof INTERCEPT_DNS[0]; i++) { - if (strcmp(pNodeName, INTERCEPT_DNS[i].name) == 0) { - char szNewAddress[16]; + if (INTERCEPT_DNS[i].address && *INTERCEPT_DNS[i].address && + strcmp(pNodeName, INTERCEPT_DNS[i].name) == 0) { sprintf_s(szNewAddress, sizeof szNewAddress, "%d.%d.%d.%d", (*INTERCEPT_DNS[i].address >> 24) & 0xff, (*INTERCEPT_DNS[i].address >> 16) & 0xff, @@ -159,8 +273,19 @@ INT WINAPI Fake_getaddrinfo(PCSTR pNodeName, PCSTR pServiceName, const ADDRINFOA // Exclude a few known services if (strcmp(pNodeName, "0.0.0.0") != 0 && strcmp(pNodeName, "127.0.0.0") != 0 && - strcmp(pNodeName, "240.0.0.0") != 0) + strcmp(pNodeName, "240.0.0.0") != 0) { + // Try doing a lookup using our upstream DNS service + DWORD nResolved; + if (_DoDnsLookup(pNodeName, &nResolved)) { + sprintf_s(szNewAddress, sizeof szNewAddress, "%d.%d.%d.%d", (nResolved >> 24) & 0xff, + (nResolved >> 16) & 0xff, (nResolved >> 8) & 0xff, nResolved & 0xff); + szNewAddress[sizeof szNewAddress - 1] = '\0'; + + return True_getaddrinfo(szNewAddress, pServiceName, pHints, ppResult); + } + log_info(plfNetwork, "GAI DNS passthrough for %s:%s", pNodeName, pServiceName); + } return True_getaddrinfo(pNodeName, pServiceName, pHints, ppResult); } @@ -235,7 +360,7 @@ void hook_network() { hook("Ws2_32.dll", "socket", Fake_socket, (void**)&True_socket); hook("Ws2_32.dll", "bind", Fake_bind, (void**)&True_bind); hook("Ws2_32.dll", "getaddrinfo", Fake_getaddrinfo, (void**)&True_getaddrinfo); - // hook("Ws2_32.dll", "sendto", Fake_sendto, (void**)&True_sendto); + hook("Ws2_32.dll", "sendto", Fake_sendto, (void**)&True_sendto); hook("Ws2_32.dll", "WSAStringToAddressA", FakeWSAStringToAddressA, (void**)&TrueWSAStringToAddressA); hook("Iphlpapi.dll", "GetIfTable", FakeGetIfTable, (void**)&TrueGetIfTable); diff --git a/src/micetools/dll/hooks/processes.c b/src/micetools/dll/hooks/processes.c index 5521e5b..22e09dc 100644 --- a/src/micetools/dll/hooks/processes.c +++ b/src/micetools/dll/hooks/processes.c @@ -1,5 +1,7 @@ #include "processes.h" +#include "../lib/mice/ipc.h" + BOOL WINAPI FakeCreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, @@ -14,6 +16,9 @@ BOOL WINAPI FakeCreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); } + // Bind everything to our one single console + dwCreationFlags &= ~CREATE_NEW_CONSOLE; + dwCreationFlags |= CREATE_NO_WINDOW; if (lpCommandLine && (strcmp(lpCommandLine, "s:\\mxkeychip.exe") == 0 || @@ -41,8 +46,15 @@ BOOL WINAPI FakeCreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, MiceFSRedirectPathA(lpApplicationName, &lpApplicationName); log_info(plfProcesses, "CreateProcessA %s %s", lpApplicationName, lpCommandLine); - return start_and_inject(INVALID_HANDLE_VALUE, lpApplicationName, lpCommandLine, MICELIB, FALSE, - 0, NULL, 0, lpProcessInformation); + if (!_miceIpcData->m_LauncherIsReady || _miceIpcData->m_MiceDll[0] == '\0') { + log_error(plfProcesses, "MiceLIB not provided via IPC!"); + return FALSE; + } + + log_info(plfProcesses, "Spawning \"%s\" %s", lpApplicationName, lpCommandLine); + return start_and_inject(INVALID_HANDLE_VALUE, lpApplicationName, lpCommandLine, + _miceIpcData->m_MiceDll, FALSE, 0, NULL, dwCreationFlags, + lpProcessInformation); } BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, @@ -50,23 +62,26 @@ BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { - log_info(plfProcesses, "CreateProcessW %ls %ls", lpApplicationName, lpCommandLine); - // if (lpCommandLine && (wcscmp(lpCommandLine, L"chkdsk.exe") == 0 || // wcscmp(lpCommandLine, L"ALLNetProc_Win.exe") == 0 || // wcscmp(lpCommandLine, L"ALLNetProc_Ring.exe") == 0 || // wcscmp(lpCommandLine, L"ALLNetProc.exe") == 0)) { - if (lpCommandLine && (wcscmp(lpCommandLine, L"chkdsk.exe") == 0)) { + if (lpCommandLine && (_wcsnicmp(lpCommandLine, L"chkdsk.exe", 10) == 0)) { if (lpProcessInformation) { lpProcessInformation->hProcess = CreateEventA(NULL, FALSE, TRUE, NULL); lpProcessInformation->hThread = CreateEventA(NULL, FALSE, TRUE, NULL); } return TRUE; } + // Bind everything to our one single console + dwCreationFlags &= ~CREATE_NEW_CONSOLE; + dwCreationFlags |= CREATE_NO_WINDOW; int nMultiChars = WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, NULL, 0, NULL, NULL); - LPSTR commandLine = malloc(nMultiChars); + LPSTR commandLine = malloc(nMultiChars + 1); + commandLine[0] = '\0'; WideCharToMultiByte(CP_ACP, 0, lpCommandLine, -1, commandLine, nMultiChars, NULL, NULL); + commandLine[nMultiChars] = '\0'; CHAR szApplicationName[MAX_PATH + 1]; LPCSTR lpApplicationNameA; @@ -88,8 +103,16 @@ BOOL WINAPI FakeCreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, } MiceFSRedirectPathA(lpApplicationNameA, &lpApplicationNameA); - return start_and_inject(INVALID_HANDLE_VALUE, lpApplicationNameA, commandLine, MICELIB, FALSE, - 0, NULL, 0, lpProcessInformation); + + if (!_miceIpcData->m_LauncherIsReady || _miceIpcData->m_MiceDll[0] == '\0') { + log_error(plfProcesses, "MiceLIB not provided via IPC!"); + return FALSE; + } + + log_info(plfProcesses, "Spawning \"%s\" %s", lpApplicationNameA, commandLine); + return start_and_inject(INVALID_HANDLE_VALUE, lpApplicationNameA, commandLine, + _miceIpcData->m_MiceDll, FALSE, 0, NULL, dwCreationFlags, + lpProcessInformation); } BOOL WINAPI FakeGetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) { diff --git a/src/micetools/dll/hooks/setupapi.c b/src/micetools/dll/hooks/setupapi.c index 8c18d56..8adbf35 100644 --- a/src/micetools/dll/hooks/setupapi.c +++ b/src/micetools/dll/hooks/setupapi.c @@ -71,13 +71,13 @@ BOOL WINAPI FakeSetupDiGetDeviceInterfaceDetailA(HDEVINFO DeviceInfoSet, PSP_DEV log_misc(plfSetupAPI, "Intercepted SetupDiGetDeviceInterfaceDetailA"); const WCHAR* res = (WCHAR*)DeviceInterfaceData->Reserved; - int new_len = (wcslen(res) + 1) * (sizeof *res); + size_t new_len = (wcslen(res) + 1) * (sizeof *res); char* new_res = (char*)malloc(new_len); size_t convertedChars = 0; wcstombs_s(&convertedChars, new_res, new_len, res, _TRUNCATE); size_t len = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath) + convertedChars; - if (RequiredSize) *RequiredSize = len; + if (RequiredSize) *RequiredSize = len & 0xffffffff; if (!DeviceInterfaceDetailData && DeviceInterfaceDetailDataSize < len) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return FALSE; diff --git a/src/micetools/dll/hooks/system.c b/src/micetools/dll/hooks/system.c index ecb3dfe..8944229 100644 --- a/src/micetools/dll/hooks/system.c +++ b/src/micetools/dll/hooks/system.c @@ -66,7 +66,9 @@ LONG WINAPI FakeRtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation) { // TODO: We should probably handle libamv_amd.dll at some point too HMODULE WINAPI FakeLoadLibraryA(LPCSTR lpLibFileName) { - if (strcmp(lpLibFileName, "libamv_nvidia.dll") == 0) { + if (_stricmp(lpLibFileName, "libamv_nvidia.dll") == 0 || + _stricmp(lpLibFileName, "libamv_amd.dll") == 0 || + _stricmp(lpLibFileName, "atipdlxx.dll") == 0) { return TrueLoadLibraryA(MICELIB); } return TrueLoadLibraryA(lpLibFileName); @@ -74,7 +76,9 @@ HMODULE WINAPI FakeLoadLibraryA(LPCSTR lpLibFileName) { #define WIDEN2(x) L##x #define WIDEN(x) WIDEN2(x) HMODULE WINAPI FakeLoadLibraryW(LPCWSTR lpLibFileName) { - if (wcscmp(lpLibFileName, L"libamv_nvidia.dll") == 0) { + if (_wcsicmp(lpLibFileName, L"libamv_nvidia.dll") == 0 || + _wcsicmp(lpLibFileName, L"libamv_amd.dll") == 0 || + _wcsicmp(lpLibFileName, L"atipdlxx.dll") == 0) { return TrueLoadLibraryW(WIDEN(MICELIB)); } return TrueLoadLibraryW(lpLibFileName); diff --git a/src/micetools/dll/input.c b/src/micetools/dll/input.c new file mode 100644 index 0000000..591cac1 --- /dev/null +++ b/src/micetools/dll/input.c @@ -0,0 +1,230 @@ +#include "input.h" +#pragma comment(lib, "dinput8.lib") +#pragma comment(lib, "dxguid.lib") +#include +#include +#include + +#include "../lib/mice/log.h" + +static PMICE_DI_DEVICE _MiceDIDevices = NULL; +static LPDIRECTINPUT8A _MiceDI = NULL; + +void MiceInputPollDevices(void) { + PMICE_DI_DEVICE lpDevice = _MiceDIDevices; + while (lpDevice != NULL) { + lpDevice->m_lpInput->lpVtbl->Poll(lpDevice->m_lpInput); + + switch (lpDevice->m_dwType) { + case DI8DEVTYPE_JOYSTICK: + case DI8DEVTYPE_GAMEPAD: + lpDevice->m_lpInput->lpVtbl->GetDeviceState(lpDevice->m_lpInput, + sizeof lpDevice->m_State.m_Joystate, + &lpDevice->m_State.m_Joystate); + break; + } + + lpDevice = lpDevice->m_Next; + } +} +void MiceInputFreeDevices(void) { + PMICE_DI_DEVICE lpDevice = _MiceDIDevices; + while (lpDevice != NULL) { + lpDevice->m_lpInput->lpVtbl->Unacquire(lpDevice->m_lpInput); + lpDevice->m_lpInput->lpVtbl->Release(lpDevice->m_lpInput); + + PMICE_DI_DEVICE next = lpDevice->m_Next; + free(lpDevice); + lpDevice = next; + } + _MiceDIDevices = NULL; +} + +BOOL MiceInputGetNewBinding(PMICE_BUTTON_BINDING lpBinding) { + MiceInputPollDevices(); + + PMICE_DI_DEVICE lpDevice = _MiceDIDevices; + while (lpDevice != NULL) { + PMICE_DI_STATE state = &lpDevice->m_State; + PMICE_DI_STATE lastState = &lpDevice->m_LastState; + if (lpBinding != NULL) { + switch (lpDevice->m_dwType) { + case DI8DEVTYPE_JOYSTICK: + case DI8DEVTYPE_GAMEPAD: + for (int button = 0; button < 32; button++) { + if (state->m_Joystate.rgbButtons[button] && + !lastState->m_Joystate.rgbButtons[button]) { + lpBinding->m_Type = MICE_BB_TYPE_DI_JOY; + lpBinding->m_DIJoy.m_lpDeviceGuid = lpDevice->m_GUID; + lpBinding->m_DIJoy.m_ButtonMajor = MICE_BUTTON_MAJOR_BUTTON; + lpBinding->m_DIJoy.m_ButtonMinor = button; + return TRUE; + } + } + + if (state->m_Joystate.rgdwPOV[0] != lastState->m_Joystate.rgdwPOV[0]) { + BOOL bound = FALSE; + int to = -1; + if (state->m_Joystate.rgdwPOV[0] == 0) { + to = MICE_DPAD_UP; + bound = TRUE; + } else if (state->m_Joystate.rgdwPOV[0] == 18000) { + to = MICE_DPAD_DOWN; + bound = TRUE; + } else if (state->m_Joystate.rgdwPOV[0] == 27000) { + to = MICE_DPAD_LEFT; + bound = TRUE; + } else if (state->m_Joystate.rgdwPOV[0] == 9000) { + to = MICE_DPAD_RIGHT; + bound = TRUE; + } + if (bound) { + lpBinding->m_Type = MICE_BB_TYPE_DI_JOY; + lpBinding->m_DIJoy.m_lpDeviceGuid = lpDevice->m_GUID; + lpBinding->m_DIJoy.m_ButtonMajor = MICE_BUTTON_MAJOR_DPAD; + lpBinding->m_DIJoy.m_ButtonMinor = to; + return TRUE; + } + } + + // TODO: Axes + break; + } + } + memcpy(lastState, state, sizeof *lastState); + + lpDevice = lpDevice->m_Next; + } + + // Skip the mouse buttons + for (int i = 8; i < 0xff; i++) { + if (GetAsyncKeyState(i) < 0) { + lpBinding->m_AsyncKey.m_Key = (CHAR)i; + lpBinding->m_Type = MICE_BB_TYPE_GET_ASYNC_KEY; + return TRUE; + } + } + + return FALSE; +} + +static BOOL CALLBACK _DInputEnum(LPDIDEVICEINSTANCEA lpddi, LPVOID data) { + PMICE_DI_DEVICE lpTail = _MiceDIDevices; + PMICE_DI_DEVICE lpNew; + if (lpTail == NULL) { + lpNew = _MiceDIDevices = malloc(sizeof *lpNew); + } else { + while (lpTail->m_Next != NULL) lpTail = lpTail->m_Next; + lpNew = lpTail->m_Next = malloc(sizeof *lpNew); + } + + ZeroMemory(lpNew, sizeof *lpNew); + lpNew->m_GUID = lpddi->guidInstance; + lpNew->m_dwType = lpddi->dwDevType & 0xff; + memcpy_s(lpNew->m_szName, sizeof lpNew->m_szName, lpddi->tszProductName, + sizeof lpddi->tszProductName); + lpNew->m_szName[sizeof lpNew->m_szName - 1] = '\0'; + + printf("Found device: %s\n", lpNew->m_szName); + + return DIENUM_CONTINUE; +} +int MiceInputGrabDevices(HWND hWnd) { + if (_MiceDI == NULL) return -2; + + // The first argument here is throwing warnings +#pragma warning(disable : 4090 4028) + _MiceDI->lpVtbl->EnumDevices(_MiceDI, DI8DEVTYPE_JOYSTICK, _DInputEnum, NULL, + DIEDFL_ATTACHEDONLY); + _MiceDI->lpVtbl->EnumDevices(_MiceDI, DI8DEVTYPE_GAMEPAD, _DInputEnum, NULL, + DIEDFL_ATTACHEDONLY); +#pragma warning(default : 4090 4028) + + PMICE_DI_DEVICE lpDevice = _MiceDIDevices; + while (lpDevice != NULL) { + _MiceDI->lpVtbl->CreateDevice(_MiceDI, &lpDevice->m_GUID, &lpDevice->m_lpInput, NULL); + + switch (lpDevice->m_dwType) { + case DI8DEVTYPE_GAMEPAD: + case DI8DEVTYPE_JOYSTICK: + lpDevice->m_lpInput->lpVtbl->SetCooperativeLevel( + lpDevice->m_lpInput, hWnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE); + lpDevice->m_lpInput->lpVtbl->SetDataFormat(lpDevice->m_lpInput, &c_dfDIJoystick); + lpDevice->m_lpInput->lpVtbl->Acquire(lpDevice->m_lpInput); + break; + } + lpDevice = lpDevice->m_Next; + } + + return 0; +} + +static BOOL _MiceInputHasInit = FALSE; +int MiceInputInit(HWND hWnd) { + if (_MiceInputHasInit) return 0; + _MiceInputHasInit = TRUE; + + if (_MiceDI == NULL) + if (!(SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, + &IID_IDirectInput8, (void **)&_MiceDI, NULL)))) { + log_error(plfMisc, "DirectInput8Create failed: %d", GetLastError()); + _MiceDI = NULL; + return -1; + } + return MiceInputGrabDevices(hWnd); +} +void MiceInputDestroy(void) { + MiceInputFreeDevices(); + + _MiceDI->lpVtbl->Release(_MiceDI); + _MiceDI = NULL; + _MiceInputHasInit = FALSE; +} + +BOOL MiceInputGetButtonState(PMICE_BUTTON_BINDING lpBinding) { + BOOL pressed = FALSE; + switch (lpBinding->m_Type) { + case MICE_BB_TYPE_GET_ASYNC_KEY: { + pressed = (GetAsyncKeyState(lpBinding->m_AsyncKey.m_Key) < 0); + break; + } + case MICE_BB_TYPE_DI_JOY: { + PMICE_DI_DEVICE lpDevice = _MiceDIDevices; + while (lpDevice != NULL) { + if (IsEqualGUID(&lpDevice->m_GUID, &lpBinding->m_DIJoy.m_lpDeviceGuid)) break; + lpDevice = lpDevice->m_Next; + } + if (lpDevice == NULL) break; + + switch (lpBinding->m_DIJoy.m_ButtonMajor) { + case MICE_BUTTON_MAJOR_AXIS: + break; + case MICE_BUTTON_MAJOR_DPAD: { + DWORD pov = lpDevice->m_State.m_Joystate.rgdwPOV[0]; + switch (lpBinding->m_DIJoy.m_ButtonMinor) { + case MICE_DPAD_UP: + pressed = pov == 0 || pov == 4500 || pov == 31500; + break; + case MICE_DPAD_DOWN: + pressed = pov == 18000 || pov == 22500 || pov == 13500; + break; + case MICE_DPAD_LEFT: + pressed = pov == 27000 || pov == 31500 || pov == 22500; + break; + case MICE_DPAD_RIGHT: + pressed = pov == 9000 || pov == 4500 || pov == 13500; + break; + } + break; + } + case MICE_BUTTON_MAJOR_BUTTON: { + DWORD button = lpBinding->m_DIJoy.m_ButtonMinor; + if (button <= 32) pressed = lpDevice->m_State.m_Joystate.rgbButtons[button] > 0; + break; + } + } + } + } + if (lpBinding->m_Invert) pressed = !pressed; + return pressed; +} diff --git a/src/micetools/dll/input.h b/src/micetools/dll/input.h new file mode 100644 index 0000000..42e6ddb --- /dev/null +++ b/src/micetools/dll/input.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#define CINTERFACE +#define INITGUID +#define DIRECTINPUT_VERSION 0x0800 +#include + +typedef enum MICE_BUTTON_BINDING_TYPE { + MICE_BB_TYPE_UNBOUND, + MICE_BB_TYPE_GET_ASYNC_KEY, + MICE_BB_TYPE_DI_JOY, +} MICE_BUTTON_BINDING_TYPE; +typedef enum MICE_BUTTON_MAJOR { + MICE_BUTTON_MAJOR_AXIS, + MICE_BUTTON_MAJOR_DPAD, + MICE_BUTTON_MAJOR_BUTTON, +} MICE_BUTTON_MAJOR; +#define MICE_DPAD_UP 0 // POV == 0 +#define MICE_DPAD_DOWN 1 // POV = 18000 +#define MICE_DPAD_LEFT 2 // POV == 27000 +#define MICE_DPAD_RIGHT 3 // POV == 9000 + +typedef struct MICE_BUTTON_BINDING { + MICE_BUTTON_BINDING_TYPE m_Type; + BOOL m_Invert; + union { + struct { + CHAR m_Key; + } m_AsyncKey; + struct { + GUID m_lpDeviceGuid; + MICE_BUTTON_MAJOR m_ButtonMajor; + DWORD m_ButtonMinor; + } m_DIJoy; + }; +} MICE_BUTTON_BINDING, *PMICE_BUTTON_BINDING; + +typedef union MICE_DI_STATE { + DIJOYSTATE m_Joystate; +} MICE_DI_STATE, *PMICE_DI_STATE; + +typedef struct MICE_DI_DEVICE { + LPDIRECTINPUTDEVICE8A m_lpInput; + GUID m_GUID; + CHAR m_szName[MAX_PATH]; + DWORD m_dwType; + MICE_DI_STATE m_State; + MICE_DI_STATE m_LastState; + struct MICE_DI_DEVICE* m_Next; +} MICE_DI_DEVICE, *PMICE_DI_DEVICE; + +int MiceInputInit(HWND hWnd); +void MiceInputDestroy(void); + +int MiceInputGrabDevices(HWND hWnd); +void MiceInputPollDevices(void); +void MiceInputFreeDevices(void); + +BOOL MiceInputGetButtonState(PMICE_BUTTON_BINDING lpBinding); +BOOL MiceInputGetNewBinding(PMICE_BUTTON_BINDING lpBinding); diff --git a/src/micetools/dll/meson.build b/src/micetools/dll/meson.build index c651727..14cf0be 100644 --- a/src/micetools/dll/meson.build +++ b/src/micetools/dll/meson.build @@ -4,11 +4,12 @@ subdir('hooks') subdir('gui') shared_library( - 'mice', + get_option('win64') ? 'mice64' : 'mice', name_prefix: '', - vs_module_defs: 'mice.def', + vs_module_defs: get_option('win64') ? 'mice64.def' : 'mice.def', sources: [ - 'amvStub/amv.c', + 'dllStub/amv.c', + 'dllStub/ati.c', 'util/misc.c', 'util/hook.c', @@ -21,11 +22,12 @@ shared_library( 'comdevice.c', 'games.c', + 'input.c', + 'micefs.c', 'dllmain.c', ], link_with: [ - dmi_lib, mice_lib, amiTimer, amiMd5, @@ -37,6 +39,7 @@ shared_library( ], dependencies: [ cimgui.get_variable('cimgui_dep'), + detours.get_variable('detours_dep'), # freeglut_lib, # forces the use of freeglut.dll! ] ) diff --git a/src/micetools/dll/mice64.def b/src/micetools/dll/mice64.def new file mode 100644 index 0000000..9d7088f --- /dev/null +++ b/src/micetools/dll/mice64.def @@ -0,0 +1 @@ +LIBRARY mice64 diff --git a/src/micetools/lib/mice/micefs.c b/src/micetools/dll/micefs.c similarity index 63% rename from src/micetools/lib/mice/micefs.c rename to src/micetools/dll/micefs.c index 88a78be..cca070d 100644 --- a/src/micetools/lib/mice/micefs.c +++ b/src/micetools/dll/micefs.c @@ -6,9 +6,7 @@ #include #include -// TODO: This is awful -extern BOOL(WINAPI* TruePathFileExistsA)(LPCSTR pszPath); -#define _PathFileExistsA (TruePathFileExistsA ? TruePathFileExistsA : PathFileExistsA) +#include "hooks/files.h" static char miceFSCwd[MAX_PATH + 1]; @@ -51,7 +49,7 @@ static volatile LONG miceFsLockCount = 0; BOOL MiceFSInit(VOID) { // By not using MiceFSSetCwd we can save a buffer allocation - GetCurrentDirectoryA(sizeof miceFSCwd, miceFSCwd); + _GetCurrentDirectoryA(sizeof miceFSCwd, miceFSCwd); return TRUE; } @@ -114,58 +112,113 @@ BOOL MiceFSAddDevLayers(VOID) { CHAR szMountPoint[4]; CHAR szTargetPath[MAX_PATH + 1]; ZeroMemory(szTargetPath, sizeof szTargetPath); - strcpy_s(szTargetPath, sizeof szTargetPath, ".\\dev\\x\\"); + strcpy_s(szTargetPath, sizeof szTargetPath, MiceIpcRelativePath("dev\\x\\")); + DWORD index = strlen(szTargetPath) - 2; + + CHAR szCurrentDir[MAX_PATH + 1]; + _GetCurrentDirectoryA(sizeof szCurrentDir, szCurrentDir); + + // See below TODO + if (szCurrentDir[0] == RING_MOUNT_APPDATA[0]) { + log_error(plfBoot, + "Booting on the %s drive is currently unsupported. There's a potential for it to " + "do nasty things to your computer at the moment.", + RING_MOUNT_APPDATA); + exit(0); + } BOOL success = TRUE; for (int i = 0; i < 26; i++) { - if (i == 2) continue; // TODO: DON'T SKIP C + // TODO: DON'T SKIP C + /** + * Note: This is currently skipped because things such as DLLs, COM + * registrations, etc. are all stored on C. + * I have, however, run into an issue with a game (I forget which) + * where _not_ hooking C caused issues. + * This will likely require fine-grained whitelists. + */ + if (i == 2) continue; + // TODO: Cleaner way to not skip the current drive? + // Currently skipped because of games that compile shaders at runtime (eg SDCM), however + // this could cause issues if a game is running on a drive letter that we really need to + // hook. _Are_ there any drive letters that would prove fatal? Appdata, probably. To + // protect users from themselves, we're currently rejecting the use of Y: as a boot drive + // entirely. This is ugly, and I hate it very much, but I have bigger fish to fry for now. + if (i == szCurrentDir[0] - 'A' || i == szCurrentDir[0] - 'a') continue; szMountPoint[0] = 'A' + (char)i; szMountPoint[1] = ':'; szMountPoint[2] = '\\'; szMountPoint[3] = '\0'; - szTargetPath[6] = 'a' + (char)i; + szTargetPath[index] = 'a' + (char)i; success &= MiceFSAddLayer(szMountPoint, szTargetPath); } return success; } BOOL MiceFSAddRingedgeLayers(BOOL bIsOsupdate) { + /** + * Note: Layers are searched in reverse order. Most lookups will be served by: + * 1. original + * 2. pathch + * 3. extend + * so those are added last! + */ BOOL success = TRUE; - // Windows file systems - success &= MiceFSAddLayer(RING_MOUNT_OS, "mount/os"); - // success &= MiceFSAddLayer(RING_MOUNT_EXTEND, "mount/extend"); - success &= MiceFSAddLayer(RING_MOUNT_RECOVERY, "mount/os_recovery"); - - // TrueCrypt Volumes - success &= MiceFSAddLayer(RING_MOUNT_SYSTEM, "mount/system"); - success &= MiceFSAddLayer(RING_MOUNT_ORIGINAL, "mount/original"); - success &= MiceFSAddLayer(RING_MOUNT_PATCH, "mount/patch"); - if (bIsOsupdate) { - success &= MiceFSAddLayer(RING_MOUNT_OS_UPDATE, "mount/os_update"); - success &= MiceFSAddLayer(RING_MOUNT_OS_DEFAULT_DRVIERS, "mount/os_default_drivers"); - } else { - // success &= MiceFSAddLayer(RING_MOUNT_EXTEND_VOL, "mount/extend/extend"); - // success &= MiceFSAddLayer(RING_MOUNT_EXTEND2_VOL, "mount/extend/extend2"); - } - - // geminifs - success &= MiceFSAddLayer(RING_MOUNT_GAME, "mount/original"); - success &= MiceFSAddLayer(RING_MOUNT_GAME, "mount/patch"); - - // External media - success &= MiceFSAddLayer(RING_MOUNT_APM, "mount/apm"); - success &= MiceFSAddLayer(RING_MOUNT_AM_LOG, "mount/am_log"); - success &= MiceFSAddLayer(RING_MOUNT_DVD, "mount/dvd"); - success &= MiceFSAddLayer(RING_MOUNT_DEV, "mount/dev"); - // TODO: Configurable! MiceFSAddLayer("R:", "H:\\Arcades\\USBs\\FiNALE_DL\\usb"); MiceFSAddLayer("S:", "C:\\Users\\Nathan\\Desktop\\ac_research\\sega\\S-Drive"); MiceFSAddLayer("C:\\System\\Execute", "C:\\Users\\Nathan\\Desktop\\ac_research\\sega\\S-Drive"); + // Windows file systems + // TODO: See note about the C: drive above + // success &= MiceFSAddLayer(RING_MOUNT_OS, MiceIpcRelativePath("mount\\os")); + // success &= MiceFSAddLayer(RING_MOUNT_APPDATA, MiceIpcRelativePath("mount\\extend")); + success &= MiceFSAddLayer(RING_MOUNT_RECOVERY, MiceIpcRelativePath("mount\\os_recovery")); + + // External media + success &= MiceFSAddLayer(RING_MOUNT_APM, MiceIpcRelativePath("mount\\apm")); + success &= MiceFSAddLayer(RING_MOUNT_AM_LOG, MiceIpcRelativePath("mount\\am_log")); + success &= MiceFSAddLayer(RING_MOUNT_DVD, MiceIpcRelativePath("mount\\dvd")); + success &= MiceFSAddLayer(RING_MOUNT_DEV, MiceIpcRelativePath("mount\\dev")); + + success &= MiceFSAddLayer("C:\\Documents and Settings\\AppUser", + MiceIpcRelativePath("mount\\appuser")); + success &= MiceFSAddLayer("C:\\Documents and Settings\\SystemUser", + MiceIpcRelativePath("mount\\systemuser")); + + success &= + MiceFSAddLayer("C:\\DOCUME~1\\SYSTEM~1\\LOCALS~1\\Temp\\", MiceIpcRelativePath("temp")); + + // TrueCrypt Volumes + success &= MiceFSAddLayer(RING_MOUNT_SYSTEM, MiceIpcRelativePath("mount\\system")); + if (bIsOsupdate) { + success &= MiceFSAddLayer(RING_MOUNT_OS_UPDATE, MiceIpcRelativePath("mount\\os_update")); + success &= MiceFSAddLayer(RING_MOUNT_OS_DEFAULT_DRVIERS, + MiceIpcRelativePath("mount\\os_default_drivers")); + } else { + // success &= MiceFSAddLayer(RING_MOUNT_EXTEND_VOL, + // MiceIpcRelativePath("mount\\extend/extend")); success &= + // MiceFSAddLayer(RING_MOUNT_EXTEND2_VOL, MiceIpcRelativePath("mount\\extend/extend2")); + } + success &= MiceFSAddLayer(RING_MOUNT_PATCH, MiceIpcRelativePath("mount\\patch")); + success &= MiceFSAddLayer(RING_MOUNT_ORIGINAL, MiceIpcRelativePath("mount\\original")); + + // geminifs + success &= MiceFSAddLayer(RING_MOUNT_GAME, MiceIpcRelativePath("mount\\patch")); + success &= MiceFSAddLayer(RING_MOUNT_GAME, MiceIpcRelativePath("mount\\original")); + + // Make directories that we're going to need + make_dirs(MiceIpcRelativePath("mount\\appuser\\temp")); + make_dirs(MiceIpcRelativePath("temp")); + // Directories games read and write + make_dirs(MiceIpcRelativePath("dev\\w")); + make_dirs(MiceIpcRelativePath("dev\\v")); + make_dirs(MiceIpcRelativePath("dev\\y")); + make_dirs(MiceIpcRelativePath("dev\\x")); + return success; } @@ -288,14 +341,17 @@ BOOL MiceFSRedirectPathA(LPCSTR lpFilename, LPCSTR* pszRedirected) { PMICE_FS_LAYER layer = miceFsRoot.m_Next; CHAR szTail[MAX_PATH + 1]; + wchar_t szwPath[MAX_PATH + 1]; BOOL bFound = FALSE; while (layer) { if (MiceFSMatchPathA(szExpanded, layer->m_lpMountPoint, szTail, sizeof szTail)) { _redirected_path[0] = '\0'; PathCombineA(_redirected_path, layer->m_lpTargetPath, szTail); - *pszRedirected = _redirected_path; + if (pszRedirected) *pszRedirected = _redirected_path; - if (_PathFileExistsA(_redirected_path)) { + MultiByteToWideChar(0, 0, _redirected_path, -1, szwPath, _countof(szwPath)); + + if (FileExistsW(szwPath)) { MiceReleaseLockShared(miceFsLockCount); return TRUE; } @@ -320,6 +376,56 @@ BOOL MiceFSRedirectPathW(LPCWSTR lpFileName, LPCWSTR* pszRedirected) { if (!bRedirected) return FALSE; MultiByteToWideChar(CP_ACP, 0, szRedirected, -1, _redirected_path_w, sizeof _redirected_path_w); - *pszRedirected = _redirected_path_w; + if (pszRedirected) *pszRedirected = _redirected_path_w; + return bRedirected; } + +void MiceFSDebugPrintLayers(void) { + MiceTakeLockShared(miceFsLockCount); + + PMICE_FS_LAYER lpLayer = &miceFsRoot; + while (lpLayer = lpLayer->m_Next) { + printf("%s -> %s\n", lpLayer->m_lpMountPoint, lpLayer->m_lpTargetPath); + } + + MiceReleaseLockShared(miceFsLockCount); +} +BOOL MiceFSDebugTraceRedirectPathA(LPCSTR lpFilename, LPCSTR* pszRedirected) { + printf("Tracing redirect of %s:\n", lpFilename); + CHAR szExpanded[MAX_PATH + 1]; + if (!MiceFSGetFullPathNameA(lpFilename, sizeof szExpanded, szExpanded, NULL)) { + printf(":: Redirect ended early with no action taken."); + return FALSE; + } + + MiceTakeLockShared(miceFsLockCount); + + PMICE_FS_LAYER layer = miceFsRoot.m_Next; + CHAR szTail[MAX_PATH + 1]; + wchar_t szwPath[MAX_PATH + 1]; + BOOL bFound = FALSE; + while (layer) { + if (MiceFSMatchPathA(szExpanded, layer->m_lpMountPoint, szTail, sizeof szTail)) { + _redirected_path[0] = '\0'; + PathCombineA(_redirected_path, layer->m_lpTargetPath, szTail); + if (pszRedirected) *pszRedirected = _redirected_path; + + MultiByteToWideChar(0, 0, _redirected_path, -1, szwPath, _countof(szwPath)); + + printf("-> %s\n", _redirected_path); + if (FileExistsW(szwPath)) { + MiceReleaseLockShared(miceFsLockCount); + printf(":: Redirect ended with valid redirection found."); + return TRUE; + } + bFound = TRUE; + } + + layer = layer->m_Next; + } + + MiceReleaseLockShared(miceFsLockCount); + printf(":: Redirect ended with no redirection found."); + return FALSE; +} diff --git a/src/micetools/lib/mice/micefs.h b/src/micetools/dll/micefs.h similarity index 89% rename from src/micetools/lib/mice/micefs.h rename to src/micetools/dll/micefs.h index 4b7785b..c445753 100644 --- a/src/micetools/lib/mice/micefs.h +++ b/src/micetools/dll/micefs.h @@ -3,7 +3,7 @@ #include #define RING_MOUNT_OS "C:" -#define RING_MOUNT_EXTEND "Y:" +#define RING_MOUNT_APPDATA "Y:" #define RING_MOUNT_RECOVERY "Z:" #define RING_MOUNT_SYSTEM "S:" #define RING_MOUNT_ORIGINAL "O:" @@ -71,7 +71,7 @@ VOID MiceFSSetCwd(LPCSTR lpNewCwd); BOOL MiceFSAddLayer(_In_z_ LPCSTR lpMountPoint, _In_z_ LPCSTR lpTargetPath); /** - * @brief Add layers for all drive letters backed by ".\dev\[letter]" + * @brief Add layers for all drive letters backed by ".\mice\dev\[letter]" * If used, this should be the first set of layers added, to act as a fallback * * @return BOOL Success. As this function adds 26 layers, and failiure could occur at any time, @@ -148,8 +148,17 @@ BOOL MiceFSMatchPathA(_In_z_ LPCSTR lpPath, _In_z_ LPCSTR lpPrefix, * @param pszRedirected Redirected real filename * @return BOOL If redirection occured */ -BOOL MiceFSRedirectPathA(_In_z_ LPCSTR lpFileName, _Out_ LPCSTR* pszRedirected); +BOOL MiceFSRedirectPathA(_In_z_ LPCSTR lpFileName, _Out_opt_ LPCSTR* pszRedirected); /** * @brief Wide variant of MiceFSRedirectPathA */ -BOOL MiceFSRedirectPathW(_In_z_ LPCWSTR lpFileName, _Out_ LPCWSTR* pszRedirected); +BOOL MiceFSRedirectPathW(_In_z_ LPCWSTR lpFileName, _Out_opt_ LPCWSTR* pszRedirected); + +/** + * @brief Output a list of all filesystem layers to stdout + */ +void MiceFSDebugPrintLayers(void); +/** + * @brief Perform MiceFSRedirectPathA, with trace logging to stdout + */ +BOOL MiceFSDebugTraceRedirectPathA(_In_z_ LPCSTR lpFileName, _Out_opt_ LPCSTR* pszRedirected); diff --git a/src/micetools/dll/util/_util.h b/src/micetools/dll/util/_util.h index 6ad7909..9919278 100644 --- a/src/micetools/dll/util/_util.h +++ b/src/micetools/dll/util/_util.h @@ -6,7 +6,8 @@ #define HDATA_FIND_VOLUME 1 #define HDATA_ANY 0xFFFFFFFF -BOOL FileExists(wchar_t* szPath); +BOOL FileExistsW(const wchar_t* szPath); +BOOL FileExistsA(const char* szPath); PVOID GetDataForHandle(HANDLE hObject, DWORD type); void SetDataForHandle(HANDLE hObject, DWORD type, PVOID pData, BOOL isHeap); BOOL RemoveDataForHandle(HANDLE hObject, DWORD type); @@ -24,7 +25,7 @@ char GetGamedataDrive(void); BOOL IsGamedataLocalPath(LPCSTR path); void make_dirs(const char* path); -void* open_mapped_file(LPCWSTR path, DWORD size, HANDLE* file, HANDLE* file_mapping); +void* open_mapped_file(LPCSTR path, DWORD size, HANDLE* file, HANDLE* file_mapping); #define char_lower(value) (('A' <= (value) && (value) <= 'Z') ? ((value) - 'A' + 'a') : (value)) #define char_upper(value) (('a' <= (value) && (value) <= 'z') ? ((value) - 'a' + 'A') : (value)) diff --git a/src/micetools/dll/util/hook.c b/src/micetools/dll/util/hook.c index 5d1b016..89b465b 100644 --- a/src/micetools/dll/util/hook.c +++ b/src/micetools/dll/util/hook.c @@ -1,5 +1,6 @@ #include "hook.h" +#include #include #include #include @@ -28,132 +29,176 @@ void hook(LPCSTR dll, LPCSTR name, void* patch, void** store) { append_hook(hook); } -void patch_at(PVOID addr, const char* patch, DWORD length) { +DWORD MiceGetImageBase(void) { + static DWORD imageBase = 0; + if (imageBase != 0) return imageBase; + + WCHAR sModulePath[MAX_PATH]; + GetModuleFileNameW(NULL, sModulePath, MAX_PATH); + + HANDLE hObject = + CreateFileW(sModulePath, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hObject == INVALID_HANDLE_VALUE) { + log_error(plfBoot, "Failed to open %ls %03x", sModulePath, GetLastError()); + return 0; + } + + IMAGE_DOS_HEADER dosHeader = { 0 }; + DWORD nRead; + SetFilePointer(hObject, 0, NULL, FILE_BEGIN); + ReadFile(hObject, &dosHeader, sizeof dosHeader, &nRead, NULL); + if (nRead != sizeof dosHeader) { + log_error(plfBoot, "Failed to read DOS header %03x", GetLastError()); + return 0; + } + if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) { + log_error(plfBoot, "Invalid DOS magic number: %04x", dosHeader.e_magic); + return 0; + } + + IMAGE_NT_HEADERS32 ntHeaders32 = { 0 }; + SetFilePointer(hObject, dosHeader.e_lfanew, NULL, FILE_BEGIN); + ReadFile(hObject, &ntHeaders32, sizeof ntHeaders32, &nRead, NULL); + if (nRead != sizeof ntHeaders32) { + log_error(plfBoot, "Failed to read NT header %03x", GetLastError()); + return 0; + } + if (ntHeaders32.Signature != IMAGE_NT_SIGNATURE) { + log_error(plfBoot, "Invalid NT signature: %08x", ntHeaders32.Signature); + return 0; + } + + CloseHandle(hObject); + return (imageBase = ntHeaders32.OptionalHeader.ImageBase); +} + +void MiceHookPatchAt(PVOID addr, const void* patch, DWORD length) { DWORD oldProt; VirtualProtect(addr, length, PAGE_EXECUTE_READWRITE, &oldProt); memcpy(addr, patch, length); VirtualProtect(addr, length, oldProt, &oldProt); } -void clear_at(PVOID addr, BYTE clearVal, DWORD length) { +void MiceHookClearAt(PVOID addr, BYTE clearVal, DWORD length) { DWORD oldProt; VirtualProtect(addr, length, PAGE_EXECUTE_READWRITE, &oldProt); memset(addr, clearVal, length); VirtualProtect(addr, length, oldProt, &oldProt); } -BOOL Detour(PVOID src, PVOID dst, const intptr_t len) { - if (len < 5) return FALSE; +// BOOL Detour(PVOID src, PVOID dst, const intptr_t len) { +// if (len < 5) return FALSE; - DWORD oldProt; - VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &oldProt); +// DWORD oldProt; +// VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &oldProt); - *(PCHAR)src = '\xE9'; - *(PINT)((int)src + 1) = (int)dst - (int)src - 5; +// *(PCHAR)src = '\xE9'; +// *(PINT)((size_t)src + 1) = size2int((size_t)dst - (size_t)src - 5); - VirtualProtect(src, len, oldProt, &oldProt); - return TRUE; -} +// VirtualProtect(src, len, oldProt, &oldProt); +// return TRUE; +// } -void* Win32HotpatchHook(PVOID src, PVOID dst) { - LPBYTE bSrc = (LPBYTE)src; +// void* Win32HotpatchHook(PVOID src, PVOID dst) { +// LPBYTE bSrc = (LPBYTE)src; - DWORD oldProt; - VirtualProtect(bSrc - 5, 7, PAGE_EXECUTE_READWRITE, &oldProt); +// DWORD oldProt; +// VirtualProtect(bSrc - 5, 7, PAGE_EXECUTE_READWRITE, &oldProt); - // relative JMP to dst - bSrc[-5] = 0xE9; - DWORD relJump = (DWORD)dst - (DWORD)src; - bSrc[-1] = (relJump >> 24) & 0xff; - bSrc[-2] = (relJump >> 16) & 0xff; - bSrc[-3] = (relJump >> 8) & 0xff; - bSrc[-4] = relJump & 0xff; - // JMP $-5 - bSrc[0] = 0xEB; - bSrc[1] = 0xF9; +// // relative JMP to dst +// bSrc[-5] = 0xE9; +// size_t relJump = (size_t)dst - (size_t)src; +// bSrc[-1] = (relJump >> 24) & 0xff; +// bSrc[-2] = (relJump >> 16) & 0xff; +// bSrc[-3] = (relJump >> 8) & 0xff; +// bSrc[-4] = relJump & 0xff; +// // JMP $-5 +// bSrc[0] = 0xEB; +// bSrc[1] = 0xF9; - VirtualProtect(bSrc - 5, 7, oldProt, &oldProt); +// VirtualProtect(bSrc - 5, 7, oldProt, &oldProt); - // We don't need a gateway; we can just jump right in - return bSrc + 2; -} +// // We don't need a gateway; we can just jump right in +// return bSrc + 2; +// } -#define CMP2(x, a, b) (x[0] == 0x##a && x[1] == 0x##b) -#define CMP3(x, a, b, c) (CMP2(x, a, b) && x[2] == 0x##c) -#define CMP4(x, a, b, c, d) (CMP3(x, a, b, c) && x[3] == 0x##d) -#define CMP5(x, a, b, c, d, e) (CMP4(x, a, b, c, d) && x[4] == 0x##e) -void* CreateHook32(PVOID src, PVOID dst) { - LPBYTE bSrc = (LPBYTE)src; +// #define CMP2(x, a, b) (x[0] == 0x##a && x[1] == 0x##b) +// #define CMP3(x, a, b, c) (CMP2(x, a, b) && x[2] == 0x##c) +// #define CMP4(x, a, b, c, d) (CMP3(x, a, b, c) && x[3] == 0x##d) +// #define CMP5(x, a, b, c, d, e) (CMP4(x, a, b, c, d) && x[4] == 0x##e) +// void* CreateHook32(PVOID src, PVOID dst) { +// LPBYTE bSrc = (LPBYTE)src; - // If this DLL is hotpatchable, sieze the opportunity - if (bSrc[0] == 0x8b && bSrc[1] == 0xff && bSrc[-1] == 0xCC && bSrc[-2] == 0xCC && - bSrc[-3] == 0xCC && bSrc[-4] == 0xCC && bSrc[-5] == 0xCC) { - return Win32HotpatchHook(src, dst); - } +// // If this DLL is hotpatchable, sieze the opportunity +// if (bSrc[0] == 0x8b && bSrc[1] == 0xff && bSrc[-1] == 0xCC && bSrc[-2] == 0xCC && +// bSrc[-3] == 0xCC && bSrc[-4] == 0xCC && bSrc[-5] == 0xCC) { +// return Win32HotpatchHook(src, dst); +// } - intptr_t len = 5; +// intptr_t len = 5; - // This is a very crude way to identify common instruction patterns - // to select or patch length. - if (CMP2(bSrc, ff, 25)) { - // jmp DWORD PTR ds:0x........ - len = 6; - } else if (bSrc[0] == 0x6a && bSrc[2] == 0x68) { - // push 0x... (byte) - // push 0x... (dword) - len = 7; - } else if (bSrc[0] == 0x6a && bSrc[2] == 0xb8) { - // push 0x... (byte) - // mov eax,0x... (dword) - len = 7; - } else if (bSrc[0] == 0x68) { - // push 0x... (dword) - len = 5; - } else if (CMP5(bSrc, 55, 8B, EC, 83, E4)) { - // pusb ebp - // mov ebp,esp - // and esp,ffffff** - len = 6; - } else if (CMP5(bSrc, 55, 8B, EC, 8B, 45)) { - // pusb ebp - // mov ebp,esp - // mov eax,DWORD PTR [...] - len = 6; - } else if (CMP5(bSrc, 55, 8B, EC, 80, 3D)) { - // pusb ebp - // mov ebp,esp - // cmd BYTE PTR ds:0x...,0x... - len = 10; - } else if (CMP5(bSrc, B8, 00, 10, 00, 00)) { - // mov eax,0x1000 - len = 5; - } else { - log_error(plfHooks, "Unable to identify gateway length! Function peek:"); - for (int i = 0; i < 16; i++) { - printf("%02x ", ((LPBYTE)src)[i]); - } - puts(""); - log_error(plfHooks, "Unsafely defaulting to 5!"); - len = 5; - } +// // This is a very crude way to identify common instruction patterns +// // to select or patch length. +// if (CMP2(bSrc, ff, 25)) { +// // jmp DWORD PTR ds:0x........ +// len = 6; +// } else if (bSrc[0] == 0x6a && bSrc[2] == 0x68) { +// // push 0x... (byte) +// // push 0x... (dword) +// len = 7; +// } else if (bSrc[0] == 0x6a && bSrc[2] == 0xb8) { +// // push 0x... (byte) +// // mov eax,0x... (dword) +// len = 7; +// } else if (bSrc[0] == 0x68) { +// // push 0x... (dword) +// len = 5; +// } else if (CMP5(bSrc, 55, 8B, EC, 83, E4)) { +// // pusb ebp +// // mov ebp,esp +// // and esp,ffffff** +// len = 6; +// } else if (CMP5(bSrc, 55, 8B, EC, 8B, 45)) { +// // pusb ebp +// // mov ebp,esp +// // mov eax,DWORD PTR [...] +// len = 6; +// } else if (CMP5(bSrc, 55, 8B, EC, 80, 3D)) { +// // pusb ebp +// // mov ebp,esp +// // cmd BYTE PTR ds:0x...,0x... +// len = 10; +// } else if (CMP5(bSrc, B8, 00, 10, 00, 00)) { +// // mov eax,0x1000 +// len = 5; +// } else { +// char hex_str[16 * 3 + 1]; +// hex_str[0] = 0; +// for (int i = 0; i < 16; i++) { +// snprintf(&hex_str[i * 3], 4, "%02x ", ((LPBYTE)src)[i]); +// } +// hex_str[sizeof hex_str - 1] = 0; +// log_error(plfHooks, "Unable to identify gateway length! Function peek: %s", hex_str); +// log_error(plfHooks, "Unsafely defaulting to 5!"); +// len = 5; +// } - PVOID gateway = VirtualAlloc(0, len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (!gateway) return NULL; +// PVOID gateway = VirtualAlloc(0, len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +// if (!gateway) return NULL; - memcpy(gateway, src, len); +// memcpy(gateway, src, len); - *(PCHAR)((int)gateway + len) = '\xE9'; - *(PINT)((int)gateway + len + 1) = (int)src - (int)gateway - 5; +// *(PCHAR)((size_t)gateway + len) = '\xE9'; +// *(PINT)((size_t)gateway + len + 1) = size2int((size_t)src - (size_t)gateway - 5); - Detour(src, dst, len); +// Detour(src, dst, len); - return gateway; -} +// return gateway; +// } -extern FARPROC (*TrueGetProcAddress)(HMODULE hModule, LPCSTR lpProcName); +// extern FARPROC (*TrueGetProcAddress)(HMODULE hModule, LPCSTR lpProcName); void setup_hooks() { - log_info(plfHooks, "attaching"); - + LONG detourError; if (hook_list == NULL) { log_warning(plfHooks, "No hooks to register!"); return; @@ -176,13 +221,27 @@ void setup_hooks() { if (original == NULL) { log_warning(plfHooks, "failed to get original %s (%03x)", hook->name, GetLastError()); } else { - void* gateway = CreateHook32(original, hook->patch); - if (hook->store != NULL) *hook->store = gateway; + // void* gateway = CreateHook(original, hook->patch); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + detourError = DetourAttach(&original, hook->patch); + if (detourError != NO_ERROR) { + log_error(plfHooks, "DetourAttach failed: %d", detourError); + DetourTransactionAbort(); + continue; + } + + detourError = DetourTransactionCommit(); + if (detourError != NO_ERROR) { + log_error(plfHooks, "DetourTransactionCommit failed: %d", detourError); + continue; + } + + if (hook->store != NULL) *hook->store = original; log_misc(plfHooks, "hooked %s", hook->name); } hook = hook->next; } while (hook != NULL); - - log_info(plfHooks, "attach complete"); } diff --git a/src/micetools/dll/util/hook.h b/src/micetools/dll/util/hook.h index f464548..d0289ee 100644 --- a/src/micetools/dll/util/hook.h +++ b/src/micetools/dll/util/hook.h @@ -10,11 +10,21 @@ typedef struct function_hook { struct function_hook* next; } function_hook_t; -static BOOL Detour(PVOID src, PVOID dst, const intptr_t len); +#define NOP 0x90 -void patch_at(PVOID addr, const char* patch, DWORD length); -void clear_at(PVOID addr, BYTE clearVal, DWORD length); -void* CreateHook32(PVOID src, PVOID dst); +DWORD MiceGetImageBase(void); + +void MiceHookPatchAt(PVOID addr, const void* patch, DWORD length); +#define MiceHookPatchAtImage(hModule, addr, patch, length) \ + MiceHookPatchAt((PVOID)((DWORD)(addr) + ((DWORD)(hModule)-MiceGetImageBase())), (patch), \ + (length)) +void MiceHookClearAt(PVOID addr, BYTE clearVal, DWORD length); +#define MiceHookClearAtImage(hModule, addr, clearVal, length) \ + MiceHookClearAt((PVOID)((DWORD)(addr) + ((DWORD)(hModule)-MiceGetImageBase())), (clearVal), \ + (length)) +#define MiceHookNopAt(addr, length) MiceHookClearAt((addr), NOP, (length)) +#define MiceHookNopAtImage(hModule, addr, length) \ + MiceHookClearAtImage((hModule), (addr), NOP, (length)) static void append_hook(function_hook_t* hook); void hook(LPCSTR dll, LPCSTR name, void* patch, void** store); diff --git a/src/micetools/dll/util/misc.c b/src/micetools/dll/util/misc.c index 96a2631..fd1137c 100644 --- a/src/micetools/dll/util/misc.c +++ b/src/micetools/dll/util/misc.c @@ -31,8 +31,14 @@ void PrintStack(void) { free(symbol); } -BOOL FileExists(wchar_t* szPath) { - DWORD dwAttrib = GetFileAttributesW(szPath); +BOOL FileExistsW(const wchar_t* szPath) { + DWORD dwAttrib = _GetFileAttributesW(szPath); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} +BOOL FileExistsA(const char* szPath) { + wchar_t szPathW[MAX_PATH + 1]; + MultiByteToWideChar(0, 0, szPath, -1, szPathW, _countof(szPathW)); + DWORD dwAttrib = _GetFileAttributesW(szPathW); return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } @@ -156,17 +162,17 @@ void make_dirs(const char* path) { free(temp); } -void* open_mapped_file(LPCWSTR lpFilename, DWORD size, HANDLE* file, HANDLE* file_mapping) { - char szFileName[MAX_PATH] = { 0 }; - WideCharToMultiByte(CP_ACP, 0, lpFilename, wcslen(lpFilename), szFileName, sizeof szFileName, - NULL, NULL); - make_dirs(szFileName); +void* open_mapped_file(LPCSTR lpFilename, DWORD size, HANDLE* file, HANDLE* file_mapping) { + // char szFileName[MAX_PATH] = { 0 }; + // WideCharToMultiByte(CP_ACP, 0, lpFilename, wcslen(lpFilename), szFileName, + // size2int(sizeof szFileName), NULL, NULL); + make_dirs(lpFilename); - *file = _CreateFileW(lpFilename, GENERIC_READ | GENERIC_WRITE, + *file = _CreateFileA(lpFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (*file == INVALID_HANDLE_VALUE) { - log_error(plfMisc, "Failed to CreateFileW(%ls): %d", lpFilename, GetLastError()); + log_error(plfMisc, "Failed to CreateFileW(%s): %d", lpFilename, GetLastError()); return NULL; } diff --git a/src/micetools/dll/util/path.c b/src/micetools/dll/util/path.c index 01b1a40..fdf2b2a 100644 --- a/src/micetools/dll/util/path.c +++ b/src/micetools/dll/util/path.c @@ -3,6 +3,8 @@ #include #include +#include "../hooks/files.h" + BOOL PathEqual(LPCSTR path1, LPCSTR path2) { char buffer1[MAX_PATH]; char buffer2[MAX_PATH]; @@ -24,10 +26,10 @@ BOOL PathPrefix(LPCSTR path, LPCSTR prefix) { static char WORKING_DIR[MAX_PATH + 1] = { 0 }; char GetGamedataDrive(void) { - if (WORKING_DIR[0] == 0x00) GetCurrentDirectoryA(sizeof WORKING_DIR, WORKING_DIR); + if (WORKING_DIR[0] == 0x00) _GetCurrentDirectoryA(sizeof WORKING_DIR, WORKING_DIR); return WORKING_DIR[0]; } BOOL IsGamedataLocalPath(LPCSTR path) { - if (WORKING_DIR[0] == 0x00) GetCurrentDirectoryA(sizeof WORKING_DIR, WORKING_DIR); + if (WORKING_DIR[0] == 0x00) _GetCurrentDirectoryA(sizeof WORKING_DIR, WORKING_DIR); return PathPrefix(path, WORKING_DIR); } diff --git a/src/micetools/launcher/locate.c b/src/micetools/launcher/locate.c index 6efacef..1e41da4 100644 --- a/src/micetools/launcher/locate.c +++ b/src/micetools/launcher/locate.c @@ -3,8 +3,14 @@ #include "../lib/mice/mice.h" const char* KNOWN_GAMES[] = { + // Ideally, we're running from the X: root + "maimai\\maimai_dump_.exe", + "maimai\\maimai_dump.exe", + "maimai\\Game.exe", // Preferentially use a decrypted dump if present "maimai_dump_.exe", + "maimai_dump.exe", + "Game.exe", // Specific games "DOA5A.exe", @@ -16,50 +22,12 @@ const char* KNOWN_GAMES[] = { "maimai.exe", }; -bool locate_file(char* path, size_t len, const char* exe) { - WIN32_FIND_DATA fdFile; - HANDLE hFind = NULL; - - char work_path[2048]; - snprintf(work_path, sizeof work_path, ".\\*.*"); - - if ((hFind = FindFirstFile(work_path, &fdFile)) == INVALID_HANDLE_VALUE) return false; - - do { - if (!strcmp(fdFile.cFileName, ".") || !strcmp(fdFile.cFileName, "..")) continue; - - if (fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; - - if (!strcmp(fdFile.cFileName, exe)) { - snprintf(path, len, ".\\%s", fdFile.cFileName); - FindClose(hFind); - return true; - } - } while (FindNextFile(hFind, &fdFile)); - - FindClose(hFind); - return false; -} - bool locate_game(char* path, size_t len) { - // This is slightly wasteful, but is needed to ensure priority! - for (size_t i = 0; i < ((sizeof KNOWN_GAMES) / (sizeof KNOWN_GAMES[0])); i++) { - if (locate_file(path, len, KNOWN_GAMES[i])) { + for (size_t i = 0; i < _countof(KNOWN_GAMES); i++) { + if (PathFileExistsA(KNOWN_GAMES[i])) { + snprintf(path, len, ".\\%s", KNOWN_GAMES[i]); return true; } } return false; } - -bool locate_library(char* path, size_t len) { - if (locate_file(path, len, MiceConfig.launcher.mice_dll)) return true; - - // Don't call DllMain! We don't want to hook ourself! - HANDLE mice = LoadLibraryExA(MiceConfig.launcher.mice_dll, NULL, DONT_RESOLVE_DLL_REFERENCES); - if (mice != NULL) { - GetModuleFileNameA(mice, path, len); - return true; - } - - return false; -} diff --git a/src/micetools/launcher/locate.h b/src/micetools/launcher/locate.h index fdfd806..1e20988 100644 --- a/src/micetools/launcher/locate.h +++ b/src/micetools/launcher/locate.h @@ -4,6 +4,4 @@ #include #include -bool locate_exe(char* path, size_t len, const char* exe); bool locate_game(char* path, size_t len); -bool locate_library(char* path, size_t len); diff --git a/src/micetools/launcher/main.c b/src/micetools/launcher/main.c index e5ef804..a047821 100644 --- a/src/micetools/launcher/main.c +++ b/src/micetools/launcher/main.c @@ -1,6 +1,7 @@ #include #include +#include "../lib/mice/ipc.h" #include "../lib/mice/mice.h" #include "../lib/util/pid.h" #include "locate.h" @@ -10,7 +11,10 @@ bool debug_wait = false; int boot_delay = 0; char exe_name[MAX_PATH + 1] = ""; -size_t nCommandLine = 256; +BYTE _MiceLauncherNext = 2; +HANDLE _MiceLauncherNextEvent = INVALID_HANDLE_VALUE; + +size_t nCommandLine = 0; char* commandLine = NULL; static void print_help(char* exe) { @@ -71,19 +75,22 @@ static bool parse_cmdline(int argc, char* argv[]) { return false; } } else { - size_t newLength = nCommandLine + 1 + strlen(argv[i]) + 1; - if (newLength > nCommandLine) { - nCommandLine = newLength; + nCommandLine += 1 + strlen(argv[i]) + 1; + if (commandLine == NULL) { + commandLine = malloc(nCommandLine); + commandLine[0] = '\0'; + } else { commandLine = realloc(commandLine, nCommandLine); } strcat_s(commandLine, nCommandLine, " "); strcat_s(commandLine, nCommandLine, argv[i]); + commandLine[nCommandLine - 1] = '\0'; } } return true; } -static DWORD WINAPI MiceMailslotWatcher(HANDLE* pSlot) { +static DWORD WINAPI MiceIpcMessageWatcher(PVOID _) { // Enable colour support in conhost HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; @@ -101,132 +108,253 @@ static DWORD WINAPI MiceMailslotWatcher(HANDLE* pSlot) { } } - OVERLAPPED ov; - HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - ov.Offset = ov.OffsetHigh = 0; - ov.hEvent = hEvent; - - char readBuffer[4096]; - DWORD nRead, nBytes; while (1) { - nRead = nBytes = 0; - ReadFile(*pSlot, readBuffer, sizeof readBuffer, &nRead, &ov); - GetOverlappedResult(*pSlot, &ov, &nBytes, TRUE); - DWORD nWrote; - WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), readBuffer, nBytes, &nWrote, NULL); + MICE_IPC_MESSAGE message; + MiceIpcPopMessage(&message, TRUE); - if (hLogFile != INVALID_HANDLE_VALUE) { - // The only VT100 sequences being used are colours, so this lazy approach works - BOOL inVt100 = FALSE; - for (unsigned int i = 0; i < nBytes; i++) { - if (readBuffer[i] == '\033') { - inVt100 = TRUE; - continue; + switch (message.m_Type) { + case MICE_IPC_TYPE_LOG: + // printf("%s", message.m_Message); + + DWORD nWrote; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), message.m_Message, message.m_Size, + &nWrote, NULL); + + if (hLogFile != INVALID_HANDLE_VALUE) { + // The only VT100 sequences being used are colours, so this lazy approach works + BOOL inVt100 = FALSE; + for (unsigned int i = 0; i < message.m_Size; i++) { + if (message.m_Message[i] == '\033') { + inVt100 = TRUE; + continue; + } + if (inVt100 && message.m_Message[i] == 'm') { + inVt100 = FALSE; + continue; + } + if (!inVt100) + WriteFile(hLogFile, &(message.m_Message[i]), 1, &nWrote, NULL); + } + // FlushFileBuffers(hLogFile); } - if (inVt100 && readBuffer[i] == 'm') { - inVt100 = FALSE; - continue; - } - if (!inVt100) WriteFile(hLogFile, &(readBuffer[i]), 1, &nWrote, NULL); - } - // FlushFileBuffers(hLogFile); + // FlushFileBuffers(GetStdHandle(STD_OUTPUT_HANDLE)); + + break; } } } -static BOOL setup_mailslot(void) { - DWORD pid = GetCurrentProcessId(); - char slotName[MAX_PATH + 1]; - sprintf_s(slotName, MAX_PATH, "\\\\.\\mailslot\\micelog%d", pid); - - HANDLE* pSlot = malloc(sizeof(HANDLE)); - if (pSlot == NULL) { - fprintf(stderr, "malloc(pSlot) failed\n"); - return FALSE; - } - *pSlot = CreateMailslotA(slotName, 0, MAILSLOT_WAIT_FOREVER, NULL); - if (*pSlot == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Creation of mailslot failed %d\n", GetLastError()); - return FALSE; - } - - CreateThread(NULL, 0, MiceMailslotWatcher, pSlot, 0, NULL); - - return TRUE; -} - /** - * Because logging is via a mailslot, we need to give it a chance to flush messages before - * terminating. While we could engineer something overly fancy, the only situation this ever occurs - * is in the main() function just below. + * Because logging is via IPC, we need to give it a chance to flush messages before + * terminating. While we could engineer something overly fancy, the only situation + * this ever occurs is in the main() function just below. * * I'll let the code speak for itself. */ -static int terminate(int err) { - Sleep(100); +static int terminate(int err, char* szMessage) { + printf("Mice termination occuring... %d\n", err); + log_info(plfBoot, "Terminating"); + Sleep(1000); + extern unsigned char g_szFault[255 + 1]; + extern BYTE g_nFault; + + if (g_nFault) { + MessageBeep(MB_ICONERROR); + MessageBoxA(NULL, (LPCSTR)g_szFault, "System Fault", + MB_OK | MB_SETFOREGROUND | MB_SYSTEMMODAL); + } else if (szMessage) { + MessageBeep(MB_ICONERROR); + MessageBoxA(NULL, szMessage, "Boot Failed", MB_OK | MB_SETFOREGROUND | MB_SYSTEMMODAL); + } return err; } HANDLE hJob = INVALID_HANDLE_VALUE; BOOL WINAPI MiceHandlerRoutine(DWORD CtrlType) { if (hJob != INVALID_HANDLE_VALUE) CloseHandle(hJob); - ExitProcess(terminate(0)); + ExitProcess(terminate(0, NULL)); } -#include "../lib/mice/spad.h" +static BOOL StartGame(LPPROCESS_INFORMATION lpPi, LPCSTR args) { + // Clear out the next flag. If the game closes without setting this, we don't want to restart + // the game. + _MiceLauncherNext = 0; + hJob = CreateJobObject(NULL, NULL); + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; + info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &info, sizeof info); + + size_t nFullCommandLine = strlen(exe_name) + 1; + if (args != NULL && strlen(args) > 0) nFullCommandLine += 1 + strlen(args); + if (nCommandLine > 0) nFullCommandLine += 1 + nCommandLine; + char* fullCommandLine = malloc(nFullCommandLine); + + if (args != NULL && nCommandLine > 0) { + sprintf_s(fullCommandLine, nFullCommandLine, "%s %s %s", exe_name, args, commandLine); + } else if (args == NULL && nCommandLine > 0) { + sprintf_s(fullCommandLine, nFullCommandLine, "%s %s", exe_name, commandLine); + } else if (args != NULL) { + sprintf_s(fullCommandLine, nFullCommandLine, "%s %s", exe_name, args); + } else { + sprintf_s(fullCommandLine, nFullCommandLine, "%s", exe_name); + } + + char* extra_injections = MiceConfig.launcher.inject; + if (!start_and_inject(hJob, exe_name, fullCommandLine, _miceIpcData->m_MiceDll, debug_wait, + boot_delay, extra_injections, 0, lpPi)) { + free(fullCommandLine); + return FALSE; + } + free(fullCommandLine); + return TRUE; +} + +static BOOL _GameIdSearcher(char* lpBasePath, char* lpGameId) { + WIN32_FIND_DATAA findFileData; + + CHAR szSearchPath[MAX_PATH + 1]; + sprintf_s(szSearchPath, _countof(szSearchPath), "%s\\*", lpBasePath); + + // Search for a table in the CWD + HANDLE hFind = FindFirstFileA(szSearchPath, &findFileData); + do { + if (strcmp(findFileData.cFileName, ".") == 0 || strcmp(findFileData.cFileName, "..") == 0) + continue; + + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + sprintf_s(szSearchPath, _countof(szSearchPath), "%s\\%s", lpBasePath, + findFileData.cFileName); + if (_GameIdSearcher(szSearchPath, lpGameId)) { + FindClose(hFind); + return TRUE; + } + } else { + if (strlen(findFileData.cFileName) == strlen("SAAA_Table.dat") && + PathMatchSpecA(findFileData.cFileName, "S*_Table.dat")) { + // maimai games have a copy of the R&D table; ignore it. + if (strncmp(findFileData.cFileName, "SBRZ", 4) == 0) continue; + lpGameId[0] = findFileData.cFileName[0]; + lpGameId[1] = findFileData.cFileName[1]; + lpGameId[2] = findFileData.cFileName[2]; + lpGameId[3] = findFileData.cFileName[3]; + + FindClose(hFind); + return TRUE; + } + if (strlen(findFileData.cFileName) == strlen("SAAA_banner.bmp") && + PathMatchSpecA(findFileData.cFileName, "S*_banner.bmp")) { + lpGameId[0] = findFileData.cFileName[0]; + lpGameId[1] = findFileData.cFileName[1]; + lpGameId[2] = findFileData.cFileName[2]; + lpGameId[3] = findFileData.cFileName[3]; + + FindClose(hFind); + return TRUE; + } + if (strlen(findFileData.cFileName) == strlen("SAAA_main.wmv") && + PathMatchSpecA(findFileData.cFileName, "S*_main.wmv")) { + lpGameId[0] = findFileData.cFileName[0]; + lpGameId[1] = findFileData.cFileName[1]; + lpGameId[2] = findFileData.cFileName[2]; + lpGameId[3] = findFileData.cFileName[3]; + + FindClose(hFind); + return TRUE; + } + } + } while (FindNextFile(hFind, &findFileData)); + FindClose(hFind); + return FALSE; +} + +BOOL MiceGuessGameId(char* lpGameId) { + if (_GameIdSearcher(".", lpGameId)) return TRUE; + + // TODO: Some more lookups. Exe name is probably not an awful fallback + + return FALSE; +} + +BOOL g_bIsInDllMain = FALSE; int main(int argc, char* argv[]) { - // unsigned char spad_new_bytes[16] = { - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x06, - // 0x94, 0x87, 0x82, 0x60, 0x37, 0x73, 0x12, 0x13, - // }; - // unsigned char spn[16]; - // Spad0Encrypt(spn, spad_new_bytes); - // for (int i = 0; i < 8; i++) printf("%02x", spn[i]); - // puts(""); - // for (int i = 8; i < 16; i++) printf("%02x", spn[i]); - // puts(""); +#if 0 + // char buffer[] = "41A800003F000000000000006801"; + char* buffer = argv[1]; + char local_10[4]; - // puts("--"); + sscanf_s(buffer, "%08x", local_10, sizeof local_10); + float measure_whole = *(float*)(void*)&local_10; + printf("measure_whole: %f\n", measure_whole); + sscanf_s(buffer + 8, "%08x", local_10, sizeof local_10); + float measure_fraction = *(float*)(void*)&local_10; + printf("measure_fraction: %f\n", measure_fraction); + sscanf_s(buffer + 16, "%08x", local_10, sizeof local_10); + float duration = *(float*)(void*)&local_10; + printf("duration: %f\n", duration); + sscanf_s(buffer + 24, "%01x", local_10, sizeof local_10); + unsigned char location = local_10[0]; + printf("location: %d\n", location); - // unsigned char back_out[16]; - // Spad0Decrypt(back_out, spn); - // for (int i = 0; i < 8; i++) printf("%02x", back_out[i]); - // puts(""); - // for (int i = 8; i < 16; i++) printf("%02x", back_out[i]); - // puts(""); + sscanf_s(buffer + 25, "%01x", local_10, sizeof local_10); + unsigned char type = local_10[0]; - // unsigned char bng[16] = { - // 0xE6, 0xBC, 0x6D, 0x56, 0x83, 0xF6, 0xB6, 0x48, - // 0x4C, 0x24, 0xE4, 0x83, 0xD5, 0xE8, 0x93, 0x7F, - // }; - // Spad0Decrypt(back_out, bng); - // for (int i = 0; i < 8; i++) printf("%02x", back_out[i]); - // puts(""); - // for (int i = 8; i < 16; i++) printf("%02x", back_out[i]); - // puts(""); + printf("Type: %d\n", local_10[0]); + if (local_10[0] < '\0') { + type = type ^ 0x80; + puts("<0"); + // field_0x42 = 1; + } + if ((type & 0x40) != 0) { + puts("&0x40"); + type = type ^ 0x40; + // field15_0x18 = 0x40000000; + } + if ((type & 0x20) != 0) { + puts("&0x20"); + type = type ^ 0x20; + // field15_0x18 = 0x40800000; + } + printf("TypeO: %01x\n", type); - // return -1; + return 0; +#endif load_mice_config(); - if (!setup_mailslot()) return 1; + + if (!MiceIpcSetup(TRUE)) return 1; + _miceIpcData->m_LauncherIsReady = TRUE; + char gameId[4]; + if (MiceGuessGameId(gameId)) memcpy(_miceIpcData->m_GameId, gameId, 4); + + CreateThread(NULL, 0, MiceIpcMessageWatcher, NULL, 0, NULL); + setup_logging(); log_info(plfBoot, "Micetools version: %s", MICE_VERSION); - - commandLine = malloc(nCommandLine); - if (commandLine == NULL) { - log_error(plfBoot, "Fatal: Failed to malloc(commandLine)"); - return terminate(-1); + if (_miceIpcData->m_GameId[0] == '\0') { + if (MiceConfig.devices.do_auto) { + log_error( + plfBoot, + "Failed to identify game. Please edit [devices].do_auto to false and restart."); + log_error(plfBoot, "Please also report this."); + return terminate(-1, "Failed to identify game."); + } + log_warning(plfBoot, "Failed to identify game, but proceeding regardless."); + } else { + log_info(plfBoot, "Booting game: %.*s", 4, _miceIpcData->m_GameId); } - commandLine[0] = '\0'; - CHAR workDir[MAX_PATH + 1]; - GetCurrentDirectory(MAX_PATH, workDir); - log_info(plfBoot, "Current directory: %s", workDir); + CHAR szPath[MAX_PATH + 1]; + GetCurrentDirectory(MAX_PATH, szPath); + szPath[sizeof szPath - 1] = '\0'; + log_info(plfBoot, "Current directory: %s", szPath); - if (!parse_cmdline(argc, argv)) return terminate(0); + if (!parse_cmdline(argc, argv)) { + puts("parse_cmdline failed"); + return terminate(-2, "parse_cmdline failed"); + } if (exe_name[0] == '\0' && MiceConfig.launcher.game_binary[0] != '\0') { snprintf(exe_name, sizeof exe_name, "%s", MiceConfig.launcher.game_binary); @@ -237,56 +365,89 @@ int main(int argc, char* argv[]) { if (exe_name[0] == '\0') { if (!locate_game(exe_name, MAX_PATH + 1)) { log_error(plfBoot, "Fatal: Failed to locate a game"); - return terminate(0); + return terminate(-3, "Failed to locate a game"); } } else { DWORD dwAttrib = GetFileAttributes(exe_name); if (dwAttrib == INVALID_FILE_ATTRIBUTES || dwAttrib & FILE_ATTRIBUTE_DIRECTORY) { log_error(plfBoot, "Fatal: %s: no such file found", exe_name); - return terminate(0); + return terminate(-4, "Specified executable not locatable"); } } + int i; + // Build IPC.m_PathPrefix + { + _miceIpcData->m_PathPrefix[0] = '\0'; + for (i = 0; i < sizeof _miceIpcData->m_PathPrefix; i++) { + _miceIpcData->m_PathPrefix[i] = exe_name[i]; + if (exe_name[i] == '\0') break; + } + // Find the final slash in the path + for (; i > 0; i--) { + if (_miceIpcData->m_PathPrefix[i] == '/' || _miceIpcData->m_PathPrefix[i] == '\\') + break; + } + _miceIpcData->m_PathPrefix[i] = '\0'; + } + + strcpy_s(_miceIpcData->m_MiceBase, sizeof _miceIpcData->m_MiceBase, szPath); + + if (!SearchPathA(NULL, MiceConfig.launcher.mice_dll, NULL, sizeof _miceIpcData->m_MiceDll, + _miceIpcData->m_MiceDll, NULL)) { + log_error(plfBoot, "Unable to locate micelib. Check your mice_dll setting!"); + return terminate(-5, "Unable to locate micelib"); + } + _miceIpcData->m_MiceDll[sizeof _miceIpcData->m_MiceDll - 1] = '\0'; + + _MiceLauncherNextEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + SetConsoleCtrlHandler(MiceHandlerRoutine, TRUE); + spawn_pcp_processes(); log_info(plfBoot, "exec: %s %s", exe_name, commandLine); - char micepath[MAX_PATH + 1]; - if (!locate_library(micepath, MAX_PATH + 1)) { - log_error(plfBoot, "Fatal: Failed to locate micelib. Check your mice_dll setting!"); - return terminate(0); + while (1) { + if (hJob != INVALID_HANDLE_VALUE) { + CloseHandle(hJob); + hJob = INVALID_HANDLE_VALUE; + } + + PROCESS_INFORMATION pi; + if (_MiceLauncherNext == 2) { + if (!StartGame(&pi, NULL)) + return terminate(-6, "Start failed. Check console for errors."); + } else if (_MiceLauncherNext == 3 || _MiceLauncherNext == 4) { + // TODO: Boot mode 3 is actually system test. We could do something here? + if (!StartGame(&pi, "gametest")) + return terminate(-7, "Gametest failed. Check console for errors."); + } else { + log_info(plfBoot, "Boot mode %d not supported. Exiting.", _MiceLauncherNext); + return terminate(-8, NULL); + } + + HANDLE objects[2]; + objects[0] = pi.hProcess; + objects[1] = _MiceLauncherNextEvent; + DWORD waited = WaitForMultipleObjects(_countof(objects), objects, FALSE, INFINITE); + if (FAILED(waited)) { + log_error(plfBoot, "Fatal: WaitForSingleObject failed: %03x", GetLastError()); + return terminate(-9, "WFSO failed"); + } + if (waited == WAIT_OBJECT_0) { + DWORD exitCode; + if (GetExitCodeProcess(pi.hProcess, &exitCode)) + log_info(plfBoot, "Shutting down (ret:%d)", exitCode); + else + log_info(plfBoot, "Shutting down (failed to get exit code)"); + CloseHandle(pi.hProcess); + continue; + // return terminate(-10); + } else if (waited == WAIT_OBJECT_0 + 1) { + continue; + } else { + log_error(plfBoot, "Unknown wait object retured: %d", waited); + return terminate(-11, "Unknown wait object!"); + } } - - hJob = CreateJobObject(NULL, NULL); - JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; - info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &info, sizeof info); - - size_t nFullCommandLine = nCommandLine + strlen(exe_name) + 1; - char* fullCommandLine = malloc(nFullCommandLine); - snprintf(fullCommandLine, nFullCommandLine, "%s %s", exe_name, commandLine); - free(commandLine); - - char* extra_injections = MiceConfig.launcher.inject; - PROCESS_INFORMATION pi; - if (!start_and_inject(hJob, exe_name, fullCommandLine, micepath, debug_wait, boot_delay, - extra_injections, 0, &pi)) { - free(fullCommandLine); - return terminate(-1); - } - free(fullCommandLine); - - SetConsoleCtrlHandler(MiceHandlerRoutine, TRUE); - - if (FAILED(WaitForSingleObject(pi.hProcess, INFINITE))) { - log_error(plfBoot, "Fatal: WaitForSingleObject failed: %03x", GetLastError()); - } else { - DWORD exitCode; - if (GetExitCodeProcess(pi.hProcess, &exitCode)) - log_info(plfBoot, "Shutting down (ret:%d)", exitCode); - else - log_info(plfBoot, "Shutting down (failed to get exit code)"); - CloseHandle(pi.hProcess); - } - return terminate(0); } diff --git a/src/micetools/launcher/meson.build b/src/micetools/launcher/meson.build index 9d96ac6..ff1c55c 100644 --- a/src/micetools/launcher/meson.build +++ b/src/micetools/launcher/meson.build @@ -1,6 +1,6 @@ rc = import('windows').compile_resources('mice.rc', depend_files: mice_ico) executable( - 'mice', + get_option('win64') ? 'mice64' : 'mice', win_subsystem: subsystem, sources: [ 'locate.c', @@ -18,5 +18,9 @@ executable( dummykeychip, dummymaster, dummyinstaller, + dummygdeliver, + dummynetwork, + + amSerialId, ], ) diff --git a/src/micetools/launcher/mice.rc b/src/micetools/launcher/mice.rc index adffb67..3851c6f 100644 --- a/src/micetools/launcher/mice.rc +++ b/src/micetools/launcher/mice.rc @@ -1,3 +1,3 @@ #include -0 ICON "../assets/mice.ico" +0 ICON "../../assets/mice.ico" diff --git a/src/micetools/launcher/spawn.c b/src/micetools/launcher/spawn.c index 5a659e1..1a3a51c 100644 --- a/src/micetools/launcher/spawn.c +++ b/src/micetools/launcher/spawn.c @@ -3,35 +3,44 @@ #include "../lib/mice/mice.h" -spawn_t mxspawns[3] = { - // { "keychip", SPAWN_DUMMY, MdkThreadProc, MxkThreadProc }, - // { "master", SPAWN_DUMMY, MdmThreadProc, NULL }, - // { "installer", SPAWN_DUMMY, MdiThreadProc, NULL }, - { "keychip", SPAWN_NONE, MdkThreadProc, MxkThreadProc }, - { "master", SPAWN_NONE, MdmThreadProc, NULL }, - { "installer", SPAWN_NONE, MdiThreadProc, NULL }, +spawn_t mxspawns[] = { + { "keychip", SPAWN_DUMMY, MdkThreadProc, MxkThreadProc }, + { "master", SPAWN_DUMMY, MdmThreadProc, NULL }, + { "installer", SPAWN_DUMMY, MdiThreadProc, NULL }, + { "network", SPAWN_DUMMY, MdnThreadProc, NULL }, + { "gdeliver", SPAWN_DUMMY, MdgdThreadProc, NULL }, }; int mxkMain(); int miceDummyKeychip(unsigned short textPort, unsigned short binaryPort, bool global); int miceDummyMaster(unsigned short textPort, unsigned short binaryPort, bool global); int miceDummyInstaller(unsigned short textPort, unsigned short binaryPort, bool global); +int miceDummyNetwork(unsigned short textPort, unsigned short binaryPort, bool global); +int miceDummyGDeliver(unsigned short textPort, unsigned short binaryPort, bool global); DWORD WINAPI MdkThreadProc(LPVOID lpParameter) { miceDummyKeychip(40106, 40107, false); return 0; -}; +} DWORD WINAPI MxkThreadProc(LPVOID lpParameter) { mxkMain(); return 0; -}; +} DWORD WINAPI MdmThreadProc(LPVOID lpParameter) { miceDummyMaster(40100, 40101, false); return 0; -}; +} DWORD WINAPI MdiThreadProc(LPVOID lpParameter) { miceDummyInstaller(40102, 40103, false); return 0; -}; +} +DWORD WINAPI MdnThreadProc(LPVOID lpParameter) { + miceDummyNetwork(40104, 40105, false); + return 0; +} +DWORD WINAPI MdgdThreadProc(LPVOID lpParameter) { + miceDummyGDeliver(40108, 40112, false); + return 0; +} static inline void spawn_one(spawn_t* spawn) { if (spawn->mode == SPAWN_NONE) { diff --git a/src/micetools/launcher/spawn.h b/src/micetools/launcher/spawn.h index d136497..e294289 100644 --- a/src/micetools/launcher/spawn.h +++ b/src/micetools/launcher/spawn.h @@ -12,11 +12,13 @@ typedef struct { #define SPAWN_REAL 2 #define SPAWN_BOTH (SPAWN_DUMMY | SPAWN_REAL) -extern spawn_t mxspawns[3]; +extern spawn_t mxspawns[5]; DWORD WINAPI MxkThreadProc(LPVOID lpParameter); DWORD WINAPI MdkThreadProc(LPVOID lpParameter); DWORD WINAPI MdmThreadProc(LPVOID lpParameter); DWORD WINAPI MdiThreadProc(LPVOID lpParameter); +DWORD WINAPI MdnThreadProc(LPVOID lpParameter); +DWORD WINAPI MdgdThreadProc(LPVOID lpParameter); void spawn_pcp_processes(void); diff --git a/src/micetools/lib/_am.h b/src/micetools/lib/_am.h index ea30b75..f1569c9 100644 --- a/src/micetools/lib/_am.h +++ b/src/micetools/lib/_am.h @@ -1,5 +1,3 @@ -#include "../mice/version_fallback.h" - #define AM_LIB_C_HEADER(name, storage_type) \ int name##DebugLevel = 0; \ struct storage_type name; \ diff --git a/src/micetools/lib/am/amOemstring.c b/src/micetools/lib/am/amOemstring.c index ffee241..c797114 100644 --- a/src/micetools/lib/am/amOemstring.c +++ b/src/micetools/lib/am/amOemstring.c @@ -2,7 +2,7 @@ #include -#include "../dmi/dmi.h" +#include "../mice/dmi.h" #include "../mice/mice.h" AM_LIB_C_HEADER(amOemstring, AM_OEMSTRING) diff --git a/src/micetools/lib/dmi/meson.build b/src/micetools/lib/dmi/meson.build deleted file mode 100644 index dbf5c28..0000000 --- a/src/micetools/lib/dmi/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -dmi_lib = static_library( - 'dmi', - sources: [ - 'dmi.c' - ], -) diff --git a/src/micetools/lib/libpcp/pcpp.c b/src/micetools/lib/libpcp/pcpp.c index 0c77fe1..6cdd771 100644 --- a/src/micetools/lib/libpcp/pcpp.c +++ b/src/micetools/lib/libpcp/pcpp.c @@ -1246,7 +1246,6 @@ e_pcpp_t pcppSendBinary(pcpp_t* stream, const unsigned char* send_binary_buffer, e_pcpp_t pcppIsBusy(pcpp_t* stream, timeout_t timeout) { size_t* psVar1; - char** send_buf; undefined4 uVar2; int iVar3; e_pcpt_t eVar4; @@ -1688,14 +1687,12 @@ e_pcpp_t pcppIsBusy(pcpp_t* stream, timeout_t timeout) { } while (stream->field_0x1fc <= stream->recv_binary_buf_len && stream->recv_binary_buf_len != stream->field_0x1fc); } - send_buf = (char**)&stream->send_buf; - psVar1 = &stream->send_buf_len; stream->state = pcpp_state_send_binary_ack; - *send_buf[0] = PCP_CHAR_BINACK; - *psVar1 = 1; + stream->send_buf[0] = PCP_CHAR_BINACK; + stream->send_buf_len = 1; local_20 = pcppGetBlockingTime(stream->field_0x20c, uVar11, stream, _amTimeMs(local_18), &timeout); - eVar4 = pcptSend(&stream->sock, (unsigned char*)*send_buf, psVar1, stream->field_0x204, + eVar4 = pcptSend(&stream->sock, (unsigned char*)stream->send_buf, &stream->send_buf_len, stream->field_0x204, timeout); eVar7 = _errT2P(eVar4); stream->err = eVar7; @@ -1713,9 +1710,9 @@ e_pcpp_t pcppIsBusy(pcpp_t* stream, timeout_t timeout) { stream->state = pcpp_state_send_binary_ack_wait; iVar3 = pcppGetBlockingTime(stream->field_0x208, uVar11, stream, _amTimeMs(local_18), &timeout); - *psVar1 = PCP_SEND_BUF_MAX; + stream->send_buf_len = PCP_SEND_BUF_MAX; ZERO(stream->send_buf); - eVar4 = pcptRecv(&stream->data_sock, (unsigned char*)*send_buf, psVar1, timeout); + eVar4 = pcptRecv(&stream->data_sock, (unsigned char*)stream->send_buf, &stream->send_buf_len, timeout); eVar7 = _errT2P(eVar4); stream->err = eVar7; if ((iVar3 != 0) && (eVar7 == e_pcpp_to)) { diff --git a/src/micetools/lib/meson.build b/src/micetools/lib/meson.build index 48847ce..af8d6bf 100644 --- a/src/micetools/lib/meson.build +++ b/src/micetools/lib/meson.build @@ -1,5 +1,4 @@ subdir('util') # This is the only lib that should ever be link_with'd by another lib -subdir('dmi') subdir('ami') subdir('libpcp') diff --git a/src/micetools/lib/mice/config.c b/src/micetools/lib/mice/config.c index 064ef80..987e490 100644 --- a/src/micetools/lib/mice/config.c +++ b/src/micetools/lib/mice/config.c @@ -6,8 +6,12 @@ #include "../../../../subprojects/inih_dep/ini.h" #include "../../dll/devices/smb_pca9535.h" +#include "../../dll/drivers/jvs_boards/jvs.h" +#include "../../dll/hooks/files.h" #include "../../dll/key_config.h" +MICE_JVS _MiceJvsBoards[JVS_IO_MAX]; + config_t MiceConfig = { #define SECTION(s, comment) .s = { #define CFG_str(s, n, default, comment) .n = default, @@ -24,7 +28,7 @@ config_t MiceConfig = { ._keep_linter_happy = true }; -void fprintf_prefix(FILE *file, const char *prefix, const char *text) { +static void fprintf_prefix(FILE *file, const char *prefix, const char *text) { char *copy = (char *)malloc(strlen(text) + 1); memcpy_s(copy, strlen(text) + 1, text, strlen(text) + 1); @@ -38,99 +42,174 @@ void fprintf_prefix(FILE *file, const char *prefix, const char *text) { free(copy); } -void make_default_config() { - FILE *config_file; - fopen_s(&config_file, CONFIG_PATH, "w"); - if (config_file == NULL) { - puts("Failed to create config file!"); +void save_keybinds() { + HANDLE hFile = CreateFileA(KEYBINDS_PATH, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE, NULL, + CREATE_ALWAYS, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Failed to open keybinds file: %d", GetLastError()); return; - }; - int first_section = true; + } -#define CFG_str(s, n, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %s ;(string)\n", #n, default); + SetFilePointer(hFile, 8, NULL, 0); -#define CFG_bool(s, n, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %s ;(bool)\n", #n, default ? "true" : "false"); + DWORD nWrote; + DWORD totalSize = 0; + WriteFile(hFile, &MiceConfig.keys.board_count, sizeof MiceConfig.keys.board_count, &nWrote, + NULL); + totalSize += nWrote; -#define CFG_int(s, n, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %d ;(int)\n", #n, default); + for (int board = 0; board < MiceConfig.keys.board_count; board++) { + WriteFile(hFile, &_MiceJvsBoards->m_Players, sizeof _MiceJvsBoards->m_Players, &nWrote, + NULL); + totalSize += nWrote; + WriteFile(hFile, &_MiceJvsBoards->m_ButtonsPerPlayer, + sizeof _MiceJvsBoards->m_ButtonsPerPlayer, &nWrote, NULL); + totalSize += nWrote; + WriteFile(hFile, &_MiceJvsBoards->m_Coins, sizeof _MiceJvsBoards->m_Coins, &nWrote, NULL); + totalSize += nWrote; + WriteFile(hFile, &_MiceJvsBoards->m_NumButtons, sizeof _MiceJvsBoards->m_NumButtons, + &nWrote, NULL); + totalSize += nWrote; -#define CFG_hex(s, n, precision, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %.*X ;(hex, %d byte%s)\n", #n, precision, 0x##default, precision, \ - precision == 1 ? "" : "s"); + WriteFile(hFile, _MiceJvsBoards[board].m_Bindings, + sizeof *_MiceJvsBoards[board].m_Bindings * _MiceJvsBoards->m_NumButtons, &nWrote, + NULL); + totalSize += nWrote; + } -#define CFG_ipv4(s, n, a, b, c, d, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %hhu.%hhu.%hhu.%hhu ;(ipv4)\n", #n, a, b, c, d); + SetFilePointer(hFile, 0, NULL, 0); + WriteFile(hFile, &totalSize, sizeof totalSize, &nWrote, NULL); -#define SECTION(s, comment) \ - if (!first_section) fprintf(config_file, "\n"); \ - first_section = false; \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "[%s]\n", #s); + amiCrc32RInit(); + DWORD readLeft = totalSize; + DWORD crc32 = 0; + SetFilePointer(hFile, 8, NULL, 0); + while (readLeft) { + BYTE readBuf[0x1000]; + DWORD toRead = sizeof readBuf; + if (readLeft < toRead) toRead = readLeft; + DWORD nRead; + ReadFile(hFile, readBuf, toRead, &nRead, NULL); -#define COMMENT(comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); + if (nRead == 0) { + fprintf(stderr, "Binding saving failed %d\n", GetLastError()); + CloseHandle(hFile); + return; + } + crc32 = amiCrc32RCalc(nRead, readBuf, crc32); + readLeft -= nRead; + } + SetFilePointer(hFile, 4, NULL, 0); + WriteFile(hFile, &crc32, sizeof crc32, &nWrote, NULL); -#include "config.def" + CloseHandle(hFile); +} +void load_keybind_config() { + HANDLE hFile = + CreateFileA(KEYBINDS_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) return; - fclose(config_file); + DWORD nRead; + DWORD nSize = 0; + ReadFile(hFile, &nSize, sizeof nSize, &nRead, NULL); + + SetFilePointer(hFile, 8, NULL, 0); + amiCrc32RInit(); + DWORD crc32 = 0; + while (nSize) { + BYTE readBuf[0x1000]; + DWORD toRead = sizeof readBuf; + if (nSize < toRead) toRead = nSize; + ReadFile(hFile, readBuf, toRead, &nRead, NULL); + + if (nRead == 0) { + fprintf(stderr, "Binding reading failed\n"); + CloseHandle(hFile); + return; + } + crc32 = amiCrc32RCalc(nRead, readBuf, crc32); + nSize -= nRead; + } + + DWORD savedCrc; + SetFilePointer(hFile, 4, NULL, 0); + ReadFile(hFile, &savedCrc, sizeof savedCrc, &nRead, NULL); + if (savedCrc != crc32) { + fprintf(stderr, "Binding file CRC failed (%08x!=%08x)\n", savedCrc, crc32); + CloseHandle(hFile); + return; + } + + ReadFile(hFile, &MiceConfig.keys.board_count, sizeof MiceConfig.keys.board_count, &nRead, NULL); + + for (int board = 0; board < MiceConfig.keys.board_count; board++) { + ReadFile(hFile, &_MiceJvsBoards->m_Players, sizeof _MiceJvsBoards->m_Players, &nRead, NULL); + ReadFile(hFile, &_MiceJvsBoards->m_ButtonsPerPlayer, + sizeof _MiceJvsBoards->m_ButtonsPerPlayer, &nRead, NULL); + ReadFile(hFile, &_MiceJvsBoards->m_Coins, sizeof _MiceJvsBoards->m_Coins, &nRead, NULL); + ReadFile(hFile, &_MiceJvsBoards->m_NumButtons, sizeof _MiceJvsBoards->m_NumButtons, &nRead, + NULL); + + free(_MiceJvsBoards[board].m_Bindings); + _MiceJvsBoards[board].m_Bindings = + malloc(sizeof *_MiceJvsBoards[board].m_Bindings * _MiceJvsBoards->m_NumButtons); + ReadFile(hFile, _MiceJvsBoards[board].m_Bindings, + sizeof *_MiceJvsBoards[board].m_Bindings * _MiceJvsBoards->m_NumButtons, &nRead, + NULL); + + free(_MiceJvsBoards[board].m_ButtonStates); + _MiceJvsBoards[board].m_ButtonStates = + malloc(sizeof *_MiceJvsBoards[board].m_ButtonStates * _MiceJvsBoards->m_NumButtons); + } + + CloseHandle(hFile); } -char keybindBuffer[8192]; -void save_current_config() { - // Dump keybind settings - int i = 0; - for (int board = 0; board < MiceConfig.keys.board_count; board++) { - i += snprintf(keybindBuffer + i, _countof(keybindBuffer) - i, "%d,", - jvsKeybindings[board].test); - i += snprintf(keybindBuffer + i, _countof(keybindBuffer) - i, "%d,", - jvsKeybindings[board].notdefault); - for (int n = 0; n < JVS_BUTTON_PAIR_COUNT * 2; n++) { - i += snprintf(keybindBuffer + i, _countof(keybindBuffer) - i, "%d,", - jvsKeybindings[board].buttons[n]); - i += snprintf(keybindBuffer + i, _countof(keybindBuffer) - i, "%d,", - jvsKeybindings[board].invert[n]); - } - } - keybindBuffer[i] = '\0'; - free(MiceConfig.keys.keys); - MiceConfig.keys.keys = _strdup(keybindBuffer); +char keybindBuffer[2 * JVS_IO_MAX * (sizeof _MiceJvsBoards[0].m_Bindings[0]) * 32]; +void save_current_config(bool writeDefault) { + CreateDirectoryA("mice", NULL); // TODO: A touch nicer? + + save_keybinds(); FILE *config_file; fopen_s(&config_file, CONFIG_PATH, "w"); if (config_file == NULL) { puts("Failed to create config file!"); return; - }; + } int first_section = true; -#define CFG_str(s, n, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %s ;(string)\n", #n, MiceConfig.s.n); +#define CFG_str(s, n, default, comment) \ + if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ + fprintf(config_file, "; (string) default = %s\n", writeDefault ? MiceConfig.s.n : default); \ + fprintf(config_file, "%s = %s\n", #n, MiceConfig.s.n); -#define CFG_bool(s, n, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %s ;(bool)\n", #n, MiceConfig.s.n ? "true" : "false"); +#define CFG_bool(s, n, default, comment) \ + if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ + fprintf(config_file, "; (bool) default = %s\n", \ + (writeDefault ? MiceConfig.s.n : default) ? "true" : "false"); \ + fprintf(config_file, "%s = %s\n", #n, MiceConfig.s.n ? "true" : "false"); -#define CFG_int(s, n, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %d ;(int)\n", #n, MiceConfig.s.n); +#define CFG_int(s, n, default, comment) \ + if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ + fprintf(config_file, "; (int) default = %d\n", writeDefault ? MiceConfig.s.n : default); \ + fprintf(config_file, "%s = %d\n", #n, MiceConfig.s.n); -#define CFG_hex(s, n, precision, default, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %.*X ;(hex, %d byte%s)\n", #n, precision, MiceConfig.s.n, \ - precision, precision == 1 ? "" : "s"); +#define CFG_hex(s, n, precision, default, comment) \ + if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ + fprintf(config_file, "; (hex, %d byte%s) default = %.*X \n", precision, \ + precision == 1 ? "" : "s", precision, writeDefault ? MiceConfig.s.n : 0x##default); \ + fprintf(config_file, "%s = %.*X\n", #n, precision, MiceConfig.s.n); -#define CFG_ipv4(s, n, a, b, c, d, comment) \ - if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ - fprintf(config_file, "%s = %hhu.%hhu.%hhu.%hhu ;(ipv4)\n", #n, (MiceConfig.s.n >> 24), \ - ((MiceConfig.s.n >> 16) & 0xff), ((MiceConfig.s.n >> 8) & 0xff), \ +#define CFG_ipv4(s, n, a, b, c, d, comment) \ + if (strlen(comment) != 0) fprintf_prefix(config_file, "; ", comment); \ + fprintf(config_file, "; (ipv4) default = %hhu.%hhu.%hhu.%hhu ;(ipv4)\n", \ + writeDefault ? MiceConfig.s.n >> 24 : a, \ + writeDefault ? (MiceConfig.s.n >> 16) & 0xff : b, \ + writeDefault ? (MiceConfig.s.n >> 8) & 0xff : c, \ + writeDefault ? (MiceConfig.s.n & 0xff) : d); \ + fprintf(config_file, "%s = %hhu.%hhu.%hhu.%hhu\n", #n, (MiceConfig.s.n >> 24), \ + ((MiceConfig.s.n >> 16) & 0xff), ((MiceConfig.s.n >> 8) & 0xff), \ (MiceConfig.s.n & 0xff)); #define SECTION(s, comment) \ @@ -189,8 +268,26 @@ const unsigned int RES_W[8] = { 640, 640, 1024, 1024, 1280, 1280, 1360, 1920 }; const unsigned int RES_H[8] = { 480, 480, 600, 768, 720, 1024, 768, 1080 }; void load_mice_config() { if (ini_parse(CONFIG_PATH, handler, &MiceConfig) < 0) { - make_default_config(); - printf("Can't load '%s', using defaults\n", CONFIG_PATH); + printf("Loading config defaults\n"); + + if (MiceConfig.sysconf.serial) free(MiceConfig.sysconf.serial); + MiceConfig.sysconf.serial = malloc(17); + if (MiceConfig.sysconf.keyid) free(MiceConfig.sysconf.keyid); + MiceConfig.sysconf.keyid = malloc(17); + GetSerialNumbers(MiceConfig.sysconf.serial, MiceConfig.sysconf.keyid); + + save_current_config(true); + } + + if (!MiceConfig.sysconf.serial || strlen(MiceConfig.sysconf.serial) == 0) { + if (MiceConfig.sysconf.serial) free(MiceConfig.sysconf.serial); + MiceConfig.sysconf.serial = malloc(17); + GetSerialNumbers(MiceConfig.sysconf.serial, NULL); + } + if (!MiceConfig.sysconf.keyid || strlen(MiceConfig.sysconf.keyid) == 0) { + if (MiceConfig.sysconf.keyid) free(MiceConfig.sysconf.keyid); + MiceConfig.sysconf.keyid = malloc(17); + GetSerialNumbers(NULL, MiceConfig.sysconf.keyid); } if (MiceConfig.window.dipsw) { @@ -214,44 +311,4 @@ void load_mice_config() { else if (MiceConfig.window.w == 640 && MiceConfig.window.h == 480) MiceConfig.sysconf.dipsw |= DIPSW_RES_640x480; } - - if (MiceConfig.keys.board_count < 0) MiceConfig.keys.board_count = 0; - if (MiceConfig.keys.board_count > JVS_IO_MAX) MiceConfig.keys.board_count = JVS_IO_MAX; - - char *text = MiceConfig.keys.keys; - if (text) { - char *copy = (char *)malloc(strlen(text) + 1); - memcpy_s(copy, strlen(text) + 1, text, strlen(text) + 1); - - char *next_token; - char *token = strtok_s(copy, ",", &next_token); - int board = 0; - int state = 0; - int n = 0; - while (token != NULL) { - int val = atoi(token); - if (state == 0) { - jvsKeybindings[board].test = val; - state = 1; - } else if (state == 1) { - jvsKeybindings[board].notdefault = val; - state = 2; - } else { - if (state == 2) { - jvsKeybindings[board].buttons[n] = val; - state = 3; - } else { - jvsKeybindings[board].invert[n] = val; - state = 2; - if (n++ == JVS_BUTTON_PAIR_COUNT * 2) { - n = 0; - state = 0; - if (board++ == JVS_IO_MAX) break; - } - } - } - token = strtok_s(NULL, ",", &next_token); - } - free(copy); - } } diff --git a/src/micetools/lib/mice/config.def b/src/micetools/lib/mice/config.def index 591e9fd..36e2dee 100644 --- a/src/micetools/lib/mice/config.def +++ b/src/micetools/lib/mice/config.def @@ -28,14 +28,20 @@ SECTION(launcher, "These options are only used during initial bootstrapping") CFG_str(launcher, game_binary, "", "Override binary detection") CFG_bool(launcher, wait_for_debugger, false, "Wait for a debugger to attach when starting") CFG_int(launcher, startup_delay, 0, "Pause during startup") +#ifdef _WIN64 +CFG_str(launcher, mice_dll, "mice64.dll", "The path to mice's DLL") +#else CFG_str(launcher, mice_dll, "mice.dll", "The path to mice's DLL") +#endif CFG_str(launcher, inject, "", "Extra DLLs to inject during boot") ENDSECTION(launcher) SECTION(sysconf, "System configuration settings") +CFG_int(sysconf, platform, 2, "System platform. 0 = RingWide, 1 = RingEdge, 2 = RingEdge2") CFG_int(sysconf, region, 1, "Board region. 1 = Jpn, 2 = USA, 4 = Exp, 8 = Chn") CFG_bool(sysconf, rental, false, "") -CFG_str(sysconf, serial, "AASE-01A65646203", "") +CFG_str(sysconf, serial, "A000-00000000000", "") +CFG_str(sysconf, keyid, "A000-00000000000", "") CFG_hex(sysconf, dipsw, 2, 40, "DIP Switch values") // Default 40 = 1280x720 ENDSECTION(sysconf) @@ -58,14 +64,15 @@ CFG_ipv4(network, subnet_mask, 255, 255, 255, 0, "") CFG_ipv4(network, gateway, 192, 168, 103, 254, "") CFG_ipv4(network, primary_dns, 192, 168, 103, 254, "") CFG_ipv4(network, secondary_dns, 0, 0, 0, 0, "") -COMMENT("Emulated DNS records. Routers must be pingable!") -CFG_ipv4(network, naominet_jp, 10, 0, 0, 2, "") -CFG_ipv4(network, ib_naominet_jp, 10, 0, 0, 2, "") -CFG_ipv4(network, aime_naominet_jp, 10, 0, 0, 2, "") -CFG_ipv4(network, tenporouter_loc, 127, 0, 0, 1, "") -CFG_ipv4(network, bbrouter_loc, 127, 0, 0, 1, "") -CFG_ipv4(network, mobirouter_loc, 127, 0, 0, 1, "") -CFG_ipv4(network, dslrouter_loc, 127, 0, 0, 1, "") +CFG_ipv4(network, upstream_dns_server, 0, 0, 0, 0, "") +COMMENT("Emulated DNS records.") +CFG_ipv4(network, naominet_jp, 134, 65, 56, 170, "") +CFG_ipv4(network, ib_naominet_jp, 127, 0, 0, 1, "") +CFG_ipv4(network, aime_naominet_jp, 134, 65, 56, 170, "") +CFG_ipv4(network, tenporouter_loc, 192, 168, 103, 254, "") +CFG_ipv4(network, bbrouter_loc, 192, 168, 103, 254, "") +CFG_ipv4(network, mobirouter_loc, 192, 168, 103, 254, "") +CFG_ipv4(network, dslrouter_loc, 192, 168, 103, 254, "") COMMENT("Second half of system mac address. The vendor prefix D8:BB:C1: will be prepended") CFG_hex(network, mac, 6, 0A2F1D, "") ENDSECTION(network) @@ -100,7 +107,6 @@ SECTION(keys, "Raw keybinding data. Edit this using the built in binding tool!") CFG_int(keys, test, 0, "") CFG_int(keys, service, 0, "") CFG_int(keys, board_count, 1, "") -CFG_str(keys, keys, "", "") ENDSECTION(keys) SECTION(devices, "Register attached hardware devices. COM4 is reserved for JVS.") @@ -112,6 +118,7 @@ CFG_str(devices, com5, "", "") CFG_str(devices, com6, "", "") CFG_str(devices, com7, "", "") CFG_str(devices, com8, "", "") +CFG_bool(devices, add_idlers, true, "Monitors for activity on disconnected virtual serial ports") ENDSECTION(devices) SECTION(aime, "Aime cards used when a TN32MSEC reader is attached\nSpecify either an access code or FeliCa ID") diff --git a/src/micetools/lib/mice/config.h b/src/micetools/lib/mice/config.h index b4b69a3..7bdcb36 100644 --- a/src/micetools/lib/mice/config.h +++ b/src/micetools/lib/mice/config.h @@ -3,7 +3,12 @@ #include #include -#define CONFIG_PATH "mice.ini" +#define CONFIG_PATH MiceIpcRelativePath("config.ini") +#define KEYBINDS_PATH MiceIpcRelativePath("keybinds.bin") + +#define MICE_PLATFORM_RINGWIDE 0 +#define MICE_PLATFORM_RINGEDGE1 1 // Explictly RingEdge1, to avoid confusion +#define MICE_PLATFORM_RINGEDGE2 2 typedef struct config { #define SECTION(s, comment) struct { @@ -19,6 +24,6 @@ typedef struct config { extern config_t MiceConfig; -void make_default_config(); -void save_current_config(); +void save_current_config(bool writeDefault); void load_mice_config(); +void load_keybind_config(); diff --git a/src/micetools/lib/dmi/dmi.c b/src/micetools/lib/mice/dmi.c similarity index 51% rename from src/micetools/lib/dmi/dmi.c rename to src/micetools/lib/mice/dmi.c index de1e10b..43d63c3 100644 --- a/src/micetools/lib/dmi/dmi.c +++ b/src/micetools/lib/mice/dmi.c @@ -1,119 +1,147 @@ -#include "dmi.h" - -#include - -LPBYTE dmi_table = NULL; -WORD dmi_size = 0; -WORD _dmi_max = 0; - -DMI_BIOS default_dmi_bios = { - .Head.Type = DmiTypeBios, - .Head.Length = 0x12, - .Head.Handle = 0x0000, - .Vendor = 0x01, // "American Megatrends Inc." - .Version = 0x02, // "080015 " - .StartSegment = 0x0000, // '00 f0'h - .ReleaseDate = 0x03, // "07/28/2011" - .ROMSize = 0x00, // 03h - .Chars = 0x1f, // '1F 90 DA 8B'h - .VerMajor = 0x08, - .VerMinor = 0x0f, - .ECVerMajor = 0xff, - .ECVerMinor = 0xff, -}; - -DMI_SYSTEM default_dmi_system = { - .Head.Type = DmiTypeSystem, - .Head.Length = 0x08, - .Head.Handle = 0x0001, - .Manufacturer = 0x01, // "NEC" - .ProductName = 0x02, // "To Be Filled By O.E.M." - .Version = 0x03, // "To Be Filled By O.E.M." - .Serial = 0x04, // "To Be Filled By O.E.M." -}; - -DMI_STRING default_dmi_string = { - .Head.Type = DmiTypeString, - .Head.Length = 0x05, - .Head.Handle = 0x0002, - .NoStrings = 0x00, -}; - -static void dmi_init(void) { - if (dmi_table) free(dmi_table); - _dmi_max = 0xff; - dmi_table = (LPBYTE)malloc(_dmi_max); - memset(dmi_table, 0, _dmi_max); - dmi_size = 0; -} - -static void dmi_append(void* data, WORD size) { - if (!dmi_table) return; - while (dmi_size + (size + 1) >= _dmi_max) { - LPBYTE new_table = (LPBYTE)realloc(dmi_table, _dmi_max += 0xff); - if (!new_table) return; - dmi_table = new_table; - } - memcpy(dmi_table + dmi_size, data, size); - dmi_size += size; - dmi_table[dmi_size++] = 0; - dmi_table[dmi_size++] = 0; -} - -static void dmi_append_with_strings(void* data, WORD size, int num_strings, ...) { - va_list args; - va_start(args, num_strings); - - dmi_append(data, size); - dmi_size -= 2; - for (int i = 0; i < num_strings; i++) { - char* str = va_arg(args, char*); - WORD len = strlen(str) & 0xffff; - memcpy((char*)dmi_table + dmi_size, str, len + 1); - dmi_size += len + 1; - dmi_table[dmi_size - 1] = 0; - } - dmi_table[dmi_size++] = 0; - - va_end(args); -} - -void dmi_build_default() { - dmi_init(); - // BIOS version - dmi_append_with_strings(&default_dmi_bios, sizeof default_dmi_bios, 3, - "American Megatrends Inc.", "080015 ", "07/28/2011"); - - // Platform AAM: Board type one of "Supermicro"(=1) or "Advantech"(=2) - // Platform AAL: Board type one of "NEC"(=0) or "AAL2"(=3) - dmi_append_with_strings(&default_dmi_system, sizeof default_dmi_system, 4, "NEC", - "To Be Filled By O.E.M.", "To Be Filled By O.E.M.", - "To Be Filled By O.E.M."); - - default_dmi_string.NoStrings = 5; - - dmi_append_with_strings(&default_dmi_string, sizeof default_dmi_string, - // OEM strings: - // 0: ?? - // 1: ?? - // 2: Platform ID (AAL, AAM) - // AAL = Nvidia drivers - // AAM = AMD drivers - // *** = No dedicated drivers - // 3: ?? - // 4: Board type (AAL, NEC, AAL2, " ", AAM, Supermicro, Advantech) - // If present -> makes board = 4 - // AAL = 4 - // AAM = 4 - // Supermicro = 4 - // Advantech = 4 - // These values are pulled from an 846-5004D - 5, "DAC-BJ02", "DAC-BJ02", "AAL", "Advantech", "AAL2"); -} - -BYTE dmi_calc_checksum(const char* buf, int len) { - int sum = 0; - int a; - for (a = 0; a < len; a++) sum += buf[a]; - return (BYTE)(sum == 0); -} +#include "dmi.h" + +#include + +#include "config.h" + +LPBYTE dmi_table = NULL; +WORD dmi_size = 0; +WORD _dmi_max = 0; + +DMI_BIOS default_dmi_bios = { + .Head.Type = DmiTypeBios, + .Head.Length = 0x12, + .Head.Handle = 0x0000, + .Vendor = 0x01, // "American Megatrends Inc." + .Version = 0x02, // "080015 " + .StartSegment = 0x0000, // '00 f0'h + .ReleaseDate = 0x03, // "07/28/2011" + .ROMSize = 0x00, // 03h + .Chars = 0x1f, // '1F 90 DA 8B'h + .VerMajor = 0x08, + .VerMinor = 0x0f, + .ECVerMajor = 0xff, + .ECVerMinor = 0xff, +}; + +DMI_SYSTEM default_dmi_system = { + .Head.Type = DmiTypeSystem, + .Head.Length = 0x08, + .Head.Handle = 0x0001, + .Manufacturer = 0x01, // "NEC" + .ProductName = 0x02, // "To Be Filled By O.E.M." + .Version = 0x03, // "To Be Filled By O.E.M." + .Serial = 0x04, // "To Be Filled By O.E.M." +}; + +DMI_STRING default_dmi_string = { + .Head.Type = DmiTypeString, + .Head.Length = 0x05, + .Head.Handle = 0x0002, + .NoStrings = 0x00, +}; + +static void dmi_init(void) { + if (dmi_table) free(dmi_table); + _dmi_max = 0xff; + dmi_table = (LPBYTE)malloc(_dmi_max); + memset(dmi_table, 0, _dmi_max); + dmi_size = 0; +} + +static void dmi_append(void* data, WORD size) { + if (!dmi_table) return; + while (dmi_size + (size + 1) >= _dmi_max) { + LPBYTE new_table = (LPBYTE)realloc(dmi_table, _dmi_max += 0xff); + if (!new_table) return; + dmi_table = new_table; + } + memcpy(dmi_table + dmi_size, data, size); + dmi_size += size; + dmi_table[dmi_size++] = 0; + dmi_table[dmi_size++] = 0; +} + +static void dmi_append_with_strings(void* data, WORD size, int num_strings, ...) { + va_list args; + va_start(args, num_strings); + + dmi_append(data, size); + dmi_size -= 2; + for (int i = 0; i < num_strings; i++) { + char* str = va_arg(args, char*); + WORD len = strlen(str) & 0xffff; + memcpy((char*)dmi_table + dmi_size, str, len + 1); + dmi_size += len + 1; + dmi_table[dmi_size - 1] = 0; + } + dmi_table[dmi_size++] = 0; + + va_end(args); +} + +// Strings are: 0, 1, Platform ID, 3, Board Type +/** + * | | Wide1 | Wide2 | Edge | Edge2 | + * | ------------ | ---------- | --------- | ---- | ----- | + * | Manufacturer | Supermicro | Advantech | NEC | NEC | + * | Platform ID | AAM | AAM | AAL | AAL | + * | Board Type | | | "" | AAL2 | + */ + +#define MANUFACTURER_RINGWIDE "Supermicro" +#define MANUFACTURER_RINGEDGE "NEC" +#define MANUFACTURER_RINGEDGE2 MANUFACTURER_RINGEDGE + +#define OEM_STRINGS_RINGWIDE "DAC-BJ02", "DAC-BJ02", "AAM", "Supermicro", "" +#define OEM_STRINGS_RINGEDGE "DAC-BJ02", "DAC-BJ02", "AAL", "NEC", "" +#define OEM_STRINGS_RINGEDGE2 "DAC-BJ02", "DAC-BJ02", "AAL", "Advantech", "AAL2" + +void dmi_build_default() { + dmi_init(); + // BIOS version + dmi_append_with_strings(&default_dmi_bios, sizeof default_dmi_bios, 3, + "American Megatrends Inc.", "080015 ", "07/28/2011"); + + switch (MiceConfig.sysconf.platform) { + case MICE_PLATFORM_RINGWIDE: { + dmi_append_with_strings(&default_dmi_system, sizeof default_dmi_system, 4, + MANUFACTURER_RINGWIDE, "To Be Filled By O.E.M.", + "To Be Filled By O.E.M.", "To Be Filled By O.E.M."); + + default_dmi_string.NoStrings = 5; + dmi_append_with_strings(&default_dmi_string, sizeof default_dmi_string, 5, + OEM_STRINGS_RINGWIDE); + break; + } + case MICE_PLATFORM_RINGEDGE1: { + dmi_append_with_strings(&default_dmi_system, sizeof default_dmi_system, 4, + MANUFACTURER_RINGEDGE, "To Be Filled By O.E.M.", + "To Be Filled By O.E.M.", "To Be Filled By O.E.M."); + + default_dmi_string.NoStrings = 5; + dmi_append_with_strings(&default_dmi_string, sizeof default_dmi_string, 5, + OEM_STRINGS_RINGEDGE); + break; + } + case MICE_PLATFORM_RINGEDGE2: + default: { + dmi_append_with_strings(&default_dmi_system, sizeof default_dmi_system, 4, + MANUFACTURER_RINGEDGE2, "To Be Filled By O.E.M.", + "To Be Filled By O.E.M.", "To Be Filled By O.E.M."); + + default_dmi_string.NoStrings = 5; + + dmi_append_with_strings(&default_dmi_string, sizeof default_dmi_string, 5, + OEM_STRINGS_RINGEDGE2); + break; + } + } +} + +BYTE dmi_calc_checksum(const char* buf, int len) { + int sum = 0; + int a; + for (a = 0; a < len; a++) sum += buf[a]; + return (BYTE)(sum == 0); +} diff --git a/src/micetools/lib/dmi/dmi.h b/src/micetools/lib/mice/dmi.h similarity index 95% rename from src/micetools/lib/dmi/dmi.h rename to src/micetools/lib/mice/dmi.h index 08e06da..660acf4 100644 --- a/src/micetools/lib/dmi/dmi.h +++ b/src/micetools/lib/mice/dmi.h @@ -1,64 +1,64 @@ -#include -#include - -extern LPBYTE dmi_table; -extern WORD dmi_size; - -enum DmiType { - DmiTypeBios = 0, - DmiTypeSystem = 1, - DmiTypeString = 11, -}; -typedef BYTE DmiType_t; - -#pragma pack(push, 1) -typedef struct { - DmiType_t Type; - BYTE Length; - WORD Handle; -} DMI_SECTION_HEADER; - -typedef struct { - CHAR Signature[5]; - BYTE Checksum; - WORD StructLength; - DWORD StructAddr; - WORD NumberOfStructs; - BYTE BCDRevision; - BYTE Reserved; -} DMI_HEADER; - -typedef struct { - DMI_SECTION_HEADER Head; - BYTE Vendor; - BYTE Version; - WORD StartSegment; - BYTE ReleaseDate; - BYTE ROMSize; - uint64_t Chars; - BYTE VerMajor; - BYTE VerMinor; - BYTE ECVerMajor; - BYTE ECVerMinor; -} DMI_BIOS; - -typedef struct { - DMI_SECTION_HEADER Head; - BYTE Manufacturer; - BYTE ProductName; - BYTE Version; - BYTE Serial; -} DMI_SYSTEM; - -typedef struct { - DMI_SECTION_HEADER Head; - BYTE NoStrings; -} DMI_STRING; -#pragma pack(pop) - -static void dmi_init(void); -static void dmi_append(void* data, WORD size); -static void dmi_append_with_strings(void* data, WORD size, int num_strings, ...); - -void dmi_build_default(void); -BYTE dmi_calc_checksum(const char* buf, int len); +#include +#include + +extern LPBYTE dmi_table; +extern WORD dmi_size; + +enum DmiType { + DmiTypeBios = 0, + DmiTypeSystem = 1, + DmiTypeString = 11, +}; +typedef BYTE DmiType_t; + +#pragma pack(push, 1) +typedef struct { + DmiType_t Type; + BYTE Length; + WORD Handle; +} DMI_SECTION_HEADER; + +typedef struct { + CHAR Signature[5]; + BYTE Checksum; + WORD StructLength; + DWORD StructAddr; + WORD NumberOfStructs; + BYTE BCDRevision; + BYTE Reserved; +} DMI_HEADER; + +typedef struct { + DMI_SECTION_HEADER Head; + BYTE Vendor; + BYTE Version; + WORD StartSegment; + BYTE ReleaseDate; + BYTE ROMSize; + uint64_t Chars; + BYTE VerMajor; + BYTE VerMinor; + BYTE ECVerMajor; + BYTE ECVerMinor; +} DMI_BIOS; + +typedef struct { + DMI_SECTION_HEADER Head; + BYTE Manufacturer; + BYTE ProductName; + BYTE Version; + BYTE Serial; +} DMI_SYSTEM; + +typedef struct { + DMI_SECTION_HEADER Head; + BYTE NoStrings; +} DMI_STRING; +#pragma pack(pop) + +static void dmi_init(void); +static void dmi_append(void* data, WORD size); +static void dmi_append_with_strings(void* data, WORD size, int num_strings, ...); + +void dmi_build_default(void); +BYTE dmi_calc_checksum(const char* buf, int len); diff --git a/src/micetools/lib/mice/exe.c b/src/micetools/lib/mice/exe.c index a2d873b..b7225f5 100644 --- a/src/micetools/lib/mice/exe.c +++ b/src/micetools/lib/mice/exe.c @@ -4,10 +4,10 @@ #include "log.h" -bool inject_debug_wait(HANDLE process) { +bool inject_debug_wait(HANDLE process, DWORD pid) { BOOL present; - log_info(plfBoot, "Waiting for debugger to attach."); + log_info(plfBoot, "Waiting for debugger to attach (pid=%d).", pid); do { Sleep(1000); if (FAILED(CheckRemoteDebuggerPresent(process, &present))) { @@ -35,7 +35,7 @@ bool remote_call(HANDLE process, LPVOID function, LPCSTR argument, DWORD* result HANDLE remote_thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)function, arg_addr, 0, NULL); - if (remote_thread == INVALID_HANDLE_VALUE) { + if (remote_thread == NULL) { log_error(plfProcesses, "CreateRemoteThread failed: %d", GetLastError()); return false; } @@ -106,15 +106,15 @@ BOOL start_and_inject(HANDLE hJob, LPCSTR path, LPSTR cmdline, LPCSTR inject, BO flags |= CREATE_SUSPENDED; if (!CreateProcessA(path, cmdline, NULL, NULL, FALSE, flags, NULL, NULL, &startupInfo, &processInformation)) { - log_error(plfProcesses, "CreateProcessA(%s, %s) failed: %d", path, cmdline, - GetLastError()); + log_error(plfProcesses, "CreateProcessA(%s, %s) failed: %d", path, cmdline, GetLastError()); goto abort; } if (hJob != INVALID_HANDLE_VALUE) AssignProcessToJobObject(hJob, processInformation.hProcess); if (debug_wait) - if (!inject_debug_wait(processInformation.hProcess)) goto abort; + if (!inject_debug_wait(processInformation.hProcess, processInformation.dwProcessId)) + goto abort; if (inject != NULL) if (!inject_dll(processInformation.hProcess, inject)) goto abort; diff --git a/src/micetools/lib/mice/exe.h b/src/micetools/lib/mice/exe.h index ebc4938..8c0cb6f 100644 --- a/src/micetools/lib/mice/exe.h +++ b/src/micetools/lib/mice/exe.h @@ -6,4 +6,10 @@ BOOL start_and_inject(HANDLE hJob, LPCSTR path, LPSTR cmdline, LPCSTR inject, BO DWORD delay, LPCSTR extra_injections, DWORD flags, LPPROCESS_INFORMATION lpProcessInformation); +#ifdef _WIN64 +#define MICELIB "mice64.dll" +#define MICEEXE "mice64.exe" +#else #define MICELIB "mice.dll" +#define MICEEXE "mice.exe" +#endif diff --git a/src/micetools/lib/mice/ipc.c b/src/micetools/lib/mice/ipc.c new file mode 100644 index 0000000..a262373 --- /dev/null +++ b/src/micetools/lib/mice/ipc.c @@ -0,0 +1,159 @@ +#include "ipc.h" + +#include + +#include "../util/pid.h" + +PMICE_IPC_DATA _miceIpcData = NULL; +static HANDLE hMapFile = NULL; +static HANDLE _miceIpcSendEvent = NULL; +static HANDLE _miceIpcPopEvent = NULL; +static HANDLE _miceIpcMutex = NULL; + +BOOL MiceIpcGetGameId(LPSTR lpGameId) { + if (_miceIpcData == NULL) return FALSE; + if (!_miceIpcData->m_LauncherIsReady) return FALSE; + memcpy(lpGameId, _miceIpcData->m_GameId, 4); + return TRUE; +} + +BOOL MiceIpcSetup(BOOL isOwner) { + DWORD pid; + if (isOwner) + pid = GetCurrentProcessId(); + else + pid = GetOutermostMiceId(); + + char mapName[MAX_PATH + 1]; + sprintf_s(mapName, MAX_PATH, "Local\\MiceMappingObject%d", pid); + + SetLastError(0); + if (isOwner) + hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, + sizeof *_miceIpcData, mapName); + else { + hMapFile = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, TRUE, mapName); + } + if (hMapFile == NULL) { + fprintf(stderr, "Opening shared memory failed: %d\n", GetLastError()); + return FALSE; + } + + SetLastError(0); + _miceIpcData = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof *_miceIpcData); + if (_miceIpcData == NULL) { + fprintf(stderr, "Mapping view of memory failed: %d\n", GetLastError()); + return FALSE; + } + + sprintf_s(mapName, MAX_PATH, "MiceMappingObject-Mutex%d", pid); + _miceIpcMutex = CreateMutexA(NULL, 0, mapName); + sprintf_s(mapName, MAX_PATH, "MiceMappingObject-SendE%d", pid); + _miceIpcSendEvent = CreateEventA(NULL, FALSE, FALSE, mapName); + sprintf_s(mapName, MAX_PATH, "MiceMappingObject-PopE%d", pid); + _miceIpcPopEvent = CreateEventA(NULL, FALSE, FALSE, mapName); + + if (isOwner) { + ZeroMemory(_miceIpcData, sizeof *_miceIpcData); + } else { + if (!_miceIpcData->m_LauncherIsReady) { + fprintf(stderr, "IPC connected, but launcher never signalled!\n"); + return FALSE; + } + } + return TRUE; +} + +void _MiceIpcLock(void) { WaitForSingleObject(_miceIpcMutex, INFINITE); } +void _MiceIpcUnlock(void) { ReleaseMutex(_miceIpcMutex); } + +BOOL MiceIpcPushMessage(PMICE_IPC_MESSAGE lpMessage, BOOL bBlocking) { + if (!_miceIpcData->m_LauncherIsReady) return FALSE; + _MiceIpcLock(); + + if (_miceIpcData->m_MessagesWaiting == MICE_IPC_MAX_MESSAGES) { + if (!bBlocking) { + _MiceIpcUnlock(); + return FALSE; + } + + do { + _MiceIpcUnlock(); + WaitForSingleObject(_miceIpcPopEvent, INFINITE); + _MiceIpcLock(); + } while (_miceIpcData->m_MessagesWaiting == MICE_IPC_MAX_MESSAGES); + } + + memcpy(&_miceIpcData->m_MessageQueue[_miceIpcData->m_MessageWriteHead], lpMessage, + sizeof *lpMessage); + _miceIpcData->m_MessageWriteHead++; + if (_miceIpcData->m_MessageWriteHead == MICE_IPC_MAX_MESSAGES) + _miceIpcData->m_MessageWriteHead = 0; + _miceIpcData->m_MessagesWaiting++; + + SetEvent(_miceIpcSendEvent); + + _MiceIpcUnlock(); + return TRUE; +} + +BOOL MiceIpcPopMessage(PMICE_IPC_MESSAGE lpMessage, BOOL bBlocking) { + _MiceIpcLock(); + + if (_miceIpcData->m_MessagesWaiting == 0) { + if (!bBlocking) { + _MiceIpcUnlock(); + return FALSE; + } + + do { + _MiceIpcUnlock(); + WaitForSingleObject(_miceIpcSendEvent, INFINITE); + _MiceIpcLock(); + } while (_miceIpcData->m_MessagesWaiting == 0); + } + + int index = _miceIpcData->m_MessageWriteHead - _miceIpcData->m_MessagesWaiting; + if (index < 0) index += MICE_IPC_MAX_MESSAGES; + // Impossible condition, but guard against it just in case! + if (index < 0 || index >= MICE_IPC_MAX_MESSAGES) { + _MiceIpcUnlock(); + return FALSE; + } + + memcpy(lpMessage, &_miceIpcData->m_MessageQueue[index], + sizeof _miceIpcData->m_MessageQueue[index]); + _miceIpcData->m_MessagesWaiting--; + + // Signal to any pushes waiting that the queue should have more now + SetEvent(_miceIpcPopEvent); + + _MiceIpcUnlock(); + return TRUE; +} + +static CHAR _szMiceRelative[MAX_PATH + 1]; +static DWORD _szMiceRelativeN = 0xffffffff; +LPSTR MiceIpcRelativePath(LPSTR lpChild) { + if (_miceIpcData && _miceIpcData->m_LauncherIsReady) { + if (_szMiceRelative[0] == '\0' || _szMiceRelativeN == 0xffffffff) { + strcpy_s(_szMiceRelative, sizeof _szMiceRelative, _miceIpcData->m_MiceBase); + _szMiceRelativeN = strlen(_szMiceRelative); + + if (_szMiceRelativeN == 0) { + strcat_s(_szMiceRelative, sizeof _szMiceRelative, "mice\\"); + _szMiceRelativeN += 5; + } else { + strcat_s(_szMiceRelative, sizeof _szMiceRelative, "\\mice\\"); + _szMiceRelativeN += 6; + } + } + + strcpy_s(&_szMiceRelative[_szMiceRelativeN], sizeof _szMiceRelative - _szMiceRelativeN, + lpChild); + } else { + sprintf_s(_szMiceRelative, sizeof _szMiceRelative, ".\\mice\\%s", lpChild); + } + + return _szMiceRelative; +} diff --git a/src/micetools/lib/mice/ipc.h b/src/micetools/lib/mice/ipc.h new file mode 100644 index 0000000..93cca88 --- /dev/null +++ b/src/micetools/lib/mice/ipc.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#define MICE_IPC_MAX_MESSAGES 160 + +typedef enum MICE_IPC_TYPE { + MICE_IPC_TYPE_INVALID = 0, + MICE_IPC_TYPE_LOG = 1, +} MICE_IPC_TYPE; + +typedef struct MICE_IPC_MESSAGE { + MICE_IPC_TYPE m_Type; + DWORD m_Size; + BYTE m_Message[1024]; +} MICE_IPC_MESSAGE, *PMICE_IPC_MESSAGE; + +typedef struct MICE_IPC_DATA { + BOOL m_LauncherIsReady; + char m_GameId[4]; + char m_PathPrefix[MAX_PATH + 1]; + char m_MiceBase[MAX_PATH + 1]; + char m_MiceDll[MAX_PATH + 1]; + + DWORD m_MessageWriteHead; + DWORD m_MessagesWaiting; + MICE_IPC_MESSAGE m_MessageQueue[MICE_IPC_MAX_MESSAGES]; +} MICE_IPC_DATA, *PMICE_IPC_DATA; + +BOOL MiceIpcSetup(BOOL isOwner); +BOOL MiceIpcGetGameId(LPSTR lpGameId); + +void _MiceIpcLock(void); +void _MiceIpcUnlock(void); + +BOOL MiceIpcPushMessage(PMICE_IPC_MESSAGE lpMessage, BOOL bBlocking); +BOOL MiceIpcPopMessage(PMICE_IPC_MESSAGE lpMessage, BOOL bBlocking); +LPSTR MiceIpcRelativePath(LPSTR lpChild); + +extern PMICE_IPC_DATA _miceIpcData; diff --git a/src/micetools/lib/mice/log.c b/src/micetools/lib/mice/log.c index 008f583..304261f 100644 --- a/src/micetools/lib/mice/log.c +++ b/src/micetools/lib/mice/log.c @@ -9,6 +9,7 @@ #include "../util/pid.h" #include "config.h" +#include "ipc.h" #define _LF(category, name, display) \ LOG_FACILITY lf##name = { \ @@ -70,8 +71,6 @@ void __stdcall amLogCallback(DWORD level, char* format) { DWORD pLogcb; DWORD* ppLogcb; -HANDLE hSlot; - static char log_buf[1024]; int _do_log(BYTE log_level, PLOG_FACILITY facility, const char* format, va_list args) { // Early-return if we have nothing to do @@ -92,7 +91,7 @@ int _do_log(BYTE log_level, PLOG_FACILITY facility, const char* format, va_list char prefix = LOG_PREFIXES[log_level]; - int col_len = strlen(log_colours[log_level]); + size_t col_len = strlen(log_colours[log_level]); EnterCriticalSection(&logger_lock); int log_len = snprintf(log_buf, _countof(log_buf), "%s%s%c:%s:", log_colours[log_level], @@ -101,11 +100,19 @@ int _do_log(BYTE log_level, PLOG_FACILITY facility, const char* format, va_list log_len += snprintf(log_buf + log_len, _countof(log_buf) - log_len, "%s\n", COLOR_RESET); log_buf[_countof(log_buf) - 1] = '\0'; - if (hSlot != INVALID_HANDLE_VALUE) { - WriteFile(hSlot, log_buf, log_len, NULL, NULL); + MICE_IPC_MESSAGE ipcMessage; + ipcMessage.m_Type = MICE_IPC_TYPE_LOG; + memcpy_s(ipcMessage.m_Message, sizeof ipcMessage.m_Message, log_buf, log_len); + if (log_len < sizeof ipcMessage.m_Message) { + ipcMessage.m_Message[log_len] = '\0'; + ipcMessage.m_Size = log_len; } else { - // This should never happen, but there's no harm being prepared in case it does - WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), log_buf, log_len, NULL, NULL); + ipcMessage.m_Message[sizeof ipcMessage.m_Message - 1] = '\0'; + ipcMessage.m_Size = sizeof ipcMessage.m_Message; + } + + if (!MiceIpcPushMessage(&ipcMessage, TRUE)) { + fprintf(stderr, "Failed to log: %.*s", log_len, log_buf); } LeaveCriticalSection(&logger_lock); @@ -172,19 +179,7 @@ int _log_game(PLOG_FACILITY facility, const char* format, ...) { return ret; } -void setup_logging() { - char slotName[MAX_PATH + 1]; - sprintf_s(slotName, MAX_PATH, "\\\\.\\mailslot\\micelog%d", GetOutermostMiceId()); - - hSlot = CreateFile(slotName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (hSlot == INVALID_HANDLE_VALUE) { - fprintf(stderr, "Fatal: Failed to open mailslot %s", slotName); - ExitProcess(2); - } - - InitializeCriticalSection(&logger_lock); -} +void setup_logging() { InitializeCriticalSection(&logger_lock); } void log_stack(PLOG_FACILITY facility) { char name[MAX_PATH * sizeof(TCHAR)]; @@ -195,12 +190,21 @@ void log_stack(PLOG_FACILITY facility) { RtlCaptureContext(&context); STACKFRAME64 stack = { 0 }; +#ifdef _WIN64 + stack.AddrPC.Offset = context.Rip; + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrStack.Offset = context.Rsp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrFrame.Offset = context.Rbp; + stack.AddrFrame.Mode = AddrModeFlat; +#else stack.AddrPC.Offset = context.Eip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = context.Esp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = context.Ebp; stack.AddrFrame.Mode = AddrModeFlat; +#endif HANDLE process = GetCurrentProcess(); HANDLE thread = GetCurrentThread(); diff --git a/src/micetools/lib/mice/log.h b/src/micetools/lib/mice/log.h index 2a697d5..74a9078 100644 --- a/src/micetools/lib/mice/log.h +++ b/src/micetools/lib/mice/log.h @@ -43,7 +43,7 @@ void log_stack(PLOG_FACILITY facility); void setup_logging(); // Disable some logging entirely at build time for speed -#define COMPILE_LOG_LEVEL 6 +#define COMPILE_LOG_LEVEL 5 #if COMPILE_LOG_LEVEL >= 6 #define log_trace _log_trace diff --git a/src/micetools/lib/mice/log_facilities.def b/src/micetools/lib/mice/log_facilities.def index 0fe129f..33666b1 100644 --- a/src/micetools/lib/mice/log_facilities.def +++ b/src/micetools/lib/mice/log_facilities.def @@ -8,6 +8,7 @@ _LF(Misc, Fprintf, "fprintf") _LF(Misc, Fprintf_s, "fprintf_s") _LF(Misc, Vfprintf_s, "vfprintf_s") _LF(Misc, AmLog, "amLog") +_LF(Misc, DebugLog, "DebugLog") _LF(Hooks, Processes, "processes") _LF(Hooks, Registry, "registry") @@ -30,6 +31,7 @@ _LF(Devices, Eeprom, "eeprom") _LF(Devices, MaiTouch, "maitouch") _LF(Devices, MaiLED, "mailed") _LF(Devices, Servo15069, "servo15069") +_LF(Devices, GacchuGuts, "gacchuGuts") _LF(Drivers, Columba, "columba") _LF(Drivers, MxJvs, "mxjvs") diff --git a/src/micetools/lib/mice/meson.build b/src/micetools/lib/mice/meson.build index 9b9748a..ead8596 100644 --- a/src/micetools/lib/mice/meson.build +++ b/src/micetools/lib/mice/meson.build @@ -7,15 +7,20 @@ mice_lib = static_library( 'ringbuf.c', 'config.c', 'kcf.c', - 'micefs.c', 'des.c', 'blowfish.c', 'solitaire.c', 'spad.c', + 'dmi.c', + 'ipc.c', + 'serial.c', ], link_with: [ inih.get_variable('lib_inih'), util_lib, amiCrc, ], + include_directories: [ + openssl_inc, + ], ) diff --git a/src/micetools/lib/mice/mice.h b/src/micetools/lib/mice/mice.h index 32b6998..839a1b7 100644 --- a/src/micetools/lib/mice/mice.h +++ b/src/micetools/lib/mice/mice.h @@ -8,9 +8,12 @@ #include "config.h" #include "exe.h" #include "ioctl.h" +#include "ipc.h" #include "kcf.h" #include "log.h" -#include "micefs.h" #include "patch.h" #include "ringbuf.h" -#include "version_fallback.h" + +#define SRAM_PATH MiceIpcRelativePath("sram.bin") + +BOOL GetSerialNumbers(LPSTR szMainId, LPSTR szKeyId); diff --git a/src/micetools/lib/mice/serial.c b/src/micetools/lib/mice/serial.c new file mode 100644 index 0000000..096d4f5 --- /dev/null +++ b/src/micetools/lib/mice/serial.c @@ -0,0 +1,140 @@ +#include +#include +#include + +#pragma comment(lib, "wbemuuid.lib") + +static BOOL PerformWmiQuery(LPCWCH szClass, LPCWCH szProperty, LPWCH lpResult, DWORD nResult) { + HRESULT hres; + IWbemLocator *pLoc = NULL; + IWbemServices *pSvc = NULL; + IEnumWbemClassObject *pEnumerator = NULL; + BOOL bSuccess = FALSE; + + hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) return FALSE; + + hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); + if (FAILED(hres)) goto cleanup; + + hres = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, + (LPVOID *)&pLoc); + if (FAILED(hres)) goto cleanup; + + hres = + pLoc->lpVtbl->ConnectServer(pLoc, L"ROOT\\CIMV2", NULL, NULL, NULL, 0, NULL, NULL, &pSvc); + + if (FAILED(hres)) goto cleanup; + + hres = CoSetProxyBlanket((IUnknown *)pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); + if (FAILED(hres)) goto cleanup; + + WCHAR szQuery[128]; + swprintf_s(szQuery, _countof(szQuery), L"SELECT * FROM %ls", szClass); + + hres = pSvc->lpVtbl->ExecQuery(pSvc, L"WQL", szQuery, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, + &pEnumerator); + if (FAILED(hres)) goto cleanup; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + while (pEnumerator) { + HRESULT hr = pEnumerator->lpVtbl->Next(pEnumerator, WBEM_INFINITE, 1, &pclsObj, &uReturn); + if (!uReturn) break; + + VARIANT vtProp; + hr = pclsObj->lpVtbl->Get(pclsObj, szProperty, 0, &vtProp, 0, 0); + + if (SUCCEEDED(hr) && vtProp.vt != VT_NULL) { + wcscpy_s(lpResult, nResult, vtProp.bstrVal); + bSuccess = TRUE; + } + + VariantClear(&vtProp); + pclsObj->lpVtbl->Release(pclsObj); + } + + pEnumerator->lpVtbl->Release(pEnumerator); + +cleanup: + if (pSvc) pSvc->lpVtbl->Release(pSvc); + if (pLoc) pLoc->lpVtbl->Release(pLoc); + CoUninitialize(); + return bSuccess; +} + +inline static void alphaNum(CHAR *lpAddr, BYTE val) { + val %= 26 + 10; + if (val < 10) + lpAddr[0] = '0' + val; + else + lpAddr[0] = 'A' + (val - 10); +} +inline static void numeric(CHAR *lpAddr, BYTE val) { + val %= 100; + lpAddr[0] = '0' + (val / 10); + lpAddr[1] = '0' + (val % 10); +} + +extern BOOL g_bIsInDllMain; +BOOL GetSerialNumbers(LPSTR szMainId, LPSTR szKeyId) { + // Making WMI queries from within DllMain is not happening. Ever. :D + if (g_bIsInDllMain) return FALSE; + + WCHAR szMbSerial[128]; + if (!PerformWmiQuery(L"Win32_BaseBoard", L"SerialNumber", szMbSerial, _countof(szMbSerial))) + return FALSE; + szMbSerial[127] = L'\0'; + WCHAR szCpuId[128]; + if (!PerformWmiQuery(L"Win32_Processor", L"ProcessorId", szCpuId, _countof(szCpuId))) + return FALSE; + szCpuId[127] = L'\0'; + + BYTE sumOut[20]; + unsigned int outlen; + + EVP_MD_CTX *ctx; + ctx = EVP_MD_CTX_create(); + EVP_DigestInit(ctx, EVP_sha1()); + EVP_DigestUpdate(ctx, szMbSerial, wcslen(szMbSerial)); + EVP_DigestUpdate(ctx, szCpuId, wcslen(szCpuId)); + EVP_DigestFinal_ex(ctx, sumOut, &outlen); + EVP_MD_CTX_destroy(ctx); + + if (szMainId) { + szMainId[0] = 'M'; + alphaNum(&szMainId[1], sumOut[0]); + alphaNum(&szMainId[2], sumOut[1]); + alphaNum(&szMainId[3], sumOut[2]); + szMainId[4] = '-'; + alphaNum(&szMainId[5], sumOut[3]); + alphaNum(&szMainId[6], sumOut[4]); + alphaNum(&szMainId[7], sumOut[5]); + numeric(&szMainId[8], sumOut[6]); + numeric(&szMainId[10], sumOut[7]); + numeric(&szMainId[12], sumOut[8]); + numeric(&szMainId[14], sumOut[9]); + szMainId[16] = '\0'; + } + + if (szKeyId) { + szKeyId[0] = 'M'; + alphaNum(&szKeyId[1], sumOut[10]); + alphaNum(&szKeyId[2], sumOut[11]); + alphaNum(&szKeyId[3], sumOut[12]); + szKeyId[4] = '-'; + alphaNum(&szKeyId[5], sumOut[13]); + alphaNum(&szKeyId[6], sumOut[14]); + alphaNum(&szKeyId[7], sumOut[15]); + numeric(&szKeyId[8], sumOut[16]); + numeric(&szKeyId[10], sumOut[17]); + numeric(&szKeyId[12], sumOut[18]); + numeric(&szKeyId[14], sumOut[19]); + szKeyId[16] = '\0'; + } + + return TRUE; +} diff --git a/src/micetools/lib/mice/version_fallback.h b/src/micetools/lib/mice/version_fallback.h deleted file mode 100644 index b8fb70b..0000000 --- a/src/micetools/lib/mice/version_fallback.h +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef MICE_VERSION -#define MICE_VERSION "0.0-pre" -#endif diff --git a/src/micetools/lib/mxk/mxk.c b/src/micetools/lib/mxk/mxk.c index fe5f064..bb1d239 100644 --- a/src/micetools/lib/mxk/mxk.c +++ b/src/micetools/lib/mxk/mxk.c @@ -152,7 +152,7 @@ BOOL mxkFlashRead(unsigned int address, unsigned int nbytes, unsigned char* buff mxkPacketReqFlashRead(packet, address, nbytes); if (!mxkSendPacket(packet)) return FALSE; - for (size_t i = 0; i < nbytes; i += 0x100) { + for (unsigned int i = 0; i < nbytes; i += 0x100) { unsigned int rest = (nbytes - i) > 0x100 ? 0x100 : (nbytes - i); if (!mxkTransportRecv(buffer + i, rest)) return FALSE; } diff --git a/src/micetools/lib/mxk/mxkCrypt.c b/src/micetools/lib/mxk/mxkCrypt.c index 8afd2ed..06f706a 100644 --- a/src/micetools/lib/mxk/mxkCrypt.c +++ b/src/micetools/lib/mxk/mxkCrypt.c @@ -268,7 +268,7 @@ void mxkSignValue(unsigned int value, unsigned char* signature) { mxkSign(&hash_data, sizeof hash_data, signature); } -int mxkCryptCalcHashWithHmacSha1(PHmacKey_t key, PHmacSum_t md, size_t* nbuffer, +int mxkCryptCalcHashWithHmacSha1(PHmacKey_t key, PHmacSum_t md, unsigned int* nbuffer, unsigned char* buffer, size_t nin) { if (key == NULL || buffer == NULL || md == NULL || nbuffer == NULL) { amiDebugLog("Error: Invalid param."); diff --git a/src/micetools/lib/mxk/mxkCrypt.h b/src/micetools/lib/mxk/mxkCrypt.h index 2105a18..a7ddfde 100644 --- a/src/micetools/lib/mxk/mxkCrypt.h +++ b/src/micetools/lib/mxk/mxkCrypt.h @@ -21,7 +21,7 @@ void mxkSwapKeys(); MXK_STATUS mxkCryptInit(void); -int mxkCryptCalcHashWithHmacSha1(PHmacKey_t key, PHmacSum_t md, size_t* nbuffer, +int mxkCryptCalcHashWithHmacSha1(PHmacKey_t key, PHmacSum_t md, unsigned int* nbuffer, unsigned char* buffer, size_t nin); void mxkCryptCalcHashWithSha1(unsigned char* data, size_t nbytes, PSha1Sum_t sum); diff --git a/src/micetools/lib/mxk/mxkN2.c b/src/micetools/lib/mxk/mxkN2.c index e4d691d..a166049 100644 --- a/src/micetools/lib/mxk/mxkN2.c +++ b/src/micetools/lib/mxk/mxkN2.c @@ -195,7 +195,7 @@ int mxkN2UtilHmacPacket(PSha1Sum_t packetSha, PHmacSum_t hmacSalt, PN2Nonce_t no unsigned char data_in[sizeof *packetSha + sizeof *hmacSalt + sizeof *nonce]; size_t count = sizeof *packetSha + sizeof *hmacSalt + sizeof *nonce; - size_t nout = HMAC_SUM_SIZE; + unsigned int nout = HMAC_SUM_SIZE; mxkN2UtilCatenateData(data_in, &count, 3, packetSha, sizeof *packetSha, hmacSalt, sizeof *hmacSalt, nonce, sizeof *nonce); diff --git a/src/micetools/lib/util/pid.c b/src/micetools/lib/util/pid.c index 415a344..b247d1a 100644 --- a/src/micetools/lib/util/pid.c +++ b/src/micetools/lib/util/pid.c @@ -3,6 +3,8 @@ #include #include +#include "../mice/exe.h" + #pragma comment(lib, "psapi.lib") DWORD GetParentProcessIdFor(DWORD pid) { @@ -30,12 +32,14 @@ DWORD GetOutermostMiceId(void) { CHAR baseName[MAX_PATH + 1]; if (GetModuleFileNameEx(hProcess, NULL, baseName, sizeof baseName)) { // TODO: Better - if (strcmp(PathFindFileNameA(baseName), "mice.exe") == 0) return pid; + if (stricmp(PathFindFileNameA(baseName), MICEEXE) == 0) + return pid; } CloseHandle(hProcess); } pid = GetParentProcessIdFor(pid); } while (pid != (DWORD)-1); + fprintf(stderr, "Likely fatal: Failed to identify parent mouse!"); return (DWORD)-1; } diff --git a/src/micetools/micekeychip/callbacks/appboot.c b/src/micetools/micekeychip/callbacks/appboot.c index 9cbefba..09eaffd 100644 --- a/src/micetools/micekeychip/callbacks/appboot.c +++ b/src/micetools/micekeychip/callbacks/appboot.c @@ -87,6 +87,6 @@ void mxkPcpAbSeed(pcpa_t* stream, void* data) { pcpaAddSendPacket(stream, "port", "40107"); char sSize[16]; - sprintf_s(sSize, sizeof sSize, "%d", sizeof mxkKcfConfig.m_Seed); + sprintf_s(sSize, sizeof sSize, "%zd", sizeof mxkKcfConfig.m_Seed); pcpaAddSendPacket(stream, "size", sSize); } diff --git a/src/micetools/micekeychip/micekeychip.rc b/src/micetools/micekeychip/micekeychip.rc index 9424796..92a9204 100644 --- a/src/micetools/micekeychip/micekeychip.rc +++ b/src/micetools/micekeychip/micekeychip.rc @@ -1,3 +1,3 @@ #include -0 ICON "../assets/micekeychip.ico" +0 ICON "../../assets/micekeychip.ico" diff --git a/src/micetools/micemaster/appLauncher.c b/src/micetools/micemaster/appLauncher.c index c4319fe..9cc8655 100644 --- a/src/micetools/micemaster/appLauncher.c +++ b/src/micetools/micemaster/appLauncher.c @@ -35,8 +35,7 @@ void nxAuthStart(LPPROCESS_INFORMATION procInfo) { wchar_t commandLine[64] = { 0 }; wchar_t path[64] = { 0 }; - MultiByteToWideChar(0, 0, Config.binary.nxAuth, -1, commandLine, - _countof(commandLine)); + MultiByteToWideChar(0, 0, Config.binary.nxAuth, -1, commandLine, _countof(commandLine)); wcscpy_s(path, _countof(path), commandLine); PathRemoveFileSpecW(path); @@ -204,20 +203,20 @@ DWORD __stdcall appLauncherAppThread(appLauncherAppInfo_t *appInfo) { ZeroMemory(wCommandLine, sizeof wCommandLine); ZeroMemory(workingDirectory, sizeof workingDirectory); - MultiByteToWideChar(0, 0, commandLine, strlen(commandLine), wCommandLine, - sizeof wCommandLine / sizeof wCommandLine[0]); + MultiByteToWideChar(0, 0, commandLine, -1, wCommandLine, + (int)(sizeof wCommandLine / sizeof wCommandLine[0])); MultiByteToWideChar(0, 0, Config.dir.game, 4, workingDirectory, - sizeof workingDirectory / sizeof workingDirectory[0]); + (int)(sizeof workingDirectory / sizeof workingDirectory[0])); wchar_t *username = appInfo->m_appLauncher->m_username; wchar_t *password = appInfo->m_appLauncher->m_password; if (wcslen(username) == 0) - MultiByteToWideChar(0, 0, Config.env.username, strlen(Config.env.username), - username, sizeof username / sizeof username[0]); + MultiByteToWideChar(0, 0, Config.env.username, -1, username, + (int)(sizeof username / sizeof username[0])); if (wcslen(password) == 0) - MultiByteToWideChar(0, 0, Config.env.password, strlen(Config.env.password), - password, sizeof password / sizeof password[0]); + MultiByteToWideChar(0, 0, Config.env.password, -1, password, + (int)(sizeof password / sizeof password[0])); amiDebugLog("Creating process: %ls", wCommandLine); success = CreateProcessWithLogonW(username, NULL, password, 0, NULL, wCommandLine, diff --git a/src/micetools/micemaster/micemaster.rc b/src/micetools/micemaster/micemaster.rc index 9424796..92a9204 100644 --- a/src/micetools/micemaster/micemaster.rc +++ b/src/micetools/micemaster/micemaster.rc @@ -1,3 +1,3 @@ #include -0 ICON "../assets/micekeychip.ico" +0 ICON "../../assets/micekeychip.ico" diff --git a/src/micetools/out.png b/src/micetools/out.png new file mode 100644 index 0000000..ca27573 Binary files /dev/null and b/src/micetools/out.png differ diff --git a/src/micetools/render.py b/src/micetools/render.py new file mode 100644 index 0000000..a99e108 --- /dev/null +++ b/src/micetools/render.py @@ -0,0 +1,92 @@ +import pygame +import pygame.gfxdraw as gfx +from PIL import Image, ImageDraw +import math + +# surf = pygame.Surface((1080, 1080)) +# surf.fill((0, 0, 0)) + +surf = Image.new("RGB", (720 * 2, 1080)) +draw = ImageDraw.Draw(surf) + + +def region(rX, rY, rW, rH): + # # Out of region + # if (mX < rX || mY < rY || mX > rX + rW || mY > rY + rH) return 0; + + # # i is now a position in a region from (-1,-1) to (1,1) + # float iX = ((mX - rX) / (rW / 2)) - 1; + # float iY = ((mY - rY) / (rH / 2)) - 1; + + # float r2 = iX * iX + iY * iY; + + # # Bound to a unit circle + # if (r2 > 1) return 0; + + mid = (rX + rW // 2, rY + rH // 2) + scale = min(rW, rH) / 2 + + outer = (rX, rY, rX + rW, rY + rH) + innerR = 298 / 540 + inner = ( + rX + (rW / 2) * (1 - innerR), + rY + (rH / 2) * (1 - innerR), + rX + rW - (rW / 2) * (1 - innerR), + rY + rH - (rW / 2) * (1 - innerR), + ) + centreR = 121 / 540 + centre = ( + rX + (rW / 2) * (1 - centreR), + rY + (rH / 2) * (1 - centreR), + rX + rW - (rW / 2) * (1 - centreR), + rY + rH - (rW / 2) * (1 - centreR), + ) + + draw.pieslice(outer, 0, 45, fill=(0, 255, 0)) + draw.pieslice(outer, 45, 90, fill=(127, 255, 127)) + draw.pieslice(outer, 90, 135, fill=(0, 255, 0)) + draw.pieslice(outer, 135, 180, fill=(127, 255, 127)) + draw.pieslice(outer, 180, 225, fill=(0, 255, 0)) + draw.pieslice(outer, 225, 270, fill=(127, 255, 127)) + draw.pieslice(outer, 270, 315, fill=(0, 255, 0)) + draw.pieslice(outer, 315, 360, fill=(127, 255, 127)) + + draw.pieslice(inner, 0, 45, fill=(255, 0, 0)) + draw.pieslice(inner, 45, 90, fill=(255, 127, 127)) + draw.pieslice(inner, 90, 135, fill=(255, 0, 0)) + draw.pieslice(inner, 135, 180, fill=(255, 127, 127)) + draw.pieslice(inner, 180, 225, fill=(255, 0, 0)) + draw.pieslice(inner, 225, 270, fill=(255, 127, 127)) + draw.pieslice(inner, 270, 315, fill=(255, 0, 0)) + draw.pieslice(inner, 315, 360, fill=(255, 127, 127)) + + draw.pieslice(centre, 0, 360, (0, 0, 255)) + + # gfx.arc(s, *mid, 500, 0, 90, (0, 255, 0)) + + # pygame.draw.arc(s, (0, 255, 0), (0, 0, 500, 500), 0, math.radians(90), 100) + + # pygame.draw.circle(s, (255, 0, 0), mid, (121/540) * scale) + + + # # Centre button + # if (r2 < (121.0f / 540.0f) * (121.0f / 540.0f)) return BUTTON_C; + + # WORD button = 0; + + # # Four quadrants + # if (iX > 0 && iY < 0) button = -iY > iX ? BUTTON_A1 : BUTTON_A2; + # if (iX > 0 && iY > 0) button = iX > iY ? BUTTON_A3 : BUTTON_A4; + # if (iX < 0 && iY > 0) button = iY > -iX ? BUTTON_A5 : BUTTON_A6; + # if (iX < 0 && iY < 0) button = -iX > -iY ? BUTTON_A7 : BUTTON_A8; + + # # Inner ring of buttons + # if (r2 < (298.0f / 540.0f) * (298.0f / 540.0f)) + # button = (button & 0xff00) | ((button & 0xff) << 1); + + # return button; + +region(0, 360, 720, 720) +region(720, 360, 720, 720) +surf.save("out.png") +# pygame.image.save(surf, "out.png") diff --git a/src/micetools/system_dummy/dummygdeliver/dummygdeliver.c b/src/micetools/system_dummy/dummygdeliver/dummygdeliver.c new file mode 100644 index 0000000..d0d19cd --- /dev/null +++ b/src/micetools/system_dummy/dummygdeliver/dummygdeliver.c @@ -0,0 +1,89 @@ +#include "dummygdeliver.h" + +#include "../../lib/ami/amiLog.h" +#include "../../lib/libpcp/libpcp.h" + +typedef struct { + pcpa_t m_pcp; + pcpa_cb_table_t m_pcpCallbacks[1]; +} mdgd_t; + +void mdgdRequest(pcpa_t* stream, void* mdgd) { + char* szRequest = pcpaGetCommand(stream, pcpaGetKeyword(stream, 0)); + + if (szRequest == NULL) { + pcpaSetSendPacket(stream, "result", "invalid_request"); + return; + } + pcpaSetSendPacket(stream, "response", szRequest); + + if (strcmp(szRequest, "terminatebeacon") == 0) { + // pcpaAddSendPacket(stream, "result", "success"); + pcpaAddSendPacket(stream, "result", "error"); + pcpaAddSendPacket(stream, "code", "4"); + return; + } else { + pcpaAddSendPacket(stream, "result", "invalid_request"); + // TODO: Remove this once enough has been implemented for most games? + pcpaPrint(stream); + return; + } +} +e_pcpa_t mdgdPcpStreamInit(mdgd_t* mdgd, unsigned short textPort, unsigned short binaryPort, + bool global) { + e_pcpa_t err; + + err = pcpaInitStream(&mdgd->m_pcp); + if (err != e_pcpa_ok) { + amiDebugLog("pcpaInitStream Error. Code:%d", err); + return err; + } + + err = pcpaSetCallbackFuncBuffer(&mdgd->m_pcp, mdgd->m_pcpCallbacks, 1); + if (err != e_pcpa_ok) { + amiDebugLog("pcpaSetCallBackFuncBuffer Error. Code:%d", err); + return err; + } + + pcpaSetCallbackFunc(&mdgd->m_pcp, "request", mdgdRequest, mdgd); + + err = pcpaOpenServerWithBinary(&mdgd->m_pcp, global ? OPEN_MODE_GLOBAL : OPEN_MODE_LOCAL, + textPort, binaryPort, 300000); + if (err != e_pcpa_ok && err != e_pcpa_to) { + amiDebugLog("pcpaOpenServerWithBinary Error. Code %d", err); + return e_pcpa_not_open; + } + if (global) + amiDebugLog("Listening on 0.0.0.0:%d (:%d)", textPort, binaryPort); + else + amiDebugLog("Listening on 127.0.0.1:%d (:%d)", textPort, binaryPort); + return e_pcpa_ok; +} + +void miceDummyGDeliver(unsigned short textPort, unsigned short binaryPort, bool global) { + mdgd_t* mdgd = malloc(sizeof *mdgd); + e_pcpa_t err; + + WSADATA wsaData; + if (WSAStartup(2, &wsaData)) { + amiDebugLog("WSAStartup Error. Code %d", GetLastError()); + return; + } + + err = mdgdPcpStreamInit(mdgd, textPort, binaryPort, global); + if (err != e_pcpa_ok) { + amiDebugLog("mdgdPcpStreamInit Error. Code %d", err); + return; + } + + while (1) { + err = pcpaServer(&mdgd->m_pcp, 16); + if (err == e_pcpa_to || err == e_pcpa_closed) err = e_pcpa_ok; + + if (err != e_pcpa_ok) { + amiDebugLog("Error pcpaServer. Code %d", err); + pcpaClose(&mdgd->m_pcp); + return; + } + } +} diff --git a/src/micetools/system_dummy/dummygdeliver/dummygdeliver.h b/src/micetools/system_dummy/dummygdeliver/dummygdeliver.h new file mode 100644 index 0000000..db1686d --- /dev/null +++ b/src/micetools/system_dummy/dummygdeliver/dummygdeliver.h @@ -0,0 +1,3 @@ +#include + +void miceDummyGDeliver(unsigned short textPort, unsigned short binaryPort, bool global); diff --git a/src/micetools/system_dummy/dummygdeliver/main.c b/src/micetools/system_dummy/dummygdeliver/main.c new file mode 100644 index 0000000..16800dc --- /dev/null +++ b/src/micetools/system_dummy/dummygdeliver/main.c @@ -0,0 +1,3 @@ +#include "dummygdeliver.h" + +int main() { miceDummyGDeliver(40108, 40112, false); } diff --git a/src/micetools/system_dummy/dummygdeliver/meson.build b/src/micetools/system_dummy/dummygdeliver/meson.build new file mode 100644 index 0000000..05d69d6 --- /dev/null +++ b/src/micetools/system_dummy/dummygdeliver/meson.build @@ -0,0 +1,7 @@ +dummygdeliver = static_library( + 'dummygdeliver', + sources: [ + 'dummygdeliver.c', + ], + link_with: [libpcp, amiDebug], +) diff --git a/src/micetools/system_dummy/dummyinstaller/dummyinstaller.c b/src/micetools/system_dummy/dummyinstaller/dummyinstaller.c index a3dfda5..7cc0d1d 100644 --- a/src/micetools/system_dummy/dummyinstaller/dummyinstaller.c +++ b/src/micetools/system_dummy/dummyinstaller/dummyinstaller.c @@ -9,15 +9,23 @@ typedef struct { } mdi_t; void mdiPcpRequest(pcpa_t* stream, void* mdi) { - char* request = pcpaGetCommand(stream, "request"); - pcpaSetSendPacket(stream, "response", request); + char* szRequest = pcpaGetCommand(stream, "request"); + pcpaSetSendPacket(stream, "response", szRequest); - if (strcmp(request, "check_appdata") == 0) { + if (strcmp(szRequest, "check_appdata") == 0) { pcpaAddSendPacket(stream, "result", "success"); - } else if (strcmp(request, "query_appdata_status") == 0) { + } else if (strcmp(szRequest, "query_appdata_status") == 0) { pcpaAddSendPacket(stream, "result", "success"); pcpaAddSendPacket(stream, "status", "available"); pcpaAddSendPacket(stream, "id", "----"); + } else if (strcmp(szRequest, "format_appdata") == 0) { + char* szId = pcpaGetCommand(stream, "id"); + if (szId) { + pcpaAddSendPacket(stream, "gameid", szId); + pcpaAddSendPacket(stream, "result", "success"); + } else { + pcpaAddSendPacket(stream, "result", "error"); + } } else { pcpaAddSendPacket(stream, "result", "invalid_request"); // TODO: Remove this once enough has been implemented for most games? @@ -25,7 +33,7 @@ void mdiPcpRequest(pcpa_t* stream, void* mdi) { } } -void mdiBeforeCb(pcpa_t* stream, void* data) { } +void mdiBeforeCb(pcpa_t* stream, void* data) {} e_pcpa_t mdiPcpStreamInit(mdi_t* mdi, unsigned short textPort, unsigned short binaryPort, bool global) { diff --git a/src/micetools/system_dummy/dummykeychip/callbacks-stub.c b/src/micetools/system_dummy/dummykeychip/callbacks-stub.c index 10f22ee..30fdab6 100644 --- a/src/micetools/system_dummy/dummykeychip/callbacks-stub.c +++ b/src/micetools/system_dummy/dummykeychip/callbacks-stub.c @@ -1,3 +1,5 @@ +#include "../../lib/mice/config.h" +#include "../../lib/mice/ipc.h" #include "callbacks.h" void mdkPcpVersion(pcpa_t* stream, void* data) { pcpaSetSendPacket(stream, KC_VERSION, "0104"); } @@ -5,9 +7,13 @@ void mdkPcpVersion(pcpa_t* stream, void* data) { pcpaSetSendPacket(stream, KC_VE void mdkPcpStatus(pcpa_t* stream, void* data) { pcpaSetSendPacket(stream, KC_STATUS, "available"); } void mdkPcpAbGameId(pcpa_t* stream, void* data) { - // TODO: Can we do better? - // pcpaSetSendPacket(stream, AB_GAMEID, "----"); - pcpaSetSendPacket(stream, AB_GAMEID, "SDEY"); + char gameId[5]; + if (MiceIpcGetGameId(gameId)) { + gameId[4] = '\0'; + pcpaSetSendPacket(stream, AB_GAMEID, gameId); + } else { + pcpaSetSendPacket(stream, AB_GAMEID, "----"); + } } void mdkPcpAbSystemFlag(pcpa_t* stream, void* data) { // systemflag 24 = billing + allnet, which should suffice for everything @@ -15,10 +21,10 @@ void mdkPcpAbSystemFlag(pcpa_t* stream, void* data) { } void mdkPcpAbModelType(pcpa_t* stream, void* data) { // ST - pcpaSetSendPacket(stream, AB_MODELTYPE, "2"); + pcpaSetSendPacket(stream, AB_MODELTYPE, "02"); } void mdkPcpAbFormatType(pcpa_t* stream, void* data) { - pcpaSetSendPacket(stream, AB_FORMATTYPE, "1"); + pcpaSetSendPacket(stream, AB_FORMATTYPE, "01"); } void mdkPcpAbRegion(pcpa_t* stream, void* data) { // All regions @@ -31,24 +37,156 @@ void mdkPcpAbPlatformId(pcpa_t* stream, void* data) { void mdkPcpAbNetworkAddress(pcpa_t* stream, void* data) { pcpaSetSendPacket(stream, AB_NETWORKADDRESS, "192.168.103.0"); } -void mdkPcpAbDvd(pcpa_t* stream, void* data) { - pcpaSetSendPacket(stream, AB_DVD, "01"); -} +void mdkPcpAbDvd(pcpa_t* stream, void* data) { pcpaSetSendPacket(stream, AB_DVD, "01"); } void mdkPcpPbKeyId(pcpa_t* stream, void* data) { - pcpaSetSendPacket(stream, BIL_KEYID, "A72E-01A00000000"); + pcpaSetSendPacket(stream, BIL_KEYID, MiceConfig.sysconf.keyid); } void mdkPcpPbMainId(pcpa_t* stream, void* data) { - pcpaSetSendPacket(stream, BIL_MAINID, ""); + pcpaSetSendPacket(stream, BIL_MAINID, MiceConfig.sysconf.serial); } void mdkPcpPbPlayCount(pcpa_t* stream, void* data) { pcpaSetSendPacket(stream, BIL_PLAYCOUNT, "00000000"); } void mdkPcpPbPlayLimit(pcpa_t* stream, void* data) { - // 8192 plays before checkin + // 8192 plays before check-in pcpaSetSendPacket(stream, BIL_PLAYLIMIT, "00002000"); } void mdkPcpPbNearfull(pcpa_t* stream, void* data) { - // Accounting mode = 1, nearfull = 512 - pcpaSetSendPacket(stream, BIL_NEARFULL, "00010200"); + // Accounting mode = 0, nearfull = 512 + pcpaSetSendPacket(stream, BIL_NEARFULL, "00000200"); +} + +void mdkPcpTdRestore(pcpa_t* stream, void* data) { + char* restart = pcpaGetCommand(stream, "restart"); + if (restart && strcmp(restart, "1") == 0) + pcpaSetSendPacket(stream, TRA_RESTORE, "1"); + else + pcpaSetSendPacket(stream, TRA_RESTORE, "2"); +} + +void mdkPcpTdPut(pcpa_t* stream, void* data) { pcpaSetSendPacket(stream, TRA_PUT, "6410"); } +void mdkPcpTdGet(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, TRA_GET, "0"); + pcpaAddSendPacket(stream, "address", "0"); +} + +// Real billing public key. Will not work on networks that have a custom key set +static BYTE BILLING_PUBKEY[] = { + 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, + 0xe9, 0xcb, 0xd9, 0x95, 0x35, 0xda, 0x64, 0x27, 0x02, 0x11, 0x42, 0x8b, 0x9c, 0x6e, 0x5a, + 0xf0, 0x5a, 0xbb, 0x01, 0x63, 0x18, 0x9c, 0xbf, 0xdd, 0xc7, 0x25, 0xcb, 0x22, 0x1f, 0xe9, + 0x98, 0x57, 0x9b, 0x8a, 0xf2, 0xf2, 0x37, 0xac, 0xd4, 0xd8, 0xed, 0x33, 0x0b, 0x46, 0x0e, + 0x7a, 0xdc, 0xe8, 0x5d, 0x90, 0x12, 0xee, 0x88, 0x76, 0x47, 0x6d, 0x19, 0x94, 0x83, 0x68, + 0x55, 0x76, 0x04, 0x17, 0xd6, 0x69, 0x21, 0xd5, 0xfa, 0xf0, 0x53, 0x99, 0xca, 0x38, 0xb5, + 0x4d, 0x9b, 0x97, 0x83, 0x8b, 0x9b, 0x8a, 0x2a, 0x28, 0x77, 0x98, 0x3b, 0xa8, 0x7a, 0xda, + 0xd4, 0x05, 0xcc, 0xfb, 0xc2, 0x14, 0xab, 0xf1, 0x80, 0x92, 0x51, 0x4b, 0x40, 0x96, 0x60, + 0x31, 0x77, 0xa9, 0x95, 0x70, 0xaa, 0xdd, 0xe8, 0xf0, 0x10, 0x8d, 0x26, 0x0d, 0x86, 0xe0, + 0x26, 0xd9, 0x27, 0x52, 0x86, 0xdf, 0x1f, 0x02, 0x03, 0x01, 0x00, 0x01, +}; +static BOOL BP_READ = FALSE; +// Not the real CA cert. This is the fake cert that a lot of people use +static BYTE CA_CERT[] = { + 0x30, 0x82, 0x03, 0x2d, 0x30, 0x82, 0x02, 0x15, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x43, + 0xae, 0xdc, 0x17, 0xc6, 0xea, 0xde, 0x6f, 0x69, 0x71, 0x6a, 0xba, 0xcc, 0x97, 0x99, 0x45, 0xcf, + 0x0d, 0xeb, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x30, 0x25, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x44, + 0x75, 0x6d, 0x6d, 0x79, 0x43, 0x41, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x08, 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x50, 0x4b, 0x49, 0x30, 0x20, 0x17, 0x0d, 0x31, 0x38, 0x31, + 0x30, 0x31, 0x35, 0x31, 0x39, 0x33, 0x30, 0x35, 0x31, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x31, 0x38, + 0x31, 0x30, 0x31, 0x35, 0x31, 0x39, 0x33, 0x30, 0x35, 0x31, 0x5a, 0x30, 0x25, 0x31, 0x10, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x43, 0x41, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x50, + 0x4b, 0x49, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xde, 0x1c, 0x42, 0x76, 0x2f, 0xe1, 0x57, 0xad, 0xc7, 0x56, 0x1b, 0xc2, 0xcd, + 0x67, 0xf5, 0xa4, 0x9d, 0x3c, 0xc4, 0xf6, 0x5b, 0x01, 0xe0, 0x8d, 0x46, 0x6f, 0xb2, 0xdc, 0xad, + 0x78, 0xbf, 0x62, 0xaf, 0xab, 0x12, 0x71, 0x7c, 0x8a, 0xbb, 0xb6, 0x87, 0x88, 0xf5, 0x89, 0xc9, + 0x83, 0x4a, 0xec, 0x0b, 0x05, 0x2f, 0xff, 0xa8, 0x43, 0x4e, 0xd8, 0xb7, 0xc4, 0xfa, 0x25, 0x35, + 0xce, 0x6b, 0x65, 0x86, 0x30, 0x1f, 0x1a, 0xff, 0x12, 0x61, 0x25, 0xe0, 0xd6, 0xcb, 0x00, 0x6c, + 0x1e, 0x92, 0xbf, 0x8b, 0xef, 0xcf, 0x45, 0x34, 0xa4, 0xd0, 0x80, 0xbe, 0x72, 0x7f, 0x2b, 0xc5, + 0x0d, 0xee, 0x40, 0x17, 0x1c, 0x1d, 0x6b, 0xc3, 0x97, 0x51, 0xbc, 0xee, 0x3b, 0x03, 0xe3, 0x35, + 0x34, 0xef, 0x12, 0xea, 0x96, 0x13, 0x2f, 0x98, 0xc9, 0x84, 0xaf, 0x81, 0x68, 0xd5, 0x66, 0x3f, + 0xac, 0x3d, 0x78, 0xe4, 0x38, 0x73, 0xed, 0x19, 0xbc, 0xa1, 0x4e, 0x11, 0x7d, 0x04, 0xfe, 0xec, + 0xb5, 0xa9, 0xc5, 0x2e, 0x4b, 0x7f, 0x3a, 0x65, 0xc3, 0xc5, 0x47, 0x14, 0x45, 0xbc, 0x4a, 0xd4, + 0xa3, 0xc5, 0xa3, 0xc8, 0xc0, 0x30, 0x57, 0xd1, 0xd9, 0xa1, 0x56, 0xb7, 0x87, 0x7b, 0x40, 0xcb, + 0x02, 0x9f, 0x0e, 0xb6, 0x23, 0x17, 0xc0, 0xed, 0x74, 0xd1, 0xbd, 0xdc, 0x6d, 0xe2, 0x83, 0x3a, + 0x6f, 0xe0, 0x8a, 0xe8, 0xe0, 0x0b, 0x84, 0x4a, 0xb3, 0xc4, 0xde, 0xde, 0x31, 0xe6, 0xe8, 0x8e, + 0xe1, 0x4c, 0x70, 0x50, 0xe6, 0xe8, 0x96, 0x45, 0x6c, 0x98, 0xe0, 0x33, 0xa3, 0xb8, 0x35, 0x42, + 0x21, 0x22, 0xd3, 0x09, 0x47, 0x4d, 0x05, 0xf5, 0x64, 0x7d, 0xdd, 0x5e, 0x06, 0x91, 0x00, 0x5e, + 0x6f, 0xbf, 0xf5, 0x74, 0xd9, 0x90, 0x27, 0x98, 0x8e, 0x64, 0x79, 0x77, 0xcf, 0x0c, 0x32, 0x2a, + 0x51, 0x17, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc4, 0x77, 0x5b, 0x5d, 0x0e, 0xdd, 0xa8, 0x5a, 0xb0, + 0x8f, 0x3c, 0xe5, 0xd3, 0x62, 0xfe, 0x51, 0xe7, 0xc4, 0xc0, 0xd8, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc4, 0x77, 0x5b, 0x5d, 0x0e, 0xdd, 0xa8, 0x5a, + 0xb0, 0x8f, 0x3c, 0xe5, 0xd3, 0x62, 0xfe, 0x51, 0xe7, 0xc4, 0xc0, 0xd8, 0x30, 0x0f, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0xa5, 0xc5, 0x86, 0xb3, 0xe0, 0x34, 0xe8, 0x06, 0xaa, 0xaf, 0xd5, 0x40, 0xc6, 0x10, 0xe9, + 0x8b, 0x8b, 0xd2, 0x57, 0x42, 0xbe, 0xfb, 0xdd, 0x76, 0x20, 0x95, 0x91, 0xbf, 0xaa, 0xd1, 0x47, + 0x2e, 0xc7, 0xcf, 0x86, 0xfb, 0x3a, 0xfe, 0x54, 0x39, 0x9e, 0xc0, 0x1e, 0xc8, 0x67, 0x40, 0xd5, + 0x90, 0xbd, 0xdf, 0x50, 0xa7, 0xe7, 0xa1, 0x84, 0xda, 0x0b, 0x8c, 0xbe, 0x63, 0xa1, 0x69, 0x3d, + 0x06, 0x6c, 0x0c, 0x3c, 0x60, 0x0f, 0x84, 0x10, 0x2f, 0x89, 0xd0, 0xe1, 0xf1, 0x05, 0x0b, 0x50, + 0x02, 0x03, 0x25, 0x60, 0xc6, 0x17, 0xc0, 0xad, 0x0c, 0x36, 0x96, 0xf2, 0x81, 0xd8, 0x8e, 0xc0, + 0x53, 0x05, 0x25, 0x63, 0x2a, 0xed, 0x4c, 0x6b, 0xf3, 0x95, 0x31, 0xcf, 0x6f, 0x17, 0x41, 0x23, + 0x31, 0x46, 0xf7, 0x2e, 0xd9, 0x4a, 0x66, 0x43, 0x54, 0xaa, 0x65, 0x02, 0x8e, 0x51, 0x3f, 0x57, + 0x66, 0xd8, 0x62, 0x3a, 0x95, 0x2c, 0xd4, 0xe8, 0x82, 0xb5, 0xce, 0xe0, 0xac, 0x57, 0xca, 0xa7, + 0xc1, 0x15, 0xd5, 0x33, 0x4c, 0x20, 0x7f, 0xa9, 0x7e, 0xd5, 0xf8, 0x73, 0x67, 0xb3, 0x30, 0xea, + 0x20, 0x51, 0xc5, 0xfb, 0x0c, 0x93, 0x92, 0x53, 0x12, 0x3b, 0x04, 0xd4, 0x22, 0xbb, 0xd6, 0xe1, + 0x1e, 0xb3, 0xa2, 0x35, 0xcb, 0xd3, 0x08, 0xd1, 0x76, 0xfb, 0x41, 0xa9, 0x74, 0xaf, 0x3c, 0x5a, + 0xb9, 0x1d, 0x62, 0x4f, 0x76, 0x0d, 0x1d, 0x7d, 0x5c, 0xb2, 0x54, 0x36, 0x6e, 0xef, 0x23, 0xce, + 0xde, 0x03, 0x09, 0x2e, 0xa9, 0x03, 0x90, 0x7b, 0x1a, 0x60, 0xbb, 0x36, 0x20, 0xa1, 0xd4, 0x5e, + 0xa2, 0x42, 0x6c, 0x70, 0x89, 0xff, 0x08, 0x69, 0x31, 0x63, 0x58, 0x62, 0x8c, 0xc3, 0x14, 0x6a, + 0xe1, 0x7a, 0xd3, 0x52, 0xbe, 0x8d, 0xec, 0x2a, 0x1b, 0x7d, 0xc2, 0xf4, 0x0a, 0xd3, 0xc9, 0x9c, + 0xca, +}; +static BOOL CA_READ = FALSE; + +void mdkPcpPbSignaturePubKey(pcpa_t* stream, void* data) { + if (!BP_READ) { + BP_READ = TRUE; + HANDLE hFile = CreateFileA(MiceIpcRelativePath("billing.pub"), GENERIC_READ, + FILE_SHARE_READ, NULL, 0, OPEN_EXISTING, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + DWORD nRead; + ReadFile(hFile, BILLING_PUBKEY, sizeof BILLING_PUBKEY, &nRead, NULL); + CloseHandle(hFile); + } + } + + pcpaSetBinaryMode(stream, binary_mode_send); + pcpaSetSendBinaryBuffer(stream, BILLING_PUBKEY, sizeof BILLING_PUBKEY); + pcpaSetSendPacket(stream, BIL_SIGNATURE, "0"); + pcpaAddSendPacket(stream, "port", "40107"); + char sSize[8]; + sprintf_s(sSize, _countof(sSize), "%d", sizeof BILLING_PUBKEY); + pcpaAddSendPacket(stream, "size", sSize); +} +void mdkPcpPbCaCertification(pcpa_t* stream, void* data) { + if (!CA_READ) { + CA_READ = TRUE; + HANDLE hFile = CreateFileA(MiceIpcRelativePath("ca.crt"), GENERIC_READ, FILE_SHARE_READ, + NULL, 0, OPEN_EXISTING, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + DWORD nRead; + ReadFile(hFile, CA_CERT, sizeof CA_CERT, &nRead, NULL); + CloseHandle(hFile); + } + } + + pcpaSetBinaryMode(stream, binary_mode_send); + pcpaSetSendBinaryBuffer(stream, CA_CERT, sizeof CA_CERT); + pcpaSetSendPacket(stream, BIL_CACERT, "0"); + pcpaAddSendPacket(stream, "port", "40107"); + char sSize[8]; + sprintf_s(sSize, _countof(sSize), "%d", sizeof CA_CERT); + pcpaAddSendPacket(stream, "size", sSize); +} + +void mdkPcpTdLogicalErase(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, TRA_LOGICALERASE, "0"); +} +void mdkPcpTdSectorErase(pcpa_t* stream, void* data) { + pcpaSetSendPacket(stream, TRA_LOGICALERASE, "0"); } diff --git a/src/micetools/system_dummy/dummykeychip/dummykeychip.c b/src/micetools/system_dummy/dummykeychip/dummykeychip.c index 03431fc..99881ab 100644 --- a/src/micetools/system_dummy/dummykeychip/dummykeychip.c +++ b/src/micetools/system_dummy/dummykeychip/dummykeychip.c @@ -2,6 +2,7 @@ #include "../../lib/ami/amiLog.h" #include "../../lib/libpcp/libpcp.h" +#include "../../lib/mice/ipc.h" #include "callbacks.h" typedef struct { @@ -55,14 +56,14 @@ e_pcpa_t mdkPcpStreamInit(mdk_t* mdk, unsigned short textPort, unsigned short bi pcpaSetCallbackFunc(&mdk->m_pcp, BIL_PLAYCOUNT, mdkPcpPbPlayCount, NULL); pcpaSetCallbackFunc(&mdk->m_pcp, BIL_PLAYLIMIT, mdkPcpPbPlayLimit, NULL); pcpaSetCallbackFunc(&mdk->m_pcp, BIL_NEARFULL, mdkPcpPbNearfull, NULL); - // pcpaSetCallbackFunc(&mdk->m_pcp, BIL_SIGNATURE, mdkPcpPbSignaturePubKey, NULL); - // pcpaSetCallbackFunc(&mdk->m_pcp, BIL_CACERT, mdkPcpPbCaCertification, NULL); + pcpaSetCallbackFunc(&mdk->m_pcp, BIL_SIGNATURE, mdkPcpPbSignaturePubKey, NULL); + pcpaSetCallbackFunc(&mdk->m_pcp, BIL_CACERT, mdkPcpPbCaCertification, NULL); // Tracedata - // pcpaSetCallbackFunc(&mdk->m_pcp, TRA_RESTORE, mdkPcpTdRestore, NULL); - // pcpaSetCallbackFunc(&mdk->m_pcp, TRA_PUT, mdkPcpTdPut, NULL); - // pcpaSetCallbackFunc(&mdk->m_pcp, TRA_GET, mdkPcpTdGet, NULL); - // pcpaSetCallbackFunc(&mdk->m_pcp, TRA_LOGICALERASE, mdkPcpTdLogicalErase, NULL); - // pcpaSetCallbackFunc(&mdk->m_pcp, TRA_SECTOREERASE, mdkPcpTdSectorErase, NULL); + pcpaSetCallbackFunc(&mdk->m_pcp, TRA_RESTORE, mdkPcpTdRestore, NULL); + pcpaSetCallbackFunc(&mdk->m_pcp, TRA_PUT, mdkPcpTdPut, NULL); + pcpaSetCallbackFunc(&mdk->m_pcp, TRA_GET, mdkPcpTdGet, NULL); + pcpaSetCallbackFunc(&mdk->m_pcp, TRA_LOGICALERASE, mdkPcpTdLogicalErase, NULL); + pcpaSetCallbackFunc(&mdk->m_pcp, TRA_SECTOREERASE, mdkPcpTdSectorErase, NULL); // Storage // pcpaSetCallbackFunc(&mdk->m_pcp, KC_EEPROM, mdkPcpEeprom, NULL); // pcpaSetCallbackFunc(&mdk->m_pcp, KC_NVRAM0, mdkPcpNvram, NULL); @@ -93,6 +94,8 @@ void miceDummyKeychip(unsigned short textPort, unsigned short binaryPort, bool g mdk_t* mdk = malloc(sizeof *mdk); e_pcpa_t err; + MiceIpcSetup(FALSE); + WSADATA wsaData; if (WSAStartup(2, &wsaData)) { amiDebugLog("WSAStartup Error. Code %d", GetLastError()); diff --git a/src/micetools/system_dummy/dummykeychip/meson.build b/src/micetools/system_dummy/dummykeychip/meson.build index b5283a5..8cc7890 100644 --- a/src/micetools/system_dummy/dummykeychip/meson.build +++ b/src/micetools/system_dummy/dummykeychip/meson.build @@ -5,7 +5,7 @@ dummykeychip = static_library( 'callbacks-crypto.c', 'dummykeychip.c', ], - link_with: [libpcp, amiDebug, util_lib], + link_with: [libpcp, amiDebug, util_lib, mice_lib], include_directories: [openssl_inc], dependencies: [openssl_lib], ) diff --git a/src/micetools/system_dummy/dummymaster/dummymaster.c b/src/micetools/system_dummy/dummymaster/dummymaster.c index 33c19b4..6b142ab 100644 --- a/src/micetools/system_dummy/dummymaster/dummymaster.c +++ b/src/micetools/system_dummy/dummymaster/dummymaster.c @@ -9,45 +9,138 @@ typedef struct { pcpa_cb_table_t m_pcpCallbacks[MDM_NUM_CALLBACKS]; } mdm_t; +// USB void mdmPcpReconnectUsbDevice(pcpa_t* stream, void* mdm) { - pcpaSetSendPacket(stream, MXM_RECONNECT_USB, "0"); -} -void mdmPcpCheckDevelopMode(pcpa_t* stream, void* mdm) { - // TODO: Do we want to support the develop flag in dummymaster? - pcpaSetSendPacket(stream, MXM_DEVELOP, "0"); -} -void mdmPcpLogAvailable(pcpa_t* stream, void* mdm) { - pcpaSetSendPacket(stream, MXM_LOG_AVAILALBE, "0"); + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), "0"); } + +// Logs +void mdmPcpEraseLog(pcpa_t* stream, void* mdm) { pcpaSetSendPacket(stream, MXM_ERASE_LOG, "0"); } void mdmPcpOutputLog(pcpa_t* stream, void* mdm) { - pcpaSetSendPacket(stream, MXM_OUTPUT_LOG, "0"); + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), "0"); pcpaAddSendPacket(stream, "path", ""); } -void mdmPcpEraseLog(pcpa_t* stream, void* mdm) { pcpaSetSendPacket(stream, MXM_ERASE_LOG, "0"); } -void mdmPcpCurrentFgprocess(pcpa_t* stream, void* mdm) { - // TODO: Handle set - pcpaSetSendPacket(stream, MXM_FG_CURRENT, "0"); // mxMaster->m_current +void mdmPcpLogAvailable(pcpa_t* stream, void* mdm) { + static bool logs_on = false; + + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), logs_on ? "1" : "0"); + + char* command = pcpaGetCommand(stream, pcpaGetKeyword(stream, 0)); + if (command) { + if (strcmp(command, "0") == 0) + logs_on = false; + else if (strcmp(command, "1") == 0) + logs_on = true; + else if (strcmp(command, "?") != 0) + pcpaAddSendPacket(stream, "code", "1"); + } + + pcpaAddSendPacket(stream, "backup_count", "1"); + pcpaAddSendPacket(stream, "clear_count", "100"); + pcpaAddSendPacket(stream, "interval", "60"); } + +// Develop flag +void mdmPcpCheckDevelopMode(pcpa_t* stream, void* mdm) { + // TODO: Do we want to support the develop flag in dummymaster? + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), "0"); +} + +// Foreground control +static bool fault = false; +void mdmPcpGetStartCount(pcpa_t* stream, void* mdm) { + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), "1"); +} +void mdmPcpFaultFgprocess(pcpa_t* stream, void* mdm) { + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), fault ? "1" : "0"); +} +static BYTE nextFg = 3; +unsigned char g_szFault[255 + 1]; +BYTE g_nFault = 0; + +extern HANDLE _MiceLauncherNextEvent; +extern BYTE _MiceLauncherNext; +static void _switchNext() { + _MiceLauncherNext = nextFg; + if (_MiceLauncherNextEvent != INVALID_HANDLE_VALUE) SetEvent(_MiceLauncherNextEvent); +} +static void _faultRecv(pcpa_t* stream, void* data) { + g_szFault[g_nFault] = '\0'; + printf("Received fault: %.*s\n", g_nFault, g_szFault); + _switchNext(); +} + void mdmPcpNextFgprocess(pcpa_t* stream, void* mdm) { // TODO: Handle set, Handle receive if "size" present - pcpaSetSendPacket(stream, MXM_FG_NEXT, "0"); // mxMaster->m_current - // mxMaster->m_next + char szNext[2]; + szNext[0] = '0' + nextFg; + szNext[1] = '\0'; + + char* szSize = pcpaGetCommand(stream, "size"); + char* szCommand = pcpaGetCommand(stream, pcpaGetKeyword(stream, 0)); + + if (szSize != NULL) { + int nSize = 0; + if (!szCommand || sscanf_s(szSize, "%d", &nSize) != 1) { + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), szNext); + pcpaAddSendPacket(stream, "code", "1"); + return; + } + if (strcmp(szCommand, "?") == 0) { + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), szNext); + + pcpaAddSendPacket(stream, "port", "40101"); + char szSendSize[4]; + sprintf_s(szSendSize, sizeof szSendSize, "%d", g_nFault); + szSendSize[3] = '\0'; + pcpaAddSendPacket(stream, "size", szSendSize); + if (g_nFault) { + // SEND FAULT + pcpaSetBinaryMode(stream, binary_mode_send); + pcpaSetSendBinaryBuffer(stream, g_szFault, g_nFault); + } + return; + } + + if (nSize > 255) nSize = 255; + g_nFault = nSize & 0xff; + pcpaSetBinaryMode(stream, binary_mode_recv); + pcpaSetAfterBinaryModeCallBackFunc(stream, _faultRecv, NULL); + pcpaSetRecvBinaryBuffer(stream, g_szFault, g_nFault); + + // RECEIVE FAULT + nextFg = szCommand[0] - '0'; + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), szCommand); + pcpaAddSendPacket(stream, "size", szSize); + pcpaAddSendPacket(stream, "port", "40101"); + return; + } + + if (!szCommand) { + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), "?"); + pcpaAddSendPacket(stream, "code", "1"); + return; + } + if (strcmp(szCommand, "?") == 0) { + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), szNext); + return; + } + pcpaSetSendPacket(stream, pcpaGetKeyword(stream, 0), szCommand); + nextFg = szCommand[0] - '0'; + _switchNext(); + return; +} +void mdmPcpCurrentFgprocess(pcpa_t* stream, void* mdm) { + pcpaSetSendPacket(stream, MXM_FG_CURRENT, "1"); } void mdmPcpActiveFgprocess(pcpa_t* stream, void* mdm) { pcpaSetSendPacket(stream, MXM_FG_ACTIVE, "0"); } -void mdmPcpFaultFgprocess(pcpa_t* stream, void* mdm) {} -void mdmPcpGetStartCount(pcpa_t* stream, void* mdm) { - pcpaSetSendPacket(stream, MXM_FG_GETCOUNT, "1"); -} void mdmPcpSetStartCount(pcpa_t* stream, void* mdm) { pcpaSetSendPacket(stream, MXM_FG_SETCOUNT, "0"); } - -void mdmBeforeCb(pcpa_t* stream, void* data) { - amiDebugLog("in:%s", (char*)data); -} +void mdmBeforeCb(pcpa_t* stream, void* data) { amiDebugLog("in:%s", (char*)data); } e_pcpa_t mdmPcpStreamInit(mdm_t* mdm, unsigned short textPort, unsigned short binaryPort, bool global) { diff --git a/src/micetools/system_dummy/dummynetwork/dummynetwork.c b/src/micetools/system_dummy/dummynetwork/dummynetwork.c new file mode 100644 index 0000000..24fabaa --- /dev/null +++ b/src/micetools/system_dummy/dummynetwork/dummynetwork.c @@ -0,0 +1,134 @@ +#include "dummynetwork.h" + +#include "../../lib/ami/amiLog.h" +#include "../../lib/libpcp/libpcp.h" +#include "../../lib/mice/config.h" + +typedef struct { + pcpa_t m_pcp; + pcpa_cb_table_t m_pcpCallbacks[1]; +} mdn_t; + +void mdnRequest(pcpa_t* stream, void* mdn) { + char* szRequest = pcpaGetCommand(stream, pcpaGetKeyword(stream, 0)); + + if (szRequest == NULL) { + pcpaSetSendPacket(stream, "result", "invalid_request"); + return; + } + pcpaSetSendPacket(stream, "response", szRequest); + + if (strcmp(szRequest, "query_dhcp_status") == 0) { + pcpaAddSendPacket(stream, "result", "0"); + pcpaAddSendPacket(stream, "dhcp_status", "3"); + return; + } else if (strcmp(szRequest, "query_nic_status") == 0) { + char ipAddress[4 * 4]; + + pcpaAddSendPacket(stream, "result", "0"); + pcpaAddSendPacket(stream, "status", "1"); + + sprintf_s(ipAddress, _countof(ipAddress), "%hhu.%hhu.%hhu.%hhu", + (MiceConfig.network.ip_address >> 24) & 0xff, + (MiceConfig.network.ip_address >> 16) & 0xff, + (MiceConfig.network.ip_address >> 8) & 0xff, + MiceConfig.network.ip_address & 0xff); + pcpaAddSendPacket(stream, "ip_address", ipAddress); + sprintf_s(ipAddress, _countof(ipAddress), "%hhu.%hhu.%hhu.%hhu", + (MiceConfig.network.subnet_mask >> 24) & 0xff, + (MiceConfig.network.subnet_mask >> 16) & 0xff, + (MiceConfig.network.subnet_mask >> 8) & 0xff, + MiceConfig.network.subnet_mask & 0xff); + pcpaAddSendPacket(stream, "subnetmask", ipAddress); + sprintf_s(ipAddress, _countof(ipAddress), "%hhu.%hhu.%hhu.%hhu", + (MiceConfig.network.gateway >> 24) & 0xff, + (MiceConfig.network.gateway >> 16) & 0xff, + (MiceConfig.network.gateway >> 8) & 0xff, MiceConfig.network.gateway & 0xff); + pcpaAddSendPacket(stream, "gateway", ipAddress); + if (MiceConfig.network.primary_dns) { + sprintf_s(ipAddress, _countof(ipAddress), "%hhu.%hhu.%hhu.%hhu", + (MiceConfig.network.primary_dns >> 24) & 0xff, + (MiceConfig.network.primary_dns >> 16) & 0xff, + (MiceConfig.network.primary_dns >> 8) & 0xff, + MiceConfig.network.primary_dns & 0xff); + pcpaAddSendPacket(stream, "primary_dns", ipAddress); + } else { + pcpaAddSendPacket(stream, "primary_dns", "-"); + } + if (MiceConfig.network.secondary_dns) { + sprintf_s(ipAddress, _countof(ipAddress), "%hhu.%hhu.%hhu.%hhu", + (MiceConfig.network.secondary_dns >> 24) & 0xff, + (MiceConfig.network.secondary_dns >> 16) & 0xff, + (MiceConfig.network.secondary_dns >> 8) & 0xff, + MiceConfig.network.secondary_dns & 0xff); + pcpaAddSendPacket(stream, "secondary_dns", ipAddress); + } else { + pcpaAddSendPacket(stream, "secondary_dns", "-"); + } + return; + } else { + pcpaAddSendPacket(stream, "result", "invalid_request"); + return; + } +} + +e_pcpa_t mdnPcpStreamInit(mdn_t* mdn, unsigned short textPort, unsigned short binaryPort, + bool global) { + e_pcpa_t err; + + err = pcpaInitStream(&mdn->m_pcp); + if (err != e_pcpa_ok) { + amiDebugLog("pcpaInitStream Error. Code:%d", err); + return err; + } + + err = pcpaSetCallbackFuncBuffer(&mdn->m_pcp, mdn->m_pcpCallbacks, 1); + if (err != e_pcpa_ok) { + amiDebugLog("pcpaSetCallBackFuncBuffer Error. Code:%d", err); + return err; + } + + pcpaSetCallbackFunc(&mdn->m_pcp, "request", mdnRequest, mdn); + + err = pcpaOpenServerWithBinary(&mdn->m_pcp, global ? OPEN_MODE_GLOBAL : OPEN_MODE_LOCAL, + textPort, binaryPort, 300000); + if (err != e_pcpa_ok && err != e_pcpa_to) { + amiDebugLog("pcpaOpenServerWithBinary Error. Code %d", err); + return e_pcpa_not_open; + } + if (global) + amiDebugLog("Listening on 0.0.0.0:%d (:%d)", textPort, binaryPort); + else + amiDebugLog("Listening on 127.0.0.1:%d (:%d)", textPort, binaryPort); + return e_pcpa_ok; +} + +void miceDummyNetwork(unsigned short textPort, unsigned short binaryPort, bool global) { + mdn_t* mdn = malloc(sizeof *mdn); + e_pcpa_t err; + + WSADATA wsaData; + if (WSAStartup(2, &wsaData)) { + amiDebugLog("WSAStartup Error. Code %d", GetLastError()); + return; + } + + load_mice_config(); + + err = mdnPcpStreamInit(mdn, textPort, binaryPort, global); + if (err != e_pcpa_ok) { + amiDebugLog("mdnPcpStreamInit Error. Code %d", err); + return; + } + + while (1) { + err = pcpaServer(&mdn->m_pcp, 16); + if (err == e_pcpa_to || err == e_pcpa_closed) err = e_pcpa_ok; + + if (err != e_pcpa_ok) { + amiDebugLog("Error pcpaServer. Code %d", err); + pcpaClose(&mdn->m_pcp); + return; + } + } +} diff --git a/src/micetools/system_dummy/dummynetwork/dummynetwork.h b/src/micetools/system_dummy/dummynetwork/dummynetwork.h new file mode 100644 index 0000000..f642a91 --- /dev/null +++ b/src/micetools/system_dummy/dummynetwork/dummynetwork.h @@ -0,0 +1,3 @@ +#include + +void miceDummyNetwork(unsigned short textPort, unsigned short binaryPort, bool global); diff --git a/src/micetools/system_dummy/dummynetwork/main.c b/src/micetools/system_dummy/dummynetwork/main.c new file mode 100644 index 0000000..5eaf823 --- /dev/null +++ b/src/micetools/system_dummy/dummynetwork/main.c @@ -0,0 +1,3 @@ +#include "dummynetwork.h" + +int main() { miceDummyNetwork(40104, 40105, false); } diff --git a/src/micetools/system_dummy/dummynetwork/meson.build b/src/micetools/system_dummy/dummynetwork/meson.build new file mode 100644 index 0000000..68c453a --- /dev/null +++ b/src/micetools/system_dummy/dummynetwork/meson.build @@ -0,0 +1,7 @@ +dummynetwork = static_library( + 'dummynetwork', + sources: [ + 'dummynetwork.c', + ], + link_with: [libpcp, amiDebug, mice_lib], +) diff --git a/src/micetools/system_dummy/meson.build b/src/micetools/system_dummy/meson.build index 656a6cd..db3d075 100644 --- a/src/micetools/system_dummy/meson.build +++ b/src/micetools/system_dummy/meson.build @@ -1,3 +1,5 @@ subdir('dummykeychip') subdir('dummymaster') subdir('dummyinstaller') +subdir('dummygdeliver') +subdir('dummynetwork') diff --git a/src/micetools/util/iatrepair.c b/src/micetools/util/iatrepair.c new file mode 100644 index 0000000..979982d --- /dev/null +++ b/src/micetools/util/iatrepair.c @@ -0,0 +1,762 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#define _Noreturn __declspec(noreturn) +#endif + +typedef struct { size_t fo; } fo_t; +typedef struct { uintptr_t va; } va_t; +typedef struct { uint32_t rva; } rva_t; + +struct list_node { + struct list_node *next; +}; + +struct list { + struct list_node *head; + struct list_node *tail; +}; + +struct cursor { + fo_t fpos; + rva_t vpos; +}; + +struct pe { + uint8_t *file; + size_t file_size; + uintptr_t base; +}; + +struct iat_list { + struct list modules; + size_t module_count; + struct cursor term_pos; +}; + +struct iat_module { + struct list_node node; + struct list entries; + char *name; + size_t name_len; + struct cursor desc_pos; + struct cursor name_pos; +}; + +struct iat_entry { + struct list_node node; + rva_t site; + char *name; + size_t name_len; + struct cursor oft_pos; + struct cursor hint_pos; + struct cursor name_pos; +}; + +static _Noreturn void die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + exit(EXIT_FAILURE); +} + +static void *xmalloc(size_t nbytes) +{ + void *mem; + + mem = malloc(nbytes); + + if (mem == NULL) { + abort(); + } + + return mem; +} + +static void *xcalloc(size_t nbytes) +{ + void *mem; + + mem = calloc(nbytes, 1); + + if (mem == NULL) { + abort(); + } + + return mem; +} + +static void *xrealloc(void *src, size_t nbytes) +{ + void *dest; + + dest = realloc(src, nbytes); + + if (dest == NULL) { + abort(); + } + + return dest; +} + +static char *xstrdup(const char *src) +{ + char *dest; + size_t len; + + assert(src != NULL); + + len = strlen(src) + 1; + dest = xmalloc(len); + memcpy(dest, src, len); + + return dest; +} + +static void file_load( + const char *filename, + void **bytes_out, + size_t *nbytes_out) +{ + assert(filename != NULL); + assert(bytes_out != NULL); + assert(nbytes_out != NULL); + + size_t nbytes; + size_t nread; + void *bytes; + FILE *f; + + f = fopen(filename, "rb"); + + if (f == NULL) { + die("%s: %s\n", filename, strerror(errno)); + } + + fseek(f, 0, SEEK_END); + nbytes = ftell(f); + fseek(f, 0, SEEK_SET); + + bytes = xmalloc(nbytes); + nread = fread(bytes, 1, nbytes, f); + + if (nread != nbytes) { + die("%s: Read error\n", filename); + } + + fclose(f); + + *bytes_out = bytes; + *nbytes_out = nbytes; +} + +static void file_save(const char *filename, void *bytes, size_t nbytes) +{ + size_t nwrit; + FILE *f; + + assert(filename != NULL); + assert(bytes != NULL); + + f = fopen(filename, "wb"); + + if (f == NULL) { + die("%s: %s\n", filename, strerror(errno)); + } + + nwrit = fwrite(bytes, 1, nbytes, f); + + if (nwrit != nbytes) { + die("%s: Write error\n", filename); + } + + fclose(f); +} + +static void list_init(struct list *list) +{ + assert(list != NULL); + memset(list, 0, sizeof(*list)); +} + +static void list_append(struct list *list, struct list_node *node) +{ + assert(list != NULL); + assert(node != NULL); + + node->next = NULL; + + if (list->head != NULL) { + list->tail->next = node; + list->tail = node; + } else { + list->head = node; + list->tail = node; + } +} + +static void *mem_offset(void *ptr, size_t off) +{ + uint8_t *base; + + assert(ptr != NULL); + + base = ptr; + + return base + off; +} + +static void cursor_advance(struct cursor *cursor, size_t delta) +{ + assert(cursor != NULL); + + cursor->fpos.fo += delta; + cursor->vpos.rva += delta; +} + +static void cursor_align(struct cursor *cursor, size_t alignment) +{ + size_t fdiff; + size_t vdiff; + + assert(cursor != NULL); + + fdiff = cursor->fpos.fo % alignment; + vdiff = cursor->vpos.rva % alignment; + + //assert(fdiff == vdiff); + + if (vdiff != 0) { + cursor_advance(cursor, alignment - vdiff); + } +} + +static IMAGE_NT_HEADERS *pe_get_nt_header(void *file) +{ + IMAGE_DOS_HEADER *dh; + IMAGE_NT_HEADERS *nth; + + assert(file != NULL); + + dh = (IMAGE_DOS_HEADER *) file; + nth = mem_offset(file, dh->e_lfanew); + + return nth; +} + +static size_t pe_get_section_count(void *bytes) +{ + IMAGE_NT_HEADERS *nth; + + nth = pe_get_nt_header(bytes); + + return nth->FileHeader.NumberOfSections; +} + +static IMAGE_SECTION_HEADER *pe_get_section_headers(void *bytes) +{ + IMAGE_NT_HEADERS *nth; + + nth = pe_get_nt_header(bytes); + + return mem_offset( + nth->OptionalHeader.DataDirectory, + nth->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY)); +} + +/* Takes ownership of bytes! Well, or would, if we ever freed anything. */ +static void pe_init(struct pe *pe, void *bytes, size_t nbytes) +{ + IMAGE_NT_HEADERS *nth; + + assert(pe != NULL); + assert(bytes != NULL); + + nth = pe_get_nt_header(bytes); + + pe->file = bytes; + pe->file_size = nbytes; + pe->base = nth->OptionalHeader.ImageBase; +} + +static rva_t pe_rva_from_va(struct pe *pe, va_t va) +{ + int64_t tmp; + rva_t result; + + assert(pe != NULL); + + tmp = (int64_t) va.va - (int64_t) pe->base; + + if (tmp > INT32_MAX) { + die( "Excessive distance between base %p and VA %p. Check both " + "inputs were derived from the same process.\n", + pe->base, + va.va); + } + + result.rva = tmp; + + return result; +} + +static fo_t pe_fo_from_rva(struct pe *pe, rva_t rva) +{ + IMAGE_SECTION_HEADER *sections; + IMAGE_SECTION_HEADER *pos; + fo_t result; + size_t section_pos; + size_t nsections; + size_t i; + + assert(pe != NULL); + + sections = pe_get_section_headers(pe->file); + nsections = pe_get_section_count(pe->file); + + for (i = 0 ; i < nsections ; i++) { + pos = §ions[i]; + + if ( rva.rva >= pos->VirtualAddress && + rva.rva < pos->VirtualAddress + pos->Misc.VirtualSize) { + section_pos = rva.rva - pos->VirtualAddress; + + if (section_pos >= pos->SizeOfRawData) { + die( "%p: Section %8s offset %p > backing store size %p\n", + rva.rva, + pos->Name, + section_pos, + pos->SizeOfRawData); + } + + result.fo = pos->PointerToRawData + section_pos; + + return result; + } + } + + die("%p: RVA is not located inside a section\n", rva); +} + +static void pe_get_limit(struct pe *pe, struct cursor *limit) +{ + IMAGE_SECTION_HEADER *sections; + IMAGE_SECTION_HEADER *pos; + size_t nsections; + size_t i; + + assert(pe != NULL); + assert(limit != NULL); + + sections = pe_get_section_headers(pe->file); + nsections = pe_get_section_count(pe->file); + + limit->fpos.fo = 0; + limit->vpos.rva = 0; + + for (i = 0 ; i < nsections ; i++) { + pos = §ions[i]; + + if (limit->fpos.fo < pos->PointerToRawData + pos->SizeOfRawData) { + limit->fpos.fo = pos->PointerToRawData + pos->SizeOfRawData; + } + + if (limit->vpos.rva < pos->VirtualAddress + pos->Misc.VirtualSize) { + limit->vpos.rva = pos->VirtualAddress + pos->Misc.VirtualSize; + } + } +} + +static void pe_add_section(struct pe *pe, const IMAGE_SECTION_HEADER *hdr) +{ + IMAGE_NT_HEADERS *nth; + IMAGE_SECTION_HEADER *sections; + uint32_t vlimit; + size_t nsections; + size_t i; + fo_t limit; + fo_t pos; + + assert(pe != NULL); + assert(hdr != NULL); + + pos.fo = hdr->PointerToRawData; + limit.fo = pos.fo + hdr->SizeOfRawData; + + if (pe->file_size < limit.fo) { + pe->file = xrealloc(pe->file, limit.fo); + memset(&pe->file[pe->file_size], 0, limit.fo - pe->file_size); + pe->file_size = limit.fo; + } + + nth = pe_get_nt_header(pe->file); + sections = pe_get_section_headers(pe->file); + nsections = nth->FileHeader.NumberOfSections; + + memcpy(§ions[nsections], hdr, sizeof(*hdr)); + nth->FileHeader.NumberOfSections++; + + for (i = 0 ; i < nth->FileHeader.NumberOfSections ; i++) { + vlimit = sections[i].VirtualAddress + sections[i].Misc.VirtualSize; + + if (nth->OptionalHeader.SizeOfImage < vlimit) { + nth->OptionalHeader.SizeOfImage = vlimit; + } + } +} + +static void pe_write(struct pe *pe, fo_t fo, const void *bytes, size_t nbytes) +{ + size_t end; + + assert(pe != NULL); + assert(pe->file != NULL); + assert(bytes != NULL); + + if (fo.fo >= pe->file_size) { + die("PE write begins out of bounds: %p >= %p\n", fo.fo, pe->file_size); + } + + end = fo.fo + nbytes; + + if (end >= pe->file_size) { + die("PE write ends out of bounds: %p >= %p\n", end, pe->file_size); + } + + memcpy(pe->file + fo.fo, bytes, nbytes); +} + +static void pe_write_dd(struct pe *pe, size_t entry_no, rva_t rva, size_t len) +{ + IMAGE_NT_HEADERS *nth; + IMAGE_DATA_DIRECTORY *dd; + + assert(pe != NULL); + + nth = pe_get_nt_header(pe->file); + dd = &nth->OptionalHeader.DataDirectory[entry_no]; + dd->VirtualAddress = rva.rva; + dd->Size = len; +} + +static struct iat_module *iat_list_get_module( + struct iat_list *list, + const char *name) +{ + struct iat_module *module; + struct iat_module *newmod; + + assert(list != NULL); + assert(name != NULL); + + if (list->modules.tail != NULL) { + module = CONTAINING_RECORD(list->modules.tail, struct iat_module, node); + + if (_stricmp(module->name, name) == 0) { + return module; + } + } + + newmod = xcalloc(sizeof(*newmod)); + list_init(&newmod->entries); + newmod->name = xstrdup(name); + list_append(&list->modules, &newmod->node); + + return newmod; +} + +static void iat_list_load( + struct iat_list *list, + const char *filename, + struct pe *pe) +{ + /* Allocate some rather large stack buffers. C++ name mangling and STL can + get quite excessive. Ideally we'd load_file() and then punch NULs + between the tokens but I really don't feel like writing a full + tokenizer FSM for this right now. */ + + va_t va; + uintptr_t dummy; + char line[1024]; + char dll_name[1024]; + char symbol_name[1024]; + struct iat_module *module; + struct iat_entry *entry; + int line_no; + int result; + FILE *f; + + assert(list != NULL); + assert(filename != NULL); + assert(pe != NULL); + + list_init(&list->modules); + list->module_count = 0; + + f = fopen(filename, "r"); + + if (f == NULL) { + die("%s: %s\n", filename, strerror(errno)); + } + + line_no = 0; + + while (fgets(line, _countof(line), f)) { + line_no++; + result = sscanf( + line, + "%p %p %s %s", + (void **) &va.va, + (void **) &dummy, + dll_name, + symbol_name); + + if (result != 4) { + die( "%s:%i: Invalid input (matched %i/4 cols)\n", + filename, + line_no, + result); + } + + module = iat_list_get_module(list, dll_name); + + entry = xcalloc(sizeof(*entry)); + entry->site = pe_rva_from_va(pe, va); + entry->name = xstrdup(symbol_name); + + list_append(&module->entries, &entry->node); + } + + fclose(f); +} + +static void iat_module_allocate( + struct iat_module *module, + struct cursor *cursor) +{ + struct iat_entry *entry; + struct list_node *pos; + + assert(module != NULL); + assert(cursor != NULL); + + /* Pass 1: Allocate OFT */ + + cursor_align(cursor, sizeof(uintptr_t)); + + for (pos = module->entries.head ; pos != NULL ; pos = pos->next) { + entry = CONTAINING_RECORD(pos, struct iat_entry, node); + entry->oft_pos = *cursor; + cursor_advance(cursor, sizeof(uintptr_t)); + } + + /* (terminating entry) */ + + cursor_advance(cursor, sizeof(uintptr_t)); + + /* Pass 2: Allocate IMAGE_IMPORT_BY_NAME records */ + + for (pos = module->entries.head ; pos != NULL ; pos = pos->next) { + cursor_align(cursor, 2); + + entry = CONTAINING_RECORD(pos, struct iat_entry, node); + entry->name_len = strlen(entry->name) + 1; + entry->hint_pos = *cursor; + + cursor_advance(cursor, sizeof(WORD)); + + entry->name_pos = *cursor; + + cursor_advance(cursor, entry->name_len); + } +} + +static void iat_list_allocate(struct iat_list *list, struct cursor *cursor) +{ + struct iat_module *module; + struct list_node *pos; + + assert(list != NULL); + assert(cursor != NULL); + + /* Pass 1: Allocate IMAGE_IMPORT_DESCRIPTOR array */ + + for (pos = list->modules.head ; pos != NULL ; pos = pos->next) { + list->module_count++; + + module = CONTAINING_RECORD(pos, struct iat_module, node); + module->desc_pos = *cursor; + + cursor_advance(cursor, sizeof(IMAGE_IMPORT_DESCRIPTOR)); + } + + /* (terminating entry) */ + + list->module_count++; + list->term_pos = *cursor; + cursor_advance(cursor, sizeof(IMAGE_IMPORT_DESCRIPTOR)); + + /* Pass 2: Allocate names */ + + for (pos = list->modules.head ; pos != NULL ; pos = pos->next) { + module = CONTAINING_RECORD(pos, struct iat_module, node); + module->name_pos = *cursor; + module->name_len = strlen(module->name) + 1; + cursor_advance(cursor, module->name_len); + } + + /* Pass 3: Allocate entries */ + + for (pos = list->modules.head ; pos != NULL ; pos = pos->next) { + module = CONTAINING_RECORD(pos, struct iat_module, node); + iat_module_allocate(module, cursor); + } +} + +static void iat_module_write(struct iat_module *module, struct pe *pe) +{ + struct iat_entry *entry; + struct list_node *pos; + uintptr_t entry_val; + uint16_t hint; + fo_t site_fo; + + assert(module != NULL); + assert(pe != NULL); + + hint = 0; + + for (pos = module->entries.head ; pos != NULL ; pos = pos->next) { + entry = CONTAINING_RECORD(pos, struct iat_entry, node); + + /* IAT entries hold either an IMAGE_IMPORT_BY_NAME RVA for symbols + imported by name or an ordinal for symbols imported by ordinal + (in which case the high bit of the entry is set). Because IAT + entries get overwritten by actual VAs (pointers) to their resolved + symbols they need to be machine word-sized, not 32 bit RVA sized. + + The first write repairs an IAT entry (which is located within an + existing section). The writes after that write to our newly-created + section holding reconstructed import metadata (which the IAT entry + will refer to). */ + + entry_val = entry->hint_pos.vpos.rva; + + site_fo = pe_fo_from_rva(pe, entry->site); + pe_write(pe, site_fo, &entry_val, sizeof(entry_val)); + pe_write(pe, entry->oft_pos.fpos, &entry_val, sizeof(entry_val)); + pe_write(pe, entry->hint_pos.fpos, &hint, sizeof(hint)); + pe_write(pe, entry->name_pos.fpos, entry->name, entry->name_len); + } +} + +static void iat_list_write(struct iat_list *list, struct pe *pe) +{ + IMAGE_IMPORT_DESCRIPTOR iid; + struct iat_module *module; + struct iat_entry *first_entry; + struct list_node *pos; + + assert(list != NULL); + assert(pe != NULL); + + for (pos = list->modules.head ; pos != NULL ; pos = pos->next) { + module = CONTAINING_RECORD(pos, struct iat_module, node); + + assert(module->entries.head != NULL); + + first_entry = CONTAINING_RECORD( + module->entries.head, + struct iat_entry, + node); + + memset(&iid, 0, sizeof(iid)); + iid.Characteristics = first_entry->oft_pos.vpos.rva; + iid.Name = module->name_pos.vpos.rva; + iid.FirstThunk = first_entry->site.rva; + + pe_write(pe, module->desc_pos.fpos, &iid, sizeof(iid)); + pe_write(pe, module->name_pos.fpos, module->name, module->name_len); + iat_module_write(module, pe); + } + + /* Write zeroed-out terminating descriptor */ + + memset(&iid, 0, sizeof(iid)); + pe_write(pe, list->term_pos.fpos, &iid, sizeof(iid)); +} + +static void iat_list_perform_repairs(struct iat_list *list, struct pe *pe) +{ + IMAGE_SECTION_HEADER sec; + struct cursor begin; + struct cursor end; + struct cursor pos; + + assert(list != NULL); + assert(pe != NULL); + + pe_get_limit(pe, &begin); + + cursor_align(&begin, 0x1000); + pos = begin; + iat_list_allocate(list, &pos); + cursor_align(&pos, 0x1000); + end = pos; + + memset(&sec, 0, sizeof(sec)); + strcpy_s((char *) sec.Name, _countof(sec.Name) - 1, ".idata"); + sec.Misc.VirtualSize = end.vpos.rva - begin.vpos.rva; + sec.VirtualAddress = begin.vpos.rva; + sec.SizeOfRawData = end.fpos.fo - begin.fpos.fo; + sec.PointerToRawData = begin.fpos.fo; + sec.Characteristics = + IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE ; + + pe_add_section(pe, &sec); + iat_list_write(list, pe); + + /* This step doesn't really fit well anywhere in particular... */ + + pe_write_dd( + pe, + IMAGE_DIRECTORY_ENTRY_IMPORT, + begin.vpos, + list->module_count * sizeof(IMAGE_IMPORT_DESCRIPTOR)); +} + +int main(int argc, char **argv) +{ + struct pe pe; + struct iat_list list; + void *bytes; + size_t nbytes; + + if (argc != 4) { + die("Usage: %s [src exe] [iat listing] [dest exe]\n", argv[0]); + } + + file_load(argv[1], &bytes, &nbytes); + pe_init(&pe, bytes, nbytes); + iat_list_load(&list, argv[2], &pe); + iat_list_perform_repairs(&list, &pe); + file_save(argv[3], pe.file, pe.file_size); + + return EXIT_SUCCESS; +} diff --git a/src/micetools/util/micereset.c b/src/micetools/util/micereset.c index 104099d..06b4036 100644 --- a/src/micetools/util/micereset.c +++ b/src/micetools/util/micereset.c @@ -21,7 +21,7 @@ BOOL wipe_eeprom() { if (i == 0) continue; if (i == 32) continue; printf("Wiping EEPROM block %d\n", i); - amEepromWrite(i * 0x20, data, sizeof data); + amEepromWrite((i * 0x20) & 0xffff, data, sizeof data); } amEepromExit(); diff --git a/src/micetools/util/storagecraft.c b/src/micetools/util/storagecraft.c index a633773..97bffff 100644 --- a/src/micetools/util/storagecraft.c +++ b/src/micetools/util/storagecraft.c @@ -60,20 +60,24 @@ physical_disk_t newSSD = { .m_TotalSize = 0, // Force re-calculation .m_DiskType = DiskType_HardDisk, .m_IsFormatted = true, + // .m_Partitions = { + // // 1.5GB boot partition + // { .m_Size = _GMKiB(1, 513, 450.5), .m_Filesystem = MBR_FS_NTFS }, + // // 1.5GB OS recovery + // { .m_Size = _GMKiB(1, 513, 482), .m_Filesystem = MBR_FS_NTFS }, + // }, + // .m_Extended = { + // { _GMKiB(0, 517, 705.5), MBR_FS_FAT16, SPD_OS }, // 512MB OS update + // { _GMKiB(0, 2055, 163.5), MBR_FS_FAT16, SPD_Patch0 }, // 2GB patch0 + // { _GMKiB(0, 2055, 163.5), MBR_FS_FAT16, SPD_Patch1 }, // 2GB patch1 + // { _GMKiB(36, 474, 556.5), MBR_FS_NTFS, SPD_AppData }, // 40GB appdata + // { _GMKiB(16, 2, 597), MBR_FS_FAT16, SPD_Original0 }, // 16GB original0 + // { 0 }, // End of table + // }, .m_Partitions = { - // 1.5GB boot partition - { .m_Size = _GMKiB(1, 513, 450.5), .m_Filesystem = MBR_FS_NTFS }, - // 1.5GB OS recovery - { .m_Size = _GMKiB(1, 513, 482), .m_Filesystem = MBR_FS_NTFS }, - }, - .m_Extended = { - { _GMKiB(0, 517, 705.5), MBR_FS_FAT16, SPD_OS }, // 512MB OS update - { _GMKiB(0, 2055, 163.5), MBR_FS_FAT16, SPD_Patch0 }, // 2GB patch0 - { _GMKiB(0, 2055, 163.5), MBR_FS_FAT16, SPD_Patch1 }, // 2GB patch1 - { _GMKiB(36, 474, 556.5), MBR_FS_NTFS, SPD_AppData }, // 40GB appdata - { _GMKiB(16, 2, 597), MBR_FS_FAT16, SPD_Original0 }, // 16GB original0 - { 0 }, // End of table + { .m_Size = 40960, .m_Filesystem = MBR_FS_NTFS }, }, + .m_Extended = { 0 }, }; sbr_t SegaBootRecord0 = { 0 }; @@ -328,8 +332,8 @@ void write_pd(HANDLE hDisk, physical_disk_t* pConfig) { void print_bar(LONGLONG nCurrent, LONGLONG nMax) { printf("["); - DWORD filled = ((float)nCurrent / (float)nMax) * 40; - int i = 0; + DWORD filled = (DWORD)(((float)nCurrent / (float)nMax) * 40); + DWORD i = 0; for (; i < filled; i++) printf("="); for (; i < 40; i++) printf(" "); printf("] %3.2f%%\r", (float)nCurrent / (float)nMax * 100); @@ -351,7 +355,7 @@ BOOL mount_OSR(LPSTR lpOSRPath) { } BOOL dismount_OSR() { char szTcCommand[1024]; - sprintf_s(szTcCommand, sizeof szTcCommand, "%s /q /d %s", TRUECRYPT, OSR_MOUNTPOINT, OSR_FLAGS); + sprintf_s(szTcCommand, sizeof szTcCommand, "%s /q /d %s", TRUECRYPT, OSR_MOUNTPOINT); int dTcRet = system(szTcCommand); if (dTcRet != 0) { @@ -418,13 +422,14 @@ end: CloseHandle(hOSR); dismount_OSR(); } -void install_OSR_xcopy(LPSTR lpDiskPath, LPSTR lpOSRPath) { - if (!mount_OSR(lpOSRPath)) return; +BOOL install_OSR_xcopy(LPSTR lpDiskPath, LPSTR lpOSRPath) { + if (!mount_OSR(lpOSRPath)) return FALSE; printf("Installing OS using xcopy %s -> %s\n", OSR_MOUNTPOINT, lpDiskPath); char szXcCommand[1024]; - sprintf_s(szXcCommand, sizeof szXcCommand, "xcopy %s %s /e /c /h /y /q", OSR_MOUNTPOINT, lpDiskPath); + sprintf_s(szXcCommand, sizeof szXcCommand, "xcopy %s %s /e /c /h /y /q", OSR_MOUNTPOINT, + lpDiskPath); int dTcRet = system(szXcCommand); if (dTcRet != 0) { @@ -432,9 +437,9 @@ void install_OSR_xcopy(LPSTR lpDiskPath, LPSTR lpOSRPath) { printf("Command used was: %s\n", szXcCommand); return FALSE; } - return TRUE; - dismount_OSR(); + if (!dismount_OSR()) return FALSE; + return TRUE; } int main(int argc, char* argv[]) { @@ -445,11 +450,12 @@ int main(int argc, char* argv[]) { HANDLE hDisk = INVALID_HANDLE_VALUE; SetLastError(0); - // hDisk = CreateFileA("H:\\NewDiskImage.img", GENERIC_READ | GENERIC_WRITE, - // FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, - // FILE_ATTRIBUTE_NORMAL, NULL); - hDisk = CreateFileA("\\\\.\\PHYSICALDRIVE4", GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + hDisk = CreateFileA("H:\\NewDiskImage1.img", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + // hDisk = CreateFileA("\\\\.\\PHYSICALDRIVE4", GENERIC_READ | GENERIC_WRITE, + // FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDisk == INVALID_HANDLE_VALUE) { printf("Failed to open disk! %d\n", GetLastError()); @@ -460,9 +466,10 @@ int main(int argc, char* argv[]) { // install_OSR(hDisk, "H:\\Arcades\\RingOS\\RecoverOS\\0009\\Minint\\system32\\OSR"); // install_OSR_xcopy(hDisk, "H:\\Arcades\\RingOS\\RecoverOS\\0009\\Minint\\system32\\OSR"); - puts("Please mount OS at W:"); - system("pause"); - install_OSR_xcopy("W:", "H:\\Arcades\\RingOS\\RecoverOS\\0009\\Minint\\system32\\OSR"); + + // puts("Please mount OS at W:"); + // system("pause"); + // install_OSR_xcopy("W:", "H:\\Arcades\\RingOS\\RecoverOS\\0009\\Minint\\system32\\OSR"); FlushFileBuffers(hDisk); CloseHandle(hDisk); diff --git a/src/noteUnify.c b/src/noteUnify.c new file mode 100644 index 0000000..5a9dc90 --- /dev/null +++ b/src/noteUnify.c @@ -0,0 +1,96 @@ +bool unifyNotes(UnifiedNote *unified, ParsedNote *parsed, Chart *chart) { + unified->measure_whole = parsed->measure_whole; + unified->measure_fraction = parsed->mesaure_fraction; + unified->duration = parsed->duration; + unified->location = parsed->location; + unified->slide_id = parsed->slide_id; + unified->slide_amount = parsed->slide_amount; + + switch (chart->type) { + case ChartTypeSR: + if ((char)parsed->type < '\0') { + unified->note_type = NoteTypeStartSlide; + unified->is_start_slide = true; + } else if (parsed->slide_id == 0) { + switch (parsed->type & 0xf) { + case 0: + case 1: + unified->note_type = NoteTypeTap; + break; + case 2: + case 3: + unified->note_type = NoteTypeHold; + break; + case 4: + case 5: + unified->note_type = NoteTypeBreak; + } + } else { + unified->note_type = NoteTypeStar; + unified->slide_amount = 1; + } + + if (parsed->type & 0x40) { + unified->field12_0x24 = 2.0; + } else if (parsed->type & 0x20) { + unified->field12_0x24 = 4.0; + } else if (parsed->type & 0x10) { + unified->field12_0x24 = 6.0; + } + + if (parsed->slide_id == 0) { + unified->slide_pattern = 0; + return true; + } + + unified->slide_amount = 0; + unified->slide_delay = parsed->slide_delay; + switch (parsed->slide_pattern) { + case 0: + unified->slide_pattern = 1; + return true; + case 1: + unified->slide_pattern = 3; + return true; + case 2: + unified->slide_pattern = 2; + return true; + default: + unified->slide_amount = 0; + return true; + } + + case ChartTypeSZ: + if (((char)parsed->type < '\0') || (parsed->type == NoteTypeStartSlide)) { + unified->note_type = NoteTypeStartSlide; + unified->is_start_slide = true; + } else { + unified->note_type = parsed->type; + } + unified->slide_pattern = parsed->slide_pattern; + if (unified->note_type == NoteTypeStar) { + unified->slide_amount = 1; + unified->slide_delay = parsed->slide_delay; + return true; + } + unified->slide_delay = parsed->slide_delay; + return true; + + case ChartTypeSC: + case ChartTypeSD: + if ((-1 < (char)parsed->type) && (parsed->type != NoteTypeStartSlide)) { + unified->note_type = parsed->type; + unified->slide_pattern = parsed->slide_pattern; + unified->slide_delay = parsed->slide_delay; + return true; + } + unified->note_type = NoteTypeStartSlide; + unified->is_start_slide = true; + unified->slide_pattern = parsed->slide_pattern; + unified->slide_delay = parsed->slide_delay; + return true; + + default: + return false; + } +} \ No newline at end of file diff --git a/src/patches/mk4expRingR.patch b/src/patches/mk4expRingR.patch new file mode 100644 index 0000000..bfcae8b --- /dev/null +++ b/src/patches/mk4expRingR.patch @@ -0,0 +1,18 @@ +*0072aab : 00000000 > 01000000 # amHmDebugLevel +*0072aac0 : 00000000 > 01000000 # amPlatformDebugLevel +*0072ab44 : 00000000 > 01000000 # amSysDataDebugLevel +*0072ab48 : 00000000 > 01000000 # amNetworkDebugLevel +*0072b0b0 : 00000000 > 01000000 # amRtcDebugLevel +*0072b2d0 : 00000000 > 01000000 # amLibDebugLevel +*0072b560 : 00000000 > 01000000 # amBackupDebugLevel +*0072b96c : 00000000 > 01000000 # amDipswDebugLevel +*0072b970 : 00000000 > 01000000 # amDongleDebugLevel +*0072edb0 : 00000000 > 01000000 # amMasterDebugLevel +*0072f6b8 : 00000000 > 01000000 # amSramDebugLevel +*0072f6bc : 00000000 > 01000000 # amEepromDebugLevel +*0072f6c0 : 00000000 > 01000000 # amJvsDebugLevel +*0073ba98 : 00000000 > 01000000 # amAtaDebugLevel +*0073baa0 : 00000000 > 01000000 # amInstallDebugLevel +*0073bfc8 : 00000000 > 01000000 # amJvstDebugLevel +*0074415c : 00000000 > 01000000 # amiTimerDebugLevel +*00744170 : 00000000 > 01000000 # pcpDebugLevel diff --git a/src/patches/patches.index b/src/patches/patches.index index 3ac0b33..02f7908 100644 --- a/src/patches/patches.index +++ b/src/patches/patches.index @@ -9,5 +9,3 @@ mxnetwork.exe: mxnetwork.patch maimai_dump_.exe: maimai_dump_.patch RingGame.exe: UnderNightInBirthExLate[st].patch nxAuth.exe: nxAuth.patch -ALLNetProc_Win.exe: ALLNetProc_Win.patch -ALLNetProc.exe: ALLNetProc_Win.patch diff --git a/subprojects/detours/CREDITS.TXT b/subprojects/detours/CREDITS.TXT new file mode 100644 index 0000000..a122676 --- /dev/null +++ b/subprojects/detours/CREDITS.TXT @@ -0,0 +1,115 @@ +============================================================================== +The following individuals have helped identify specific bugs and improvements +in Detours. The entire Detours community has benefited from their help. +============================================================================== + +* Jay Krell: Identified issue with VirtualSize == 0 files created in + NT 3.1 images. (Build_339) + +* Igor Odnovorov: Identified an issue with the placement of the trampoline + region when a function is detoured twice and the second + trampoline region is outside of the +/- 2GB range of + the target. (Build_337) + +* Jay Krell: Identified need for some programs to enumerate the + address of IAT entries. (Build_336) + +* Calvin Hsia: Identified need for some program to change the excluded + system region. (Build_336) + +* Adam Smith: Identified error in failure handling when VirtualProect + cannot make pages executable because the Prohibit + Dynamic Code Generation mitigation policy has been + applied to a process. (Build_335) + +* Ben Faull: Identified fix to detour_alloc_region_from_lo and + detour_alloc_region_from_hi that preserves ASLR entropy. + (Build_334) + +* Shaoxiang Su: Reported errors building with Visual Studio 2015. + (Build_332) + +* Jay Krell: Identified and resolved significant gaps in the X86, X64 + and IA64 disassemblers for instruction found in code, + but seldom found in function prologues. (Build_331) + +* Allan Murphy: Identify error in rep and jmp ds: encodings. (Build_331) + +* Philip Bacon: Identified incorrect entry point return for pure + resource-only binaries. (Build_330) + +* Jay Krell: Identified failure in DetourAttachEx to update nAlign. + (Build_330) + +* Sumit Sarin: Helped debug error with packed binaries. + (Build_329) + +* Nitya Kumar Sharma: Reported bug in DetourAfterWithDll for 32/64 agnostic + EXEs. + (Build_327) + +* Richard Black: Identified a large number of typos in documentation. + (Build_326) + +* Michael Bilodeau: Identified bug in DetourUpdateProcessWithDll when the + target process contains a Detours payload *after* all + valid PE binaries. + (Build_324) + +* Meera Jindal: Reported bug in identification of target address in + DetourCopyInstruction for jmp[] and call[] on x86 & x64, + the ff15 and ff25 opcodes. + (Build_323) + +* Ken Johnson: Assistance with SAL 2.0 annotations. + (Build_319) + +* Nick Wood: Identified bug in DetourFindFunction on ARM. + (Build_314) + +* Mark Russinovich: Helped debug DetourCreateProcessWithDllEx. + (Build_314) + +* John Lin: Implementation idea for DetoursCreateProcessWithDllEx. + (Build_314) + +* Andrew Zawadowskiy Reported an improper memory page permissions + vulnerability in Detours 2.1. (Vulnerability does not + exist in versions later than Detours 2.1.) + (Build_223) + +* Nightxie: Identified bug in detour_alloc_round_up_to_region. + (Build_310) + +* Diana Milirud: Identified bug in B* instructions on ARM. + (Build_309) + +* Juan Carlos Identified correct MSIL entry point for unsigned MSIL. + Luciani: (Build_308) + +* Lee Hunt Suggested improvements in algorithm for allocation of + Lawrence Landauer trampoline regions on x64 to avoid collisions with + Joe Laughlin: system DLLs. + (Build_307) + +* Tyler Sims Identified bug in handling of "anycpu" MSIL binaries + Darren Kennedy: on x64. + (Build_307) + +* Andre Vachon: Help with optimized binaries. + (Build 301) + +* Chris Mann: Identified fix not forward ported from 2.2 to 3.0. + (Build_301) + +* Mark Irving: Identified bug with EXEs missing second import table. + (Build_300) + +* Ben Schwarz: Identified bug in handling of multi-byte NOPs. + (Build_300) + +* Aaron Giles Coded initial ARM/Thumb2 disassembler. + Jared Henderson: (Build_300) + +* Doug Brubacher: Coded initial x86 disassembler. + (Build_100) diff --git a/subprojects/detours/LICENSE.md b/subprojects/detours/LICENSE.md new file mode 100644 index 0000000..e6a4c56 --- /dev/null +++ b/subprojects/detours/LICENSE.md @@ -0,0 +1,23 @@ +# Copyright (c) Microsoft Corporation + +All rights reserved. + +# MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/subprojects/detours/README.md b/subprojects/detours/README.md new file mode 100644 index 0000000..b5185ad --- /dev/null +++ b/subprojects/detours/README.md @@ -0,0 +1,38 @@ +# Microsoft Research Detours Package + +Detours is a software package for monitoring and instrumenting API calls on Windows. Detours +has been used by many ISVs and is also used by product teams at Microsoft. Detours is now available under +a standard open source license (MIT). This simplifies licensing for programmers using Detours +and allows the community to support Detours using open source tools and processes. + +Detours is compatible with the Windows NT family of +operating systems: Windows NT, Windows XP, Windows Server 2003, Windows 7, +Windows 8, and Windows 10. It cannot be used by Window Store apps +because Detours requires APIs not available to those applications. +This repo contains the source code for version 4.0.1 of Detours. + +For technical documentation on Detours, see the [Detours Wiki](https://github.com/microsoft/Detours/wiki). +For directions on how to build and run samples, see the +samples [README.txt](https://github.com/Microsoft/Detours/blob/master/samples/README.TXT) file. + +## Contributing + +The [`Detours`](https://github.com/microsoft/detours) repository is where development is done. +Here are some ways you can participate in the project: + +* [Answer questions](https://github.com/microsoft/detours/issues) about using Detours. +* [Improve the Wiki](https://github.com/microsoft/detours/Wiki). +* [Submit bugs](https://github.com/microsoft/detours/issues) and help us verify fixes and changes as they are checked in. +* Review [source code changes](https://github.com/microsoft/detours/pulls). + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Issues, questions, and feedback + +* Open an issue on [GitHub Issues](https://github.com/Microsoft/detours/issues). + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the [MIT](LICENSE.txt) License. diff --git a/subprojects/detours/include/detours.h b/subprojects/detours/include/detours.h new file mode 100644 index 0000000..1caae40 --- /dev/null +++ b/subprojects/detours/include/detours.h @@ -0,0 +1,1061 @@ +///////////////////////////////////////////////////////////////////////////// +// +// Core Detours Functionality (detours.h of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#pragma once +#ifndef _DETOURS_H_ +#define _DETOURS_H_ + +#include + +#define DETOURS_VERSION 0x4c0c1 // 0xMAJORcMINORcPATCH + +////////////////////////////////////////////////////////////////////////////// +// + +#undef DETOURS_X64 +#undef DETOURS_X86 +#undef DETOURS_IA64 +#undef DETOURS_ARM +#undef DETOURS_ARM64 +#undef DETOURS_BITS +#undef DETOURS_32BIT +#undef DETOURS_64BIT + +#if defined(_X86_) +#define DETOURS_X86 +#define DETOURS_OPTION_BITS 64 + +#elif defined(_AMD64_) +#define DETOURS_X64 +#define DETOURS_OPTION_BITS 32 + +#elif defined(_IA64_) +#define DETOURS_IA64 +#define DETOURS_OPTION_BITS 32 + +#elif defined(_ARM_) +#define DETOURS_ARM + +#elif defined(_ARM64_) +#define DETOURS_ARM64 + +#else +#error Unknown architecture (x86, amd64, ia64, arm, arm64) +#endif + +#ifdef _WIN64 +#undef DETOURS_32BIT +#define DETOURS_64BIT 1 +#define DETOURS_BITS 64 +// If all 64bit kernels can run one and only one 32bit architecture. +//#define DETOURS_OPTION_BITS 32 +#else +#define DETOURS_32BIT 1 +#undef DETOURS_64BIT +#define DETOURS_BITS 32 +// If all 64bit kernels can run one and only one 32bit architecture. +//#define DETOURS_OPTION_BITS 32 +#endif + +#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) + +////////////////////////////////////////////////////////////////////////////// +// + +#if (_MSC_VER < 1299) +typedef LONG LONG_PTR; +typedef ULONG ULONG_PTR; +#endif + +///////////////////////////////////////////////// SAL 2.0 Annotations w/o SAL. +// +// These definitions are include so that Detours will build even if the +// compiler doesn't have full SAL 2.0 support. +// +#ifndef DETOURS_DONT_REMOVE_SAL_20 + +#ifdef DETOURS_TEST_REMOVE_SAL_20 +#undef _Analysis_assume_ +#undef _Benign_race_begin_ +#undef _Benign_race_end_ +#undef _Field_range_ +#undef _Field_size_ +#undef _In_ +#undef _In_bytecount_ +#undef _In_count_ +#undef _In_opt_ +#undef _In_opt_bytecount_ +#undef _In_opt_count_ +#undef _In_opt_z_ +#undef _In_range_ +#undef _In_reads_ +#undef _In_reads_bytes_ +#undef _In_reads_opt_ +#undef _In_reads_opt_bytes_ +#undef _In_reads_or_z_ +#undef _In_z_ +#undef _Inout_ +#undef _Inout_opt_ +#undef _Inout_z_count_ +#undef _Out_ +#undef _Out_opt_ +#undef _Out_writes_ +#undef _Outptr_result_maybenull_ +#undef _Readable_bytes_ +#undef _Success_ +#undef _Writable_bytes_ +#undef _Pre_notnull_ +#endif + +#if defined(_Deref_out_opt_z_) && !defined(_Outptr_result_maybenull_) +#define _Outptr_result_maybenull_ _Deref_out_opt_z_ +#endif + +#if defined(_In_count_) && !defined(_In_reads_) +#define _In_reads_(x) _In_count_(x) +#endif + +#if defined(_In_opt_count_) && !defined(_In_reads_opt_) +#define _In_reads_opt_(x) _In_opt_count_(x) +#endif + +#if defined(_In_opt_bytecount_) && !defined(_In_reads_opt_bytes_) +#define _In_reads_opt_bytes_(x) _In_opt_bytecount_(x) +#endif + +#if defined(_In_bytecount_) && !defined(_In_reads_bytes_) +#define _In_reads_bytes_(x) _In_bytecount_(x) +#endif + +#ifndef _In_ +#define _In_ +#endif + +#ifndef _In_bytecount_ +#define _In_bytecount_(x) +#endif + +#ifndef _In_count_ +#define _In_count_(x) +#endif + +#ifndef _In_opt_ +#define _In_opt_ +#endif + +#ifndef _In_opt_bytecount_ +#define _In_opt_bytecount_(x) +#endif + +#ifndef _In_opt_count_ +#define _In_opt_count_(x) +#endif + +#ifndef _In_opt_z_ +#define _In_opt_z_ +#endif + +#ifndef _In_range_ +#define _In_range_(x,y) +#endif + +#ifndef _In_reads_ +#define _In_reads_(x) +#endif + +#ifndef _In_reads_bytes_ +#define _In_reads_bytes_(x) +#endif + +#ifndef _In_reads_opt_ +#define _In_reads_opt_(x) +#endif + +#ifndef _In_reads_opt_bytes_ +#define _In_reads_opt_bytes_(x) +#endif + +#ifndef _In_reads_or_z_ +#define _In_reads_or_z_ +#endif + +#ifndef _In_z_ +#define _In_z_ +#endif + +#ifndef _Inout_ +#define _Inout_ +#endif + +#ifndef _Inout_opt_ +#define _Inout_opt_ +#endif + +#ifndef _Inout_z_count_ +#define _Inout_z_count_(x) +#endif + +#ifndef _Out_ +#define _Out_ +#endif + +#ifndef _Out_opt_ +#define _Out_opt_ +#endif + +#ifndef _Out_writes_ +#define _Out_writes_(x) +#endif + +#ifndef _Outptr_result_maybenull_ +#define _Outptr_result_maybenull_ +#endif + +#ifndef _Writable_bytes_ +#define _Writable_bytes_(x) +#endif + +#ifndef _Readable_bytes_ +#define _Readable_bytes_(x) +#endif + +#ifndef _Success_ +#define _Success_(x) +#endif + +#ifndef _Pre_notnull_ +#define _Pre_notnull_ +#endif + +#ifdef DETOURS_INTERNAL + +#pragma warning(disable:4615) // unknown warning type (suppress with older compilers) + +#ifndef _Benign_race_begin_ +#define _Benign_race_begin_ +#endif + +#ifndef _Benign_race_end_ +#define _Benign_race_end_ +#endif + +#ifndef _Field_size_ +#define _Field_size_(x) +#endif + +#ifndef _Field_range_ +#define _Field_range_(x,y) +#endif + +#ifndef _Analysis_assume_ +#define _Analysis_assume_(x) +#endif + +#endif // DETOURS_INTERNAL +#endif // DETOURS_DONT_REMOVE_SAL_20 + +////////////////////////////////////////////////////////////////////////////// +// +#ifndef GUID_DEFINED +#define GUID_DEFINED +typedef struct _GUID +{ + DWORD Data1; + WORD Data2; + WORD Data3; + BYTE Data4[ 8 ]; +} GUID; + +#ifdef INITGUID +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#else +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name +#endif // INITGUID +#endif // !GUID_DEFINED + +#if defined(__cplusplus) +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID & +#endif // !_REFGUID_DEFINED +#else // !__cplusplus +#ifndef _REFGUID_DEFINED +#define _REFGUID_DEFINED +#define REFGUID const GUID * const +#endif // !_REFGUID_DEFINED +#endif // !__cplusplus + +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) +#endif + +// +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/////////////////////////////////////////////////// Instruction Target Macros. +// +#define DETOUR_INSTRUCTION_TARGET_NONE ((PVOID)0) +#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PVOID)(LONG_PTR)-1) +#define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444 // "Dtr\0" + +extern const GUID DETOUR_EXE_RESTORE_GUID; +extern const GUID DETOUR_EXE_HELPER_GUID; + +#define DETOUR_TRAMPOLINE_SIGNATURE 0x21727444 // Dtr! +typedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, *PDETOUR_TRAMPOLINE; + +/////////////////////////////////////////////////////////// Binary Structures. +// +#pragma pack(push, 8) +typedef struct _DETOUR_SECTION_HEADER +{ + DWORD cbHeaderSize; + DWORD nSignature; + DWORD nDataOffset; + DWORD cbDataSize; + + DWORD nOriginalImportVirtualAddress; + DWORD nOriginalImportSize; + DWORD nOriginalBoundImportVirtualAddress; + DWORD nOriginalBoundImportSize; + + DWORD nOriginalIatVirtualAddress; + DWORD nOriginalIatSize; + DWORD nOriginalSizeOfImage; + DWORD cbPrePE; + + DWORD nOriginalClrFlags; + DWORD reserved1; + DWORD reserved2; + DWORD reserved3; + + // Followed by cbPrePE bytes of data. +} DETOUR_SECTION_HEADER, *PDETOUR_SECTION_HEADER; + +typedef struct _DETOUR_SECTION_RECORD +{ + DWORD cbBytes; + DWORD nReserved; + GUID guid; +} DETOUR_SECTION_RECORD, *PDETOUR_SECTION_RECORD; + +typedef struct _DETOUR_CLR_HEADER +{ + // Header versioning + ULONG cb; + USHORT MajorRuntimeVersion; + USHORT MinorRuntimeVersion; + + // Symbol table and startup information + IMAGE_DATA_DIRECTORY MetaData; + ULONG Flags; + + // Followed by the rest of the IMAGE_COR20_HEADER +} DETOUR_CLR_HEADER, *PDETOUR_CLR_HEADER; + +typedef struct _DETOUR_EXE_RESTORE +{ + DWORD cb; + DWORD cbidh; + DWORD cbinh; + DWORD cbclr; + + PBYTE pidh; + PBYTE pinh; + PBYTE pclr; + + IMAGE_DOS_HEADER idh; + union { + IMAGE_NT_HEADERS inh; + IMAGE_NT_HEADERS32 inh32; + IMAGE_NT_HEADERS64 inh64; + BYTE raw[sizeof(IMAGE_NT_HEADERS64) + + sizeof(IMAGE_SECTION_HEADER) * 32]; + }; + DETOUR_CLR_HEADER clr; + +} DETOUR_EXE_RESTORE, *PDETOUR_EXE_RESTORE; + +typedef struct _DETOUR_EXE_HELPER +{ + DWORD cb; + DWORD pid; + DWORD nDlls; + CHAR rDlls[4]; +} DETOUR_EXE_HELPER, *PDETOUR_EXE_HELPER; + +#pragma pack(pop) + +#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \ +{ \ + sizeof(DETOUR_SECTION_HEADER),\ + DETOUR_SECTION_HEADER_SIGNATURE,\ + sizeof(DETOUR_SECTION_HEADER),\ + (cbSectionSize),\ + \ + 0,\ + 0,\ + 0,\ + 0,\ + \ + 0,\ + 0,\ + 0,\ + 0,\ +} + +/////////////////////////////////////////////////////////////// Helper Macros. +// +#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) +#define DETOURS_STRINGIFY_(x) #x + +///////////////////////////////////////////////////////////// Binary Typedefs. +// +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)( + _In_opt_ PVOID pContext, + _In_opt_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile); + +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_FILE_CALLBACK)( + _In_opt_ PVOID pContext, + _In_ LPCSTR pszOrigFile, + _In_ LPCSTR pszFile, + _Outptr_result_maybenull_ LPCSTR *ppszOutFile); + +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_SYMBOL_CALLBACK)( + _In_opt_ PVOID pContext, + _In_ ULONG nOrigOrdinal, + _In_ ULONG nOrdinal, + _Out_ ULONG *pnOutOrdinal, + _In_opt_ LPCSTR pszOrigSymbol, + _In_opt_ LPCSTR pszSymbol, + _Outptr_result_maybenull_ LPCSTR *ppszOutSymbol); + +typedef BOOL (CALLBACK *PF_DETOUR_BINARY_COMMIT_CALLBACK)( + _In_opt_ PVOID pContext); + +typedef BOOL (CALLBACK *PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext, + _In_ ULONG nOrdinal, + _In_opt_ LPCSTR pszName, + _In_opt_ PVOID pCode); + +typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext, + _In_opt_ HMODULE hModule, + _In_opt_ LPCSTR pszFile); + +typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext, + _In_ DWORD nOrdinal, + _In_opt_ LPCSTR pszFunc, + _In_opt_ PVOID pvFunc); + +// Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter. +typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext, + _In_ DWORD nOrdinal, + _In_opt_ LPCSTR pszFunc, + _In_opt_ PVOID* ppvFunc); + +typedef VOID * PDETOUR_BINARY; +typedef VOID * PDETOUR_LOADED_BINARY; + +//////////////////////////////////////////////////////////// Transaction APIs. +// +LONG WINAPI DetourTransactionBegin(VOID); +LONG WINAPI DetourTransactionAbort(VOID); +LONG WINAPI DetourTransactionCommit(VOID); +LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer); + +LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread); + +LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour); + +LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour, + _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline, + _Out_opt_ PVOID *ppRealTarget, + _Out_opt_ PVOID *ppRealDetour); + +LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour); + +BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore); +BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain); +PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound); +PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound); + +////////////////////////////////////////////////////////////// Code Functions. +// +PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule, + _In_ LPCSTR pszFunction); +PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer, + _Out_opt_ PVOID *ppGlobals); +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra); +BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule, + _In_ BOOL fLimitReferencesToModule); + +///////////////////////////////////////////////////// Loaded Binary Functions. +// +HMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr); +HMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast); +PVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule); +ULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule); +BOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule, + _In_opt_ PVOID pContext, + _In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport); +BOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule, + _In_opt_ PVOID pContext, + _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, + _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc); + +BOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule, + _In_opt_ PVOID pContext, + _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, + _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFuncEx); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid, + _Out_ DWORD * pcbData); + +DWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule); + +///////////////////////////////////////////////// Persistent Binary Functions. +// + +PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary, + _Out_opt_ GUID *pGuid, + _Out_ DWORD *pcbData, + _Inout_ DWORD *pnIterator); + +_Writable_bytes_(*pcbData) +_Readable_bytes_(*pcbData) +_Success_(return != NULL) +PVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _Out_ DWORD *pcbData); + +PVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary, + _In_ REFGUID rguid, + _In_reads_opt_(cbData) PVOID pData, + _In_ DWORD cbData); +BOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary, _In_ REFGUID rguid); +BOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary); +BOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary); +BOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary, + _In_opt_ PVOID pContext, + _In_opt_ PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway, + _In_opt_ PF_DETOUR_BINARY_FILE_CALLBACK pfFile, + _In_opt_ PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol, + _In_opt_ PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommit); +BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pBinary, _In_ HANDLE hFile); +BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary); + +/////////////////////////////////////////////////// Create Process & Load Dll. +// +typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEA)( + _In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation); + +typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEW)( + _In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation); + +BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourCreateProcessWithDll DetourCreateProcessWithDllW +#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEW +#else +#define DetourCreateProcessWithDll DetourCreateProcessWithDllA +#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEA +#endif // !UNICODE + +BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExW +#else +#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExA +#endif // !UNICODE + +BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsW +#else +#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsA +#endif // !UNICODE + +BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourProcessViaHelper DetourProcessViaHelperW +#else +#define DetourProcessViaHelper DetourProcessViaHelperA +#endif // !UNICODE + +BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA); + +BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW); + +#ifdef UNICODE +#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsW +#else +#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsA +#endif // !UNICODE + +BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls); + +BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, + _In_ HMODULE hImage, + _In_ BOOL bIs32Bit, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls); + +BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _In_reads_bytes_(cbData) PVOID pvData, + _In_ DWORD cbData); +BOOL WINAPI DetourRestoreAfterWith(VOID); +BOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData, + _In_ DWORD cbData); +BOOL WINAPI DetourIsHelperProcess(VOID); +VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, + _In_ HINSTANCE, + _In_ LPSTR, + _In_ INT); + +// +////////////////////////////////////////////////////////////////////////////// +#ifdef __cplusplus +} +#endif // __cplusplus + +//////////////////////////////////////////////// Detours Internal Definitions. +// +#ifdef __cplusplus +#ifdef DETOURS_INTERNAL + +#define NOTHROW +// #define NOTHROW (nothrow) + +////////////////////////////////////////////////////////////////////////////// +// +#if (_MSC_VER < 1299) +#include +typedef IMAGEHLP_MODULE IMAGEHLP_MODULE64; +typedef PIMAGEHLP_MODULE PIMAGEHLP_MODULE64; +typedef IMAGEHLP_SYMBOL SYMBOL_INFO; +typedef PIMAGEHLP_SYMBOL PSYMBOL_INFO; + +static inline +LONG InterlockedCompareExchange(_Inout_ LONG *ptr, _In_ LONG nval, _In_ LONG oval) +{ + return (LONG)::InterlockedCompareExchange((PVOID*)ptr, (PVOID)nval, (PVOID)oval); +} +#else +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#include +#pragma warning(pop) +#endif + +#ifdef IMAGEAPI // defined by DBGHELP.H +typedef LPAPI_VERSION (NTAPI *PF_ImagehlpApiVersionEx)(_In_ LPAPI_VERSION AppVersion); + +typedef BOOL (NTAPI *PF_SymInitialize)(_In_ HANDLE hProcess, + _In_opt_ LPCSTR UserSearchPath, + _In_ BOOL fInvadeProcess); +typedef DWORD (NTAPI *PF_SymSetOptions)(_In_ DWORD SymOptions); +typedef DWORD (NTAPI *PF_SymGetOptions)(VOID); +typedef DWORD64 (NTAPI *PF_SymLoadModule64)(_In_ HANDLE hProcess, + _In_opt_ HANDLE hFile, + _In_ LPSTR ImageName, + _In_opt_ LPSTR ModuleName, + _In_ DWORD64 BaseOfDll, + _In_opt_ DWORD SizeOfDll); +typedef BOOL (NTAPI *PF_SymGetModuleInfo64)(_In_ HANDLE hProcess, + _In_ DWORD64 qwAddr, + _Out_ PIMAGEHLP_MODULE64 ModuleInfo); +typedef BOOL (NTAPI *PF_SymFromName)(_In_ HANDLE hProcess, + _In_ LPSTR Name, + _Out_ PSYMBOL_INFO Symbol); + +typedef struct _DETOUR_SYM_INFO +{ + HANDLE hProcess; + HMODULE hDbgHelp; + PF_ImagehlpApiVersionEx pfImagehlpApiVersionEx; + PF_SymInitialize pfSymInitialize; + PF_SymSetOptions pfSymSetOptions; + PF_SymGetOptions pfSymGetOptions; + PF_SymLoadModule64 pfSymLoadModule64; + PF_SymGetModuleInfo64 pfSymGetModuleInfo64; + PF_SymFromName pfSymFromName; +} DETOUR_SYM_INFO, *PDETOUR_SYM_INFO; + +PDETOUR_SYM_INFO DetourLoadImageHlp(VOID); + +#endif // IMAGEAPI + +#if defined(_INC_STDIO) && !defined(_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS) +#error detours.h must be included before stdio.h (or at least define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS earlier) +#endif +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 + +#ifndef DETOUR_TRACE +#if DETOUR_DEBUG +#define DETOUR_TRACE(x) printf x +#define DETOUR_BREAK() __debugbreak() +#include +#include +#else +#define DETOUR_TRACE(x) +#define DETOUR_BREAK() +#endif +#endif + +#if 1 || defined(DETOURS_IA64) + +// +// IA64 instructions are 41 bits, 3 per bundle, plus 5 bit bundle template => 128 bits per bundle. +// + +#define DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE (3) + +#define DETOUR_IA64_TEMPLATE_OFFSET (0) +#define DETOUR_IA64_TEMPLATE_SIZE (5) + +#define DETOUR_IA64_INSTRUCTION_SIZE (41) +#define DETOUR_IA64_INSTRUCTION0_OFFSET (DETOUR_IA64_TEMPLATE_SIZE) +#define DETOUR_IA64_INSTRUCTION1_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) +#define DETOUR_IA64_INSTRUCTION2_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE + DETOUR_IA64_INSTRUCTION_SIZE) + +C_ASSERT(DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * DETOUR_IA64_INSTRUCTION_SIZE == 128); + +__declspec(align(16)) struct DETOUR_IA64_BUNDLE +{ + public: + union + { + BYTE data[16]; + UINT64 wide[2]; + }; + + enum { + A_UNIT = 1u, + I_UNIT = 2u, + M_UNIT = 3u, + B_UNIT = 4u, + F_UNIT = 5u, + L_UNIT = 6u, + X_UNIT = 7u, + }; + struct DETOUR_IA64_METADATA + { + ULONG nTemplate : 8; // Instruction template. + ULONG nUnit0 : 4; // Unit for slot 0 + ULONG nUnit1 : 4; // Unit for slot 1 + ULONG nUnit2 : 4; // Unit for slot 2 + }; + + protected: + static const DETOUR_IA64_METADATA s_rceCopyTable[33]; + + UINT RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; + + bool RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst, + _In_ BYTE slot, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const; + + // 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0 + // f. e. d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0. + + // 00 + // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0. + // 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0] + // 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41.. 5] + // 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42] + // 0000 0000 0007 ffff ffff c000 0000 0000 : One [ 82.. 46] + // 0000 0000 0078 0000 0000 0000 0000 0000 : One [ 86.. 83] + // 0fff ffff ff80 0000 0000 0000 0000 0000 : Two [123.. 87] + // f000 0000 0000 0000 0000 0000 0000 0000 : Two [127..124] + BYTE GetTemplate() const; + // Get 4 bit opcodes. + BYTE GetInst0() const; + BYTE GetInst1() const; + BYTE GetInst2() const; + BYTE GetUnit(BYTE slot) const; + BYTE GetUnit0() const; + BYTE GetUnit1() const; + BYTE GetUnit2() const; + // Get 37 bit data. + UINT64 GetData0() const; + UINT64 GetData1() const; + UINT64 GetData2() const; + + // Get/set the full 41 bit instructions. + UINT64 GetInstruction(BYTE slot) const; + UINT64 GetInstruction0() const; + UINT64 GetInstruction1() const; + UINT64 GetInstruction2() const; + void SetInstruction(BYTE slot, UINT64 instruction); + void SetInstruction0(UINT64 instruction); + void SetInstruction1(UINT64 instruction); + void SetInstruction2(UINT64 instruction); + + // Get/set bitfields. + static UINT64 GetBits(UINT64 Value, UINT64 Offset, UINT64 Count); + static UINT64 SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field); + + // Get specific read-only fields. + static UINT64 GetOpcode(UINT64 instruction); // 4bit opcode + static UINT64 GetX(UINT64 instruction); // 1bit opcode extension + static UINT64 GetX3(UINT64 instruction); // 3bit opcode extension + static UINT64 GetX6(UINT64 instruction); // 6bit opcode extension + + // Get/set specific fields. + static UINT64 GetImm7a(UINT64 instruction); + static UINT64 SetImm7a(UINT64 instruction, UINT64 imm7a); + static UINT64 GetImm13c(UINT64 instruction); + static UINT64 SetImm13c(UINT64 instruction, UINT64 imm13c); + static UINT64 GetSignBit(UINT64 instruction); + static UINT64 SetSignBit(UINT64 instruction, UINT64 signBit); + static UINT64 GetImm20a(UINT64 instruction); + static UINT64 SetImm20a(UINT64 instruction, UINT64 imm20a); + static UINT64 GetImm20b(UINT64 instruction); + static UINT64 SetImm20b(UINT64 instruction, UINT64 imm20b); + + static UINT64 SignExtend(UINT64 Value, UINT64 Offset); + + BOOL IsMovlGp() const; + + VOID SetInst(BYTE Slot, BYTE nInst); + VOID SetInst0(BYTE nInst); + VOID SetInst1(BYTE nInst); + VOID SetInst2(BYTE nInst); + VOID SetData(BYTE Slot, UINT64 nData); + VOID SetData0(UINT64 nData); + VOID SetData1(UINT64 nData); + VOID SetData2(UINT64 nData); + BOOL SetNop(BYTE Slot); + BOOL SetNop0(); + BOOL SetNop1(); + BOOL SetNop2(); + + public: + BOOL IsBrl() const; + VOID SetBrl(); + VOID SetBrl(UINT64 target); + UINT64 GetBrlTarget() const; + VOID SetBrlTarget(UINT64 target); + VOID SetBrlImm(UINT64 imm); + UINT64 GetBrlImm() const; + + UINT64 GetMovlGp() const; + VOID SetMovlGp(UINT64 gp); + + VOID SetStop(); + + UINT Copy(_Out_ DETOUR_IA64_BUNDLE *pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra = NULL) const; +}; +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + +#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1)) +#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1)) + +#endif // DETOURS_ARM + +////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#define DETOUR_OFFLINE_LIBRARY(x) \ +PVOID WINAPI DetourCopyInstruction##x(_In_opt_ PVOID pDst, \ + _Inout_opt_ PVOID *ppDstPool, \ + _In_ PVOID pSrc, \ + _Out_opt_ PVOID *ppTarget, \ + _Out_opt_ LONG *plExtra); \ + \ +BOOL WINAPI DetourSetCodeModule##x(_In_ HMODULE hModule, \ + _In_ BOOL fLimitReferencesToModule); \ + +DETOUR_OFFLINE_LIBRARY(X86) +DETOUR_OFFLINE_LIBRARY(X64) +DETOUR_OFFLINE_LIBRARY(ARM) +DETOUR_OFFLINE_LIBRARY(ARM64) +DETOUR_OFFLINE_LIBRARY(IA64) + +#undef DETOUR_OFFLINE_LIBRARY + +////////////////////////////////////////////////////////////////////////////// +// +// Helpers for manipulating page protection. +// + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess, + _In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect); + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect); +#ifdef __cplusplus +} +#endif // __cplusplus + +////////////////////////////////////////////////////////////////////////////// + +#define MM_ALLOCATION_GRANULARITY 0x10000 + +////////////////////////////////////////////////////////////////////////////// + +#endif // DETOURS_INTERNAL +#endif // __cplusplus + +#endif // _DETOURS_H_ +// +//////////////////////////////////////////////////////////////// End of File. diff --git a/subprojects/detours/include/detver.h b/subprojects/detours/include/detver.h new file mode 100644 index 0000000..f0aae9b --- /dev/null +++ b/subprojects/detours/include/detver.h @@ -0,0 +1,27 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Common version parameters. +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#define _USING_V110_SDK71_ 1 +#include "winver.h" +#if 0 +#include +#include +#else +#ifndef DETOURS_STRINGIFY +#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) +#define DETOURS_STRINGIFY_(x) #x +#endif + +#define VER_FILEFLAGSMASK 0x3fL +#define VER_FILEFLAGS 0x0L +#define VER_FILEOS 0x00040004L +#define VER_FILETYPE 0x00000002L +#define VER_FILESUBTYPE 0x00000000L +#endif +#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) diff --git a/subprojects/detours/meson.build b/subprojects/detours/meson.build new file mode 100644 index 0000000..b6d88d0 --- /dev/null +++ b/subprojects/detours/meson.build @@ -0,0 +1,32 @@ +project('detours', 'cpp', + version: '4.0.1', + license: 'MIT', +) + +add_project_arguments( + '/W4', '/WX', '/Zi', '/MT', '/Gy', '/Gm-', '/Zl', '/Os', + '/DWIN32_LEAN_AND_MEAN', + '/D_WIN32_WINNT=0x501', + + language: 'cpp', +) + +include_dirs = include_directories('include') + +detours = static_library( + 'detours', + sources: [ + 'src/detours.cpp', + 'src/modules.cpp', + 'src/disasm.cpp', + 'src/image.cpp', + 'src/creatwth.cpp', + 'src/disasm.cpp', + 'src/disasm.cpp', + 'src/disasm.cpp', + 'src/disasm.cpp', + 'src/disasm.cpp', + ], + include_directories: include_dirs +) +detours_dep = declare_dependency(include_directories: include_dirs, link_with: detours) diff --git a/subprojects/detours/src/creatwth.cpp b/subprojects/detours/src/creatwth.cpp new file mode 100644 index 0000000..341a62e --- /dev/null +++ b/subprojects/detours/src/creatwth.cpp @@ -0,0 +1,1577 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Create a process with a DLL (creatwth.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#endif +#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1 +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 +#include +#include +#pragma warning(push) +#if _MSC_VER > 1400 +#pragma warning(disable:6102 6103) // /analyze warnings +#endif +#include +#pragma warning(pop) + +// #define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL + +#include "detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#if _MSC_VER >= 1900 +#pragma warning(pop) +#endif + +#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] +#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] +#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] +#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] + +////////////////////////////////////////////////////////////////////////////// +// +const GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */ + 0xea0251b9, 0x5cde, 0x41b5, + { 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }}; + +////////////////////////////////////////////////////////////////////////////// +// +// Enumate through modules in the target process. +// +static BOOL WINAPI LoadNtHeaderFromProcess(HANDLE hProcess, + HMODULE hModule, + PIMAGE_NT_HEADERS32 pNtHeader) +{ + PBYTE pbModule = (PBYTE)hModule; + + if (pbModule == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) { + return FALSE; + } + + IMAGE_DOS_HEADER idh; + + if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", + pbModule, pbModule + sizeof(idh), GetLastError())); + return FALSE; + } + + if (idh.e_magic != IMAGE_DOS_SIGNATURE || + (DWORD)idh.e_lfanew > mbi.RegionSize || + (DWORD)idh.e_lfanew < sizeof(idh)) { + + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + + if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, + pNtHeader, sizeof(*pNtHeader), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %d\n", + pbModule + idh.e_lfanew, + pbModule + idh.e_lfanew + sizeof(*pNtHeader), + pbModule, + GetLastError())); + return FALSE; + } + + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + SetLastError(ERROR_BAD_EXE_FORMAT); + return FALSE; + } + + return TRUE; +} + +static HMODULE WINAPI EnumerateModulesInProcess(HANDLE hProcess, + HMODULE hModuleLast, + PIMAGE_NT_HEADERS32 pNtHeader) +{ + PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY; + + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + // Find the next memory region that contains a mapped PE image. + // + + for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { + if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { + break; + } + + // Usermode address space has such an unaligned region size always at the + // end and only at the end. + // + if ((mbi.RegionSize & 0xfff) == 0xfff) { + break; + } + if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) { + break; + } + + // Skip uncommitted regions and guard pages. + // + if ((mbi.State != MEM_COMMIT) || + ((mbi.Protect & 0xff) == PAGE_NOACCESS) || + (mbi.Protect & PAGE_GUARD)) { + continue; + } + + if (LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader)) { + return (HMODULE)pbLast; + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Find a region of memory in which we can create a replacement import table. +// +static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbBase, DWORD cbAlloc) +{ + MEMORY_BASIC_INFORMATION mbi; + ZeroMemory(&mbi, sizeof(mbi)); + + PBYTE pbLast = pbBase; + for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { + + ZeroMemory(&mbi, sizeof(mbi)); + if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + break; + } + DETOUR_TRACE(("VirtualQueryEx(%p) failed: %d\n", + pbLast, GetLastError())); + break; + } + // Usermode address space has such an unaligned region size always at the + // end and only at the end. + // + if ((mbi.RegionSize & 0xfff) == 0xfff) { + break; + } + + // Skip anything other than a pure free region. + // + if (mbi.State != MEM_FREE) { + continue; + } + + PBYTE pbAddress = (PBYTE)(((DWORD_PTR)mbi.BaseAddress + 0xffff) & ~(DWORD_PTR)0xffff); + +#ifdef _WIN64 + // The distance from pbBase to pbAddress must fit in 32 bits. + // + const size_t GB4 = ((((size_t)1) << 32) - 1); + if ((size_t)(pbAddress - pbBase) > GB4) { + DETOUR_TRACE(("FindAndAllocateNearBase(1) failing due to distance >4GB %p\n", pbAddress)); + return NULL; + } +#endif + + DETOUR_TRACE(("Free region %p..%p\n", + mbi.BaseAddress, + (PBYTE)mbi.BaseAddress + mbi.RegionSize)); + + for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) { + PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc, + MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (pbAlloc == NULL) { + DETOUR_TRACE(("VirtualAllocEx(%p) failed: %d\n", pbAddress, GetLastError())); + continue; + } +#ifdef _WIN64 + // The distance from pbBase to pbAddress must fit in 32 bits. + // + if ((size_t)(pbAddress - pbBase) > GB4) { + DETOUR_TRACE(("FindAndAllocateNearBase(2) failing due to distance >4GB %p\n", pbAddress)); + return NULL; + } +#endif + DETOUR_TRACE(("[%p..%p] Allocated for import table.\n", + pbAlloc, pbAlloc + cbAlloc)); + return pbAlloc; + } + } + return NULL; +} + +static inline DWORD PadToDword(DWORD dw) +{ + return (dw + 3) & ~3u; +} + +static inline DWORD PadToDwordPtr(DWORD dw) +{ + return (dw + 7) & ~7u; +} + +static inline HRESULT ReplaceOptionalSizeA(_Inout_z_count_(cchDest) LPSTR pszDest, + _In_ size_t cchDest, + _In_z_ LPCSTR pszSize) +{ + if (cchDest == 0 || pszDest == NULL || pszSize == NULL || + pszSize[0] == '\0' || pszSize[1] == '\0' || pszSize[2] != '\0') { + + // can not write into empty buffer or with string other than two chars. + return ERROR_INVALID_PARAMETER; + } + + for (; cchDest >= 2; cchDest--, pszDest++) { + if (pszDest[0] == '?' && pszDest[1] == '?') { + pszDest[0] = pszSize[0]; + pszDest[1] = pszSize[1]; + break; + } + } + + return S_OK; +} + +static BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der) +{ + // Save the various headers for DetourRestoreAfterWith. + ZeroMemory(&der, sizeof(der)); + der.cb = sizeof(der); + + der.pidh = (PBYTE)hModule; + der.cbidh = sizeof(der.idh); + if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", + der.pidh, der.pidh + der.cbidh, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("IDH: %p..%p\n", der.pidh, der.pidh + der.cbidh)); + + // We read the NT header in two passes to get the full size. + // First we read just the Signature and FileHeader. + der.pinh = der.pidh + der.idh.e_lfanew; + der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader); + if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", + der.pinh, der.pinh + der.cbinh, GetLastError())); + return FALSE; + } + + // Second we read the OptionalHeader and Section headers. + der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + der.inh.FileHeader.SizeOfOptionalHeader + + der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)); + + if (der.cbinh > sizeof(der.raw)) { + return FALSE; + } + + if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", + der.pinh, der.pinh + der.cbinh, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("INH: %p..%p\n", der.pinh, der.pinh + der.cbinh)); + + // Third, we read the CLR header + + if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 && + der.inh32.CLR_DIRECTORY.Size != 0) { + + DETOUR_TRACE(("CLR32.VirtAddr=%x, CLR.Size=%x\n", + der.inh32.CLR_DIRECTORY.VirtualAddress, + der.inh32.CLR_DIRECTORY.Size)); + + der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress; + } + } + else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 && + der.inh64.CLR_DIRECTORY.Size != 0) { + + DETOUR_TRACE(("CLR64.VirtAddr=%x, CLR.Size=%x\n", + der.inh64.CLR_DIRECTORY.VirtualAddress, + der.inh64.CLR_DIRECTORY.Size)); + + der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress; + } + } + + if (der.pclr != 0) { + der.cbclr = sizeof(der.clr); + if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(clr@%p..%p) failed: %d\n", + der.pclr, der.pclr + der.cbclr, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); + } + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// +#if DETOURS_32BIT +#define DWORD_XX DWORD32 +#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS32 +#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR32_MAGIC +#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG32 +#define UPDATE_IMPORTS_XX UpdateImports32 +#define DETOURS_BITS_XX 32 +#include "uimports.cpp" +#undef DETOUR_EXE_RESTORE_FIELD_XX +#undef DWORD_XX +#undef IMAGE_NT_HEADERS_XX +#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX +#undef IMAGE_ORDINAL_FLAG_XX +#undef UPDATE_IMPORTS_XX +#endif // DETOURS_32BIT + +#if DETOURS_64BIT +#define DWORD_XX DWORD64 +#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS64 +#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR64_MAGIC +#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG64 +#define UPDATE_IMPORTS_XX UpdateImports64 +#define DETOURS_BITS_XX 64 +#include "uimports.cpp" +#undef DETOUR_EXE_RESTORE_FIELD_XX +#undef DWORD_XX +#undef IMAGE_NT_HEADERS_XX +#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX +#undef IMAGE_ORDINAL_FLAG_XX +#undef UPDATE_IMPORTS_XX +#endif // DETOURS_64BIT + +////////////////////////////////////////////////////////////////////////////// +// +#if DETOURS_64BIT + +C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16); + +static BOOL UpdateFrom32To64(HANDLE hProcess, HMODULE hModule, WORD machine, + DETOUR_EXE_RESTORE& der) +{ + IMAGE_DOS_HEADER idh; + IMAGE_NT_HEADERS32 inh32; + IMAGE_NT_HEADERS64 inh64; + IMAGE_SECTION_HEADER sects[32]; + PBYTE pbModule = (PBYTE)hModule; + DWORD n; + + ZeroMemory(&inh32, sizeof(inh32)); + ZeroMemory(&inh64, sizeof(inh64)); + ZeroMemory(sects, sizeof(sects)); + + DETOUR_TRACE(("UpdateFrom32To64(%04x)\n", machine)); + //////////////////////////////////////////////////////// Read old headers. + // + if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", + pbModule, pbModule + sizeof(idh), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p)\n", + pbModule, pbModule + sizeof(idh))); + + PBYTE pnh = pbModule + idh.e_lfanew; + if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) { + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", + pnh, pnh + sizeof(inh32), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh32))); + + if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) { + return FALSE; + } + + PBYTE psects = pnh + + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + inh32.FileHeader.SizeOfOptionalHeader; + ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (!ReadProcessMemory(hProcess, psects, §s, cb, NULL)) { + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %d\n", + psects, psects + cb, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p)\n", psects, psects + cb)); + + ////////////////////////////////////////////////////////// Convert header. + // + inh64.Signature = inh32.Signature; + inh64.FileHeader = inh32.FileHeader; + inh64.FileHeader.Machine = machine; + inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64); + + inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; + inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion; + inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion; + inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode; + inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData; + inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData; + inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint; + inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode; + inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase; + inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment; + inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment; + inh64.OptionalHeader.MajorOperatingSystemVersion + = inh32.OptionalHeader.MajorOperatingSystemVersion; + inh64.OptionalHeader.MinorOperatingSystemVersion + = inh32.OptionalHeader.MinorOperatingSystemVersion; + inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion; + inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion; + inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion; + inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion; + inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue; + inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage; + inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders; + inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum; + inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem; + inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics; + inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve; + inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit; + inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve; + inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit; + inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags; + inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes; + for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) { + inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n]; + } + + /////////////////////////////////////////////////////// Write new headers. + // + DWORD dwProtect = 0; + if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, + PAGE_EXECUTE_READWRITE, &dwProtect)) { + return FALSE; + } + + if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %d\n", + pnh, pnh + sizeof(inh64), GetLastError())); + return FALSE; + } + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh64))); + + psects = pnh + + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + + inh64.FileHeader.SizeOfOptionalHeader; + cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); + if (!WriteProcessMemory(hProcess, psects, §s, cb, NULL)) { + DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p) failed: %d\n", + psects, psects + cb, GetLastError())); + return FALSE; + } + DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb)); + + // Record the updated headers. + if (!RecordExeRestore(hProcess, hModule, der)) { + return FALSE; + } + + // Remove the import table. + if (der.pclr != NULL && (der.clr.Flags & 1)) { + inh64.IMPORT_DIRECTORY.VirtualAddress = 0; + inh64.IMPORT_DIRECTORY.Size = 0; + + if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %d\n", + pnh, pnh + sizeof(inh64), GetLastError())); + return FALSE; + } + } + + DWORD dwOld = 0; + if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, + dwProtect, &dwOld)) { + return FALSE; + } + + return TRUE; +} +#endif // DETOURS_64BIT + +////////////////////////////////////////////////////////////////////////////// +// +BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls) +{ + // Find the next memory region that contains a mapped PE image. + // + BOOL bHas64BitDll = FALSE; + BOOL bHas32BitExe = FALSE; + BOOL bIs32BitProcess; + HMODULE hModule = NULL; + HMODULE hLast = NULL; + + DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%d)\n", hProcess, nDlls)); + + for (;;) { + IMAGE_NT_HEADERS32 inh; + + if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh)) == NULL) { + break; + } + + DETOUR_TRACE(("%p machine=%04x magic=%04x\n", + hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic)); + + if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) { + hModule = hLast; + if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC + && inh.FileHeader.Machine != 0) { + + bHas32BitExe = TRUE; + } + DETOUR_TRACE(("%p Found EXE\n", hLast)); + } + else { + if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC + && inh.FileHeader.Machine != 0) { + + bHas64BitDll = TRUE; + } + } + } + + if (hModule == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + if (!bHas32BitExe) { + bIs32BitProcess = FALSE; + } + else if (!bHas64BitDll) { + bIs32BitProcess = TRUE; + } + else { + if (!IsWow64Process(hProcess, &bIs32BitProcess)) { + return FALSE; + } + } + + DETOUR_TRACE((" 32BitExe=%d 32BitProcess\n", bHas32BitExe, bIs32BitProcess)); + + return DetourUpdateProcessWithDllEx(hProcess, + hModule, + bIs32BitProcess, + rlpDlls, + nDlls); +} + +BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, + _In_ HMODULE hModule, + _In_ BOOL bIs32BitProcess, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ DWORD nDlls) +{ + // Find the next memory region that contains a mapped PE image. + // + BOOL bIs32BitExe = FALSE; + + DETOUR_TRACE(("DetourUpdateProcessWithDllEx(%p,%p,dlls=%d)\n", hProcess, hModule, nDlls)); + + IMAGE_NT_HEADERS32 inh; + + if (hModule == NULL || LoadNtHeaderFromProcess(hProcess, hModule, &inh) == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC + && inh.FileHeader.Machine != 0) { + + bIs32BitExe = TRUE; + } + + DETOUR_TRACE((" 32BitExe=%d 32BitProcess\n", bIs32BitExe, bIs32BitProcess)); + + if (hModule == NULL) { + SetLastError(ERROR_INVALID_OPERATION); + return FALSE; + } + + // Save the various headers for DetourRestoreAfterWith. + // + DETOUR_EXE_RESTORE der; + + if (!RecordExeRestore(hProcess, hModule, der)) { + return FALSE; + } + +#if defined(DETOURS_64BIT) + // Try to convert a neutral 32-bit managed binary to a 64-bit managed binary. + if (bIs32BitExe && !bIs32BitProcess) { + if (!der.pclr // Native binary + || (der.clr.Flags & 1) == 0 // Or mixed-mode MSIL + || (der.clr.Flags & 2) != 0) { // Or 32BIT Required MSIL + + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!UpdateFrom32To64(hProcess, hModule, +#if defined(DETOURS_X64) + IMAGE_FILE_MACHINE_AMD64, +#elif defined(DETOURS_IA64) + IMAGE_FILE_MACHINE_IA64, +#elif defined(DETOURS_ARM64) + IMAGE_FILE_MACHINE_ARM64, +#else +#error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit. +#endif + der)) { + return FALSE; + } + bIs32BitExe = FALSE; + } +#endif // DETOURS_64BIT + + // Now decide if we can insert the detour. + +#if defined(DETOURS_32BIT) + if (bIs32BitProcess) { + // 32-bit native or 32-bit managed process on any platform. + if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) { + return FALSE; + } + } + else { + // 64-bit native or 64-bit managed process. + // + // Can't detour a 64-bit process with 32-bit code. + // Note: This happens for 32-bit PE binaries containing only + // manage code that have been marked as 64-bit ready. + // + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } +#elif defined(DETOURS_64BIT) + if (bIs32BitProcess || bIs32BitExe) { + // Can't detour a 32-bit process with 64-bit code. + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + else { + // 64-bit native or 64-bit managed process on any platform. + if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) { + return FALSE; + } + } +#else +#pragma Must define one of DETOURS_32BIT or DETOURS_64BIT. +#endif // DETOURS_64BIT + + /////////////////////////////////////////////////// Update the CLR header. + // + if (der.pclr != NULL) { + DETOUR_CLR_HEADER clr; + CopyMemory(&clr, &der.clr, sizeof(clr)); + clr.Flags &= 0xfffffffe; // Clear the IL_ONLY flag. + + DWORD dwProtect; + if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %d\n", GetLastError())); + return FALSE; + } + + if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) { + DETOUR_TRACE(("WriteProcessMemory(clr) failed: %d\n", GetLastError())); + return FALSE; + } + + if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) { + DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %d\n", GetLastError())); + return FALSE; + } + DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); + +#if DETOURS_64BIT + if (der.clr.Flags & 0x2) { // Is the 32BIT Required Flag set? + // X64 never gets here because the process appears as a WOW64 process. + // However, on IA64, it doesn't appear to be a WOW process. + DETOUR_TRACE(("CLR Requires 32-bit\n", der.pclr, der.pclr + der.cbclr)); + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } +#endif // DETOURS_64BIT + } + + //////////////////////////////// Save the undo data to the target process. + // + if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); + return FALSE; + } + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// +BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); + PROCESS_INFORMATION pi; + BOOL fResult = FALSE; + + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + fResult = pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwMyCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi); + + if (lpProcessInformation != NULL) { + CopyMemory(lpProcessInformation, &pi, sizeof(pi)); + } + + if (!fResult) { + return FALSE; + } + + LPCSTR rlpDlls[2]; + DWORD nDlls = 0; + if (lpDllName != NULL) { + rlpDlls[nDlls++] = lpDllName; + } + + if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { + TerminateProcess(pi.hProcess, ~0u); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(pi.hThread); + } + return TRUE; +} + + +BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); + PROCESS_INFORMATION pi; + + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + BOOL fResult = pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwMyCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + &pi); + + if (lpProcessInformation) { + CopyMemory(lpProcessInformation, &pi, sizeof(pi)); + } + + if (!fResult) { + return FALSE; + } + + LPCSTR rlpDlls[2]; + DWORD nDlls = 0; + if (lpDllName != NULL) { + rlpDlls[nDlls++] = lpDllName; + } + + if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { + TerminateProcess(pi.hProcess, ~0u); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(pi.hThread); + } + return TRUE; +} + +BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, + _In_ REFGUID rguid, + _In_reads_bytes_(cbData) PVOID pvData, + _In_ DWORD cbData) +{ + DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) + + sizeof(IMAGE_NT_HEADERS) + + sizeof(IMAGE_SECTION_HEADER) + + sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + + PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal, + MEM_COMMIT, PAGE_READWRITE); + if (pbBase == NULL) { + DETOUR_TRACE(("VirtualAllocEx(%d) failed: %d\n", cbTotal, GetLastError())); + return FALSE; + } + + PBYTE pbTarget = pbBase; + IMAGE_DOS_HEADER idh; + IMAGE_NT_HEADERS inh; + IMAGE_SECTION_HEADER ish; + DETOUR_SECTION_HEADER dsh; + DETOUR_SECTION_RECORD dsr; + SIZE_T cbWrote = 0; + + ZeroMemory(&idh, sizeof(idh)); + idh.e_magic = IMAGE_DOS_SIGNATURE; + idh.e_lfanew = sizeof(idh); + if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) || + cbWrote != sizeof(idh)) { + DETOUR_TRACE(("WriteProcessMemory(idh) failed: %d\n", GetLastError())); + return FALSE; + } + pbTarget += sizeof(idh); + + ZeroMemory(&inh, sizeof(inh)); + inh.Signature = IMAGE_NT_SIGNATURE; + inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader); + inh.FileHeader.Characteristics = IMAGE_FILE_DLL; + inh.FileHeader.NumberOfSections = 1; + inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC; + if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) || + cbWrote != sizeof(inh)) { + return FALSE; + } + pbTarget += sizeof(inh); + + ZeroMemory(&ish, sizeof(ish)); + memcpy(ish.Name, ".detour", sizeof(ish.Name)); + ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase); + ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) || + cbWrote != sizeof(ish)) { + return FALSE; + } + pbTarget += sizeof(ish); + + ZeroMemory(&dsh, sizeof(dsh)); + dsh.cbHeaderSize = sizeof(dsh); + dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE; + dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER); + dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) + + sizeof(DETOUR_SECTION_RECORD) + + cbData); + if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) || + cbWrote != sizeof(dsh)) { + return FALSE; + } + pbTarget += sizeof(dsh); + + ZeroMemory(&dsr, sizeof(dsr)); + dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD); + dsr.nReserved = 0; + dsr.guid = rguid; + if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) || + cbWrote != sizeof(dsr)) { + return FALSE; + } + pbTarget += sizeof(dsr); + + if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) || + cbWrote != cbData) { + return FALSE; + } + pbTarget += cbData; + + DETOUR_TRACE(("Copied %d byte payload into target process at %p\n", + cbTotal, pbTarget - cbTotal)); + return TRUE; +} + +static BOOL s_fSearchedForHelper = FALSE; +static PDETOUR_EXE_HELPER s_pHelper = NULL; + +VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, + _In_ HINSTANCE, + _In_ LPSTR, + _In_ INT) +{ + LPCSTR * rlpDlls = NULL; + DWORD Result = 9900; + + if (s_pHelper == NULL) { + DETOUR_TRACE(("DetourFinishHelperProcess called with s_pHelper = NULL.\n")); + Result = 9905; + goto Cleanup; + } + + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid); + if (hProcess == NULL) { + DETOUR_TRACE(("OpenProcess(pid=%d) failed: %d\n", + s_pHelper->pid, GetLastError())); + Result = 9901; + goto Cleanup; + } + + rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls]; + DWORD cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER); + DWORD cOffset = 0; + for (DWORD n = 0; n < s_pHelper->nDlls; n++) { + size_t cchDest = 0; + HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest); + if (!SUCCEEDED(hr)) { + Result = 9902; + goto Cleanup; + } + + rlpDlls[n] = &s_pHelper->rDlls[cOffset]; + cOffset += (DWORD)cchDest + 1; + } + + if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) { + DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%d) failed: %d\n", + s_pHelper->pid, GetLastError())); + Result = 9903; + goto Cleanup; + } + Result = 0; + + Cleanup: + if (rlpDlls != NULL) { + delete[] rlpDlls; + rlpDlls = NULL; + } + + ExitProcess(Result); +} + +BOOL WINAPI DetourIsHelperProcess(VOID) +{ + PVOID pvData; + DWORD cbData; + + if (s_fSearchedForHelper) { + return (s_pHelper != NULL); + } + + s_fSearchedForHelper = TRUE; + pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData); + + if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) { + return FALSE; + } + + s_pHelper = (PDETOUR_EXE_HELPER)pvData; + if (s_pHelper->cb < sizeof(*s_pHelper)) { + s_pHelper = NULL; + return FALSE; + } + + return TRUE; +} + +static +BOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper, + _In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls) +{ + PDETOUR_EXE_HELPER Helper = NULL; + BOOL Result = FALSE; + + if (pHelper == NULL) { + goto Cleanup; + } + *pHelper = NULL; + + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + + DWORD cSize = 4; + for (DWORD n = 0; n < nDlls; n++) { + HRESULT hr; + size_t cchDest = 0; + + hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + cSize += (DWORD)cchDest + 1; + } + + Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize]; + if (Helper == NULL) { + goto Cleanup; + } + + Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize; + Helper->pid = dwTargetPid; + Helper->nDlls = nDlls; + + _Field_range_(0, cSize - 4) DWORD cOffset = 0; + for (DWORD n = 0; n < nDlls; n++) { + HRESULT hr; + size_t cchDest = 0; + + if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) { + goto Cleanup; + } + + if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) { + goto Cleanup; + } + + _Analysis_assume_(cOffset + 1 < cSize); + _Analysis_assume_(cOffset < 0x10000); + _Analysis_assume_(cSize < 0x10000); + + PCHAR psz = &Helper->rDlls[cOffset]; + + hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + +// REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call. +// REVIEW 28313 Analysis will not proceed past this point because of annotation evaluation. The annotation expression *_Param_(3)<_Param_(2)&&*_Param_(3)<=stringLength$(_Param_(1)) cannot be true under any assumptions at this point in the program. +#pragma warning(suppress:28020 28313) + hr = StringCchLengthA(psz, cSize - cOffset, &cchDest); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + // Replace "32." with "64." or "64." with "32." + + for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) { +#if DETOURS_32BIT + if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') { + psz[c - 3] = '6'; psz[c - 2] = '4'; + break; + } +#else + if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') { + psz[c - 3] = '3'; psz[c - 2] = '2'; + break; + } +#endif + } + + cOffset += (DWORD)cchDest + 1; + } + + *pHelper = Helper; + Helper = NULL; + Result = TRUE; + + Cleanup: + if (Helper != NULL) { + delete[] (PBYTE)Helper; + Helper = NULL; + } + return Result; +} + +static +VOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper) +{ + if (*pHelper != NULL) { + delete[] (PBYTE)*pHelper; + *pHelper = NULL; + } +} + +BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA); +} + + +BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + BOOL Result = FALSE; + PROCESS_INFORMATION pi; + STARTUPINFOA si; + CHAR szExe[MAX_PATH]; + CHAR szCommand[MAX_PATH]; + PDETOUR_EXE_HELPER helper = NULL; + HRESULT hr; + + DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%d,dlls=%d)\n", dwTargetPid, nDlls)); + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { + goto Cleanup; + } + + DWORD nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe)); + if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { + goto Cleanup; + } + +#if DETOURS_OPTION_BITS +#if DETOURS_32BIT + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe"); +#else // !DETOURS_32BIT + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe"); +#endif // !DETOURS_32BIT +#else // DETOURS_OPTIONS_BITS + hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe"); +#endif // DETOURS_OPTIONS_BITS + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand), + "rundll32.exe \"%hs\",#1", &helper->rDlls[0]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%hs\", \"%hs\")\n", szExe, szCommand)); + if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi)) { + + if (!DetourCopyPayloadToProcess(pi.hProcess, + DETOUR_EXE_HELPER_GUID, + helper, helper->cb)) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); + TerminateProcess(pi.hProcess, ~0u); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + goto Cleanup; + } + + ResumeThread(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD dwResult = 500; + GetExitCodeProcess(pi.hProcess, &dwResult); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (dwResult != 0) { + DETOUR_TRACE(("Rundll32.exe failed: result=%d\n", dwResult)); + goto Cleanup; + } + Result = TRUE; + } + else { + DETOUR_TRACE(("CreateProcess failed: %d\n", GetLastError())); + goto Cleanup; + } + + Cleanup: + FreeExeHelper(&helper); + return Result; +} + +BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, + _In_ LPCSTR lpDllName, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW); +} + +BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + BOOL Result = FALSE; + PROCESS_INFORMATION pi; + STARTUPINFOW si; + WCHAR szExe[MAX_PATH]; + WCHAR szCommand[MAX_PATH]; + PDETOUR_EXE_HELPER helper = NULL; + HRESULT hr; + + DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%d,dlls=%d)\n", dwTargetPid, nDlls)); + if (nDlls < 1 || nDlls > 4096) { + SetLastError(ERROR_INVALID_PARAMETER); + goto Cleanup; + } + if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { + goto Cleanup; + } + + DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe)); + if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { + goto Cleanup; + } + +#if DETOURS_OPTION_BITS +#if DETOURS_32BIT + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe"); +#else // !DETOURS_32BIT + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe"); +#endif // !DETOURS_32BIT +#else // DETOURS_OPTIONS_BITS + hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe"); +#endif // DETOURS_OPTIONS_BITS + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand), + L"rundll32.exe \"%hs\",#1", &helper->rDlls[0]); + if (!SUCCEEDED(hr)) { + goto Cleanup; + } + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%ls\", \"%ls\")\n", szExe, szCommand)); + if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, + NULL, NULL, &si, &pi)) { + + if (!DetourCopyPayloadToProcess(pi.hProcess, + DETOUR_EXE_HELPER_GUID, + helper, helper->cb)) { + DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); + TerminateProcess(pi.hProcess, ~0u); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + goto Cleanup; + } + + ResumeThread(pi.hThread); + + ResumeThread(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD dwResult = 500; + GetExitCodeProcess(pi.hProcess, &dwResult); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (dwResult != 0) { + DETOUR_TRACE(("Rundll32.exe failed: result=%d\n", dwResult)); + goto Cleanup; + } + Result = TRUE; + } + else { + DETOUR_TRACE(("CreateProcess failed: %d\n", GetLastError())); + goto Cleanup; + } + + Cleanup: + FreeExeHelper(&helper); + return Result; +} + +BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + LPCSTR szDll = lpDllName; + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) && + !DetourProcessViaHelperA(lpProcessInformation->dwProcessId, + lpDllName, + pfCreateProcessA)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ LPCSTR lpDllName, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + + LPCSTR sz = lpDllName; + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) && + !DetourProcessViaHelperW(lpProcessInformation->dwProcessId, + lpDllName, + pfCreateProcessW)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, + _Inout_opt_ LPSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOA lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) +{ + if (pfCreateProcessA == NULL) { + pfCreateProcessA = CreateProcessA; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessA(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && + !DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId, + nDlls, + rlpDlls, + pfCreateProcessA)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + + return TRUE; +} + +BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, + _Inout_opt_ LPWSTR lpCommandLine, + _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ BOOL bInheritHandles, + _In_ DWORD dwCreationFlags, + _In_opt_ LPVOID lpEnvironment, + _In_opt_ LPCWSTR lpCurrentDirectory, + _In_ LPSTARTUPINFOW lpStartupInfo, + _Out_ LPPROCESS_INFORMATION lpProcessInformation, + _In_ DWORD nDlls, + _In_reads_(nDlls) LPCSTR *rlpDlls, + _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) +{ + if (pfCreateProcessW == NULL) { + pfCreateProcessW = CreateProcessW; + } + + PROCESS_INFORMATION backup; + if (lpProcessInformation == NULL) { + lpProcessInformation = &backup; + ZeroMemory(&backup, sizeof(backup)); + } + + if (!pfCreateProcessW(lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwCreationFlags | CREATE_SUSPENDED, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation)) { + return FALSE; + } + + + if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && + !DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId, + nDlls, + rlpDlls, + pfCreateProcessW)) { + + TerminateProcess(lpProcessInformation->hProcess, ~0u); + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + return FALSE; + } + + if (!(dwCreationFlags & CREATE_SUSPENDED)) { + ResumeThread(lpProcessInformation->hThread); + } + + if (lpProcessInformation == &backup) { + CloseHandle(lpProcessInformation->hProcess); + CloseHandle(lpProcessInformation->hThread); + } + return TRUE; +} + +// +///////////////////////////////////////////////////////////////// End of File. diff --git a/subprojects/detours/src/detours.cpp b/subprojects/detours/src/detours.cpp new file mode 100644 index 0000000..8345c4d --- /dev/null +++ b/subprojects/detours/src/detours.cpp @@ -0,0 +1,2364 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Core Detours Functionality (detours.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#pragma warning(disable:4068) // unknown pragma (suppress) + +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#endif + +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 +#include + +#if (_MSC_VER < 1299) +#pragma warning(disable: 4710) +#endif + +//#define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL + +#include "detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#if _MSC_VER >= 1900 +#pragma warning(pop) +#endif + +#define NOTHROW + +////////////////////////////////////////////////////////////////////////////// +// +struct _DETOUR_ALIGN +{ + BYTE obTarget : 3; + BYTE obTrampoline : 5; +}; + +C_ASSERT(sizeof(_DETOUR_ALIGN) == 1); + +////////////////////////////////////////////////////////////////////////////// +// +// Region reserved for system DLLs, which cannot be used for trampolines. +// +static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000; +static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000; + +////////////////////////////////////////////////////////////////////////////// +// +static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress) +{ + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi)); + __try { + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + return false; + } + + PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + + pDosHeader->e_lfanew); + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { + return false; + } + + if (pbAddress >= ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) && + pbAddress < ((PBYTE)pDosHeader + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) { + return true; + } + } +#pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.") + __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + return false; + } + return false; +} + +inline ULONG_PTR detour_2gb_below(ULONG_PTR address) +{ + return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000; +} + +inline ULONG_PTR detour_2gb_above(ULONG_PTR address) +{ +#if defined(DETOURS_64BIT) + return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000; +#else + return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000; +#endif +} + +///////////////////////////////////////////////////////////////////////// X86. +// +#ifdef DETOURS_X86 + +struct _DETOUR_TRAMPOLINE +{ + BYTE rbCode[30]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + // And, within +/- 2GB of relative jmp targets. + if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", lo, pbCode, hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X86 + +///////////////////////////////////////////////////////////////////////// X64. +// +#ifdef DETOURS_X64 + +struct _DETOUR_TRAMPOLINE +{ + // An X64 instuction can be 15 bytes long. + // In practice 11 seems to be the limit. + BYTE rbCode[30]; // target code + jmp to pbRemain. + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[30]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + BYTE rbCodeIn[8]; // jmp [pbDetour] +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 96); + +enum { + SIZE_OF_JMP = 5 +}; + +inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xE9; // jmp +imm32 + *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 6; + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; + *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc); + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) { // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew)); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + // Looks like an import alias jump, then get the code it points to. + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + if (detour_is_imported(pbCode, pbTarget)) { + pbNew = *(UNALIGNED PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew)); + pbCode = pbNew; + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + // And, within +/- 2GB of relative jmp vectors. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] [+imm32]\n", lo, pbCode, hi)); + } + // And, within +/- 2GB of relative jmp targets. + else if (pbCode[0] == 0xe9) { // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1]; + + if (pbNew < pbCode) { + hi = detour_2gb_above((ULONG_PTR)pbNew); + } + else { + lo = detour_2gb_below((ULONG_PTR)pbNew); + } + DETOUR_TRACE(("[%p..%p..%p] +imm32\n", lo, pbCode, hi)); + } + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) { // brk + return TRUE; + } + else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret + return TRUE; + } + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32] + return TRUE; + } + else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) { + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && + pbCode[3] == 0x00) { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && + pbCode[3] == 0x00 && pbCode[4] == 0x00) { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00) { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && + pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00) { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && + pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && + pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00) { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && + pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 && + pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && + pbCode[9] == 0x00 && pbCode[10] == 0x00) { + return 11; + } + + // int 3. + if (pbCode[0] == 0xcc) { + return 1; + } + return 0; +} + +#endif // DETOURS_X64 + +//////////////////////////////////////////////////////////////////////// IA64. +// +#ifdef DETOURS_IA64 + +struct _DETOUR_TRAMPOLINE +{ + // On the IA64, a trampoline is used for both incoming and outgoing calls. + // + // The trampoline contains the following bundles for the outgoing call: + // movl gp=target_gp; + // + // brl target_code; + // + // The trampoline contains the following bundles for the incoming call: + // alloc r41=ar.pfs, b, 0, 8, 0 + // mov r40=rp + // + // adds r50=0, r39 + // adds r49=0, r38 + // adds r48=0, r37 ;; + // + // adds r47=0, r36 + // adds r46=0, r35 + // adds r45=0, r34 + // + // adds r44=0, r33 + // adds r43=0, r32 + // adds r42=0, gp ;; + // + // movl gp=ffffffff`ffffffff ;; + // + // brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;; + // + // adds gp=0, r42 + // mov rp=r40, +0 ;; + // mov.i ar.pfs=r41 + // + // br.ret.sptk.many rp ;; + // + // This way, we only have to relocate a single bundle. + // + // The complicated incoming trampoline is required because we have to + // create an additional stack frame so that we save and restore the gp. + // We must do this because gp is a caller-saved register, but not saved + // if the caller thinks the target is in the same DLL, which changes + // when we insert a detour. + // + DETOUR_IA64_BUNDLE bMovlTargetGp; // Bundle which sets target GP + BYTE rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle. + DETOUR_IA64_BUNDLE bBrlRemainEip; // Brl to pbRemain + // This must be adjacent to bBranchIslands. + + // Each instruction in the moved bundle could be a IP-relative chk or branch or call. + // Any such instructions are changed to point to a brl in bBranchIslands. + // This must be adjacent to bBrlRemainEip -- see "pbPool". + DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE]; + + // Target of brl inserted in target function + DETOUR_IA64_BUNDLE bAllocFrame; // alloc frame + DETOUR_IA64_BUNDLE bSave37to39; // save r37, r38, r39. + DETOUR_IA64_BUNDLE bSave34to36; // save r34, r35, r36. + DETOUR_IA64_BUNDLE bSaveGPto33; // save gp, r32, r33. + DETOUR_IA64_BUNDLE bMovlDetourGp; // set detour GP. + DETOUR_IA64_BUNDLE bCallDetour; // call detour. + DETOUR_IA64_BUNDLE bPopFrameGp; // pop frame and restore gp. + DETOUR_IA64_BUNDLE bReturn; // return to caller. + + PLABEL_DESCRIPTOR pldTrampoline; + + BYTE rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle. + BYTE cbRestore; // size of original target code. + BYTE cbCode; // size of moved target code. + _DETOUR_ALIGN rAlign[14]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. + PPLABEL_DESCRIPTOR ppldDetour; // [pbDetour,gpDetour] + PPLABEL_DESCRIPTOR ppldTarget; // [pbTarget,gpDetour] +}; + +C_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16); +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16); + +enum { + SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE) +}; + +inline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals) +{ + PBYTE pGlobals = NULL; + PBYTE pbCode = NULL; + + if (pPointer != NULL) { + PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer; + pbCode = (PBYTE)ppld->EntryPoint; + pGlobals = (PBYTE)ppld->GlobalPointer; + } + if (ppGlobals != NULL) { + *ppGlobals = pGlobals; + } + if (pbCode == NULL) { + return NULL; + } + + DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode; + + // IA64 Local Import Jumps look like: + // addl r2=ffffffff`ffe021c0, gp ;; + // ld8 r2=[r2] + // nop.i 0 ;; + // + // ld8 r3=[r2], 8 ;; + // ld8 gp=[r2] + // mov b6=r3, +0 + // + // nop.m 0 + // nop.i 0 + // br.cond.sptk.few b6 + // + + // 002024000200100b + if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b && + pb[0].wide[1] == 0x0004000000203008 && + pb[1].wide[0] == 0x001014180420180a && + pb[1].wide[1] == 0x07000830c0203008 && + pb[2].wide[0] == 0x0000000100000010 && + pb[2].wide[1] == 0x0080006000000200) { + + ULONG64 offset = + ((pb[0].wide[0] & 0x0000000001fc0000) >> 18) | // imm7b + ((pb[0].wide[0] & 0x000001ff00000000) >> 25) | // imm9d + ((pb[0].wide[0] & 0x00000000f8000000) >> 11); // imm5c + if (pb[0].wide[0] & 0x0000020000000000) { // sign + offset |= 0xffffffffffe00000; + } + PBYTE pbTarget = pGlobals + offset; + DETOUR_TRACE(("%p: potential import jump, target=%p\n", pb, pbTarget)); + + if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) { + DETOUR_TRACE(("%p: is import jump, label=%p\n", pb, *(PBYTE *)pbTarget)); + + PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget; + pbCode = (PBYTE)ppld->EntryPoint; + pGlobals = (PBYTE)ppld->GlobalPointer; + if (ppGlobals != NULL) { + *ppGlobals = pGlobals; + } + } + } + return pbCode; +} + + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + (void)pbCode; + *ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000; + *ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + // Routine not needed on IA64. + (void)pbCode; + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + // Routine not needed on IA64. + (void)pbCode; + return 0; +} + +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + +struct _DETOUR_TRAMPOLINE +{ + // A Thumb-2 instruction can be 2 or 4 bytes long. + BYTE rbCode[62]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak; // padding to make debugging easier. + BYTE rbRestore[22]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104); + +enum { + SIZE_OF_JMP = 8 +}; + +inline PBYTE align4(PBYTE pValue) +{ + return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u); +} + +inline ULONG fetch_thumb_opcode(PBYTE pbCode) +{ + ULONG Opcode = *(UINT16 *)&pbCode[0]; + if (Opcode >= 0xe800) { + Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2]; + } + return Opcode; +} + +inline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode) +{ + if (Opcode >= 0x10000) { + *((UINT16*&)pbCode)++ = Opcode >> 16; + } + *((UINT16*&)pbCode)++ = (UINT16)Opcode; +} + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 4; + pbLiteral = *ppPool; + } + else { + pbLiteral = align4(pbCode + 6); + } + + *((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal); + LONG delta = pbLiteral - align4(pbCode + 4); + + write_thumb_opcode(pbCode, 0xf8dff000 | delta); // LDR PC,[PC+n] + + if (ppPool == NULL) { + if (((ULONG)pbCode & 2) != 0) { + write_thumb_opcode(pbCode, 0xdefe); // BREAK + } + pbCode += 4; + } + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + write_thumb_opcode(pbCode, 0xdefe); + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // Skip over the import jump if there is one. + pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode); + ULONG Opcode = fetch_thumb_opcode(pbCode); + + if ((Opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx + ULONG Opcode2 = fetch_thumb_opcode(pbCode+4); + + if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) { // movt r12,#xxxx + ULONG Opcode3 = fetch_thumb_opcode(pbCode+8); + if (Opcode3 == 0xf8dcf000) { // ldr pc,[r12] + PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) | + ((Opcode2 << 1) & 0x08000000) | + ((Opcode2 << 16) & 0x00ff0000) | + ((Opcode >> 4) & 0x0000f700) | + ((Opcode >> 15) & 0x00000800) | + ((Opcode >> 0) & 0x000000ff)); + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(PBYTE *)pbTarget; + pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew); + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + return pbNew; + } + } + } + } + return pbCode; +} + +inline void detour_find_jmp_bounds(PBYTE pbCode, + PDETOUR_TRAMPOLINE *ppLower, + PDETOUR_TRAMPOLINE *ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode); + ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode); + DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi)); + + *ppLower = (PDETOUR_TRAMPOLINE)lo; + *ppUpper = (PDETOUR_TRAMPOLINE)hi; +} + + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + ULONG Opcode = fetch_thumb_opcode(pbCode); + if ((Opcode & 0xffffff87) == 0x4700 || // bx + (Opcode & 0xf800d000) == 0xf0009000) { // b + return TRUE; + } + if ((Opcode & 0xffff8000) == 0xe8bd8000) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + if ((Opcode & 0xffffff00) == 0x0000bd00) { // pop {...,pc} + __debugbreak(); + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop. + return 2; + } + if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding. + return 2; + } + return 0; +} + +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + +struct _DETOUR_TRAMPOLINE +{ + // An ARM64 instruction is 4 bytes long. + BYTE rbCode[64]; // target code + jmp to pbRemain + BYTE cbCode; // size of moved target code. + BYTE cbCodeBreak[3]; // padding to make debugging easier. + BYTE rbRestore[24]; // original target code. + BYTE cbRestore; // size of original target code. + BYTE cbRestoreBreak[3]; // padding to make debugging easier. + _DETOUR_ALIGN rAlign[8]; // instruction alignment array. + PBYTE pbRemain; // first instruction after moved code. [free list] + PBYTE pbDetour; // first instruction of detour function. +}; + +C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 120); + +enum { + SIZE_OF_JMP = 8 +}; + +inline ULONG fetch_opcode(PBYTE pbCode) +{ + return *(ULONG *)pbCode; +} + +inline void write_opcode(PBYTE &pbCode, ULONG Opcode) +{ + *(ULONG *)pbCode = Opcode; + pbCode += 4; +} + +PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) { + *ppPool = *ppPool - 8; + pbLiteral = *ppPool; + } + else { + pbLiteral = pbCode + 2*4; + } + + *((PBYTE*&)pbLiteral) = pbJmpVal; + LONG delta = (LONG)(pbLiteral - pbCode); + + write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n] + write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17 + + if (ppPool == NULL) { + pbCode += 8; + } + return pbCode; +} + +inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit) +{ + while (pbCode < pbLimit) { + write_opcode(pbCode, 0xd4100000 | (0xf000 << 5)); + } + return pbCode; +} + +inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals) +{ + if (pbCode == NULL) { + return NULL; + } + if (ppGlobals != NULL) { + *ppGlobals = NULL; + } + + // Skip over the import jump if there is one. + pbCode = (PBYTE)pbCode; + ULONG Opcode = fetch_opcode(pbCode); + if ((Opcode & 0x9f00001f) == 0x90000010) { // adrp x16, IAT + ULONG Opcode2 = fetch_opcode(pbCode+4); + + if ((Opcode2 & 0xffe003ff) == 0xf9400210) { // ldr x16, [x16, IAT] + ULONG Opcode3 = fetch_opcode(pbCode+8); + + if (Opcode3 == 0xd61f0200) { // br x16 + + ULONG PageOffset = ((Opcode & 0x60000000) >> 29) | ((Opcode & 0x00ffffe0) >> 3); + PageOffset = (LONG)(Opcode << 11) >> 11; + + PBYTE pbTarget = (PBYTE)(((ULONG64)pbCode & 0xfffffffffffff000ULL) + PageOffset + + ((Opcode2 >> 10) & 0xfff)); + if (detour_is_imported(pbCode, pbTarget)) { + PBYTE pbNew = *(PBYTE *)pbTarget; + DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew)); + return pbNew; + } + } + } + } + return pbCode; +} + +inline BOOL detour_does_code_end_function(PBYTE pbCode) +{ + ULONG Opcode = fetch_opcode(pbCode); + if ((Opcode & 0xfffffc1f) == 0xd65f0000 || // br + (Opcode & 0xfc000000) == 0x14000000) { // b + return TRUE; + } + return FALSE; +} + +inline ULONG detour_is_code_filler(PBYTE pbCode) +{ + if (*(ULONG *)pbCode == 0xd503201f) { // nop. + return 4; + } + if (*(ULONG *)pbCode == 0x00000000) { // zero-filled padding. + return 4; + } + return 0; +} + +#endif // DETOURS_ARM64 + +//////////////////////////////////////////////// Trampoline Memory Management. +// +struct DETOUR_REGION +{ + ULONG dwSignature; + DETOUR_REGION * pNext; // Next region in list of regions. + DETOUR_TRAMPOLINE * pFree; // List of free trampolines in this region. +}; +typedef DETOUR_REGION * PDETOUR_REGION; + +const ULONG DETOUR_REGION_SIGNATURE = 'Rrtd'; +const ULONG DETOUR_REGION_SIZE = 0x10000; +const ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE + / sizeof(DETOUR_TRAMPOLINE)) - 1; +static PDETOUR_REGION s_pRegions = NULL; // List of all regions. +static PDETOUR_REGION s_pRegion = NULL; // Default region. + +static DWORD detour_writable_trampoline_regions() +{ + // Mark all of the regions as writable. + for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) { + DWORD dwOld; + if (!VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READWRITE, &dwOld)) { + return GetLastError(); + } + } + return NO_ERROR; +} + +static void detour_runnable_trampoline_regions() +{ + HANDLE hProcess = GetCurrentProcess(); + + // Mark all of the regions as executable. + for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) { + DWORD dwOld; + VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READ, &dwOld); + FlushInstructionCache(hProcess, pRegion, DETOUR_REGION_SIZE); + } +} + +static PBYTE detour_alloc_round_down_to_region(PBYTE pbTry) +{ + // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications. + ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1); + if (extra != 0) { + pbTry -= extra; + } + return pbTry; +} + +static PBYTE detour_alloc_round_up_to_region(PBYTE pbTry) +{ + // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications. + ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1); + if (extra != 0) { + ULONG_PTR adjust = DETOUR_REGION_SIZE - extra; + pbTry += adjust; + } + return pbTry; +} + +// Starting at pbLo, try to allocate a memory region, continue until pbHi. + +static PVOID detour_alloc_region_from_lo(PBYTE pbLo, PBYTE pbHi) +{ + PBYTE pbTry = detour_alloc_round_up_to_region(pbLo); + + DETOUR_TRACE((" Looking for free region in %p..%p from %p:\n", pbLo, pbHi, pbTry)); + + for (; pbTry < pbHi;) { + MEMORY_BASIC_INFORMATION mbi; + + if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) { + // Skip region reserved for system DLLs, but preserve address space entropy. + pbTry += 0x08000000; + continue; + } + + ZeroMemory(&mbi, sizeof(mbi)); + if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) { + break; + } + + DETOUR_TRACE((" Try %p => %p..%p %6x\n", + pbTry, + mbi.BaseAddress, + (PBYTE)mbi.BaseAddress + mbi.RegionSize - 1, + mbi.State)); + + if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) { + + PVOID pv = VirtualAlloc(pbTry, + DETOUR_REGION_SIZE, + MEM_COMMIT|MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (pv != NULL) { + return pv; + } + pbTry += DETOUR_REGION_SIZE; + } + else { + pbTry = detour_alloc_round_up_to_region((PBYTE)mbi.BaseAddress + mbi.RegionSize); + } + } + return NULL; +} + +// Starting at pbHi, try to allocate a memory region, continue until pbLo. + +static PVOID detour_alloc_region_from_hi(PBYTE pbLo, PBYTE pbHi) +{ + PBYTE pbTry = detour_alloc_round_down_to_region(pbHi - DETOUR_REGION_SIZE); + + DETOUR_TRACE((" Looking for free region in %p..%p from %p:\n", pbLo, pbHi, pbTry)); + + for (; pbTry > pbLo;) { + MEMORY_BASIC_INFORMATION mbi; + + DETOUR_TRACE((" Try %p\n", pbTry)); + if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) { + // Skip region reserved for system DLLs, but preserve address space entropy. + pbTry -= 0x08000000; + continue; + } + + ZeroMemory(&mbi, sizeof(mbi)); + if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) { + break; + } + + DETOUR_TRACE((" Try %p => %p..%p %6x\n", + pbTry, + mbi.BaseAddress, + (PBYTE)mbi.BaseAddress + mbi.RegionSize - 1, + mbi.State)); + + if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) { + + PVOID pv = VirtualAlloc(pbTry, + DETOUR_REGION_SIZE, + MEM_COMMIT|MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + if (pv != NULL) { + return pv; + } + pbTry -= DETOUR_REGION_SIZE; + } + else { + pbTry = detour_alloc_round_down_to_region((PBYTE)mbi.AllocationBase + - DETOUR_REGION_SIZE); + } + } + return NULL; +} + +static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget) +{ + // We have to place trampolines within +/- 2GB of target. + + PDETOUR_TRAMPOLINE pLo; + PDETOUR_TRAMPOLINE pHi; + + detour_find_jmp_bounds(pbTarget, &pLo, &pHi); + + PDETOUR_TRAMPOLINE pTrampoline = NULL; + + // Insure that there is a default region. + if (s_pRegion == NULL && s_pRegions != NULL) { + s_pRegion = s_pRegions; + } + + // First check the default region for an valid free block. + if (s_pRegion != NULL && s_pRegion->pFree != NULL && + s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) { + + found_region: + pTrampoline = s_pRegion->pFree; + // do a last sanity check on region. + if (pTrampoline < pLo || pTrampoline > pHi) { + return NULL; + } + s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain; + memset(pTrampoline, 0xcc, sizeof(*pTrampoline)); + return pTrampoline; + } + + // Then check the existing regions for a valid free block. + for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) { + if (s_pRegion != NULL && s_pRegion->pFree != NULL && + s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) { + goto found_region; + } + } + + // We need to allocate a new region. + + // Round pbTarget down to 64KB block. + pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff); + + PVOID pbTry = NULL; + + // NB: We must always also start the search at an offset from pbTarget + // in order to maintain ASLR entropy. + +#if defined(DETOURS_64BIT) + // Try looking 1GB below or lower. + if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) { + pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000); + } + // Try looking 1GB above or higher. + if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) { + pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi); + } + // Try looking 1GB below or higher. + if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) { + pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget); + } + // Try looking 1GB above or lower. + if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) { + pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000); + } +#endif + + // Try anything below. + if (pbTry == NULL) { + pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget); + } + // try anything above. + if (pbTry == NULL) { + pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi); + } + + if (pbTry != NULL) { + s_pRegion = (DETOUR_REGION*)pbTry; + s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE; + s_pRegion->pFree = NULL; + s_pRegion->pNext = s_pRegions; + s_pRegions = s_pRegion; + DETOUR_TRACE((" Allocated region %p..%p\n\n", + s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1)); + + // Put everything but the first trampoline on the free list. + PBYTE pFree = NULL; + pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1; + for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) { + pTrampoline[i].pbRemain = pFree; + pFree = (PBYTE)&pTrampoline[i]; + } + s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree; + goto found_region; + } + + DETOUR_TRACE(("Couldn't find available memory region!\n")); + return NULL; +} + +static void detour_free_trampoline(PDETOUR_TRAMPOLINE pTrampoline) +{ + PDETOUR_REGION pRegion = (PDETOUR_REGION) + ((ULONG_PTR)pTrampoline & ~(ULONG_PTR)0xffff); + + memset(pTrampoline, 0, sizeof(*pTrampoline)); + pTrampoline->pbRemain = (PBYTE)pRegion->pFree; + pRegion->pFree = pTrampoline; +} + +static BOOL detour_is_region_empty(PDETOUR_REGION pRegion) +{ + // Stop if the region isn't a region (this would be bad). + if (pRegion->dwSignature != DETOUR_REGION_SIGNATURE) { + return FALSE; + } + + PBYTE pbRegionBeg = (PBYTE)pRegion; + PBYTE pbRegionLim = pbRegionBeg + DETOUR_REGION_SIZE; + + // Stop if any of the trampolines aren't free. + PDETOUR_TRAMPOLINE pTrampoline = ((PDETOUR_TRAMPOLINE)pRegion) + 1; + for (int i = 0; i < DETOUR_TRAMPOLINES_PER_REGION; i++) { + if (pTrampoline[i].pbRemain != NULL && + (pTrampoline[i].pbRemain < pbRegionBeg || + pTrampoline[i].pbRemain >= pbRegionLim)) { + return FALSE; + } + } + + // OK, the region is empty. + return TRUE; +} + +static void detour_free_unused_trampoline_regions() +{ + PDETOUR_REGION *ppRegionBase = &s_pRegions; + PDETOUR_REGION pRegion = s_pRegions; + + while (pRegion != NULL) { + if (detour_is_region_empty(pRegion)) { + *ppRegionBase = pRegion->pNext; + + VirtualFree(pRegion, 0, MEM_RELEASE); + s_pRegion = NULL; + } + else { + ppRegionBase = &pRegion->pNext; + } + pRegion = *ppRegionBase; + } +} + +///////////////////////////////////////////////////////// Transaction Structs. +// +struct DetourThread +{ + DetourThread * pNext; + HANDLE hThread; +}; + +struct DetourOperation +{ + DetourOperation * pNext; + BOOL fIsRemove; + PBYTE * ppbPointer; + PBYTE pbTarget; + PDETOUR_TRAMPOLINE pTrampoline; + ULONG dwPerm; +}; + +static BOOL s_fIgnoreTooSmall = FALSE; +static BOOL s_fRetainRegions = FALSE; + +static LONG s_nPendingThreadId = 0; // Thread owning pending transaction. +static LONG s_nPendingError = NO_ERROR; +static PVOID * s_ppPendingError = NULL; +static DetourThread * s_pPendingThreads = NULL; +static DetourOperation * s_pPendingOperations = NULL; + +////////////////////////////////////////////////////////////////////////////// +// +PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer, + _Out_opt_ PVOID *ppGlobals) +{ + return detour_skip_jmp((PBYTE)pPointer, ppGlobals); +} + +//////////////////////////////////////////////////////////// Transaction APIs. +// +BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore) +{ + BOOL fPrevious = s_fIgnoreTooSmall; + s_fIgnoreTooSmall = fIgnore; + return fPrevious; +} + +BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain) +{ + BOOL fPrevious = s_fRetainRegions; + s_fRetainRegions = fRetain; + return fPrevious; +} + +PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound) +{ + PVOID pPrevious = s_pSystemRegionLowerBound; + s_pSystemRegionLowerBound = pSystemRegionLowerBound; + return pPrevious; +} + +PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound) +{ + PVOID pPrevious = s_pSystemRegionUpperBound; + s_pSystemRegionUpperBound = pSystemRegionUpperBound; + return pPrevious; +} + +LONG WINAPI DetourTransactionBegin() +{ + // Only one transaction is allowed at a time. +_Benign_race_begin_ + if (s_nPendingThreadId != 0) { + return ERROR_INVALID_OPERATION; + } +_Benign_race_end_ + + // Make sure only one thread can start a transaction. + if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) { + return ERROR_INVALID_OPERATION; + } + + s_pPendingOperations = NULL; + s_pPendingThreads = NULL; + s_ppPendingError = NULL; + + // Make sure the trampoline pages are writable. + s_nPendingError = detour_writable_trampoline_regions(); + + return s_nPendingError; +} + +LONG WINAPI DetourTransactionAbort() +{ + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + return ERROR_INVALID_OPERATION; + } + + // Restore all of the page permissions. + for (DetourOperation *o = s_pPendingOperations; o != NULL;) { + // We don't care if this fails, because the code is still accessible. + DWORD dwOld; + VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore, + o->dwPerm, &dwOld); + + if (!o->fIsRemove) { + if (o->pTrampoline) { + detour_free_trampoline(o->pTrampoline); + o->pTrampoline = NULL; + } + } + + DetourOperation *n = o->pNext; + delete o; + o = n; + } + s_pPendingOperations = NULL; + + // Make sure the trampoline pages are no longer writable. + detour_runnable_trampoline_regions(); + + // Resume any suspended threads. + for (DetourThread *t = s_pPendingThreads; t != NULL;) { + // There is nothing we can do if this fails. + ResumeThread(t->hThread); + + DetourThread *n = t->pNext; + delete t; + t = n; + } + s_pPendingThreads = NULL; + s_nPendingThreadId = 0; + + return NO_ERROR; +} + +LONG WINAPI DetourTransactionCommit() +{ + return DetourTransactionCommitEx(NULL); +} + +static BYTE detour_align_from_trampoline(PDETOUR_TRAMPOLINE pTrampoline, BYTE obTrampoline) +{ + for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) { + if (pTrampoline->rAlign[n].obTrampoline == obTrampoline) { + return pTrampoline->rAlign[n].obTarget; + } + } + return 0; +} + +static LONG detour_align_from_target(PDETOUR_TRAMPOLINE pTrampoline, LONG obTarget) +{ + for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) { + if (pTrampoline->rAlign[n].obTarget == obTarget) { + return pTrampoline->rAlign[n].obTrampoline; + } + } + return 0; +} + +LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer) +{ + if (pppFailedPointer != NULL) { + // Used to get the last error. + *pppFailedPointer = s_ppPendingError; + } + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + return ERROR_INVALID_OPERATION; + } + + // If any of the pending operations failed, then we abort the whole transaction. + if (s_nPendingError != NO_ERROR) { + DETOUR_BREAK(); + DetourTransactionAbort(); + return s_nPendingError; + } + + // Common variables. + DetourOperation *o; + DetourThread *t; + BOOL freed = FALSE; + + // Insert or remove each of the detours. + for (o = s_pPendingOperations; o != NULL; o = o->pNext) { + if (o->fIsRemove) { + CopyMemory(o->pbTarget, + o->pTrampoline->rbRestore, + o->pTrampoline->cbRestore); +#ifdef DETOURS_IA64 + *o->ppbPointer = (PBYTE)o->pTrampoline->ppldTarget; +#endif // DETOURS_IA64 + +#ifdef DETOURS_X86 + *o->ppbPointer = o->pbTarget; +#endif // DETOURS_X86 + +#ifdef DETOURS_X64 + *o->ppbPointer = o->pbTarget; +#endif // DETOURS_X64 + +#ifdef DETOURS_ARM + *o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pbTarget); +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + *o->ppbPointer = o->pbTarget; +#endif // DETOURS_ARM + } + else { + DETOUR_TRACE(("detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbRestore=%d\n", + o->pTrampoline, + o->pTrampoline->pbRemain, + o->pTrampoline->pbDetour, + o->pTrampoline->cbRestore)); + + DETOUR_TRACE(("detours: pbTarget=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x [before]\n", + o->pbTarget, + o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3], + o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7], + o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11])); + +#ifdef DETOURS_IA64 + ((DETOUR_IA64_BUNDLE*)o->pbTarget) + ->SetBrl((UINT64)&o->pTrampoline->bAllocFrame); + *o->ppbPointer = (PBYTE)&o->pTrampoline->pldTrampoline; +#endif // DETOURS_IA64 + +#ifdef DETOURS_X64 + detour_gen_jmp_indirect(o->pTrampoline->rbCodeIn, &o->pTrampoline->pbDetour); + PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->rbCodeIn); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = o->pTrampoline->rbCode; + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_X64 + +#ifdef DETOURS_X86 + PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbDetour); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = o->pTrampoline->rbCode; + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_X86 + +#ifdef DETOURS_ARM + PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->rbCode); + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour); + pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain); + *o->ppbPointer = o->pTrampoline->rbCode; + UNREFERENCED_PARAMETER(pbCode); +#endif // DETOURS_ARM64 + + DETOUR_TRACE(("detours: pbTarget=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x [after]\n", + o->pbTarget, + o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3], + o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7], + o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11])); + + DETOUR_TRACE(("detours: pbTramp =%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + o->pTrampoline, + o->pTrampoline->rbCode[0], o->pTrampoline->rbCode[1], + o->pTrampoline->rbCode[2], o->pTrampoline->rbCode[3], + o->pTrampoline->rbCode[4], o->pTrampoline->rbCode[5], + o->pTrampoline->rbCode[6], o->pTrampoline->rbCode[7], + o->pTrampoline->rbCode[8], o->pTrampoline->rbCode[9], + o->pTrampoline->rbCode[10], o->pTrampoline->rbCode[11])); + +#ifdef DETOURS_IA64 + DETOUR_TRACE(("\n")); + DETOUR_TRACE(("detours: &pldTrampoline =%p\n", + &o->pTrampoline->pldTrampoline)); + DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n", + &o->pTrampoline->bMovlTargetGp, + o->pTrampoline->bMovlTargetGp.GetMovlGp())); + DETOUR_TRACE(("detours: &rbCode =%p [%p]\n", + &o->pTrampoline->rbCode, + ((DETOUR_IA64_BUNDLE&)o->pTrampoline->rbCode).GetBrlTarget())); + DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n", + &o->pTrampoline->bBrlRemainEip, + o->pTrampoline->bBrlRemainEip.GetBrlTarget())); + DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n", + &o->pTrampoline->bMovlDetourGp, + o->pTrampoline->bMovlDetourGp.GetMovlGp())); + DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n", + &o->pTrampoline->bCallDetour, + o->pTrampoline->bCallDetour.GetBrlTarget())); + DETOUR_TRACE(("detours: pldDetour =%p [%p]\n", + o->pTrampoline->ppldDetour->EntryPoint, + o->pTrampoline->ppldDetour->GlobalPointer)); + DETOUR_TRACE(("detours: pldTarget =%p [%p]\n", + o->pTrampoline->ppldTarget->EntryPoint, + o->pTrampoline->ppldTarget->GlobalPointer)); + DETOUR_TRACE(("detours: pbRemain =%p\n", + o->pTrampoline->pbRemain)); + DETOUR_TRACE(("detours: pbDetour =%p\n", + o->pTrampoline->pbDetour)); + DETOUR_TRACE(("\n")); +#endif // DETOURS_IA64 + } + } + + // Update any suspended threads. + for (t = s_pPendingThreads; t != NULL; t = t->pNext) { + CONTEXT cxt; + cxt.ContextFlags = CONTEXT_CONTROL; + +#undef DETOURS_EIP + +#ifdef DETOURS_X86 +#define DETOURS_EIP Eip +#endif // DETOURS_X86 + +#ifdef DETOURS_X64 +#define DETOURS_EIP Rip +#endif // DETOURS_X64 + +#ifdef DETOURS_IA64 +#define DETOURS_EIP StIIP +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM +#define DETOURS_EIP Pc +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 +#define DETOURS_EIP Pc +#endif // DETOURS_ARM64 + +typedef ULONG_PTR DETOURS_EIP_TYPE; + + if (GetThreadContext(t->hThread, &cxt)) { + for (o = s_pPendingOperations; o != NULL; o = o->pNext) { + if (o->fIsRemove) { + if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline && + cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pTrampoline + + sizeof(o->pTrampoline)) + ) { + + cxt.DETOURS_EIP = (DETOURS_EIP_TYPE) + ((ULONG_PTR)o->pbTarget + + detour_align_from_trampoline(o->pTrampoline, + (BYTE)(cxt.DETOURS_EIP + - (DETOURS_EIP_TYPE)(ULONG_PTR) + o->pTrampoline))); + + SetThreadContext(t->hThread, &cxt); + } + } + else { + if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget && + cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pbTarget + + o->pTrampoline->cbRestore) + ) { + + cxt.DETOURS_EIP = (DETOURS_EIP_TYPE) + ((ULONG_PTR)o->pTrampoline + + detour_align_from_target(o->pTrampoline, + (BYTE)(cxt.DETOURS_EIP + - (DETOURS_EIP_TYPE)(ULONG_PTR) + o->pbTarget))); + + SetThreadContext(t->hThread, &cxt); + } + } + } + } +#undef DETOURS_EIP + } + + // Restore all of the page permissions and flush the icache. + HANDLE hProcess = GetCurrentProcess(); + for (o = s_pPendingOperations; o != NULL;) { + // We don't care if this fails, because the code is still accessible. + DWORD dwOld; + VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore, o->dwPerm, &dwOld); + FlushInstructionCache(hProcess, o->pbTarget, o->pTrampoline->cbRestore); + + if (o->fIsRemove && o->pTrampoline) { + detour_free_trampoline(o->pTrampoline); + o->pTrampoline = NULL; + freed = true; + } + + DetourOperation *n = o->pNext; + delete o; + o = n; + } + s_pPendingOperations = NULL; + + // Free any trampoline regions that are now unused. + if (freed && !s_fRetainRegions) { + detour_free_unused_trampoline_regions(); + } + + // Make sure the trampoline pages are no longer writable. + detour_runnable_trampoline_regions(); + + // Resume any suspended threads. + for (t = s_pPendingThreads; t != NULL;) { + // There is nothing we can do if this fails. + ResumeThread(t->hThread); + + DetourThread *n = t->pNext; + delete t; + t = n; + } + s_pPendingThreads = NULL; + s_nPendingThreadId = 0; + + if (pppFailedPointer != NULL) { + *pppFailedPointer = s_ppPendingError; + } + + return s_nPendingError; +} + +LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread) +{ + LONG error; + + // If any of the pending operations failed, then we don't need to do this. + if (s_nPendingError != NO_ERROR) { + return s_nPendingError; + } + + // Silently (and safely) drop any attempt to suspend our own thread. + if (hThread == GetCurrentThread()) { + return NO_ERROR; + } + + DetourThread *t = new NOTHROW DetourThread; + if (t == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + fail: + if (t != NULL) { + delete t; + t = NULL; + } + s_nPendingError = error; + s_ppPendingError = NULL; + DETOUR_BREAK(); + return error; + } + + if (SuspendThread(hThread) == (DWORD)-1) { + error = GetLastError(); + DETOUR_BREAK(); + goto fail; + } + + t->hThread = hThread; + t->pNext = s_pPendingThreads; + s_pPendingThreads = t; + + return NO_ERROR; +} + +///////////////////////////////////////////////////////////// Transacted APIs. +// +LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour) +{ + return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL); +} + +LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour, + _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline, + _Out_opt_ PVOID *ppRealTarget, + _Out_opt_ PVOID *ppRealDetour) +{ + LONG error = NO_ERROR; + + if (ppRealTrampoline != NULL) { + *ppRealTrampoline = NULL; + } + if (ppRealTarget != NULL) { + *ppRealTarget = NULL; + } + if (ppRealDetour != NULL) { + *ppRealDetour = NULL; + } + if (pDetour == NULL) { + DETOUR_TRACE(("empty detour\n")); + return ERROR_INVALID_PARAMETER; + } + + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + DETOUR_TRACE(("transaction conflict with thread id=%d\n", s_nPendingThreadId)); + return ERROR_INVALID_OPERATION; + } + + // If any of the pending operations failed, then we don't need to do this. + if (s_nPendingError != NO_ERROR) { + DETOUR_TRACE(("pending transaction error=%d\n", s_nPendingError)); + return s_nPendingError; + } + + if (ppPointer == NULL) { + DETOUR_TRACE(("ppPointer is null\n")); + return ERROR_INVALID_HANDLE; + } + if (*ppPointer == NULL) { + error = ERROR_INVALID_HANDLE; + s_nPendingError = error; + s_ppPendingError = ppPointer; + DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer)); + DETOUR_BREAK(); + return error; + } + + PBYTE pbTarget = (PBYTE)*ppPointer; + PDETOUR_TRAMPOLINE pTrampoline = NULL; + DetourOperation *o = NULL; + +#ifdef DETOURS_IA64 + PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour; + PPLABEL_DESCRIPTOR ppldTarget = (PPLABEL_DESCRIPTOR)pbTarget; + PVOID pDetourGlobals = NULL; + PVOID pTargetGlobals = NULL; + + pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals); + pbTarget = (PBYTE)DetourCodeFromPointer(ppldTarget, &pTargetGlobals); + DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n", + ppldDetour, pDetour, pDetourGlobals)); + DETOUR_TRACE((" ppldTarget=%p, code=%p [gp=%p]\n", + ppldTarget, pbTarget, pTargetGlobals)); +#else // DETOURS_IA64 + pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL); + pDetour = DetourCodeFromPointer(pDetour, NULL); +#endif // !DETOURS_IA64 + + // Don't follow a jump if its destination is the target function. + // This happens when the detour does nothing other than call the target. + if (pDetour == (PVOID)pbTarget) { + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + if (ppRealTarget != NULL) { + *ppRealTarget = pbTarget; + } + if (ppRealDetour != NULL) { + *ppRealDetour = pDetour; + } + + o = new NOTHROW DetourOperation; + if (o == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + fail: + s_nPendingError = error; + DETOUR_BREAK(); + stop: + if (pTrampoline != NULL) { + detour_free_trampoline(pTrampoline); + pTrampoline = NULL; + if (ppRealTrampoline != NULL) { + *ppRealTrampoline = NULL; + } + } + if (o != NULL) { + delete o; + o = NULL; + } + s_ppPendingError = ppPointer; + return error; + } + + pTrampoline = detour_alloc_trampoline(pbTarget); + if (pTrampoline == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + DETOUR_BREAK(); + goto fail; + } + + if (ppRealTrampoline != NULL) { + *ppRealTrampoline = pTrampoline; + } + + DETOUR_TRACE(("detours: pbTramp=%p, pDetour=%p\n", pTrampoline, pDetour)); + + memset(pTrampoline->rAlign, 0, sizeof(pTrampoline->rAlign)); + + // Determine the number of movable target instructions. + PBYTE pbSrc = pbTarget; + PBYTE pbTrampoline = pTrampoline->rbCode; +#ifdef DETOURS_IA64 + PBYTE pbPool = (PBYTE)(&pTrampoline->bBranchIslands + 1); +#else + PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode); +#endif + ULONG cbTarget = 0; + ULONG cbJump = SIZE_OF_JMP; + ULONG nAlign = 0; + +#ifdef DETOURS_ARM + // On ARM, we need an extra instruction when the function isn't 32-bit aligned. + // Check if the existing code is another detour (or at least a similar + // "ldr pc, [PC+0]" jump. + if ((ULONG)pbTarget & 2) { + cbJump += 2; + + ULONG op = fetch_thumb_opcode(pbSrc); + if (op == 0xbf00) { + op = fetch_thumb_opcode(pbSrc + 2); + if (op == 0xf8dff000) { // LDR PC,[PC] + *((PUSHORT&)pbTrampoline)++ = *((PUSHORT&)pbSrc)++; + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + cbTarget = (LONG)(pbSrc - pbTarget); + // We will fall through the "while" because cbTarget is now >= cbJump. + } + } + } + else { + ULONG op = fetch_thumb_opcode(pbSrc); + if (op == 0xf8dff000) { // LDR PC,[PC] + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++; + cbTarget = (LONG)(pbSrc - pbTarget); + // We will fall through the "while" because cbTarget is now >= cbJump. + } + } +#endif + + while (cbTarget < cbJump) { + PBYTE pbOp = pbSrc; + LONG lExtra = 0; + + DETOUR_TRACE((" DetourCopyInstruction(%p,%p)\n", + pbTrampoline, pbSrc)); + pbSrc = (PBYTE) + DetourCopyInstruction(pbTrampoline, (PVOID*)&pbPool, pbSrc, NULL, &lExtra); + DETOUR_TRACE((" DetourCopyInstruction() = %p (%d bytes)\n", + pbSrc, (int)(pbSrc - pbOp))); + pbTrampoline += (pbSrc - pbOp) + lExtra; + cbTarget = (LONG)(pbSrc - pbTarget); + pTrampoline->rAlign[nAlign].obTarget = cbTarget; + pTrampoline->rAlign[nAlign].obTrampoline = pbTrampoline - pTrampoline->rbCode; + nAlign++; + + if (nAlign >= ARRAYSIZE(pTrampoline->rAlign)) { + break; + } + + if (detour_does_code_end_function(pbOp)) { + break; + } + } + + // Consume, but don't duplicate padding if it is needed and available. + while (cbTarget < cbJump) { + LONG cFiller = detour_is_code_filler(pbSrc); + if (cFiller == 0) { + break; + } + + pbSrc += cFiller; + cbTarget = (LONG)(pbSrc - pbTarget); + } + +#if DETOUR_DEBUG + { + DETOUR_TRACE((" detours: rAlign [")); + LONG n = 0; + for (n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) { + if (pTrampoline->rAlign[n].obTarget == 0 && + pTrampoline->rAlign[n].obTrampoline == 0) { + break; + } + DETOUR_TRACE((" %d/%d", + pTrampoline->rAlign[n].obTarget, + pTrampoline->rAlign[n].obTrampoline + )); + + } + DETOUR_TRACE((" ]\n")); + } +#endif + + if (cbTarget < cbJump || nAlign > ARRAYSIZE(pTrampoline->rAlign)) { + // Too few instructions. + + error = ERROR_INVALID_BLOCK; + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + if (pbTrampoline > pbPool) { + __debugbreak(); + } + + pTrampoline->cbCode = (BYTE)(pbTrampoline - pTrampoline->rbCode); + pTrampoline->cbRestore = (BYTE)cbTarget; + CopyMemory(pTrampoline->rbRestore, pbTarget, cbTarget); + +#if !defined(DETOURS_IA64) + if (cbTarget > sizeof(pTrampoline->rbCode) - cbJump) { + // Too many instructions. + error = ERROR_INVALID_HANDLE; + DETOUR_BREAK(); + goto fail; + } +#endif // !DETOURS_IA64 + + pTrampoline->pbRemain = pbTarget + cbTarget; + pTrampoline->pbDetour = (PBYTE)pDetour; + +#ifdef DETOURS_IA64 + pTrampoline->ppldDetour = ppldDetour; + pTrampoline->ppldTarget = ppldTarget; + pTrampoline->pldTrampoline.EntryPoint = (UINT64)&pTrampoline->bMovlTargetGp; + pTrampoline->pldTrampoline.GlobalPointer = (UINT64)pDetourGlobals; + + ((DETOUR_IA64_BUNDLE *)pTrampoline->rbCode)->SetStop(); + + pTrampoline->bMovlTargetGp.SetMovlGp((UINT64)pTargetGlobals); + pTrampoline->bBrlRemainEip.SetBrl((UINT64)pTrampoline->pbRemain); + + // Alloc frame: alloc r41=ar.pfs,11,0,8,0; mov r40=rp + pTrampoline->bAllocFrame.wide[0] = 0x00000580164d480c; + pTrampoline->bAllocFrame.wide[1] = 0x00c4000500000200; + // save r36, r37, r38. + pTrampoline->bSave37to39.wide[0] = 0x031021004e019001; + pTrampoline->bSave37to39.wide[1] = 0x8401280600420098; + // save r34,r35,r36: adds r47=0,r36; adds r46=0,r35; adds r45=0,r34 + pTrampoline->bSave34to36.wide[0] = 0x02e0210048017800; + pTrampoline->bSave34to36.wide[1] = 0x84011005a042008c; + // save gp,r32,r33" adds r44=0,r33; adds r43=0,r32; adds r42=0,gp ;; + pTrampoline->bSaveGPto33.wide[0] = 0x02b0210042016001; + pTrampoline->bSaveGPto33.wide[1] = 0x8400080540420080; + // set detour GP. + pTrampoline->bMovlDetourGp.SetMovlGp((UINT64)pDetourGlobals); + // call detour: brl.call.sptk.few rp=detour ;; + pTrampoline->bCallDetour.wide[0] = 0x0000000100000005; + pTrampoline->bCallDetour.wide[1] = 0xd000001000000000; + pTrampoline->bCallDetour.SetBrlTarget((UINT64)pDetour); + // pop frame & gp: adds gp=0,r42; mov rp=r40,+0;; mov.i ar.pfs=r41 + pTrampoline->bPopFrameGp.wide[0] = 0x4000210054000802; + pTrampoline->bPopFrameGp.wide[1] = 0x00aa029000038005; + // return to caller: br.ret.sptk.many rp ;; + pTrampoline->bReturn.wide[0] = 0x0000000100000019; + pTrampoline->bReturn.wide[1] = 0x0084000880000200; + + DETOUR_TRACE(("detours: &bMovlTargetGp=%p\n", &pTrampoline->bMovlTargetGp)); + DETOUR_TRACE(("detours: &bMovlDetourGp=%p\n", &pTrampoline->bMovlDetourGp)); +#endif // DETOURS_IA64 + + pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode; +#ifdef DETOURS_X64 + pbTrampoline = detour_gen_jmp_indirect(pbTrampoline, &pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_X64 + +#ifdef DETOURS_X86 + pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_X86 + +#ifdef DETOURS_ARM + pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_ARM + +#ifdef DETOURS_ARM64 + pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain); + pbTrampoline = detour_gen_brk(pbTrampoline, pbPool); +#endif // DETOURS_ARM64 + + (void)pbTrampoline; + + DWORD dwOld = 0; + if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) { + error = GetLastError(); + DETOUR_BREAK(); + goto fail; + } + + DETOUR_TRACE(("detours: pbTarget=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + pbTarget, + pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3], + pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7], + pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11])); + DETOUR_TRACE(("detours: pbTramp =%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + pTrampoline, + pTrampoline->rbCode[0], pTrampoline->rbCode[1], + pTrampoline->rbCode[2], pTrampoline->rbCode[3], + pTrampoline->rbCode[4], pTrampoline->rbCode[5], + pTrampoline->rbCode[6], pTrampoline->rbCode[7], + pTrampoline->rbCode[8], pTrampoline->rbCode[9], + pTrampoline->rbCode[10], pTrampoline->rbCode[11])); + + o->fIsRemove = FALSE; + o->ppbPointer = (PBYTE*)ppPointer; + o->pTrampoline = pTrampoline; + o->pbTarget = pbTarget; + o->dwPerm = dwOld; + o->pNext = s_pPendingOperations; + s_pPendingOperations = o; + + return NO_ERROR; +} + +LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer, + _In_ PVOID pDetour) +{ + LONG error = NO_ERROR; + + if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) { + return ERROR_INVALID_OPERATION; + } + + // If any of the pending operations failed, then we don't need to do this. + if (s_nPendingError != NO_ERROR) { + return s_nPendingError; + } + + if (pDetour == NULL) { + return ERROR_INVALID_PARAMETER; + } + if (ppPointer == NULL) { + return ERROR_INVALID_HANDLE; + } + if (*ppPointer == NULL) { + error = ERROR_INVALID_HANDLE; + s_nPendingError = error; + s_ppPendingError = ppPointer; + DETOUR_BREAK(); + return error; + } + + DetourOperation *o = new NOTHROW DetourOperation; + if (o == NULL) { + error = ERROR_NOT_ENOUGH_MEMORY; + fail: + s_nPendingError = error; + DETOUR_BREAK(); + stop: + if (o != NULL) { + delete o; + o = NULL; + } + s_ppPendingError = ppPointer; + return error; + } + + +#ifdef DETOURS_IA64 + PPLABEL_DESCRIPTOR ppldTrampo = (PPLABEL_DESCRIPTOR)*ppPointer; + PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour; + PVOID pDetourGlobals = NULL; + PVOID pTrampoGlobals = NULL; + + pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals); + PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE) + DetourCodeFromPointer(ppldTrampo, &pTrampoGlobals); + DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n", + ppldDetour, pDetour, pDetourGlobals)); + DETOUR_TRACE((" ppldTrampo=%p, code=%p [gp=%p]\n", + ppldTrampo, pTrampoline, pTrampoGlobals)); + + + DETOUR_TRACE(("\n")); + DETOUR_TRACE(("detours: &pldTrampoline =%p\n", + &pTrampoline->pldTrampoline)); + DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n", + &pTrampoline->bMovlTargetGp, + pTrampoline->bMovlTargetGp.GetMovlGp())); + DETOUR_TRACE(("detours: &rbCode =%p [%p]\n", + &pTrampoline->rbCode, + ((DETOUR_IA64_BUNDLE&)pTrampoline->rbCode).GetBrlTarget())); + DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n", + &pTrampoline->bBrlRemainEip, + pTrampoline->bBrlRemainEip.GetBrlTarget())); + DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n", + &pTrampoline->bMovlDetourGp, + pTrampoline->bMovlDetourGp.GetMovlGp())); + DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n", + &pTrampoline->bCallDetour, + pTrampoline->bCallDetour.GetBrlTarget())); + DETOUR_TRACE(("detours: pldDetour =%p [%p]\n", + pTrampoline->ppldDetour->EntryPoint, + pTrampoline->ppldDetour->GlobalPointer)); + DETOUR_TRACE(("detours: pldTarget =%p [%p]\n", + pTrampoline->ppldTarget->EntryPoint, + pTrampoline->ppldTarget->GlobalPointer)); + DETOUR_TRACE(("detours: pbRemain =%p\n", + pTrampoline->pbRemain)); + DETOUR_TRACE(("detours: pbDetour =%p\n", + pTrampoline->pbDetour)); + DETOUR_TRACE(("\n")); +#else // !DETOURS_IA64 + PDETOUR_TRAMPOLINE pTrampoline = + (PDETOUR_TRAMPOLINE)DetourCodeFromPointer(*ppPointer, NULL); + pDetour = DetourCodeFromPointer(pDetour, NULL); +#endif // !DETOURS_IA64 + + ////////////////////////////////////// Verify that Trampoline is in place. + // + LONG cbTarget = pTrampoline->cbRestore; + PBYTE pbTarget = pTrampoline->pbRemain - cbTarget; + if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) { + error = ERROR_INVALID_BLOCK; + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + if (pTrampoline->pbDetour != pDetour) { + error = ERROR_INVALID_BLOCK; + if (s_fIgnoreTooSmall) { + goto stop; + } + else { + DETOUR_BREAK(); + goto fail; + } + } + + DWORD dwOld = 0; + if (!VirtualProtect(pbTarget, cbTarget, + PAGE_EXECUTE_READWRITE, &dwOld)) { + error = GetLastError(); + DETOUR_BREAK(); + goto fail; + } + + o->fIsRemove = TRUE; + o->ppbPointer = (PBYTE*)ppPointer; + o->pTrampoline = pTrampoline; + o->pbTarget = pbTarget; + o->dwPerm = dwOld; + o->pNext = s_pPendingOperations; + s_pPendingOperations = o; + + return NO_ERROR; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Helpers for manipulating page protection. +// + +// For reference: +// PAGE_NOACCESS 0x01 +// PAGE_READONLY 0x02 +// PAGE_READWRITE 0x04 +// PAGE_WRITECOPY 0x08 +// PAGE_EXECUTE 0x10 +// PAGE_EXECUTE_READ 0x20 +// PAGE_EXECUTE_READWRITE 0x40 +// PAGE_EXECUTE_WRITECOPY 0x80 +// PAGE_GUARD ... +// PAGE_NOCACHE ... +// PAGE_WRITECOMBINE ... + +#define DETOUR_PAGE_EXECUTE_ALL (PAGE_EXECUTE | \ + PAGE_EXECUTE_READ | \ + PAGE_EXECUTE_READWRITE | \ + PAGE_EXECUTE_WRITECOPY) + +#define DETOUR_PAGE_NO_EXECUTE_ALL (PAGE_NOACCESS | \ + PAGE_READONLY | \ + PAGE_READWRITE | \ + PAGE_WRITECOPY) + +#define DETOUR_PAGE_ATTRIBUTES (~(DETOUR_PAGE_EXECUTE_ALL | DETOUR_PAGE_NO_EXECUTE_ALL)) + +C_ASSERT((DETOUR_PAGE_NO_EXECUTE_ALL << 4) == DETOUR_PAGE_EXECUTE_ALL); + +static DWORD DetourPageProtectAdjustExecute(_In_ DWORD dwOldProtect, + _In_ DWORD dwNewProtect) +// Copy EXECUTE from dwOldProtect to dwNewProtect. +{ + bool const fOldExecute = ((dwOldProtect & DETOUR_PAGE_EXECUTE_ALL) != 0); + bool const fNewExecute = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) != 0); + + if (fOldExecute && !fNewExecute) { + dwNewProtect = ((dwNewProtect & DETOUR_PAGE_NO_EXECUTE_ALL) << 4) + | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES); + } + else if (!fOldExecute && fNewExecute) { + dwNewProtect = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) >> 4) + | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES); + } + return dwNewProtect; +} + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess, + _In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect) +// Some systems do not allow executability of a page to change. This function applies +// dwNewProtect to [pAddress, nSize), but preserving the previous executability. +// This function is meant to be a drop-in replacement for some uses of VirtualProtectEx. +// When "restoring" page protection, there is no need to use this function. +{ + MEMORY_BASIC_INFORMATION mbi; + + // Query to get existing execute access. + + ZeroMemory(&mbi, sizeof(mbi)); + + if (VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) == 0) { + return FALSE; + } + return VirtualProtectEx(hProcess, pAddress, nSize, + DetourPageProtectAdjustExecute(mbi.Protect, dwNewProtect), + pdwOldProtect); +} + +_Success_(return != FALSE) +BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress, + _In_ SIZE_T nSize, + _In_ DWORD dwNewProtect, + _Out_ PDWORD pdwOldProtect) +{ + return DetourVirtualProtectSameExecuteEx(GetCurrentProcess(), + pAddress, nSize, dwNewProtect, pdwOldProtect); +} + +// End of File diff --git a/subprojects/detours/src/disasm.cpp b/subprojects/detours/src/disasm.cpp new file mode 100644 index 0000000..f002ff4 --- /dev/null +++ b/subprojects/detours/src/disasm.cpp @@ -0,0 +1,4236 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Detours Disassembler (disasm.cpp of detours.lib) +// +// Microsoft Research Detours Package, Version 4.0.1 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#if _MSC_VER >= 1900 +#pragma warning(push) +#pragma warning(disable:4091) // empty typedef +#endif + +#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1 +#include +#include + +// #define DETOUR_DEBUG 1 +#define DETOURS_INTERNAL + +#include "detours.h" + +#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH +#error detours.h version mismatch +#endif + +#if _MSC_VER >= 1900 +#pragma warning(pop) +#endif + +#undef ASSERT +#define ASSERT(x) + +////////////////////////////////////////////////////////////////////////////// +// +// Special macros to handle the case when we are building disassembler for +// offline processing. +// + + +#if defined(DETOURS_X86_OFFLINE_LIBRARY) \ + || defined(DETOURS_X64_OFFLINE_LIBRARY) \ + || defined(DETOURS_ARM_OFFLINE_LIBRARY) \ + || defined(DETOURS_ARM64_OFFLINE_LIBRARY) \ + || defined(DETOURS_IA64_OFFLINE_LIBRARY) + +#undef DETOURS_X64 +#undef DETOURS_X86 +#undef DETOURS_IA64 +#undef DETOURS_ARM +#undef DETOURS_ARM64 + +#if defined(DETOURS_X86_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionX86 +#define DetourSetCodeModule DetourSetCodeModuleX86 +#define CDetourDis CDetourDisX86 +#define DETOURS_X86 + +#elif defined(DETOURS_X64_OFFLINE_LIBRARY) + +#if !defined(DETOURS_64BIT) +// Fix this as/if bugs are discovered. +//#error X64 disassembler can only build for 64-bit. +#endif + +#define DetourCopyInstruction DetourCopyInstructionX64 +#define DetourSetCodeModule DetourSetCodeModuleX64 +#define CDetourDis CDetourDisX64 +#define DETOURS_X64 + +#elif defined(DETOURS_ARM_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionARM +#define DetourSetCodeModule DetourSetCodeModuleARM +#define CDetourDis CDetourDisARM +#define DETOURS_ARM + +#elif defined(DETOURS_ARM64_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionARM64 +#define DetourSetCodeModule DetourSetCodeModuleARM64 +#define CDetourDis CDetourDisARM64 +#define DETOURS_ARM64 + +#elif defined(DETOURS_IA64_OFFLINE_LIBRARY) + +#define DetourCopyInstruction DetourCopyInstructionIA64 +#define DetourSetCodeModule DetourSetCodeModuleIA64 +#define DETOURS_IA64 + +#else + +#error + +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Function: +// DetourCopyInstruction(PVOID pDst, +// PVOID *ppDstPool +// PVOID pSrc, +// PVOID *ppTarget, +// LONG *plExtra) +// Purpose: +// Copy a single instruction from pSrc to pDst. +// +// Arguments: +// pDst: +// Destination address for the instruction. May be NULL in which +// case DetourCopyInstruction is used to measure an instruction. +// If not NULL then the source instruction is copied to the +// destination instruction and any relative arguments are adjusted. +// ppDstPool: +// Destination address for the end of the constant pool. The +// constant pool works backwards toward pDst. All memory between +// pDst and *ppDstPool must be available for use by this function. +// ppDstPool may be NULL if pDst is NULL. +// pSrc: +// Source address of the instruction. +// ppTarget: +// Out parameter for any target instruction address pointed to by +// the instruction. For example, a branch or a jump insruction has +// a target, but a load or store instruction doesn't. A target is +// another instruction that may be executed as a result of this +// instruction. ppTarget may be NULL. +// plExtra: +// Out parameter for the number of extra bytes needed by the +// instruction to reach the target. For example, lExtra = 3 if the +// instruction had an 8-bit relative offset, but needs a 32-bit +// relative offset. +// +// Returns: +// Returns the address of the next instruction (following in the source) +// instruction. By subtracting pSrc from the return value, the caller +// can determinte the size of the instruction copied. +// +// Comments: +// By following the pTarget, the caller can follow alternate +// instruction streams. However, it is not always possible to determine +// the target based on static analysis. For example, the destination of +// a jump relative to a register cannot be determined from just the +// instruction stream. The output value, pTarget, can have any of the +// following outputs: +// DETOUR_INSTRUCTION_TARGET_NONE: +// The instruction has no targets. +// DETOUR_INSTRUCTION_TARGET_DYNAMIC: +// The instruction has a non-deterministic (dynamic) target. +// (i.e. the jump is to an address held in a register.) +// Address: The instruction has the specified target. +// +// When copying instructions, DetourCopyInstruction insures that any +// targets remain constant. It does so by adjusting any IP relative +// offsets. +// + +#pragma data_seg(".detourd") +#pragma const_seg(".detourc") + +//////////////////////////////////////////////////// X86 and X64 Disassembler. +// +// Includes full support for all x86 chips prior to the Pentium III, and some newer stuff. +// +#if defined(DETOURS_X64) || defined(DETOURS_X86) + +class CDetourDis +{ + public: + CDetourDis(_Out_opt_ PBYTE *ppbTarget, + _Out_opt_ LONG *plExtra); + + PBYTE CopyInstruction(PBYTE pbDst, PBYTE pbSrc); + static BOOL SanityCheckSystem(); + static BOOL SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule); + + public: + struct COPYENTRY; + typedef const COPYENTRY * REFCOPYENTRY; + + typedef PBYTE (CDetourDis::* COPYFUNC)(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + + // nFlagBits flags. + enum { + DYNAMIC = 0x1u, + ADDRESS = 0x2u, + NOENLARGE = 0x4u, + RAX = 0x8u, + }; + + // ModR/M Flags + enum { + SIB = 0x10u, + RIP = 0x20u, + NOTSIB = 0x0fu, + }; + + struct COPYENTRY + { + // Many of these fields are often ignored. See ENTRY_DataIgnored. + ULONG nOpcode : 8; // Opcode (ignored) + ULONG nFixedSize : 4; // Fixed size of opcode + ULONG nFixedSize16 : 4; // Fixed size when 16 bit operand + ULONG nModOffset : 4; // Offset to mod/rm byte (0=none) + ULONG nRelOffset : 4; // Offset to relative target. + ULONG nTargetBack : 4; // Offset back to absolute or rip target + ULONG nFlagBits : 4; // Flags for DYNAMIC, etc. + COPYFUNC pfCopy; // Function pointer. + }; + + protected: + // These macros define common uses of nFixedSize..pfCopy. +#define ENTRY_DataIgnored 0, 0, 0, 0, 0, 0, +#define ENTRY_CopyBytes1 1, 1, 0, 0, 0, 0, &CDetourDis::CopyBytes +#ifdef DETOURS_X64 +#define ENTRY_CopyBytes1Address 9, 5, 0, 0, 0, ADDRESS, &CDetourDis::CopyBytes +#else +#define ENTRY_CopyBytes1Address 5, 3, 0, 0, 0, ADDRESS, &CDetourDis::CopyBytes +#endif +#define ENTRY_CopyBytes1Dynamic 1, 1, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes2 2, 2, 0, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes2Jump ENTRY_DataIgnored &CDetourDis::CopyBytesJump +#define ENTRY_CopyBytes2CantJump 2, 2, 0, 1, 0, NOENLARGE, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes2Dynamic 2, 2, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes3 3, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes3Dynamic 3, 3, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes3Or5 5, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes3Or5Dynamic 5, 3, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes // x86 only +#ifdef DETOURS_X64 +#define ENTRY_CopyBytes3Or5Rax 5, 3, 0, 0, 0, RAX, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes3Or5Target 5, 5, 0, 1, 0, 0, &CDetourDis::CopyBytes +#else +#define ENTRY_CopyBytes3Or5Rax 5, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes3Or5Target 5, 3, 0, 1, 0, 0, &CDetourDis::CopyBytes +#endif +#define ENTRY_CopyBytes4 4, 4, 0, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes5 5, 5, 0, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes5Or7Dynamic 7, 5, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes7 7, 7, 0, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes2Mod 2, 2, 1, 0, 0, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes2ModDynamic 2, 2, 1, 0, 0, DYNAMIC, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes2Mod1 3, 3, 1, 0, 1, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes2ModOperand 6, 4, 1, 0, 4, 0, &CDetourDis::CopyBytes +#define ENTRY_CopyBytes3Mod 3, 3, 2, 0, 0, 0, &CDetourDis::CopyBytes // SSE3 0F 38 opcode modrm +#define ENTRY_CopyBytes3Mod1 4, 4, 2, 0, 1, 0, &CDetourDis::CopyBytes // SSE3 0F 3A opcode modrm .. imm8 +#define ENTRY_CopyBytesPrefix ENTRY_DataIgnored &CDetourDis::CopyBytesPrefix +#define ENTRY_CopyBytesSegment ENTRY_DataIgnored &CDetourDis::CopyBytesSegment +#define ENTRY_CopyBytesRax ENTRY_DataIgnored &CDetourDis::CopyBytesRax +#define ENTRY_CopyF2 ENTRY_DataIgnored &CDetourDis::CopyF2 +#define ENTRY_CopyF3 ENTRY_DataIgnored &CDetourDis::CopyF3 // 32bit x86 only +#define ENTRY_Copy0F ENTRY_DataIgnored &CDetourDis::Copy0F +#define ENTRY_Copy0F78 ENTRY_DataIgnored &CDetourDis::Copy0F78 +#define ENTRY_Copy0F00 ENTRY_DataIgnored &CDetourDis::Copy0F00 // 32bit x86 only +#define ENTRY_Copy0FB8 ENTRY_DataIgnored &CDetourDis::Copy0FB8 // 32bit x86 only +#define ENTRY_Copy66 ENTRY_DataIgnored &CDetourDis::Copy66 +#define ENTRY_Copy67 ENTRY_DataIgnored &CDetourDis::Copy67 +#define ENTRY_CopyF6 ENTRY_DataIgnored &CDetourDis::CopyF6 +#define ENTRY_CopyF7 ENTRY_DataIgnored &CDetourDis::CopyF7 +#define ENTRY_CopyFF ENTRY_DataIgnored &CDetourDis::CopyFF +#define ENTRY_CopyVex2 ENTRY_DataIgnored &CDetourDis::CopyVex2 +#define ENTRY_CopyVex3 ENTRY_DataIgnored &CDetourDis::CopyVex3 +#define ENTRY_Invalid ENTRY_DataIgnored &CDetourDis::Invalid +#define ENTRY_End ENTRY_DataIgnored NULL + + PBYTE CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesSegment(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesRax(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + + PBYTE Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + + PBYTE AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp, + UINT cbTargetOffset, UINT cbTargetSize); + + protected: + PBYTE Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE Copy0F00(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // x86 only sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 + PBYTE Copy0F78(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // vmread, 66/extrq/ib/ib, F2/insertq/ib/ib + PBYTE Copy0FB8(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // jmpe or F3/popcnt + PBYTE Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // x86 only + PBYTE CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyVex2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyVex3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); + PBYTE CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc); + + protected: + static const COPYENTRY s_rceCopyTable[257]; + static const COPYENTRY s_rceCopyTable0F[257]; + static const BYTE s_rbModRm[256]; + static PBYTE s_pbModuleBeg; + static PBYTE s_pbModuleEnd; + static BOOL s_fLimitReferencesToModule; + + protected: + BOOL m_bOperandOverride; + BOOL m_bAddressOverride; + BOOL m_bRaxOverride; // AMD64 only + BOOL m_bVex; + BOOL m_bF2; + BOOL m_bF3; // x86 only + BYTE m_nSegmentOverride; + + PBYTE * m_ppbTarget; + LONG * m_plExtra; + + LONG m_lScratchExtra; + PBYTE m_pbScratchTarget; + BYTE m_rbScratchDst[64]; +}; + +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra) +{ + UNREFERENCED_PARAMETER(ppDstPool); // x86 & x64 don't use a constant pool. + + CDetourDis oDetourDisasm((PBYTE*)ppTarget, plExtra); + return oDetourDisasm.CopyInstruction((PBYTE)pDst, (PBYTE)pSrc); +} + +/////////////////////////////////////////////////////////// Disassembler Code. +// +CDetourDis::CDetourDis(_Out_opt_ PBYTE *ppbTarget, _Out_opt_ LONG *plExtra) +{ + m_bOperandOverride = FALSE; + m_bAddressOverride = FALSE; + m_bRaxOverride = FALSE; + m_bF2 = FALSE; + m_bF3 = FALSE; + m_bVex = FALSE; + + m_ppbTarget = ppbTarget ? ppbTarget : &m_pbScratchTarget; + m_plExtra = plExtra ? plExtra : &m_lScratchExtra; + + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE; + *m_plExtra = 0; +} + +PBYTE CDetourDis::CopyInstruction(PBYTE pbDst, PBYTE pbSrc) +{ + // Configure scratch areas if real areas are not available. + if (NULL == pbDst) { + pbDst = m_rbScratchDst; + } + if (NULL == pbSrc) { + // We can't copy a non-existent instruction. + SetLastError(ERROR_INVALID_DATA); + return NULL; + } + + // Figure out how big the instruction is, do the appropriate copy, + // and figure out what the target of the instruction is if any. + // + REFCOPYENTRY pEntry = &s_rceCopyTable[pbSrc[0]]; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + UINT nBytesFixed; + + ASSERT(!m_bVex || pEntry->nFlagBits == 0); + ASSERT(!m_bVex || pEntry->nFixedSize == pEntry->nFixedSize16); + + UINT const nModOffset = pEntry->nModOffset; + UINT const nFlagBits = pEntry->nFlagBits; + UINT const nFixedSize = pEntry->nFixedSize; + UINT const nFixedSize16 = pEntry->nFixedSize16; + + if (nFlagBits & ADDRESS) { + nBytesFixed = m_bAddressOverride ? nFixedSize16 : nFixedSize; + } +#ifdef DETOURS_X64 + // REX.W trumps 66 + else if (m_bRaxOverride) { + nBytesFixed = nFixedSize + ((nFlagBits & RAX) ? 4 : 0); + } +#endif + else { + nBytesFixed = m_bOperandOverride ? nFixedSize16 : nFixedSize; + } + + UINT nBytes = nBytesFixed; + UINT nRelOffset = pEntry->nRelOffset; + UINT cbTarget = nBytes - nRelOffset; + if (nModOffset > 0) { + ASSERT(nRelOffset == 0); + BYTE const bModRm = pbSrc[nModOffset]; + BYTE const bFlags = s_rbModRm[bModRm]; + + nBytes += bFlags & NOTSIB; + + if (bFlags & SIB) { + BYTE const bSib = pbSrc[nModOffset + 1]; + + if ((bSib & 0x07) == 0x05) { + if ((bModRm & 0xc0) == 0x00) { + nBytes += 4; + } + else if ((bModRm & 0xc0) == 0x40) { + nBytes += 1; + } + else if ((bModRm & 0xc0) == 0x80) { + nBytes += 4; + } + } + cbTarget = nBytes - nRelOffset; + } +#ifdef DETOURS_X64 + else if (bFlags & RIP) { + UINT nTargetBack = pEntry->nTargetBack; + // nTargetBack describes immediate bytes at the end: 1, 2, or 4. + // 2 vs. 4 is selected via 66 operand size override. + ASSERT(nTargetBack == 0 || nTargetBack == 1 || nTargetBack == 4); + if (nTargetBack == 4 && m_bOperandOverride && !m_bRaxOverride) { + nTargetBack = 2; + } + + nRelOffset = nBytes - (4 + nTargetBack); + cbTarget = 4; + } +#endif + } + CopyMemory(pbDst, pbSrc, nBytes); + + if (nRelOffset) { + *m_ppbTarget = AdjustTarget(pbDst, pbSrc, nBytes, nRelOffset, cbTarget); +#ifdef DETOURS_X64 + if (pEntry->nRelOffset == 0) { + // This is a data target, not a code target, so we shouldn't return it. + *m_ppbTarget = NULL; + } +#endif + } + if (nFlagBits & NOENLARGE) { + *m_plExtra = -*m_plExtra; + } + if (nFlagBits & DYNAMIC) { + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + return pbSrc + nBytes; +} + +PBYTE CDetourDis::CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + pbDst[0] = pbSrc[0]; + pEntry = &s_rceCopyTable[pbSrc[1]]; + return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1); +} + +PBYTE CDetourDis::CopyBytesSegment(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + m_nSegmentOverride = pbSrc[0]; + return CopyBytesPrefix(0, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyBytesRax(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ // AMD64 only + if (pbSrc[0] & 0x8) { + m_bRaxOverride = TRUE; + } + return CopyBytesPrefix(0, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pEntry; + + PVOID pvSrcAddr = &pbSrc[1]; + PVOID pvDstAddr = NULL; + LONG_PTR nOldOffset = (LONG_PTR)*(signed char*&)pvSrcAddr; + LONG_PTR nNewOffset = 0; + + *m_ppbTarget = pbSrc + 2 + nOldOffset; + + if (pbSrc[0] == 0xeb) { + pbDst[0] = 0xe9; + pvDstAddr = &pbDst[1]; + nNewOffset = nOldOffset - ((pbDst - pbSrc) + 3); + *(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset; + + *m_plExtra = 3; + return pbSrc + 2; + } + + ASSERT(pbSrc[0] >= 0x70 && pbSrc[0] <= 0x7f); + + pbDst[0] = 0x0f; + pbDst[1] = 0x80 | (pbSrc[0] & 0xf); + pvDstAddr = &pbDst[2]; + nNewOffset = nOldOffset - ((pbDst - pbSrc) + 4); + *(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset; + + *m_plExtra = 4; + return pbSrc + 2; +} + +PBYTE CDetourDis::AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp, + UINT cbTargetOffset, UINT cbTargetSize) +{ + PBYTE pbTarget = NULL; +#if 1 // fault injection to test test code +#if defined(DETOURS_X64) + typedef LONGLONG T; +#else + typedef LONG T; +#endif + T nOldOffset; + T nNewOffset; + PVOID pvTargetAddr = &pbDst[cbTargetOffset]; + + switch (cbTargetSize) { + case 1: + nOldOffset = *(signed char*&)pvTargetAddr; + break; + case 2: + nOldOffset = *(UNALIGNED SHORT*&)pvTargetAddr; + break; + case 4: + nOldOffset = *(UNALIGNED LONG*&)pvTargetAddr; + break; +#if defined(DETOURS_X64) + case 8: + nOldOffset = *(UNALIGNED LONGLONG*&)pvTargetAddr; + break; +#endif + default: + ASSERT(!"cbTargetSize is invalid."); + nOldOffset = 0; + break; + } + + pbTarget = pbSrc + cbOp + nOldOffset; + nNewOffset = nOldOffset - (T)(pbDst - pbSrc); + + switch (cbTargetSize) { + case 1: + *(CHAR*&)pvTargetAddr = (CHAR)nNewOffset; + if (nNewOffset < SCHAR_MIN || nNewOffset > SCHAR_MAX) { + *m_plExtra = sizeof(ULONG) - 1; + } + break; + case 2: + *(UNALIGNED SHORT*&)pvTargetAddr = (SHORT)nNewOffset; + if (nNewOffset < SHRT_MIN || nNewOffset > SHRT_MAX) { + *m_plExtra = sizeof(ULONG) - 2; + } + break; + case 4: + *(UNALIGNED LONG*&)pvTargetAddr = (LONG)nNewOffset; + if (nNewOffset < LONG_MIN || nNewOffset > LONG_MAX) { + *m_plExtra = sizeof(ULONG) - 4; + } + break; +#if defined(DETOURS_X64) + case 8: + *(UNALIGNED LONGLONG*&)pvTargetAddr = nNewOffset; + break; +#endif + } +#ifdef DETOURS_X64 + // When we are only computing size, source and dest can be + // far apart, distance not encodable in 32bits. Ok. + // At least still check the lower 32bits. + + if (pbDst >= m_rbScratchDst && pbDst < (sizeof(m_rbScratchDst) + m_rbScratchDst)) { + ASSERT((((size_t)pbDst + cbOp + nNewOffset) & 0xFFFFFFFF) == (((size_t)pbTarget) & 0xFFFFFFFF)); + } + else +#endif + { + ASSERT(pbDst + cbOp + nNewOffset == pbTarget); + } +#endif + return pbTarget; +} + +PBYTE CDetourDis::Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pbDst; + (void)pEntry; + ASSERT(!"Invalid Instruction"); + return pbSrc + 1; +} + +////////////////////////////////////////////////////// Individual Bytes Codes. +// +PBYTE CDetourDis::Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + pbDst[0] = pbSrc[0]; + pEntry = &s_rceCopyTable0F[pbSrc[1]]; + return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1); +} + +PBYTE CDetourDis::Copy0F78(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + // vmread, 66/extrq, F2/insertq + + static const COPYENTRY vmread = { 0x78, ENTRY_CopyBytes2Mod }; + static const COPYENTRY extrq_insertq = { 0x78, ENTRY_CopyBytes4 }; + + ASSERT(!(m_bF2 && m_bOperandOverride)); + + // For insertq and presumably despite documentation extrq, mode must be 11, not checked. + // insertq/extrq/78 are followed by two immediate bytes, and given mode == 11, mod/rm byte is always one byte, + // and the 0x78 makes 4 bytes (not counting the 66/F2/F which are accounted for elsewhere) + + REFCOPYENTRY const pEntry = ((m_bF2 || m_bOperandOverride) ? &extrq_insertq : &vmread); + + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy0F00(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + // jmpe is 32bit x86 only + // Notice that the sizes are the same either way, but jmpe is marked as "dynamic". + + static const COPYENTRY other = { 0xB8, ENTRY_CopyBytes2Mod }; // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6 invalid/7 + static const COPYENTRY jmpe = { 0xB8, ENTRY_CopyBytes2ModDynamic }; // jmpe/6 x86-on-IA64 syscalls + + REFCOPYENTRY const pEntry = (((6 << 3) == ((7 << 3) & pbSrc[1])) ? &jmpe : &other); + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy0FB8(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +{ + // jmpe is 32bit x86 only + + static const COPYENTRY popcnt = { 0xB8, ENTRY_CopyBytes2Mod }; + static const COPYENTRY jmpe = { 0xB8, ENTRY_CopyBytes3Or5Dynamic }; // jmpe x86-on-IA64 syscalls + REFCOPYENTRY const pEntry = m_bF3 ? &popcnt : &jmpe; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // Operand-size override prefix + m_bOperandOverride = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // Address size override prefix + m_bAddressOverride = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + m_bF2 = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // x86 only + m_bF3 = TRUE; + return CopyBytesPrefix(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pEntry; + + // TEST BYTE /0 + if (0x00 == (0x38 & pbSrc[1])) { // reg(bits 543) of ModR/M == 0 + static const COPYENTRY ce = { 0xf6, ENTRY_CopyBytes2Mod1 }; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); + } + // DIV /6 + // IDIV /7 + // IMUL /5 + // MUL /4 + // NEG /3 + // NOT /2 + + static const COPYENTRY ce = { 0xf6, ENTRY_CopyBytes2Mod }; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ + (void)pEntry; + + // TEST WORD /0 + if (0x00 == (0x38 & pbSrc[1])) { // reg(bits 543) of ModR/M == 0 + static const COPYENTRY ce = { 0xf7, ENTRY_CopyBytes2ModOperand }; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); + } + + // DIV /6 + // IDIV /7 + // IMUL /5 + // MUL /4 + // NEG /3 + // NOT /2 + static const COPYENTRY ce = { 0xf7, ENTRY_CopyBytes2Mod }; + return (this->*ce.pfCopy)(&ce, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc) +{ // INC /0 + // DEC /1 + // CALL /2 + // CALL /3 + // JMP /4 + // JMP /5 + // PUSH /6 + // invalid/7 + (void)pEntry; + + static const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod }; + PBYTE pbOut = (this->*ce.pfCopy)(&ce, pbDst, pbSrc); + + BYTE const b1 = pbSrc[1]; + + if (0x15 == b1 || 0x25 == b1) { // CALL [], JMP [] +#ifdef DETOURS_X64 + // All segments but FS and GS are equivalent. + if (m_nSegmentOverride != 0x64 && m_nSegmentOverride != 0x65) +#else + if (m_nSegmentOverride == 0 || m_nSegmentOverride == 0x2E) +#endif + { +#ifdef DETOURS_X64 + INT32 offset = *(UNALIGNED INT32*)&pbSrc[2]; + PBYTE *ppbTarget = (PBYTE *)(pbSrc + 6 + offset); +#else + PBYTE *ppbTarget = (PBYTE *)(SIZE_T)*(UNALIGNED ULONG*)&pbSrc[2]; +#endif + if (s_fLimitReferencesToModule && + (ppbTarget < (PVOID)s_pbModuleBeg || ppbTarget >= (PVOID)s_pbModuleEnd)) { + + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + else { + // This can access violate on random bytes. Use DetourSetCodeModule. + *m_ppbTarget = *ppbTarget; + } + } + else { + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + } + else if (0x10 == (0x30 & b1) || // CALL /2 or /3 --> reg(bits 543) of ModR/M == 010 or 011 + 0x20 == (0x30 & b1)) { // JMP /4 or /5 --> reg(bits 543) of ModR/M == 100 or 101 + *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + return pbOut; +} + +PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc) +// m is first instead of last in the hopes of pbDst/pbSrc being +// passed along efficiently in the registers they were already in. +{ + static const COPYENTRY ceF38 = { 0x38, ENTRY_CopyBytes2Mod }; + static const COPYENTRY ceF3A = { 0x3A, ENTRY_CopyBytes2Mod1 }; + static const COPYENTRY Invalid = { 0xC4, ENTRY_Invalid }; + + m_bVex = TRUE; + REFCOPYENTRY pEntry; + switch (m) { + default: pEntry = &Invalid; break; + case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]]; break; + case 2: pEntry = &ceF38; break; + case 3: pEntry = &ceF3A; break; + } + + switch (pbSrc[-1] & 3) { // p in last byte + case 0: break; + case 1: m_bOperandOverride = TRUE; break; + case 2: m_bF3 = TRUE; break; + case 3: m_bF2 = TRUE; break; + } + + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); +} + +PBYTE CDetourDis::CopyVex3(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +// 3 byte VEX prefix 0xC4 +{ +#ifdef DETOURS_X86 + const static COPYENTRY ceLES = { 0xC4, ENTRY_CopyBytes2Mod }; + if ((pbSrc[1] & 0xC0) != 0xC0) { + REFCOPYENTRY pEntry = &ceLES; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); + } +#endif + pbDst[0] = pbSrc[0]; + pbDst[1] = pbSrc[1]; + pbDst[2] = pbSrc[2]; +#ifdef DETOURS_X64 + m_bRaxOverride |= !!(pbSrc[2] & 0x80); // w in last byte, see CopyBytesRax +#else + // + // TODO + // + // Usually the VEX.W bit changes the size of a general purpose register and is ignored for 32bit. + // Sometimes it is an opcode extension. + // Look in the Intel manual, in the instruction-by-instruction reference, for ".W1", + // without nearby wording saying it is ignored for 32bit. + // For example: "VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values". + // + // Then, go through each such case and determine if W0 vs. W1 affect the size of the instruction. Probably not. + // Look for the same encoding but with "W1" changed to "W0". + // Here is one such pairing: + // VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values + // + // VEX.DDS.128.66.0F38.W1 98 /r A V/V FMA Multiply packed double-precision floating-point values + // from xmm0 and xmm2/mem, add to xmm1 and + // put result in xmm0. + // VFMADD132PD xmm0, xmm1, xmm2/m128 + // + // VFMADD132PS/VFMADD213PS/VFMADD231PS Fused Multiply-Add of Packed Single-Precision Floating-Point Values + // VEX.DDS.128.66.0F38.W0 98 /r A V/V FMA Multiply packed single-precision floating-point values + // from xmm0 and xmm2/mem, add to xmm1 and put + // result in xmm0. + // VFMADD132PS xmm0, xmm1, xmm2/m128 + // +#endif + return CopyVexCommon(pbSrc[1] & 0x1F, pbDst + 3, pbSrc + 3); +} + +PBYTE CDetourDis::CopyVex2(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc) +// 2 byte VEX prefix 0xC5 +{ +#ifdef DETOURS_X86 + const static COPYENTRY ceLDS = { 0xC5, ENTRY_CopyBytes2Mod }; + if ((pbSrc[1] & 0xC0) != 0xC0) { + REFCOPYENTRY pEntry = &ceLDS; + return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc); + } +#endif + pbDst[0] = pbSrc[0]; + pbDst[1] = pbSrc[1]; + return CopyVexCommon(1, pbDst + 2, pbSrc + 2); +} + +////////////////////////////////////////////////////////////////////////////// +// +PBYTE CDetourDis::s_pbModuleBeg = NULL; +PBYTE CDetourDis::s_pbModuleEnd = (PBYTE)~(ULONG_PTR)0; +BOOL CDetourDis::s_fLimitReferencesToModule = FALSE; + +BOOL CDetourDis::SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule) +{ + if (pbEnd < pbBeg) { + return FALSE; + } + + s_pbModuleBeg = pbBeg; + s_pbModuleEnd = pbEnd; + s_fLimitReferencesToModule = fLimitReferencesToModule; + + return TRUE; +} + +///////////////////////////////////////////////////////// Disassembler Tables. +// +const BYTE CDetourDis::s_rbModRm[256] = { + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 0x + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 1x + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 2x + 0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 3x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 4x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 5x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 6x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 7x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 8x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 9x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Ax + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Bx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Cx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Dx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Ex + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 // Fx +}; + +const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] = +{ + { 0x00, ENTRY_CopyBytes2Mod }, // ADD /r + { 0x01, ENTRY_CopyBytes2Mod }, // ADD /r + { 0x02, ENTRY_CopyBytes2Mod }, // ADD /r + { 0x03, ENTRY_CopyBytes2Mod }, // ADD /r + { 0x04, ENTRY_CopyBytes2 }, // ADD ib + { 0x05, ENTRY_CopyBytes3Or5 }, // ADD iw +#ifdef DETOURS_X64 + { 0x06, ENTRY_Invalid }, // Invalid + { 0x07, ENTRY_Invalid }, // Invalid +#else + { 0x06, ENTRY_CopyBytes1 }, // PUSH + { 0x07, ENTRY_CopyBytes1 }, // POP +#endif + { 0x08, ENTRY_CopyBytes2Mod }, // OR /r + { 0x09, ENTRY_CopyBytes2Mod }, // OR /r + { 0x0A, ENTRY_CopyBytes2Mod }, // OR /r + { 0x0B, ENTRY_CopyBytes2Mod }, // OR /r + { 0x0C, ENTRY_CopyBytes2 }, // OR ib + { 0x0D, ENTRY_CopyBytes3Or5 }, // OR iw +#ifdef DETOURS_X64 + { 0x0E, ENTRY_Invalid }, // Invalid +#else + { 0x0E, ENTRY_CopyBytes1 }, // PUSH +#endif + { 0x0F, ENTRY_Copy0F }, // Extension Ops + { 0x10, ENTRY_CopyBytes2Mod }, // ADC /r + { 0x11, ENTRY_CopyBytes2Mod }, // ADC /r + { 0x12, ENTRY_CopyBytes2Mod }, // ADC /r + { 0x13, ENTRY_CopyBytes2Mod }, // ADC /r + { 0x14, ENTRY_CopyBytes2 }, // ADC ib + { 0x15, ENTRY_CopyBytes3Or5 }, // ADC id +#ifdef DETOURS_X64 + { 0x16, ENTRY_Invalid }, // Invalid + { 0x17, ENTRY_Invalid }, // Invalid +#else + { 0x16, ENTRY_CopyBytes1 }, // PUSH + { 0x17, ENTRY_CopyBytes1 }, // POP +#endif + { 0x18, ENTRY_CopyBytes2Mod }, // SBB /r + { 0x19, ENTRY_CopyBytes2Mod }, // SBB /r + { 0x1A, ENTRY_CopyBytes2Mod }, // SBB /r + { 0x1B, ENTRY_CopyBytes2Mod }, // SBB /r + { 0x1C, ENTRY_CopyBytes2 }, // SBB ib + { 0x1D, ENTRY_CopyBytes3Or5 }, // SBB id +#ifdef DETOURS_X64 + { 0x1E, ENTRY_Invalid }, // Invalid + { 0x1F, ENTRY_Invalid }, // Invalid +#else + { 0x1E, ENTRY_CopyBytes1 }, // PUSH + { 0x1F, ENTRY_CopyBytes1 }, // POP +#endif + { 0x20, ENTRY_CopyBytes2Mod }, // AND /r + { 0x21, ENTRY_CopyBytes2Mod }, // AND /r + { 0x22, ENTRY_CopyBytes2Mod }, // AND /r + { 0x23, ENTRY_CopyBytes2Mod }, // AND /r + { 0x24, ENTRY_CopyBytes2 }, // AND ib + { 0x25, ENTRY_CopyBytes3Or5 }, // AND id + { 0x26, ENTRY_CopyBytesSegment }, // ES prefix +#ifdef DETOURS_X64 + { 0x27, ENTRY_Invalid }, // Invalid +#else + { 0x27, ENTRY_CopyBytes1 }, // DAA +#endif + { 0x28, ENTRY_CopyBytes2Mod }, // SUB /r + { 0x29, ENTRY_CopyBytes2Mod }, // SUB /r + { 0x2A, ENTRY_CopyBytes2Mod }, // SUB /r + { 0x2B, ENTRY_CopyBytes2Mod }, // SUB /r + { 0x2C, ENTRY_CopyBytes2 }, // SUB ib + { 0x2D, ENTRY_CopyBytes3Or5 }, // SUB id + { 0x2E, ENTRY_CopyBytesSegment }, // CS prefix +#ifdef DETOURS_X64 + { 0x2F, ENTRY_Invalid }, // Invalid +#else + { 0x2F, ENTRY_CopyBytes1 }, // DAS +#endif + { 0x30, ENTRY_CopyBytes2Mod }, // XOR /r + { 0x31, ENTRY_CopyBytes2Mod }, // XOR /r + { 0x32, ENTRY_CopyBytes2Mod }, // XOR /r + { 0x33, ENTRY_CopyBytes2Mod }, // XOR /r + { 0x34, ENTRY_CopyBytes2 }, // XOR ib + { 0x35, ENTRY_CopyBytes3Or5 }, // XOR id + { 0x36, ENTRY_CopyBytesSegment }, // SS prefix +#ifdef DETOURS_X64 + { 0x37, ENTRY_Invalid }, // Invalid +#else + { 0x37, ENTRY_CopyBytes1 }, // AAA +#endif + { 0x38, ENTRY_CopyBytes2Mod }, // CMP /r + { 0x39, ENTRY_CopyBytes2Mod }, // CMP /r + { 0x3A, ENTRY_CopyBytes2Mod }, // CMP /r + { 0x3B, ENTRY_CopyBytes2Mod }, // CMP /r + { 0x3C, ENTRY_CopyBytes2 }, // CMP ib + { 0x3D, ENTRY_CopyBytes3Or5 }, // CMP id + { 0x3E, ENTRY_CopyBytesSegment }, // DS prefix +#ifdef DETOURS_X64 + { 0x3F, ENTRY_Invalid }, // Invalid +#else + { 0x3F, ENTRY_CopyBytes1 }, // AAS +#endif +#ifdef DETOURS_X64 // For Rax Prefix + { 0x40, ENTRY_CopyBytesRax }, // Rax + { 0x41, ENTRY_CopyBytesRax }, // Rax + { 0x42, ENTRY_CopyBytesRax }, // Rax + { 0x43, ENTRY_CopyBytesRax }, // Rax + { 0x44, ENTRY_CopyBytesRax }, // Rax + { 0x45, ENTRY_CopyBytesRax }, // Rax + { 0x46, ENTRY_CopyBytesRax }, // Rax + { 0x47, ENTRY_CopyBytesRax }, // Rax + { 0x48, ENTRY_CopyBytesRax }, // Rax + { 0x49, ENTRY_CopyBytesRax }, // Rax + { 0x4A, ENTRY_CopyBytesRax }, // Rax + { 0x4B, ENTRY_CopyBytesRax }, // Rax + { 0x4C, ENTRY_CopyBytesRax }, // Rax + { 0x4D, ENTRY_CopyBytesRax }, // Rax + { 0x4E, ENTRY_CopyBytesRax }, // Rax + { 0x4F, ENTRY_CopyBytesRax }, // Rax +#else + { 0x40, ENTRY_CopyBytes1 }, // INC + { 0x41, ENTRY_CopyBytes1 }, // INC + { 0x42, ENTRY_CopyBytes1 }, // INC + { 0x43, ENTRY_CopyBytes1 }, // INC + { 0x44, ENTRY_CopyBytes1 }, // INC + { 0x45, ENTRY_CopyBytes1 }, // INC + { 0x46, ENTRY_CopyBytes1 }, // INC + { 0x47, ENTRY_CopyBytes1 }, // INC + { 0x48, ENTRY_CopyBytes1 }, // DEC + { 0x49, ENTRY_CopyBytes1 }, // DEC + { 0x4A, ENTRY_CopyBytes1 }, // DEC + { 0x4B, ENTRY_CopyBytes1 }, // DEC + { 0x4C, ENTRY_CopyBytes1 }, // DEC + { 0x4D, ENTRY_CopyBytes1 }, // DEC + { 0x4E, ENTRY_CopyBytes1 }, // DEC + { 0x4F, ENTRY_CopyBytes1 }, // DEC +#endif + { 0x50, ENTRY_CopyBytes1 }, // PUSH + { 0x51, ENTRY_CopyBytes1 }, // PUSH + { 0x52, ENTRY_CopyBytes1 }, // PUSH + { 0x53, ENTRY_CopyBytes1 }, // PUSH + { 0x54, ENTRY_CopyBytes1 }, // PUSH + { 0x55, ENTRY_CopyBytes1 }, // PUSH + { 0x56, ENTRY_CopyBytes1 }, // PUSH + { 0x57, ENTRY_CopyBytes1 }, // PUSH + { 0x58, ENTRY_CopyBytes1 }, // POP + { 0x59, ENTRY_CopyBytes1 }, // POP + { 0x5A, ENTRY_CopyBytes1 }, // POP + { 0x5B, ENTRY_CopyBytes1 }, // POP + { 0x5C, ENTRY_CopyBytes1 }, // POP + { 0x5D, ENTRY_CopyBytes1 }, // POP + { 0x5E, ENTRY_CopyBytes1 }, // POP + { 0x5F, ENTRY_CopyBytes1 }, // POP +#ifdef DETOURS_X64 + { 0x60, ENTRY_Invalid }, // Invalid + { 0x61, ENTRY_Invalid }, // Invalid + { 0x62, ENTRY_Invalid }, // Invalid (not yet implemented Intel EVEX support) +#else + { 0x60, ENTRY_CopyBytes1 }, // PUSHAD + { 0x61, ENTRY_CopyBytes1 }, // POPAD + { 0x62, ENTRY_CopyBytes2Mod }, // BOUND /r +#endif + { 0x63, ENTRY_CopyBytes2Mod }, // 32bit ARPL /r, 64bit MOVSXD + { 0x64, ENTRY_CopyBytesSegment }, // FS prefix + { 0x65, ENTRY_CopyBytesSegment }, // GS prefix + { 0x66, ENTRY_Copy66 }, // Operand Prefix + { 0x67, ENTRY_Copy67 }, // Address Prefix + { 0x68, ENTRY_CopyBytes3Or5 }, // PUSH + { 0x69, ENTRY_CopyBytes2ModOperand }, // IMUL /r iz + { 0x6A, ENTRY_CopyBytes2 }, // PUSH + { 0x6B, ENTRY_CopyBytes2Mod1 }, // IMUL /r ib + { 0x6C, ENTRY_CopyBytes1 }, // INS + { 0x6D, ENTRY_CopyBytes1 }, // INS + { 0x6E, ENTRY_CopyBytes1 }, // OUTS/OUTSB + { 0x6F, ENTRY_CopyBytes1 }, // OUTS/OUTSW + { 0x70, ENTRY_CopyBytes2Jump }, // JO // 0f80 + { 0x71, ENTRY_CopyBytes2Jump }, // JNO // 0f81 + { 0x72, ENTRY_CopyBytes2Jump }, // JB/JC/JNAE // 0f82 + { 0x73, ENTRY_CopyBytes2Jump }, // JAE/JNB/JNC // 0f83 + { 0x74, ENTRY_CopyBytes2Jump }, // JE/JZ // 0f84 + { 0x75, ENTRY_CopyBytes2Jump }, // JNE/JNZ // 0f85 + { 0x76, ENTRY_CopyBytes2Jump }, // JBE/JNA // 0f86 + { 0x77, ENTRY_CopyBytes2Jump }, // JA/JNBE // 0f87 + { 0x78, ENTRY_CopyBytes2Jump }, // JS // 0f88 + { 0x79, ENTRY_CopyBytes2Jump }, // JNS // 0f89 + { 0x7A, ENTRY_CopyBytes2Jump }, // JP/JPE // 0f8a + { 0x7B, ENTRY_CopyBytes2Jump }, // JNP/JPO // 0f8b + { 0x7C, ENTRY_CopyBytes2Jump }, // JL/JNGE // 0f8c + { 0x7D, ENTRY_CopyBytes2Jump }, // JGE/JNL // 0f8d + { 0x7E, ENTRY_CopyBytes2Jump }, // JLE/JNG // 0f8e + { 0x7F, ENTRY_CopyBytes2Jump }, // JG/JNLE // 0f8f + { 0x80, ENTRY_CopyBytes2Mod1 }, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate byte + { 0x81, ENTRY_CopyBytes2ModOperand }, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate word or dword +#ifdef DETOURS_X64 + { 0x82, ENTRY_Invalid }, // Invalid +#else + { 0x82, ENTRY_CopyBytes2Mod1 }, // MOV al,x +#endif + { 0x83, ENTRY_CopyBytes2Mod1 }, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 reg, immediate byte + { 0x84, ENTRY_CopyBytes2Mod }, // TEST /r + { 0x85, ENTRY_CopyBytes2Mod }, // TEST /r + { 0x86, ENTRY_CopyBytes2Mod }, // XCHG /r @todo + { 0x87, ENTRY_CopyBytes2Mod }, // XCHG /r @todo + { 0x88, ENTRY_CopyBytes2Mod }, // MOV /r + { 0x89, ENTRY_CopyBytes2Mod }, // MOV /r + { 0x8A, ENTRY_CopyBytes2Mod }, // MOV /r + { 0x8B, ENTRY_CopyBytes2Mod }, // MOV /r + { 0x8C, ENTRY_CopyBytes2Mod }, // MOV /r + { 0x8D, ENTRY_CopyBytes2Mod }, // LEA /r + { 0x8E, ENTRY_CopyBytes2Mod }, // MOV /r + { 0x8F, ENTRY_CopyBytes2Mod }, // POP /0 + { 0x90, ENTRY_CopyBytes1 }, // NOP + { 0x91, ENTRY_CopyBytes1 }, // XCHG + { 0x92, ENTRY_CopyBytes1 }, // XCHG + { 0x93, ENTRY_CopyBytes1 }, // XCHG + { 0x94, ENTRY_CopyBytes1 }, // XCHG + { 0x95, ENTRY_CopyBytes1 }, // XCHG + { 0x96, ENTRY_CopyBytes1 }, // XCHG + { 0x97, ENTRY_CopyBytes1 }, // XCHG + { 0x98, ENTRY_CopyBytes1 }, // CWDE + { 0x99, ENTRY_CopyBytes1 }, // CDQ +#ifdef DETOURS_X64 + { 0x9A, ENTRY_Invalid }, // Invalid +#else + { 0x9A, ENTRY_CopyBytes5Or7Dynamic }, // CALL cp +#endif + { 0x9B, ENTRY_CopyBytes1 }, // WAIT/FWAIT + { 0x9C, ENTRY_CopyBytes1 }, // PUSHFD + { 0x9D, ENTRY_CopyBytes1 }, // POPFD + { 0x9E, ENTRY_CopyBytes1 }, // SAHF + { 0x9F, ENTRY_CopyBytes1 }, // LAHF + { 0xA0, ENTRY_CopyBytes1Address }, // MOV + { 0xA1, ENTRY_CopyBytes1Address }, // MOV + { 0xA2, ENTRY_CopyBytes1Address }, // MOV + { 0xA3, ENTRY_CopyBytes1Address }, // MOV + { 0xA4, ENTRY_CopyBytes1 }, // MOVS + { 0xA5, ENTRY_CopyBytes1 }, // MOVS/MOVSD + { 0xA6, ENTRY_CopyBytes1 }, // CMPS/CMPSB + { 0xA7, ENTRY_CopyBytes1 }, // CMPS/CMPSW + { 0xA8, ENTRY_CopyBytes2 }, // TEST + { 0xA9, ENTRY_CopyBytes3Or5 }, // TEST + { 0xAA, ENTRY_CopyBytes1 }, // STOS/STOSB + { 0xAB, ENTRY_CopyBytes1 }, // STOS/STOSW + { 0xAC, ENTRY_CopyBytes1 }, // LODS/LODSB + { 0xAD, ENTRY_CopyBytes1 }, // LODS/LODSW + { 0xAE, ENTRY_CopyBytes1 }, // SCAS/SCASB + { 0xAF, ENTRY_CopyBytes1 }, // SCAS/SCASD + { 0xB0, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB1, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB2, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB3, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB4, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB5, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB6, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB7, ENTRY_CopyBytes2 }, // MOV B0+rb + { 0xB8, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xB9, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xBA, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xBB, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xBC, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xBD, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xBE, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xBF, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb + { 0xC0, ENTRY_CopyBytes2Mod1 }, // RCL/2 ib, etc. + { 0xC1, ENTRY_CopyBytes2Mod1 }, // RCL/2 ib, etc. + { 0xC2, ENTRY_CopyBytes3 }, // RET + { 0xC3, ENTRY_CopyBytes1 }, // RET + { 0xC4, ENTRY_CopyVex3 }, // LES, VEX 3-byte opcodes. + { 0xC5, ENTRY_CopyVex2 }, // LDS, VEX 2-byte opcodes. + { 0xC6, ENTRY_CopyBytes2Mod1 }, // MOV + { 0xC7, ENTRY_CopyBytes2ModOperand }, // MOV/0 XBEGIN/7 + { 0xC8, ENTRY_CopyBytes4 }, // ENTER + { 0xC9, ENTRY_CopyBytes1 }, // LEAVE + { 0xCA, ENTRY_CopyBytes3Dynamic }, // RET + { 0xCB, ENTRY_CopyBytes1Dynamic }, // RET + { 0xCC, ENTRY_CopyBytes1Dynamic }, // INT 3 + { 0xCD, ENTRY_CopyBytes2Dynamic }, // INT ib +#ifdef DETOURS_X64 + { 0xCE, ENTRY_Invalid }, // Invalid +#else + { 0xCE, ENTRY_CopyBytes1Dynamic }, // INTO +#endif + { 0xCF, ENTRY_CopyBytes1Dynamic }, // IRET + { 0xD0, ENTRY_CopyBytes2Mod }, // RCL/2, etc. + { 0xD1, ENTRY_CopyBytes2Mod }, // RCL/2, etc. + { 0xD2, ENTRY_CopyBytes2Mod }, // RCL/2, etc. + { 0xD3, ENTRY_CopyBytes2Mod }, // RCL/2, etc. +#ifdef DETOURS_X64 + { 0xD4, ENTRY_Invalid }, // Invalid + { 0xD5, ENTRY_Invalid }, // Invalid +#else + { 0xD4, ENTRY_CopyBytes2 }, // AAM + { 0xD5, ENTRY_CopyBytes2 }, // AAD +#endif + { 0xD6, ENTRY_Invalid }, // Invalid + { 0xD7, ENTRY_CopyBytes1 }, // XLAT/XLATB + { 0xD8, ENTRY_CopyBytes2Mod }, // FADD, etc. + { 0xD9, ENTRY_CopyBytes2Mod }, // F2XM1, etc. + { 0xDA, ENTRY_CopyBytes2Mod }, // FLADD, etc. + { 0xDB, ENTRY_CopyBytes2Mod }, // FCLEX, etc. + { 0xDC, ENTRY_CopyBytes2Mod }, // FADD/0, etc. + { 0xDD, ENTRY_CopyBytes2Mod }, // FFREE, etc. + { 0xDE, ENTRY_CopyBytes2Mod }, // FADDP, etc. + { 0xDF, ENTRY_CopyBytes2Mod }, // FBLD/4, etc. + { 0xE0, ENTRY_CopyBytes2CantJump }, // LOOPNE cb + { 0xE1, ENTRY_CopyBytes2CantJump }, // LOOPE cb + { 0xE2, ENTRY_CopyBytes2CantJump }, // LOOP cb + { 0xE3, ENTRY_CopyBytes2CantJump }, // JCXZ/JECXZ + { 0xE4, ENTRY_CopyBytes2 }, // IN ib + { 0xE5, ENTRY_CopyBytes2 }, // IN id + { 0xE6, ENTRY_CopyBytes2 }, // OUT ib + { 0xE7, ENTRY_CopyBytes2 }, // OUT ib + { 0xE8, ENTRY_CopyBytes3Or5Target }, // CALL cd + { 0xE9, ENTRY_CopyBytes3Or5Target }, // JMP cd +#ifdef DETOURS_X64 + { 0xEA, ENTRY_Invalid }, // Invalid +#else + { 0xEA, ENTRY_CopyBytes5Or7Dynamic }, // JMP cp +#endif + { 0xEB, ENTRY_CopyBytes2Jump }, // JMP cb + { 0xEC, ENTRY_CopyBytes1 }, // IN ib + { 0xED, ENTRY_CopyBytes1 }, // IN id + { 0xEE, ENTRY_CopyBytes1 }, // OUT + { 0xEF, ENTRY_CopyBytes1 }, // OUT + { 0xF0, ENTRY_CopyBytesPrefix }, // LOCK prefix + { 0xF1, ENTRY_CopyBytes1Dynamic }, // INT1 / ICEBP somewhat documented by AMD, not by Intel + { 0xF2, ENTRY_CopyF2 }, // REPNE prefix +//#ifdef DETOURS_X86 + { 0xF3, ENTRY_CopyF3 }, // REPE prefix +//#else +// This does presently suffice for AMD64 but it requires tracing +// through a bunch of code to verify and seems not worth maintaining. +// { 0xF3, ENTRY_CopyBytesPrefix }, // REPE prefix +//#endif + { 0xF4, ENTRY_CopyBytes1 }, // HLT + { 0xF5, ENTRY_CopyBytes1 }, // CMC + { 0xF6, ENTRY_CopyF6 }, // TEST/0, DIV/6 + { 0xF7, ENTRY_CopyF7 }, // TEST/0, DIV/6 + { 0xF8, ENTRY_CopyBytes1 }, // CLC + { 0xF9, ENTRY_CopyBytes1 }, // STC + { 0xFA, ENTRY_CopyBytes1 }, // CLI + { 0xFB, ENTRY_CopyBytes1 }, // STI + { 0xFC, ENTRY_CopyBytes1 }, // CLD + { 0xFD, ENTRY_CopyBytes1 }, // STD + { 0xFE, ENTRY_CopyBytes2Mod }, // DEC/1,INC/0 + { 0xFF, ENTRY_CopyFF }, // CALL/2 + { 0, ENTRY_End }, +}; + +const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable0F[257] = +{ +#ifdef DETOURS_X86 + { 0x00, ENTRY_Copy0F00 }, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 +#else + { 0x00, ENTRY_CopyBytes2Mod }, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 +#endif + { 0x01, ENTRY_CopyBytes2Mod }, // INVLPG/7, etc. + { 0x02, ENTRY_CopyBytes2Mod }, // LAR/r + { 0x03, ENTRY_CopyBytes2Mod }, // LSL/r + { 0x04, ENTRY_Invalid }, // _04 + { 0x05, ENTRY_CopyBytes1 }, // SYSCALL + { 0x06, ENTRY_CopyBytes1 }, // CLTS + { 0x07, ENTRY_CopyBytes1 }, // SYSRET + { 0x08, ENTRY_CopyBytes1 }, // INVD + { 0x09, ENTRY_CopyBytes1 }, // WBINVD + { 0x0A, ENTRY_Invalid }, // _0A + { 0x0B, ENTRY_CopyBytes1 }, // UD2 + { 0x0C, ENTRY_Invalid }, // _0C + { 0x0D, ENTRY_CopyBytes2Mod }, // PREFETCH + { 0x0E, ENTRY_CopyBytes1 }, // FEMMS (3DNow -- not in Intel documentation) + { 0x0F, ENTRY_CopyBytes2Mod1 }, // 3DNow Opcodes + { 0x10, ENTRY_CopyBytes2Mod }, // MOVSS MOVUPD MOVSD + { 0x11, ENTRY_CopyBytes2Mod }, // MOVSS MOVUPD MOVSD + { 0x12, ENTRY_CopyBytes2Mod }, // MOVLPD + { 0x13, ENTRY_CopyBytes2Mod }, // MOVLPD + { 0x14, ENTRY_CopyBytes2Mod }, // UNPCKLPD + { 0x15, ENTRY_CopyBytes2Mod }, // UNPCKHPD + { 0x16, ENTRY_CopyBytes2Mod }, // MOVHPD + { 0x17, ENTRY_CopyBytes2Mod }, // MOVHPD + { 0x18, ENTRY_CopyBytes2Mod }, // PREFETCHINTA... + { 0x19, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD + { 0x1A, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD + { 0x1B, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD + { 0x1C, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD + { 0x1D, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD + { 0x1E, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD + { 0x1F, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop + { 0x20, ENTRY_CopyBytes2Mod }, // MOV/r + { 0x21, ENTRY_CopyBytes2Mod }, // MOV/r + { 0x22, ENTRY_CopyBytes2Mod }, // MOV/r + { 0x23, ENTRY_CopyBytes2Mod }, // MOV/r +#ifdef DETOURS_X64 + { 0x24, ENTRY_Invalid }, // _24 +#else + { 0x24, ENTRY_CopyBytes2Mod }, // MOV/r,TR TR is test register on 80386 and 80486, removed in Pentium +#endif + { 0x25, ENTRY_Invalid }, // _25 +#ifdef DETOURS_X64 + { 0x26, ENTRY_Invalid }, // _26 +#else + { 0x26, ENTRY_CopyBytes2Mod }, // MOV TR/r TR is test register on 80386 and 80486, removed in Pentium +#endif + { 0x27, ENTRY_Invalid }, // _27 + { 0x28, ENTRY_CopyBytes2Mod }, // MOVAPS MOVAPD + { 0x29, ENTRY_CopyBytes2Mod }, // MOVAPS MOVAPD + { 0x2A, ENTRY_CopyBytes2Mod }, // CVPI2PS & + { 0x2B, ENTRY_CopyBytes2Mod }, // MOVNTPS MOVNTPD + { 0x2C, ENTRY_CopyBytes2Mod }, // CVTTPS2PI & + { 0x2D, ENTRY_CopyBytes2Mod }, // CVTPS2PI & + { 0x2E, ENTRY_CopyBytes2Mod }, // UCOMISS UCOMISD + { 0x2F, ENTRY_CopyBytes2Mod }, // COMISS COMISD + { 0x30, ENTRY_CopyBytes1 }, // WRMSR + { 0x31, ENTRY_CopyBytes1 }, // RDTSC + { 0x32, ENTRY_CopyBytes1 }, // RDMSR + { 0x33, ENTRY_CopyBytes1 }, // RDPMC + { 0x34, ENTRY_CopyBytes1 }, // SYSENTER + { 0x35, ENTRY_CopyBytes1 }, // SYSEXIT + { 0x36, ENTRY_Invalid }, // _36 + { 0x37, ENTRY_CopyBytes1 }, // GETSEC + { 0x38, ENTRY_CopyBytes3Mod }, // SSE3 Opcodes + { 0x39, ENTRY_Invalid }, // _39 + { 0x3A, ENTRY_CopyBytes3Mod1 }, // SSE3 Opcodes + { 0x3B, ENTRY_Invalid }, // _3B + { 0x3C, ENTRY_Invalid }, // _3C + { 0x3D, ENTRY_Invalid }, // _3D + { 0x3E, ENTRY_Invalid }, // _3E + { 0x3F, ENTRY_Invalid }, // _3F + { 0x40, ENTRY_CopyBytes2Mod }, // CMOVO (0F 40) + { 0x41, ENTRY_CopyBytes2Mod }, // CMOVNO (0F 41) + { 0x42, ENTRY_CopyBytes2Mod }, // CMOVB & CMOVNE (0F 42) + { 0x43, ENTRY_CopyBytes2Mod }, // CMOVAE & CMOVNB (0F 43) + { 0x44, ENTRY_CopyBytes2Mod }, // CMOVE & CMOVZ (0F 44) + { 0x45, ENTRY_CopyBytes2Mod }, // CMOVNE & CMOVNZ (0F 45) + { 0x46, ENTRY_CopyBytes2Mod }, // CMOVBE & CMOVNA (0F 46) + { 0x47, ENTRY_CopyBytes2Mod }, // CMOVA & CMOVNBE (0F 47) + { 0x48, ENTRY_CopyBytes2Mod }, // CMOVS (0F 48) + { 0x49, ENTRY_CopyBytes2Mod }, // CMOVNS (0F 49) + { 0x4A, ENTRY_CopyBytes2Mod }, // CMOVP & CMOVPE (0F 4A) + { 0x4B, ENTRY_CopyBytes2Mod }, // CMOVNP & CMOVPO (0F 4B) + { 0x4C, ENTRY_CopyBytes2Mod }, // CMOVL & CMOVNGE (0F 4C) + { 0x4D, ENTRY_CopyBytes2Mod }, // CMOVGE & CMOVNL (0F 4D) + { 0x4E, ENTRY_CopyBytes2Mod }, // CMOVLE & CMOVNG (0F 4E) + { 0x4F, ENTRY_CopyBytes2Mod }, // CMOVG & CMOVNLE (0F 4F) + { 0x50, ENTRY_CopyBytes2Mod }, // MOVMSKPD MOVMSKPD + { 0x51, ENTRY_CopyBytes2Mod }, // SQRTPS & + { 0x52, ENTRY_CopyBytes2Mod }, // RSQRTTS RSQRTPS + { 0x53, ENTRY_CopyBytes2Mod }, // RCPPS RCPSS + { 0x54, ENTRY_CopyBytes2Mod }, // ANDPS ANDPD + { 0x55, ENTRY_CopyBytes2Mod }, // ANDNPS ANDNPD + { 0x56, ENTRY_CopyBytes2Mod }, // ORPS ORPD + { 0x57, ENTRY_CopyBytes2Mod }, // XORPS XORPD + { 0x58, ENTRY_CopyBytes2Mod }, // ADDPS & + { 0x59, ENTRY_CopyBytes2Mod }, // MULPS & + { 0x5A, ENTRY_CopyBytes2Mod }, // CVTPS2PD & + { 0x5B, ENTRY_CopyBytes2Mod }, // CVTDQ2PS & + { 0x5C, ENTRY_CopyBytes2Mod }, // SUBPS & + { 0x5D, ENTRY_CopyBytes2Mod }, // MINPS & + { 0x5E, ENTRY_CopyBytes2Mod }, // DIVPS & + { 0x5F, ENTRY_CopyBytes2Mod }, // MASPS & + { 0x60, ENTRY_CopyBytes2Mod }, // PUNPCKLBW/r + { 0x61, ENTRY_CopyBytes2Mod }, // PUNPCKLWD/r + { 0x62, ENTRY_CopyBytes2Mod }, // PUNPCKLWD/r + { 0x63, ENTRY_CopyBytes2Mod }, // PACKSSWB/r + { 0x64, ENTRY_CopyBytes2Mod }, // PCMPGTB/r + { 0x65, ENTRY_CopyBytes2Mod }, // PCMPGTW/r + { 0x66, ENTRY_CopyBytes2Mod }, // PCMPGTD/r + { 0x67, ENTRY_CopyBytes2Mod }, // PACKUSWB/r + { 0x68, ENTRY_CopyBytes2Mod }, // PUNPCKHBW/r + { 0x69, ENTRY_CopyBytes2Mod }, // PUNPCKHWD/r + { 0x6A, ENTRY_CopyBytes2Mod }, // PUNPCKHDQ/r + { 0x6B, ENTRY_CopyBytes2Mod }, // PACKSSDW/r + { 0x6C, ENTRY_CopyBytes2Mod }, // PUNPCKLQDQ + { 0x6D, ENTRY_CopyBytes2Mod }, // PUNPCKHQDQ + { 0x6E, ENTRY_CopyBytes2Mod }, // MOVD/r + { 0x6F, ENTRY_CopyBytes2Mod }, // MOV/r + { 0x70, ENTRY_CopyBytes2Mod1 }, // PSHUFW/r ib + { 0x71, ENTRY_CopyBytes2Mod1 }, // PSLLW/6 ib,PSRAW/4 ib,PSRLW/2 ib + { 0x72, ENTRY_CopyBytes2Mod1 }, // PSLLD/6 ib,PSRAD/4 ib,PSRLD/2 ib + { 0x73, ENTRY_CopyBytes2Mod1 }, // PSLLQ/6 ib,PSRLQ/2 ib + { 0x74, ENTRY_CopyBytes2Mod }, // PCMPEQB/r + { 0x75, ENTRY_CopyBytes2Mod }, // PCMPEQW/r + { 0x76, ENTRY_CopyBytes2Mod }, // PCMPEQD/r + { 0x77, ENTRY_CopyBytes1 }, // EMMS + // extrq/insertq require mode=3 and are followed by two immediate bytes + { 0x78, ENTRY_Copy0F78 }, // VMREAD/r, 66/EXTRQ/r/ib/ib, F2/INSERTQ/r/ib/ib + // extrq/insertq require mod=3, therefore ENTRY_CopyBytes2, but it ends up the same + { 0x79, ENTRY_CopyBytes2Mod }, // VMWRITE/r, 66/EXTRQ/r, F2/INSERTQ/r + { 0x7A, ENTRY_Invalid }, // _7A + { 0x7B, ENTRY_Invalid }, // _7B + { 0x7C, ENTRY_CopyBytes2Mod }, // HADDPS + { 0x7D, ENTRY_CopyBytes2Mod }, // HSUBPS + { 0x7E, ENTRY_CopyBytes2Mod }, // MOVD/r + { 0x7F, ENTRY_CopyBytes2Mod }, // MOV/r + { 0x80, ENTRY_CopyBytes3Or5Target }, // JO + { 0x81, ENTRY_CopyBytes3Or5Target }, // JNO + { 0x82, ENTRY_CopyBytes3Or5Target }, // JB,JC,JNAE + { 0x83, ENTRY_CopyBytes3Or5Target }, // JAE,JNB,JNC + { 0x84, ENTRY_CopyBytes3Or5Target }, // JE,JZ,JZ + { 0x85, ENTRY_CopyBytes3Or5Target }, // JNE,JNZ + { 0x86, ENTRY_CopyBytes3Or5Target }, // JBE,JNA + { 0x87, ENTRY_CopyBytes3Or5Target }, // JA,JNBE + { 0x88, ENTRY_CopyBytes3Or5Target }, // JS + { 0x89, ENTRY_CopyBytes3Or5Target }, // JNS + { 0x8A, ENTRY_CopyBytes3Or5Target }, // JP,JPE + { 0x8B, ENTRY_CopyBytes3Or5Target }, // JNP,JPO + { 0x8C, ENTRY_CopyBytes3Or5Target }, // JL,NGE + { 0x8D, ENTRY_CopyBytes3Or5Target }, // JGE,JNL + { 0x8E, ENTRY_CopyBytes3Or5Target }, // JLE,JNG + { 0x8F, ENTRY_CopyBytes3Or5Target }, // JG,JNLE + { 0x90, ENTRY_CopyBytes2Mod }, // CMOVO (0F 40) + { 0x91, ENTRY_CopyBytes2Mod }, // CMOVNO (0F 41) + { 0x92, ENTRY_CopyBytes2Mod }, // CMOVB & CMOVC & CMOVNAE (0F 42) + { 0x93, ENTRY_CopyBytes2Mod }, // CMOVAE & CMOVNB & CMOVNC (0F 43) + { 0x94, ENTRY_CopyBytes2Mod }, // CMOVE & CMOVZ (0F 44) + { 0x95, ENTRY_CopyBytes2Mod }, // CMOVNE & CMOVNZ (0F 45) + { 0x96, ENTRY_CopyBytes2Mod }, // CMOVBE & CMOVNA (0F 46) + { 0x97, ENTRY_CopyBytes2Mod }, // CMOVA & CMOVNBE (0F 47) + { 0x98, ENTRY_CopyBytes2Mod }, // CMOVS (0F 48) + { 0x99, ENTRY_CopyBytes2Mod }, // CMOVNS (0F 49) + { 0x9A, ENTRY_CopyBytes2Mod }, // CMOVP & CMOVPE (0F 4A) + { 0x9B, ENTRY_CopyBytes2Mod }, // CMOVNP & CMOVPO (0F 4B) + { 0x9C, ENTRY_CopyBytes2Mod }, // CMOVL & CMOVNGE (0F 4C) + { 0x9D, ENTRY_CopyBytes2Mod }, // CMOVGE & CMOVNL (0F 4D) + { 0x9E, ENTRY_CopyBytes2Mod }, // CMOVLE & CMOVNG (0F 4E) + { 0x9F, ENTRY_CopyBytes2Mod }, // CMOVG & CMOVNLE (0F 4F) + { 0xA0, ENTRY_CopyBytes1 }, // PUSH + { 0xA1, ENTRY_CopyBytes1 }, // POP + { 0xA2, ENTRY_CopyBytes1 }, // CPUID + { 0xA3, ENTRY_CopyBytes2Mod }, // BT (0F A3) + { 0xA4, ENTRY_CopyBytes2Mod1 }, // SHLD + { 0xA5, ENTRY_CopyBytes2Mod }, // SHLD + { 0xA6, ENTRY_CopyBytes2Mod }, // XBTS + { 0xA7, ENTRY_CopyBytes2Mod }, // IBTS + { 0xA8, ENTRY_CopyBytes1 }, // PUSH + { 0xA9, ENTRY_CopyBytes1 }, // POP + { 0xAA, ENTRY_CopyBytes1 }, // RSM + { 0xAB, ENTRY_CopyBytes2Mod }, // BTS (0F AB) + { 0xAC, ENTRY_CopyBytes2Mod1 }, // SHRD + { 0xAD, ENTRY_CopyBytes2Mod }, // SHRD + + // 0F AE mod76=mem mod543=0 fxsave + // 0F AE mod76=mem mod543=1 fxrstor + // 0F AE mod76=mem mod543=2 ldmxcsr + // 0F AE mod76=mem mod543=3 stmxcsr + // 0F AE mod76=mem mod543=4 xsave + // 0F AE mod76=mem mod543=5 xrstor + // 0F AE mod76=mem mod543=6 saveopt + // 0F AE mod76=mem mod543=7 clflush + // 0F AE mod76=11b mod543=5 lfence + // 0F AE mod76=11b mod543=6 mfence + // 0F AE mod76=11b mod543=7 sfence + // F3 0F AE mod76=11b mod543=0 rdfsbase + // F3 0F AE mod76=11b mod543=1 rdgsbase + // F3 0F AE mod76=11b mod543=2 wrfsbase + // F3 0F AE mod76=11b mod543=3 wrgsbase + { 0xAE, ENTRY_CopyBytes2Mod }, // fxsave fxrstor ldmxcsr stmxcsr xsave xrstor saveopt clflush lfence mfence sfence rdfsbase rdgsbase wrfsbase wrgsbase + { 0xAF, ENTRY_CopyBytes2Mod }, // IMUL (0F AF) + { 0xB0, ENTRY_CopyBytes2Mod }, // CMPXCHG (0F B0) + { 0xB1, ENTRY_CopyBytes2Mod }, // CMPXCHG (0F B1) + { 0xB2, ENTRY_CopyBytes2Mod }, // LSS/r + { 0xB3, ENTRY_CopyBytes2Mod }, // BTR (0F B3) + { 0xB4, ENTRY_CopyBytes2Mod }, // LFS/r + { 0xB5, ENTRY_CopyBytes2Mod }, // LGS/r + { 0xB6, ENTRY_CopyBytes2Mod }, // MOVZX/r + { 0xB7, ENTRY_CopyBytes2Mod }, // MOVZX/r +#ifdef DETOURS_X86 + { 0xB8, ENTRY_Copy0FB8 }, // jmpe f3/popcnt +#else + { 0xB8, ENTRY_CopyBytes2Mod }, // f3/popcnt +#endif + { 0xB9, ENTRY_Invalid }, // _B9 + { 0xBA, ENTRY_CopyBytes2Mod1 }, // BT & BTC & BTR & BTS (0F BA) + { 0xBB, ENTRY_CopyBytes2Mod }, // BTC (0F BB) + { 0xBC, ENTRY_CopyBytes2Mod }, // BSF (0F BC) + { 0xBD, ENTRY_CopyBytes2Mod }, // BSR (0F BD) + { 0xBE, ENTRY_CopyBytes2Mod }, // MOVSX/r + { 0xBF, ENTRY_CopyBytes2Mod }, // MOVSX/r + { 0xC0, ENTRY_CopyBytes2Mod }, // XADD/r + { 0xC1, ENTRY_CopyBytes2Mod }, // XADD/r + { 0xC2, ENTRY_CopyBytes2Mod1 }, // CMPPS & + { 0xC3, ENTRY_CopyBytes2Mod }, // MOVNTI + { 0xC4, ENTRY_CopyBytes2Mod1 }, // PINSRW /r ib + { 0xC5, ENTRY_CopyBytes2Mod1 }, // PEXTRW /r ib + { 0xC6, ENTRY_CopyBytes2Mod1 }, // SHUFPS & SHUFPD + { 0xC7, ENTRY_CopyBytes2Mod }, // CMPXCHG8B (0F C7) + { 0xC8, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd + { 0xC9, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd + { 0xCA, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd + { 0xCB, ENTRY_CopyBytes1 }, // CVTPD2PI BSWAP 0F C8 + rd + { 0xCC, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd + { 0xCD, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd + { 0xCE, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd + { 0xCF, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd + { 0xD0, ENTRY_CopyBytes2Mod }, // ADDSUBPS (untestd) + { 0xD1, ENTRY_CopyBytes2Mod }, // PSRLW/r + { 0xD2, ENTRY_CopyBytes2Mod }, // PSRLD/r + { 0xD3, ENTRY_CopyBytes2Mod }, // PSRLQ/r + { 0xD4, ENTRY_CopyBytes2Mod }, // PADDQ + { 0xD5, ENTRY_CopyBytes2Mod }, // PMULLW/r + { 0xD6, ENTRY_CopyBytes2Mod }, // MOVDQ2Q / MOVQ2DQ + { 0xD7, ENTRY_CopyBytes2Mod }, // PMOVMSKB/r + { 0xD8, ENTRY_CopyBytes2Mod }, // PSUBUSB/r + { 0xD9, ENTRY_CopyBytes2Mod }, // PSUBUSW/r + { 0xDA, ENTRY_CopyBytes2Mod }, // PMINUB/r + { 0xDB, ENTRY_CopyBytes2Mod }, // PAND/r + { 0xDC, ENTRY_CopyBytes2Mod }, // PADDUSB/r + { 0xDD, ENTRY_CopyBytes2Mod }, // PADDUSW/r + { 0xDE, ENTRY_CopyBytes2Mod }, // PMAXUB/r + { 0xDF, ENTRY_CopyBytes2Mod }, // PANDN/r + { 0xE0, ENTRY_CopyBytes2Mod }, // PAVGB + { 0xE1, ENTRY_CopyBytes2Mod }, // PSRAW/r + { 0xE2, ENTRY_CopyBytes2Mod }, // PSRAD/r + { 0xE3, ENTRY_CopyBytes2Mod }, // PAVGW + { 0xE4, ENTRY_CopyBytes2Mod }, // PMULHUW/r + { 0xE5, ENTRY_CopyBytes2Mod }, // PMULHW/r + { 0xE6, ENTRY_CopyBytes2Mod }, // CTDQ2PD & + { 0xE7, ENTRY_CopyBytes2Mod }, // MOVNTQ + { 0xE8, ENTRY_CopyBytes2Mod }, // PSUBB/r + { 0xE9, ENTRY_CopyBytes2Mod }, // PSUBW/r + { 0xEA, ENTRY_CopyBytes2Mod }, // PMINSW/r + { 0xEB, ENTRY_CopyBytes2Mod }, // POR/r + { 0xEC, ENTRY_CopyBytes2Mod }, // PADDSB/r + { 0xED, ENTRY_CopyBytes2Mod }, // PADDSW/r + { 0xEE, ENTRY_CopyBytes2Mod }, // PMAXSW /r + { 0xEF, ENTRY_CopyBytes2Mod }, // PXOR/r + { 0xF0, ENTRY_CopyBytes2Mod }, // LDDQU + { 0xF1, ENTRY_CopyBytes2Mod }, // PSLLW/r + { 0xF2, ENTRY_CopyBytes2Mod }, // PSLLD/r + { 0xF3, ENTRY_CopyBytes2Mod }, // PSLLQ/r + { 0xF4, ENTRY_CopyBytes2Mod }, // PMULUDQ/r + { 0xF5, ENTRY_CopyBytes2Mod }, // PMADDWD/r + { 0xF6, ENTRY_CopyBytes2Mod }, // PSADBW/r + { 0xF7, ENTRY_CopyBytes2Mod }, // MASKMOVQ + { 0xF8, ENTRY_CopyBytes2Mod }, // PSUBB/r + { 0xF9, ENTRY_CopyBytes2Mod }, // PSUBW/r + { 0xFA, ENTRY_CopyBytes2Mod }, // PSUBD/r + { 0xFB, ENTRY_CopyBytes2Mod }, // FSUBQ/r + { 0xFC, ENTRY_CopyBytes2Mod }, // PADDB/r + { 0xFD, ENTRY_CopyBytes2Mod }, // PADDW/r + { 0xFE, ENTRY_CopyBytes2Mod }, // PADDD/r + { 0xFF, ENTRY_Invalid }, // _FF + { 0, ENTRY_End }, +}; + +BOOL CDetourDis::SanityCheckSystem() +{ + ULONG n = 0; + for (; n < 256; n++) { + REFCOPYENTRY pEntry = &s_rceCopyTable[n]; + + if (n != pEntry->nOpcode) { + ASSERT(n == pEntry->nOpcode); + return FALSE; + } + } + if (s_rceCopyTable[256].pfCopy != NULL) { + ASSERT(!"Missing end marker."); + return FALSE; + } + + for (n = 0; n < 256; n++) { + REFCOPYENTRY pEntry = &s_rceCopyTable0F[n]; + + if (n != pEntry->nOpcode) { + ASSERT(n == pEntry->nOpcode); + return FALSE; + } + } + if (s_rceCopyTable0F[256].pfCopy != NULL) { + ASSERT(!"Missing end marker."); + return FALSE; + } + + return TRUE; +} +#endif // defined(DETOURS_X64) || defined(DETOURS_X86) + +/////////////////////////////////////////////////////////// IA64 Disassembler. +// +#ifdef DETOURS_IA64 + +#if defined(_IA64_) != defined(DETOURS_IA64_OFFLINE_LIBRARY) +// Compile DETOUR_IA64_BUNDLE for native IA64 or cross, but not both -- we get duplicates otherwise. +const DETOUR_IA64_BUNDLE::DETOUR_IA64_METADATA DETOUR_IA64_BUNDLE::s_rceCopyTable[33] = +{ + { 0x00, M_UNIT, I_UNIT, I_UNIT, }, + { 0x01, M_UNIT, I_UNIT, I_UNIT, }, + { 0x02, M_UNIT, I_UNIT, I_UNIT, }, + { 0x03, M_UNIT, I_UNIT, I_UNIT, }, + { 0x04, M_UNIT, L_UNIT, X_UNIT, }, + { 0x05, M_UNIT, L_UNIT, X_UNIT, }, + { 0x06, 0, 0, 0, }, + { 0x07, 0, 0, 0, }, + { 0x08, M_UNIT, M_UNIT, I_UNIT, }, + { 0x09, M_UNIT, M_UNIT, I_UNIT, }, + { 0x0a, M_UNIT, M_UNIT, I_UNIT, }, + { 0x0b, M_UNIT, M_UNIT, I_UNIT, }, + { 0x0c, M_UNIT, F_UNIT, I_UNIT, }, + { 0x0d, M_UNIT, F_UNIT, I_UNIT, }, + { 0x0e, M_UNIT, M_UNIT, F_UNIT, }, + { 0x0f, M_UNIT, M_UNIT, F_UNIT, }, + { 0x10, M_UNIT, I_UNIT, B_UNIT, }, + { 0x11, M_UNIT, I_UNIT, B_UNIT, }, + { 0x12, M_UNIT, B_UNIT, B_UNIT, }, + { 0x13, M_UNIT, B_UNIT, B_UNIT, }, + { 0x14, 0, 0, 0, }, + { 0x15, 0, 0, 0, }, + { 0x16, B_UNIT, B_UNIT, B_UNIT, }, + { 0x17, B_UNIT, B_UNIT, B_UNIT, }, + { 0x18, M_UNIT, M_UNIT, B_UNIT, }, + { 0x19, M_UNIT, M_UNIT, B_UNIT, }, + { 0x1a, 0, 0, 0, }, + { 0x1b, 0, 0, 0, }, + { 0x1c, M_UNIT, F_UNIT, B_UNIT, }, + { 0x1d, M_UNIT, F_UNIT, B_UNIT, }, + { 0x1e, 0, 0, 0, }, + { 0x1f, 0, 0, 0, }, + { 0x00, 0, 0, 0, }, +}; + +// 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0 +// f. e. d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0. + +// 00 +// f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0. +// 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0] +// 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41.. 5] +// 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42] +// 0000 0000 0007 ffff ffff c000 0000 0000 : One [ 82.. 46] +// 0000 0000 0078 0000 0000 0000 0000 0000 : One [ 86.. 83] +// 0fff ffff ff80 0000 0000 0000 0000 0000 : Two [123.. 87] +// f000 0000 0000 0000 0000 0000 0000 0000 : Two [127..124] +BYTE DETOUR_IA64_BUNDLE::GetTemplate() const +{ + return (data[0] & 0x1f); +} + +BYTE DETOUR_IA64_BUNDLE::GetInst0() const +{ + return ((data[5] & 0x3c) >> 2); +} + +BYTE DETOUR_IA64_BUNDLE::GetInst1() const +{ + return ((data[10] & 0x78) >> 3); +} + +BYTE DETOUR_IA64_BUNDLE::GetInst2() const +{ + return ((data[15] & 0xf0) >> 4); +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit(BYTE slot) const +{ + switch (slot) { + case 0: return GetUnit0(); + case 1: return GetUnit1(); + case 2: return GetUnit2(); + } + __debugbreak(); + return 0; +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit0() const +{ + return s_rceCopyTable[data[0] & 0x1f].nUnit0; +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit1() const +{ + return s_rceCopyTable[data[0] & 0x1f].nUnit1; +} + +BYTE DETOUR_IA64_BUNDLE::GetUnit2() const +{ + return s_rceCopyTable[data[0] & 0x1f].nUnit2; +} + +UINT64 DETOUR_IA64_BUNDLE::GetData0() const +{ + return (((wide[0] & 0x000003ffffffffe0) >> 5)); +} + +UINT64 DETOUR_IA64_BUNDLE::GetData1() const +{ + return (((wide[0] & 0xffffc00000000000) >> 46) | + ((wide[1] & 0x000000000007ffff) << 18)); +} + +UINT64 DETOUR_IA64_BUNDLE::GetData2() const +{ + return (((wide[1] & 0x0fffffffff800000) >> 23)); +} + +VOID DETOUR_IA64_BUNDLE::SetInst(BYTE slot, BYTE nInst) +{ + switch (slot) + { + case 0: SetInst0(nInst); return; + case 1: SetInst1(nInst); return; + case 2: SetInst2(nInst); return; + } + __debugbreak(); +} + +VOID DETOUR_IA64_BUNDLE::SetInst0(BYTE nInst) +{ + data[5] = (data[5] & ~0x3c) | ((nInst << 2) & 0x3c); +} + +VOID DETOUR_IA64_BUNDLE::SetInst1(BYTE nInst) +{ + data[10] = (data[10] & ~0x78) | ((nInst << 3) & 0x78); +} + +VOID DETOUR_IA64_BUNDLE::SetInst2(BYTE nInst) +{ + data[15] = (data[15] & ~0xf0) | ((nInst << 4) & 0xf0); +} + +VOID DETOUR_IA64_BUNDLE::SetData(BYTE slot, UINT64 nData) +{ + switch (slot) + { + case 0: SetData0(nData); return; + case 1: SetData1(nData); return; + case 2: SetData2(nData); return; + } + __debugbreak(); +} + +VOID DETOUR_IA64_BUNDLE::SetData0(UINT64 nData) +{ + wide[0] = (wide[0] & ~0x000003ffffffffe0) | (( nData << 5) & 0x000003ffffffffe0); +} + +VOID DETOUR_IA64_BUNDLE::SetData1(UINT64 nData) +{ + wide[0] = (wide[0] & ~0xffffc00000000000) | ((nData << 46) & 0xffffc00000000000); + wide[1] = (wide[1] & ~0x000000000007ffff) | ((nData >> 18) & 0x000000000007ffff); +} + +VOID DETOUR_IA64_BUNDLE::SetData2(UINT64 nData) +{ + wide[1] = (wide[1] & ~0x0fffffffff800000) | ((nData << 23) & 0x0fffffffff800000); +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction(BYTE slot) const +{ + switch (slot) { + case 0: return GetInstruction0(); + case 1: return GetInstruction1(); + case 2: return GetInstruction2(); + } + __debugbreak(); + return 0; +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction0() const +{ + // 41 bits from wide[0], skipping the 5 bit template. + return GetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE); +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction1() const +{ + // 64-46 bits from wide[0] and the rest from wide[1]. + const UINT count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET; + const UINT count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0; + return GetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0) | (GetBits(wide[1], 0, count1) << count0); +} + +UINT64 DETOUR_IA64_BUNDLE::GetInstruction2() const +{ + // Upper 41 bits of wide[1]. + return wide[1] >> (64 - DETOUR_IA64_INSTRUCTION_SIZE); +} + +void DETOUR_IA64_BUNDLE::SetInstruction(BYTE slot, UINT64 instruction) +{ + switch (slot) { + case 0: SetInstruction0(instruction); return; + case 1: SetInstruction1(instruction); return; + case 2: SetInstruction2(instruction); return; + } + __debugbreak(); +} + +void DETOUR_IA64_BUNDLE::SetInstruction0(UINT64 instruction) +{ + wide[0] = SetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE, instruction); +} + +void DETOUR_IA64_BUNDLE::SetInstruction1(UINT64 instruction) +{ + UINT const count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET; + UINT const count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0; + UINT64 const wide0 = SetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0, instruction); + UINT64 const wide1 = SetBits(wide[1], 0, count1, instruction >> count0); + wide[0] = wide0; + wide[1] = wide1; +} + +void DETOUR_IA64_BUNDLE::SetInstruction2(UINT64 instruction) +{ + // Set upper 41 bits of wide[1]. + wide[1] = SetBits(wide[1], 64 - DETOUR_IA64_INSTRUCTION_SIZE, DETOUR_IA64_INSTRUCTION_SIZE, instruction); +} + +UINT64 DETOUR_IA64_BUNDLE::SignExtend(UINT64 Value, UINT64 Offset) +// This definition is from the IA64 manual. +{ + if ((Value & (((UINT64)1) << (Offset - 1))) == 0) + return Value; + UINT64 const new_value = Value | ((~(UINT64)0) << Offset); + return new_value; +} + +UINT64 DETOUR_IA64_BUNDLE::GetBits(UINT64 Value, UINT64 Offset, UINT64 Count) +{ + UINT64 const new_value = (Value >> Offset) & ~(~((UINT64)0) << Count); + return new_value; +} + +UINT64 DETOUR_IA64_BUNDLE::SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field) +{ + UINT64 const mask = (~((~(UINT64)0) << Count)) << Offset; + UINT64 const new_value = (Value & ~mask) | ((Field << Offset) & mask); + return new_value; +} + +UINT64 DETOUR_IA64_BUNDLE::GetOpcode(UINT64 instruction) +// Get 4bit primary opcode. +{ + UINT64 const opcode = GetBits(instruction, DETOUR_IA64_INSTRUCTION_SIZE - 4, 4); + return opcode; +} + +UINT64 DETOUR_IA64_BUNDLE::GetX(UINT64 instruction) +// Get 1bit opcode extension. +{ + UINT64 const x = GetBits(instruction, 33, 1); + return x; +} + +UINT64 DETOUR_IA64_BUNDLE::GetX3(UINT64 instruction) +// Get 3bit opcode extension. +{ + UINT64 const x3 = GetBits(instruction, 33, 3); + return x3; +} + +UINT64 DETOUR_IA64_BUNDLE::GetX6(UINT64 instruction) +// Get 6bit opcode extension. +{ + UINT64 const x6 = GetBits(instruction, 27, 6); + return x6; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm7a(UINT64 instruction) +{ + UINT64 const imm7a = GetBits(instruction, 6, 7); + return imm7a; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm7a(UINT64 instruction, UINT64 imm7a) +{ + UINT64 const new_instruction = SetBits(instruction, 6, 7, imm7a); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm13c(UINT64 instruction) +{ + UINT64 const imm13c = GetBits(instruction, 20, 13); + return imm13c; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm13c(UINT64 instruction, UINT64 imm13c) +{ + UINT64 const new_instruction = SetBits(instruction, 20, 13, imm13c); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetSignBit(UINT64 instruction) +{ + UINT64 const signBit = GetBits(instruction, 36, 1); + return signBit; +} + +UINT64 DETOUR_IA64_BUNDLE::SetSignBit(UINT64 instruction, UINT64 signBit) +{ + UINT64 const new_instruction = SetBits(instruction, 36, 1, signBit); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm20a(UINT64 instruction) +{ + UINT64 const imm20a = GetBits(instruction, 6, 20); + return imm20a; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm20a(UINT64 instruction, UINT64 imm20a) +{ + UINT64 const new_instruction = SetBits(instruction, 6, 20, imm20a); + return new_instruction; +} + +UINT64 DETOUR_IA64_BUNDLE::GetImm20b(UINT64 instruction) +{ + UINT64 const imm20b = GetBits(instruction, 13, 20); + return imm20b; +} + +UINT64 DETOUR_IA64_BUNDLE::SetImm20b(UINT64 instruction, UINT64 imm20b) +{ + UINT64 const new_instruction = SetBits(instruction, 13, 20, imm20b); + return new_instruction; +} + +bool DETOUR_IA64_BUNDLE::RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst, + _In_ BYTE slot, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const +/* + If pBundleExtra is provided and instruction is IP-relative, + this function relocates instruction to target pBundleExtra, + pBundleExtra is set to brl the original target, and return true. + + [Not used] If pBundleExtra is not provided and instruction is IP-relative, return true. + + Else return false. + + The following IP-relative forms are recognized: + br and br.call + chk.s.m integer and float + chk.a.nc integer and float + chk.a.clr integer and float + chk.s.i + fchkf + + Brl is handled elsewhere, because the code was previously written. + + Branch prediction hints are not relocated. +*/ +{ + UINT64 const instruction = GetInstruction(slot); + UINT64 const opcode = GetOpcode(instruction); + size_t const dest = (size_t)pDst; + size_t const extra = (size_t)pBundleExtra; + + switch (GetUnit(slot)) { + case F_UNIT: + // F14 fchkf + if (opcode == 0 && GetX(instruction) == 0 && GetX6(instruction) == 8) { + goto imm20a; + } + return false; + + case M_UNIT: + // M20 x3 == 1 integer chk.s.m + // M21 x3 == 3 floating point chk.s + if (opcode == 1) { + UINT64 const x3 = GetX3(instruction); + if (x3 == 1 || x3 == 3) { + goto imm13_7; + } + } + + // M22 x3 == 4 integer chk.a.nc + // M22 x3 == 5 integer chk.a.clr + // M23 x3 == 6 floating point chk.a.nc + // M23 x3 == 7 floating point chk.a.clr + if (opcode == 0) { + UINT64 const x3 = GetX3(instruction); + if (x3 == 4 || x3 == 5 || x3 == 6 || x3 == 7) { + goto imm20b; + } + } + return false; + case I_UNIT: + // I20 + if (opcode == 0 && GetX3(instruction) == 1) { // chk.s.i + goto imm13_7; + } + return false; + case B_UNIT: + // B1 B2 B3 + // 4 br + // 5 br.call + if (opcode == 4 || opcode == 5) { + goto imm20b; + } + return false; + } + return false; + + UINT64 imm; + UINT64 new_instruction; + +imm13_7: + imm = SignExtend((GetSignBit(instruction) << 20) | (GetImm13c(instruction) << 7) | GetImm7a(instruction), 21) << 4; + new_instruction = SetSignBit(SetImm13c(SetImm7a(instruction, (extra - dest) >> 4), (extra - dest) >> 11), extra < dest); + goto set_brl; + +imm20a: + imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20a(instruction), 21) << 4; + new_instruction = SetSignBit(SetImm20a(instruction, (extra - dest) >> 4), extra < dest); + goto set_brl; + +imm20b: + imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20b(instruction), 21) << 4; + new_instruction = SetSignBit(SetImm20b(instruction, (extra - dest) >> 4), extra < dest); + goto set_brl; + +set_brl: + if (pBundleExtra != NULL) { + pDst->SetInstruction(slot, new_instruction); + pBundleExtra->SetBrl((size_t)this + imm); + } + return true; +} + +UINT DETOUR_IA64_BUNDLE::RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const +/* + Having already copied the bundle unchanged, then relocate its instructions one at a time. + Return how many extra bytes are required to relocate the bundle. +*/ +{ + UINT nExtraBytes = 0; + for (BYTE slot = 0; slot < DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE; ++slot) { + if (!RelocateInstruction(pDst, slot, pBundleExtra)) { + continue; + } + pBundleExtra -= !!pBundleExtra; + nExtraBytes += sizeof(DETOUR_IA64_BUNDLE); + } + return nExtraBytes; +} + +BOOL DETOUR_IA64_BUNDLE::IsBrl() const +{ + // f.e. d.c. b.a. 9.8. 7.6. 5. 4. 3. 2. 1. 0. + // c000 0070 0000 0000 0000 00 01 00 00 00 05 : brl.sptk.few + // c8ff fff0 007f fff0 ffff 00 01 00 00 00 05 : brl.sptk.few + // c000 0048 0000 0000 0001 00 00 00 00 00 05 : brl.sptk.many + return ((wide[0] & 0x000000000000001e) == 0x0000000000000004 && // 4 or 5. + (wide[1] & 0xe000000000000000) == 0xc000000000000000); // c or d. +} + +VOID DETOUR_IA64_BUNDLE::SetBrl() +{ + wide[0] = 0x0000000100000005; // few + //wide[0] = 0x0000000180000005; // many + wide[1] = 0xc000000800000000; +} + +UINT64 DETOUR_IA64_BUNDLE::GetBrlImm() const +{ + return ( + // 0x0000000000fffff0 + ((wide[1] & 0x00fffff000000000) >> 32) | // all 20 bits of imm20b. + // 0x000000ffff000000 + ((wide[0] & 0xffff000000000000) >> 24) | // bottom 16 bits of imm39. + // 0x7fffff0000000000 + ((wide[1] & 0x00000000007fffff) << 40) | // top 23 bits of imm39. + // 0x8000000000000000 + ((wide[1] & 0x0800000000000000) << 4) // single bit of i. + ); +} + +VOID DETOUR_IA64_BUNDLE::SetBrlImm(UINT64 imm) +{ + wide[0] = ((wide[0] & ~0xffff000000000000) | + // 0xffff000000000000 + ((imm & 0x000000ffff000000) << 24) // bottom 16 bits of imm39. + ); + wide[1] = ((wide[1] & ~0x08fffff0007fffff) | + // 0x00fffff000000000 + ((imm & 0x0000000000fffff0) << 32) | // all 20 bits of imm20b. + // 0x00000000007fffff + ((imm & 0x7fffff0000000000) >> 40) | // top 23 bits of imm39. + // 0x0800000000000000 + ((imm & 0x8000000000000000) >> 4) // single bit of i. + ); +} + +UINT64 DETOUR_IA64_BUNDLE::GetBrlTarget() const +{ + return (UINT64)this + GetBrlImm(); +} + +VOID DETOUR_IA64_BUNDLE::SetBrl(UINT64 target) +{ + UINT64 imm = target - (UINT64)this; + SetBrl(); + SetBrlImm(imm); +} + +VOID DETOUR_IA64_BUNDLE::SetBrlTarget(UINT64 target) +{ + UINT64 imm = target - (UINT64)this; + SetBrlImm(imm); +} + +BOOL DETOUR_IA64_BUNDLE::IsMovlGp() const +{ + // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0. + // 6fff f7f0 207f ffff ffff c001 0000 0004 + // 6000 0000 2000 0000 0000 0001 0000 0004 + return ((wide[0] & 0x00003ffffffffffe) == 0x0000000100000004 && + (wide[1] & 0xf000080fff800000) == 0x6000000020000000); +} + +UINT64 DETOUR_IA64_BUNDLE::GetMovlGp() const +{ + UINT64 raw = ( + // 0x0000000000000070 + ((wide[1] & 0x000007f000000000) >> 36) | + // 0x000000000000ff80 + ((wide[1] & 0x07fc000000000000) >> 43) | + // 0x00000000001f0000 + ((wide[1] & 0x0003e00000000000) >> 29) | + // 0x0000000000200000 + ((wide[1] & 0x0000100000000000) >> 23) | + // 0x000000ffffc00000 + ((wide[0] & 0xffffc00000000000) >> 24) | + // 0x7fffff0000000000 + ((wide[1] & 0x00000000007fffff) << 40) | + // 0x8000000000000000 + ((wide[1] & 0x0800000000000000) << 4) + ); + + return (INT64)raw; +} + +VOID DETOUR_IA64_BUNDLE::SetMovlGp(UINT64 gp) +{ + UINT64 raw = (UINT64)gp; + + wide[0] = (0x0000000100000005 | + // 0xffffc00000000000 + ((raw & 0x000000ffffc00000) << 24) + ); + wide[1] = ( + 0x6000000020000000 | + // 0x0000070000000000 + ((raw & 0x0000000000000070) << 36) | + // 0x07fc000000000000 + ((raw & 0x000000000000ff80) << 43) | + // 0x0003e00000000000 + ((raw & 0x00000000001f0000) << 29) | + // 0x0000100000000000 + ((raw & 0x0000000000200000) << 23) | + // 0x00000000007fffff + ((raw & 0x7fffff0000000000) >> 40) | + // 0x0800000000000000 + ((raw & 0x8000000000000000) >> 4) + ); +} + +UINT DETOUR_IA64_BUNDLE::Copy(_Out_ DETOUR_IA64_BUNDLE *pDst, + _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const +{ + // Copy the bytes unchanged. + +#pragma warning(suppress:6001) // using uninitialized *pDst + pDst->wide[0] = wide[0]; + pDst->wide[1] = wide[1]; + + // Relocate if necessary. + + UINT nExtraBytes = RelocateBundle(pDst, pBundleExtra); + + if (GetUnit1() == L_UNIT && IsBrl()) { + pDst->SetBrlTarget(GetBrlTarget()); + } + + return nExtraBytes; +} + +BOOL DETOUR_IA64_BUNDLE::SetNop(BYTE slot) +{ + switch (GetUnit(slot)) { + case I_UNIT: + case M_UNIT: + case F_UNIT: + SetInst(slot, 0); + SetData(slot, 0x8000000); + return true; + case B_UNIT: + SetInst(slot, 2); + SetData(slot, 0); + return true; + } + DebugBreak(); + return false; +} + +BOOL DETOUR_IA64_BUNDLE::SetNop0() +{ + return SetNop(0); +} + +BOOL DETOUR_IA64_BUNDLE::SetNop1() +{ + return SetNop(1); +} + +BOOL DETOUR_IA64_BUNDLE::SetNop2() +{ + return SetNop(2); +} + +VOID DETOUR_IA64_BUNDLE::SetStop() +{ + data[0] |= 0x01; +} + +#endif // DETOURS_IA64 + +PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst, + _Inout_opt_ PVOID *ppDstPool, + _In_ PVOID pSrc, + _Out_opt_ PVOID *ppTarget, + _Out_opt_ LONG *plExtra) +{ + LONG nExtra; + DETOUR_IA64_BUNDLE bExtra; + DETOUR_IA64_BUNDLE *pbSrc = (DETOUR_IA64_BUNDLE *)pSrc; + DETOUR_IA64_BUNDLE *pbDst = pDst ? (DETOUR_IA64_BUNDLE *)pDst : &bExtra; + + plExtra = plExtra ? plExtra : &nExtra; + *plExtra = 0; + + if (ppTarget != NULL) { + if (pbSrc->IsBrl()) { + *ppTarget = (PVOID)pbSrc->GetBrlTarget(); + } + else { + *ppTarget = DETOUR_INSTRUCTION_TARGET_NONE; + } + } + *plExtra = (LONG)pbSrc->Copy(pbDst, ppDstPool ? ((DETOUR_IA64_BUNDLE*)*ppDstPool) - 1 : (DETOUR_IA64_BUNDLE*)NULL); + return pbSrc + 1; +} + +#endif // DETOURS_IA64 + +#ifdef DETOURS_ARM + +#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1)) +#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1)) + +#define c_PCAdjust 4 // The PC value of an instruction is the PC address plus 4. +#define c_PC 15 // The register number for the Program Counter +#define c_LR 14 // The register number for the Link Register +#define c_SP 13 // The register number for the Stack Pointer +#define c_NOP 0xbf00 // A nop instruction +#define c_BREAK 0xdefe // A nop instruction + +class CDetourDis +{ + public: + CDetourDis(); + + PBYTE CopyInstruction(PBYTE pDst, + PBYTE *ppDstPool, + PBYTE pSrc, + PBYTE *ppTarget, + LONG *plExtra); + + public: + typedef BYTE (CDetourDis::* COPYFUNC)(PBYTE pbDst, PBYTE pbSrc); + + struct COPYENTRY { + USHORT nOpcode; + COPYFUNC pfCopy; + }; + + typedef const COPYENTRY * REFCOPYENTRY; + + struct Branch5 + { + DWORD Register : 3; + DWORD Imm5 : 5; + DWORD Padding : 1; + DWORD I : 1; + DWORD OpCode : 6; + }; + + struct Branch5Target + { + DWORD Padding : 1; + DWORD Imm5 : 5; + DWORD I : 1; + DWORD Padding2 : 25; + }; + + struct Branch8 + { + DWORD Imm8 : 8; + DWORD Condition : 4; + DWORD OpCode : 4; + }; + + struct Branch8Target + { + DWORD Padding : 1; + DWORD Imm8 : 8; + DWORD Padding2 : 23; + }; + + struct Branch11 + { + DWORD Imm11 : 11; + DWORD OpCode : 5; + }; + + struct Branch11Target + { + DWORD Padding : 1; + DWORD Imm11 : 11; + DWORD Padding2 : 20; + }; + + struct Branch20 + { + DWORD Imm11 : 11; + DWORD J2 : 1; + DWORD IT : 1; + DWORD J1 : 1; + DWORD Other : 2; + DWORD Imm6 : 6; + DWORD Condition : 4; + DWORD Sign : 1; + DWORD OpCode : 5; + }; + + struct Branch20Target + { + DWORD Padding : 1; + DWORD Imm11 : 11; + DWORD Imm6 : 6; + DWORD J1 : 1; + DWORD J2 : 1; + DWORD Sign : 1; + INT32 Padding2 : 11; + }; + + struct Branch24 + { + DWORD Imm11 : 11; + DWORD J2 : 1; + DWORD InstructionSet : 1; + DWORD J1 : 1; + DWORD Link : 1; + DWORD Branch : 1; + DWORD Imm10 : 10; + DWORD Sign : 1; + DWORD OpCode : 5; + }; + + struct Branch24Target + { + DWORD Padding : 1; + DWORD Imm11 : 11; + DWORD Imm10 : 10; + DWORD I2 : 1; + DWORD I1 : 1; + DWORD Sign : 1; + INT32 Padding2 : 7; + }; + + struct LiteralLoad8 + { + DWORD Imm8 : 8; + DWORD Register : 3; + DWORD OpCode : 5; + }; + + struct LiteralLoad8Target + { + DWORD Padding : 2; + DWORD Imm8 : 8; + DWORD Padding2 : 22; + }; + + struct LiteralLoad12 + { + DWORD Imm12 : 12; + DWORD Register : 4; + DWORD OpCodeSuffix : 7; + DWORD Add : 1; + DWORD OpCodePrefix : 8; + }; + + struct LiteralLoad12Target + { + DWORD Imm12 : 12; + DWORD Padding : 20; + }; + + struct ImmediateRegisterLoad32 + { + DWORD Imm12 : 12; + DWORD DestinationRegister : 4; + DWORD SourceRegister: 4; + DWORD OpCode : 12; + }; + + struct ImmediateRegisterLoad16 + { + DWORD DestinationRegister : 3; + DWORD SourceRegister: 3; + DWORD OpCode : 10; + }; + + struct TableBranch + { + DWORD IndexRegister : 4; + DWORD HalfWord : 1; + DWORD OpCodeSuffix : 11; + DWORD BaseRegister : 4; + DWORD OpCodePrefix : 12; + }; + + struct Shift + { + DWORD Imm2 : 2; + DWORD Imm3 : 3; + }; + + struct Add32 + { + DWORD SecondOperandRegister : 4; + DWORD Type : 2; + DWORD Imm2 : 2; + DWORD DestinationRegister : 4; + DWORD Imm3 : 3; + DWORD Padding : 1; + DWORD FirstOperandRegister : 4; + DWORD SetFlags : 1; + DWORD OpCode : 11; + }; + + struct LogicalShiftLeft32 + { + DWORD SourceRegister : 4; + DWORD Padding : 2; + DWORD Imm2 : 2; + DWORD DestinationRegister : 4; + DWORD Imm3 : 3; + DWORD Padding2 : 5; + DWORD SetFlags : 1; + DWORD OpCode : 11; + }; + + struct StoreImmediate12 + { + DWORD Imm12 : 12; + DWORD SourceRegister : 4; + DWORD BaseRegister : 4; + DWORD OpCode : 12; + }; + + protected: + BYTE PureCopy16(BYTE* pSource, BYTE* pDest); + BYTE PureCopy32(BYTE* pSource, BYTE* pDest); + BYTE CopyMiscellaneous16(BYTE* pSource, BYTE* pDest); + BYTE CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest); + BYTE CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest); + BYTE CopyLiteralLoad16(BYTE* pSource, BYTE* pDest); + BYTE CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest); + BYTE CopyBranch24(BYTE* pSource, BYTE* pDest); + BYTE CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest); + BYTE CopyLiteralLoad32(BYTE* pSource, BYTE* pDest); + BYTE CopyLoadAndStoreSingle(BYTE* pSource, BYTE* pDest); + BYTE CopyLoadAndStoreMultipleAndSRS(BYTE* pSource, BYTE* pDest); + BYTE CopyTableBranch(BYTE* pSource, BYTE* pDest); + BYTE BeginCopy32(BYTE* pSource, BYTE* pDest); + + LONG DecodeBranch5(ULONG opcode); + USHORT EncodeBranch5(ULONG originalOpCode, LONG delta); + LONG DecodeBranch8(ULONG opcode); + USHORT EncodeBranch8(ULONG originalOpCode, LONG delta); + LONG DecodeBranch11(ULONG opcode); + USHORT EncodeBranch11(ULONG originalOpCode, LONG delta); + BYTE EmitBranch11(PUSHORT& pDest, LONG relativeAddress); + LONG DecodeBranch20(ULONG opcode); + ULONG EncodeBranch20(ULONG originalOpCode, LONG delta); + LONG DecodeBranch24(ULONG opcode, BOOL& fLink); + ULONG EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink); + LONG DecodeLiteralLoad8(ULONG instruction); + LONG DecodeLiteralLoad12(ULONG instruction); + BYTE EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral); + BYTE EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral); + BYTE EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg); + BYTE EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg); + BYTE EmitLongLiteralLoad(PUSHORT& pDest, BYTE reg, PVOID pTarget); + BYTE EmitLongBranch(PUSHORT& pDest, PVOID pTarget); + USHORT CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd); + + protected: + ULONG GetLongInstruction(BYTE* pSource) + { + return (((PUSHORT)pSource)[0] << 16) | (((PUSHORT)pSource)[1]); + } + + BYTE EmitLongInstruction(PUSHORT& pDstInst, ULONG instruction) + { + *pDstInst++ = (USHORT)(instruction >> 16); + *pDstInst++ = (USHORT)instruction; + return sizeof(ULONG); + } + + BYTE EmitShortInstruction(PUSHORT& pDstInst, USHORT instruction) + { + *pDstInst++ = instruction; + return sizeof(USHORT); + } + + PBYTE Align4(PBYTE pValue) + { + return (PBYTE)(((size_t)pValue) & ~(ULONG)3u); + } + + PBYTE CalculateTarget(PBYTE pSource, LONG delta) + { + return (pSource + delta + c_PCAdjust); + } + + LONG CalculateNewDelta(PBYTE pTarget, BYTE* pDest) + { + return (LONG)(pTarget - (pDest + c_PCAdjust)); + } + + BYTE EmitAdd32(PUSHORT& pDstInst, BYTE op1Reg, BYTE op2Reg, BYTE dstReg, BYTE shiftAmount) + { + Shift& shift = (Shift&)(shiftAmount); + const BYTE shiftType = 0x00; // LSL + Add32 add = { op2Reg, shiftType, shift.Imm2, dstReg, shift.Imm3, + 0x0, op1Reg, 0x0, 0x758 }; + return EmitLongInstruction(pDstInst, (ULONG&)add); + } + + BYTE EmitLogicalShiftLeft32(PUSHORT& pDstInst, BYTE srcReg, BYTE dstReg, BYTE shiftAmount) + { + Shift& shift = (Shift&)(shiftAmount); + LogicalShiftLeft32 shiftLeft = { srcReg, 0x00, shift.Imm2, dstReg, shift.Imm3, 0x1E, + 0x00, 0x752 }; + return EmitLongInstruction(pDstInst, (ULONG&)shiftLeft); + } + + BYTE EmitStoreImmediate12(PUSHORT& pDstInst, BYTE srcReg, BYTE baseReg, USHORT offset) + { + StoreImmediate12 store = { offset, srcReg, baseReg, 0xF8C }; + return EmitLongInstruction(pDstInst, (ULONG&)store); + } + + protected: + PBYTE m_pbTarget; + PBYTE m_pbPool; + LONG m_lExtra; + + BYTE m_rbScratchDst[64]; + + static const COPYENTRY s_rceCopyTable[33]; +}; + +LONG CDetourDis::DecodeBranch5(ULONG opcode) +{ + Branch5& branch = (Branch5&)(opcode); + + Branch5Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm5 = branch.Imm5; + target.I = branch.I; + + // Return zero-extended value + return (LONG&)target; +} + +USHORT CDetourDis::EncodeBranch5(ULONG originalOpCode, LONG delta) +{ + // Too large for a 5 bit branch (5 bit branches can be up to 7 bits due to I and the trailing 0) + if (delta < 0 || delta > 0x7F) { + return 0; + } + + Branch5& branch = (Branch5&)(originalOpCode); + Branch5Target& target = (Branch5Target&)(delta); + + branch.Imm5 = target.Imm5; + branch.I = target.I; + + return (USHORT&)branch; +} + +LONG CDetourDis::DecodeBranch8(ULONG opcode) +{ + Branch8& branch = (Branch8&)(opcode); + + Branch8Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm8 = branch.Imm8; + + // Return sign extended value + return (((LONG&)target) << 23) >> 23; +} + +USHORT CDetourDis::EncodeBranch8(ULONG originalOpCode, LONG delta) +{ + // Too large for 8 bit branch (8 bit branches can be up to 9 bits due to the trailing 0) + if (delta < (-(int)0x100) || delta > 0xFF) { + return 0; + } + + Branch8& branch = (Branch8&)(originalOpCode); + Branch8Target& target = (Branch8Target&)(delta); + + branch.Imm8 = target.Imm8; + + return (USHORT&)branch; +} + +LONG CDetourDis::DecodeBranch11(ULONG opcode) +{ + Branch11& branch = (Branch11&)(opcode); + + Branch11Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm11 = branch.Imm11; + + // Return sign extended value + return (((LONG&)target) << 20) >> 20; +} + +USHORT CDetourDis::EncodeBranch11(ULONG originalOpCode, LONG delta) +{ + // Too large for an 11 bit branch (11 bit branches can be up to 12 bits due to the trailing 0) + if (delta < (-(int)0x800) || delta > 0x7FF) { + return 0; + } + + Branch11& branch = (Branch11&)(originalOpCode); + Branch11Target& target = (Branch11Target&)(delta); + + branch.Imm11 = target.Imm11; + + return (USHORT&)branch; +} + +BYTE CDetourDis::EmitBranch11(PUSHORT& pDest, LONG relativeAddress) +{ + Branch11Target& target = (Branch11Target&)(relativeAddress); + Branch11 branch11 = { target.Imm11, 0x1C }; + + *pDest++ = (USHORT&)branch11; + return sizeof(USHORT); +} + +LONG CDetourDis::DecodeBranch20(ULONG opcode) +{ + Branch20& branch = (Branch20&)(opcode); + + Branch20Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm11 = branch.Imm11; + target.Imm6 = branch.Imm6; + target.Sign = branch.Sign; + target.J1 = branch.J1; + target.J2 = branch.J2; + + // Sign extend + if (target.Sign) { + target.Padding2 = -1; + } + + return (LONG&)target; +} + +ULONG CDetourDis::EncodeBranch20(ULONG originalOpCode, LONG delta) +{ + // Too large for 20 bit branch (20 bit branches can be up to 21 bits due to the trailing 0) + if (delta < (-(int)0x100000) || delta > 0xFFFFF) { + return 0; + } + + Branch20& branch = (Branch20&)(originalOpCode); + Branch20Target& target = (Branch20Target&)(delta); + + branch.Imm11 = target.Imm11; + branch.Imm6 = target.Imm6; + branch.Sign = target.Sign; + branch.J1 = target.J1; + branch.J2 = target.J2; + + return (ULONG&)branch; +} + +LONG CDetourDis::DecodeBranch24(ULONG opcode, BOOL& fLink) +{ + Branch24& branch = (Branch24&)(opcode); + + Branch24Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm11 = branch.Imm11; + target.Imm10 = branch.Imm10; + target.Sign = branch.Sign; + target.I1 = ~(branch.J1 ^ target.Sign); + target.I2 = ~(branch.J2 ^ target.Sign); + fLink = branch.Link; + + // Sign extend + if (target.Sign) { + target.Padding2 = -1; + } + + return (LONG&)target; +} + +ULONG CDetourDis::EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink) +{ + // Too large for 24 bit branch (24 bit branches can be up to 25 bits due to the trailing 0) + if (delta < static_cast(0xFF000000) || delta > static_cast(0xFFFFFF)) { + return 0; + } + + Branch24& branch = (Branch24&)(originalOpCode); + Branch24Target& target = (Branch24Target&)(delta); + + branch.Imm11 = target.Imm11; + branch.Imm10 = target.Imm10; + branch.Link = fLink; + branch.Sign = target.Sign; + branch.J1 = ~(target.I1 ^ branch.Sign); + branch.J2 = ~(target.I2 ^ branch.Sign); + + return (ULONG&)branch; +} + +LONG CDetourDis::DecodeLiteralLoad8(ULONG instruction) +{ + LiteralLoad8& load = (LiteralLoad8&)(instruction); + + LiteralLoad8Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm8 = load.Imm8; + + return (LONG&)target; +} + +BYTE CDetourDis::EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral) +{ + // Note: We add 2 (which gets rounded down) because literals must be 32-bit + // aligned, but the ldr can be 16-bit aligned. + LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest); + LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0x3FF); + + LiteralLoad8Target& target = (LiteralLoad8Target&)(relative); + LiteralLoad8 load = { target.Imm8, targetRegister, 0x9 }; + + return EmitShortInstruction(pDest, (USHORT&)load); +} + +LONG CDetourDis::DecodeLiteralLoad12(ULONG instruction) +{ + LiteralLoad12& load = (LiteralLoad12&)(instruction); + + LiteralLoad12Target target; + ZeroMemory(&target, sizeof(target)); + target.Imm12 = load.Imm12; + + return (LONG&)target; +} + +BYTE CDetourDis::EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral) +{ + // Note: We add 2 (which gets rounded down) because literals must be 32-bit + // aligned, but the ldr can be 16-bit aligned. + LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest); + LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0xFFF); + + LiteralLoad12Target& target = (LiteralLoad12Target&)(relative); + target.Imm12 -= target.Imm12 & 3; + LiteralLoad12 load = { target.Imm12, targetRegister, 0x5F, (DWORD)(newDelta > 0), 0xF8 }; + + return EmitLongInstruction(pDest, (ULONG&)load); +} + +BYTE CDetourDis::EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg) +{ + ImmediateRegisterLoad32 load = { 0, reg, reg, 0xF8D }; + return EmitLongInstruction(pDest, (ULONG&)load); +} + +BYTE CDetourDis::EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg) +{ + ImmediateRegisterLoad16 load = { reg, reg, 0x680 >> 2 }; + return EmitShortInstruction(pDest, (USHORT&)load); +} + +BYTE CDetourDis::EmitLongLiteralLoad(PUSHORT& pDest, BYTE targetRegister, PVOID pTarget) +{ + *--((PULONG&)m_pbPool) = (ULONG)(size_t)pTarget; + + // ldr rn, target. + BYTE size = EmitLiteralLoad12(pDest, targetRegister, m_pbPool); + + // This only makes sense if targetRegister != PC; + // otherwise, we would have branched with the previous instruction anyway + if (targetRegister != c_PC) { + // ldr rn, [rn] + if (targetRegister <= 7) { + size = (BYTE)(size + EmitImmediateRegisterLoad16(pDest, targetRegister)); + } + else { + size = (BYTE)(size + EmitImmediateRegisterLoad32(pDest, targetRegister)); + } + } + + return size; +} + +BYTE CDetourDis::EmitLongBranch(PUSHORT& pDest, PVOID pTarget) +{ + // Emit a long literal load into PC + BYTE size = EmitLongLiteralLoad(pDest, c_PC, DETOURS_PBYTE_TO_PFUNC(pTarget)); + return size; +} + +BYTE CDetourDis::PureCopy16(BYTE* pSource, BYTE* pDest) +{ + *(USHORT *)pDest = *(USHORT *)pSource; + return sizeof(USHORT); +} + +BYTE CDetourDis::PureCopy32(BYTE* pSource, BYTE* pDest) +{ + *(UNALIGNED ULONG *)pDest = *(UNALIGNED ULONG*)pSource; + return sizeof(DWORD); +} + +USHORT CDetourDis::CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd) +{ + ULONG destinationLength = (ULONG)(pDestEnd - pDestStart); + return static_cast((destinationLength > sourceLength) ? (destinationLength - sourceLength) : 0); +} + +BYTE CDetourDis::CopyMiscellaneous16(BYTE* pSource, BYTE* pDest) +{ + USHORT instruction = *(PUSHORT)(pSource); + + // Compare and branch imm5 (CBZ, CBNZ) + if ((instruction & 0x100) && !(instruction & 0x400)) { // (1011x0x1xxxxxxxx) + LONG oldDelta = DecodeBranch5(instruction); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch5(instruction, newDelta); + + if (instruction) { + // Copy the 16 bit instruction over + *(PUSHORT)(pDest) = instruction; + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, re-encode with 'conditional branch' logic, without using the condition flags + // For example, cbz r2,+0x56 (0x90432) becomes: + // + // 001df73a b92a cbnz r2,001df748 + // 001df73c e002 b 001df744 + // 001df73e bf00 nop + // 001df740 0432 dc.h 0432 + // 001df742 0009 dc.h 0009 + // 001df744 f85ff008 ldr pc,=0x90432 + // + + // Store where we will be writing our conditional branch, and move past it so we can emit a long branch + PUSHORT pDstInst = (PUSHORT)(pDest); + PUSHORT pConditionalBranchInstruction = pDstInst++; + + // Emit the long branch instruction + BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget); + + // Invert the CBZ/CBNZ instruction to move past our 'long branch' if the inverse comparison succeeds + // Write the CBZ/CBNZ instruction *before* the long branch we emitted above + // This had to be done out of order, since the size of a long branch can vary due to alignment restrictions + instruction = EncodeBranch5(*(PUSHORT)(pSource), longBranchSize - c_PCAdjust + sizeof(USHORT)); + Branch5& branch = (Branch5&)(instruction); + branch.OpCode = (branch.OpCode & 0x02) ? 0x2C : 0x2E; // Invert the CBZ/CBNZ comparison + *pConditionalBranchInstruction = instruction; + + // Compute the extra space needed for the branch sequence + m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst)); + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If-Then Instruction (IT) + if ((instruction >> 8 == 0xBF) && (instruction & 0xF)) { //(10111111xxxx(mask != 0b0000)) + // ToDo: Implement IT handler + ASSERT(false); + return sizeof(USHORT); + } + + // ADD/SUB, SXTH, SXTB, UXTH, UXTB, CBZ, CBNZ, PUSH, POP, REV, REV15, REVSH, NOP, YIELD, WFE, WFI, SEV, etc. + return PureCopy16(pSource, pDest); +} + +BYTE CDetourDis::CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest) +{ + USHORT instruction = *(PUSHORT)(pSource); + + // Could be a conditional branch, an Undefined instruction or a Service System Call + // Only the former needs special logic + if ((instruction & 0xE00) != 0xE00) { // 1101(!=111x)xxxxxxxx + LONG oldDelta = DecodeBranch8(instruction); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch8(instruction, newDelta); + if (instruction) { + // Copy the 16 bit instruction over + *(PUSHORT)(pDest) = instruction; + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, re-encode as a sequence of branches + // For example, bne +0x6E (0x90452) becomes: + // + // 001df758 d100 bne 001df75c + // 001df75a e005 b 001df768 + // 001df75c e002 b 001df764 + // 001df75e bf00 nop + // 001df760 0452 dc.h 0452 + // 001df762 0009 dc.h 0009 + // 001df764 f85ff008 ldr pc,=0x90452 + // + + // First, reuse the existing conditional branch to, if successful, branch down to a 'long branch' that we will emit below + USHORT newInstruction = EncodeBranch8(*(PUSHORT)(pSource), 0); // Due to the size of c_PCAdjust a zero-length branch moves 4 bytes forward, past the following unconditional branch + ASSERT(newInstruction); + PUSHORT pDstInst = (PUSHORT)(pDest); + *pDstInst++ = newInstruction; + + // Next, prepare to insert an unconditional branch that will be hit if the condition above is not met. This branch will branch over the following 'long branch' + // We can't actually encode this branch yet though, because 'long branches' can vary in size + PUSHORT pUnconditionalBranchInstruction = pDstInst++; + + // Then, emit a 'long branch' that will be hit if the original condition is met + BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget); + + // Finally, encode and emit the unconditional branch that will be used to branch past the 'long branch' if the initial condition was not met + Branch11 branch11 = { 0x00, 0x1C }; + newInstruction = EncodeBranch11(*(DWORD*)(&branch11), longBranchSize - c_PCAdjust + sizeof(USHORT)); + ASSERT(newInstruction); + *pUnconditionalBranchInstruction = newInstruction; + + // Compute the extra space needed for the branch sequence + m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst)); + return sizeof(USHORT); // The source instruction was 16 bits + } + + return PureCopy16(pSource, pDest); +} + +BYTE CDetourDis::CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = *(PUSHORT)(pSource); + + LONG oldDelta = DecodeBranch11(instruction); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch11(instruction, newDelta); + if (instruction) { + // Copy the 16 bit instruction over + *(PUSHORT)(pDest) = (USHORT)instruction; + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, re-encode as 32-bit + PUSHORT pDstInst = (PUSHORT)(pDest); + instruction = EncodeBranch24(0xf0009000, newDelta, FALSE); + if (instruction) { + // Copy both bytes of the instruction + EmitLongInstruction(pDstInst, instruction); + + m_lExtra = sizeof(DWORD) - sizeof(USHORT); // The destination instruction was 32 bits + return sizeof(USHORT); // The source instruction was 16 bits + } + + // If that fails, emit as a 'long branch' + if (!instruction) { + // For example, b +0x7FE (00090be6) becomes: + // 003f6d02 e001 b 003f6d08 + // 003f6d04 0be6 dc.h 0be6 + // 003f6d06 0009 dc.h 0009 + // 003f6d08 f85ff008 ldr pc,=0x90BE6 + EmitLongBranch(pDstInst, pTarget); + + // Compute the extra space needed for the branch sequence + m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst)); + return sizeof(USHORT); // The source instruction was 16 bits + } + + return sizeof(USHORT); // The source instruction was 16 bits +} + +BYTE CDetourDis::CopyLiteralLoad16(BYTE* pSource, BYTE* pDest) +{ + PBYTE pStart = pDest; + USHORT instruction = *(PUSHORT)(pSource); + + LONG oldDelta = DecodeLiteralLoad8(instruction); + PBYTE pTarget = CalculateTarget(Align4(pSource), oldDelta); + + // Re-encode as a 'long literal load' + // For example, ldr r0, [PC + 1E0] (0x905B4) becomes: + // + // 001df72c f85f0008 ldr r0,=0x905B4 + // 001df730 f8d00000 ldr.w r0,[r0] + LiteralLoad8& load8 = (LiteralLoad8&)(instruction); + EmitLongLiteralLoad((PUSHORT&)pDest, load8.Register, pTarget); + + m_lExtra = (LONG)(pDest - pStart - sizeof(USHORT)); + return sizeof(USHORT); // The source instruction was 16 bits +} + +BYTE CDetourDis::CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = *(PUSHORT)(pSource); + + // BX + if ((instruction & 0xff80) == 0x4700) { + // The target is stored in a register + m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + + // AND, LSR, TST, ADD, CMP, MOV + return PureCopy16(pSource, pDest); +} + +const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[33] = +{ + // Shift by immediate, move register + // ToDo: Not handling moves from PC + /* 0b00000 */ { 0x00, &CDetourDis::PureCopy16 }, + /* 0b00001 */ { 0x01, &CDetourDis::PureCopy16 }, + /* 0b00010 */ { 0x02, &CDetourDis::PureCopy16 }, + + // Add/subtract register + // Add/subtract immediate + /* 0b00011 */ { 0x03, &CDetourDis::PureCopy16}, + + // Add/subtract/compare/move immediate + /* 0b00100 */ { 0x04, &CDetourDis::PureCopy16 }, + /* 0b00101 */ { 0x05, &CDetourDis::PureCopy16 }, + /* 0b00110 */ { 0x06, &CDetourDis::PureCopy16 }, + /* 0b00111 */ { 0x07, &CDetourDis::PureCopy16 }, + + // Data-processing register + // Special data processing + // Branch/exchange instruction set + /* 0b01000 */ { 0x08, &CDetourDis::CopyBranchExchangeOrDataProcessing16 }, + + // Load from literal pool + /* 0b01001 */ { 0x09, &CDetourDis::CopyLiteralLoad16 }, + + // Load/store register offset + /* 0b01010 */ { 0x0a, &CDetourDis::PureCopy16 }, + /* 0b01011 */ { 0x0b, &CDetourDis::PureCopy16 }, + + // Load/store word/byte immediate offset. + /* 0b01100 */ { 0x0c, &CDetourDis::PureCopy16 }, + /* 0b01101 */ { 0x0d, &CDetourDis::PureCopy16 }, + /* 0b01110 */ { 0x0e, &CDetourDis::PureCopy16 }, + /* 0b01111 */ { 0x0f, &CDetourDis::PureCopy16 }, + + // Load/store halfword immediate offset. + /* 0b10000 */ { 0x10, &CDetourDis::PureCopy16 }, + /* 0b10001 */ { 0x11, &CDetourDis::PureCopy16 }, + + // Load from or store to stack + /* 0b10010 */ { 0x12, &CDetourDis::PureCopy16 }, + /* 0b10011 */ { 0x13, &CDetourDis::PureCopy16 }, + + // Add to SP or PC + /* 0b10100 */ { 0x14, &CDetourDis::PureCopy16 }, + // ToDo: Is ADR (T1) blitt-able? + // It adds a value to PC and stores the result in a register. + // Does this count as a 'target' for detours? + /* 0b10101 */ { 0x15, &CDetourDis::PureCopy16 }, + + // Miscellaneous + /* 0b10110 */ { 0x16, &CDetourDis::CopyMiscellaneous16 }, + /* 0b10111 */ { 0x17, &CDetourDis::CopyMiscellaneous16 }, + + // Load/store multiple + /* 0b11000 */ { 0x18, &CDetourDis::PureCopy16 }, + /* 0b11001 */ { 0x19, &CDetourDis::PureCopy16 }, + // ToDo: Are we sure these are all safe? + // LDMIA, for example, can include an 'embedded' branch. + // Does this count as a 'target' for detours? + + // Conditional branch + /* 0b11010 */ { 0x1a, &CDetourDis::CopyConditionalBranchOrOther16 }, + + // Conditional branch + // Undefined instruction + // Service (system) call + /* 0b11011 */ { 0x1b, &CDetourDis::CopyConditionalBranchOrOther16 }, + + // Unconditional branch + /* 0b11100 */ { 0x1c, &CDetourDis::CopyUnConditionalBranch16 }, + + // 32-bit instruction + /* 0b11101 */ { 0x1d, &CDetourDis::BeginCopy32 }, + /* 0b11110 */ { 0x1e, &CDetourDis::BeginCopy32 }, + /* 0b11111 */ { 0x1f, &CDetourDis::BeginCopy32 }, + { 0, NULL } +}; + +BYTE CDetourDis::CopyBranch24(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = GetLongInstruction(pSource); + BOOL fLink; + LONG oldDelta = DecodeBranch24(instruction, fLink); + PBYTE pTarget = CalculateTarget(pSource, oldDelta); + m_pbTarget = pTarget; + + // Re-encode as 32-bit + PUSHORT pDstInst = (PUSHORT)(pDest); + LONG newDelta = CalculateNewDelta(pTarget, pDest); + instruction = EncodeBranch24(instruction, newDelta, fLink); + if (instruction) { + // Copy both bytes of the instruction + EmitLongInstruction(pDstInst, instruction); + return sizeof(DWORD); + } + + // If that fails, re-encode as a 'long branch' + EmitLongBranch(pDstInst, pTarget); + + // Compute the extra space needed for the instruction + m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst)); + return sizeof(DWORD); // The source instruction was 32 bits +} + +BYTE CDetourDis::CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest) +{ + ULONG instruction = GetLongInstruction(pSource); + if ((instruction & 0xf800d000) == 0xf0008000) { // B.W