Major injector refactoring
This commit is contained in:
74
injector/src/dll.c
Normal file
74
injector/src/dll.c
Normal file
@ -0,0 +1,74 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <inject.h>
|
||||
|
||||
#include <game_p.h>
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||
// Only listen for attach
|
||||
if (reason != DLL_PROCESS_ATTACH) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Get target EXE path
|
||||
char *targetExe = getenv(ENV_EXE_PATH);
|
||||
|
||||
// Get the path of the DLL to inject
|
||||
char *injectDll = getenv(ENV_DLL_PATH);
|
||||
|
||||
// Get game commandline
|
||||
char *cmdline = getenv(ENV_PROC_CMD);
|
||||
|
||||
// Compute the working directory path
|
||||
char workdir[MAX_PATH];
|
||||
strcpy(workdir, targetExe);
|
||||
*(strrchr(workdir, '\\')) = '\0';
|
||||
|
||||
// Start the game
|
||||
STARTUPINFO si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
if (!CreateProcessA(
|
||||
NULL,
|
||||
cmdline,
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE,
|
||||
CREATE_SUSPENDED,
|
||||
NULL,
|
||||
workdir,
|
||||
&si,
|
||||
&pi
|
||||
)) {
|
||||
char message[64];
|
||||
sprintf(message, "Failed to start game process: %ld", GetLastError());
|
||||
MessageBoxA(NULL, message, "Jadeite Launcher Payload", 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);
|
||||
|
||||
// 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, "Jadeite Launcher Payload", MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
// Resume the process
|
||||
ResumeThread(pi.hThread);
|
||||
|
||||
// The launcher process should now hang untill the game terminates
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <injshared.h>
|
||||
#include <inject.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 +108,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
78
injector/src/game_p.asm
Normal file
@ -0,0 +1,78 @@
|
||||
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
|
||||
|
||||
|
||||
%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
|
||||
88
injector/src/inject.c
Normal file
88
injector/src/inject.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include <inject.h>
|
||||
|
||||
static inline void write_protected_process_memory(HANDLE process, void *address, const void *buf, size_t size) {
|
||||
DWORD oldProtect;
|
||||
VirtualProtectEx(process, address, size, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
size_t bytesWritten;
|
||||
WriteProcessMemory(process, address, buf, size, &bytesWritten);
|
||||
|
||||
VirtualProtectEx(process, address, size, oldProtect, &oldProtect);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
char *remoteAlloc = VirtualAllocEx(process, NULL, payloadSize + dllPathLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
WriteProcessMemory(process, remoteAlloc, payload, payloadSize, &_);
|
||||
WriteProcessMemory(process, remoteAlloc + payloadSize, dllPath, dllPathLen, &_);
|
||||
|
||||
// Find the EXE header in the process
|
||||
char exeHeader[1024];
|
||||
IMAGE_DOS_HEADER *dosHeader = NULL;
|
||||
IMAGE_NT_HEADERS64 *ntHeaders = NULL;
|
||||
|
||||
MEMORY_BASIC_INFORMATION memoryInfo;
|
||||
char *currentAddress = 0x0;
|
||||
while (VirtualQueryEx(process, currentAddress, &memoryInfo, sizeof(memoryInfo))) {
|
||||
ReadProcessMemory(process, currentAddress, exeHeader, sizeof(exeHeader), &_);
|
||||
|
||||
dosHeader = (IMAGE_DOS_HEADER*)exeHeader;
|
||||
|
||||
// DOS header magic "MZ"
|
||||
if (dosHeader->e_magic != 0x5A4D) {
|
||||
goto cont;
|
||||
}
|
||||
|
||||
ntHeaders = (IMAGE_NT_HEADERS64*)(exeHeader + dosHeader->e_lfanew);
|
||||
|
||||
// NT header signature "PE"
|
||||
if (ntHeaders->Signature != 0x4550) {
|
||||
goto cont;
|
||||
}
|
||||
|
||||
// Skip DLLs
|
||||
if ((ntHeaders->FileHeader.Characteristics | IMAGE_FILE_DLL) == IMAGE_FILE_DLL) {
|
||||
goto cont;
|
||||
}
|
||||
|
||||
// Skip potential headers without an entry point
|
||||
// I have no idea how and why they exist, but apparently they do
|
||||
if (ntHeaders->OptionalHeader.AddressOfEntryPoint == 0) {
|
||||
goto cont;
|
||||
}
|
||||
|
||||
// Found EXE header
|
||||
break;
|
||||
|
||||
cont:
|
||||
currentAddress += memoryInfo.RegionSize;
|
||||
}
|
||||
|
||||
char *exe = (char*)memoryInfo.BaseAddress;
|
||||
|
||||
// Replace the entry point with a jump to the loader
|
||||
char *entryPoint = exe + ntHeaders->OptionalHeader.AddressOfEntryPoint;
|
||||
|
||||
const unsigned char JUMP_INST[] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
write_protected_process_memory(process, entryPoint, JUMP_INST, sizeof(JUMP_INST));
|
||||
write_protected_process_memory(process, entryPoint + sizeof(JUMP_INST), &remoteAlloc, sizeof(remoteAlloc));
|
||||
|
||||
// Break the import table to prevent any dlls from being loaded
|
||||
// Step 1: break the first import descriptor
|
||||
char *importDescriptors = exe + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
||||
IMAGE_IMPORT_DESCRIPTOR firstDescriptor;
|
||||
ZeroMemory(&firstDescriptor, sizeof(firstDescriptor));
|
||||
|
||||
write_protected_process_memory(process, importDescriptors, &firstDescriptor, sizeof(firstDescriptor));
|
||||
|
||||
// Step 2: break the image data directory entry
|
||||
size_t ddOffset = ((char*)&(ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)) - exeHeader;
|
||||
DWORD newSize = 0;
|
||||
|
||||
write_protected_process_memory(process, exe + ddOffset, &newSize, sizeof(newSize));
|
||||
}
|
||||
@ -28,7 +28,7 @@ main: ; Replacement entry point
|
||||
ret
|
||||
|
||||
|
||||
%include "gpa.inc"
|
||||
%include "gpa.asm"
|
||||
|
||||
|
||||
; Strings
|
||||
Reference in New Issue
Block a user