Major injector refactoring

This commit is contained in:
mkrsym1
2023-06-25 12:32:19 +03:00
parent 55fd21feef
commit a0e79dcea0
11 changed files with 60 additions and 55 deletions

74
injector/src/dll.c Normal file
View 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;
}

View File

@ -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
View 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
View 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));
}

View File

@ -28,7 +28,7 @@ main: ; Replacement entry point
ret
%include "gpa.inc"
%include "gpa.asm"
; Strings