#pragma once #include #include #include "memcury.h" #include "Class.h" inline __int64 GetFunctionIdxOrPtr2(UFunction* Function) { auto NativeAddr = __int64(Function->GetFunc()); auto FuncName = Function->GetName(); std::wstring ValidateWStr = (std::wstring(FuncName.begin(), FuncName.end()) + L"_Validate"); const wchar_t* ValidateWCStr = ValidateWStr.c_str(); bool bHasValidateFunc = Memcury::Scanner::FindStringRef(ValidateWCStr, false).Get(); bool bFoundValidate = !bHasValidateFunc; __int64 RetAddr = 0; for (int i = 0; i < 2000; i++) { // std::cout << std::format("CURRENT 0x{:x}\n", __int64((uint8_t*)(NativeAddr + i)) - __int64(GetModuleHandleW(0))); if (!RetAddr && *(uint8_t*)(NativeAddr + i) == 0xC3) { RetAddr = NativeAddr + i; break; } } int i = 0; __int64 functionAddyOrOffset = 0; for (__int64 CurrentAddy = RetAddr; CurrentAddy != NativeAddr && i < 2000; CurrentAddy -= 1) // Find last call { // LOG_INFO(LogDev, "[{}] 0x{:x}", i, *(uint8_t*)CurrentAddy); /* if (*(uint8_t*)CurrentAddy == 0xE8) // BAD { // LOG_INFO(LogDev, "CurrentAddy 0x{:x}", CurrentAddy - __int64(GetModuleHandleW(0))); functionAddyOrOffset = (CurrentAddy + 1 + 4) + *(int*)(CurrentAddy + 1); break; } else */ if ((*(uint8_t*)(CurrentAddy) == 0xFF && *(uint8_t*)(CurrentAddy + 1) == 0x90) || *(uint8_t*)(CurrentAddy) == 0xFF && *(uint8_t*)(CurrentAddy + 1) == 0x93) { auto SecondByte = *(uint8_t*)(CurrentAddy + 2); auto ThirdByte = *(uint8_t*)(CurrentAddy + 3); std::string bytes = GetBytes(CurrentAddy + 2, 2); std::string last2bytes; last2bytes += bytes[3]; last2bytes += bytes[4]; std::string neww; if (last2bytes != "00") neww = last2bytes; neww += bytes[0]; neww += bytes[1]; bytes = neww; functionAddyOrOffset = HexToDec(bytes); break; } i++; } return functionAddyOrOffset; } inline __int64 GetFunctionIdxOrPtr(UFunction* Function) { auto NativeAddr = __int64(Function->GetFunc()); auto FuncName = Function->GetName(); std::wstring ValidateWStr = (std::wstring(FuncName.begin(), FuncName.end()) + L"_Validate"); const wchar_t* ValidateWCStr = ValidateWStr.c_str(); bool bHasValidateFunc = Memcury::Scanner::FindStringRef(ValidateWCStr, false).Get(); // LOG_INFO(LogDev, "[{}] bHasValidateFunc: {}", Function->GetName(), bHasValidateFunc); // LOG_INFO(LogDev, "NativeAddr: 0x{:x}", __int64(NativeAddr) - __int64(GetModuleHandleW(0))); bool bFoundValidate = !bHasValidateFunc; __int64 RetAddr = 0; for (int i = 0; i < 2000; i++) { // LOG_INFO(LogDev, "0x{:x}", *(uint8_t*)(NativeAddr + i)); if ((*(uint8_t*)(NativeAddr + i) == 0xFF && *(uint8_t*)(NativeAddr + i + 1) == 0x90) || // call qword ptr *(uint8_t*)(NativeAddr + i) == 0xFF && *(uint8_t*)(NativeAddr + i + 1) == 0x93) // call qword ptr { if (bFoundValidate) { std::string wtf = ""; int shots = 0; bool bFoundFirstNumber = false; for (__int64 z = (NativeAddr + i + 5); z != (NativeAddr + i + 1); z -= 1) { auto anafa = (int)(*(uint8_t*)z); auto asfk = anafa < 10 ? "0" + std::format("{:x}", anafa) : std::format("{:x}", anafa); // std::cout << std::format("[{}] 0x{}\n", shots, asfk); if (*(uint8_t*)z == 0 ? bFoundFirstNumber : true) { wtf += asfk; bFoundFirstNumber = true; } shots++; } std::transform(wtf.begin(), wtf.end(), wtf.begin(), ::toupper); // LOG_INFO(LogDev, "wtf: {}", wtf); return HexToDec(wtf); } else { bFoundValidate = true; continue; } } if ((*(uint8_t*)(NativeAddr + i) == 0x48 && *(uint8_t*)(NativeAddr + i + 1) == 0xFF) && *(uint8_t*)(NativeAddr + i + 2) == 0xA0) // jmp qword ptr { if (bFoundValidate) { std::string wtf = ""; int shots = 0; bool bFoundFirstNumber = false; for (__int64 z = (NativeAddr + i + 6); z != (NativeAddr + i + 2); z -= 1) { auto anafa = (int)(*(uint8_t*)z); auto asfk = anafa < 10 ? "0" + std::format("{:x}", anafa) : std::format("{:x}", anafa); // std::cout << std::format("[{}] 0x{}\n", shots, asfk); if (*(uint8_t*)z == 0 ? bFoundFirstNumber : true) { wtf += asfk; bFoundFirstNumber = true; } shots++; } std::transform(wtf.begin(), wtf.end(), wtf.begin(), ::toupper); // LOG_INFO(LogDev, "wtf: {}", wtf); return HexToDec(wtf); } } if (!RetAddr && *(uint8_t*)(NativeAddr + i) == 0xC3) { RetAddr = NativeAddr + i; // break; } } // The function isn't virtual __int64 functionAddy = 0; if (RetAddr) { // LOG_INFO(LogDev, "RetAddr 0x{:x}", RetAddr - __int64(GetModuleHandleW(0))); int i = 0; for (__int64 CurrentAddy = RetAddr; CurrentAddy != NativeAddr && i < 2000; CurrentAddy -= 1) // Find last call { // LOG_INFO(LogDev, "[{}] 0x{:x}", i, *(uint8_t*)CurrentAddy); if (*(uint8_t*)CurrentAddy == 0xE8) { // LOG_INFO(LogDev, "CurrentAddy 0x{:x}", CurrentAddy - __int64(GetModuleHandleW(0))); functionAddy = (CurrentAddy + 1 + 4) + *(int*)(CurrentAddy + 1); break; } i++; } } return !functionAddy ? -1 : functionAddy; } namespace Hooking { namespace MinHook { static bool Hook(void* Addr, void* Detour, void** Original = nullptr) { LOG_INFO(LogDev, "Hooking 0x{:x}", __int64(Addr) - __int64(GetModuleHandleW(0))); auto ret1 = MH_CreateHook(Addr, Detour, Original); auto ret2 = MH_EnableHook(Addr); return ret1 == MH_OK && ret2 == MH_OK; } static bool PatchCall(void* Addr, void* Detour/*, void** Original = nullptr*/) { // int64_t delta = targetAddr - (instrAddr + 5); // *(int32_t*)(instrAddr + 1) = static_cast(delta); } static bool Hook(UObject* DefaultClass, UFunction* Function, void* Detour, void** Original = nullptr, bool bUseSecondMethod = true, bool bHookExec = false) // Native hook { if (!Function) return false; if (!DefaultClass || !DefaultClass->VFTable) { LOG_WARN(LogHook, "DefaultClass or the vtable for function {} is null! ({})", Function->GetName(), __int64(DefaultClass)); return false; } auto& Exec = Function->GetFunc(); if (bHookExec) { LOG_INFO(LogDev, "Hooking Exec {}", Function->GetName()); if (Original) *Original = Exec; Exec = Detour; return true; } auto AddrOrIdx = bUseSecondMethod ? GetFunctionIdxOrPtr2(Function) : GetFunctionIdxOrPtr(Function); if (AddrOrIdx == -1) { LOG_ERROR(LogInit, "Failed to find anything for {}.", Function->GetName()); return false; } if (IsBadReadPtr((void*)AddrOrIdx)) { auto Idx = AddrOrIdx / 8; if (Original) *Original = DefaultClass->VFTable[Idx]; LOG_INFO(LogDev, "Hooking {} with Idx 0x{:x}", Function->GetName(), AddrOrIdx); VirtualSwap(DefaultClass->VFTable, Idx, Detour); return true; } return Hook((PVOID)AddrOrIdx, Detour, Original); } static bool Unhook(void* Addr) { return MH_DisableHook((PVOID)Addr) == MH_OK; } } }