EFTCheatPVE/ioperator/dllmain.cpp

808 lines
No EOL
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "pch.h"
// =================================================================
// 0. IL2CPP Structs and Function Type Definitions
// =================================================================
typedef void (*Il2CppMethodPointer)();
struct Il2CppDomain;
struct Il2CppAssembly;
struct Il2CppThread;
struct Il2CppClass;
struct Il2CppMethod; // Forward declaration
struct Il2CppImage;
// IL2CPP API function pointer type definitions
using il2cpp_get_root_domain_prot = Il2CppDomain * (*)();
static il2cpp_get_root_domain_prot il2cpp_get_root_domain = nullptr;
using il2cpp_thread_attach_prot = Il2CppThread * (*)(Il2CppDomain*);
static il2cpp_thread_attach_prot il2cpp_thread_attach = nullptr;
using il2cpp_domain_get_assemblies_prot = const Il2CppAssembly** (*)(Il2CppDomain*, size_t*);
static il2cpp_domain_get_assemblies_prot il2cpp_domain_get_assemblies = nullptr;
using il2cpp_class_from_name_prot = Il2CppClass * (*)(const Il2CppImage*, const char*, const char*);
static il2cpp_class_from_name_prot il2cpp_class_from_name = nullptr;
using il2cpp_class_get_methods_prot = const Il2CppMethod* (*)(Il2CppClass*, void**);
static il2cpp_class_get_methods_prot il2cpp_class_get_methods = nullptr;
using il2cpp_method_get_name_prot = const char* (*)(const Il2CppMethod*);
static il2cpp_method_get_name_prot il2cpp_method_get_name = nullptr;
using il2cpp_assembly_get_image_prot = const Il2CppImage* (*)(const Il2CppAssembly*);
static il2cpp_assembly_get_image_prot il2cpp_assembly_get_image = nullptr;
using il2cpp_image_get_name_prot = const char* (*)(const Il2CppImage*);
static il2cpp_image_get_name_prot il2cpp_image_get_name = nullptr;
// IL2CPP runtime invoke function pointer type
using il2cpp_runtime_invoke_prot = void* (*)(const Il2CppMethod*, void*, void**, void**);
static il2cpp_runtime_invoke_prot il2cpp_runtime_invoke = nullptr;
// Global variables for storing search results and log synchronization
std::vector<const Il2CppMethod*> found_methods;
std::mutex log_mutex;
// Global variable for storing ShowErrorScreen methods
std::vector<const Il2CppMethod*> error_screen_methods;
/// GVARS BEGIN
// CameraManager tracking related variables
const Il2CppMethod* g_cachedGetInstanceMethod = nullptr; // Cache the get_Instance method
void* lastCameraManager = nullptr; // Track the previous CameraManager instance
// TarkovApplication instance tracking
void* g_TarkovApplicationInstance = nullptr;
void* g_CameraManagerInstance = nullptr;
// =======================
// IMGUI
// =======================
static bool g_ShowMenu = true;
static ID3D11Device* g_pd3dDevice = nullptr;
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
static IDXGISwapChain* g_pSwapChain = nullptr;
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
static WNDPROC g_oOriginalWndProc = nullptr;
/// GVARS END
#define STATIC_FIELDS_OFFSET 0xB8
/// IMGUI START
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL isGameReady()
{
HWND gameWindow = FindWindowA("UnityWndClass", nullptr);
if (!gameWindow)
{
return false;
}
return true;
}
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_KEYDOWN && wParam == VK_INSERT)
g_ShowMenu = !g_ShowMenu;
if (g_ShowMenu && ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
return CallWindowProc(g_oOriginalWndProc, hWnd, msg, wParam, lParam);
}
// Present hooks
typedef HRESULT(__stdcall* PresentFn)(IDXGISwapChain*, UINT, UINT);
static PresentFn oPresent = nullptr;
HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags)
{
if (!g_pd3dDevice)
{
g_pSwapChain = pSwapChain;
// D3D11 Device
if (FAILED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pd3dDevice)))
return oPresent(pSwapChain, SyncInterval, Flags);
g_pd3dDevice->GetImmediateContext(&g_pd3dDeviceContext);
// hwnd
DXGI_SWAP_CHAIN_DESC sd{};
pSwapChain->GetDesc(&sd);
HWND hWnd = sd.OutputWindow;
// WndProc for Insert key listener
g_oOriginalWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc);
// ImGui
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui::StyleColorsDark();
ImGui_ImplWin32_Init(hWnd);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
// create RenderTargetView
ID3D11Texture2D* pBackBuffer = nullptr;
if (SUCCEEDED(pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer)))
{
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
pBackBuffer->Release();
}
else
{
return oPresent(pSwapChain, SyncInterval, Flags);
}
}
// render
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
if (g_ShowMenu)
{
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
ImGui::Begin("Operator PvE for v1.0 - build 0.0.1", &g_ShowMenu);
ImGui::TextColored(ImVec4(0, 1, 0, 1), "第一阶段彻底成功!");
ImGui::Separator();
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::Text("按 INSERT 键切换菜单");
ImGui::Text("所有初始化已完成,无 goto 漏洞");
ImGui::TextColored(ImVec4(1, 1, 0, 1), "你可以开始第二阶段了!");
ImGui::End();
}
ImGui::Render();
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
return oPresent(pSwapChain, SyncInterval, Flags);
}
// VTable Hook
void HookPresent()
{
while (true)
{
// 1. 等待游戏窗口创建EFT 的窗口类名是 "UnityWndClass"
HWND gameWindow = FindWindowA("UnityWndClass", nullptr);
if (!gameWindow)
{
Sleep(1000);
continue;
}
// 2. 获取游戏真正的 SwapChain
IDXGISwapChain* pSwapChain = nullptr;
ID3D11Device* pDevice = nullptr;
// 用游戏窗口创建一个“伪”描述,但实际会返回游戏正在用的 SwapChain
DXGI_SWAP_CHAIN_DESC sd{};
sd.BufferCount = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = gameWindow;
sd.SampleDesc.Count = 1;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
HRESULT hr = D3D11CreateDeviceAndSwapChain(
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0,
D3D11_SDK_VERSION, &sd, &pSwapChain, &pDevice, nullptr, nullptr);
if (SUCCEEDED(hr) && pSwapChain && pDevice)
{
void** vtable = *(void***)pSwapChain;
oPresent = (PresentFn)vtable[8];
// 关键:用原子操作写内存,防止崩溃
DWORD oldProtect;
VirtualProtect(&vtable[8], 8, PAGE_EXECUTE_READWRITE, &oldProtect);
vtable[8] = (void*)hkPresent;
VirtualProtect(&vtable[8], 8, oldProtect, &oldProtect);
printf("[+] We're hooked @ %p\n", &hkPresent);
pSwapChain->Release();
pDevice->Release();
break;
}
if (pSwapChain) pSwapChain->Release();
if (pDevice) pDevice->Release();
std::cout << "Hit 4." << std::endl;
Sleep(1000);
}
}
/// IMGUI END
// =================================================================
// 1. Logging and Helper Functions
// =================================================================
// Thread-safe logging function to avoid conflicts during multi-threaded writes
void log_info(const char* fmt, ...)
{
std::lock_guard<std::mutex> lock(log_mutex);
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
printf("\n");
va_end(args);
}
/**
* @brief Finds methods with specified names in a given IL2CPP class
* @Param klass Target IL2CPP class pointer
* @Param names List of method names to find
*/
void find_methods_by_names(Il2CppClass* klass, const std::vector<std::string>& names)
{
found_methods.clear();
if (!klass) return;
void* iter = nullptr;
const Il2CppMethod* method;
while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(klass, &iter)))
{
const char* mname = il2cpp_method_get_name(method);
if (!mname) continue;
for (const auto& target : names) // Use const auto& for optimized iteration
{
if (strcmp(mname, target.c_str()) == 0)
{
found_methods.push_back(method);
log_info("[FIND] Method %s found @ %p", mname, method);
}
}
}
}
/**
* @brief Core patch function: Applies a RET Hook (0xC3)
* @details Changes the first byte of the method's entry address to RET (0xC3), causing the function to return immediately, effectively disabling the feature.
* @Param method Target IL2Cpp method pointer
*/
void patch_method_ret(const Il2CppMethod* method)
{
if (!method) return;
// Get the actual function pointer of the method
void* fn = *(void**)method;
if (!fn)
{
log_info("[-] Method pointer is null. Skipping patch.");
return;
}
DWORD oldProtect;
// Change memory protection to PAGE_EXECUTE_READWRITE
if (!VirtualProtect(fn, 1, PAGE_EXECUTE_READWRITE, &oldProtect))
{
log_info("[-] VirtualProtect failed on address %p", fn);
return;
}
// Write the RET instruction (0xC3)
*(uint8_t*)fn = 0xC3;
// Restore original memory protection
VirtualProtect(fn, 1, oldProtect, &oldProtect);
log_info("[+] Patch successfully applied, address %p -> RET", fn);
}
// Safe version of patch_method_ret with waiting
void patch_method_ret_safe(const Il2CppMethod* method) {
if (!method) return;
void* fn = nullptr;
while (!(fn = *(void**)method)) {
Sleep(50);
}
DWORD oldProtect;
if (VirtualProtect(fn, 16, PAGE_EXECUTE_READWRITE, &oldProtect)) {
uint8_t* code = (uint8_t*)fn;
code[0] = 0xC3;
VirtualProtect(fn, 16, oldProtect, &oldProtect);
}
}
/**
* @brief Core patch function: Forces a boolean Getter to return a specified value
* @details Changes the method machine code to MOV AL, value; RET, used to force a property to return True or False.
* @Param fn Target function pointer
* @Param value Boolean value to force return
*/
void patch_bool_getter(void* fn, bool value)
{
if (!fn) return;
DWORD oldProtect;
// Change memory protection
if (!VirtualProtect(fn, 16, PAGE_EXECUTE_READWRITE, &oldProtect))
{
log_info("[-] VirtualProtect failed on patch_bool_getter at address %p", fn);
return;
}
uint8_t* code = (uint8_t*)fn;
code[0] = 0xB0; // mov al, imm8 (opcode)
code[1] = value ? 1 : 0; // immediate value (1 or 0)
code[2] = 0xC3; // ret (return instruction)
// Restore original memory protection
VirtualProtect(fn, 16, oldProtect, &oldProtect);
}
void patch_float_getter(void* fn, float value)
{
if (!fn) return;
static float forced_value = 0.0f;
forced_value = value;
uint8_t stub[32];
memset(stub, 0x90, sizeof(stub));
stub[0] = 0x48;
stub[1] = 0xB8;
uint64_t addr = (uint64_t)&forced_value;
memcpy(&stub[2], &addr, sizeof(addr)); // imm64
int idx = 2 + (int)sizeof(addr);
stub[idx++] = 0xF3;
stub[idx++] = 0x0F;
stub[idx++] = 0x10;
stub[idx++] = 0x00;
stub[idx++] = 0xC3;
DWORD oldProtect;
SIZE_T len = idx;
VirtualProtect(fn, len, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(fn, stub, len);
FlushInstructionCache(GetCurrentProcess(), fn, len);
VirtualProtect(fn, len, oldProtect, &oldProtect);
}
/**
* @brief Finds and applies the RET Hook patch to a group of methods
*/
void patch_methods_with_logging(Il2CppClass* klass,
const char* ns, const char* name,
const std::vector<std::string>& methods)
{
if (!klass)
{
log_info("[-] Cannot find class %s.%s. Skipping patch.", ns, name);
return;
}
log_info("[PATCHING] Applying patches for class %s.%s", ns, name);
void* iter = nullptr;
const Il2CppMethod* method;
while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(klass, &iter)))
{
const char* mname = il2cpp_method_get_name(method);
if (!mname) continue;
for (const auto& t : methods)
{
if (strcmp(mname, t.c_str()) == 0)
{
void* fn = *(void**)method;
log_info("[PATCH] %s.%s.%s @ %p", ns, name, mname, fn);
patch_method_ret(method);
}
}
}
}
/**
* @brief Finds the BattleEye initialization method (EFT.TarkovApplication)
*/
const Il2CppMethod* find_battleye_init(Il2CppClass* main_application)
{
void* iter = nullptr;
const Il2CppMethod* method;
while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(main_application, &iter)))
{
const char* name = il2cpp_method_get_name(method);
if (!name) continue;
// In some versions, the BE init method name is obfuscated, identified by a characteristic byte sequence
if ((unsigned(name[0]) & 0xFF) == 0xEE &&
(unsigned(name[1]) & 0xFF) == 0x80 &&
(unsigned(name[2]) & 0xFF) == 0x81)
return method;
// In other versions, it might be ValidateAnticheat
if (strcmp(name, "ValidateAnticheat") == 0)
return method;
}
return nullptr;
}
// Wait for battleye init method
const Il2CppMethod* wait_for_battleye_init(Il2CppClass* main_application) {
const Il2CppMethod* method = nullptr;
while (!(method = find_battleye_init(main_application))) Sleep(200);
return method;
}
// Find show error screen methods
void find_show_error_screen(Il2CppClass* preloader_ui)
{
error_screen_methods.clear();
void* iter = nullptr;
const Il2CppMethod* method;
while ((method = il2cpp_class_get_methods(preloader_ui, &iter)))
{
const char* name = il2cpp_method_get_name(method);
if (!name) continue;
if (strcmp(name, "ShowErrorScreen") == 0)
{
error_screen_methods.push_back(method);
}
}
}
/**
* @brief Finds the Unity core Assembly-CSharp.dll module
*/
Il2CppImage* il2cpp_image_loaded(const char* image_name)
{
Il2CppDomain* domain = il2cpp_get_root_domain();
if (!domain) return nullptr;
size_t size = 0;
const Il2CppAssembly* const* asmbl = il2cpp_domain_get_assemblies(domain, &size);
if (!asmbl) return nullptr;
for (size_t i = 0; i < size; i++)
{
const Il2CppImage* img = il2cpp_assembly_get_image(asmbl[i]);
if (!img) continue;
const char* nm = il2cpp_image_get_name(img);
if (!nm) continue;
if (_stricmp(nm, image_name) == 0)
return (Il2CppImage*)img;
}
return nullptr;
}
/**
* @brief Gets the singleton instance of EFT.CameraControl.CameraManager via IL2CPP runtime invoke.
* @Param image Il2CppImage pointer of Assembly-CSharp.dll
* @returN Pointer to the CameraManager instance, or nullptr on failure
*/
void* get_camera_manager_instance(Il2CppImage* image)
{
// 1. Find CameraManager class
auto cameraManagerClass = il2cpp_class_from_name(image, "EFT.CameraControl", "CameraManager");
if (!cameraManagerClass) return nullptr;
// 2. Find and cache get_Instance method (only search on first execution)
if (!g_cachedGetInstanceMethod) {
void* iter = nullptr;
const Il2CppMethod* method;
while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(cameraManagerClass, &iter)))
{
const char* mname = il2cpp_method_get_name(method);
if (!mname) continue;
if (strcmp(mname, "get_Instance") == 0) {
g_cachedGetInstanceMethod = method;
break;
}
}
if (!g_cachedGetInstanceMethod) return nullptr; // get_Instance not found
}
// 3. Execute il2cpp_runtime_invoke
if (!il2cpp_runtime_invoke) return nullptr; // Ensure API is loaded
void* exc = nullptr; // Exception pointer
// Call static method get_Instance, so instance object pointer is nullptr
void* instance = il2cpp_runtime_invoke(g_cachedGetInstanceMethod, nullptr, nullptr, &exc);
// 4. Check call result and exception
if (exc) {
// In a real application, exception information could be logged
return nullptr;
}
return instance;
}
// Get TarkovApplication instance
void* get_tarkov_application_instance(Il2CppClass* klass)
{
uintptr_t static_fields_ptr = *(uintptr_t*)((uintptr_t)klass + STATIC_FIELDS_OFFSET);
if (!static_fields_ptr) return nullptr;
uintptr_t* static_fields = (uintptr_t*)static_fields_ptr;
for (int i = 0; i < 32; i++)
{
uintptr_t candidate = static_fields[i];
if (!candidate) continue;
Il2CppClass* objClass = *(Il2CppClass**)candidate;
if (objClass == klass)
return (void*)candidate;
}
return nullptr;
}
// =================================================================
// 2. Main Execution Logic
// =================================================================
void start()
{
// Enable console output
AllocConsole();
FILE* conout = nullptr;
if (freopen_s(&conout, "CONOUT$", "w", stdout) != 0)
{
printf("freopen_s failed\n");
}
if (freopen_s(&conout, "CONOUT$", "w", stderr) != 0)
{
printf("freopen_s failed\n");
}
if (freopen_s(&conout, "CONIN$", "r", stdin) != 0)
{
printf("freopen_s failed\n");
}
// 1. Wait for GameAssembly.dll to load and get IL2CPP API addresses
HMODULE il2cpp = nullptr;
while (!(il2cpp = GetModuleHandleA("GameAssembly.dll")))
{
log_info("[-] Waiting for GameAssembly.dll...");
Sleep(2000);
}
printf("GameAssembly Address = %p\n", il2cpp);
// Get all required IL2CPP core function pointers
il2cpp_get_root_domain = (il2cpp_get_root_domain_prot)GetProcAddress(il2cpp, "il2cpp_domain_get");
il2cpp_thread_attach = (il2cpp_thread_attach_prot)GetProcAddress(il2cpp, "il2cpp_thread_attach");
il2cpp_domain_get_assemblies = (il2cpp_domain_get_assemblies_prot)GetProcAddress(il2cpp, "il2cpp_domain_get_assemblies");
il2cpp_class_from_name = (il2cpp_class_from_name_prot)GetProcAddress(il2cpp, "il2cpp_class_from_name");
il2cpp_class_get_methods = (il2cpp_class_get_methods_prot)GetProcAddress(il2cpp, "il2cpp_class_get_methods");
il2cpp_method_get_name = (il2cpp_method_get_name_prot)GetProcAddress(il2cpp, "il2cpp_method_get_name");
il2cpp_assembly_get_image = (il2cpp_assembly_get_image_prot)GetProcAddress(il2cpp, "il2cpp_assembly_get_image");
il2cpp_image_get_name = (il2cpp_image_get_name_prot)GetProcAddress(il2cpp, "il2cpp_image_get_name");
// Load runtime invoke function
il2cpp_runtime_invoke = (il2cpp_runtime_invoke_prot)GetProcAddress(il2cpp, "il2cpp_runtime_invoke");
// Add a startup delay to avoid operating too early
printf("[-] Waiting for game window.");
while (!isGameReady())
{
printf(".");
Sleep(100);
}
printf("\n");
Il2CppDomain* domain = il2cpp_get_root_domain();
if (!domain) {
log_info("[-] Could not get root domain. Exiting.");
return;
}
il2cpp_thread_attach(domain);
// 2. Wait for Assembly-CSharp.dll to load
Il2CppImage* image = nullptr;
while (!(image = il2cpp_image_loaded("Assembly-CSharp.dll")))
{
log_info("[-] Waiting for Assembly-CSharp.dll...");
Sleep(2000);
}
printf("[+] Assembly-CSharp.dll Address @ %p\n", image);
// 3. Thread Attach: Must be done after the core DLL (Assembly-CSharp) is loaded
il2cpp_thread_attach(domain);
printf("[+] Successfully attached to IL2CPP domain\n");
// 4. Find and apply BattleEye & UI patches
// Find EFT.TarkovApplication class
auto main_app = il2cpp_class_from_name(image, "EFT", "TarkovApplication");
if (!main_app)
{
printf("[-] Could not find TarkovApplication. Patching aborted.\n");
}
else
{
printf("[+] TarkovApplication Address @ %p\n", main_app);
// Patch BattleEye initialization method
const Il2CppMethod* be = find_battleye_init(main_app);
if (be)
{
log_info("[+] BattleEye initialization method found.");
patch_method_ret(be);
}
else {
log_info("[-] BattleEye initialization method not found. Will attempt manual DLL unload.");
}
}
// Find EFT.UI.PreloaderUI class to disable the error screen
auto preloader_ui = il2cpp_class_from_name(image, "EFT.UI", "PreloaderUI");
if (!preloader_ui)
{
printf("[-] Could not find PreloaderUI. Skipping error screen patch.\n");
}
else
{
printf("[+] PreloaderUI Address @ %p\n", preloader_ui);
// Patch ShowErrorScreen
error_screen_methods.clear();
const Il2CppMethod* m = nullptr;
void* iter = nullptr;
while ((m = (const Il2CppMethod*)il2cpp_class_get_methods(preloader_ui, &iter)))
{
const char* name = il2cpp_method_get_name(m);
if (!name) continue;
if (strcmp(name, "ShowErrorScreen") == 0)
{
printf("[+] Found ShowErrorScreen @ %p\n", m);
error_screen_methods.push_back(m);
}
}
for (const auto& method_to_patch : error_screen_methods)
patch_method_ret(method_to_patch);
}
// 5. Apply core PVE patches (RET Hooks)
// Make the player immortal and disable fall damage
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.HealthSystem", "ActiveHealthController"),
"EFT.HealthSystem", "ActiveHealthController",
{ "ChangeHydration", "ChangeEnergy", "HandleFall", "SetEncumbered", "SetOverEncumbered", "AddFatigue" });
// Infinite stamina
patch_methods_with_logging(il2cpp_class_from_name(image, "", "Stamina"),
"", "Stamina", { "Process", "Consume" });
// Disable AI attack and tracking
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT", "BotMemory"),
"EFT", "BotMemory",
{ "Spotted", "SetLastTimeSeeEnemy", "LoseVisionCurrentEnemy", "LastEnemyVisionOld", "set_GoalEnemy" });
// Disable recoil (multiple classes)
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations.NewRecoil", "NewRecoilShotEffect"),
"EFT.Animations.NewRecoil", "NewRecoilShotEffect",
{ "AddRecoilForce" });
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "NewRotationRecoilProcess"),
"EFT.Animations", "NewRotationRecoilProcess",
{ "CalculateRecoil" });
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "OldRecoilShotEffect"),
"EFT.Animations", "OldRecoilShotEffect",
{ "AddRecoilForce" });
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "OldRotationRecoilProcess"),
"EFT.Animations", "OldRotationRecoilProcess",
{ "CalculateRecoil" });
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "RecoilProcessBase"),
"EFT.Animations", "RecoilProcessBase",
{ "CalculateRecoil" });
// Disable boundary and trap damage
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Interactive", "BarbedWire"),
"EFT.Interactive", "BarbedWire",
{ "AddPenalty", "ProceedDamage" });
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Interactive", "Minefield"),
"EFT.Interactive", "Minefield",
{ "DealExplosionDamage", "Explode" });
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Interactive", "SniperFiringZone"),
"EFT.Interactive", "SniperFiringZone",
{ "Shoot" });
// Disable weapon wear/durability loss
patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.InventoryLogic", "Weapon"),
"EFT.InventoryLogic", "Weapon",
{ "OnShot" });
// Can breach any door with the "Breach" option successfully
auto door_class = il2cpp_class_from_name(image, "EFT.Interactive", "Door");
find_methods_by_names(door_class, { "BreachSuccessRoll" });
if (!found_methods.empty())
{
void* fn = *(void**)found_methods[0];
patch_bool_getter(fn, true);
log_info("Patched Door.BreachSuccessRoll @ %p to return TRUE", fn);
}
// 6. Apply Boolean Getter Patches (Force return False)
auto skill_class = il2cpp_class_from_name(image, "EFT", "SkillManager");
if (skill_class) {
find_methods_by_names(skill_class, { "get_AttentionEliteLuckySearchValue" });
if (!found_methods.empty()) {
struct MyIl2CppMethod { void* methodPointer; };
void* fn = ((MyIl2CppMethod*)found_methods[0])->methodPointer;
if (fn) {
patch_float_getter(fn, 1.0f);
printf("patched EFT.SkillManager.get_AttentionEliteLuckySearchValue @ %p -> returns 1.0f\n", fn);
}
else {
printf("[-] methodPointer is null for get_AttentionEliteLuckySearchValue\n");
}
}
else {
printf("[-] EFT.SkillManager.get_AttentionEliteLuckySearchValue unfound\n");
}
}
else {
printf("[-] EFT.SkillManager unfound\n");
}
auto weapon_class = il2cpp_class_from_name(image, "EFT.InventoryLogic", "Weapon");
// Disable weapon malfunction
find_methods_by_names(weapon_class, { "get_AllowMalfunction" });
if (!found_methods.empty())
{
void* fn = *(void**)found_methods[0];
patch_bool_getter(fn, false);
log_info("Patched get_AllowMalfunction @ %p to return FALSE", fn);
}
// Disable weapon overheating
find_methods_by_names(weapon_class, { "get_AllowOverheat" });
if (!found_methods.empty())
{
void* fn = *(void**)found_methods[0];
patch_bool_getter(fn, false);
log_info("Patched get_AllowOverheat @ %p to return FALSE", fn);
}
// 7. Unload BattleEye client DLL
HMODULE beclient = GetModuleHandleA("BEClient_x64.dll");
if (beclient)
{
printf("[+] BEClient_x64 @ %p is unloading...\n", beclient);
FreeLibrary(beclient);
}
// =================================================================
// 8. Idle Loop and CameraManager Tracking (Core Merge)
// =================================================================
log_info("\n[+] All patches applied. Entering idle tracking loop. Hooking menu.");
CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)HookPresent, nullptr, 0, nullptr);
// Keep the thread alive to prevent it from exiting the IL2CPP domain, avoiding fatal GC errors.
while (true)
{
// TarkovApplication instance tracking
if (!g_TarkovApplicationInstance)
{
g_TarkovApplicationInstance = get_tarkov_application_instance(main_app);
if (g_TarkovApplicationInstance)
log_info("[+] TarkovApplication = 0x%llX", (uintptr_t)g_TarkovApplicationInstance);
}
// CameraManager tracking logic
void* cm = get_camera_manager_instance(image);
if (cm)
{
// Track CameraManager instance changes - print on initial detection OR when instance changes
if (cm != g_CameraManagerInstance)
{
if (!g_CameraManagerInstance) {
log_info("[+] CameraManager initial detection: 0x%llX", (uintptr_t)cm);
}
else {
log_info("[+] CameraManager instance updated: 0x%llX", (uintptr_t)cm);
}
g_CameraManagerInstance = cm;
}
}
// Keep the thread active, using 3-second sleep to minimize CPU usage and set the tracking interval
Sleep(3000);
}
}
// =================================================================
// 3. DLL Entry Point
// =================================================================
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
{
// Create a new thread to run the main logic when the DLL is attached to the process
if (reason == DLL_PROCESS_ATTACH)
CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)start, nullptr, 0, nullptr);
return TRUE;
}