10 Commits

Author SHA1 Message Date
15f56c9e5a v1.1.7 2023-07-03 14:07:38 +03:00
b860834be1 Switch to using wide strings in the injector 2023-07-03 14:04:04 +03:00
8c900f93fc Fix freeing heap on directory check 2023-07-03 11:57:08 +03:00
a7d68776bd Update credits section 2023-07-02 23:48:54 +03:00
6d742b2a15 v1.1.6 2023-07-02 23:29:47 +03:00
cf5d87f7a7 Refuse to launch if the patcher is inside the game directory 2023-07-02 23:21:17 +03:00
181d14e4ce Minor readme styling changes 2023-07-02 20:51:49 +03:00
0067ceb85c Document AAT third-party launchers 2023-07-02 20:50:49 +03:00
33cf0a65e8 Document HI3 v6.7.0 support 2023-06-30 11:55:12 +03:00
d30a2aba9e Update comment in hi3.c 2023-06-29 12:58:57 +03:00
12 changed files with 107 additions and 67 deletions

View File

@ -1,5 +1,5 @@
### Games and regions ### Games and regions
- **3rd**: glb v6.6.0 - **3rd**: glb v6.7.0
- **SR**: os/cn v1.1.0 (unsafe, refer to [configuration](#configuration)) - **SR**: os/cn v1.1.0 (unsafe, refer to [configuration](#configuration))
It may be possilbe to completely remove the region and version-specific data in the future. Refer to the source code in `game_payload/src` for details. It may be possilbe to completely remove the region and version-specific data in the future. Refer to the source code in `game_payload/src` for details.
@ -14,7 +14,7 @@ The anticheat the games use is fundamentally incompatible with Wine in multiple
**This is not a cheating tool**. Using it with Windows is not possible, and Windows support is not planned or intended in any way. However, as it does not perform any on-disk file modifications, you may reuse the same game install for Windows if you have a dual-boot setup. **This is not a cheating tool**. Using it with Windows is not possible, and Windows support is not planned or intended in any way. However, as it does not perform any on-disk file modifications, you may reuse the same game install for Windows if you have a dual-boot setup.
### Usage ### Usage
**Refer to [third-party launchers](#third-party-launchers) (will be written later)** for convenient usage. If you don't want to (or can't) use third-party launchers, continue reading the section below. **Refer to [third-party launchers](#third-party-launchers)** for convenient usage. If you don't want to (or can't) use third-party launchers, continue reading the section below.
**Wine 8.0+ is recommended**, as lower versions leak "The Wine project" as the device identifier. Not critical, but taking a precaution never hurt anyone. **DXVK is strongly recommended.** **Wine 8.0+ is recommended**, as lower versions leak "The Wine project" as the device identifier. Not critical, but taking a precaution never hurt anyone. **DXVK is strongly recommended.**
@ -55,11 +55,13 @@ A part of the source code is witheld (`game_payload/src/tp6.c`). This is a force
Please do not report any issues with the Game to the official channels. Use the issue tracker of this repository Please do not report any issues with the Game to the official channels. Use the issue tracker of this repository
### Third-party launchers ### Third-party launchers
Will be written later - Honkers Launcher — Linux launcher for 3rd ([GitHub](https://github.com/an-anime-team/honkers-launcher) | [Codeberg](https://codeberg.org/an-anime-team/honkers-launcher))
- The Honkers Railway Launcher — Linux launcher for SR ([GitHub](https://github.com/an-anime-team/the-honkers-railway-launcher) | [Codeberg](https://codeberg.org/an-anime-team/the-honkers-railway-launcher))
### Credits ### Credits
- mkrsym1 — project leader, reverse engineering - mkrsym1 — project leader, reverse engineering
- Yor#1920 — major help with analyzing network activity - [EternalStudentDesuKa](https://github.com/EternalStudentDesuKa) — major help with analyzing network activity
- [An Anime Team](https://github.com/an-anime-team) — Honkers Launcher and The Honkers Railway Launcher
- Some others credited in the source code - Some others credited in the source code
License: MIT License: MIT

View File

@ -17,7 +17,7 @@ const struct crc_id_pair HI3_REGIONS[] = {
// Only glb for now // Only glb for now
// It may be possible to get rid of region-specific data altogether in the future // It may be possible to get rid of region-specific data altogether in the future
{ 0x45221647, GAME_HI3_GLB } // glb v6.6.0 { 0x45221647, GAME_HI3_GLB } // glb v6.7.0
}; };
void hi3_fill_data(struct game_data *buf) { void hi3_fill_data(struct game_data *buf) {

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#define EPFX "__JADEITE_" #define EPFX L"__JADEITE_"
#define ENV_EXE_PATH EPFX"TARGET_EXE_PATH" #define ENV_EXE_PATH EPFX"TARGET_EXE_PATH"
#define ENV_DLL_PATH EPFX"INJECT_DLL_PATH" #define ENV_DLL_PATH EPFX"INJECT_DLL_PATH"

View File

@ -2,4 +2,4 @@
#include <windows.h> #include <windows.h>
void inject(HANDLE process, const void *payload, size_t payloadSize, const char *dllPath); void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar_t *dllPath);

View File

@ -34,7 +34,8 @@ executable(
'src/inject.c', 'src/inject.c',
exe_res_files, exe_res_files,
include_directories: include_dir, include_directories: include_dir,
name_prefix: '' name_prefix: '',
link_args: '-municode'
) )
# Dll that will be injected into the launcher # Dll that will be injected into the launcher
@ -44,5 +45,6 @@ shared_library(
'src/inject.c', 'src/inject.c',
dll_res_files, dll_res_files,
include_directories: include_dir, include_directories: include_dir,
name_prefix: '' name_prefix: '',
link_args: '-municode'
) )

View File

@ -5,6 +5,10 @@
#include <game_p.h> #include <game_p.h>
typedef char *(*wgufn_t)(wchar_t* path); // wine_get_unix_file_name
const wchar_t *J_MB_TITLE = L"Jadeite Launcher Payload";
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) { BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
// Only listen for attach // Only listen for attach
if (reason != DLL_PROCESS_ATTACH) { if (reason != DLL_PROCESS_ATTACH) {
@ -12,28 +16,60 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
} }
// Get target EXE path // Get target EXE path
char *targetExe = getenv(ENV_EXE_PATH); wchar_t targetExe[MAX_PATH];
GetEnvironmentVariableW(ENV_EXE_PATH, targetExe, MAX_PATH);
// Get the path of the DLL to inject // Get the path of the DLL to inject
char *injectDll = getenv(ENV_DLL_PATH); wchar_t injectDll[MAX_PATH];
GetEnvironmentVariableW(ENV_DLL_PATH, injectDll, MAX_PATH);
// Get game commandline // Get game commandline
char *cmdline = getenv(ENV_PROC_CMD); wchar_t cmdline[8192];
GetEnvironmentVariableW(ENV_PROC_CMD, cmdline, sizeof(cmdline) / sizeof(wchar_t));
// Compute the working directory path // Compute the working directory path
char workdir[MAX_PATH]; wchar_t workdir[MAX_PATH];
strcpy(workdir, targetExe); wcscpy(workdir, targetExe);
*(strrchr(workdir, '\\')) = '\0'; *(wcsrchr(workdir, '\\')) = '\0';
// 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) {
char *unixInjectDll = wine_get_unix_file_name(injectDll);
char *unixWorkdir = wine_get_unix_file_name(workdir);
char *i = unixInjectDll, *w = unixWorkdir;
char startsWith = 0;
while (*i != '\0' && *w != '\0') {
startsWith = *i == *w;
if (!startsWith) break;
i++, w++;
}
HANDLE heap = GetProcessHeap();
HeapFree(heap, 0, unixInjectDll);
HeapFree(heap, 0, unixWorkdir);
if (startsWith) {
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);
exit(1);
}
} else {
MessageBoxW(NULL, L"Could not find wine_get_unix_file_name! Wine version too old?", J_MB_TITLE, MB_OK | MB_ICONWARNING);
}
// Start the game // Start the game
STARTUPINFO si; STARTUPINFOW si;
ZeroMemory(&si, sizeof(si)); ZeroMemory(&si, sizeof(si));
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
si.cb = sizeof(si); si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessA( if (!CreateProcessW(
NULL, NULL,
cmdline, cmdline,
NULL, NULL,
@ -45,9 +81,9 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
&si, &si,
&pi &pi
)) { )) {
char message[64]; wchar_t message[1024];
sprintf(message, "Failed to start game process: %ld", GetLastError()); wsprintfW(message, L"Failed to start game process: %ld", GetLastError());
MessageBoxA(NULL, message, "Jadeite Launcher Payload", MB_OK | MB_ICONERROR); MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONERROR);
exit(1); exit(1);
} }
@ -60,9 +96,9 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
// Optional: wait for user input before resuming (useful for debugging) // Optional: wait for user input before resuming (useful for debugging)
char *waitEnabled = getenv("WAIT_BEFORE_RESUME"); char *waitEnabled = getenv("WAIT_BEFORE_RESUME");
if (waitEnabled && strcmp(waitEnabled, "") != 0) { if (waitEnabled && strcmp(waitEnabled, "") != 0) {
char message[64]; wchar_t message[64];
sprintf(message, "PID: %ld. Press OK to continue", pi.dwProcessId); wsprintfW(message, L"PID: %ld. Press OK to continue", pi.dwProcessId);
MessageBoxA(NULL, message, "Jadeite Launcher Payload", MB_OK | MB_ICONINFORMATION); MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION);
} }
// Resume the process // Resume the process

View File

@ -5,28 +5,28 @@
#include <launcher_p.h> #include <launcher_p.h>
const char LAUNCHER_INJECT_DLL[] = "launcher_payload.dll"; const wchar_t *LAUNCHER_INJECT_DLL = L"launcher_payload.dll";
const char GAME_INJECT_DLL[] = "game_payload.dll"; const wchar_t *GAME_INJECT_DLL = L"game_payload.dll";
#define SHIFT(argc, argv) argc--, argv++ #define SHIFT(argc, argv) argc--, argv++
int main(int argc, char **argv) { int wmain(int argc, wchar_t **argv) {
// Read arguments // Read arguments
char *gamePath = NULL; wchar_t *gamePath = NULL;
char *launcherPath = NULL; wchar_t *launcherPath = NULL;
// Skip executable // Skip executable
SHIFT(argc, argv); SHIFT(argc, argv);
switch (argc) { switch (argc) {
case 0: case 0:
printf("Usage: wine jadeite.exe [game path] <launcher path>\n"); wprintf(L"Usage: wine jadeite.exe [game path] <launcher path>\n");
return 0; return 0;
case 1: case 1:
gamePath = argv[0]; gamePath = argv[0];
SHIFT(argc, argv); SHIFT(argc, argv);
launcherPath = "--"; launcherPath = L"--";
break; break;
default: default:
@ -40,57 +40,57 @@ int main(int argc, char **argv) {
} }
// Default launcher path // Default launcher path
if (strcmp(launcherPath, "--") == 0) { if (wcscmp(launcherPath, L"--") == 0) {
printf("No launcher process specified! Using explorer.exe\n"); wprintf(L"No launcher process specified! Using explorer.exe\n");
launcherPath = "C:\\Windows\\explorer.exe"; launcherPath = L"C:\\Windows\\explorer.exe";
} }
// cd into the injector directory // cd into the injector directory
char injectorPath[MAX_PATH]; wchar_t injectorPath[MAX_PATH];
GetModuleFileNameA(GetModuleHandleA(NULL), injectorPath, sizeof(injectorPath)); GetModuleFileNameW(GetModuleHandleW(NULL), injectorPath, MAX_PATH);
*(strrchr(injectorPath, '\\')) = '\0'; *(wcsrchr(injectorPath, L'\\')) = L'\0';
SetCurrentDirectoryA(injectorPath); SetCurrentDirectoryW(injectorPath);
// Compute absolute paths // Compute absolute paths
char gameExePath[MAX_PATH]; wchar_t gameExePath[MAX_PATH];
GetFullPathNameA(gamePath, sizeof(gameExePath), gameExePath, NULL); GetFullPathNameW(gamePath, MAX_PATH, gameExePath, NULL);
char gamePayloadPath[MAX_PATH]; wchar_t gamePayloadPath[MAX_PATH];
GetFullPathNameA(GAME_INJECT_DLL, sizeof(gamePayloadPath), gamePayloadPath, NULL); GetFullPathNameW(GAME_INJECT_DLL, MAX_PATH, gamePayloadPath, NULL);
char launcherPayloadPath[MAX_PATH]; wchar_t launcherPayloadPath[MAX_PATH];
GetFullPathNameA(LAUNCHER_INJECT_DLL, sizeof(launcherPayloadPath), launcherPayloadPath, NULL); GetFullPathNameW(LAUNCHER_INJECT_DLL, MAX_PATH, launcherPayloadPath, NULL);
// Construct commandline for the game process // Construct commandline for the game process
char cmdline[8192]; wchar_t cmdline[8192];
sprintf(cmdline, "\"%s\"", gameExePath); wsprintfW(cmdline, L"\"%ls\"", gameExePath);
while (argc) { while (argc) {
char arg[8192]; wchar_t arg[8192];
sprintf(arg, " \"%s\"", argv[0]); wsprintfW(arg, L" \"%ls\"", argv[0]);
strcat(cmdline, arg); wcscat(cmdline, arg);
SHIFT(argc, argv); SHIFT(argc, argv);
} }
// Set envvars // Set envvars
SetEnvironmentVariableA(ENV_EXE_PATH, gameExePath); SetEnvironmentVariableW(ENV_EXE_PATH, gameExePath);
SetEnvironmentVariableA(ENV_DLL_PATH, gamePayloadPath); SetEnvironmentVariableW(ENV_DLL_PATH, gamePayloadPath);
SetEnvironmentVariableA(ENV_PROC_CMD, cmdline); SetEnvironmentVariableW(ENV_PROC_CMD, cmdline);
// Start the launcher // Start the launcher
printf("Starting '%s' via '%s'\n", gameExePath, launcherPath); wprintf(L"Starting '%ls' via '%ls'\n", gameExePath, launcherPath);
STARTUPINFO si; STARTUPINFOW si;
ZeroMemory(&si, sizeof(si)); ZeroMemory(&si, sizeof(si));
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
si.cb = sizeof(si); si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessA( if (!CreateProcessW(
launcherPath, launcherPath,
NULL, NULL,
NULL, NULL,
@ -102,11 +102,11 @@ int main(int argc, char **argv) {
&si, &si,
&pi &pi
)) { )) {
fprintf(stderr, "Could not start process! (%ld)\n", GetLastError()); fwprintf(stderr, L"Could not start process! (%ld)\n", GetLastError());
exit(1); exit(1);
} }
printf("Started launcher process (%ld)\n", pi.dwProcessId); wprintf(L"Started launcher process (%ld)\n", pi.dwProcessId);
// Inject // Inject
void *payloadStart = &_binary_launcher_p_o_p_launcher_p_bin_start; void *payloadStart = &_binary_launcher_p_o_p_launcher_p_bin_start;

View File

@ -17,11 +17,11 @@ main: ; Replacement entry point
mov rcx, rsi ; kernel32.dll mov rcx, rsi ; kernel32.dll
lea rdx, [rel s_LoadLibraryA] lea rdx, [rel s_LoadLibraryW]
call rdi ; rax = *LoadLibraryA call rdi ; rax = *LoadLibraryW
lea rcx, [rel dllPath] lea rcx, [rel dllPath]
call rax ; LoadLibraryA(dllPath) call rax ; LoadLibraryW(dllPath)
mov rcx, rsi ; kernel32.dll mov rcx, rsi ; kernel32.dll
@ -67,7 +67,7 @@ main: ; Replacement entry point
; Strings ; Strings
s_LoadLibraryA: db "LoadLibraryA", 0 s_LoadLibraryW: db "LoadLibraryW", 0
s_GetModuleHandleA: db "GetModuleHandleA", 0 s_GetModuleHandleA: db "GetModuleHandleA", 0
s_GetCommandLineW: db "GetCommandLineW", 0 s_GetCommandLineW: db "GetCommandLineW", 0
s_UnityPlayer.dll: db "UnityPlayer.dll", 0 s_UnityPlayer.dll: db "UnityPlayer.dll", 0

View File

@ -10,11 +10,11 @@ static inline void write_protected_process_memory(HANDLE process, void *address,
VirtualProtectEx(process, address, size, oldProtect, &oldProtect); VirtualProtectEx(process, address, size, oldProtect, &oldProtect);
} }
void inject(HANDLE process, const void *payload, size_t payloadSize, const char *dllPath) { void inject(HANDLE process, const void *payload, size_t payloadSize, const wchar_t *dllPath) {
size_t _; // Contrary to the docs, {Write,Read}ProcessMemory likes to crash if the last arg is NULL size_t _; // Contrary to the docs, {Write,Read}ProcessMemory likes to crash if the last arg is NULL
// Inject the loader into the module // Inject the loader into the module
size_t dllPathLen = strlen(dllPath) + 1; size_t dllPathLen = (wcslen(dllPath) + 1) * sizeof(wchar_t);
char *remoteAlloc = VirtualAllocEx(process, NULL, payloadSize + dllPathLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE); char *remoteAlloc = VirtualAllocEx(process, NULL, payloadSize + dllPathLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(process, remoteAlloc, payload, payloadSize, &_); WriteProcessMemory(process, remoteAlloc, payload, payloadSize, &_);

View File

@ -12,8 +12,8 @@ main: ; Replacement entry point
mov rcx, rsi ; kernel32.dll mov rcx, rsi ; kernel32.dll
lea rdx, [rel s_LoadLibraryA] lea rdx, [rel s_LoadLibraryW]
call rax ; rax = *LoadLibraryA call rax ; rax = *LoadLibraryW
lea rcx, [rel dllPath] lea rcx, [rel dllPath]
call rax ; LoadLibraryA(dllPath) call rax ; LoadLibraryA(dllPath)
@ -27,7 +27,7 @@ main: ; Replacement entry point
; Strings ; Strings
s_LoadLibraryA: db "LoadLibraryA", 0 s_LoadLibraryW: db "LoadLibraryW", 0
dllPath: dllPath:
; This will be filled out by the injector ; This will be filled out by the injector

View File

@ -1,4 +1,4 @@
project('jadeite', 'c', version: '1.1.5') project('jadeite', 'c', version: '1.1.7')
nasm = find_program('nasm') nasm = find_program('nasm')
gen_res = find_program('gen_resources.sh') gen_res = find_program('gen_resources.sh')

View File

@ -1,6 +1,6 @@
{ {
"jadeite": { "jadeite": {
"version": "1.1.5" "version": "1.1.7"
}, },
"games": { "games": {
"hi3rd": { "hi3rd": {