values = {} local counter = 0 function AddMyValue(v) counter = counter + 1 --print(tostring(counter)..': AddMyValue('..tostring(v)..')') values[v] = values[v] or 0 values[v] = values[v] + 1 return counter end function TimeIt(v) values['time'] = v print('TimeIt('..tostring(v)..')') end --[[ The purpose of this script is to make it easier to call lua code in the Cheat Engine process from the game. The following commands will be made available: CELUA_SYNC(functionName, arg1, arg2, ...) CELUA_ASYNC(functionName, arg1, arg2, ...) Reference: https://forum.cheatengine.org/viewtopic.php?t=605733 The process is currently multi-step, you have to use the following calls: loadlibrary(luaclient-i386.dll) luacall(openLuaServer('CELUASERVER')) First you have to push the server name and call CELUA_Initialize, and make sure that it is only called once, this is how I do it in a separate script: alloc(newmem,$1000) label(server_name) label(__celua_initialized__) newmem: push server_name call CELUA_Initialize mov [__celua_initialized__],eax ret ALIGN 16 server_name: db 'CELUASERVER',0 ALIGN 16 __celua_initialized__: dd 0 registersymbol(__celua_initialized__) CREATETHREAD(newmem) Then for each function you have to find a reference to it, then you have to have code to call the function. If doing it in separate scripts, it would be like this to find the function: // make sure lua client is initialized assert(__celua_initialized__,FFFFFFFF) alloc(newmem,$1000) label(__celua_AddMyValue__) label(name) newmem: // we haven't found the function pointer yet, try to find it push name call CELUA_GetFunctionReferenceFromName mov [__celua_AddMyValue__],eax // save it ret notfound: inc dword ptr [globals+3010] ret name: db 'AddMyValue',0 ALIGN 16 __celua_AddMyValue__: dd 0 registersymbol(__celua_AddMyValue__) CREATETHREAD(newmem) Now to call the function you would do something like this if you wanted to store the parameters on the stack: // push parameters, we will have to pop later, just using stack as place for them push #9999 mov ecx,esp // save because this is our parameter list push 1 // 0=sync, 1=async - use async if you do not wish to update the gui (faster) push ecx // parameter list pointer (it's on stack, pointer saved in ecx) push 1 // number of parameters push [__celua_AddMyValue__] // id of function call CELUA_ExecuteFunctionByReference mov [__celua_AddMyValue__+4],eax add esp,4 // get rid of the #9999 we have saved on the stack pop ecx These AA commands handle that for you. 1) Loads the luaclient-i386.dll into the process 2) If symbol __celua_initialized__ exists a) if it points to valid memory and [__celua_initialized__+4] is magic number 1234567f, and __celua_initialized__+8 is the server name: db 'CELUASERVER',0 it is fine and we leave it alone, continue to 4) b) if not, we delete the symbol and continue to 3) 3) Allocate global memory for __celua_initialized__ and do: dd 0 1234567f 4) Like we did for __celua_initialized__, allocate (or ensure it is already allocated) memory for __celua_func_FUNCTIONNAME__. This will initialize to 0 and the magic number 1234567f, then the FUNCTIONNAME as a string. 5) Generate and return an AA script to ensure lua is initialized and that the function reference has been found, then call the function. This code will save flags and all registers except eax for the return value. SAMPLE SCRIPT: CELUA_async(MyFunction, esi, [ebp+08], [ebp+0c], [ebp+04]) pushad pushfd mov eax,ffffffff // default return value if we don't do anything cmp [__celua_initialized__],ffffffff jne skip cmp [__celua_FUNC_MyFunction__],0 je skip push esi // arg1 push [ebp+08] // arg2 push [ebp+0c] // arg3 push [ebp+04] // arg4 mov ecx,esp // address of arg4 push 1 // async push ecx // pointer to parameters push 4 // number of parameters push [__celua_FUNC_MyFunction__] // function reference call CELUA_ExecuteFunctionByReference add esp,10 // remove 4 arguments skip: mov [esp+20],eax // replace eax that will be popped with popad popfd popad --]] function MyFunction(...) print('MyFunction() called!') local args = {...} for i,a in ipairs(args) do print(' arg '..tostring(i)..': '..tostring(a)) end end --CELUAOverrides = CELUAOverrides or {} CELUAOverrides = {} local me = CELUAOverrides -- split() helper function function me.split(s, pattern, maxsplit) local pattern = pattern or ' ' local maxsplit = maxsplit or -1 local s = s local t = {} local patsz = #pattern while maxsplit ~= 0 do local curpos = 1 local found = string.find(s, pattern) if found ~= nil then table.insert(t, string.sub(s, curpos, found - 1)) curpos = found + patsz s = string.sub(s, curpos) else table.insert(t, string.sub(s, curpos)) break end maxsplit = maxsplit - 1 if maxsplit == 0 then table.insert(t, string.sub(s, curpos - patsz - 1)) end end return t end -- trim() helper function function me.trim(s) return s:gsub("^%s+", ""):gsub("%s+$", "") end local aaInitialize_i386 = [[ loadlibrary(luaclient-i386.dll) globalalloc(__celua_initialized__,$1000) __celua_initialized__: dd 0 // result will go here, should be FFFFFFFF on success dd 1234567f // magic number db 'CELUASERVER',0 // server name __celua_initialized__+100: // code push __celua_initialized__+8 // server name call CELUA_Initialize mov [__celua_initialized__],eax // save result ret CREATETHREAD(__celua_initialized__+100) ]] local aaInitialize_x86_64 = [[ loadlibrary(luaclient-x86_64.dll) globalalloc(__celua_initialized__,$1000) __celua_initialized__: dd 0 // result will go here, should be FFFFFFFF on success dd 1234567f // magic number db 'CELUASERVER',0 // server name __celua_initialized__+100: // code push __celua_initialized__+8 // server name call CELUA_Initialize mov [__celua_initialized__],eax // save result ret CREATETHREAD(__celua_initialized__+100) ]] local aaFunction_i386 = [[ globalalloc(__celua_FUNCTION_MYFUNCTIONNAME__,$1000) __celua_FUNCTION_MYFUNCTIONNAME__: dd 0 // result will go here, should be non-zero on success dd 1234567f // magic number db 'MYFUNCTIONNAME',0 // function name __celua_FUNCTION_MYFUNCTIONNAME__+100: // code mov eax, [__celua_FUNCTION_MYFUNCTIONNAME__] cmp eax,0 jne @F cmp [__celua_initialized__],ffffffff jne @F push __celua_FUNCTION_MYFUNCTIONNAME__+8 // function name call CELUA_GetFunctionReferenceFromName mov [__celua_FUNCTION_MYFUNCTIONNAME__],eax // save result @@: ret ]] local aaFunction_x86_64 = [[ globalalloc(__celua_FUNCTION_MYFUNCTIONNAME__,$1000) __celua_FUNCTION_MYFUNCTIONNAME__: dd 0 // result will go here, should be non-zero on success dd 1234567f // magic number db 'MYFUNCTIONNAME',0 // function name __celua_FUNCTION_MYFUNCTIONNAME__+100: // code mov eax, [__celua_FUNCTION_MYFUNCTIONNAME__] cmp eax,0 jne @F cmp [__celua_initialized__],ffffffff jne @F push __celua_FUNCTION_MYFUNCTIONNAME__+8 // function name call CELUA_GetFunctionReferenceFromName mov [__celua_FUNCTION_MYFUNCTIONNAME__],eax // save result @@: ret ]] -- Ensure that __celua_initialized__ is setup correctly -- returns true if it's ok, or false,errorMessage if not function me.EnsureInitialized() local symbolname = '__celua_initialized__' -- see if the symbol is already defined local addr = getAddressSafe(symbolname) if addr ~= nil then -- we found it, ensure that it contains correct data local magicNumber = readInteger(symbolname..'+4') if magicNumber == 0x1234567f then return true end -- already created unregisterSymbol(symbolname) -- invalid data at that address, unregister end -- assemble appropriate script if targetIs64Bit() then if not autoAssemble(aaInitialize_x86_64) then return false, 'Error assembling initialize script' end else if not autoAssemble(aaInitialize_i386) then return false, 'Error assembling initialize script' end end return true end function me.GetFunctionSymbol(functionName) if type(functionName) ~= 'string' then return nil end if string.len(functionName) <= 0 then return nil end return '__celua_FUNCTION_'..functionName..'__' end function me.EnsureFunction(functionName) local symbolname = me.GetFunctionSymbol(functionName) if symbolname == nil then return false, 'Unable to create symbol for function name "'..tostring(functionName)..'"' end local addr = getAddressSafe(symbolname) if addr ~= nil then -- we found it, ensure that it contains correct data local magicNumber = readInteger(symbolname..'+4') if magicNumber == 0x1234567f then return true end -- already created unregisterSymbol(symbolname) -- invalid data at that address, unregister end local aa if targetIs64Bit() then aa = string.gsub(aaFunction_x86_64, 'MYFUNCTIONNAME', functionName) else aa = string.gsub(aaFunction_i386, 'MYFUNCTIONNAME', functionName) end if not autoAssemble(aa) then print("Error assembling lua function aa code:") print(string.gsub(aa, '\n', '\r\n')) return false, 'Error assembling function script' end return true end local labelCounter = math.random(0x70000000) + 0x10000000 local aaPerformCall_i386 = [[ label(${LABEL_NAME}) pushad pushfd ${PARAMETERS} call __celua_FUNCTION_${FUNCTION_NAME}__+100 test eax,eax jz ${LABEL_NAME} mov ecx, esp push ${ASYNC_VALUE} push ecx push ${PARAMETERS_COUNT} push eax call CELUA_ExecuteFunctionByReference ${LABEL_NAME}: add esp,${PARAMETERS_SIZE} mov [esp+20],eax popfd popad ]] function me.AACall(functionName, parameters, async) local label = 'skip_'..functionName..'_'..string.format('%2x', labelCounter) labelCounter = labelCounter + 1 local pushes ={} for i=#parameters,1,-1 do table.insert(pushes, 'push '..parameters[i]) end local asyncValueString = '0' if async then asyncValueString = '1' end if targetIs64Bit() then local result = aaPerformCall_i386 result = result:gsub('${LABEL_NAME}', label) result = result:gsub('${FUNCTION_NAME}', functionName) result = result:gsub('${PARAMETERS}', table.concat(pushes, '\n')) result = result:gsub('${PARAMETERS_SIZE}', string.format('%x', (#parameters)*4)) result = result:gsub('${PARAMETERS_COUNT}', string.format('%x', #parameters)) result = result:gsub('${ASYNC_VALUE}', asyncValueString) return result else local result = aaPerformCall_i386 result = result:gsub('${LABEL_NAME}', label) result = result:gsub('${FUNCTION_NAME}', functionName) result = result:gsub('${PARAMETERS}', table.concat(pushes, '\n')) result = result:gsub('${PARAMETERS_SIZE}', string.format('%x', (#parameters)*4)) result = result:gsub('${PARAMETERS_COUNT}', string.format('%x', #parameters)) result = result:gsub('${ASYNC_VALUE}', asyncValueString) return result end end -- sync version function me.AAOverride_SYNC(parameters, syntaxCheckOnly) print('CELUAOverrides.AAOverride("'..tostring(parameters)..'", '..tostring(syntaxCheckOnly)..')') -- open the server ourselves openLuaServer('CELUASERVER') local result, msg = me.EnsureInitialized() if not result then return nil, msg end local vals = me.split(parameters, ',') local functionName = me.trim(vals[1]) result, msg = me.EnsureFunction(functionName) if not result then return nil, msg end -- now we have to create the actual script local actualParameters = {} for i = 2,#vals do local value = me.trim(vals[i]) table.insert(actualParameters, value) end local final = me.AACall(functionName, actualParameters, false) --print(final) --showMessage(final) return final end -- async version function me.AAOverride_ASYNC(parameters, syntaxCheckOnly) print('CELUAOverrides.AAOverride("'..tostring(parameters)..'", '..tostring(syntaxCheckOnly)..')') -- open the server ourselves openLuaServer('CELUASERVER') local result, msg = me.EnsureInitialized() if not result then return nil, msg end local vals = me.split(parameters, ',') local functionName = me.trim(vals[1]) result, msg = me.EnsureFunction(functionName) if not result then return nil, msg end -- now we have to create the actual script local actualParameters = {} for i = 2,#vals do local value = me.trim(vals[i]) table.insert(actualParameters, value) end local final = me.AACall(functionName, actualParameters, true) --print(final) --showMessage(final) return final end -- register the actual commands registerAutoAssemblerCommand('CELUA_SYNC', CELUAOverrides.AAOverride_SYNC) registerAutoAssemblerCommand('CELUA_ASYNC', CELUAOverrides.AAOverride_ASYNC) --[[ return CELUAOverrides.EnsureInitialized() return CELUAOverrides.AAOverride('MyFunction, eax, esi, [ebp+08]') --]]