2023-06-06 17:36:00 +00:00
# include <stdio.h>
2023-06-25 09:32:19 +00:00
# include <inject.h>
2023-06-26 09:53:07 +00:00
# include <envs.h>
2023-06-05 21:23:08 +00:00
2023-06-25 09:32:19 +00:00
# include <game_p.h>
2023-06-05 21:23:08 +00:00
2023-07-02 20:21:17 +00:00
typedef char * ( * wgufn_t ) ( wchar_t * path ) ; // wine_get_unix_file_name
2023-07-03 11:04:04 +00:00
const wchar_t * J_MB_TITLE = L " Jadeite Launcher Payload " ;
2023-07-02 20:21:17 +00:00
2023-06-05 21:23:08 +00:00
BOOL WINAPI DllMain ( HINSTANCE inst , DWORD reason , LPVOID reserved ) {
// Only listen for attach
if ( reason ! = DLL_PROCESS_ATTACH ) {
return TRUE ;
}
// Get target EXE path
2023-07-03 11:04:04 +00:00
wchar_t targetExe [ MAX_PATH ] ;
GetEnvironmentVariableW ( ENV_EXE_PATH , targetExe , MAX_PATH ) ;
2023-06-05 21:23:08 +00:00
// Get the path of the DLL to inject
2023-07-03 11:04:04 +00:00
wchar_t injectDll [ MAX_PATH ] ;
GetEnvironmentVariableW ( ENV_DLL_PATH , injectDll , MAX_PATH ) ;
2023-06-05 21:23:08 +00:00
2023-06-11 15:04:24 +00:00
// Get game commandline
2023-07-03 11:04:04 +00:00
wchar_t cmdline [ 8192 ] ;
GetEnvironmentVariableW ( ENV_PROC_CMD , cmdline , sizeof ( cmdline ) / sizeof ( wchar_t ) ) ;
2023-06-11 15:04:24 +00:00
2023-06-05 21:23:08 +00:00
// Compute the working directory path
2023-07-03 11:04:04 +00:00
wchar_t workdir [ MAX_PATH ] ;
wcscpy ( workdir , targetExe ) ;
2023-07-03 22:08:03 +00:00
* ( wcsrchr ( workdir , L ' \\ ' ) ) = L ' \0 ' ;
2023-06-05 21:23:08 +00:00
2023-07-02 20:21:17 +00:00
// SAFETY: verify that the injector is not inside the game directory
HMODULE kernel32 = GetModuleHandleA ( " kernel32.dll " ) ;
wgufn_t wine_get_unix_file_name = ( wgufn_t ) GetProcAddress ( kernel32 , " wine_get_unix_file_name " ) ;
if ( wine_get_unix_file_name ) {
2023-07-03 11:04:04 +00:00
char * unixInjectDll = wine_get_unix_file_name ( injectDll ) ;
char * unixWorkdir = wine_get_unix_file_name ( workdir ) ;
2023-07-02 20:21:17 +00:00
2023-07-03 08:57:08 +00:00
char * i = unixInjectDll , * w = unixWorkdir ;
2023-07-02 20:21:17 +00:00
char startsWith = 0 ;
2023-07-03 14:04:32 +00:00
while ( * i & & * w ) {
2023-07-03 08:57:08 +00:00
startsWith = * i = = * w ;
2023-07-02 20:21:17 +00:00
if ( ! startsWith ) break ;
2023-07-03 08:57:08 +00:00
i + + , w + + ;
2023-07-02 20:21:17 +00:00
}
HANDLE heap = GetProcessHeap ( ) ;
HeapFree ( heap , 0 , unixInjectDll ) ;
HeapFree ( heap , 0 , unixWorkdir ) ;
if ( startsWith ) {
2023-07-03 11:04:04 +00:00
MessageBoxW ( NULL , L " Putting the patcher (or any other foreign PE binaries) inside the game directory is dangerous! Please move it elsewhere. " , J_MB_TITLE , MB_OK | MB_ICONERROR ) ;
2023-07-02 20:21:17 +00:00
exit ( 1 ) ;
}
} else {
2023-07-03 11:04:04 +00:00
MessageBoxW ( NULL , L " Could not find wine_get_unix_file_name! Wine version too old? " , J_MB_TITLE , MB_OK | MB_ICONWARNING ) ;
2023-07-02 20:21:17 +00:00
}
2023-08-05 09:15:08 +00:00
// Get restart flag file path
wchar_t restartFlagFile [ MAX_PATH ] ;
GetTempPathW ( MAX_PATH , restartFlagFile ) ;
wcscat ( restartFlagFile , L " jadeite \\ restart_flag " ) ;
2023-06-05 21:23:08 +00:00
2023-08-04 12:35:29 +00:00
do {
// Start the game
STARTUPINFOW si ;
ZeroMemory ( & si , sizeof ( si ) ) ;
2023-08-05 06:22:15 +00:00
si . cb = sizeof ( si ) ;
2023-08-04 12:35:29 +00:00
PROCESS_INFORMATION pi ;
ZeroMemory ( & pi , sizeof ( pi ) ) ;
if ( ! CreateProcessW (
NULL ,
cmdline ,
NULL ,
NULL ,
FALSE ,
CREATE_SUSPENDED ,
NULL ,
workdir ,
& si ,
& pi
) ) {
wchar_t message [ 1024 ] ;
2023-08-12 14:20:58 +00:00
wsprintfW ( message , L " Failed to start game process: %ld \n Game executable path: '%ls' " , GetLastError ( ) , targetExe ) ;
2023-08-04 12:35:29 +00:00
MessageBoxW ( NULL , message , J_MB_TITLE , MB_OK | MB_ICONERROR ) ;
exit ( 1 ) ;
}
// Inject
void * payloadStart = & _binary_game_p_o_p_game_p_bin_start ;
size_t payloadSize = ( size_t ) & _binary_game_p_o_p_game_p_bin_size ;
inject ( pi . hProcess , payloadStart , payloadSize , injectDll ) ;
2023-08-05 09:15:08 +00:00
// Remove the restart flag file
DeleteFileW ( restartFlagFile ) ;
2023-08-04 12:35:29 +00:00
// Optional: wait for user input before resuming (useful for debugging)
char * waitEnabled = getenv ( " WAIT_BEFORE_RESUME " ) ;
if ( waitEnabled & & * waitEnabled ) {
wchar_t message [ 64 ] ;
wsprintfW ( message , L " PID: %ld. Press OK to continue " , pi . dwProcessId ) ;
MessageBoxW ( NULL , message , J_MB_TITLE , MB_OK | MB_ICONINFORMATION ) ;
}
2023-06-10 15:23:43 +00:00
2023-08-04 12:35:29 +00:00
// Resume the process
ResumeThread ( pi . hThread ) ;
2023-06-05 21:23:08 +00:00
2023-08-04 12:35:29 +00:00
// The launcher process should now hang untill the game terminates
WaitForSingleObject ( pi . hProcess , INFINITE ) ;
2023-08-05 09:15:08 +00:00
} while ( GetFileAttributesW ( restartFlagFile ) ! = INVALID_FILE_ATTRIBUTES ) ;
2023-06-05 21:23:08 +00:00
return TRUE ;
}