WELCOME TO INFOCHEATS.NET

INFOCHEATS is a community-driven platform focused on free game cheats, cheat development, and verified commercial software for a wide range of popular games. We provide a large collection of free cheats shared by the community. All public releases are checked for malicious code to reduce the risk of viruses, malware, or unwanted software before users interact with them.

Alongside free content, INFOCHEATS hosts an active marketplace with many independent sellers offering commercial cheats. Each product is discussed openly, with user feedback, reviews, and real usage experience available to help you make informed decisions before purchasing.

Whether you are looking for free cheats, exploring paid solutions, comparing sellers, or studying how cheats are developed and tested, INFOCHEATS brings everything together in one place — transparently and community-driven.

Guide [Source] FiveM Adhesive Blocker — Anti-Cheat Thread Suspension

byte_corvus

Newbie
Newbie
Newbie
Newbie
Status
Offline
Joined
Mar 3, 2026
Messages
546
Reaction score
7
FiveM Adhesive Blocker — Thread Suspension Method

Ever dealt with the aggressive scanning from FiveM's adhesive module? Most people try to patch bytes or hook functions, but that usually results in an immediate integrity check failure. This approach is much cleaner — a "No-Patch" technique that avoids triggering the AC's self-defense by simply putting the scanner to sleep.

The logic is straightforward: we identify the high-CPU thread within the adhesive.dll module responsible for the scanning loops and freeze it using SuspendThread. Since we aren't modifying the actual code of the module, the integrity checks are bypassed silently.

Technical Breakdown
  1. Locate GTAProcess.exe and find the adhesive.dll base address and image size.
  2. Enumerate all active threads in the target process.
  3. Use NtQueryInformationThread with
    Code:
    ThreadQuerySetWin32StartAddress
    to see if the thread belongs to the adhesive module.
  4. Filter for the most active threads using GetThreadTimes — the scanner is almost always the thread consuming the most cycles.
  5. Nuke the thread via SuspendThread once the user time threshold is met.

Code:
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>
#include <vector>
#include <string>
#include <psapi.h>
#include <algorithm>
 
#pragma comment(lib, "psapi.lib")
 
typedef NTSTATUS(NTAPI* pNtQueryInformationThread)(
    HANDLE ThreadHandle,
    ULONG ThreadInformationClass,
    PVOID ThreadInformation,
    ULONG ThreadInformationLength,
    PULONG ReturnLength
);
 
uintptr_t GetAdhesiveBase(HANDLE hProcess, size_t& modSize) {
    HMODULE hMods[1024]; DWORD cbNeeded;
    if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL)) {
        for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
            char szModName[MAX_PATH];
            if (GetModuleBaseNameA(hProcess, hMods[i], szModName, sizeof(szModName))) {
                std::string name = szModName;
                for (auto& c : name) c = tolower(c);
                if (name.find("adhesive") != std::string::npos) {
                    MODULEINFO modInfo; GetModuleInformation(hProcess, hMods[i], &modInfo, sizeof(modInfo));
                    modSize = modInfo.SizeOfImage; return (uintptr_t)modInfo.lpBaseOfDll;
                }
            }
        }
    }
    return 0;
}
 
struct ThreadProfile {
    DWORD id;
    uintptr_t start;
    unsigned __int64 userTime;
};
 
bool ProcessTarget(DWORD pid) {
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
    if (!hProcess) return false;
 
    size_t adhesiveSize = 0;
    uintptr_t adhesiveBase = GetAdhesiveBase(hProcess, adhesiveSize);
    if (!adhesiveBase) {
        CloseHandle(hProcess);
        return false;
    }
 
    std::cout << "[+] Found adhesive.dll at 0x" << std::hex << adhesiveBase << std::endl;
 
    pNtQueryInformationThread NtQueryInfoThread = (pNtQueryInformationThread)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationThread");
    std::vector<ThreadProfile> candidates;
 
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    THREADENTRY32 te; te.dwSize = sizeof(te);
    if (Thread32First(hSnap, &te)) {
        do {
            if (te.th32OwnerProcessID == pid) {
                HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te.th32ThreadID);
                if (hThread) {
                    uintptr_t startAddr = 0;
                    if (NtQueryInfoThread(hThread, 9, &startAddr, sizeof(startAddr), NULL) == 0) {
                        if (startAddr >= adhesiveBase && startAddr < adhesiveBase + adhesiveSize) {
                            FILETIME c, e, k, u;
                            if (GetThreadTimes(hThread, &c, &e, &k, &u)) {
                                ULARGE_INTEGER uli; uli.LowPart = u.dwLowDateTime; uli.HighPart = u.dwHighDateTime;
                                candidates.push_back({ te.th32ThreadID, startAddr, uli.QuadPart });
                            }
                        }
                    }
                    CloseHandle(hThread);
                }
            }
        } while (Thread32Next(hSnap, &te));
    }
    CloseHandle(hSnap);
 
    if (candidates.empty()) {
        CloseHandle(hProcess);
        return false;
    }
 
    std::sort(candidates.begin(), candidates.end(), [](const ThreadProfile& a, const ThreadProfile& b) {
        return a.userTime > b.userTime;
    });
 
    int suspended = 0;
    for (size_t i = 0; i < candidates.size() && i < 3; i++) {
        // Only suspend if UserTime > 500,000 (meaning it actually did something substantial)
        if (candidates[i].userTime > 500000) {
            HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, candidates[i].id);
            if (hThread) {
                std::cout << "[+] [removed]ing Security Thread [" << candidates[i].id << "] CPU: " << std::dec << candidates[i].userTime << " (Offset: +0x" << std::hex << (candidates[i].start - adhesiveBase) << ")" << std::endl;
                SuspendThread(hThread);
                suspended++;
                CloseHandle(hThread);
            }
        }
    }
 
    CloseHandle(hProcess);
    return suspended > 0;
}
 
int main() {
    std::cout << "--- FiveM Adhesive Blocker  ---" << std::endl;
    std::cout << "[*] Hunting for scanner threads..." << std::endl;
    
    while (true) {
        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        PROCESSENTRY32 pe; pe.dwSize = sizeof(pe);
        if (Process32First(hSnap, &pe)) {
            do {
                std::string n = pe.szExeFile;
                if (n.find("FiveM") != std::string::npos && n.find("GTAProcess.exe") != std::string::npos) {
                    if (ProcessTarget(pe.th32ProcessID)) {
                        std::cout << "\n[SUCCESS] Hitted. Scanner got [removed]ed" << std::endl;
                        CloseHandle(hSnap);
                        system("pause");
                        return 0;
                    }
                }
            } while (Process32Next(hSnap, &pe));
        }
        CloseHandle(hSnap);
        Sleep(1000);
    }
    return 0;
}

Risks & Implementation
While this works for now, be aware that future Adhesive revisions can easily implement a watchdog thread on a separate timer to check if the main scanner threads are still running (heartbeat check). If you use this, ensure you run your loader with admin rights to access ntdll.dll functions properly.

This technique is primarily for research. If you plan on using this for long sessions, you might want to randomly resume and re-suspend the threads to simulate regular activity if they ever implement active execution checks.

Anyone managed to find the specific offset for the heartbeat thread in the latest FiveM build? Have fun reversing.
 
Top