EFTCheatPVE/ioperator/dllmain.cpp

890 lines
30 KiB
C++

#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; // harmless
using il2cpp_thread_attach_prot = Il2CppThread * (*)(Il2CppDomain*);
static il2cpp_thread_attach_prot il2cpp_thread_attach = nullptr; // harmless
using il2cpp_domain_get_assemblies_prot = const Il2CppAssembly** (*)(Il2CppDomain*, size_t*);
static il2cpp_domain_get_assemblies_prot il2cpp_domain_get_assemblies = nullptr; //harmless
using il2cpp_class_from_name_prot = Il2CppClass * (*)(const Il2CppImage*, const char*, const char*);
static il2cpp_class_from_name_prot il2cpp_class_from_name = nullptr; // not working, test needed
using il2cpp_class_get_methods_prot = const Il2CppMethod* (*)(Il2CppClass*, void**);
static il2cpp_class_get_methods_prot il2cpp_class_get_methods = nullptr; // not working, test needed
using il2cpp_method_get_name_prot = const char* (*)(const Il2CppMethod*);
static il2cpp_method_get_name_prot il2cpp_method_get_name = nullptr; // not working
using il2cpp_assembly_get_image_prot = const Il2CppImage* (*)(const Il2CppAssembly*);
static il2cpp_assembly_get_image_prot il2cpp_assembly_get_image = nullptr; // harmless
using il2cpp_image_get_name_prot = const char* (*)(const Il2CppImage*);
static il2cpp_image_get_name_prot il2cpp_image_get_name = nullptr; // harmless
// 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; // harmless
// 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;
// =======================
// MINHOOK
// =======================
static bool g_MinHookInitialized = false;
static bool g_Hooked = false;
/// GVARS END
#define STATIC_FIELDS_OFFSET 0xB8
/// RNG START
auto getRandomSeed()
-> std::seed_seq
{
// This gets a source of actual, honest-to-god randomness
std::random_device source;
// Here, we fill an array of random data from the source
unsigned int random_data[10];
for (auto& elem : random_data) {
elem = source();
}
// this creates the random seed sequence out of the random data
return std::seed_seq(random_data + 0, random_data + 10);
}
double randomnumber() {
// Making rng static ensures that it stays the same
// Between different invocations of the function
static auto seed = getRandomSeed();
static std::default_random_engine rng(seed);
std::uniform_real_distribution<double> dist(0.0, 1.0);
return dist(rng);
}
/// RNG END
/// 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)
{
static bool initialized = false;
if (!initialized)
{
if (!pSwapChain)
return S_OK;
g_pSwapChain = pSwapChain;
if (FAILED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pd3dDevice)))
return S_OK;
g_pd3dDevice->GetImmediateContext(&g_pd3dDeviceContext);
DXGI_SWAP_CHAIN_DESC sd{};
pSwapChain->GetDesc(&sd);
HWND hWnd = sd.OutputWindow;
g_oOriginalWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui::StyleColorsDark();
if (!ImGui_ImplWin32_Init(hWnd) || !ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext))
return S_OK;
ID3D11Texture2D* pBackBuffer = nullptr;
if (FAILED(pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer)))
return S_OK;
if (FAILED(g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView)))
{
pBackBuffer->Release();
return S_OK;
}
pBackBuffer->Release();
initialized = true;
}
// 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("所有初始化已完成,无递归漏洞");
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);
}
void log_info(const char* fmt, ...);
void InitMinHook()
{
if (g_MinHookInitialized) return;
if (MH_Initialize() == MH_OK)
{
g_MinHookInitialized = true;
log_info("[+] MinHook initialized");
return;
}
log_info("[-] MinHook not initialized");
}
void HookPresent()
{
if (g_Hooked) return;
HWND gameWindow = FindWindowA("UnityWndClass", nullptr);
if (!gameWindow)
{
return;
}
InitMinHook();
IDXGISwapChain* pSwapChain = nullptr;
ID3D11Device* pDevice = nullptr;
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 (FAILED(hr) || !pSwapChain || !pDevice)
{
if (pSwapChain) pSwapChain->Release();
if (pDevice) pDevice->Release();
return;
}
void** vtable = *(void***)pSwapChain;
void* presentAddr = vtable[8];
if (MH_CreateHook(presentAddr, hkPresent, (LPVOID*)&oPresent) != MH_OK)
{
log_info("[-] MinHook creation failed");
goto post;
}
if (MH_EnableHook(presentAddr) != MH_OK)
{
log_info("[-] MinHook enable failed");
goto post;
}
log_info("[+] MinHook Present hooked @ %p -> %p", presentAddr, hkPresent);
g_Hooked = true;
post:
pSwapChain->Release();
pDevice->Release();
}
/// 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)
{
log_info("freopen_s failed");
}
// 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 = find_il2cpp_class_from_name(il2cpp);
if (! il2cpp_class_from_name)
{
log_info("[-] il2cpp_class_from_name was not found.");
Sleep(10000);
exit(-1);
}
log_info("[+] il2cpp_class_from_name loaded.");
il2cpp_class_get_methods = find_il2cpp_class_get_methods(il2cpp);
if (!il2cpp_class_get_methods)
{
log_info("[-] il2cpp_class_get_methods was not found.");
Sleep(10000);
exit(-1);
}
log_info("[+] il2cpp_class_get_methods loaded.");
il2cpp_method_get_name = find_il2cpp_method_get_name(il2cpp);
if (!il2cpp_method_get_name)
{
log_info("[-] il2cpp_method_get_name was not found.");
Sleep(10000);
exit(-1);
}
log_info("[+] il2cpp_method_get_name loaded.");
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);
}
log_info("");
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");
Sleep(2000);
// 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.");
log_info("[!] This cheat is free of charge and available on unknowncheats.me .");
log_info("[!] If you payed for this cheat, you've been scammed.");
log_info("[!] 此作弊模块在 unknowncheats.me 上免费公开共享。");
log_info("[!] 如果你花钱了,你被骗了,请举报卖家。");
// Keep the thread alive to prevent it from exiting the IL2CPP domain, avoiding fatal GC errors.
while (true)
{
// Keep the thread active, using 3-second sleep to minimize CPU usage and set the tracking interval
Sleep(3000);
HookPresent(); // yolo
// Signature repetition
if ((int)(randomnumber() * 10) < 2)
{
log_info("[!] This cheat is free of charge and available on unknowncheats.me .");
log_info("[!] If you payed for this cheat, you've been scammed.");
log_info("[!] 此作弊模块在 unknowncheats.me 上免费公开共享。");
log_info("[!] 如果你花钱了,你被骗了,请举报卖家。");
}
// 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)
continue;
// Track CameraManager instance changes - print on initial detection OR when instance changes
if (cm == g_CameraManagerInstance)
continue;
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;
}
}
// =================================================================
// 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;
}