some hooking struggles. still has random stackoverflow crashes.

This commit is contained in:
NukedBart 2025-12-14 18:51:20 +08:00
parent 1cd8378078
commit f29f966846
8 changed files with 180 additions and 1014 deletions

View file

@ -69,114 +69,43 @@ 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
// il2cpp patch
// Helper: pattern scan with mask ('x' == match, '?' == wildcard)
static uint8_t* find_pattern(uint8_t* base, SIZE_T size, const unsigned char* pattern, const char* mask, SIZE_T pattern_len)
/// RNG START
auto getRandomSeed()
-> std::seed_seq
{
if (!base || !pattern || !mask || pattern_len == 0) return nullptr;
SIZE_T max_scan = (size > pattern_len) ? (size - pattern_len) : 0;
for (SIZE_T i = 0; i <= max_scan; ++i)
{
bool matched = true;
for (SIZE_T j = 0; j < pattern_len; ++j)
{
if (mask[j] == '?') continue;
if (base[i + j] != pattern[j])
{
matched = false;
break;
}
}
if (matched)
return base + i;
}
return nullptr;
}
// This gets a source of actual, honest-to-god randomness
std::random_device source;
// Find the real il2cpp_class_from_name implementation by scanning GameAssembly for an IDA-provided signature.
// Returns the function pointer if found, otherwise nullptr.
static il2cpp_class_from_name_prot find_il2cpp_class_from_name(HMODULE handle)
{
if (!handle) return nullptr;
// Get module size via PE headers
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)handle;
if (dos->e_magic != IMAGE_DOS_SIGNATURE) return nullptr;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((uint8_t*)handle + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) return nullptr;
SIZE_T module_size = nt->OptionalHeader.SizeOfImage;
uint8_t* base = (uint8_t*)handle;
// IDA signature:
// 4C 89 44 24 ? 48 89 54 24 ? 53 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??
// Build pattern and mask (use 0x00 as placeholder for wildcard bytes)
const unsigned char pattern[] = {
0x4C, 0x89, 0x44, 0x24, 0x00, // 4C 89 44 24 ?
0x48, 0x89, 0x54, 0x24, 0x00, // 48 89 54 24 ?
0x53, 0x55, 0x56, 0x57,
0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57,
0x48, 0x81, 0xEC, 0x00, 0x00, 0x00, 0x00 // 48 81 EC ?? ?? ?? ??
};
// Mask: 'x' for fixed bytes, '?' for wildcard
const char mask[] = "xxxx?xxxx?xxxxxxxxxxxx????";
const SIZE_T pattern_len = sizeof(pattern);
uint8_t* found = find_pattern(base, module_size, pattern, mask, pattern_len);
if (found)
{
// The found address should point to the real function entry. Return as function pointer.
return (il2cpp_class_from_name_prot)found;
// Here, we fill an array of random data from the source
unsigned int random_data[10];
for (auto& elem : random_data) {
elem = source();
}
// If not found with this signature, attempt a looser fallback:
// Search for the common prologue bytes "53 55 56 57 41 54 41 55 41 56 41 57" (function push regs)
const unsigned char fallback_pattern[] = {
0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57
};
const char fallback_mask[] = "xxxxxxxxxxxx";
uint8_t* fallback_found = find_pattern(base, module_size, fallback_pattern, fallback_mask, sizeof(fallback_pattern));
if (fallback_found)
{
// Walk backwards up to a small range to try to find the full expected prologue start (heuristic).
// Limit to 32 bytes back to avoid scanning too far.
for (int back = 0; back < 32; ++back)
{
uint8_t* candidate = fallback_found - back;
// Check the bytes before candidate for the 0x4C 0x89 sequence which often starts the prologue we expect.
if (candidate >= base && candidate[0] == 0x4C && candidate[1] == 0x89)
{
return (il2cpp_class_from_name_prot)candidate;
}
}
// Otherwise return the fallback_found as best-effort.
return (il2cpp_class_from_name_prot)fallback_found;
}
return nullptr;
// this creates the random seed sequence out of the random data
return std::seed_seq(random_data + 0, random_data + 10);
}
// Find the real il2cpp_class_get_methods.
// Returns the function pointer if found, otherwise nullptr.
static il2cpp_class_get_methods_prot find_il2cpp_class_get_methods(HMODULE handle)
{
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);
if (!handle) return nullptr;
return (il2cpp_class_get_methods_prot)GetProcAddress(handle, "mono_class_get_methods");
std::uniform_real_distribution<double> dist(0.0, 1.0);
return dist(rng);
}
static il2cpp_method_get_name_prot find_il2cpp_method_get_name(HMODULE handle)
{
if (!handle) return nullptr;
return (il2cpp_method_get_name_prot)GetProcAddress(handle, "mono_property_get_set_method");
}
// il2cpp patch end
/// RNG END
/// IMGUI START
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
@ -208,45 +137,47 @@ static PresentFn oPresent = nullptr;
HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags)
{
if (!g_pd3dDevice)
static bool initialized = false;
if (!initialized)
{
if (!pSwapChain)
return S_OK;
g_pSwapChain = pSwapChain;
// D3D11 Device
if (FAILED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pd3dDevice)))
return oPresent(pSwapChain, SyncInterval, Flags);
return S_OK;
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);
if (!ImGui_ImplWin32_Init(hWnd) || !ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext))
return S_OK;
// create RenderTargetView
ID3D11Texture2D* pBackBuffer = nullptr;
if (SUCCEEDED(pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer)))
if (FAILED(pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer)))
return S_OK;
if (FAILED(g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView)))
{
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
pBackBuffer->Release();
return S_OK;
}
else
{
return oPresent(pSwapChain, SyncInterval, Flags);
}
pBackBuffer->Release();
initialized = true;
}
// render
@ -258,14 +189,12 @@ HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT
{
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::Text("所有初始化已完成,无递归漏洞");
ImGui::TextColored(ImVec4(1, 1, 0, 1), "你可以开始第二阶段了!");
ImGui::End();
}
@ -276,59 +205,77 @@ HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT
return oPresent(pSwapChain, SyncInterval, Flags);
}
// VTable Hook
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()
{
while (true)
if (g_Hooked) return;
HWND gameWindow = FindWindowA("UnityWndClass", nullptr);
if (!gameWindow)
{
// 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);
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
@ -515,14 +462,11 @@ const Il2CppMethod* find_battleye_init(Il2CppClass* main_application)
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;
}
@ -643,15 +587,7 @@ void start()
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");
log_info("freopen_s failed");
}
// 1. Wait for GameAssembly.dll to load and get IL2CPP API addresses
@ -670,29 +606,29 @@ void start()
il2cpp_class_from_name = find_il2cpp_class_from_name(il2cpp);
if (! il2cpp_class_from_name)
{
printf("[-] il2cpp_class_from_name was not found.");
log_info("[-] il2cpp_class_from_name was not found.");
Sleep(10000);
exit(-1);
}
printf("[+] il2cpp_class_from_name loaded.");
log_info("[+] il2cpp_class_from_name loaded.");
il2cpp_class_get_methods = find_il2cpp_class_get_methods(il2cpp);
if (!il2cpp_class_get_methods)
{
printf("[-] il2cpp_class_get_methods was not found.");
log_info("[-] il2cpp_class_get_methods was not found.");
Sleep(10000);
exit(-1);
}
printf("[+] il2cpp_class_get_methods loaded.");
log_info("[+] il2cpp_class_get_methods loaded.");
il2cpp_method_get_name = find_il2cpp_method_get_name(il2cpp);
if (!il2cpp_method_get_name)
{
printf("[-] il2cpp_method_get_name was not found.");
log_info("[-] il2cpp_method_get_name was not found.");
Sleep(10000);
exit(-1);
}
printf("[+] il2cpp_method_get_name loaded.");
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");
@ -706,7 +642,7 @@ void start()
printf(".");
Sleep(100);
}
printf("\n");
log_info("");
Il2CppDomain* domain = il2cpp_get_root_domain();
if (!domain) {
@ -891,13 +827,29 @@ void start()
// 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);
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)
{
@ -908,23 +860,20 @@ void start()
// 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;
}
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);
}
// Keep the thread active, using 3-second sleep to minimize CPU usage and set the tracking interval
Sleep(3000);
g_CameraManagerInstance = cm;
}
}