Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Lua FFI Plugin

Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Source -> Plugin development
View previous topic :: View next topic  
Author Message
Newbie cheater
Reputation: 2

Joined: 13 Mar 2013
Posts: 11

PostPosted: Sun Dec 07, 2014 12:04 pm    Post subject: Lua FFI Plugin This post has 2 review(s) Reply with quote

Edit: Not sure when or what changed, but it looks like this plugin is no longer needed, as the ffi C-extension loads and works fine when the binaries are placed in the clibs32/clibs64 folders. I went ahead and added a new release containing the FFI binaries built against CE's customized Lua 5.3. (Up to date as of CE 6.5)

Original Post


I wrote (using the word loosely) this plugin sometime back in 2013, and ended up losing interest in the project before I ever got around to making it release-worthy. Furthermore, I took a strange approach when loading in the Lua extension, and I don't really remember why. Regardless, it's worked for the last year, and I probably won't ever go back and clean it up, so I figured I'd go ahead and release it.

I've gone ahead and thrown the project up on github, but since I'm unable to post urls at this time, you'll have to navigate there on your own: juntalis/ce-luacontrib



local ffi = require('ffi')

-- Load all of our DLLs
local ntdll = ffi.load('ntdll.dll')
local kernel32 = ffi.load('kernel32.dll')
local user32 = ffi.load('user32.dll')
local msvcrt = ffi.load('msvcrt.dll')

  CRT Declarations (msvcrt.dll)
unsigned int wcstombs(char* s, const wchar_t* w, unsigned int c);
unsigned int mbstowcs(wchar_t* w, const char* s, unsigned int c);
unsigned int wcslen(const wchar_t* s);
unsigned int strlen(const char* s);
char *strcpy(char* d, const char* s);

  Lua wrapper functions for the above declarations.

function wcslen(ws)
   return tonumber(
         ffi.cast('const wchar_t*', ws)

function wstr2str(ws, length)
   local szbuf = length or wcslen(ws)
   local buf ='char[?]', szbuf+1)
   if msvcrt.wcstombs(buf, ffi.cast('const wchar_t*', ws), szbuf) == -1 then
      return nil
   return ffi.string(buf, szbuf)

function str2wstr(s)
   local szbuf = s:len()
   local buf ='wchar_t[?]', szbuf+1)
   if msvcrt.mbstowcs(buf, s, szbuf) == -1 then
      return nil
   return buf

function str2cstr(s)
   local szbuf = s:len()
   local buf ='char[?]', szbuf+1)
   msvcrt.strcpy(buf, s)
   return buf

  Native Declarations (ntdll.dll)
typedef union _LARGE_INTEGER {
      unsigned long   LowPart;
      long         HighPart;
   } s;
      unsigned long   LowPart;
      long         HighPart;
   } u;
   long long   QuadPart;

typedef struct _UNICODE_STRING {
   unsigned short   Length;
   unsigned short   MaximumLength;
   wchar_t         ***Buffer;

   unsigned long   NextEntryOffset;
   unsigned long   NumberOfThreads;
   LARGE_INTEGER   SpareLi1;
   LARGE_INTEGER   SpareLi2;
   LARGE_INTEGER   SpareLi3;
   LARGE_INTEGER   CreateTime;
   LARGE_INTEGER   UserTime;
   LARGE_INTEGER   KernelTime;
   UNICODE_STRING   ImageName;
   long         BasePriority;
   void         *UniqueProcessId;
   unsigned long   InheritedFromUniqueProcessId;
   unsigned long   HandleCount;
   unsigned char   Reserved4[4];
   void         *Reserved5[11];
   unsigned long   PeakPagefileUsage;
   unsigned long   PrivatePageCount;
   LARGE_INTEGER   Reserved6[6];

long NtQuerySystemInformation(unsigned long, void*, unsigned long, unsigned long*);

-- Constants
local SystemProcessInformation = 5

  Given a process's PID, return the filename of the executable.
function pid2name(pid)
   local status, result, psinfo, retlength =
        0, nil,
        ffi.cast('PSYSTEM_PROCESS_INFORMATION_DETAILD','unsigned char[?]', 102400)),'unsigned long[1]')
   status = tonumber(ntdll.NtQuerySystemInformation(
      ffi.cast('void*', psinfo),
   if status ~= 0 then return result end
   while (status == 0) and (tonumber(psinfo.NextEntryOffset) > 0) do
      if pid == tonumber(psinfo.UniqueProcessId) then
         result = cutil.wstr2str(psinfo.ImageName.Buffer, psinfo.ImageName.Length - 4)
         status = 1
         psinfo = ffi.cast(
            tonumber(ffi.cast('void*', psinfo)) + psinfo.NextEntryOffset
   return result

  Win32 API Declarations (user32.dll, kernel32.dll)
typedef void* HWND, *LPARAM;
typedef signed long BOOL;
typedef unsigned long DWORD;
typedef BOOL(__stdcall* WNDENUMPROC)(HWND hWnd, LPARAM lParam);

typedef struct _ENUMPARAM {
   HWND hWnd;

HWND SetFocus(HWND hWnd);
DWORD GetCurrentThreadId(void);
HWND GetForegroundWindow(void);
BOOL BringWindowToTop(HWND hWnd);
BOOL SetForegroundWindow(HWND hWnd);
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
DWORD GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);
BOOL AttachThreadInput(DWORD idAttach, DWORD idAttachTo, BOOL fAttach);

-- Constants & Explicit Imports
local TRUE,FALSE = 1,0
local GetCurrentThreadId = kernel32.GetCurrentThreadId
local SetFocus = user32.SetFocus
local GetForegroundWindow = user32.GetForegroundWindow
local BringWindowToTop = user32.BringWindowToTop
local SetForegroundWindow = user32.SetForegroundWindow
local EnumWindows = user32.EnumWindows
local GetWindowThreadProcessId = user32.GetWindowThreadProcessId
local AttachThreadInput = user32.AttachThreadInput

local enumWndsProc = ffi.cast('WNDENUMPROC', function(hWnd, lParam)
   local lpdwPID ='DWORD[1]')
   local ep = ffi.cast('PENUMPARAM', lParam)
   GetWindowThreadProcessId(hWnd, lpdwPID)
   if ep[0].PID == lpdwPID[0] then
      ep[0].hWnd = hWnd
      return FALSE
   return TRUE

-- Obviously this function suffers a few serious
-- flaws. It only returns the first window found
-- for a particular PID, so if your target has more
-- than one window open, there's no guarantee that
-- you're finding the window you're looking for.
pid2hwnd = function(pid)
   pid = pid or getOpenedProcessID()
   local ep ='ENUMPARAM[1]')
   ep[0].hWnd = nil
   ep[0].PID  = pid
   EnumWindows(enumWndsProc, ffi.cast('LPARAM', ep))
   return ep[0].hWnd

setForegroundWindow = function(hWnd)
   local dwFgPID ='DWORD[1]')
   local hWndFg = GetForegroundWindow()
   local dwFgTID = GetWindowThreadProcessId(hWndFg, dwFgPID)
   local dwMyTID = GetCurrentThreadId()
   AttachThreadInput(dwMyTID, dwFgTID, TRUE)
   AttachThreadInput(dwMyTID, dwFgTID, False)

One of the ways I've been using it:


  Code below is in its own file under the lua folder.(startup.lua)
function _M.onOpenProcess(pid)
   local procname = ntdll.pid2name(pid)
   while (pid == 0) or (procname == nil) do
      pid = getOpenedProcessID()
      procname = ntdll.pid2name(pid)
   for i,fn in ipairs(getpriv('onstartup')) do fn() end
   setpriv('onstartup', nil)

  Code found in my main autorun script.
onOpenProcess = require('startup').onOpenProcess

Note: Not sure why noblanks.exe is in this project's folder, but here's the source for that, too: (The executable was compiled using pypy translators)


import os, sys

def entry_point(argv):
   #stdin =, os.O_RDONLY, 0777)
   stdin = 0
   app_running = True
   while app_running:
      # Readline implementation.
      line = ""
      count = 0
      while True:
         c =, 1)
         if len(c) == 0:
            app_running = False
         elif c == '\n':
         elif c == "\r":
         elif c != ' ' and c != '\t':
            count += 1
         line += c
      # Check count. If 0, skip the line.
      if count == 0:
      print line
   return 0

def target(*args):
   return entry_point, None

if __name__ == "__main__":
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Source -> Plugin development All times are GMT - 6 Hours
Page 1 of 1

Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum

Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites