diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cfab07f..21b89d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,46 +1,54 @@ name: Build on: - push: - branches: - - main - pull_request: - branches: - - main + push: + branches: + - main + pull_request: + branches: + - main jobs: - build: - strategy: - matrix: - game_version: ['official', 'steam'] - runs-on: windows-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: Download SDK - run: | - $sdk_link = "${{ secrets.SDK_LINK }}" - Invoke-WebRequest -Uri $sdk_link -OutFile sdk.zip - Expand-Archive -Path sdk.zip -DestinationPath CppSDK - - - name: Set up MSVC environment - uses: microsoft/setup-msbuild@v2 - - - uses: lukka/get-cmake@latest - - - name: Configure CMake - run: cmake -B build -S . -DBUILD_STEAM_VERSION=${{ matrix.game_version == 'steam' && 'ON' || 'OFF' }} - - - name: Build project - run: cmake --build build --config Release - - - name: Archive artifacts - uses: actions/upload-artifact@v4 - with: - name: TOFInternal-${{ matrix.game_version }} - path: | - build/bin/Release/TOFInternal.dll - build/bin/Release/Injector.exe + build: + strategy: + matrix: + game_version: ["official", "steam"] + runs-on: windows-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: "recursive" + + - name: Download SDK + run: | + $sdk_link = "${{ secrets.SDK_LINK }}" + Invoke-WebRequest -Uri $sdk_link -OutFile sdk.zip + Expand-Archive -Path sdk.zip -DestinationPath CppSDK + + - name: Set up MSVC environment + uses: microsoft/setup-msbuild@v2 + + - uses: lukka/get-cmake@latest + + - name: Configure TOF-SIH + run: cmake -B build -S . -DBUILD_STEAM_VERSION=${{ matrix.game_version == 'steam' && 'ON' || 'OFF' }} + + - name: Build TOF-SIH + run: cmake --build build --config Release + + - name: Configure Injector + run: cmake -B injector/build -S injector + + - name: Build Injector + run: cmake --build injector/build --config Release + + - name: Archive artifacts + uses: actions/upload-artifact@v4 + with: + name: TOFInternal-${{ matrix.game_version }} + path: | + build/bin/Release/TOFInternal.dll + build/bin/Release/proc.exe + injector/build/bin/Release/injector.exe + injector/build/bin/Release/_aux.dll diff --git a/.vscode/settings.json b/.vscode/settings.json index f299eac..2bc20c7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -88,5 +88,10 @@ }, "cmake.preferredGenerators": [ "Visual Studio 17 2022" - ] + ], + "cmake.sourceDirectory": [ + "${workspaceFolder}", + "${workspaceFolder}/injector" + ], + "cmake.buildDirectory": "${sourceDirectory}/build" } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bd3288a..18736a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,13 +17,8 @@ if (BUILD_STEAM_VERSION) add_definitions(-DIS_STEAM_VERSION) endif() -include(FetchContent) -FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz) -FetchContent_MakeAvailable(json) - add_subdirectory(src) add_subdirectory(CppSDK) add_subdirectory(cfg) -add_subdirectory(injector) add_subdirectory(tools) add_subdirectory(kiero/minhook) \ No newline at end of file diff --git a/README.md b/README.md index ccd4997..7e12122 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ > This is for educational purposes only! > **The safety of your account is entirely your responsibility. If it gets banned by using this, it's your own fault! (You have been warned.)** +## How to Use + +Simply run the provided injector and select the game launcher executable. +If you're playing the Steam version, start the launcher via Steam, then run the injector. + ## External Library Credits - [Dear ImGui](https://github.com/ocornut/imgui) diff --git a/cfg/CMakeLists.txt b/cfg/CMakeLists.txt index 97e34c2..cd2c1f1 100644 --- a/cfg/CMakeLists.txt +++ b/cfg/CMakeLists.txt @@ -1,3 +1,7 @@ +include(FetchContent) +FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz) +FetchContent_MakeAvailable(json) + add_library(Config STATIC config.cpp ) diff --git a/cfg/config.cpp b/cfg/config.cpp index 3eee276..95ae25c 100644 --- a/cfg/config.cpp +++ b/cfg/config.cpp @@ -76,5 +76,8 @@ namespace Config { saveThreadStarted = true; } - void shutdown() { shuttingDown = true; } + void shutdown() { + actualSave(); + shuttingDown = true; + } } // namespace Config diff --git a/injector/CMakeLists.txt b/injector/CMakeLists.txt index 8f49b7a..bebcbdf 100644 --- a/injector/CMakeLists.txt +++ b/injector/CMakeLists.txt @@ -1,6 +1,24 @@ +cmake_minimum_required(VERSION 3.20) + +project(TOFInjector VERSION 0.0.1) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +add_definitions(-DUNICODE -D_UNICODE) + add_executable(Injector main.cpp) +add_library(_aux SHARED _aux.cpp) +add_subdirectory(../cfg ${CMAKE_BINARY_DIR}/cfg) +add_subdirectory(../kiero/minhook ${CMAKE_BINARY_DIR}/kiero/minhook) target_precompile_headers(Injector PRIVATE pch.hpp) target_include_directories(Injector PRIVATE ../cfg ) +target_include_directories(_aux PRIVATE + ../kiero/minhook/include +) target_link_libraries(Injector Config ntdll.lib) +target_link_libraries(_aux minhook ntdll.lib) \ No newline at end of file diff --git a/injector/_aux.cpp b/injector/_aux.cpp new file mode 100644 index 0000000..4a1deaf --- /dev/null +++ b/injector/_aux.cpp @@ -0,0 +1,92 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#include "MinHook.h" + +void *createProcAddr = nullptr; +bool isSteam = false; +HANDLE gameThread = nullptr; + +void rehook() { + // For some reason immediate rehook (or not removing the hook at all) will cause + // a crash for the Steam version of the game. + Sleep(1); + MH_EnableHook(createProcAddr); +} + +typedef BOOL(WINAPI *createProcessW_t)(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, + DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation); +createProcessW_t oCreateProcessW = nullptr; +BOOL WINAPI createProcess(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) { + const auto isGame = wcsstr(lpApplicationName, L"QRSL.exe") != nullptr; + const auto isLauncher = wcsstr(lpApplicationName, L"x64launcher.exe") != nullptr; + + if (isGame) { + dwCreationFlags |= CREATE_SUSPENDED; + SetEnvironmentVariable(L"__COMPAT_LAYER", L"RUNASINVOKER"); + } + + const auto result = + oCreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, + dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); + + if (result) { + if (isGame) { + SetEnvironmentVariable(L"__COMPAT_LAYER", L""); + // Wait for main injector to do its thing + Sleep(250); + MH_DisableHook(createProcAddr); + + if (isSteam) { + gameThread = lpProcessInformation->hThread; + } else { + ResumeThread(lpProcessInformation->hThread); + } + + // Call rehook to re-enable the hook. + // This is important because for some reason Steam will try to use + // thread hijacking DLL injection via a remote process and we want to make sure + // to only resume the main game thread after that injection is finished. + CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)rehook, nullptr, 0, nullptr); + } + + if (isLauncher) { + ResumeThread(gameThread); + } + } + + return result; +} + +WNDPROC oWndProc = nullptr; +LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_USER + 0x420) { + createProcAddr = (void *)wParam; + MH_Initialize(); + + MH_CreateHook(createProcAddr, createProcess, (void **)&oCreateProcessW); + MH_EnableHook(createProcAddr); + + isSteam = GetModuleHandle(L"gameoverlayrenderer.dll") != nullptr; + + return true; + } + + return CallWindowProc(oWndProc, hWnd, uMsg, wParam, lParam); +} + +BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) { + if (fdwReason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls(hInstDLL); + oWndProc = + (WNDPROC)SetWindowLongPtr(FindWindow(L"TWINCONTROL", L"Tower of Fantasy"), GWLP_WNDPROC, (LONG_PTR)wndProc); + } + + return true; +} diff --git a/injector/inject.hpp b/injector/inject.hpp index 5ae6dbd..1c3ed65 100644 --- a/injector/inject.hpp +++ b/injector/inject.hpp @@ -1,423 +1,27 @@ -#ifdef _WIN64 -using f_Routine = UINT_PTR(__fastcall *)(void *pArg); -#else -using f_Routine = UINT_PTR(__stdcall *)(void *pArg); -#endif +HANDLE InjectDll(HANDLE proc, const std::wstring dll) { + const auto dllAddr = VirtualAllocEx(proc, nullptr, dll.size() * sizeof(wchar_t), MEM_COMMIT, PAGE_READWRITE); -#ifdef UNICODE -#define LOAD_LIBRARY_NAME "LoadLibraryW" -#else -#define LOAD_LIBRARY_NAME "LoadLibraryA" -#endif - -struct HookData { - HHOOK m_hHook; - HWND m_hWnd; -}; - -struct EnumWindowsCallback_Data { - std::vector m_HookData; - DWORD m_PID; - HOOKPROC m_pHook; - HINSTANCE m_hModule; -}; - -HINSTANCE GetModuleHandleEx(HANDLE hTargetProc, const TCHAR *lpModuleName) { - MODULEENTRY32 ME32{0}; - ME32.dwSize = sizeof(ME32); - - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(hTargetProc)); - if (hSnap == INVALID_HANDLE_VALUE) { - while (GetLastError() == ERROR_BAD_LENGTH) { - hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(hTargetProc)); - if (hSnap != INVALID_HANDLE_VALUE) - break; - } - } - - if (hSnap == INVALID_HANDLE_VALUE) { - return NULL; - } - - BOOL bRet = Module32First(hSnap, &ME32); - do { - if (!_tcsicmp(lpModuleName, ME32.szModule)) - break; - - bRet = Module32Next(hSnap, &ME32); - - } while (bRet); - - CloseHandle(hSnap); - - if (!bRet) { - return NULL; - } - - return ME32.hModule; -} - -void *GetProcAddressEx(HANDLE hTargetProc, const TCHAR *lpModuleName, const char *lpProcName) { - BYTE *modBase = reinterpret_cast(GetModuleHandleEx(hTargetProc, lpModuleName)); - if (!modBase) - return nullptr; - - BYTE *pe_header = new BYTE[0x1000]; - if (!pe_header) - return nullptr; - - if (!ReadProcessMemory(hTargetProc, modBase, pe_header, 0x1000, nullptr)) { - delete[] pe_header; - - return nullptr; - } - - auto *pNT = - reinterpret_cast(pe_header + reinterpret_cast(pe_header)->e_lfanew); - auto *pExportEntry = &pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; - - if (!pExportEntry->Size) { - delete[] pe_header; - - return nullptr; - } - - BYTE *export_data = new BYTE[pExportEntry->Size]; - if (!export_data) { - delete[] pe_header; - - return nullptr; + if (!dllAddr) { + std::cout << "Failed to allocate memory for DLL path" << std::endl; + return 0; } - if (!ReadProcessMemory(hTargetProc, modBase + pExportEntry->VirtualAddress, export_data, pExportEntry->Size, - nullptr)) { - delete[] export_data; - delete[] pe_header; - - return nullptr; + if (!WriteProcessMemory(proc, dllAddr, dll.c_str(), dll.size() * sizeof(wchar_t), nullptr)) { + std::cout << "Failed to write DLL path into memory" << std::endl; + return 0; } - BYTE *localBase = export_data - pExportEntry->VirtualAddress; - auto *pExportDir = reinterpret_cast(export_data); - - auto Forward = [&](DWORD FuncRVA) -> void * { - char pFullExport[MAX_PATH + 1]{0}; - auto Len = strlen(reinterpret_cast(localBase + FuncRVA)); - if (!Len) - return nullptr; - - memcpy(pFullExport, reinterpret_cast(localBase + FuncRVA), Len); - - char *pFuncName = strchr(pFullExport, '.'); - *(pFuncName++) = 0; - if (*pFuncName == '#') - pFuncName = reinterpret_cast(LOWORD(atoi(++pFuncName))); - -#ifdef UNICODE - TCHAR ModNameW[MAX_PATH + 1]{0}; - size_t SizeOut = 0; - mbstowcs_s(&SizeOut, ModNameW, pFullExport, MAX_PATH); - - return GetProcAddressEx(hTargetProc, ModNameW, pFuncName); -#else - - return GetProcAddressEx(hTargetProc, pFullExport, pFuncName); -#endif - }; - - if ((reinterpret_cast(lpProcName) & 0xFFFFFF) <= MAXWORD) { - WORD Base = LOWORD(pExportDir->Base - 1); - WORD Ordinal = LOWORD(lpProcName) - Base; - DWORD FuncRVA = reinterpret_cast(localBase + pExportDir->AddressOfFunctions)[Ordinal]; - - delete[] export_data; - delete[] pe_header; - - if (FuncRVA >= pExportEntry->VirtualAddress && FuncRVA < pExportEntry->VirtualAddress + pExportEntry->Size) { - return Forward(FuncRVA); - } - - return modBase + FuncRVA; - } - - DWORD max = pExportDir->NumberOfNames - 1; - DWORD min = 0; - DWORD FuncRVA = 0; - - while (min <= max) { - DWORD mid = (min + max) / 2; - - DWORD CurrNameRVA = reinterpret_cast(localBase + pExportDir->AddressOfNames)[mid]; - char *szName = reinterpret_cast(localBase + CurrNameRVA); - - int cmp = strcmp(szName, lpProcName); - if (cmp < 0) - min = mid + 1; - else if (cmp > 0) - max = mid - 1; - else { - WORD Ordinal = reinterpret_cast(localBase + pExportDir->AddressOfNameOrdinals)[mid]; - FuncRVA = reinterpret_cast(localBase + pExportDir->AddressOfFunctions)[Ordinal]; - - break; - } - } - - delete[] export_data; - delete[] pe_header; - - if (!FuncRVA) - return nullptr; - - if (FuncRVA >= pExportEntry->VirtualAddress && FuncRVA < pExportEntry->VirtualAddress + pExportEntry->Size) { - return Forward(FuncRVA); - } - - return modBase + FuncRVA; -} - -bool SR_SetWindowsHookEx(HANDLE hTargetProc, f_Routine *pRoutine, void *pArg, DWORD &LastWin32Error, UINT_PTR &Out) { - void *pCodecave = VirtualAllocEx(hTargetProc, nullptr, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (!pCodecave) { - LastWin32Error = GetLastError(); - - return false; - } - - void *pCallNextHookEx = GetProcAddressEx(hTargetProc, TEXT("user32.dll"), "CallNextHookEx"); - if (!pCallNextHookEx) { - VirtualFreeEx(hTargetProc, pCodecave, 0, MEM_RELEASE); - - return false; - } - -#ifdef _WIN64 - - BYTE Shellcode[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - 0x18 -> pArg / returned value / rax - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - 0x10 -> pRoutine - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - 0x08 -> CallNextHookEx - - 0x55, // + 0x00 -> push rbp - 0x54, // + 0x01 -> push rsp - 0x53, // + 0x02 -> push rbx - - 0x48, 0x8D, 0x1D, 0xDE, 0xFF, 0xFF, 0xFF, // + 0x03 -> lea rbx, [pArg] - - 0x48, 0x83, 0xEC, 0x20, // + 0x0A -> sub rsp, 0x20 - 0x4D, 0x8B, 0xC8, // + 0x0E -> mov r9,r8 - 0x4C, 0x8B, 0xC2, // + 0x11 -> mov r8, rdx - 0x48, 0x8B, 0xD1, // + 0x14 -> mov rdx,rcx - 0xFF, 0x53, 0x10, // + 0x17 -> call [rbx + 0x10] - 0x48, 0x83, 0xC4, 0x20, // + 0x1A -> add rsp, 0x20 - - 0x48, 0x8B, 0xC8, // + 0x1E -> mov rcx, rax - - 0xEB, 0x00, // + 0x21 -> jmp $ + 0x02 - 0xC6, 0x05, 0xF8, 0xFF, 0xFF, 0xFF, 0x18, // + 0x23 -> mov byte ptr[$ - 0x01], 0x1A - - 0x48, 0x87, 0x0B, // + 0x2A -> xchg [rbx], rcx - 0x48, 0x83, 0xEC, 0x20, // + 0x2D -> sub rsp, 0x20 - 0xFF, 0x53, 0x08, // + 0x31 -> call [rbx + 0x08] - 0x48, 0x83, 0xC4, 0x20, // + 0x34 -> add rsp, 0x20 - - 0x48, 0x87, 0x03, // + 0x38 -> xchg [rbx], rax - - 0x5B, // + 0x3B -> pop rbx - 0x5C, // + 0x3C -> pop rsp - 0x5D, // + 0x3D -> pop rbp - - 0xC3 // + 0x3E -> ret - }; // SIZE = 0x3F (+ 0x18) - - DWORD CodeOffset = 0x18; - DWORD CheckByteOffset = 0x22 + CodeOffset; - - *reinterpret_cast(Shellcode + 0x00) = pArg; - *reinterpret_cast(Shellcode + 0x08) = pRoutine; - *reinterpret_cast(Shellcode + 0x10) = pCallNextHookEx; - -#else - - BYTE Shellcode[] = { - 0x00, 0x00, 0x00, 0x00, // - 0x08 -> pArg - 0x00, 0x00, 0x00, 0x00, // - 0x04 -> pRoutine - - 0x55, // + 0x00 -> push ebp - 0x8B, 0xEC, // + 0x01 -> mov ebp, esp - - 0xFF, 0x75, 0x10, // + 0x03 -> push [ebp + 0x10] - 0xFF, 0x75, 0x0C, // + 0x06 -> push [ebp + 0x0C] - 0xFF, 0x75, 0x08, // + 0x09 -> push [ebp + 0x08] - 0x6A, 0x00, // + 0x0C -> push 0x00 - 0xE8, 0x00, 0x00, 0x00, 0x00, // + 0x0E (+ 0x0F) -> call CallNextHookEx - - 0xEB, 0x00, // + 0x13 -> jmp $ + 0x02 - - 0x50, // + 0x15 -> push eax - 0x53, // + 0x16 -> push ebx - - 0xBB, 0x00, 0x00, 0x00, 0x00, // + 0x17 (+ 0x18) -> mov ebx, pArg - 0xC6, 0x43, 0x1C, 0x14, // + 0x1C -> mov [ebx + 0x1C], 0x17 - - 0xFF, 0x33, // + 0x20 -> push [ebx] - - 0xFF, 0x53, 0x04, // + 0x22 -> call [ebx + 0x04] - - 0x89, 0x03, // + 0x25 -> mov [ebx], eax - - 0x5B, // + 0x27 -> pop ebx - 0x58, // + 0x28 -> pop eax - - 0x5D, // + 0x29 -> pop ebp - 0xC2, 0x0C, 0x00 // + 0x2A -> ret 0x000C - }; // SIZE = 0x3D (+ 0x08) - - DWORD CodeOffset = 0x08; - DWORD CheckByteOffset = 0x14 + CodeOffset; - - *reinterpret_cast(Shellcode + 0x00) = pArg; - *reinterpret_cast(Shellcode + 0x04) = pRoutine; - - *reinterpret_cast(Shellcode + 0x0F + CodeOffset) = - reinterpret_cast(pCallNextHookEx) - (reinterpret_cast(pCodecave) + 0x0E + CodeOffset) - 5; - *reinterpret_cast(Shellcode + 0x18 + CodeOffset) = pCodecave; - -#endif - - if (!WriteProcessMemory(hTargetProc, pCodecave, Shellcode, sizeof(Shellcode), nullptr)) { - LastWin32Error = GetLastError(); - - VirtualFreeEx(hTargetProc, pCodecave, 0, MEM_RELEASE); - - return false; - } - - static EnumWindowsCallback_Data data; - data.m_HookData.clear(); - - data.m_pHook = reinterpret_cast(reinterpret_cast(pCodecave) + CodeOffset); - data.m_PID = GetProcessId(hTargetProc); - data.m_hModule = GetModuleHandle(TEXT("user32.dll")); - - WNDENUMPROC EnumWindowsCallback = [](HWND hWnd, LPARAM) -> BOOL { - DWORD winPID = 0; - DWORD winTID = GetWindowThreadProcessId(hWnd, &winPID); - if (winPID == data.m_PID) { - TCHAR szWindow[MAX_PATH]{0}; - if (IsWindowVisible(hWnd) && GetWindowText(hWnd, szWindow, MAX_PATH)) { - if (GetClassName(hWnd, szWindow, MAX_PATH) && _tcscmp(szWindow, TEXT("ConsoleWindowClass"))) { - HHOOK hHook = SetWindowsHookEx(WH_CALLWNDPROC, data.m_pHook, data.m_hModule, winTID); - if (hHook) { - data.m_HookData.push_back({hHook, hWnd}); - } - } - } - } - - return TRUE; - }; - - if (!EnumWindows(EnumWindowsCallback, reinterpret_cast(&data))) { - LastWin32Error = GetLastError(); - - VirtualFreeEx(hTargetProc, pCodecave, 0, MEM_RELEASE); - - return false; - } - - if (data.m_HookData.empty()) { - VirtualFreeEx(hTargetProc, pCodecave, 0, MEM_RELEASE); - - return false; - } - - HWND hForegroundWnd = GetForegroundWindow(); - for (auto i : data.m_HookData) { - SetForegroundWindow(i.m_hWnd); - SendMessageA(i.m_hWnd, WM_KEYDOWN, VK_SPACE, 0); - Sleep(10); - SendMessageA(i.m_hWnd, WM_KEYUP, VK_SPACE, 0); - UnhookWindowsHookEx(i.m_hHook); - } - SetForegroundWindow(hForegroundWnd); - - DWORD Timer = GetTickCount(); - BYTE CheckByte = 0; - - do { - ReadProcessMemory(hTargetProc, reinterpret_cast(pCodecave) + CheckByteOffset, &CheckByte, 1, nullptr); - - if (GetTickCount() - Timer > 5000) { - return false; - } - - Sleep(10); - - } while (!CheckByte); - - ReadProcessMemory(hTargetProc, pCodecave, &Out, sizeof(Out), nullptr); - - VirtualFreeEx(hTargetProc, pCodecave, 0, MEM_RELEASE); - - return true; -} - -bool InjectDll(HANDLE hProc, const TCHAR *szPath) { - if (!hProc) { - DWORD dwErr = GetLastError(); - printf("OpenProcess failed: 0x%08X\n", dwErr); - - return false; - } - - auto len = _tcslen(szPath) * sizeof(TCHAR); - void *pArg = VirtualAllocEx(hProc, nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (!pArg) { - DWORD dwErr = GetLastError(); - printf("VirtualAllocEx failed: 0x%08X\n", dwErr); - - CloseHandle(hProc); - - return false; - } - - BOOL bRet = WriteProcessMemory(hProc, pArg, szPath, len, nullptr); - if (!bRet) { - DWORD dwErr = GetLastError(); - printf("WriteProcessMemory failed: 0x%08X\n", dwErr); - - VirtualFreeEx(hProc, pArg, 0, MEM_RELEASE); - CloseHandle(hProc); - - return false; - } - - f_Routine *p_LoadLibrary = - reinterpret_cast(GetProcAddressEx(hProc, TEXT("kernel32.dll"), LOAD_LIBRARY_NAME)); - if (!p_LoadLibrary) { - printf("Can't find LoadLibrary\n"); - - VirtualFreeEx(hProc, pArg, 0, MEM_RELEASE); - CloseHandle(hProc); - - return false; - } - - UINT_PTR hDllOut = 0; - DWORD last_error = 0; - bool dwErr = SR_SetWindowsHookEx(hProc, p_LoadLibrary, pArg, last_error, hDllOut); - - CloseHandle(hProc); + const auto loadLib = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); - if (!dwErr) { - printf("StartRoutine failed\n"); - printf("LastWin32Error: 0x%08X\n", last_error); + const auto thread = + CreateRemoteThreadEx(proc, nullptr, 0, (PTHREAD_START_ROUTINE)loadLib, dllAddr, 0, nullptr, nullptr); - return false; + if (!thread) { + std::cout << "Failed to create remote thread" << std::endl; + return thread; } - printf("Success! LoadLibrary returned 0x%p\n", reinterpret_cast(hDllOut)); + std::cout << "Created remote thread for loading DLL" << std::endl; - return true; -} + return thread; +} \ No newline at end of file diff --git a/injector/main.cpp b/injector/main.cpp index 1c7b6b2..8c79631 100644 --- a/injector/main.cpp +++ b/injector/main.cpp @@ -1,6 +1,5 @@ #include "config.hpp" #include "inject.hpp" -#include "manual.hpp" #define ThreadQuerySetWin32StartAddress 9 @@ -97,121 +96,17 @@ DWORD GetProcId(const wchar_t *procName) { return procId; } -uintptr_t GetModuleBaseAddress(HANDLE handleSnapshot, const wchar_t *moduleName) { - uintptr_t address = 0; - - if (handleSnapshot != INVALID_HANDLE_VALUE) { - MODULEENTRY32 moduleEntry; - - moduleEntry.dwSize = sizeof(moduleEntry); - - if (!Module32First(handleSnapshot, &moduleEntry)) { - return address; - } - - do { - if (_wcsicmp(moduleEntry.szModule, moduleName) == 0) { - address = (uintptr_t)moduleEntry.modBaseAddr; - break; - } - } while (Module32Next(handleSnapshot, &moduleEntry)); - } - - return address; -} - -bool SuspendProtection(HANDLE hProcess, DWORD pid, uintptr_t protAddr, HANDLE ntdllHandle) { - THREADENTRY32 te32{}; - HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); - te32.dwSize = sizeof(te32); - for (Thread32First(hThreadSnap, &te32); Thread32Next(hThreadSnap, &te32);) { - if (te32.th32OwnerProcessID == pid) { - PVOID threadInfo; - ULONG retLen; - auto NtQueryInformationThread = - (NtQueryInformationThread_t)GetProcAddress((HMODULE)ntdllHandle, "NtQueryInformationThread"); - if (NtQueryInformationThread == nullptr) - return false; - - HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, te32.th32ThreadID); - NTSTATUS ntqiRet = - NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &threadInfo, sizeof(PVOID), &retLen); - - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQueryEx(hProcess, (LPCVOID)threadInfo, &mbi, sizeof(mbi))) { - auto baseAddress = reinterpret_cast(mbi.AllocationBase); - if (baseAddress == protAddr) { - std::cout << "Suspending protection thread" << std::endl; - SuspendThread(hThread); - CloseHandle(hThread); - return true; - } - } - } - } - CloseHandle(hThreadSnap); - return false; -} - -void undoProtection(HANDLE handle) { - std::cout << "Attempting to undo protection..." << std::endl; - - const auto procId = GetProcessId(handle); - const auto names = {"NtQueryAttributesFile", "NtCreateThread", "NtCreateThreadEx", "LdrInitializeThunk"}; - const auto originals = {_byteswap_uint64(0x4C8BD1B83D000000), _byteswap_uint64(0x4C8BD1B84E000000), - _byteswap_uint64(0x4C8BD1B8C2000000), _byteswap_uint64(0x40534883EC20488B)}; - - uintptr_t QRSL_es = 0; - uintptr_t ntdllHandle = 0; - uint8_t restored = 0; - - do { - const auto moduleSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId); - QRSL_es = GetModuleBaseAddress(moduleSnapshot, L"QRSL_es.dll"); - ntdllHandle = GetModuleBaseAddress(moduleSnapshot, L"ntdll.dll"); - CloseHandle(moduleSnapshot); - } while (!QRSL_es || !ntdllHandle); - - std::cout << std::hex; - std::cout << "QRSL_es.dll: " << QRSL_es << std::endl; - std::cout << "ntdll.dll: " << ntdllHandle << std::endl; - - std::cout << "Waiting for a few seconds before restoring hooked functions..." << std::endl; - - std::this_thread::sleep_for(std::chrono::seconds(10)); - - while (restored < names.size()) { - for (size_t i = 0; i < names.size(); i++) { - const auto name = *(names.begin() + i); - const auto original = *(originals.begin() + i); - const auto byteArr = reinterpret_cast(&original); - const auto address = (uintptr_t)GetProcAddress((HMODULE)ntdllHandle, name); - if (address) { - size_t current; - ReadProcessMemory(handle, (LPCVOID)address, ¤t, sizeof(size_t), nullptr); - - if (original != current) { - std::cout << "Restoring " << name << std::endl; - DWORD oldProtect; - VirtualProtectEx(handle, (LPVOID)address, sizeof(size_t), PAGE_EXECUTE_READWRITE, &oldProtect); - WriteProcessMemory(handle, (LPVOID)address, byteArr, sizeof(size_t), nullptr); - VirtualProtectEx(handle, (LPVOID)address, sizeof(size_t), oldProtect, nullptr); - restored++; - } - } - } - } - - SuspendProtection(handle, procId, QRSL_es, (HANDLE)ntdllHandle); -} +HWND launcherWindow = NULL; int main() { - const auto launcherPid = GetProcId(L"tof_launcher.exe"); + auto launcherPid = GetProcId(L"tof_launcher.exe"); if (launcherPid != 0) { std::cout << "Launcher is already running." << std::endl; - std::cout << "If it wasn't started by this injector. Please close it and launch the injector again." - << std::endl; + std::cout + << "If you're running the official build of the game, please close the launcher and run the injector again." + << std::endl; + std::cout << "If you're running the Steam build of the game, you can ignore this message" << std::endl; } const uint16_t pathSize = 2048; @@ -248,16 +143,24 @@ int main() { return 1; } - auto configuredInjectionMethod = Config::get("/injectionMethod", ""); - std::string injectionMethod = "manual"; + launcherPid = GetProcId(L"tof_launcher.exe"); + const auto launcherProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, launcherPid); + + // Sleep for a bit to wait for the launcher to start + Sleep(1000); + + const auto thread = InjectDll(launcherProc, directory + L"_aux.dll"); + WaitForSingleObject(thread, INFINITE); - if (!configuredInjectionMethod->empty()) { - injectionMethod = std::string(configuredInjectionMethod->begin(), configuredInjectionMethod->end()); - } else { - configuredInjectionMethod = injectionMethod; + launcherWindow = FindWindow(L"TWINCONTROL", L"Tower of Fantasy"); + + if (launcherWindow == nullptr) { + std::cout << "Failed to find launcher window." << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(3)); + return 1; } - std::cout << "Injection method: " << injectionMethod << std::endl; + SendMessage(launcherWindow, WM_USER + 0x420, (uintptr_t)CreateProcessW, 0); std::cout << "Launcher has been started. Please start the game from the launcher." << std::endl; @@ -269,54 +172,11 @@ int main() { if (qrslPid != 0) { break; } - - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - - auto shouldUndoProtection = Config::get("/undoProtection", false); - - HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, qrslPid); - bool result = false; - - if (*shouldUndoProtection) { - undoProtection(proc); } - const auto dllPath = directory + L"TOFInternal.dll"; - - std::wcout << L"Injecting " + dllPath << std::endl; - - if (injectionMethod == "manual") { - std::ifstream dllFile(dllPath, std::ios::binary | std::ios::ate); - auto dllSize = dllFile.tellg(); - BYTE *pSrcData = new BYTE[(UINT_PTR)dllSize]; - dllFile.seekg(0, std::ios::beg); - dllFile.read((char *)(pSrcData), dllSize); - dllFile.close(); - - result = - ManualMapDll(proc, pSrcData, dllSize, "preMain", directory.c_str(), directory.size() * 2); - - delete[] pSrcData; - } else if (injectionMethod == "loadLibrary") { - result = InjectDll(proc, dllPath.c_str()); - } else { - std::cout << "Invalid injection method." << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(3)); - return 1; - } - - if (result) { - std::cout << "Injected successfully." << std::endl; - } else { - std::cout << "Failed to inject." << std::endl; - } - - CloseHandle(proc); - Config::shutdown(); - std::this_thread::sleep_for(std::chrono::seconds(4)); + ShellExecuteW(nullptr, nullptr, L"proc.exe", std::to_wstring(qrslPid).c_str(), nullptr, SW_HIDE); return 0; } \ No newline at end of file diff --git a/injector/manual.hpp b/injector/manual.hpp deleted file mode 100644 index 848671f..0000000 --- a/injector/manual.hpp +++ /dev/null @@ -1,578 +0,0 @@ -using f_LoadLibraryA = HINSTANCE(WINAPI *)(const char *lpLibFilename); -using f_GetProcAddress = FARPROC(WINAPI *)(HMODULE hModule, LPCSTR lpProcName); -using f_DLL_ENTRY_POINT = BOOL(WINAPI *)(void *hDll, DWORD dwReason, void *pReserved); - -#ifdef _WIN64 -using f_RtlAddFunctionTable = BOOL(WINAPIV *)(PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount, DWORD64 BaseAddress); -#endif - -template struct MANUAL_MAPPING_DATA { - f_LoadLibraryA pLoadLibraryA; - f_GetProcAddress pGetProcAddress; -#ifdef _WIN64 - f_RtlAddFunctionTable pRtlAddFunctionTable; -#endif - BYTE *pbase; - HINSTANCE hMod; - DWORD fdwReasonParam; - LPVOID reservedParam; - BOOL SEHSupport; - void (*preMain)(T); - T preMainArg; -}; - -// Note: Exception support only x64 with build params /EHa or /EHc -template -bool ManualMapDll(HANDLE hProc, BYTE *pSrcData, SIZE_T FileSize, const char *preMain = "", T preMainArg = nullptr, - uint32_t preMainArgSize = 0, bool ClearHeader = true, bool ClearNonNeededSections = true, - bool AdjustProtections = true, bool SEHExceptionSupport = true, DWORD fdwReason = DLL_PROCESS_ATTACH, - LPVOID lpReserved = 0); - -template void __stdcall loader(MANUAL_MAPPING_DATA *pData); - -#if defined(DISABLE_OUTPUT) -#define ILog(data, ...) -#else -#define ILog(text, ...) printf(text, __VA_ARGS__); -#endif - -#ifdef _WIN64 -#define CURRENT_ARCH IMAGE_FILE_MACHINE_AMD64 -#else -#define CURRENT_ARCH IMAGE_FILE_MACHINE_I386 -#endif - -template -bool ManualMapDll(HANDLE hProc, BYTE *pSrcData, SIZE_T FileSize, const char *preMain, T preMainArg, - uint32_t preMainArgSize, bool ClearHeader, bool ClearNonNeededSections, bool AdjustProtections, - bool SEHExceptionSupport, DWORD fdwReason, LPVOID lpReserved) { - IMAGE_NT_HEADERS *pOldNtHeader = nullptr; - IMAGE_OPTIONAL_HEADER *pOldOptHeader = nullptr; - IMAGE_FILE_HEADER *pOldFileHeader = nullptr; - BYTE *pTargetBase = nullptr; - - if (reinterpret_cast(pSrcData)->e_magic != 0x5A4D) { //"MZ" - ILog("Invalid file\n"); - return false; - } - - pOldNtHeader = - reinterpret_cast(pSrcData + reinterpret_cast(pSrcData)->e_lfanew); - pOldOptHeader = &pOldNtHeader->OptionalHeader; - pOldFileHeader = &pOldNtHeader->FileHeader; - - if (pOldFileHeader->Machine != CURRENT_ARCH) { - ILog("Invalid platform\n"); - return false; - } - - ILog("File ok\n"); - - pTargetBase = reinterpret_cast( - VirtualAllocEx(hProc, nullptr, pOldOptHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); - if (!pTargetBase) { - ILog("Target process memory allocation failed (ex) 0x%X\n", GetLastError()); - return false; - } - - DWORD oldp = 0; - VirtualProtectEx(hProc, pTargetBase, pOldOptHeader->SizeOfImage, PAGE_EXECUTE_READWRITE, &oldp); - - // File header - if (!WriteProcessMemory(hProc, pTargetBase, pSrcData, 0x1000, nullptr)) { // only first 0x1000 bytes for the header - ILog("Can't write file header 0x%X\n", GetLastError()); - VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); - return false; - } - - IMAGE_SECTION_HEADER *pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader); - for (UINT i = 0; i != pOldFileHeader->NumberOfSections; ++i, ++pSectionHeader) { - if (pSectionHeader->SizeOfRawData) { - if (!WriteProcessMemory(hProc, pTargetBase + pSectionHeader->VirtualAddress, - pSrcData + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData, - nullptr)) { - ILog("Can't map sections: 0x%x\n", GetLastError()); - VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); - return false; - } - } - } - - void *resolvedPreMain = nullptr; - - if (strlen(preMain) > 0) { - IMAGE_EXPORT_DIRECTORY exportDir; - ReadProcessMemory(hProc, - pTargetBase + pOldOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, - &exportDir, sizeof(exportDir), nullptr); - - for (DWORD i = 0; i < exportDir.NumberOfNames; i++) { - char szName[256]; - DWORD nameOffset = 0; - ReadProcessMemory(hProc, pTargetBase + exportDir.AddressOfNames + i * 4, &nameOffset, sizeof(nameOffset), - nullptr); - const auto nameAddr = pTargetBase + nameOffset; - ReadProcessMemory(hProc, nameAddr, &szName, sizeof(szName), nullptr); - - if (strcmp(szName, preMain) == 0) { - DWORD funcRva = 0; - ReadProcessMemory(hProc, pTargetBase + exportDir.AddressOfFunctions + i * 4, &funcRva, sizeof(funcRva), - nullptr); - - resolvedPreMain = pTargetBase + funcRva; - ILog("Found %s at %p\n", preMain, pTargetBase + funcRva); - break; - } - } - } - - LPVOID pPreMainArg = 0; - - if (preMainArgSize > 0) { - pPreMainArg = VirtualAllocEx(hProc, nullptr, preMainArgSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - WriteProcessMemory(hProc, pPreMainArg, preMainArg, preMainArgSize, nullptr); - } - - MANUAL_MAPPING_DATA data{0}; - data.pLoadLibraryA = LoadLibraryA; - data.pGetProcAddress = GetProcAddress; -#ifdef _WIN64 - data.pRtlAddFunctionTable = (f_RtlAddFunctionTable)RtlAddFunctionTable; -#else - SEHExceptionSupport = false; -#endif - data.pbase = pTargetBase; - data.fdwReasonParam = fdwReason; - data.reservedParam = lpReserved; - data.SEHSupport = SEHExceptionSupport; - data.preMain = (void (*)(T))resolvedPreMain; - data.preMainArg = (T)pPreMainArg; - - // Mapping params - BYTE *MappingDataAlloc = reinterpret_cast( - VirtualAllocEx(hProc, nullptr, sizeof(MANUAL_MAPPING_DATA), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); - if (!MappingDataAlloc) { - ILog("Target process mapping allocation failed (ex) 0x%X\n", GetLastError()); - VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); - return false; - } - - if (!WriteProcessMemory(hProc, MappingDataAlloc, &data, sizeof(MANUAL_MAPPING_DATA), nullptr)) { - ILog("Can't write mapping 0x%X\n", GetLastError()); - VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); - VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); - return false; - } - - // Shell code - void *pShellcode = VirtualAllocEx(hProc, nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (!pShellcode) { - ILog("Memory allocation failed (ex) 0x%X\n", GetLastError()); - VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); - VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); - return false; - } - - if (!WriteProcessMemory(hProc, pShellcode, loader, 0x1000, nullptr)) { - ILog("Can't write code 0x%X\n", GetLastError()); - VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); - VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); - VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE); - return false; - } - - ILog("Mapped DLL at %p\n", pTargetBase); - ILog("Mapping info at %p\n", MappingDataAlloc); - ILog("Code at %p\n", pShellcode); - -#ifdef _DEBUG - ILog("My shellcode pointer %p\n", Shellcode); - ILog("Target point %p\n", pShellcode); - system("pause"); -#endif - - THREADENTRY32 TE32{0}; - TE32.dwSize = sizeof(TE32); - - HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetProcessId(hProc)); - if (hSnap == INVALID_HANDLE_VALUE) { - return false; - } - - DWORD dwTargetPID = GetProcessId(hProc); - DWORD ThreadID = 0; - - BOOL bRet = Thread32First(hSnap, &TE32); - if (!bRet) { - CloseHandle(hSnap); - return false; - } - - do { - if (TE32.th32OwnerProcessID == dwTargetPID) { - ThreadID = TE32.th32ThreadID; - break; - } - - bRet = Thread32Next(hSnap, &TE32); - } while (bRet); - - if (!ThreadID) { - return false; - } - - HANDLE hThread = OpenThread(THREAD_SET_CONTEXT | THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, FALSE, ThreadID); - if (!hThread) { - return false; - } - - if (SuspendThread(hThread) == (DWORD)-1) { - CloseHandle(hThread); - return false; - } - - CONTEXT OldContext{0}; - OldContext.ContextFlags = CONTEXT_CONTROL; - - if (!GetThreadContext(hThread, &OldContext)) { - ResumeThread(hThread); - CloseHandle(hThread); - return false; - } - - void *pCodecave = VirtualAllocEx(hProc, nullptr, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (!pCodecave) { - ResumeThread(hThread); - CloseHandle(hThread); - return false; - } - -#ifdef _WIN64 - - BYTE Shellcode[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // - 0x08 -> returned value - - 0x48, 0x83, 0xEC, 0x08, // + 0x00 -> sub rsp, 0x08 - - 0xC7, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, // + 0x04 (+ 0x07) -> mov [rsp], RipLowPart - 0xC7, 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, // + 0x0B (+ 0x0F) -> mov [rsp + 0x04], RipHighPart - - 0x50, 0x51, 0x52, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, - 0x53, // + 0x13 -> push r(a/c/d)x / r(8 - 11) - 0x9C, // + 0x1E -> pushfq - - 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x1F (+ 0x21) -> mov rax, pRoutine - 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x29 (+ 0x2B) -> mov rcx, pArg - - 0x48, 0x83, 0xEC, 0x20, // + 0x33 -> sub rsp, 0x20 - 0xFF, 0xD0, // + 0x37 -> call rax - 0x48, 0x83, 0xC4, 0x20, // + 0x39 -> add rsp, 0x20 - - 0x48, 0x8D, 0x0D, 0xB4, 0xFF, 0xFF, 0xFF, // + 0x3D -> lea rcx, [pCodecave] - 0x48, 0x89, 0x01, // + 0x44 -> mov [rcx], rax - - 0x9D, // + 0x47 -> popfq - 0x41, 0x5B, 0x41, 0x5A, 0x41, 0x59, 0x41, 0x58, 0x5A, 0x59, - 0x58, // + 0x48 -> pop r(11-8) / r(d/c/a)x - - 0xC6, 0x05, 0xA9, 0xFF, 0xFF, 0xFF, 0x00, // + 0x53 -> mov byte ptr[$ - 0x57], 0 - - 0xC3 // + 0x5A -> ret - }; // SIZE = 0x5B (+ 0x08) - - DWORD FuncOffset = 0x08; - DWORD CheckByteOffset = 0x03 + FuncOffset; - - DWORD dwLoRIP = (DWORD)(OldContext.Rip & 0xFFFFFFFF); - DWORD dwHiRIP = (DWORD((OldContext.Rip) >> 0x20) & 0xFFFFFFFF); - - *reinterpret_cast(Shellcode + 0x07 + FuncOffset) = dwLoRIP; - *reinterpret_cast(Shellcode + 0x0F + FuncOffset) = dwHiRIP; - - *reinterpret_cast(Shellcode + 0x21 + FuncOffset) = pShellcode; - *reinterpret_cast(Shellcode + 0x2B + FuncOffset) = MappingDataAlloc; - - OldContext.Rip = reinterpret_cast(pCodecave) + FuncOffset; - -#else - - BYTE Shellcode[] = { - 0x00, 0x00, 0x00, 0x00, // - 0x04 (pCodecave) -> returned value - // ;buffer to store returned value (eax) - - 0x83, 0xEC, 0x04, // + 0x00 -> sub esp, 0x04 - // ;prepare stack for ret - 0xC7, 0x04, 0x24, 0x00, 0x00, 0x00, - 0x00, // + 0x03 (+ 0x06) -> mov [esp], OldEip ;store - // old eip as return address - - 0x50, 0x51, 0x52, // + 0x0A -> psuh e(a/c/d) - // ;save e(a/c/d)x - 0x9C, // + 0x0D -> pushfd - // ;save flags register - - 0xB9, 0x00, 0x00, 0x00, 0x00, // + 0x0E (+ 0x0F) -> mov ecx, pArg - // ;load pArg into ecx - 0xB8, 0x00, 0x00, 0x00, 0x00, // + 0x13 (+ 0x14) -> mov eax, pRoutine - - 0x51, // + 0x18 -> push ecx - // ;push pArg - 0xFF, 0xD0, // + 0x19 -> call eax - // ;call target function - - 0xA3, 0x00, 0x00, 0x00, 0x00, // + 0x1B (+ 0x1C) -> mov dword ptr[pCodecave], eax - // ;store returned value - - 0x9D, // + 0x20 -> popfd - // ;restore flags register - 0x5A, 0x59, 0x58, // + 0x21 -> pop e(d/c/a) - // ;restore e(d/c/a)x - - 0xC6, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, // + 0x24 (+ 0x26) -> mov byte ptr[pCodecave + 0x06], 0x00 ;set checkbyte to 0 - - 0xC3 // + 0x2B -> ret - // ;return to OldEip - }; // SIZE = 0x2C (+ 0x04) - - DWORD FuncOffset = 0x04; - DWORD CheckByteOffset = 0x02 + FuncOffset; - - *reinterpret_cast(Shellcode + 0x06 + FuncOffset) = OldContext.Eip; - - *reinterpret_cast(Shellcode + 0x0F + FuncOffset) = pArg; - *reinterpret_cast(Shellcode + 0x14 + FuncOffset) = pRoutine; - - *reinterpret_cast(Shellcode + 0x1C + FuncOffset) = pCodecave; - *reinterpret_cast(Shellcode + 0x26 + FuncOffset) = reinterpret_cast(pCodecave) + CheckByteOffset; - - OldContext.Eip = reinterpret_cast(pCodecave) + FuncOffset; - -#endif - - if (!WriteProcessMemory(hProc, pCodecave, Shellcode, sizeof(Shellcode), nullptr)) { - ResumeThread(hThread); - CloseHandle(hThread); - VirtualFreeEx(hProc, pCodecave, 0, MEM_RELEASE); - return false; - } - - if (!SetThreadContext(hThread, &OldContext)) { - ResumeThread(hThread); - CloseHandle(hThread); - VirtualFreeEx(hProc, pCodecave, 0, MEM_RELEASE); - return false; - } - - if (ResumeThread(hThread) == (DWORD)-1) { - CloseHandle(hThread); - VirtualFreeEx(hProc, pCodecave, 0, MEM_RELEASE); - return false; - } - - CloseHandle(hThread); - - ILog("Thread created at: %p, waiting for return...\n", pShellcode); - - HINSTANCE hCheck = NULL; - while (!hCheck) { - DWORD exitcode = 0; - GetExitCodeProcess(hProc, &exitcode); - if (exitcode != STILL_ACTIVE) { - ILog("Process crashed, exit code: %d\n", exitcode); - return false; - } - - MANUAL_MAPPING_DATA data_checked{0}; - ReadProcessMemory(hProc, MappingDataAlloc, &data_checked, sizeof(data_checked), nullptr); - hCheck = data_checked.hMod; - - if (hCheck == (HINSTANCE)0x404040) { - ILog("Wrong mapping ptr\n"); - VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); - VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); - VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE); - return false; - } else if (hCheck == (HINSTANCE)0x505050) { - ILog("WARNING: Exception support failed!\n"); - } - - Sleep(10); - } - - BYTE *emptyBuffer = (BYTE *)malloc(1024 * 1024 * 20); - if (emptyBuffer == nullptr) { - ILog("Unable to allocate memory\n"); - return false; - } - memset(emptyBuffer, 0, 1024 * 1024 * 20); - - // CLEAR PE HEAD - if (ClearHeader) { - if (!WriteProcessMemory(hProc, pTargetBase, emptyBuffer, 0x1000, nullptr)) { - ILog("WARNING!: Can't clear HEADER\n"); - } - } - // END CLEAR PE HEAD - - if (ClearNonNeededSections) { - pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader); - for (UINT i = 0; i != pOldFileHeader->NumberOfSections; ++i, ++pSectionHeader) { - if (pSectionHeader->Misc.VirtualSize) { - if ((SEHExceptionSupport ? 0 : strcmp((char *)pSectionHeader->Name, ".pdata") == 0) || - strcmp((char *)pSectionHeader->Name, ".rsrc") == 0 || - strcmp((char *)pSectionHeader->Name, ".reloc") == 0) { - ILog("Processing %s removal\n", pSectionHeader->Name); - if (!WriteProcessMemory(hProc, pTargetBase + pSectionHeader->VirtualAddress, emptyBuffer, - pSectionHeader->Misc.VirtualSize, nullptr)) { - ILog("Can't clear section %s: 0x%x\n", pSectionHeader->Name, GetLastError()); - } - } - } - } - } - - if (AdjustProtections) { - pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader); - for (UINT i = 0; i != pOldFileHeader->NumberOfSections; ++i, ++pSectionHeader) { - if (pSectionHeader->Misc.VirtualSize) { - DWORD old = 0; - DWORD newP = PAGE_READONLY; - - if ((pSectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) > 0) { - newP = PAGE_READWRITE; - } else if ((pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) > 0) { - newP = PAGE_EXECUTE_READ; - } - if (VirtualProtectEx(hProc, pTargetBase + pSectionHeader->VirtualAddress, - pSectionHeader->Misc.VirtualSize, newP, &old)) { - ILog("section %s set as %lX\n", (char *)pSectionHeader->Name, newP); - } else { - ILog("FAIL: section %s not set as %lX\n", (char *)pSectionHeader->Name, newP); - } - } - } - DWORD old = 0; - VirtualProtectEx(hProc, pTargetBase, IMAGE_FIRST_SECTION(pOldNtHeader)->VirtualAddress, PAGE_READONLY, &old); - } - - WriteProcessMemory(hProc, pShellcode, emptyBuffer, 0x1000, nullptr); - VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE); - VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); - - return true; -} - -#define RELOC_FLAG32(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_HIGHLOW) -#define RELOC_FLAG64(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_DIR64) - -#ifdef _WIN64 -#define RELOC_FLAG RELOC_FLAG64 -#else -#define RELOC_FLAG RELOC_FLAG32 -#endif - -#pragma runtime_checks("", off) -#pragma optimize("", off) -template void __stdcall loader(MANUAL_MAPPING_DATA *pData) { - if (!pData) { - pData->hMod = (HINSTANCE)0x404040; - return; - } - - BYTE *pBase = pData->pbase; - auto *pOpt = - &reinterpret_cast(pBase + reinterpret_cast((uintptr_t)pBase)->e_lfanew) - ->OptionalHeader; - - auto _LoadLibraryA = pData->pLoadLibraryA; - auto _GetProcAddress = pData->pGetProcAddress; -#ifdef _WIN64 - auto _RtlAddFunctionTable = pData->pRtlAddFunctionTable; -#endif - auto _DllMain = reinterpret_cast(pBase + pOpt->AddressOfEntryPoint); - - BYTE *LocationDelta = pBase - pOpt->ImageBase; - if (LocationDelta) { - if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) { - auto *pRelocData = reinterpret_cast( - pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); - const auto *pRelocEnd = reinterpret_cast( - reinterpret_cast(pRelocData) + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); - while (pRelocData < pRelocEnd && pRelocData->SizeOfBlock) { - UINT AmountOfEntries = (pRelocData->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); - WORD *pRelativeInfo = reinterpret_cast(pRelocData + 1); - - for (UINT i = 0; i != AmountOfEntries; ++i, ++pRelativeInfo) { - if (RELOC_FLAG(*pRelativeInfo)) { - UINT_PTR *pPatch = reinterpret_cast(pBase + pRelocData->VirtualAddress + - ((*pRelativeInfo) & 0xFFF)); - *pPatch += reinterpret_cast(LocationDelta); - } - } - pRelocData = reinterpret_cast(reinterpret_cast(pRelocData) + - pRelocData->SizeOfBlock); - } - } - } - - if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) { - auto *pImportDescr = reinterpret_cast( - pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); - while (pImportDescr->Name) { - char *szMod = reinterpret_cast(pBase + pImportDescr->Name); - HINSTANCE hDll = _LoadLibraryA(szMod); - - ULONG_PTR *pThunkRef = reinterpret_cast(pBase + pImportDescr->OriginalFirstThunk); - ULONG_PTR *pFuncRef = reinterpret_cast(pBase + pImportDescr->FirstThunk); - - if (!pThunkRef) - pThunkRef = pFuncRef; - - for (; *pThunkRef; ++pThunkRef, ++pFuncRef) { - if (IMAGE_SNAP_BY_ORDINAL(*pThunkRef)) { - *pFuncRef = (ULONG_PTR)_GetProcAddress(hDll, reinterpret_cast(*pThunkRef & 0xFFFF)); - } else { - auto *pImport = reinterpret_cast(pBase + (*pThunkRef)); - *pFuncRef = (ULONG_PTR)_GetProcAddress(hDll, pImport->Name); - } - } - ++pImportDescr; - } - } - - if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) { - auto *pTLS = reinterpret_cast( - pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); - auto *pCallback = reinterpret_cast(pTLS->AddressOfCallBacks); - for (; pCallback && *pCallback; ++pCallback) - (*pCallback)(pBase, DLL_PROCESS_ATTACH, nullptr); - } - - bool ExceptionSupportFailed = false; - -#ifdef _WIN64 - - if (pData->SEHSupport) { - auto excep = pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; - if (excep.Size) { - if (!_RtlAddFunctionTable(reinterpret_cast(pBase + excep.VirtualAddress), - excep.Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY), (DWORD64)pBase)) { - ExceptionSupportFailed = true; - } - } - } - -#endif - - if (pData->preMain != nullptr) { - pData->preMain(pData->preMainArg); - } - - _DllMain(pBase, pData->fdwReasonParam, pData->reservedParam); - - if (ExceptionSupportFailed) - pData->hMod = reinterpret_cast(0x505050); - else - pData->hMod = reinterpret_cast(pBase); -} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64189e8..295289c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,7 @@ add_library(TOFInternal SHARED feats/esp/particle_fish.cpp feats/esp/fish_baiter.cpp ) +add_executable(proc proc.cpp) target_precompile_headers(TOFInternal PRIVATE pch.hpp) add_subdirectory(ext/imgui) add_subdirectory(ext/kiero) diff --git a/src/proc.cpp b/src/proc.cpp new file mode 100644 index 0000000..14b8549 --- /dev/null +++ b/src/proc.cpp @@ -0,0 +1,23 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include + +#include "../injector/inject.hpp" + +int main(int argc, char **argv) { + if (argc < 2) { + return 1; + } + + const auto hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, atoi(argv[1])); + if (!hProc) { + return 1; + } + + const auto path = std::filesystem::current_path().wstring() + L"\\TOFInternal.dll"; + InjectDll(hProc, path); + + return 0; +} \ No newline at end of file