// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include "MinHook.h" #include <string> #include <sstream> #include <filesystem> #include <iostream> #include <iomanip> #define DLL_NAME L"Northstar.dll" class TempReadWrite { private: DWORD m_origProtection; void* m_ptr; public: TempReadWrite(void* ptr) { m_ptr = ptr; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(m_ptr, &mbi, sizeof(mbi)); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect); m_origProtection = mbi.Protect; } ~TempReadWrite() { MEMORY_BASIC_INFORMATION mbi; VirtualQuery(m_ptr, &mbi, sizeof(mbi)); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, m_origProtection, &mbi.Protect); } }; typedef BOOL(WINAPI *CreateProcessWType)( LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); CreateProcessWType CreateProcessWOriginal; HMODULE ownHModule; std::filesystem::path tf2DirPath; BOOL WINAPI CreateProcessWHook( LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { bool isTitanfallProcess = false; // origin doesn't use lpApplicationName std::wcout << lpCommandLine << std::endl; isTitanfallProcess = wcsstr(lpCommandLine, L"Titanfall2\\Titanfall2.exe"); // steam will start processes suspended (since we don't actually inject into steam directly this isn't required anymore, but whatever) bool alreadySuspended = dwCreationFlags & CREATE_SUSPENDED; // suspend process on creation so we can hook if (isTitanfallProcess && !alreadySuspended) dwCreationFlags |= CREATE_SUSPENDED; BOOL ret = CreateProcessWOriginal(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); if (isTitanfallProcess) { std::cout << "Creating titanfall process!" << std::endl; std::cout << "Handle: " << lpProcessInformation->hProcess << " ID: " << lpProcessInformation->dwProcessId << " Thread: " << lpProcessInformation->hThread << std::endl; STARTUPINFO si; memset(&si, 0, sizeof(si)); PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(pi)); // check if we're launching EASteamProxy for steam users, or just launching tf2 directly for origin users // note: atm we fully disable steam integration in origin when we inject, return to this later if (!wcsstr(lpApplicationName, L"Origin\\EASteamProxy.exe")) { std::stringstream argStr; argStr << lpProcessInformation->dwProcessId; argStr << " "; argStr << lpProcessInformation->dwThreadId; CreateProcessA((tf2DirPath / "InjectionProxy64.exe").string().c_str(), (LPSTR)(argStr.str().c_str()), 0, 0, false, 0, 0, tf2DirPath.string().c_str(), (LPSTARTUPINFOA)&si, &pi); WaitForSingleObject(pi.hThread, INFINITE); } else { // for easteamproxy, we have to inject ourself into it // todo: atm we fully disable steam integration in origin when we inject, do this properly later } // this doesn't seem to work super well //if (!alreadySuspended) ResumeThread(lpProcessInformation->hThread); // cleanup MH_DisableHook(&CreateProcessW); MH_RemoveHook(&CreateProcessW); MH_Uninitialize(); // allow steam integrations to work again void* ptr = (char*)GetModuleHandleA("OriginClient.dll") + 0x2A83FA; TempReadWrite rw(ptr); *((char*)ptr) = 0x0F; // jmp => je *((char*)ptr + 1) = 0x84; *((char*)ptr + 2) = 0xE5; *((char*)ptr + 3) = 0x01; *((char*)ptr + 4) = 0x00; *((char*)ptr + 5) = 0x00; // is this undefined behaviour? idk FreeLibrary(ownHModule); } return ret; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: DisableThreadLibraryCalls(hModule); case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } //AllocConsole(); //freopen("CONOUT$", "w", stdout); ownHModule = hModule; char ownDllPath[MAX_PATH]; GetModuleFileNameA(hModule, ownDllPath, MAX_PATH); tf2DirPath = std::filesystem::path(ownDllPath).parent_path(); // hook CreateProcessW if (MH_Initialize() > MH_ERROR_ALREADY_INITIALIZED) // MH_ERROR_ALREADY_INITIALIZED = 1, MH_OK = 0, these are the only results we should expect return TRUE; MH_CreateHook(&CreateProcessW, &CreateProcessWHook, reinterpret_cast<LPVOID*>(&CreateProcessWOriginal)); MH_EnableHook(&CreateProcessW); // TEMP: temporarily disable steam stuff because it's a huge pain // change conditional jump to EASteamProxy stuff in launchStep2 to never hit EASteamProxy launch void* ptr = (char*)GetModuleHandleA("OriginClient.dll") + 0x2A83FA; TempReadWrite rw(ptr); *((char*)ptr) = 0xE9; // je => jmp *((char*)ptr + 1) = 0xE6; *((char*)ptr + 2) = 0x01; *((char*)ptr + 3) = 0x00; *((char*)ptr + 4) = 0x00; return TRUE; }