Inside the DLL, we set up hooks for luaL_loadstring or lua_pcall to intercept script execution.
// dllmain.cpp #include <windows.h> #include "minhook.h" // MinHook library #include <lua.hpp>typedef int(luaL_loadstring_t)(lua_State L, const char* s); luaL_loadstring_t original_luaL_loadstring = nullptr;
int hooked_luaL_loadstring(lua_State* L, const char* s) // Log or modify script before execution OutputDebugStringA(s); return original_luaL_loadstring(L, s);
DWORD WINAPI MainThread(LPVOID lpReserved) // Wait for FiveM to load Lua engine while (!GetModuleHandleA("lua53.dll")) Sleep(100);
MH_Initialize(); // Hook luaL_loadstring inside FiveM's Lua module void* target = GetProcAddress(GetModuleHandleA("lua53.dll"), "luaL_loadstring"); MH_CreateHook(target, &hooked_luaL_loadstring, (void**)&original_luaL_loadstring); MH_EnableHook(target); return 0;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) if (reason == DLL_PROCESS_ATTACH) CreateThread(NULL, 0, MainThread, NULL, 0, NULL); return TRUE;fivem lua executor source
| Category | Details | |----------|---------| | Ban risk | FiveM uses automatic detection for external injection – bans are common and can be hardware ID based. | | Malware | Many “free executor sources” include hidden RATs, keyloggers, or miners. | | Instability | Hooking into FiveM incorrectly causes crashes, save corruption, or OS instability. | | Legal | Reverse engineering game clients violates ToS and, in some regions, computer misuse laws. | | Outdated | FiveM updates break executor hooks quickly; sources are often abandoned. |
To execute custom scripts, we need our own Lua state or reuse FiveM's existing one. Here we create a console that accepts user input and runs it.
// lua_engine.cpp #include <lua.hpp> #include <iostream> #include <string>lua_State* g_LuaState = nullptr;
// Custom print for our executor int executor_print(lua_State* L) int n = lua_gettop(L); for (int i = 1; i <= n; i++) std::cout << lua_tostring(L, i); if (i < n) std::cout << "\t"; std::cout << std::endl; return 0; Inside the DLL, we set up hooks for
// Example: trigger game native (simplified) int trigger_native(lua_State* L) const char* native = lua_tostring(L, 1); // Call native via pattern scanning (omitted for brevity) lua_pushboolean(L, true); return 1;
void InitializeLua() g_LuaState = luaL_newstate(); luaL_openlibs(g_LuaState);
// Register custom functions lua_register(g_LuaState, "print", executor_print); lua_register(g_LuaState, "TriggerNative", trigger_native); // Run a test script const char* testScript = R"( print("Executor loaded!") local result = TriggerNative("PLAYER_PED_ID") print("Player ped handle:", result) )"; if (luaL_dostring(g_LuaState, testScript) != LUA_OK) std::cout << "Lua error: " << lua_tostring(g_LuaState, -1) << std::endl;
void ExecuteString(const char* code) if (luaL_dostring(g_LuaState, code) != LUA_OK) std::cout << "Error: " << lua_tostring(g_LuaState, -1) << std::endl; lua_pop(g_LuaState, 1);
The injector finds the FiveM process and loads our DLL.
// injector.cpp #include <windows.h> #include <tlhelp32.h> #include <iostream>DWORD GetProcessIdByName(const char* procName) HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(snapshot, &entry)) do if (!strcmp(entry.szExeFile, procName)) CloseHandle(snapshot); return entry.th32ProcessID; while (Process32Next(snapshot, &entry)); CloseHandle(snapshot); return 0;int main() DWORD pid = GetProcessIdByName("FiveM_GTAProcess.exe"); if (!pid) std::cout << "FiveM not found.\n"; return 1;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); void* alloc = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT, PAGE_READWRITE); char dllPath[MAX_PATH]; GetFullPathNameA("executor.dll", MAX_PATH, dllPath, NULL); WriteProcessMemory(hProcess, alloc, dllPath, strlen(dllPath) + 1, NULL); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, alloc, 0, NULL); WaitForSingleObject(hThread, INFINITE); std::cout << "Injected.\n"; return 0;