16 Commits

19 changed files with 207 additions and 247 deletions

View File

@ -1,4 +1,11 @@
#!/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"
@ -6,12 +13,12 @@ rm -f jadeite.zip
rm -rf out
sh setup.sh --buildtype=release
ninja -C build
meson compile -C build
mkdir out
cp ./build/injector/jadeite.exe ./out
cp ./build/injector/launcher_payload/launcher_payload.dll ./out
cp ./build/injector/launcher_payload.dll ./out
cp ./build/game_payload/game_payload.dll ./out
cp ./LICENSE.txt ./out

Binary file not shown.

View File

@ -17,7 +17,7 @@ const struct crc_id_pair HI3_REGIONS[] = {
// Only glb for now
// It may be possible to get rid of region-specific data altogether in the future
{ 0x34bdec99, GAME_HI3_GLB } // glb v6.6.0
{ 0x45221647, GAME_HI3_GLB } // glb v6.6.0
};
void hi3_fill_data(struct game_data *buf) {
@ -31,7 +31,7 @@ void hi3_fill_data(struct game_data *buf) {
}
if (id == GAME_INVALID) {
msg_err_a("Invalid UnityPlayer.dll checksum: %d", crc);
msg_err_a("Invalid UnityPlayer.dll checksum: %x", crc);
}
buf->id = id;

View File

@ -2,9 +2,6 @@
#include <msg.h>
#include <main.h>
#include <crc32.h>
#include <pe.h>
#include <game.h>
const char *HSR_NAME = "StarRail";
@ -52,8 +49,9 @@ static void _unityplayer_callback(HMODULE unityModule) {
}
// Remove dependency on shared resources by patching WriteTextureStatisticUserData
wtsud_patch_addr = ((char*)unityModule) + 0x16430;
unload_ctr_inc();
wtsud_patch_addr = ((char*)unityModule) + WTSUD_PATCH_OFFSET;
DWORD oldProtect;
VirtualProtect(wtsud_patch_addr, JUMP_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
@ -89,7 +87,7 @@ void hsr_fill_data(struct game_data *buf) {
}
if (id == GAME_INVALID) {
msg_err_a("Invalid UnityPlayer.dll checksum: %d", crc);
msg_err_a("Invalid UnityPlayer.dll checksum: %x", crc);
}
buf->id = id;
@ -98,6 +96,5 @@ void hsr_fill_data(struct game_data *buf) {
buf->tp6_section_name = HSR_TP6_SECTION_NAME;
buf->tvm_section_name = HSR_TVM_SECTION_NAME;
unload_ctr_inc();
buf->unityplayer_callback = &_unityplayer_callback;
}

View File

@ -1,5 +1,3 @@
#include <stdint.h>
#include <pe.h>
void pe_find_section(HMODULE module, const char *section, MEMORY_BASIC_INFORMATION *buf) {
@ -8,11 +6,11 @@ void pe_find_section(HMODULE module, const char *section, MEMORY_BASIC_INFORMATI
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)module;
IMAGE_NT_HEADERS64* ntHeaders = (IMAGE_NT_HEADERS64*)(cModule + dosHeader->e_lfanew);
uint16_t sectionCount = ntHeaders->FileHeader.NumberOfSections;
WORD sectionCount = ntHeaders->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)(ntHeaders + 1);
void* targetAddress = 0x0;
for (uint16_t i = 0; i < sectionCount; i++) {
for (WORD i = 0; i < sectionCount; i++) {
if (strncmp((char*)sectionHeader->Name, section, 8) == 0) {
targetAddress = (void*)(cModule + sectionHeader->VirtualAddress);
break;

7
injector/include/envs.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#define EPFX "__JADEITE_"
#define ENV_EXE_PATH EPFX"TARGET_EXE_PATH"
#define ENV_DLL_PATH EPFX"INJECT_DLL_PATH"
#define ENV_PROC_CMD EPFX"PROCESS_COMMAND"

View File

@ -1,33 +1,5 @@
BITS 64
main: ; Replacement entry point
push rbp
mov rbp, rsp
sub rsp, 10h + 90h
call GetKernel32ModuleHandle
mov [rbp - 8h], rax ; kernel32.dll
mov rcx, rax
call GetAddressOf_GetProcAddress
mov [rbp - 10h], rax ; *GetProcAddress
mov rcx, [rbp - 8h] ; kernel32.dll
lea rdx, [rel s_LoadLibraryA]
mov rax, [rbp - 10h] ; *GetProcAddress
call rax ; rax = *LoadLibraryA
lea rcx, [rel dllPath]
call rax ; LoadLibraryA(dllPath)
add rsp, 10h + 90h
pop rbp
ret
; https://dennisbabkin.com/blog/?t=how-to-implement-getprocaddress-in-shellcode
GetKernel32ModuleHandle:
mov rax, gs:[60h]
@ -54,15 +26,15 @@ GetAddressOf_GetProcAddress:
mov r10, 41636f7250746547h ; "GetProcA"
mov r11, 0073736572646441h ; "Address\0"
GAO_GPA@1:
.1:
mov r9d, [r8]
lea r9, [rcx + r9]
; Function name comparision
cmp r10, [r9]
jnz GAO_GPA@2
jnz .2
cmp r11, [r9 + 7]
jnz GAO_GPA@2
jnz .2
; Found GetProcAddress
neg rdx
@ -79,20 +51,12 @@ GAO_GPA@1:
mov r10d, [r10 + rdx * 4]
lea rax, [rcx + r10] ; Function address
jmp GAO_GPA@end
jmp .end
GAO_GPA@2:
.2:
add r8, 4
dec rdx
jnz GAO_GPA@1
jnz .1
GAO_GPA@end:
ret
; Strings
s_LoadLibraryA: db "LoadLibraryA", 0
dllPath:
; This will be filled out by the injector
; Path to the dll to inject into the launcher
.end:
ret

View File

@ -0,0 +1,5 @@
#pragma once
#include <windows.h>
void inject(HANDLE process, const void *payload, size_t payloadSize, const char *dllPath);

View File

@ -1,18 +0,0 @@
# Assemble the payload that will be injected into the game
l_payload_bin = asm_gen.process('src/payload.asm')
# Embed it into the library
l_res_files = custom_target(
'lpayload.[oh]',
output: [ 'lpayload.o', 'lpayload.h' ],
input: [ l_payload_bin ],
command: [ gen_res, './injector/launcher_payload', '@OUTPUT0@', '@OUTPUT1@', '@INPUT@' ]
)
shared_library(
'launcher_payload',
'src/dll.c',
l_res_files,
include_directories: '../include',
name_prefix: ''
)

View File

@ -1,137 +0,0 @@
BITS 64
main: ; Replacement entry point
push rbp
mov rbp, rsp
sub rsp, 30h + 90h
call GetKernel32ModuleHandle
mov [rbp - 8h], rax ; kernel32.dll
mov rcx, rax
call GetAddressOf_GetProcAddress
mov [rbp - 10h], rax ; *GetProcAddress
mov rcx, [rbp - 8h] ; kernel32.dll
lea rdx, [rel s_LoadLibraryA]
mov rax, [rbp - 10h] ; *GetProcAddress
call rax ; rax = *LoadLibraryA
lea rcx, [rel dllPath]
call rax ; LoadLibraryA(dllPath)
mov rcx, [rbp - 8h] ; kernel32.dll
lea rdx, [rel s_GetModuleHandleA]
mov rax, [rbp - 10h] ; *GetProcAddress
call rax ; rax = *GetModuleHandle
mov [rbp - 18h], rax
mov rcx, 0
call rax ; rax = .exe base address
mov [rbp - 20h], rax
mov rcx, [rbp - 8h] ; kernel32.dll
lea rdx, [rel s_GetCommandLineW]
mov rax, [rbp - 10h] ; *GetProcAddress
call rax ; rax = *GetCommandLineW
call rax ; rax = command line
mov [rbp - 28h], rax
lea rcx, [rel s_UnityPlayer.dll]
mov rax, [rbp - 18h] ; *GetModuleHandleA
call rax ; rax = UnityPlayer.dll
mov rcx, rax
lea rdx, [rel s_UnityMain]
mov rax, [rbp - 10h] ; *GetProcAddress
call rax ; rax = *UnityMain
mov rcx, [rbp - 20h] ; .exe base address
mov rdx, 0 ; hPrevInstance - 0
mov r8, [rbp - 28h] ; command line
mov r9, 1 ; SW_NORMAL
call rax ; UnityMain(...)
add rsp, 30h + 90h
pop rbp
ret
; https://dennisbabkin.com/blog/?t=how-to-implement-getprocaddress-in-shellcode
GetKernel32ModuleHandle:
mov rax, gs:[60h]
mov rax, [rax + 18h]
mov rax, [rax + 20h]
mov rax, [rax]
mov rax, [rax]
mov rax, [rax + 20h]
ret
GetAddressOf_GetProcAddress:
mov eax, [rcx + 3ch]
add rax, rcx
lea rax, [rax + 88h]
mov edx, [rax]
lea rax, [rcx + rdx]
mov edx, [rax + 18h]
mov r8d, [rax + 20h]
lea r8, [rcx + r8]
mov r10, 41636f7250746547h ; "GetProcA"
mov r11, 0073736572646441h ; "Address\0"
GAO_GPA@1:
mov r9d, [r8]
lea r9, [rcx + r9]
; Function name comparision
cmp r10, [r9]
jnz GAO_GPA@2
cmp r11, [r9 + 7]
jnz GAO_GPA@2
; Found GetProcAddress
neg rdx
mov r10d, [rax + 18h]
lea rdx, [r10 + rdx]
mov r10d, [rax + 24h]
lea r10, [rcx + r10]
movzx rdx, word [r10 + rdx * 2]
mov r10d, [rax + 1ch]
lea r10, [rcx + r10]
mov r10d, [r10 + rdx * 4]
lea rax, [rcx + r10] ; Function address
jmp GAO_GPA@end
GAO_GPA@2:
add r8, 4
dec rdx
jnz GAO_GPA@1
GAO_GPA@end:
ret
; Strings
s_LoadLibraryA: db "LoadLibraryA", 0
s_GetModuleHandleA: db "GetModuleHandleA", 0
s_GetCommandLineW: db "GetCommandLineW", 0
s_UnityPlayer.dll: db "UnityPlayer.dll", 0
s_UnityMain: db "UnityMain", 0
dllPath:
; This will be filled out by the launcher payload dll
; Path to the dll to inject into the game

View File

@ -1,21 +1,48 @@
# Assemble the payload that will be injected into the launcher
inj_payload_bin = asm_gen.process('src/payload.asm')
include_dir = include_directories('include')
str_include_dir = join_paths(meson.current_source_dir(), 'include')
# Embed it into the library
inj_res_files = custom_target(
'ipayload.[oh]',
output: [ 'ipayload.o', 'ipayload.h' ],
input: [ inj_payload_bin ],
# Assemble the payloads
launcher_payload_bin = asm_gen.process(
'src/launcher_p.asm',
extra_args: [ '-i', str_include_dir ]
)
game_payload_bin = asm_gen.process(
'src/game_p.asm',
extra_args: [ '-i', str_include_dir ]
)
# Embed them into .o files
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@' ]
)
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@' ]
)
# Main injector exe
executable(
'jadeite',
'src/injector.c',
inj_res_files,
include_directories: 'include',
'src/exe.c',
'src/inject.c',
exe_res_files,
include_directories: include_dir,
name_prefix: ''
)
subdir('launcher_payload')
# Dll that will be injected into the launcher
shared_library(
'launcher_payload',
'src/dll.c',
'src/inject.c',
dll_res_files,
include_directories: include_dir,
name_prefix: ''
)

View File

@ -1,8 +1,9 @@
#include <stdio.h>
#include <injshared.h>
#include <inject.h>
#include <envs.h>
#include <lpayload.h>
#include <game_p.h>
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
// Only listen for attach
@ -52,8 +53,8 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
}
// Inject
void *payloadStart = &_binary_lpayload_o_p_payload_bin_start;
size_t payloadSize = (size_t)&_binary_lpayload_o_p_payload_bin_size;
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);
// Optional: wait for user input before resuming (useful for debugging)

View File

@ -1,8 +1,9 @@
#include <stdio.h>
#include <injshared.h>
#include <inject.h>
#include <envs.h>
#include <ipayload.h>
#include <launcher_p.h>
const char LAUNCHER_INJECT_DLL[] = "launcher_payload.dll";
const char GAME_INJECT_DLL[] = "game_payload.dll";
@ -108,8 +109,8 @@ int main(int argc, char **argv) {
printf("Started launcher process (%ld)\n", pi.dwProcessId);
// Inject
void *payloadStart = &_binary_ipayload_o_p_payload_bin_start;
size_t payloadSize = (size_t)&_binary_ipayload_o_p_payload_bin_size; // yes this is valid
void *payloadStart = &_binary_launcher_p_o_p_launcher_p_bin_start;
size_t payloadSize = (size_t)&_binary_launcher_p_o_p_launcher_p_bin_size; // yes this is valid
inject(pi.hProcess, payloadStart, payloadSize, launcherPayloadPath);
// Resume the process

78
injector/src/game_p.asm Normal file
View File

@ -0,0 +1,78 @@
BITS 64
main: ; Replacement entry point
push rsi
push rdi
push r12
push r13
push r14
call GetKernel32ModuleHandle
mov rsi, rax ; kernel32.dll
mov rcx, rax
call GetAddressOf_GetProcAddress
mov rdi, rax ; *GetProcAddress
mov rcx, rsi ; kernel32.dll
lea rdx, [rel s_LoadLibraryA]
call rdi ; rax = *LoadLibraryA
lea rcx, [rel dllPath]
call rax ; LoadLibraryA(dllPath)
mov rcx, rsi ; kernel32.dll
lea rdx, [rel s_GetModuleHandleA]
call rdi ; rax = *GetModuleHandle
mov r12, rax
mov rcx, 0
call rax ; rax = .exe base address
mov r13, rax
mov rcx, rsi ; kernel32.dll
lea rdx, [rel s_GetCommandLineW]
call rdi ; rax = *GetCommandLineW
call rax ; rax = command line
mov r14, rax
lea rcx, [rel s_UnityPlayer.dll]
call r12 ; rax = UnityPlayer.dll
mov rcx, rax
lea rdx, [rel s_UnityMain]
call rdi ; rax = *UnityMain
mov rcx, r13 ; .exe base address
mov rdx, 0 ; hPrevInstance - 0
mov r8, r14 ; command line
mov r9, 1 ; SW_NORMAL
call rax ; UnityMain(...)
pop r14
pop r13
pop r12
pop rdi
pop rsi
ret
%include "gpa.asm"
; Strings
s_LoadLibraryA: db "LoadLibraryA", 0
s_GetModuleHandleA: db "GetModuleHandleA", 0
s_GetCommandLineW: db "GetCommandLineW", 0
s_UnityPlayer.dll: db "UnityPlayer.dll", 0
s_UnityMain: db "UnityMain", 0
dllPath:
; This will be filled out by the launcher payload dll
; Path to the dll to inject into the game

View File

@ -1,10 +1,4 @@
#include <windows.h>
#define EPFX "__JADEITE_"
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";
#include <inject.h>
static inline void write_protected_process_memory(HANDLE process, void *address, const void *buf, size_t size) {
DWORD oldProtect;
@ -16,8 +10,8 @@ static inline void write_protected_process_memory(HANDLE process, void *address,
VirtualProtectEx(process, address, size, oldProtect, &oldProtect);
}
static inline void inject(HANDLE process, const void *payload, size_t payloadSize, const char *dllPath) {
size_t _;
void inject(HANDLE process, const void *payload, size_t payloadSize, const char *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;
@ -87,7 +81,8 @@ static inline void inject(HANDLE process, const void *payload, size_t payloadSiz
write_protected_process_memory(process, importDescriptors, &firstDescriptor, sizeof(firstDescriptor));
// Step 2: break the image data directory entry
ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = 0;
size_t ddOffset = ((char*)&(ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)) - exeHeader;
DWORD newSize = 0;
write_protected_process_memory(process, exe, exeHeader, sizeof(exeHeader));
write_protected_process_memory(process, exe + ddOffset, &newSize, sizeof(newSize));
}

View File

@ -0,0 +1,34 @@
BITS 64
main: ; Replacement entry point
push rsi
call GetKernel32ModuleHandle
mov rsi, rax ; kernel32.dll
mov rcx, rax
call GetAddressOf_GetProcAddress
mov rcx, rsi ; kernel32.dll
lea rdx, [rel s_LoadLibraryA]
call rax ; rax = *LoadLibraryA
lea rcx, [rel dllPath]
call rax ; LoadLibraryA(dllPath)
pop rsi
ret
%include "gpa.asm"
; Strings
s_LoadLibraryA: db "LoadLibraryA", 0
dllPath:
; This will be filled out by the injector
; Path to the dll to inject into the launcher

View File

@ -1,4 +1,4 @@
project('jadeite', 'c', version: '1.1.3')
project('jadeite', 'c', version: '1.1.5')
nasm = find_program('nasm')
gen_res = find_program('gen_resources.sh')
@ -8,6 +8,7 @@ asm_gen = generator(
nasm,
output: '@BASENAME@.bin',
arguments: [
'@EXTRA_ARGS@',
'-f', 'bin',
'@INPUT@',
'-o', '@OUTPUT@'

View File

@ -1,12 +1,12 @@
{
"jadeite": {
"version": "1.1.3"
"version": "1.1.5"
},
"games": {
"hi3rd": {
"global": {
"status": "verified",
"version": "6.6.0"
"version": "6.7.0"
}
},
"hsr": {