diff --git a/Alien Isolation/Main.cpp b/Alien Isolation/Main.cpp index 258836c..9402a2f 100644 --- a/Alien Isolation/Main.cpp +++ b/Alien Isolation/Main.cpp @@ -196,6 +196,9 @@ bool Main::Initialize() if (!g_d3d11Device || !g_d3d11Context || !g_dxgiSwapChain) { util::log::Error("Failed to capture DirectX interfaces via Present hook fallback"); + util::log::Error("Present hook installed: %s | Device 0x%p | Context 0x%p | SwapChain 0x%p", + util::hooks::IsPresentHookInstalled() ? "yes" : "no", + g_d3d11Device, g_d3d11Context, g_dxgiSwapChain); return false; } @@ -205,17 +208,9 @@ bool Main::Initialize() // This disables the object glow thing // Apply small byte patch, but only if target lies within module image { - BYTE GlowPatch[7] = { 0x80, 0xB9, 0x65, 0x70, 0x02, 0x00, 0x01 }; void* patchAddr = (void*)((int)g_gameHandle + 0x3A3494); - if (util::IsAddressInModule(g_gameHandle, patchAddr, sizeof(GlowPatch))) - { - if (!util::WriteMemory((DWORD_PTR)patchAddr, GlowPatch, (DWORD)sizeof(GlowPatch))) - util::log::Warning("Glow patch VirtualProtect/WriteMemory failed at %p", patchAddr); - } - else - { - util::log::Warning("Skipping glow patch: address %p outside module image (possible version mismatch)", patchAddr); - } + if (util::IsAddressInModule(g_gameHandle, patchAddr, 7)) + util::log::Warning("Skipping legacy glow patch at %p (offset 0x3A3494) to avoid version mismatch", patchAddr); } // Make timescale writable @@ -235,7 +230,7 @@ bool Main::Initialize() // Retrieve game version and make a const variable for whatever version // the tools support. If versions mismatch, scan for offsets. - // util::offsets::Scan(); + util::offsets::Scan(); m_pRenderer = std::make_unique(); if (!m_pRenderer->Initialize()) diff --git a/Alien Isolation/Util/Hooks.cpp b/Alien Isolation/Util/Hooks.cpp index dcc16c4..b981cbf 100644 --- a/Alien Isolation/Util/Hooks.cpp +++ b/Alien Isolation/Util/Hooks.cpp @@ -4,12 +4,17 @@ #include "../AlienIsolation.h" #include #include +#include #include #include +#include #include +#pragma comment(lib, "d3d11.lib") + // Function definitions typedef HRESULT(__stdcall* tIDXGISwapChain_Present)(IDXGISwapChain*, UINT, UINT); +typedef HRESULT(__stdcall* tIDXGISwapChain1_Present1)(IDXGISwapChain1*, UINT, UINT, const DXGI_PRESENT_PARAMETERS*); typedef BOOL(WINAPI* tSetCursorPos)(int, int); typedef int(__thiscall* tCameraUpdate)(CATHODE::AICameraManager*); @@ -29,43 +34,92 @@ typedef bool(__thiscall* tCombatManagerUpdate)(void*, CATHODE::Character*); // on top, like the tools UI. tIDXGISwapChain_Present oIDXGISwapChain_Present = nullptr; +tIDXGISwapChain1_Present1 oIDXGISwapChain1_Present1 = nullptr; -HRESULT __stdcall hIDXGISwapChain_Present(IDXGISwapChain* pSwapchain, UINT SyncInterval, UINT Flags) +namespace { - static bool loggedDeviceFailure = false; + void HandlePresent(IDXGISwapChain* pSwapchain, UINT SyncInterval, UINT Flags) + { + static bool loggedDeviceFailure = false; + static bool loggedFirstPresent = false; + static bool loggedSwapChainCapture = false; - if (!g_dxgiSwapChain) - g_dxgiSwapChain = pSwapchain; + if (!loggedFirstPresent) + { + util::log::Write(">>> Present hook CALLED! pSwapchain=0x%p SyncInterval=%u Flags=%u", pSwapchain, SyncInterval, Flags); + loggedFirstPresent = true; + } - if (!g_d3d11Device) - { - HRESULT hr = pSwapchain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast(&g_d3d11Device)); - if (FAILED(hr) && !loggedDeviceFailure) + if (!g_dxgiSwapChain) + g_dxgiSwapChain = pSwapchain; + + if (g_dxgiSwapChain == pSwapchain && !loggedSwapChainCapture) { - util::log::Warning("SwapChain::GetDevice failed while capturing interfaces, HRESULT 0x%X", hr); - loggedDeviceFailure = true; + util::log::Ok("Captured IDXGISwapChain from Present hook (0x%p)", g_dxgiSwapChain); + loggedSwapChainCapture = true; } - } - if (g_d3d11Device && !g_d3d11Context) - g_d3d11Device->GetImmediateContext(&g_d3d11Context); + if (!g_d3d11Device) + { + ID3D11Device* pDevice = nullptr; + HRESULT hr = pSwapchain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast(&pDevice)); + if (FAILED(hr)) + { + if (!loggedDeviceFailure) + { + util::log::Warning("SwapChain::GetDevice failed while capturing interfaces, HRESULT 0x%X", hr); + loggedDeviceFailure = true; + } + } + else if (!pDevice) + { + if (!loggedDeviceFailure) + { + util::log::Warning("SwapChain::GetDevice succeeded but returned null device pointer"); + loggedDeviceFailure = true; + } + } + else + { + g_d3d11Device = pDevice; + util::log::Ok("Captured ID3D11Device from Present hook (0x%p)", g_d3d11Device); + } + } - if (!g_shutdown && g_mainHandle) - { - CTRenderer* pRenderer = g_mainHandle->GetRenderer(); - UI* pUI = g_mainHandle->GetUI(); - CameraManager* pCameraManager = g_mainHandle->GetCameraManager(); + if (g_d3d11Device && !g_d3d11Context) + { + g_d3d11Device->GetImmediateContext(&g_d3d11Context); + if (g_d3d11Context) + util::log::Ok("Captured ID3D11DeviceContext from Present hook (0x%p)", g_d3d11Context); + } - if (pRenderer && pRenderer->IsReady() && pUI && pUI->IsReady() && pCameraManager) + if (!g_shutdown && g_mainHandle) { - pUI->BindRenderTarget(); - pRenderer->UpdateMatrices(); - //g_mainHandle->GetCameraManager()->DrawTrack(); - pUI->Draw(); + CTRenderer* pRenderer = g_mainHandle->GetRenderer(); + UI* pUI = g_mainHandle->GetUI(); + CameraManager* pCameraManager = g_mainHandle->GetCameraManager(); + + if (pRenderer && pRenderer->IsReady() && pUI && pUI->IsReady() && pCameraManager) + { + pUI->BindRenderTarget(); + pRenderer->UpdateMatrices(); + //g_mainHandle->GetCameraManager()->DrawTrack(); + pUI->Draw(); + } } } +} - return oIDXGISwapChain_Present(pSwapchain, SyncInterval, Flags); +HRESULT __stdcall hIDXGISwapChain_Present(IDXGISwapChain* pSwapchain, UINT SyncInterval, UINT Flags) +{ + HandlePresent(pSwapchain, SyncInterval, Flags); + return oIDXGISwapChain_Present ? oIDXGISwapChain_Present(pSwapchain, SyncInterval, Flags) : S_OK; +} + +HRESULT __stdcall hIDXGISwapChain1_Present1(IDXGISwapChain1* pSwapchain, UINT SyncInterval, UINT Flags, const DXGI_PRESENT_PARAMETERS* pPresentParameters) +{ + HandlePresent(pSwapchain, SyncInterval, Flags); + return oIDXGISwapChain1_Present1 ? oIDXGISwapChain1_Present1(pSwapchain, SyncInterval, Flags, pPresentParameters) : S_OK; } ////////////////////////// @@ -80,10 +134,10 @@ HRESULT __stdcall hIDXGISwapChain_Present(IDXGISwapChain* pSwapchain, UINT SyncI tCameraUpdate oCameraUpdate = nullptr; -int __fastcall hCameraUpdate(CATHODE::AICameraManager* pCameraManager) +int __fastcall hCameraUpdate(CATHODE::AICameraManager* pCameraManager, void* /*edx*/) { g_mainHandle->GetCameraManager()->OnCameraUpdateBegin(); - int result = oCameraUpdate(pCameraManager); + int result = oCameraUpdate ? oCameraUpdate(pCameraManager) : 0; g_mainHandle->GetCameraManager()->OnCameraUpdateEnd(); return result; } @@ -97,16 +151,16 @@ tInputUpdate oInputUpdate = nullptr; tGamepadUpdate oGamepadUpdate = nullptr; tSetCursorPos oSetCursorPos = nullptr; -int __fastcall hInputUpdate(void* _this) +int __fastcall hInputUpdate(void* _this, void* /*edx*/) { CameraManager* pCameraManager = g_mainHandle->GetCameraManager(); if (pCameraManager->IsCameraEnabled() && pCameraManager->IsKbmDisabled()) return 0; - return oInputUpdate(_this); + return oInputUpdate ? oInputUpdate(_this) : 0; } -int __fastcall hGamepadUpdate(void* _this) +int __fastcall hGamepadUpdate(void* _this, void* /*edx*/) { CameraManager* pCameraManager = g_mainHandle->GetCameraManager(); InputSystem* pInputSystem = g_mainHandle->GetInputSystem(); @@ -116,7 +170,7 @@ int __fastcall hGamepadUpdate(void* _this) && !pInputSystem->IsUsingSecondPad()) return 0; - return oGamepadUpdate(_this); + return oGamepadUpdate ? oGamepadUpdate(_this) : 0; } BOOL WINAPI hSetCursorPos(int x, int y) @@ -125,7 +179,7 @@ BOOL WINAPI hSetCursorPos(int x, int y) if (pUI && pUI->IsEnabled()) return TRUE; - return oSetCursorPos(x, y); + return oSetCursorPos ? oSetCursorPos(x, y) : TRUE; } @@ -143,13 +197,20 @@ tPostProcessUpdate oPostProcessUpdate = nullptr; tCombatManagerUpdate oCombatManagerUpdate = nullptr; tTonemapUpdate oTonemapUpdate = nullptr; -int __fastcall hPostProcessUpdate(int _this) +int __fastcall hPostProcessUpdate(int _this, void* /*edx*/) { - int result = oPostProcessUpdate(_this); - - CATHODE::PostProcess* pPostProcess = reinterpret_cast(_this + 0x1918); - g_mainHandle->GetCameraManager()->OnPostProcessUpdate(pPostProcess); - g_mainHandle->GetVisualsController()->OnPostProcessUpdate(pPostProcess); + int result = oPostProcessUpdate ? oPostProcessUpdate(_this) : 0; + __try { + auto* pPostProcess = reinterpret_cast(_this + 0x1918); + if (util::IsPtrReadable(pPostProcess, sizeof(void*))) + { + g_mainHandle->GetCameraManager()->OnPostProcessUpdate(pPostProcess); + g_mainHandle->GetVisualsController()->OnPostProcessUpdate(pPostProcess); + } + } __except(EXCEPTION_EXECUTE_HANDLER) { + // skip on bad offset + util::log::Warning("Exception in hPostProcessUpdate, likely bad offset for PostProcess struct. Skipping update."); + } return result; } @@ -162,12 +223,12 @@ bool __fastcall hCombatManagerUpdate(void* _this, void* _EDX, CATHODE::Character return false; } - return oCombatManagerUpdate(_this, pTargetChr); + return oCombatManagerUpdate ? oCombatManagerUpdate(_this, pTargetChr) : false; } char __stdcall hTonemapSettings(CATHODE::DayToneMapSettings* pTonemapSettings, int a2) { - char result = oTonemapUpdate(pTonemapSettings, a2); + char result = oTonemapUpdate ? oTonemapUpdate(pTonemapSettings, a2) : 0; g_mainHandle->GetVisualsController()->OnTonemapUpdate(); return result; @@ -313,6 +374,12 @@ static bool CreateDXGIPresentHook() featureLevels, _countof(featureLevels), D3D11_SDK_VERSION, &desc, &pSwapChain, &pDevice, &obtainedLevel, &pContext); } + if (FAILED(hr)) + { + hr = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_REFERENCE, nullptr, createFlags, + featureLevels, _countof(featureLevels), D3D11_SDK_VERSION, &desc, &pSwapChain, &pDevice, &obtainedLevel, &pContext); + } + if (FAILED(hr)) { util::log::Error("Failed to create dummy D3D11 device for Present hook, HRESULT 0x%X", hr); @@ -321,8 +388,28 @@ static bool CreateDXGIPresentHook() } void** vtbl = *reinterpret_cast(pSwapChain); + util::log::Write("SwapChain vtable at 0x%p, Present at slot 8 is 0x%p", vtbl, vtbl[8]); + bool hookCreated = CreateHook("SwapChainPresent", (int)vtbl[8], hIDXGISwapChain_Present, &oIDXGISwapChain_Present); + if (hookCreated) + { + IDXGISwapChain1* pSwapChain1 = nullptr; + HRESULT qiHr = pSwapChain->QueryInterface(__uuidof(IDXGISwapChain1), reinterpret_cast(&pSwapChain1)); + if (SUCCEEDED(qiHr) && pSwapChain1) + { + void** vtbl1 = *reinterpret_cast(pSwapChain1); + util::log::Write("SwapChain1 vtable at 0x%p, Present1 at slot 22 is 0x%p", vtbl1, vtbl1[22]); + if (CreateHook("SwapChainPresent1", (int)vtbl1[22], hIDXGISwapChain1_Present1, &oIDXGISwapChain1_Present1)) + util::log::Ok("Installed DXGI Present1 hook via dummy device"); + pSwapChain1->Release(); + } + else + { + util::log::Warning("IDXGISwapChain1 interface unavailable for Present1 hook, HRESULT 0x%X", qiHr); + } + } + pSwapChain->Release(); pDevice->Release(); pContext->Release(); @@ -332,6 +419,7 @@ static bool CreateDXGIPresentHook() return false; g_presentHookCreated = true; + util::log::Ok("Installed DXGI Present hook via dummy device"); return true; } @@ -377,6 +465,11 @@ bool util::hooks::Init() return true; } +bool util::hooks::IsPresentHookInstalled() +{ + return g_presentHookCreated; +} + void util::hooks::InstallGameHooks() { if (g_gameHooksInstalled) @@ -385,6 +478,8 @@ void util::hooks::InstallGameHooks() if (!EnsureMinHookInitialized()) return; + const bool isSteamBuild = util::IsSteamBuild(); + auto safeCreate = [](const char* name, const char* key, auto hook, auto original) { int addr = util::offsets::GetOffset(key); @@ -393,15 +488,43 @@ void util::hooks::InstallGameHooks() util::log::Warning("Skipping hook %s: address 0x%X outside module image", name, addr); return; } + + // Sanity check executable memory + MEMORY_BASIC_INFORMATION mbi{}; + if (VirtualQuery((LPCVOID)addr, &mbi, sizeof(mbi)) && !(mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE))) + { + util::log::Warning("Skipping hook %s: target 0x%X not in executable memory", name, addr); + return; + } + + BYTE preview[6] = { 0 }; + if (!util::IsPtrReadable(reinterpret_cast(addr), sizeof(preview))) + { + util::log::Warning("Skipping hook %s: target 0x%X unreadable", name, addr); + return; + } + + memcpy(preview, reinterpret_cast(addr), sizeof(preview)); + util::log::Write("Hook %s targeting 0x%X bytes %02X %02X %02X %02X %02X %02X", name, addr, + preview[0], preview[1], preview[2], preview[3], preview[4], preview[5]); + CreateHook(name, addr, hook, original); }; - safeCreate("CameraUpdate", "OFFSET_CAMERAUPDATE", hCameraUpdate, &oCameraUpdate); - //safeCreate("InputUpdate", "OFFSET_INPUTUPDATE", hInputUpdate, &oInputUpdate); - safeCreate("GamepadUpdate", "OFFSET_GAMEPADUPDATE", hGamepadUpdate, &oGamepadUpdate); - safeCreate("PostProcessUpdate", "OFFSET_POSTPROCESSUPDATE", hPostProcessUpdate, &oPostProcessUpdate); - safeCreate("TonemapUpdate", "OFFSET_TONEMAPUPDATE", hTonemapSettings, &oTonemapUpdate); - //CreateHook("AICombatManagerUpdate", util::offsets::GetOffset("OFFSET_COMBATMANAGERUPDATE"), hCombatManagerUpdate, &oCombatManagerUpdate); + if (isSteamBuild) + { + util::log::Write("Steam build detected, installing all gameplay hooks..."); + safeCreate("CameraUpdate", "OFFSET_CAMERAUPDATE", hCameraUpdate, &oCameraUpdate); + //safeCreate("InputUpdate", "OFFSET_INPUTUPDATE", hInputUpdate, &oInputUpdate); + safeCreate("GamepadUpdate", "OFFSET_GAMEPADUPDATE", hGamepadUpdate, &oGamepadUpdate); + safeCreate("PostProcessUpdate", "OFFSET_POSTPROCESSUPDATE", hPostProcessUpdate, &oPostProcessUpdate); + safeCreate("TonemapUpdate", "OFFSET_TONEMAPUPDATE", hTonemapSettings, &oTonemapUpdate); + //CreateHook("AICombatManagerUpdate", util::offsets::GetOffset("OFFSET_COMBATMANAGERUPDATE"), hCombatManagerUpdate, &oCombatManagerUpdate); + } + else + { + util::log::Warning("Non-Steam build detected - skipping cinematic/gameplay hooks to avoid crashes. Overlay/UI only."); + } FARPROC setCursorProc = GetProcAddress(GetModuleHandleA("user32.dll"), "SetCursorPos"); if (setCursorProc) diff --git a/Alien Isolation/Util/Offsets.cpp b/Alien Isolation/Util/Offsets.cpp index 5b88751..039b48a 100644 --- a/Alien Isolation/Util/Offsets.cpp +++ b/Alien Isolation/Util/Offsets.cpp @@ -1,136 +1,210 @@ #include "Util.h" #include "../Main.h" -#include #include #include +#include +#include +#include #include -using namespace boost::assign; +namespace util::offsets +{ + struct CompiledSig { + std::vector bytes; // compact: only actual bytes (no spaces) + std::string mask; // same length as bytes, 'x' or '?' + int refStart = -1; // index in bytes where [ ... ] begins (first '?') + int refSize = 0; // number of bytes inside brackets + }; +} namespace { + using util::offsets::CompiledSig; + bool m_UseScannedResults = false; std::unordered_map m_Signatures; // Fill with hardcoded offsets if you don't want to use scanning // These should be relative to the module base. - std::unordered_map m_HardcodedOffsets = map_list_of - ("OFFSET_D3D", 0x17DF5CC) - ("OFFSET_MAIN", 0x12F0C88) - - ("OFFSET_CAMERAUPDATE", 0x32300) - ("OFFSET_GETCAMERAMATRIX", 0x5B0B40) - ("OFFSET_POSTPROCESSUPDATE", 0x608C50) - ("OFFSET_TONEMAPUPDATE", 0x208490) - - ("OFFSET_INPUTUPDATE", 0x57D6C0) - ("OFFSET_GAMEPADUPDATE", 0x60EE30) - ("OFFSET_COMBATMANAGERUPDATE", 0x37A800) - - ("OFFSET_SHOWMOUSE", 0x1359B44) - ("OFFSET_DRAWUI", 0x1240F27) - ("OFFSET_FREEZETIME", 0x12F194C) - ("OFFSET_SCALEFORM", 0x134A78C) - ("OFFSET_TIMESCALE", 0xDC6EA0) - ("OFFSET_POSTPROCESS", 0x15D0970); - - bool DataCompare(BYTE* pData, BYTE* bSig, const char* szMask) - { - for (; *szMask; ++szMask, ++pData, ++bSig) - { - if (*szMask == 'x' && *pData != *bSig) - return false; + std::unordered_map m_HardcodedOffsets = { + {"OFFSET_D3D", 0x17DF5CC}, + {"OFFSET_MAIN", 0x12F0C88}, + + {"OFFSET_CAMERAUPDATE", 0x32300}, + {"OFFSET_GETCAMERAMATRIX", 0x5B0B40}, + {"OFFSET_POSTPROCESSUPDATE", 0x608C50}, + {"OFFSET_TONEMAPUPDATE", 0x208490}, + + {"OFFSET_INPUTUPDATE", 0x57D6C0}, + {"OFFSET_GAMEPADUPDATE", 0x60EE30}, + {"OFFSET_COMBATMANAGERUPDATE", 0x37A800}, + + {"OFFSET_SHOWMOUSE", 0x1359B44}, + {"OFFSET_DRAWUI", 0x1240F27}, + {"OFFSET_FREEZETIME", 0x12F194C}, + {"OFFSET_SCALEFORM", 0x134A78C}, + {"OFFSET_TIMESCALE", 0x0DC6EA0}, + {"OFFSET_POSTPROCESS", 0x15D0970} + }; + + static CompiledSig Compile(std::string const& sig) { + CompiledSig out; + bool inRef = false; + for (size_t i = 0; i < sig.size();) { + char c = sig[i]; + if (c == ' ') { ++i; continue; } + if (c == '[') { inRef = true; ++i; continue; } + if (c == ']') { inRef = false; ++i; continue; } + + if (c == '?') { + // accept "?" or "??" + if (i + 1 < sig.size() && sig[i+1] == '?') ++i; + out.bytes.push_back(0x00); + out.mask.push_back('?'); + if (inRef) { + if (out.refStart < 0) out.refStart = (int)out.bytes.size() - 1; + out.refSize++; + } + ++i; + } else { + // read two hex chars -> one byte + auto hex = [](char h)->int { + if (h >= '0' && h <= '9') return h - '0'; + if (h >= 'A' && h <= 'F') return 10 + (h - 'A'); + if (h >= 'a' && h <= 'f') return 10 + (h - 'a'); + return -1; + }; + if (i + 1 >= sig.size()) throw std::runtime_error("Odd hex length"); + int hi = hex(sig[i]), lo = hex(sig[i+1]); + if (hi < 0 || lo < 0) throw std::runtime_error("Bad hex in signature"); + out.bytes.push_back((BYTE)((hi << 4) | lo)); + out.mask.push_back('x'); + i += 2; + } } - return (*szMask) == 0; + return out; } - BYTE* FindPattern(BYTE* dwAddress, __int64 dwSize, BYTE* pbSig, const char* szMask) - { - register BYTE bFirstByte = *(BYTE*)pbSig; - - __int64 length = (__int64)dwAddress + dwSize - strlen(szMask); - - for (register __int64 i = (__int64)dwAddress; i < length; i += 4) // might run faster with 8 bytes but I am too lazy - { - unsigned x = *(unsigned*)(i); - - if ((x & 0xFF) == bFirstByte) - if (DataCompare(reinterpret_cast(i), pbSig, szMask)) - return reinterpret_cast(i); - - if ((x & 0xFF00) >> 8 == bFirstByte) - if (DataCompare(reinterpret_cast(i + 1), pbSig, szMask)) - return reinterpret_cast(i + 1); - - if ((x & 0xFF0000) >> 16 == bFirstByte) - if (DataCompare(reinterpret_cast(i + 2), pbSig, szMask)) - return reinterpret_cast(i + 2); - - if ((x & 0xFF000000) >> 24 == bFirstByte) - if (DataCompare(reinterpret_cast(i + 3), pbSig, szMask)) - return reinterpret_cast(i + 3); + static BYTE* FindPattern(BYTE* base, size_t size, CompiledSig const& s) { + size_t n = s.bytes.size(); + if (n == 0 || n > size) return nullptr; + BYTE* end = base + (size - n); + for (BYTE* p = base; p <= end; ++p) { + size_t j = 0; + for (; j < n; ++j) { + if (s.mask[j] == 'x' && p[j] != s.bytes[j]) break; + } + if (j == n) return p; } - return 0; + return nullptr; } } util::offsets::Signature::Signature(std::string const& sig, int offset /* = 0 */) { - Pattern = new BYTE[sig.size()](); AddOffset = offset; - - for (size_t i = 0; i < sig.size(); ++i) - { - switch (sig[i]) - { - case ' ': - { - break; - } - case '[': - { - HasReference = true; - ReferenceOffset = i; - break; - } - case ']': - { - ReferenceSize = i - ReferenceOffset; - break; - } - case '?': - { - Mask += '?'; - // In signature it's clearer to mark one wildcard byte as ?? - // so skip the next character. - i += 1; - } - default: - { - Mask += 'x'; - // Process 2 characters into a single byte - Pattern[i] = (util::CharToByte(sig[i]) << 4) + util::CharToByte(sig[i+1]); - i += 1; - } - } - } + Compiled = std::make_unique(Compile(sig)); + HasReference = (Compiled->refStart >= 0); + ReferenceSize = Compiled->refSize; } void util::offsets::Scan() { - // Signature example - // Scan memory and find this pattern. Question marks are wildcard bytes. - // Brackets mean that the offset is extracted from the assembly reference - // For example: - // mov rax,[123456] - // We want 123456, its place should be marked with wildcard bytes surrounded - // by brackets in the signature. - // - // The last argument is the offset to be added to the result, useful when - // you need a code offset for byte patches. - - m_Signatures.emplace("OFFSET_EXAMPLE", Signature("12 34 56 78 [ ?? ?? ?? ?? ] AA BB ?? DF", 0x20)); + m_Signatures.clear(); + + // Gameplay/update hooks + m_Signatures.emplace("OFFSET_CAMERAUPDATE", Signature( + "55 8B EC 83 E4 F0 81 EC 74 01 00 00 53 56 8B F1 " + "8B 86 D8 01 00 00 33 DB 57 85 C0 74 12 38 98 4D 01 00 00 74 0A " + "38 98 4F 01 00 00 75 02 8B D8 " + "80 BE 21 02 00 00 00 74 0C " + "8B 86 F0 01 00 00 85 C0 74 02 8B D8 " + "85 DB 0F 84 ?? ?? ?? ?? " + "F3 0F 10 43 44 F3 0F 10 4B 40 F3 0F 10 53 3C F3 0F 10 5B 2C " + "F3 0F 11 44 24 48 0F 57 C0 " + "E8 ?? ?? ?? ?? " + "F3 0F 7E 00 " + "F3 0F 10 35 [ ?? ?? ?? ?? ]", 0)); + + m_Signatures.emplace("OFFSET_GETCAMERAMATRIX", Signature( + "8B 44 24 04 56 8B F1 " + "8D 96 4B 03 00 00 2B D0 90 " + "8A 08 88 0C 02 40 84 C9 75 F6 " + "8B 44 24 0C 89 86 14 03 00 00 " + "83 F8 01 75 4E " + "A1 [ ?? ?? ?? ?? ] " + "50 E8 ?? ?? ?? ?? 6A 01 50 " + "89 86 70 03 00 00 " + "E8 ?? ?? ?? ??", 0)); + + m_Signatures.emplace("OFFSET_POSTPROCESSUPDATE", Signature( + "83 EC 08 53 8B 5C 24 10 55 56 57 " + "8B 7C 24 20 8B CF 2B CB " + "B8 AB AA AA 2A F7 E9 D1 FA 8B C2 C1 E8 1F 03 C2 " + "83 F8 20 0F 8E ?? ?? ?? ?? " + "8B 74 24 24 85 F6 0F 8E ?? ?? ?? ?? " + "57 8D 44 24 14 53 50 E8 ?? ?? ?? ?? " + "8B 6C 24 20 8B C6 99 2B C2 D1 F8 " + "8B F0 99 2B C2 D1 F8 03 F0 " + "8B CF 2B CD B8 AB AA AA 2A F7 E9 " + "8B 4C 24 1C D1 FA 8B C2 C1 E8 1F 03 C2 " + "2B CB 89 44 24 2C", 0)); + + m_Signatures.emplace("OFFSET_TONEMAPUPDATE", Signature( + "83 EC 20 53 56 8B F1 " + "E8 ?? ?? ?? ?? " + "8B 98 74 03 00 00 85 DB 0F 84 ?? ?? ?? ?? " + "57 8D 4C 24 0C E8 ?? ?? ?? ?? " + "8B 7C 24 30 57 8B CE E8 ?? ?? ?? ?? D9 5C 24 10 " + "57 8B CE E8 ?? ?? ?? ?? D9 5C 24 18 " + "57 8B CE E8 ?? ?? ?? ?? D9 5C 24 1C " + "F3 0F 10 44 24 10 0F 2E 43 10 9F F6 C4 44 7A ?? " + "F3 0F 10 44 24 14 0F 2E 43 14 9F F6 C4 44 7A ?? " + "F3 0F 10 44 24 18 0F 2E 43 18 9F F6 C4 44 7A ?? " + "F3 0F 10 44 24 1C 0F 2E 43 1C 9F F6 C4 44", 0)); + + m_Signatures.emplace("OFFSET_INPUTUPDATE", Signature( + "0F 57 ED 56 8B B1 40 10 00 00 85 F6 74 32 " + "80 7E 10 00 74 2C 80 7E 11 00 74 26 " + "33 C0 39 81 58 10 00 00 76 1C " + "8D 91 80 0A 00 00 F3 0F 11 2A 40 83 C2 44 " + "3B 81 58 10 00 00 72 F0 " + "F3 0F 10 35 [ ?? ?? ?? ?? ] " + "B0 02 84 41 3C 74 08 F3 0F 11 B1 C4 0A 00 00", 0)); + + m_Signatures.emplace("OFFSET_GAMEPADUPDATE", Signature( + "8B 44 24 04 F6 44 08 14 80 74 15 " + "F3 0F 10 05 [ ?? ?? ?? ?? ] F3 0F 11 44 24 04 D9 44 24 04 C2 04 00 " + "0F 57 C0 F3 0F 11 44 24 04 D9 44 24 04 C2 04 00 " + "80 7C 24 08 00 74 0A " + "F3 0F 10 05 [ ?? ?? ?? ?? ] EB 08 " + "F3 0F 10 05 [ ?? ?? ?? ?? ] " + "8B 44 24 04 F6 44 08 14 80", 0)); + + m_Signatures.emplace("OFFSET_COMBATMANAGERUPDATE", Signature( + "55 8B EC 83 E4 F0 F3 0F 10 55 0C 83 EC 64 53 56 8B F1 " + "F3 0F 10 86 A0 01 00 00 F3 0F 59 C2 F3 0F 58 86 74 01 00 00 " + "0F 28 C8 " + "F3 0F 59 0D [ ?? ?? ?? ?? ] " + "0F 2F 0D [ ?? ?? ?? ?? ] " + "57 76 15 " + "F3 0F 58 0D [ ?? ?? ?? ?? ] " + "F3 0F 2C C1 0F 57 C9 F3 0F 2A C8 " + "F3 0F 59 0D [ ?? ?? ?? ?? ]", 0)); + + // Globals / data pointers + m_Signatures.emplace("OFFSET_POSTPROCESS", Signature( + "A1 [ ?? ?? ?? ?? ] 85 C0 74 ?? 8B 48 ??", 0)); + + m_Signatures.emplace("OFFSET_SCALEFORM", Signature( + "8B 0D [ ?? ?? ?? ?? ] 85 C9 74 ?? 8B 01 FF 50 ??", 0)); + + m_Signatures.emplace("OFFSET_FREEZETIME", Signature( + "F6 05 [ ?? ?? ?? ?? ] 00 75 ??", 0)); + + m_Signatures.emplace("OFFSET_TIMESCALE", Signature( + "F3 0F 10 05 [ ?? ?? ?? ?? ] F3 0F 59 ?? ??", 0)); util::log::Write("Scanning for offsets..."); @@ -143,35 +217,59 @@ void util::offsets::Scan() } bool allFound = true; + bool foundAny = false; + uintptr_t moduleBase = reinterpret_cast(info.lpBaseOfDll); - for (auto& entry : m_Signatures) + for (auto& kv : m_Signatures) { - Signature& sig = entry.second; - int result = reinterpret_cast(FindPattern((BYTE*)info.lpBaseOfDll, info.SizeOfImage, sig.Pattern, sig.Mask.c_str())); - if (!result) + auto& sig = kv.second; + auto* cs = sig.Compiled.get(); + if (!cs) { - util::log::Error("Could not find pattern for %s", entry.first.c_str()); + util::log::Error("Signature %s has no compiled data", kv.first.c_str()); + allFound = false; + sig.Result = 0; + continue; + } + + BYTE* p = FindPattern((BYTE*)info.lpBaseOfDll, info.SizeOfImage, *cs); + if (!p) { + util::log::Error("Could not find pattern for %s", kv.first.c_str()); allFound = false; + sig.Result = 0; + continue; } if (sig.HasReference) { - // Get the assembly reference - int* pReference = (int*)(result + sig.ReferenceOffset); - // Assembly reference is relative to the address after the reference - sig.Result = ((int)pReference + sig.ReferenceSize) + *pReference; - sig.Result += sig.AddOffset; + if (cs->refSize != 4) + { + util::log::Error("Signature %s expected 4-byte reference, got %d", kv.first.c_str(), cs->refSize); + allFound = false; + sig.Result = 0; + continue; + } + + BYTE* ref = p + cs->refStart; + uint32_t addr = *reinterpret_cast(ref); + sig.Result = static_cast(addr) + sig.AddOffset; } else - sig.Result = result + sig.AddOffset; + { + sig.Result = reinterpret_cast(p) + sig.AddOffset; + } + + foundAny = true; + uintptr_t rva = sig.Result >= moduleBase ? sig.Result - moduleBase : 0; + util::log::Write("%s resolved at 0x%08X (RVA 0x%X)", kv.first.c_str(), static_cast(sig.Result), static_cast(rva)); } - if (allFound) + if (allFound && foundAny) util::log::Ok("All offsets found"); else util::log::Warning("All offsets could not be found, this might result in a crash"); - m_UseScannedResults = true; + m_UseScannedResults = (allFound && foundAny); } int util::offsets::GetOffset(std::string const& name) @@ -186,7 +284,7 @@ int util::offsets::GetOffset(std::string const& name) if (result != m_Signatures.end()) { if (result->second.Result) - return result->second.Result; + return static_cast(result->second.Result); } } @@ -196,7 +294,10 @@ int util::offsets::GetOffset(std::string const& name) auto hardcodedResult = m_HardcodedOffsets.find(name); if (hardcodedResult != m_HardcodedOffsets.end()) - return hardcodedResult->second + (int)g_gameHandle; + { + uintptr_t base = reinterpret_cast(g_gameHandle); + return static_cast(base + hardcodedResult->second); + } util::log::Error("Offset %s does not exist", name.c_str()); return 0; @@ -210,7 +311,7 @@ int util::offsets::GetRelOffset(std::string const& name) if (result != m_Signatures.end()) { if (result->second.Result) - return result->second.Result - (int)g_gameHandle; + return static_cast(result->second.Result - reinterpret_cast(g_gameHandle)); } } diff --git a/Alien Isolation/Util/Util.cpp b/Alien Isolation/Util/Util.cpp index 78b9580..fb34997 100644 --- a/Alien Isolation/Util/Util.cpp +++ b/Alien Isolation/Util/Util.cpp @@ -122,20 +122,25 @@ std::string util::KeyLparamToString(LPARAM lparam) BYTE util::CharToByte(char c) { - BYTE b; - sscanf_s(&c, "%hhx", &b); - return b; + if (c >= '0' && c <= '9') + return static_cast(c - '0'); + if (c >= 'A' && c <= 'F') + return static_cast(10 + (c - 'A')); + if (c >= 'a' && c <= 'f') + return static_cast(10 + (c - 'a')); + return 0; } BOOL util::WriteMemory(DWORD_PTR dwAddress, const void* cpvPatch, DWORD dwSize) { - DWORD dwProtect; - if (VirtualProtect((void*)dwAddress, dwSize, PAGE_READWRITE, &dwProtect)) //Unprotect the memory - memcpy((void*)dwAddress, cpvPatch, dwSize); //Write our patch - else - return false; //Failed to unprotect, so return false.. + DWORD oldProtect = 0; + if (!VirtualProtect(reinterpret_cast(dwAddress), dwSize, PAGE_READWRITE, &oldProtect)) + return FALSE; - return VirtualProtect((void*)dwAddress, dwSize, dwProtect, new DWORD); //Reprotect the memory + memcpy(reinterpret_cast(dwAddress), cpvPatch, dwSize); + + DWORD unused = 0; + return VirtualProtect(reinterpret_cast(dwAddress), dwSize, oldProtect, &unused); } bool util::IsPtrReadable(const void* ptr, size_t bytes) @@ -169,3 +174,10 @@ bool util::IsAddressInModule(HMODULE hModule, const void* addr, size_t size) return (a >= base) && (b <= end); } +bool util::IsSteamBuild() +{ + // simplest heuristic: Steam client dll present OR steam_api loaded by the game + return GetModuleHandleA("steam_api.dll") != nullptr + || GetModuleHandleA("steamclient.dll") != nullptr; +} + diff --git a/Alien Isolation/Util/Util.h b/Alien Isolation/Util/Util.h index 66a3c72..124d39c 100644 --- a/Alien Isolation/Util/Util.h +++ b/Alien Isolation/Util/Util.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include namespace util @@ -26,6 +28,7 @@ namespace util bool Init(); void InstallGameHooks(); + bool IsPresentHookInstalled(); // if name is empty, then perform on all hooks void SetHookState(bool enabled, std::string const& name = ""); @@ -43,17 +46,15 @@ namespace util namespace offsets { + struct CompiledSig; + struct Signature { - BYTE* Pattern{ nullptr }; // The pattern to search - std::string Mask; // Which bytes should be evaluated (x = evaluate, ? = skip) - - bool HasReference{ false }; // Interpret the offset from the assembly reference - int ReferenceOffset{ 0 }; // How far in the signature is the assembly reference - int ReferenceSize{ 0 }; // How many bytes is the assembly reference (usually 4, obsolete?) - int AddOffset{ 0 }; // How much bytes should be added to the final result - - int Result; + std::unique_ptr Compiled; + int AddOffset{ 0 }; + bool HasReference{ false }; + int ReferenceSize{ 0 }; + uintptr_t Result{ 0 }; Signature(std::string const& sig, int offset = 0); }; @@ -75,6 +76,8 @@ namespace util bool IsPtrReadable(const void* ptr, size_t bytes = 1); // Returns true if [addr, addr+size) lies within the specified module's image range. bool IsAddressInModule(HMODULE hModule, const void* addr, size_t size); + // Returns true if the game appears to be a Steam build. + bool IsSteamBuild(); namespace math {