- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 519
- Reaction score
- 7
Anyone else getting clapped by the "Corrupted Memory Kick" in Siege lately? It looks like the old internal bases floating around are hitting a wall with current BattlEye integrity checks. I've been messing with a private base, and while the logic is solid, the hook implementation is screaming for a manual ban.
The Technical Breakdown
The current setup uses a standard trampoline hook to hijack an entity call at ImageBase + 0x45374B0. The logic relies on a 12-byte absolute jump to redirect execution to the hook function. However, the implementation is using VirtualAlloc with PAGE_EXECUTE_READWRITE (RWX), which is basically a giant flare for any modern anti-cheat.
Why BattlEye is Kicking You
Changing the injection method might help with initial entry, but as long as that hook is sitting in the .text section, you're going to keep catching the corrupted memory kick after a few minutes of play.
Has anyone found a cleaner entry point for the entity loop on the latest patch?
The Technical Breakdown
The current setup uses a standard trampoline hook to hijack an entity call at ImageBase + 0x45374B0. The logic relies on a 12-byte absolute jump to redirect execution to the hook function. However, the implementation is using VirtualAlloc with PAGE_EXECUTE_READWRITE (RWX), which is basically a giant flare for any modern anti-cheat.
Code:
inline void init()
{
void* entity_call = reinterpret_cast<void*>(ImageBase + 0x45374B0);
void* trampoline = VirtualAlloc(nullptr, 32, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!trampoline) return;
memcpy(trampoline, entity_call, 12);
uint8_t* t = (uint8_t*)trampoline;
t[12] = 0x48; t[13] = 0xB8;
*(void**)(&t[14]) = (uint8_t*)entity_call + 12;
t[22] = 0xFF; t[23] = 0xE0;
uint32_t oldProtect;
utils::syscall::vm_protect(reinterpret_cast<uintptr_t>(entity_call), 12, PAGE_EXECUTE_READWRITE, &oldProtect);
uint8_t* dst = (uint8_t*)entity_call;
dst[0] = 0x48; dst[1] = 0xB8;
*(void**)(&dst[2]) = reinterpret_cast<void*>(hk::entity);
dst[10] = 0xFF; dst[11] = 0xE0;
utils::syscall::vm_protect(reinterpret_cast<uintptr_t>(entity_call), 12, oldProtect, &oldProtect);
original_entity_hook = trampoline;
}
Why BattlEye is Kicking You
- RWX Pages: Allocating RWX memory via VirtualAlloc is heavily scrutinized. BattlEye scans for executable pages that don't belong to a signed module.
- .text Section Integrity: You are directly modifying the game's executable code. BE performs periodic integrity checks on the module's memory. Even with a syscall to vm_protect, the modified bytes stick out during a scan.
- Instruction Patching: Patching 12 bytes in a high-frequency call like this is an easy target for pattern-based detection.
If you want to stay UD, you need to ditch the direct trampoline in the code section. Consider these alternatives:
— VMT Hooking: Swap the pointer in the virtual method table if the entity call allows it. It's less invasive than patching code.
— Import Address Table (IAT) Hooking: If the target is an imported function, redirection through the IAT is often safer than a raw trampoline.
— Stealth Allocations: If you must use a trampoline, use a kernel-level driver to allocate memory or use a codecave in a legitimate module to hide your transition code.
— VMT Hooking: Swap the pointer in the virtual method table if the entity call allows it. It's less invasive than patching code.
— Import Address Table (IAT) Hooking: If the target is an imported function, redirection through the IAT is often safer than a raw trampoline.
— Stealth Allocations: If you must use a trampoline, use a kernel-level driver to allocate memory or use a codecave in a legitimate module to hide your transition code.
Changing the injection method might help with initial entry, but as long as that hook is sitting in the .text section, you're going to keep catching the corrupted memory kick after a few minutes of play.
Has anyone found a cleaner entry point for the entity loop on the latest patch?