BOOL thcrap_inject_into_new(const char *exe_fn, char *args, const char *run_cfg_fn) { int ret = 0; json_t *run_cfg = json_load_file_report(run_cfg_fn); if(!run_cfg) { return 1; }; json_object_set_new(run_cfg, "run_cfg_fn", json_string(run_cfg_fn)); runconfig_set(run_cfg); { STRLEN_DEC(exe_fn); VLA(char, exe_dir_local, exe_fn_len); VLA(char, exe_fn_local, exe_fn_len); STARTUPINFOA si = {0}; PROCESS_INFORMATION pi = {0}; char *exe_dir = NULL; strcpy(exe_fn_local, exe_fn); str_slash_normalize_win(exe_fn_local); strcpy(exe_dir_local, exe_fn); if(PathRemoveFileSpec(exe_dir_local)) { exe_dir = exe_dir_local; } /** * Sure, the alternative would be to set up the entire engine * with all plug-ins and modules to correctly run any additional * detours. While it would indeed be nice to allow those to control * initial startup, it really shouldn't be necessary for now - and * it really does run way too much unnecessary code for my taste. */ ret = W32_ERR_WRAP(inject_CreateProcessU( exe_fn_local, args, NULL, NULL, TRUE, 0, NULL, exe_dir, &si, &pi )); if(ret) { char *msg_str = ""; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg_str, 0, NULL ); log_mboxf(NULL, MB_OK | MB_ICONEXCLAMATION, "Failed to start %s: %s", exe_fn, msg_str ); LocalFree(msg_str); } VLA_FREE(exe_fn_local); VLA_FREE(exe_dir_local); } return ret; }
int CreateShortcuts(const char *run_cfg_fn, json_t *games) { #ifdef _DEBUG const char *loader_exe = "thcrap_loader_d.exe"; #else const char *loader_exe = "thcrap_loader.exe"; #endif int ret = 0; size_t self_fn_len = GetModuleFileNameU(NULL, NULL, 0) + 1; VLA(char, self_fn, self_fn_len); GetModuleFileNameU(NULL, self_fn, self_fn_len); PathRemoveFileSpec(self_fn); PathAddBackslashA(self_fn); // Yay, COM. CoInitializeEx(NULL, COINIT_MULTITHREADED); { const char *key = NULL; json_t *cur_game = NULL; VLA(char, self_path, self_fn_len); strcpy(self_path, self_fn); strcat(self_fn, loader_exe); log_printf("Creating shortcuts"); json_object_foreach(games, key, cur_game) { const char *game_fn = json_string_value(cur_game); const char *link_fn = strings_sprintf(LINK_FN, "%s (%s).lnk", key, run_cfg_fn); const char *link_args = strings_sprintf(LINK_ARGS, "\"%s.js\" %s", run_cfg_fn, key); log_printf("."); if( CreateLink(link_fn, self_fn, link_args, self_path, game_fn) && !file_write_error(link_fn) ) { ret = 1; break; } } VLA_FREE(self_path); } VLA_FREE(self_fn); CoUninitialize(); return ret; }
int BP_gentext(x86_reg_t *regs, json_t *bp_info) { gentext_cache_t *gc = gc_tls_get(); // Parameters // ---------- json_t *strs = json_object_get(bp_info, "str"); const char *file = json_object_get_string(bp_info, "file"); json_t *ids = json_object_get(bp_info, "ids"); json_t *line_obj = json_object_get(bp_info, "line"); // ---------- if(file) { gc->file = jsondata_game_get(file); } if(ids) { const size_t value_len = DECIMAL_DIGITS_BOUND(size_t); size_t key_new_len = value_len * json_flex_array_size(ids) + 1; VLA(char, key_new, key_new_len); char *p = key_new; size_t i; json_t *id; json_flex_array_foreach(ids, i, id) { char id_str[value_len + 1]; const char *q = id_str; size_t id_val = json_immediate_value(id, regs); snprintf(id_str, sizeof(id_str), "%u", id_val); if(i > 0) { *p++ = '_'; } while(*p++ = *q++); p--; } gentext_cache_key_set(gc, key_new, key_new_len); VLA_FREE(key_new); }
int binhack_render(BYTE *binhack_buf, size_t target_addr, const char *binhack_str) { const char *c = binhack_str; const char *fs = NULL; // function start size_t written = 0; int func_rel = 0; // Relative function pointer flag char conv[3]; int ret = 0; if(!binhack_buf || !binhack_str) { return -1; } conv[2] = 0; while(*c) { if(!fs && is_valid_hex(*c) && is_valid_hex(*(c+1)) ) { memcpy(conv, c, 2); *binhack_buf = (char)strtol(conv, NULL, 16); binhack_buf++; c++; written++; } else if(*c == '[' || *c == '<') { if(fs) { log_printf("ERROR: Nested function pointers near %s!\n", c); return 0; } func_rel = (*c == '['); fs = c + 1; } else if(fs && (*c == ']' || *c == '>')) { VLA(char, function, (c - fs) + 1); size_t fp = 0; strncpy(function, fs, c - fs); function[c - fs] = 0; fp = (size_t)func_get(function); if(fp) { if(func_rel) { fp -= target_addr + written + sizeof(void*); } memcpy(binhack_buf, &fp, sizeof(void*)); binhack_buf += sizeof(void*); written += sizeof(void*); } else { log_printf("ERROR: No pointer for function '%s'...\n", function); ret = 2; } fs = NULL; VLA_FREE(function); if(ret) { break; } } c++; }
int thcrap_inject_into_running(HANDLE hProcess, const char *run_cfg_fn) { int ret = -1; HMODULE inj_mod = NULL; if(GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPTSTR)thcrap_inject_into_running, &inj_mod )) { size_t cur_dir_len = GetCurrentDirectory(0, NULL) + 1; size_t inj_dir_len = GetModuleFileNameU(inj_mod, NULL, 0) + 1; VLA(char, inj_dll, inj_dir_len); VLA(char, inj_dir, inj_dir_len); STRLEN_DEC(run_cfg_fn); size_t param_len = cur_dir_len + run_cfg_fn_len; VLA(char, abs_run_cfg_fn, param_len); const char *param; GetModuleFileNameU(inj_mod, inj_dir, inj_dir_len); strncpy(inj_dll, inj_dir, inj_dir_len); PathRemoveFileSpec(inj_dir); PathAddBackslashA(inj_dir); // Allow for relative directory names if(PathIsRelativeA(run_cfg_fn)) { GetCurrentDirectory(cur_dir_len, abs_run_cfg_fn); PathAppendA(abs_run_cfg_fn, run_cfg_fn); param = abs_run_cfg_fn; } else { param = run_cfg_fn; param_len = run_cfg_fn_len; } ret = Inject(hProcess, inj_dir, inj_dll, "thcrap_init", param, param_len); VLA_FREE(abs_run_cfg_fn); VLA_FREE(inj_dir); VLA_FREE(inj_dll); } return ret; }
static int zip_file_extra_read(zip_file_info_t *file, zip_t *zip, int extra_len) { assert(file); assert(zip); VLA(char, extra_buf, extra_len); DWORD byte_ret; int ret = W32_ERR_WRAP(ReadFile( zip->hArc, extra_buf, extra_len, &byte_ret, NULL )); if(!ret) { zip_file_extra_parse(file, extra_buf, byte_ret); } VLA_FREE(extra_buf); return 0; }
int binhack_render(BYTE *binhack_buf, size_t target_addr, const char *binhack_str) { const char *c = binhack_str; const char *fs = NULL; // function start size_t written = 0; int func_rel = 0; // Relative function pointer flag int ret = 0; if(!binhack_buf || !binhack_str) { return -1; } while(*c) { if(*c == '[' || *c == '<') { if(fs) { log_printf("ERROR: Nested function pointers near %s!\n", c); return 0; } func_rel = (*c == '['); fs = c + 1; c++; } else if(fs && (*c == ']' || *c == '>')) { VLA(char, function, (c - fs) + 1); defer({ VLA_FREE(function); }); size_t fp = 0; strncpy(function, fs, c - fs); function[c - fs] = 0; fp = (size_t)func_get(function); if(fp) { if(func_rel) { fp -= target_addr + written + sizeof(void*); } memcpy(binhack_buf, &fp, sizeof(void*)); binhack_buf += sizeof(void*); written += sizeof(void*); } else { return hackpoints_error_function_not_found(function, 2); } fs = NULL; if(ret) { break; } c++; } else if(fs) {
json_t* patch_bootstrap(const json_t *sel, json_t *repo_servers) { const char *main_fn = "patch.js"; char *patch_js_buffer; DWORD patch_js_size; json_t *patch_info = patch_build(sel); const json_t *patch_id = json_array_get(sel, 1); size_t patch_len = json_string_length(patch_id) + 1; size_t remote_patch_fn_len = patch_len + 1 + strlen(main_fn) + 1; VLA(char, remote_patch_fn, remote_patch_fn_len); sprintf(remote_patch_fn, "%s/%s", json_string_value(patch_id), main_fn); patch_js_buffer = (char*)ServerDownloadFile(repo_servers, remote_patch_fn, &patch_js_size, NULL); patch_file_store(patch_info, main_fn, patch_js_buffer, patch_js_size); // TODO: Nice, friendly error VLA_FREE(remote_patch_fn); SAFE_FREE(patch_js_buffer); return patch_info; }
char* console_read(char *str, int n) { int ret; int i; fgets(str, n, stdin); { // Ensure UTF-8 VLA(wchar_t, str_w, n); StringToUTF16(str_w, str, n); StringToUTF8(str, str_w, n); VLA_FREE(str_w); } // Get rid of the \n for(i = 0; i < n; i++) { if(str[i] == '\n') { str[i] = 0; return str; } } while((ret = getchar()) != '\n' && ret != EOF); return str; }
/// Indexing helpers /// ---------------- // Adds an individual zipped file to the index. // Expects the file name at the current file pointer of [zip->hArc]. static int zip_file_add(zip_t *zip, zip_file_shared_t *zf, json_int_t offset) { int ret = -1; if(zip && zf) { DWORD byte_ret; VLA(char, fn, zf->fn_len + 1); ReadFile(zip->hArc, fn, zf->fn_len, &byte_ret, NULL); fn[zf->fn_len] = 0; SetFilePointer(zip->hArc, zf->extra_len, NULL, FILE_CURRENT); if(zf->fn_len) { if(zf->size_compressed != 0) { json_object_set_new(zip->files, fn, json_integer(offset)); } else { char last = fn[zf->fn_len - 1]; if(last != '/' && last != '\\') { json_array_append_new(zip->files_empty, json_string(fn)); } } } VLA_FREE(fn); ret = 0; }
static LRESULT CALLBACK loader_update_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static loader_update_state_t *state = nullptr; switch (uMsg) { case WM_CREATE: { CREATESTRUCTW *param = (CREATESTRUCTW*)lParam; state = (loader_update_state_t*)param->lpCreateParams; break; } case WM_COMMAND: switch LOWORD(wParam) { case HWND_BUTTON_RUN: if (HIWORD(wParam) == BN_CLICKED) { if (state->background_updates == false) { state->cancel_update = true; } EnterCriticalSection(&state->cs); state->game_started = true; LeaveCriticalSection(&state->cs); thcrap_inject_into_new(state->exe_fn, state->args, NULL, NULL); } break; case HWND_BUTTON_UPDATE: if (HIWORD(wParam) == BN_CLICKED) { SetEvent(state->event_require_update); } break; case HWND_CHECKBOX_KEEP_UPDATER: if (HIWORD(wParam) == BN_CLICKED) { BOOL enable_state; if (SendMessage(state->hwnd[HWND_CHECKBOX_KEEP_UPDATER], BM_GETCHECK, 0, 0) == BST_CHECKED) { state->background_updates = true; enable_state = TRUE; } else { state->background_updates = false; enable_state = FALSE; } EnableWindow(state->hwnd[HWND_STATIC_UPDATES_INTERVAL], enable_state); EnableWindow(state->hwnd[HWND_EDIT_UPDATES_INTERVAL], enable_state); EnableWindow(state->hwnd[HWND_UPDOWN], enable_state); } break; case HWND_EDIT_UPDATES_INTERVAL: if (HIWORD(wParam) == EN_CHANGE) { BOOL success; UINT n = GetDlgItemInt(state->hwnd[HWND_MAIN], HWND_EDIT_UPDATES_INTERVAL, &success, FALSE); if (success) { EnterCriticalSection(&state->cs); state->time_between_updates = n; LeaveCriticalSection(&state->cs); } } break; case HWND_CHECKBOX_UPDATE_AT_EXIT: if (HIWORD(wParam) == BN_CLICKED) { if (SendMessage(state->hwnd[HWND_CHECKBOX_UPDATE_AT_EXIT], BM_GETCHECK, 0, 0) == BST_CHECKED) { state->update_at_exit = true; } else { state->update_at_exit = false; } } break; case HWND_CHECKBOX_UPDATE_OTHERS: if (HIWORD(wParam) == BN_CLICKED) { if (SendMessage(state->hwnd[HWND_CHECKBOX_UPDATE_OTHERS], BM_GETCHECK, 0, 0) == BST_CHECKED) { state->update_others = true; } else { state->update_others = false; } } break; case HWND_BUTTON_DISABLE_UPDATES: if (HIWORD(wParam) == BN_CLICKED) { int len = GetCurrentDirectory(0, NULL); VLA(char, current_directory, len + 1); GetCurrentDirectory(len + 1, current_directory); if (log_mboxf(NULL, MB_YESNO, "Do you really want to completely disable updates?\n\n" "If you want to enable them again, you will need to run\n" "%s\\thcrap_enable_updates.bat", current_directory) == IDYES) { MoveFile("thcrap_update" DEBUG_OR_RELEASE ".dll", "thcrap_update_disabled" DEBUG_OR_RELEASE ".dll"); const char *bat_file = "@echo off\n" "if not exist \"%~dp0\"\\thcrap_update" DEBUG_OR_RELEASE ".dll (\n" "move \"%~dp0\"\\thcrap_update_disabled" DEBUG_OR_RELEASE ".dll \"%~dp0\"\\thcrap_update" DEBUG_OR_RELEASE ".dll\n" "echo Updates enabled\n" ") else (\n" "echo Updates are already enabled\n" ")\n" "pause\n" "(goto) 2>nul & del \"%~f0\"\n"; file_write("thcrap_enable_updates.bat", bat_file, strlen(bat_file)); log_mbox(NULL, MB_OK, "Updates are now disabled."); PostQuitMessage(0); } VLA_FREE(current_directory); } break; case HWND_BUTTON_EXPAND_LOGS: if (HIWORD(wParam) == BN_CLICKED) { if (state->settings_visible) { // Hide log window SetWindowPos(state->hwnd[HWND_MAIN], 0, 0, 0, 500, 165, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); state->settings_visible = false; } else { // Show log window SetWindowPos(state->hwnd[HWND_MAIN], 0, 0, 0, 500, 435, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); state->settings_visible = true; } } break; } break; case WM_CTLCOLORSTATIC: if ((HWND)lParam == state->hwnd[HWND_LABEL_STATUS] || (HWND)lParam == state->hwnd[HWND_CHECKBOX_UPDATE_AT_EXIT] || (HWND)lParam == state->hwnd[HWND_CHECKBOX_KEEP_UPDATER] || (HWND)lParam == state->hwnd[HWND_CHECKBOX_UPDATE_OTHERS] || (HWND)lParam == state->hwnd[HWND_STATIC_UPDATES_INTERVAL] || (HWND)lParam == state->hwnd[HWND_STATIC_UPDATE_AT_EXIT]) { HDC hdc = (HDC)wParam; SetTextColor(hdc, RGB(0, 0, 0)); SetBkMode(hdc, TRANSPARENT); return (LRESULT)GetSysColorBrush(COLOR_WINDOW); } break; case WM_DESTROY: state->cancel_update = true; PostQuitMessage(0); break; }
int Inject(HANDLE hProcess, const char *dll_dir, const char *dll_fn, const char *func_name, const void *param, const size_t param_size) { // String constants const char *injectError1Format = "Could not inject %s.\n" "\n" "If you're running Windows Vista or 7, make sure that you have installed the KB2533623 update:\n" "\n" "\thttp://support.microsoft.com/kb/2533623/"; const char *injectError2Format = "Could not load the function: %s"; //------------------------------------------// // Function variables. // //------------------------------------------// // Main DLL we will need to load HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); // Main functions we will need to import. // If [dll_fn] is absolute, LoadLibraryEx() with the LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR // flag is used to guarantee that the injected DLL and its dependencies really // are only loaded from the given directory. Otherwise, LoadLibrary() may load // a possible other DLL with the same name from the directory of [hProcess]. FARPROC getcurrentdirectory = GetProcAddress(kernel32, "GetCurrentDirectoryW"); FARPROC setcurrentdirectory = GetProcAddress(kernel32, "SetCurrentDirectoryW"); FARPROC loadlibrary = GetProcAddress(kernel32, "LoadLibraryW"); FARPROC loadlibraryex = GetProcAddress(kernel32, "LoadLibraryExW"); FARPROC getprocaddress = GetProcAddress(kernel32, "GetProcAddress"); FARPROC exitthread = GetProcAddress(kernel32, "ExitThread"); FARPROC freelibraryandexitthread = GetProcAddress(kernel32, "FreeLibraryAndExitThread"); int have_kb2269637 = GetProcAddress(kernel32, "SetDefaultDllDirectories") != 0; // The workspace we will build the codecave on locally. // workspaceSize gets incremented with the final length of the error strings. size_t workspaceSize = 2048; LPBYTE workspace = NULL; LPBYTE p = NULL; // The memory in the process we write to LPBYTE codecaveAddress = NULL; // Strings we have to write into the process size_t injectError1_len = _scprintf(injectError1Format, dll_fn) + 1; size_t injectError2_len = _scprintf(injectError2Format, func_name) + 1; char *injectError0 = "Error"; VLA(char, injectError1, injectError1_len); VLA(char, injectError2, injectError2_len); char *user32Name = "user32.dll"; char *msgboxName = "MessageBoxW"; // Placeholder addresses to use the strings LPBYTE user32NameAddr = 0; LPBYTE user32Addr = 0; LPBYTE msgboxNameAddr = 0; LPBYTE msgboxAddr = 0; LPBYTE dllAddr = 0; LPBYTE dllDirAddr = 0; LPBYTE dllNameAddr = 0; LPBYTE funcNameAddr = 0; LPBYTE funcParamAddr = 0; LPBYTE error0Addr = 0; LPBYTE error1Addr = 0; LPBYTE error2Addr = 0; // Where the codecave execution should begin at LPTHREAD_START_ROUTINE codecaveExecAddr = 0; // Handle to the thread we create in the process HANDLE hThread = NULL; // Old protection on page we are writing to in the process and the bytes written DWORD oldProtect = 0; DWORD byte_ret = 0; // Return code of injection function DWORD injRet; //------------------------------------------// // Variable initialization. // //------------------------------------------// // This section will cause compiler warnings on VS8, // you can upgrade the functions or ignore them // Build error messages sprintf(injectError1, injectError1Format, dll_fn); sprintf(injectError2, injectError2Format, func_name); workspaceSize += ( strlen(dll_dir) + 1 + strlen(dll_fn) + 1 + strlen(func_name) + 1 + param_size + strlen(injectError1) + 1 + strlen(injectError2) + 1 ) * sizeof(wchar_t); // Create the workspace workspace = (LPBYTE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, workspaceSize); p = workspace; // Allocate space for the codecave in the process codecaveAddress = (LPBYTE)VirtualAllocEx(hProcess, 0, workspaceSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // Note there is no error checking done above for any functions that return a pointer/handle. // I could have added them, but it'd just add more messiness to the code and not provide any real // benefit. It's up to you though in your final code if you want it there or not. //------------------------------------------// // Data and string writing. // //------------------------------------------// // Reserve space for the user32 dll address, the MessageBox address, // and the address of the injected DLL's module. user32Addr = (p - workspace) + codecaveAddress; p += sizeof(LPBYTE); msgboxAddr = (p - workspace) + codecaveAddress; p += sizeof(LPBYTE); dllAddr = (p - workspace) + codecaveAddress; p += sizeof(LPBYTE); // User32 Dll Name user32NameAddr = (p - workspace) + codecaveAddress; p = StringToUTF16_advance_dst(p, user32Name); // MessageBox name msgboxNameAddr = (p - workspace) + codecaveAddress; p = memcpy_advance_dst(p, msgboxName, strlen(msgboxName) + 1); // Directory name if(dll_dir) { dllDirAddr = (p - workspace) + codecaveAddress; p = StringToUTF16_advance_dst(p, dll_dir); } // Dll Name dllNameAddr = (p - workspace) + codecaveAddress; p = StringToUTF16_advance_dst(p, dll_fn); // Function Name funcNameAddr = (p - workspace) + codecaveAddress; p = memcpy_advance_dst(p, func_name, strlen(func_name) + 1); // Function Parameter funcParamAddr = (p - workspace) + codecaveAddress; p = memcpy_advance_dst(p, param, param_size); // Error Message 1 error0Addr = (p - workspace) + codecaveAddress; p = StringToUTF16_advance_dst(p, injectError0); // Error Message 2 error1Addr = (p - workspace) + codecaveAddress; p = StringToUTF16_advance_dst(p, injectError1); // Error Message 3 error2Addr = (p - workspace) + codecaveAddress; p = StringToUTF16_advance_dst(p, injectError2); // Pad a few INT3s after string data is written for seperation *p++ = 0xCC; *p++ = 0xCC; *p++ = 0xCC; // Store where the codecave execution should begin codecaveExecAddr = (LPTHREAD_START_ROUTINE)((p - workspace) + codecaveAddress); // For debugging - infinite loop, attach onto process and step over //*p++ = 0xEB; //*p++ = 0xFE; //------------------------------------------// // User32.dll loading. // //------------------------------------------// // User32 DLL Loading // PUSH 0x00000000 - Push the address of the DLL name to use in LoadLibrary *p++ = 0x68; p = ptrcpy_advance_dst(p, user32NameAddr); // MOV EAX, ADDRESS - Move the address of LoadLibrary into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, loadlibrary); // CALL EAX - Call LoadLibrary *p++ = 0xFF; *p++ = 0xD0; // MessageBox Loading // PUSH 0x000000 - Push the address of the function name to load *p++ = 0x68; p = ptrcpy_advance_dst(p, msgboxNameAddr); // Push EAX, module to use in GetProcAddress *p++ = 0x50; // MOV EAX, ADDRESS - Move the address of GetProcAddress into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, getprocaddress); // CALL EAX - Call GetProcAddress *p++ = 0xFF; *p++ = 0xD0; // MOV [ADDRESS], EAX - Save the address to our variable *p++ = 0xA3; p = ptrcpy_advance_dst(p, msgboxAddr); //------------------------------------------// // Injected dll loading. // //------------------------------------------// /* // This is the way the following assembly code would look like in C/C++ // In case the injected DLL depends on other DLLs, // we need to change the current directory to the one given as parameter if(dll_dir) { size_t cur_dir_len = GetCurrentDirectory(0, NULL) + 1; VLA(wchar_t, cur_dir, cur_dir_len); GetCurrentDirectory(cur_dir, cur_dir_len); SetCurrentDirectory(dll_dir); } // Load the injected DLL into this process HMODULE h = LoadLibraryEx(dll_fn, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); if(!h) { MessageBox(0, injectError1, "Error", MB_ICONERROR); ExitThread(1); } if(dll_dir) { SetCurrentDirectory(cur_dir); } // Get the address of the export function FARPROC p = GetProcAddress(h, func_name); if(!p) { MessageBox(0, injectError2, "Error", MB_ICONERROR); FreeLibraryAndExitThread(h, 2); } // So we do not need a function pointer interface __asm call p // Exit the thread so the loader continues ExitThread(0); */ // DLL Loading if(dllDirAddr) { // Registers: // ebp: Base stack frame // esi: GetCurrentDirectory / SetCurrentDirectory // ebx: Current directory of process (on stack) // ecx: byte length of string at ebx // mov ebp, esp - Save stack frame *p++ = 0x89; *p++ = 0xe5; // Get length for current directory // push 0 // push 0 *p++ = 0x6a; *p++ = 0x00; *p++ = 0x6a; *p++ = 0x00; // mov esi, GetCurrentDirectory *p++ = 0xbe; p = ptrcpy_advance_dst(p, getcurrentdirectory); // call esi *p++ = 0xFF; *p++ = 0xD6; /// Calculate byte size of directory buffer. /// Also do some poor man's DWORD boundary alignment /// in order to not f**k up the stack // mov ecx, eax // shl ecx, 1 // and ecx, fffffff8 // add ecx, 4 *p++ = 0x89; *p++ = 0xc1; *p++ = 0xd1; *p++ = 0xe1; *p++ = 0x83; *p++ = 0xe1; *p++ = 0xf8; *p++ = 0x83; *p++ = 0xc1; *p++ = 0x04; /// "Allocate" ecx bytes on stack and store buffer pointer to ebx // sub esp, ecx // mov ebx, esp *p++ = 0x29; *p++ = 0xcc; *p++ = 0x89; *p++ = 0xe3; /// Call GetCurrentDirectory // push ebx // push eax // call esi *p++ = 0x53; *p++ = 0x50; *p++ = 0xff; *p++ = 0xd6; /// PUSH 0x00000000 - Push the address of our directory *p++ = 0x68; p = ptrcpy_advance_dst(p, dllDirAddr); // mov esi, SetCurrentDirectory *p++ = 0xbe; p = ptrcpy_advance_dst(p, setcurrentdirectory); // call esi *p++ = 0xFF; *p++ = 0xD6; } if(PathIsRelativeA(dll_fn) || !have_kb2269637) { // PUSH 0x00 (dwFlags = 0) *p++ = 0x6a; *p++ = 0x00; } else { // PUSH 0x00000900 (dwFlags = LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32) *p++ = 0x68; *p++ = 0x00; *p++ = 0x09; *p++ = 0x00; *p++ = 0x00; } // PUSH 0x00 (hFile = NULL) *p++ = 0x6a; *p++ = 0x00; // PUSH 0x00000000 - Push the address of the DLL name to use in LoadLibraryEx *p++ = 0x68; p = ptrcpy_advance_dst(p, dllNameAddr); // MOV EAX, ADDRESS - Move the address of LoadLibraryEx into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, loadlibraryex); // CALL EAX - Call LoadLibraryEx *p++ = 0xFF; *p++ = 0xD0; // mov edi, eax - Save return value *p++ = 0x89; *p++ = 0xc7; if(dllDirAddr) { /// Reset directory to the original one of the process // push ebx // call esi *p++ = 0x53; *p++ = 0xFF; *p++ = 0xD6; /// Reset stack frame // mov esp, ebp *p++ = 0x89; *p++ = 0xec; } // Error Checking // CMP EDI, 0 *p++ = 0x83; *p++ = 0xFF; *p++ = 0x00; // JNZ EIP + 0x1E to skip over eror code *p++ = 0x75; *p++ = 0x1E; // Error Code 1 // MessageBox // PUSH 0x10 (MB_ICONHAND) *p++ = 0x6A; *p++ = 0x10; // PUSH 0x000000 - Push the address of the MessageBox title *p++ = 0x68; p = ptrcpy_advance_dst(p, error0Addr); // PUSH 0x000000 - Push the address of the MessageBox message *p++ = 0x68; p = ptrcpy_advance_dst(p, error1Addr); // Push 0 *p++ = 0x6A; *p++ = 0x00; // MOV EAX, [ADDRESS] - Move the address of MessageBox into EAX *p++ = 0xA1; p = ptrcpy_advance_dst(p, msgboxAddr); // CALL EAX - Call MessageBoxW *p++ = 0xFF; *p++ = 0xD0; // ExitThread // PUSH 1 *p++ = 0x6A; *p++ = 0x01; // MOV EAX, ADDRESS - Move the address of ExitThread into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, exitthread); // CALL EAX - Call ExitThread *p++ = 0xFF; *p++ = 0xD0; // Now we have the address of the injected DLL, so save the handle // MOV [ADDRESS], EAX - Save the address to our variable *p++ = 0x89; *p++ = 0x3D; p = ptrcpy_advance_dst(p, dllAddr); // Load the initilize function from it // PUSH 0x000000 - Push the address of the function name to load *p++ = 0x68; p = ptrcpy_advance_dst(p, funcNameAddr); // Push EDI - module to use in GetProcAddress *p++ = 0x57; // MOV EAX, ADDRESS - Move the address of GetProcAddress into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, getprocaddress); // CALL EAX - Call GetProcAddress *p++ = 0xFF; *p++ = 0xD0; // Error Checking // CMP EAX, 0 *p++ = 0x83; *p++ = 0xF8; *p++ = 0x00; // JNZ EIP + 0x23 to skip eror code *p++ = 0x75; *p++ = 0x23; // Error Code 2 // MessageBox // PUSH 0x10 (MB_ICONHAND) *p++ = 0x6A; *p++ = 0x10; // PUSH 0x000000 - Push the address of the MessageBox title *p++ = 0x68; p = ptrcpy_advance_dst(p, error0Addr); // PUSH 0x000000 - Push the address of the MessageBox message *p++ = 0x68; p = ptrcpy_advance_dst(p, error2Addr); // Push 0 *p++ = 0x6A; *p++ = 0x00; // MOV EAX, ADDRESS - Move the address of MessageBox into EAX *p++ = 0xA1; p = ptrcpy_advance_dst(p, msgboxAddr); // CALL EAX - Call MessageBoxA *p++ = 0xFF; *p++ = 0xD0; // FreeLibraryAndExitThread // PUSH 2 *p++ = 0x6A; *p++ = 0x02; // PUSH 0x000000 - Push the injected DLL's module handle *p++ = 0x68; p = ptrcpy_advance_dst(p, dllAddr); // MOV EAX, ADDRESS - Move the address of FreeLibraryAndExitThread into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, freelibraryandexitthread); // CALL EAX - Call ExitThread function *p++ = 0xFF; *p++ = 0xD0; // PUSH 0x000000 - Push the address of the function parameter *p++ = 0x68; p = ptrcpy_advance_dst(p, funcParamAddr); // CALL EAX - Call [func_name] *p++ = 0xFF; *p++ = 0xD0; // If we get here, [func_name] has been called, // so it's time to close this thread and optionally unload the DLL. //------------------------------------------// // Exiting from the injected dll. // //------------------------------------------// // Call ExitThread to leave the DLL loaded #if 1 // PUSH 0 (exit code) *p++ = 0x6A; *p++ = 0x00; // MOV EAX, ADDRESS - Move the address of ExitThread into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, exitthread); // CALL EAX - Call ExitThread *p++ = 0xFF; *p++ = 0xD0; #endif // Call FreeLibraryAndExitThread to unload DLL #if 0 // Push 0 (exit code) *p++ = 0x6A; *p++ = 0x00; // PUSH [0x000000] - Push the address of the DLL module to unload *p++ = 0xFF; *p++ = 0x35; p = ptrcpy_advance_dst(p, dllAddr); // MOV EAX, ADDRESS - Move the address of FreeLibraryAndExitThread into EAX *p++ = 0xB8; p = ptrcpy_advance_dst(p, freelibraryandexitthread); // CALL EAX - Call FreeLibraryAndExitThread *p++ = 0xFF; *p++ = 0xD0; #endif //------------------------------------------// // Code injection and cleanup. // //------------------------------------------// // Change page protection so we can write executable code VirtualProtectEx(hProcess, codecaveAddress, p - workspace, PAGE_EXECUTE_READWRITE, &oldProtect); // Write out the patch WriteProcessMemory(hProcess, codecaveAddress, workspace, p - workspace, &byte_ret); // Restore page protection VirtualProtectEx(hProcess, codecaveAddress, p - workspace, oldProtect, &oldProtect); // Make sure our changes are written right away FlushInstructionCache(hProcess, codecaveAddress, p - workspace); // Free the workspace memory HeapFree(GetProcessHeap(), 0, workspace); // Execute the thread now and wait for it to exit, note we execute where the code starts, and not the codecave start // (since we wrote strings at the start of the codecave) hThread = CreateRemoteThread(hProcess, NULL, 0, codecaveExecAddr, 0, 0, NULL); WaitForSingleObject(hThread, INFINITE); GetExitCodeThread(hThread, &injRet); CloseHandle(hThread); // Free the memory in the process that we allocated VirtualFreeEx(hProcess, codecaveAddress, 0, MEM_RELEASE); VLA_FREE(injectError1); VLA_FREE(injectError2); return injRet; }