20 Commits

Author SHA1 Message Date
c979c980c1 v1.1.11 2023-07-09 00:03:31 +03:00
3d943b641b Fix additional issue introduced by previous rework 2023-07-09 00:01:48 +03:00
215af6e3db v1.1.10 2023-07-08 23:44:25 +03:00
7aef85851c Fixed subtle core patch bug introduced by previous rework 2023-07-08 23:43:34 +03:00
f7c24f5ab7 v1.1.9 2023-07-08 21:24:17 +03:00
2ebd40b621 Internal rework to avoid a possible edge case 2023-07-08 21:17:28 +03:00
7f43a042e8 v1.1.8 2023-07-08 16:34:12 +03:00
8164694c8f Point user to Jadeite Readme 2023-07-08 15:11:52 +02:00
61e657b2e4 Readme maintenance 2023-07-07 14:14:28 +03:00
7a8087e8a1 Refactor resource gen script 2023-07-07 13:59:28 +03:00
351fe85e2f Move blob file to objects kwarg in target declaration 2023-07-06 00:04:41 +03:00
938b663bd9 Change blob file extension 2023-07-06 00:02:17 +03:00
df1f611199 Publish precompiled tp6.c blob (lots of dirty hacks) 2023-07-05 23:59:27 +03:00
94efee7496 Don't use narrow characters with widestring functions 2023-07-04 01:08:03 +03:00
06be53ad8e Fix an incredibly dumb bitflag error 2023-07-04 01:03:21 +03:00
ce427556a3 More efficient string comparisions 2023-07-03 17:04:32 +03:00
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
19 changed files with 201 additions and 128 deletions

View File

@ -5,7 +5,7 @@
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.
### Information
The anticheat the games use is fundamentally incompatible with Wine in multiple ways. This tool launches the game without it (`injector/launcher_payload`) and imitates it's behaviour (`game_payload`).
The anticheat the games use is fundamentally incompatible with Wine in multiple ways. This tool launches the game without it (`injector`) and imitates it's behaviour (`game_payload`).
**SR-specific**: this tool disables the use of DirectX shared resources in a rather hacky way. It is required, as there is no (and most likely never will be) shared resources support in DirectX translation layers (WineD3D/DXVK). Refer to [configuration](#configuration) if you wish to run the game without the fix.
@ -18,7 +18,7 @@ The anticheat the games use is fundamentally incompatible with Wine in multiple
**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.**
**3rd-specific**: In some cases, and if you're not using Proton GE, **a fix for Media Foundation may be required to play videos. The Game may crash without it.** You can download it from [here](https://github.com/z0z0z/mf-install). You might need to [limit the number of cores available to the game](https://github.com/z0z0z/mf-install/issues/44) if your CPU has more than 8.
**3rd-specific**: In some cases, and if you're not using Proton GE, **a fix for Media Foundation may be required to play videos. The Game may crash without it.** You can download it from [here](https://github.com/z0z0z/mf-install). You might need to [limit the number of cores available to the game](https://github.com/z0z0z/mf-install/issues/44) if your CPU has more than 8. **IMPORTANT: do not run the mfplat fix under Proton GE. Doing so may irreparably damage your game installation!**
Manual usage instructions:
- Download the game you want to run
@ -32,20 +32,20 @@ This tool is capable of starting the games from a different process. This may be
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
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. Any value except empty string counts as set. `1` will be used in all examples.
- `WAIT_BEFORE_RESUME=1` - show a messagebox and wait for user input before resuming the game process. Useful on my side for debugging
**SR-exclusive**:
- `I_WANT_A_BAN=1` - allows to launch SR. Please only use testing accounts, as there is an extremely high risk of getting banned
- `SRFIX_DISABLE=1` - disable shared resources fix
- `SRFIX_DISABLE=1` - disable shared resources fix. Not recommended. Doing so will most likely cause the game to not run at all
### Internals
This tool consists of three parts: the main injector (`injector`), the launcher payload (`injector/launcher_payload`) and the game payload (`game_payload`).
This tool consists of three parts: the main injector (`injector/src/exe.c`), the launcher payload (`injector/src/dll.c`) and the game payload (`game_payload`).
I am very bad at explaining, so just take a look at the source code. Maybe I'll write a detailed explanation in the future.
A part of the source code is witheld (`game_payload/src/tp6.c`). This is a forced measure to make abuse more difficult.
A part of the source code is witheld (`game_payload/src/tp6.c`). This is a forced measure to make abuse more difficult. However, a precompiled blob is provided in the repo. `build.sh` will use it automatically.
### Guildelines
1. **Please don't share this project in public.** This might attract unnecessary attention from either the Game Company or the Anticheat Company
@ -60,7 +60,8 @@ Please do not report any issues with the Game to the official channels. Use the
### Credits
- 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
License: MIT

View File

@ -1,12 +1,6 @@
#!/usr/bin/env bash
set -e
if ! [ "x$1" = "xdo" ]; then
echo "A part of the source code is witheld (game_payload/src/tp6.c) to make abuse more difficult. Please download a binary release"
exit
fi
shift
strip="x86_64-w64-mingw32-strip"
rm -f jadeite.zip

BIN
game_payload/blob/tp6c.o Normal file

Binary file not shown.

View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
cp "$1" "$2"
cp "$1" "$3"

View File

@ -1,3 +1,7 @@
fs = import('fs')
include_dir = include_directories('include')
# Input files
sources = [
'src/main.c',
@ -8,10 +12,7 @@ sources = [
'src/hi3.c',
'src/hsr.c',
'src/utils.c',
'src/msg.c',
# File withheld to make abuse more difficult
'src/tp6.c'
'src/msg.c'
]
resources = [
'res/hi3/glb/allocations.dat',
@ -24,17 +25,56 @@ resources = [
]
# Generate resource files for ./res
res_files = custom_target(
'resources.[ho]',
output: [ 'resources.o', 'resources.h' ],
res_header = custom_target(
'resources.h',
output: 'resources.h',
input: resources,
command: [ gen_res, meson.current_source_dir(), '@OUTPUT0@', '@OUTPUT1@', '@INPUT@' ]
command: [ gen_res, '--header', meson.current_source_dir(), '@OUTPUT0@', '@INPUT@' ]
)
res_object = custom_target(
'resources.o',
output: 'resources.o',
input: resources,
command: [ gen_res, '--object', meson.current_source_dir(), '@OUTPUT0@', '@INPUT@' ]
)
if fs.exists('src/tp6.c')
# Compile the real file first (dirty hack)
tp6c_fake_exe = executable(
'tp6c.o',
'src/tp6.c',
res_header,
link_args: [ '-r' ], # Output an object file
include_directories: include_dir
)
# another dirty hack
copy_tp6c = find_program('copy_tp6c.sh')
tp6c_target = [custom_target(
'copy_tp6c',
output: 'tp6c.o',
input: tp6c_fake_exe.extract_all_objects(recursive: false),
command: [
copy_tp6c,
'@INPUT0@',
'@OUTPUT0@', meson.current_source_dir() / 'blob/tp6c.o'
]
)]
tp6c_blob = []
else
message('Using precompiled tp6c blob. Refer to the readme for more details')
tp6c_target = []
tp6c_blob = [ 'blob/tp6c.o' ]
endif
shared_library(
'game_payload',
sources,
res_files,
include_directories: 'include',
res_header,
res_object,
tp6c_target,
objects: tp6c_blob,
include_directories: include_dir,
name_prefix: ''
)

View File

@ -72,7 +72,7 @@ static void _unityplayer_callback(HMODULE unityModule) {
void hsr_fill_data(struct game_data *buf) {
if (!utils_env_enabled("I_WANT_A_BAN")) {
msg_err_a("Using this tool with HSR is unsafe. Refer to the readme for more details");
msg_err_a("Using this tool with HSR is unsafe. Refer to the readme for more details: https://codeberg.org/mkrsym1/jadeite");
} else {
msg_warn_a("Using this tool with HSR will most likely result in a ban. Please only use testing accounts");
}

View File

@ -3,3 +3,12 @@
### 1.1.0
- HSR support
### 1.1.9
- Fixed a bug which could cause the game to crash in odd scenarios
### 1.1.10
- Fixed a subtle bug introduced in 1.1.9
### 1.1.11
- Fixed an additional issue introduced in 1.1.9

View File

@ -31,5 +31,5 @@ uint32_t utils_file_crc32c(const char *filePath) {
char utils_env_enabled(const char *env) {
char *envText = getenv(env);
return envText && strcmp(envText, "") != 0;
return envText && *envText;
}

View File

@ -2,19 +2,38 @@
linker="x86_64-w64-mingw32-ld"
# Select output types
for i in {0..1}
do
case "$1" in
--header)
gen_header=1
shift
;;
--object)
gen_object=1
shift
;;
esac
done
# Read project directory
proj_dir=`realpath "$1"`
shift
# Read output file destinations
# Read output file destinations and make sure they don't exist
if [ "x${gen_object}" = "x1" ]; then
resources_o=`realpath "$1"`
shift
rm -f "${resources_h}"
fi
if [ "x${gen_header}" = "x1" ]; then
resources_h=`realpath "$1"`
shift
# Make sure that the header does not exist
rm -f "${resources_h}"
rm -f "${resources_o}"
fi
# Recomupte relative paths to parameters
idx=0
@ -26,11 +45,14 @@ do
idx="$(("${idx}" + 1))"
done
if [ "x${gen_object}" = "x1" ]; then
# Create the object file
pushd "${proj_dir}" >> /dev/null
$linker -r -b binary -o "${resources_o}" "${resource_files[@]}"
popd >> /dev/null
fi
if [ "x${gen_header}" = "x1" ]; then
# Include stddef.h in the resources header (for size_t)
echo "#include <stddef.h>" >> "${resources_h}"
@ -47,3 +69,4 @@ do
echo "extern void *${var_name}_size;" >> "${resources_h}"
echo "" >> "${resources_h}"
done
fi

View File

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

View File

@ -2,4 +2,4 @@
#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

@ -17,14 +17,14 @@ exe_res_files = custom_target(
'launcher_p.[oh]',
output: [ 'launcher_p.o', 'launcher_p.h' ],
input: [ launcher_payload_bin ],
command: [ gen_res, './injector', '@OUTPUT0@', '@OUTPUT1@', '@INPUT@' ]
command: [ gen_res, '--header', '--object', './injector', '@OUTPUT0@', '@OUTPUT1@', '@INPUT@' ]
)
dll_res_files = custom_target(
'game_p.[oh]',
output: [ 'game_p.o', 'game_p.h' ],
input: [ game_payload_bin ],
command: [ gen_res, './injector', '@OUTPUT0@', '@OUTPUT1@', '@INPUT@' ]
command: [ gen_res, '--header', '--object', './injector', '@OUTPUT0@', '@OUTPUT1@', '@INPUT@' ]
)
# Main injector exe
@ -34,7 +34,8 @@ executable(
'src/inject.c',
exe_res_files,
include_directories: include_dir,
name_prefix: ''
name_prefix: '',
link_args: '-municode'
)
# Dll that will be injected into the launcher
@ -44,5 +45,6 @@ shared_library(
'src/inject.c',
dll_res_files,
include_directories: include_dir,
name_prefix: ''
name_prefix: '',
link_args: '-municode'
)

View File

@ -7,7 +7,7 @@
typedef char *(*wgufn_t)(wchar_t* path); // wine_get_unix_file_name
const char *J_MB_TITLE = "Jadeite Launcher Payload";
const wchar_t *J_MB_TITLE = L"Jadeite Launcher Payload";
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
// Only listen for attach
@ -16,37 +16,37 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
}
// 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
char *injectDll = getenv(ENV_DLL_PATH);
wchar_t injectDll[MAX_PATH];
GetEnvironmentVariableW(ENV_DLL_PATH, injectDll, MAX_PATH);
// 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
char workdir[MAX_PATH];
strcpy(workdir, targetExe);
*(strrchr(workdir, '\\')) = '\0';
wchar_t workdir[MAX_PATH];
wcscpy(workdir, targetExe);
*(wcsrchr(workdir, L'\\')) = L'\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) {
wchar_t wInjectDll[MAX_PATH], wWorkdir[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, injectDll, strlen(injectDll) + 1, wInjectDll, MAX_PATH);
MultiByteToWideChar(CP_UTF8, 0, workdir, strlen(workdir) + 1, wWorkdir, MAX_PATH);
char *unixInjectDll = wine_get_unix_file_name(wInjectDll);
char *unixWorkdir = wine_get_unix_file_name(wWorkdir);
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 (*unixInjectDll != '\0' && *unixWorkdir != '\0') {
startsWith = *unixInjectDll == *unixWorkdir;
while (*i && *w) {
startsWith = *i == *w;
if (!startsWith) break;
unixInjectDll++, unixWorkdir++;
i++, w++;
}
HANDLE heap = GetProcessHeap();
@ -54,22 +54,22 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
HeapFree(heap, 0, unixWorkdir);
if (startsWith) {
MessageBoxA(NULL, "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);
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 {
MessageBoxA(NULL, "Could not find wine_get_unix_file_name! Wine version too old?", J_MB_TITLE, MB_OK | MB_ICONWARNING);
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
STARTUPINFO si;
STARTUPINFOW si;
ZeroMemory(&si, sizeof(si));
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessA(
if (!CreateProcessW(
NULL,
cmdline,
NULL,
@ -81,9 +81,9 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
&si,
&pi
)) {
char message[64];
sprintf(message, "Failed to start game process: %ld", GetLastError());
MessageBoxA(NULL, message, J_MB_TITLE, MB_OK | MB_ICONERROR);
wchar_t message[1024];
wsprintfW(message, L"Failed to start game process: %ld", GetLastError());
MessageBoxW(NULL, message, J_MB_TITLE, MB_OK | MB_ICONERROR);
exit(1);
}
@ -95,10 +95,10 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
// Optional: wait for user input before resuming (useful for debugging)
char *waitEnabled = getenv("WAIT_BEFORE_RESUME");
if (waitEnabled && strcmp(waitEnabled, "") != 0) {
char message[64];
sprintf(message, "PID: %ld. Press OK to continue", pi.dwProcessId);
MessageBoxA(NULL, message, J_MB_TITLE, MB_OK | MB_ICONINFORMATION);
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);
}
// Resume the process

View File

@ -5,28 +5,28 @@
#include <launcher_p.h>
const char LAUNCHER_INJECT_DLL[] = "launcher_payload.dll";
const char GAME_INJECT_DLL[] = "game_payload.dll";
const wchar_t *LAUNCHER_INJECT_DLL = L"launcher_payload.dll";
const wchar_t *GAME_INJECT_DLL = L"game_payload.dll";
#define SHIFT(argc, argv) argc--, argv++
int main(int argc, char **argv) {
int wmain(int argc, wchar_t **argv) {
// Read arguments
char *gamePath = NULL;
char *launcherPath = NULL;
wchar_t *gamePath = NULL;
wchar_t *launcherPath = NULL;
// Skip executable
SHIFT(argc, argv);
switch (argc) {
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;
case 1:
gamePath = argv[0];
SHIFT(argc, argv);
launcherPath = "--";
launcherPath = L"--";
break;
default:
@ -40,57 +40,57 @@ int main(int argc, char **argv) {
}
// Default launcher path
if (strcmp(launcherPath, "--") == 0) {
printf("No launcher process specified! Using explorer.exe\n");
launcherPath = "C:\\Windows\\explorer.exe";
if (wcscmp(launcherPath, L"--") == 0) {
wprintf(L"No launcher process specified! Using explorer.exe\n");
launcherPath = L"C:\\Windows\\explorer.exe";
}
// cd into the injector directory
char injectorPath[MAX_PATH];
GetModuleFileNameA(GetModuleHandleA(NULL), injectorPath, sizeof(injectorPath));
wchar_t injectorPath[MAX_PATH];
GetModuleFileNameW(GetModuleHandleW(NULL), injectorPath, MAX_PATH);
*(strrchr(injectorPath, '\\')) = '\0';
*(wcsrchr(injectorPath, L'\\')) = L'\0';
SetCurrentDirectoryA(injectorPath);
SetCurrentDirectoryW(injectorPath);
// Compute absolute paths
char gameExePath[MAX_PATH];
GetFullPathNameA(gamePath, sizeof(gameExePath), gameExePath, NULL);
wchar_t gameExePath[MAX_PATH];
GetFullPathNameW(gamePath, MAX_PATH, gameExePath, NULL);
char gamePayloadPath[MAX_PATH];
GetFullPathNameA(GAME_INJECT_DLL, sizeof(gamePayloadPath), gamePayloadPath, NULL);
wchar_t gamePayloadPath[MAX_PATH];
GetFullPathNameW(GAME_INJECT_DLL, MAX_PATH, gamePayloadPath, NULL);
char launcherPayloadPath[MAX_PATH];
GetFullPathNameA(LAUNCHER_INJECT_DLL, sizeof(launcherPayloadPath), launcherPayloadPath, NULL);
wchar_t launcherPayloadPath[MAX_PATH];
GetFullPathNameW(LAUNCHER_INJECT_DLL, MAX_PATH, launcherPayloadPath, NULL);
// Construct commandline for the game process
char cmdline[8192];
sprintf(cmdline, "\"%s\"", gameExePath);
wchar_t cmdline[8192];
wsprintfW(cmdline, L"\"%ls\"", gameExePath);
while (argc) {
char arg[8192];
sprintf(arg, " \"%s\"", argv[0]);
strcat(cmdline, arg);
wchar_t arg[8192];
wsprintfW(arg, L" \"%ls\"", argv[0]);
wcscat(cmdline, arg);
SHIFT(argc, argv);
}
// Set envvars
SetEnvironmentVariableA(ENV_EXE_PATH, gameExePath);
SetEnvironmentVariableA(ENV_DLL_PATH, gamePayloadPath);
SetEnvironmentVariableA(ENV_PROC_CMD, cmdline);
SetEnvironmentVariableW(ENV_EXE_PATH, gameExePath);
SetEnvironmentVariableW(ENV_DLL_PATH, gamePayloadPath);
SetEnvironmentVariableW(ENV_PROC_CMD, cmdline);
// 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));
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessA(
if (!CreateProcessW(
launcherPath,
NULL,
NULL,
@ -102,11 +102,11 @@ int main(int argc, char **argv) {
&si,
&pi
)) {
fprintf(stderr, "Could not start process! (%ld)\n", GetLastError());
fwprintf(stderr, L"Could not start process! (%ld)\n", GetLastError());
exit(1);
}
printf("Started launcher process (%ld)\n", pi.dwProcessId);
wprintf(L"Started launcher process (%ld)\n", pi.dwProcessId);
// Inject
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
lea rdx, [rel s_LoadLibraryA]
call rdi ; rax = *LoadLibraryA
lea rdx, [rel s_LoadLibraryW]
call rdi ; rax = *LoadLibraryW
lea rcx, [rel dllPath]
call rax ; LoadLibraryA(dllPath)
call rax ; LoadLibraryW(dllPath)
mov rcx, rsi ; kernel32.dll
@ -67,7 +67,7 @@ main: ; Replacement entry point
; Strings
s_LoadLibraryA: db "LoadLibraryA", 0
s_LoadLibraryW: db "LoadLibraryW", 0
s_GetModuleHandleA: db "GetModuleHandleA", 0
s_GetCommandLineW: db "GetCommandLineW", 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);
}
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
// 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);
WriteProcessMemory(process, remoteAlloc, payload, payloadSize, &_);
@ -45,7 +45,7 @@ void inject(HANDLE process, const void *payload, size_t payloadSize, const char
}
// Skip DLLs
if ((ntHeaders->FileHeader.Characteristics | IMAGE_FILE_DLL) == IMAGE_FILE_DLL) {
if ((ntHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == IMAGE_FILE_DLL) {
goto cont;
}

View File

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

View File

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

View File

@ -1,6 +1,6 @@
{
"jadeite": {
"version": "1.1.6"
"version": "1.1.11"
},
"games": {
"hi3rd": {