|
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
T4g1 How do I cheat? Reputation: 0
Joined: 18 Jan 2018 Posts: 1
|
Posted: Thu Jan 18, 2018 3:05 pm Post subject: Making a trainer using LUA |
|
|
Hi ! I'm having a lot of somewhat related questions regarding the toolchain to work on trainers:
I'm trying to get my hands on how to make a trainer efficiently and I really feel like using Cheat Engine Lua form is the worst to write LUA code.
Is there a way to use a proper editor (Sublime Text, Notepad++ or anything else ?). I already tried a LUA plugin that added a "external editor" feature but it was not working (lot of errors) and was only aimed at script in the CE table.
Also, is it possible to compile the trainer outside CE ? Using libraries and more traditional stuff ? Like a library that I just need to setup in a C or C++ project or even Python maybe ? I feel like CE tends to silently fails a lot of things which make it hard to debug some basic behaviors :/
Is there a complete documentation out here except the wiki ? (wiki cheatengine org (I can't post URL for wathever reason)) The only function I looked up there where missing informations like return value and/or error codes and examples.
Also, I'm trying to setup a basic AOBScan, apparently there is two ways of doing this, using AOBScan in Lua or aobscan into autoAssemble. In the various use case i've tried, I didn't manage to get anything else than nil return value out of AOBScan without having used it in the CE table first. Is there some requierements to met before using this function ?
This is how i'm doing it right now:
Code: |
function onClose(sender)
updateTimer.destroy()
return caFree -- Gives access violation on re-start but caHide does not close the trainer for real. What is wrong ? This is undocumented
end
function toggleMemoryRecord(record, checkbox, maxValue)
record.value = maxValue
if (checkbox_getState(checkbox) == 1) then
memoryrecord_freeze(record)
else
memoryrecord_unfreeze(record)
end
end
function toggleMoney(sender)
local record = getAddressList().getMemoryRecordByDescription("Money") -- Is it possible to get rid of this using [_ressources+offset] directly in LUA ?
toggleMemoryRecord(record, sender, 2000)
end
function toggleWood(sender)
local record = getAddressList().getMemoryRecordByDescription("Wood")
toggleMemoryRecord(record, sender, 50)
end
function toggleStone(sender)
local record = getAddressList().getMemoryRecordByDescription("Stone")
toggleMemoryRecord(record, sender, 50)
end
function toggleIron(sender)
local record = getAddressList().getMemoryRecordByDescription("Iron")
toggleMemoryRecord(record, sender, 50)
end
function toggleOil(sender)
local record = getAddressList().getMemoryRecordByDescription("Oil")
toggleMemoryRecord(record, sender, 50)
end
function onGameLoaded()
local results = AOBScan("29 83") -- Dummy value used for testing purpose
if results ~= nil then
count = stringlist_getCount(results)
print(count)
end
object_destroy(results)
results = nil
end
--[[ Here is the code that works in CE table:
[ENABLE]
aobscan(get_ressources,8B B8 E8 00 00 00 E8 ?? ?? ?? ?? 8B 80 F0 00 00 00 8D 04 80 8B D8 C1 E3 03 E8 ?? ?? ?? ??)
alloc(newmem,$1000,7FE894E5A7F) -- How not to hardcode this value ? Why $1000 and not 1000 ?
label(code)
label(return)
globalalloc(_ressources, 4)
newmem: -- Useless ? or code useless ?
code:
mov [_ressources],rax
mov edi,[rax+000000E8]
jmp return
get_ressources:
jmp newmem
nop
return:
registersymbol(get_ressources)
[DISABLE]
get_ressources:
db 8B B8 E8 00 00 00
unregistersymbol(get_ressources)
dealloc(newmem)
]]
function update()
if getOpenedProcessID() == 0 then -- BUG: This always returns a process ID even after the game has been closed
trainer.executable_status.state = 0
else
trainer.executable_status.state = 1
end
end
form_show(trainer)
getAutoAttachList().add("TheyAreBillions.exe")
updateTimer = createTimer(getMainForm())
updateTimer.Interval = 1000
updateTimer.OnTimer = update
createHotkey(onGameLoaded, VK_F2) -- VK_F1 popup an annoying help window, how to deactivate ?
|
Let me know if this is not the right place or way of asking all those questions so I can rectify that.
Thanks in advance and happy hacking ![/code]
|
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 138
Joined: 06 Jul 2014 Posts: 4275
|
Posted: Thu Jan 18, 2018 3:59 pm Post subject: |
|
|
For documentation, look at celua.txt (main.lua prior to 6.7) in the main CE directory.
Feel free to write code in other text editors, but everything has to go into the main Lua script in the end.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
TheyCallMeTim13 Wiki Contributor Reputation: 50
Joined: 24 Feb 2017 Posts: 976 Location: Pluto
|
Posted: Thu Jan 18, 2018 4:36 pm Post subject: |
|
|
If you want to write the Lua in external files, add them as table files, then import/require. Then this is a function I use for that.
Note that it allows to have files in a "luaFiles" folder in the same folder as the table to override table files for working on debugging or new versions. And that when dealing with table files it always loads the file, so any variables get reinitialized, unlike Lua's require, but if you just load the files only when the table loads it works out fine.
Code: |
--------
-------- CE Table Require
--------
local TableLuaFilesDirectory = 'luaFiles'
function CETrequire(moduleStr)
if moduleStr ~= nil then
local localTableLuaFilePath = moduleStr
if TableLuaFilesDirectory ~= nil or TableLuaFilesDirectory ~= '' then
local sep = package.config:sub(1,1)
localTableLuaFilePath = TableLuaFilesDirectory .. sep .. moduleStr
end
local f, err = io.open(localTableLuaFilePath .. '.lua')
if f and not err then
f:close()
return require(localTableLuaFilePath)
else
local tableFile = findTableFile(moduleStr .. '.lua')
if tableFile == nil then
return nil
end
local stream = tableFile.getData()
local fileStr = nil
local bytes = stream.read(stream.Size)
for i = 1, #bytes do
if fileStr == nil then
fileStr = ''
end
fileStr = fileStr .. string.char(bytes[i])
end
if fileStr then
return assert(loadstring(fileStr))()
end
end
end
return nil
end
-- CETrequire('SomeLuaModuleStoredAsTableFile')
-- SomeLuaModuleStoredAsTableFile.printTest() -- Just an example.
---- "SomeLuaModuleStoredAsTableFile.lua" needs to be a table file or in "{Cheat table Folder or Working folder}/luaFiles". |
_________________
|
|
Back to top |
|
|
FreeER Grandmaster Cheater Supreme Reputation: 53
Joined: 09 Aug 2013 Posts: 1091
|
Posted: Thu Jan 18, 2018 7:03 pm Post subject: |
|
|
To be as complete as I can
editing: Lua code is just text so you can use any editor you like and copy paste back and forth, but in the end it needs to be loaded through the main table's lua script (because that's what CE runs when it launches a trainer) and there's nothing I know of with any knowledge of the functions CE provides etc. to provide extra info when editing (like VS's intellisense).
Multiple files: While I haven't tried out Tim's method I'm sure they've tested it and it'll work great, I personally just don't tend to make trainers let alone ones large enough to make the extra effort of multiple files worth it
Outside CE: you can certainly pick any language you like and learn how to do GUIs in them, then use ReadProcessMemory and WriteProcessMemory or create a dll that you inject into the target process, etc. There may even be some libraries available to help you, I know I've seen C# code use a BlackMagic library that someone coded and I'm sure there are others. You will not have access to anything Cheat Engine provides if you do this, CE is not a library (someone did start one but it has since been abandoned). You won't be able to use auto assemble scripts etc. unless you can implement it yourself (potentially using libraries like keystone/capstone).
Outside CE cont: Since CE is built using Lazarus 1.6.4 (DB said he has some custom patches as well but not sure what they are for and I've built it without any)- I suppose it may be possible to create the form itself in Lazarus and export it to some format CE can open but I've never looked into anything like that. Most likely you'd still have to deal with everything via lua saved in CE however so...probably not a huge help even if you could. If you happen to know Pascal then you could fork the CE source code on github and work on improving trainer creation, even if it's never merged into CE proper you'd still have your own version that suits your needs and desires.
Documentation: just the source https://www.github.com/cheat-engine/cheat-engine
while celua.txt / main.lua (pre CE6.7) exist in the install directories but they aren't entirely complete (mostly but not entirely).
AOBScan: it should work without needing it to be run in an AA script first, as far as I know they use the same logic by default as far as where they search etc. (though lua does have a way to specify to search only executable memory etc.)
Script:
AA:
"How not to hardcode this value ? Why $1000 and not 1000 ? " $1000 is hex rather like 0x1000 (pascal syntax). As for hardcoding it, you can pass get_resources instead, I honestly have no idea why it doesn't do that by default...
"newmem: -- Useless ? or code useless ? " - refers to the address of the newly allocated memory, "code" is technically useless in that you can remove it and the label but it's often nice to have at least one label to jump to after a compare.
---
Lua:
"Is it possible to get rid of this {getting memory record} using [_ressources+offset] directly in LUA ?" - lua has functions to read and write memory so as long as you have a way to get the address (static/pointer/symbol) sure, you could do it purely in lua.
Code: | if (checkbox_getState(checkbox) == 1) then
memoryrecord_freeze(record)
else
memoryrecord_unfreeze(record)
end |
can be simplified to record.Active = checkbox.Checked in CE 6.7, at least CE 6.7, I'm not actually sure when userdata objects got the properties and methods added to them.
depending on how the checkboxes are named in CE you can also do something like this
Code: | local getmr = getAddressList().getMemoryByDescription
local info = {}
info.add = function(name, amount)
info[name] = {}
info[name].mr = getmr(name)
info[name].amount = amount
end
info.add('Money', 2000)
info.add('Wood')
info.add('Stone')
info.add('Iron')
info.add('Oil')
-- set every checkbox to use this handler
function checkboxHandler(sender)
local name = sender.name
local rec = info[name].mr
toggleMemoryRecord(rec, sender, info[name].amount or 50)
-- optionally use metatables to move the default of 50 out of this code
-- and into the data, ie. the "info" table, or just repeat 50 in each add call
end |
it may need to be modified a bit to handle how you have things named, eg local name = sender.name:match('CECheckBox-(%S+)'), but it's possible to have nicer code
I'll have to do a bit of testing with the process and F1...
edit: F1 appears to be hardcoded https://github.com/cheat-engine/cheat-engine/blob/bf6b67a036e7a837f15cb871b0e6473399799e2a/Cheat%20Engine/MainUnit.pas#L4876-L4884 (if it was just the menu shortcut that could have been disabled with lua)
Hm, it's using HtmlHelpA and that's the only place that uses it however so if you could patch that in some manner to return you'd be good to go.... writeBytesLocal('HtmlHelpA', 0xC3) seems to work
edit: of course x86 needs to pop some args so it'd be better to use Code: | if cheatEngineIs64Bit() then writeBytesLocal('HtmlHelpA', 0xC3) else writeBytesLocal('HtmlHelpA', 0xC2, 0x10, 0x00) end |
edit: process issue workaround:
Code: | function processStillAttached()
return getProcesslist()[getOpenedProcessID()] ~= nil
end |
|
|
Back to top |
|
|
|
|
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
|
|