Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14c90f7137 | |||
| 8f96ec4eec | |||
| 5421487212 | |||
| 326ccd188e | |||
| 0b0216e41e | |||
| 4c0c35ba43 | |||
| e299d264de |
@ -29,6 +29,8 @@ Manual usage instructions:
|
|||||||
|
|
||||||
This tool is capable of starting the games from a different process. This may be useful for spoofing the parent process (SR is known to report it). Use `wine jadeite.exe 'Z:\wine\path\to\game.exe' 'Z:\wine\path\to\launcher.exe'`. `explorer.exe` is used as the default.
|
This tool is capable of starting the games from a different process. This may be useful for spoofing the parent process (SR is known to report it). Use `wine jadeite.exe 'Z:\wine\path\to\game.exe' 'Z:\wine\path\to\launcher.exe'`. `explorer.exe` is used as the default.
|
||||||
|
|
||||||
|
To pass commandline arguments to the game, append them after the launcher path: `wine jadeite.exe 'Z:\wine\path\to\game.exe' 'Z:\wine\path\to\launcher.exe' -arg1 -arg2 -arg3`. To use the default launcher process, use `--`: `wine jadeite.exe 'Z:\wine\path\to\game.exe' -- -arg1 -arg2 -arg3`.
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
These environment variables can be used to configure the behaviour of the tool.
|
These environment variables can be used to configure the behaviour of the tool.
|
||||||
|
|
||||||
|
|||||||
1
build.sh
1
build.sh
@ -14,7 +14,6 @@ cp ./build/injector/jadeite.exe ./out
|
|||||||
cp ./build/injector/launcher_payload/launcher_payload.dll ./out
|
cp ./build/injector/launcher_payload/launcher_payload.dll ./out
|
||||||
cp ./build/game_payload/game_payload.dll ./out
|
cp ./build/game_payload/game_payload.dll ./out
|
||||||
cp ./LICENSE.txt ./out
|
cp ./LICENSE.txt ./out
|
||||||
cp ./metadata.json ./out
|
|
||||||
|
|
||||||
$strip ./out/*.{exe,dll}
|
$strip ./out/*.{exe,dll}
|
||||||
|
|
||||||
|
|||||||
4
game_payload/include/main.h
Normal file
4
game_payload/include/main.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void unload_ctr_inc();
|
||||||
|
void unload_ctr_dec();
|
||||||
@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
#include <game.h>
|
#include <game.h>
|
||||||
|
|
||||||
void tp6_setup_patcher(struct game_data *game, HMODULE thisModule, HMODULE baseModule);
|
void tp6_setup_patcher(struct game_data *game, HMODULE baseModule);
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
#include <msg.h>
|
#include <msg.h>
|
||||||
|
#include <main.h>
|
||||||
|
|
||||||
|
#include <crc32.h>
|
||||||
|
#include <pe.h>
|
||||||
|
|
||||||
#include <game.h>
|
#include <game.h>
|
||||||
|
|
||||||
@ -20,24 +24,52 @@ const struct crc_id_pair HSR_REGIONS[] = {
|
|||||||
{ 0x3e644d26, GAME_HSR_CN } // cn v1.1.0
|
{ 0x3e644d26, GAME_HSR_CN } // cn v1.1.0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define JUMP_SIZE (6 + sizeof(void*))
|
||||||
|
|
||||||
|
// Temporarily hardcoded offset
|
||||||
|
// v1.1.0, same for os and cn
|
||||||
|
#define WTSUD_PATCH_OFFSET 0x16430
|
||||||
|
|
||||||
|
char wtsud_original_bytes[JUMP_SIZE];
|
||||||
|
char *wtsud_patch_addr;
|
||||||
|
|
||||||
|
static void _wtsud_stub() {
|
||||||
|
// Recover original bytes
|
||||||
|
DWORD oldProtect;
|
||||||
|
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||||
|
|
||||||
|
memcpy(wtsud_patch_addr, wtsud_original_bytes, JUMP_SIZE);
|
||||||
|
|
||||||
|
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, oldProtect, &oldProtect);
|
||||||
|
|
||||||
|
unload_ctr_dec();
|
||||||
|
}
|
||||||
|
|
||||||
static void _unityplayer_callback(HMODULE unityModule) {
|
static void _unityplayer_callback(HMODULE unityModule) {
|
||||||
if (utils_env_enabled("SRFIX_DISABLE")) {
|
if (utils_env_enabled("SRFIX_DISABLE")) {
|
||||||
msg_info_a("Shared resources fix disabled. The game may not work");
|
msg_info_a("Shared resources fix disabled. The game may not work");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable shared resources
|
|
||||||
|
|
||||||
// Temporarily hardcoded offset
|
// Remove dependency on shared resources by patching WriteTextureStatisticUserData
|
||||||
// v1.1.0, same for os and cn
|
|
||||||
unsigned char *srAddr = ((unsigned char*)unityModule) + 0x16430;
|
wtsud_patch_addr = ((char*)unityModule) + 0x16430;
|
||||||
|
|
||||||
DWORD oldProtect;
|
DWORD oldProtect;
|
||||||
VirtualProtect(srAddr, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
|
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||||
|
|
||||||
*srAddr = 0xC3; // ret
|
// Save original bytes
|
||||||
|
memcpy(wtsud_original_bytes, wtsud_patch_addr, JUMP_SIZE);
|
||||||
|
|
||||||
VirtualProtect(srAddr, 1, oldProtect, &oldProtect);
|
// Write jump
|
||||||
|
const char JUMP_INST[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; // jmp [$ + 6]
|
||||||
|
memcpy(wtsud_patch_addr, JUMP_INST, sizeof(JUMP_INST));
|
||||||
|
|
||||||
|
// Write destination address
|
||||||
|
void *destAddr = &_wtsud_stub;
|
||||||
|
memcpy(wtsud_patch_addr + sizeof(JUMP_INST), &destAddr, sizeof(destAddr));
|
||||||
|
|
||||||
|
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, oldProtect, &oldProtect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hsr_fill_data(struct game_data *buf) {
|
void hsr_fill_data(struct game_data *buf) {
|
||||||
@ -66,5 +98,6 @@ void hsr_fill_data(struct game_data *buf) {
|
|||||||
buf->tp6_section_name = HSR_TP6_SECTION_NAME;
|
buf->tp6_section_name = HSR_TP6_SECTION_NAME;
|
||||||
buf->tvm_section_name = HSR_TVM_SECTION_NAME;
|
buf->tvm_section_name = HSR_TVM_SECTION_NAME;
|
||||||
|
|
||||||
|
unload_ctr_inc();
|
||||||
buf->unityplayer_callback = &_unityplayer_callback;
|
buf->unityplayer_callback = &_unityplayer_callback;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,31 @@
|
|||||||
#include <tp6.h>
|
#include <tp6.h>
|
||||||
#include <utils.h>
|
#include <utils.h>
|
||||||
|
|
||||||
|
#include <main.h>
|
||||||
|
|
||||||
|
HMODULE this_module;
|
||||||
|
size_t unload_ctr = 0;
|
||||||
|
|
||||||
|
void unload_ctr_inc() {
|
||||||
|
unload_ctr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unload_ctr_dec() {
|
||||||
|
unload_ctr--;
|
||||||
|
if (unload_ctr == 0) {
|
||||||
|
void *pFreeLibrary = GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeLibrary");
|
||||||
|
CreateThread(NULL, 0, pFreeLibrary, this_module, 0, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
||||||
// Only listen to attach
|
// Only listen to attach
|
||||||
if (reason != DLL_PROCESS_ATTACH) {
|
if (reason != DLL_PROCESS_ATTACH) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this_module = instance;
|
||||||
|
|
||||||
// Dynamically link functions from ntdll
|
// Dynamically link functions from ntdll
|
||||||
ntdll_link();
|
ntdll_link();
|
||||||
|
|
||||||
@ -27,7 +46,7 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
|
|||||||
ace_load_driver_module();
|
ace_load_driver_module();
|
||||||
|
|
||||||
// ...magic
|
// ...magic
|
||||||
tp6_setup_patcher(&game, instance, baseModule);
|
tp6_setup_patcher(&game, baseModule);
|
||||||
|
|
||||||
// Load the UnityPlayer module and invoke the callback
|
// Load the UnityPlayer module and invoke the callback
|
||||||
HMODULE unityModule = LoadLibraryA("UnityPlayer.dll");
|
HMODULE unityModule = LoadLibraryA("UnityPlayer.dll");
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
const char ENV_EXE_PATH[] = "JADEITE_TARGET_EXE_PATH";
|
#define EPFX "__JADEITE_"
|
||||||
const char ENV_DLL_PATH[] = "JADEITE_INJECT_DLL_PATH";
|
|
||||||
|
const char ENV_EXE_PATH[] = EPFX"TARGET_EXE_PATH";
|
||||||
|
const char ENV_DLL_PATH[] = EPFX"INJECT_DLL_PATH";
|
||||||
|
const char ENV_PROC_CMD[] = EPFX"PROCESS_COMMAND";
|
||||||
|
|
||||||
static inline void write_protected_process_memory(HANDLE process, void *address, const void *buf, size_t size) {
|
static inline void write_protected_process_memory(HANDLE process, void *address, const void *buf, size_t size) {
|
||||||
DWORD oldProtect;
|
DWORD oldProtect;
|
||||||
|
|||||||
@ -16,6 +16,9 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
|||||||
// Get the path of the DLL to inject
|
// Get the path of the DLL to inject
|
||||||
char *injectDll = getenv(ENV_DLL_PATH);
|
char *injectDll = getenv(ENV_DLL_PATH);
|
||||||
|
|
||||||
|
// Get game commandline
|
||||||
|
char *cmdline = getenv(ENV_PROC_CMD);
|
||||||
|
|
||||||
// Compute the working directory path
|
// Compute the working directory path
|
||||||
char workdir[MAX_PATH];
|
char workdir[MAX_PATH];
|
||||||
strcpy(workdir, targetExe);
|
strcpy(workdir, targetExe);
|
||||||
@ -30,8 +33,8 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
|||||||
ZeroMemory(&pi, sizeof(pi));
|
ZeroMemory(&pi, sizeof(pi));
|
||||||
|
|
||||||
if (!CreateProcessA(
|
if (!CreateProcessA(
|
||||||
targetExe,
|
|
||||||
NULL,
|
NULL,
|
||||||
|
cmdline,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
FALSE,
|
FALSE,
|
||||||
|
|||||||
@ -7,29 +7,43 @@
|
|||||||
const char LAUNCHER_INJECT_DLL[] = "launcher_payload.dll";
|
const char LAUNCHER_INJECT_DLL[] = "launcher_payload.dll";
|
||||||
const char GAME_INJECT_DLL[] = "game_payload.dll";
|
const char GAME_INJECT_DLL[] = "game_payload.dll";
|
||||||
|
|
||||||
|
#define SHIFT(argc, argv) argc--, argv++
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
// Read arguments
|
// Read arguments
|
||||||
char *gamePath = NULL;
|
char *gamePath = NULL;
|
||||||
char *launcherPath = NULL;
|
char *launcherPath = NULL;
|
||||||
|
|
||||||
|
// Skip executable
|
||||||
|
SHIFT(argc, argv);
|
||||||
|
|
||||||
switch (argc) {
|
switch (argc) {
|
||||||
case 1:
|
case 0:
|
||||||
printf("Usage: wine jadeite.exe [game path] <launcher path>\n");
|
printf("Usage: wine jadeite.exe [game path] <launcher path>\n");
|
||||||
return 0;
|
return 0;
|
||||||
case 2:
|
case 1:
|
||||||
printf("No launcher process specified! Using explorer.exe\n");
|
gamePath = argv[0];
|
||||||
gamePath = argv[1];
|
SHIFT(argc, argv);
|
||||||
launcherPath = "C:\\Windows\\explorer.exe";
|
|
||||||
break;
|
launcherPath = "--";
|
||||||
case 3:
|
|
||||||
gamePath = argv[1];
|
|
||||||
launcherPath = argv[2];
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Too many arguments! (%d)\n", argc);
|
gamePath = argv[0];
|
||||||
return 1;
|
SHIFT(argc, argv);
|
||||||
|
|
||||||
|
launcherPath = argv[0];
|
||||||
|
SHIFT(argc, argv);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default launcher path
|
||||||
|
if (strcmp(launcherPath, "--") == 0) {
|
||||||
|
printf("No launcher process specified! Using explorer.exe\n");
|
||||||
|
launcherPath = "C:\\Windows\\explorer.exe";
|
||||||
|
}
|
||||||
|
|
||||||
// cd into the injector directory
|
// cd into the injector directory
|
||||||
char injectorPath[MAX_PATH];
|
char injectorPath[MAX_PATH];
|
||||||
GetModuleFileNameA(GetModuleHandleA(NULL), injectorPath, sizeof(injectorPath));
|
GetModuleFileNameA(GetModuleHandleA(NULL), injectorPath, sizeof(injectorPath));
|
||||||
@ -48,13 +62,26 @@ int main(int argc, char **argv) {
|
|||||||
char launcherPayloadPath[MAX_PATH];
|
char launcherPayloadPath[MAX_PATH];
|
||||||
GetFullPathNameA(LAUNCHER_INJECT_DLL, sizeof(launcherPayloadPath), launcherPayloadPath, NULL);
|
GetFullPathNameA(LAUNCHER_INJECT_DLL, sizeof(launcherPayloadPath), launcherPayloadPath, NULL);
|
||||||
|
|
||||||
printf("Starting '%s' via '%s'\n", gameExePath, launcherPath);
|
// Construct commandline for the game process
|
||||||
|
char cmdline[8192];
|
||||||
|
sprintf(cmdline, "\"%s\"", gameExePath);
|
||||||
|
|
||||||
|
while (argc) {
|
||||||
|
char arg[8192];
|
||||||
|
sprintf(arg, " \"%s\"", argv[0]);
|
||||||
|
strcat(cmdline, arg);
|
||||||
|
|
||||||
|
SHIFT(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
// Set envvars
|
// Set envvars
|
||||||
SetEnvironmentVariableA(ENV_EXE_PATH, gameExePath);
|
SetEnvironmentVariableA(ENV_EXE_PATH, gameExePath);
|
||||||
SetEnvironmentVariableA(ENV_DLL_PATH, gamePayloadPath);
|
SetEnvironmentVariableA(ENV_DLL_PATH, gamePayloadPath);
|
||||||
|
SetEnvironmentVariableA(ENV_PROC_CMD, cmdline);
|
||||||
|
|
||||||
// Start the launcher
|
// Start the launcher
|
||||||
|
printf("Starting '%s' via '%s'\n", gameExePath, launcherPath);
|
||||||
|
|
||||||
STARTUPINFO si;
|
STARTUPINFO si;
|
||||||
ZeroMemory(&si, sizeof(si));
|
ZeroMemory(&si, sizeof(si));
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
project('jadeite', 'c', version: '1.1.1')
|
project('jadeite', 'c', version: '1.1.3')
|
||||||
|
|
||||||
nasm = find_program('nasm')
|
nasm = find_program('nasm')
|
||||||
gen_res = find_program('gen_resources.sh')
|
gen_res = find_program('gen_resources.sh')
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"jadeite": {
|
"jadeite": {
|
||||||
"version": "1.1.1"
|
"version": "1.1.3"
|
||||||
},
|
},
|
||||||
"games": {
|
"games": {
|
||||||
"hi3rd": {
|
"hi3rd": {
|
||||||
@ -11,11 +11,11 @@
|
|||||||
},
|
},
|
||||||
"hsr": {
|
"hsr": {
|
||||||
"global": {
|
"global": {
|
||||||
"status": "verified",
|
"status": "unsafe",
|
||||||
"version": "1.1.0"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
"china": {
|
"china": {
|
||||||
"status": "verified",
|
"status": "unsafe",
|
||||||
"version": "1.1.0"
|
"version": "1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user