Перейти к содержанию

Туторы по CE Lua Engine 6.0


MasterGH

Рекомендуемые сообщения

1) туторы LUA с форума CE - здесь;

2) последние изменения/добавления в LUA можно увидеть здесь;

3) константы здесь;

4) как работают LUA функции в коде - здесь;

О LUA в CE 6.0

LUA движок используется в Cheat Engine 6.0, а также и в множестве игр. Вспомните что у большинства компьютерных игр под Windows есть вызов игровой консоли так вот я точно не знаю, но вроде в большинстве случаев для этой консоли используется LUA движки. Например, в игре СТАЛКЕР точно есть LUA. Я всё пишу к тому, что LUA может вам пригодиться для модинга, спавна и т.п. Но сейчас речь не об этом.

О достоинствах LUA я писал в нескольких темах на форуме, одна из них.

Привожу справочный материал из справки CE 6.0

Cheat engine also has a script engine in which you can execute simple lua scripts

Моя переработанная справка по LUA-поддрежке в CE 6.0

List of CE specific functions:

readBytes(address,bytecount) : Reads the bytes at the given address and returns it

writeBytes(address, x,x,x,x) : Write the given bytes to the given address

readBytesLocal(address,bytecount) : See readBytes but then it's for Cheat engine's memory

writeBytesLocal(address, x,x,x,x) : See writeBytes but then it's for Cheat Engine's memory

readInteger(address) : Reads a integer from the specified address

readFloat(address) : Reads a single precision floating point value from the specified address

readDouble(address) : Reads a double precision floating point value from the specified address

readString(address, maxlength) : maxlength is just so you won't freeze for too long, set to 6000 if you don't care too much

writeInteger(address,value) : Writes an integer to the specified address. Returns true on success

writeFloat(address,value) : Writes a single precision floating point to the specified address. Returns true on success

writeDouble(address,value) : Writes a double precision floating point to the specified address. Returns true on success

writeString(address,string) : Write a string to the specified address. Returns true on success

generateAPIHookScript(address, addresstojumpto, addresstogetnewcalladdress OPT) : Generates an auto assembler script which will hook the given address when executed

autoAssemble(text) : runs the auto assembler with the given text. Returns true on success

showMessage(text) : shows a messagebox with the given text

sleep(milliseconds): pauses for the number of specified milliseconds (1000= 1 sec...)

getProcessIDFromProcessName(name) : returns a processid

openProcess(processid) : causes cheat engine to open the given processid

openProcess(processname): causes cheat engine to find and open the given process

pause : pauses the current opened process

unpause: resumes the current opened process

getPixel(x,y) : returns the rgb value of the pixel at the specific screen coordinate

getMousePos: returns the x,y coordinates of the mouse

setMousePos(x,y): sets the mouse position

isKeyPressed(key) : returns true if the specified key is currently pressed

keyDown(key) : causes the key to go into down state

keyUp(key) :causes the key to go up

doKeyPress(key) : simmulates a key press

messageDialog(text, type, buttons...) : pops up a message with a specific icon/sound with the specified buttons (mbok, mbyes, ....)

speedhack_setSpeed(speed)

injectDll(filename): Injects a dll, and returns true on success

Cheat table functions:

createTableEntry: creates an generic cheat table entry and add it to the list. Returns a tableentry pointer you can use with memrec routines

getTableEntry(descriptionname): returns a tableEntry pointer for use with memrec functions

memrec_setDescription(te, description): sets the specified description for this entry

memrec_getDescription(te): gets the current description of this entry

memrec_getAddress(te): returns the address and optional offsets for a pointer (note that in 64-bit kernelmode addresses will be rounded down...)

memrec_setAddress(te,address,offsets OPTIONAL) : Sets the address of a entry. You can give as many offsets as you need

memrec_getType(te) : returns the Variable type. (vtByte to vtCustom)

memrec_setType(te, vartype) : sets the type of the entry

memrec_getValue(te): returns the current value of the cheat table entry as a string

memrec_setValue(te, value): sets the value of a cheat table entry

memrec_getScript(te) : If the entry is of type vtAutoAssembler then you can get the script with this routine

memrec_setScript(te, script)

memrec_isFrozen(te)

memrec_freeze(te, updownfreeze OPTIONAL): sets the entry to frozen state. updownfreeze is optional. 0=freeze, 1=allow increase, 2=allow decrease

memrec_unfreeze(te) :unfreezes an entry

memrec_setColor(te, colorrgb): Sets the color of the entry

memrec_appendToEntry(te,te) : Adds the entry to another entry

memrec_delete(te) : It's unknown what this function does, all that is known is that after using this command other memrec routines with this table entry value don't work anymore...

Table related routines:

If a cheat entry is about to get enabled or disabled it will check if a lua function named "_memrec_description_activating" or "_memrec_description_deactivating" is available, and if so call it.

If a cheat entry is enabled or disabled it will check if a lua function named "_memrec_description_activated" or "_memrec_description_deactivated" is available, and if so call it.

It passes the tableEntry pointer as parameter

Example:

If the cheat entry table with description "xxx" gets enabled it will call "_memrec_xxx_activating(te)" before it is activated and "_memrec_xxx_activated(te)" after it has been activated (check with isFrozen to see if it actually did get activated in case of errors in a script or unreadable memory)

If the cheat entry table with description "xxx" gets disabled it will call "_memrec_xxx_deactivating(te)" before it is activated and "_memrec_xxx_deactivated(te)" after it has been deactivated

-debugging--

debug variables

EAX, EBX, ECX, EDX, EDI, ESP, EBP, ESP, EIP

RAX, EBX, RBX, RDX, RDI, RSP, RBP, RSP, RIP, R8, R9, R10, R11, R12, R13, R14, R15 : The value of the register

Debug related routines:

When a breaking breakpoint hits and the lua function debugger_onBreakpoint() is defined it will be called and the global variables EAX, EBX, .... will be filled in

Return 0 if you want the userinterface toi be updated and enything else if not (e.g: You continued from the breakpoint in your script)

createProcess(path, parameters OPTIONAL, debug OPTIONAL, breakonentrypoint OPTIONAL) : Creates a process. If debug is true it will be created using the windows debugger and if breakonentry is true it will cause a breakpoint to occur on entrypoint

debugProcess(interface OPT): starts the debugger for the currently opened process (won't ask the user) Optional interface: 0=default, 1=windows debug, 2=VEHDebug, 3=Kerneldebug

debug_setBreakpoint(address, size OPTIONAL, trigger OPTIONAL) : sets a breakpoint of a specific size at the given address. if trigger is bptExecute then size is ignored. If trigger is ignored then it will be of type bptExecute, which obviously also ignores the size then as well

debug_removeBreakpoint(address) : if the given address is a part of a breakpoint it will be removed

debug_continueFromBreakpoint(continueMethod) : if the debugger is currently waiting to continue you can continue with this. Valid parameters are :co_run (just continue), co_stepinto(when on top of a call, follow it), c_stepover (when on top of a call run till after the call)

Changing registers:

This annoying method has been chosen because LUA only supports encoding up to 52-bits, after which rounding will happen

So automatically setting the new value would surely cause unpredictable behaviour if the target app uses higher values

hasChangedARegister : Set this to true before continuing and the changedREG variables will be checked to see if the new value should be set (just an optimization so not every variable has to be checked each time even if you didn't change a thing)

changedEAX, changedRAX, changedEBX, changedRBX, changed.....

--gui--

closeCE() : just closes ce

hideAllCEWindows() : makes all normal ce windows invisible (e.g trainer table)

unhideMainCEwindow() : shows the main cheat engine window

--class-objects-

The following objects are descendent from the object named "control", therefore the returned pointer when they are created can also be used for functions that start with control_

createForm(visible OPT): creates a form (window) and returns the pointer for it. Visible is default true but can be changed

form_centerScreen(form);

form_onClose(form, function)

form_hide(form)

form_show(form)

The following create routines take an owner as parameter. Owner can be a Form, Panel or Groupbox

The x,y position will be based on the client array of the owner

createPanel(owner)

createGroupBox(owner)

createButton(owner)

createImage(owner)

image_loadImageFromFile(filename)

image_stretch(boolean)

image_transparent(boolean)

createLabel(owner)

createEdit(owner)

createMemo(owner)

control_setCaption(control, caption) : sets the text on a control. All the gui objects fall in this category

control_getCaption(control)

control_setPosition(control, x,y): sets the x and y position of the object base don the top left position (relative to the client array of the owner object)

control_getPosition(contron): returns the x and y position of the object (relative to the client array of the owner object)

control_setSize(control, width,height) :

control_getSize(control)

control_align(control, alignmentoption):

control_onclick(control, function) :

createTimer(owner)

timer_setInterval(timer, interval)

timer_onInterval(timer, function)

getAutoAttachList(): returns the AutoAttach StringList object. It can be controlled with the stringlist_ routines (it's not recommended to destroy this list object)

stringlist_add(list, string);

stringlist_remove(list, string);

object_destroy(object) : Destroys the object (basically everything inherits from this class)

1. Функции для работы с графическим интерфейсом пользователя (GUI):

1.1 Работа с GUI Cheat Engine

closeCE() : just closes ce

hideAllCEWindows() : makes all normal ce windows invisible (e.g trainer table)

unhideMainCEwindow() : shows the main cheat engine window

getAutoAttachList(): returns the AutoAttach StringList object. It can be controlled with the stringlist_ routines (it's not recommended to destroy this list object)

stringlist_add(list, string);

stringlist_remove(list, string);

Функции для работы с таблицей CE:

createTableEntry: creates an generic cheat table entry and add it to the list. Returns a tableentry pointer you can use with memrec routines

getTableEntry(descriptionname): returns a tableEntry pointer for use with memrec functions

memrec_setDescription(te, description): sets the specified description for this entry

memrec_getDescription(te): gets the current description of this entry

memrec_getAddress(te): returns the address and optional offsets for a pointer (note that in 64-bit kernelmode addresses will be rounded down...)

memrec_setAddress(te,address,offsets OPTIONAL) : Sets the address of a entry. You can give as many offsets as you need

memrec_getType(te) : returns the Variable type. (vtByte to vtCustom)

memrec_setType(te, vartype) : sets the type of the entry

memrec_getValue(te): returns the current value of the cheat table entry as a string

memrec_setValue(te, value): sets the value of a cheat table entry

memrec_getScript(te) : If the entry is of type vtAutoAssembler then you can get the script with this routine

memrec_setScript(te, script)

memrec_isFrozen(te)

memrec_freeze(te, updownfreeze OPTIONAL): sets the entry to frozen state. updownfreeze is optional. 0=freeze, 1=allow increase, 2=allow decrease

memrec_unfreeze(te) :unfreezes an entry

memrec_setColor(te, colorrgb): Sets the color of the entry

memrec_appendToEntry(te,te) : Adds the entry to another entry

memrec_delete(te) : It's unknown what this function does, all that is known is that after using this command other memrec routines with this table entry value don't work anymore...

Table related routines:

If a cheat entry is about to get enabled or disabled it will check if a lua function named "_memrec_description_activating" or "_memrec_description_deactivating" is available, and if so call it.

If a cheat entry is enabled or disabled it will check if a lua function named "_memrec_description_activated" or "_memrec_description_deactivated" is available, and if so call it.

It passes the tableEntry pointer as parameter

Example:

If the cheat entry table with description "xxx" gets enabled it will call "_memrec_xxx_activating(te)" before it is activated and "_memrec_xxx_activated(te)" after it has been activated (check with isFrozen to see if it actually did get activated in case of errors in a script or unreadable memory)

If the cheat entry table with description "xxx" gets disabled it will call "_memrec_xxx_deactivating(te)" before it is activated and "_memrec_xxx_deactivated(te)" after it has been deactivated

1.2 GUI создаваемых элементов

--class-objects-

The following objects are descendent from the object named "control", therefore the returned pointer when they are created can also be used for functions that start with control_

Создаваяемая форма:

createForm(visible OPT): creates a form (window) and returns the pointer for it. Visible is default true but can be changed

form_centerScreen(form);

form_hide(form)

form_show(form)

Другие GUI элементы:

The following create routines take an owner as parameter. Owner can be a Form, Panel or Groupbox

The x,y position will be based on the client array of the owner

createPanel(owner)

createGroupBox(owner)

createButton(owner)

createImage(owner)

image_loadImageFromFile(filename)

image_stretch(boolean)

image_transparent(boolean)

createLabel(owner)

createEdit(owner)

createMemo(owner)

Установка свойств GUI-элементам:

control_setCaption(control, caption) : sets the text on a control. All the gui objects fall in this category

control_getCaption(control)

control_setPosition(control, x,y): sets the x and y position of the object base don the top left position (relative to the client array of the owner object)

control_getPosition(contron): returns the x and y position of the object (relative to the client array of the owner object)

control_setSize(control, width,height) :

control_getSize(control)

control_align(control, alignmentoption):

Назначение обработчиков GUI:

form_onClose(form, function)

control_onclick(control, function) :

Разрушение GUI- элементов:

object_destroy(object) : Destroys the object (basically everything inherits from this class)

Показать сообщении с информацией:

showMessage(text) : shows a messagebox with the given text

messageDialog(text, type, buttons...) : pops up a message with a specific icon/sound with the specified buttons (mbok, mbyes, ....)

2. Функции связанные с процессом (игры)

2.1 Фунции управления процессом:

getProcessIDFromProcessName(name) : returns a processid

openProcess(processid) : causes cheat engine to open the given processid

openProcess(processname): causes cheat engine to find and open the given process

pause : pauses the current opened process

unpause: resumes the current opened process

sleep(milliseconds): pauses for the number of specified milliseconds (1000= 1 sec...)

speedhack_setSpeed(speed)

2.2 Функции для работы с памятью процесса (к сожалению нет readWord/writeWord):

readBytes(address,bytecount) : Reads the bytes at the given address and returns it

writeBytes(address, x,x,x,x) : Write the given bytes to the given address

readBytesLocal(address,bytecount) : See readBytes but then it's for Cheat engine's memory

writeBytesLocal(address, x,x,x,x) : See writeBytes but then it's for Cheat Engine's memory

readInteger(address) : Reads a integer from the specified address

readFloat(address) : Reads a single precision floating point value from the specified address

readDouble(address) : Reads a double precision floating point value from the specified address

readString(address, maxlength) : maxlength is just so you won't freeze for too long, set to 6000 if you don't care too much

writeInteger(address,value) : Writes an integer to the specified address. Returns true on success

writeFloat(address,value) : Writes a single precision floating point to the specified address. Returns true on success

writeDouble(address,value) : Writes a double precision floating point to the specified address. Returns true on success

writeString(address,string) : Write a string to the specified address. Returns true on success

generateAPIHookScript(address, addresstojumpto, addresstogetnewcalladdress OPT) : Generates an auto assembler script which will hook the given address when executed

autoAssemble(text) : runs the auto assembler with the given text. Returns true on success

injectDll(filename): Injects a dll, and returns true on success

2.3 Функции для работы в отладке

-debugging--

debug variables

EAX, EBX, ECX, EDX, EDI, ESP, EBP, ESP, EIP

RAX, EBX, RBX, RDX, RDI, RSP, RBP, RSP, RIP, R8, R9, R10, R11, R12, R13, R14, R15 : The value of the register

Debug related routines:

When a breaking breakpoint hits and the lua function debugger_onBreakpoint() is defined it will be called and the global variables EAX, EBX, .... will be filled in

Return 0 if you want the userinterface toi be updated and enything else if not (e.g: You continued from the breakpoint in your script)

createProcess(path, parameters OPTIONAL, debug OPTIONAL, breakonentrypoint OPTIONAL) : Creates a process. If debug is true it will be created using the windows debugger and if breakonentry is true it will cause a breakpoint to occur on entrypoint

debugProcess(interface OPT): starts the debugger for the currently opened process (won't ask the user) Optional interface: 0=default, 1=windows debug, 2=VEHDebug, 3=Kerneldebug

debug_setBreakpoint(address, size OPTIONAL, trigger OPTIONAL) : sets a breakpoint of a specific size at the given address. if trigger is bptExecute then size is ignored. If trigger is ignored then it will be of type bptExecute, which obviously also ignores the size then as well

debug_removeBreakpoint(address) : if the given address is a part of a breakpoint it will be removed

debug_continueFromBreakpoint(continueMethod) : if the debugger is currently waiting to continue you can continue with this. Valid parameters are :co_run (just continue), co_stepinto(when on top of a call, follow it), c_stepover (when on top of a call run till after the call)

Changing registers:

This annoying method has been chosen because LUA only supports encoding up to 52-bits, after which rounding will happen

So automatically setting the new value would surely cause unpredictable behaviour if the target app uses higher values

hasChangedARegister : Set this to true before continuing and the changedREG variables will be checked to see if the new value should be set (just an optimization so not every variable has to be checked each time even if you didn't change a thing)

changedEAX, changedRAX, changedEBX, changedRBX, changed.....

3. Другие функции:

getPixel(x,y) : returns the rgb value of the pixel at the specific screen coordinate

Таймер, задание ему свойства и регистрация обработчика таймера

createTimer(owner)

timer_setInterval(timer, interval)

timer_onInterval(timer, function)

Ввод/вывод:

1)Курсор мыши:

getMousePos: returns the x,y coordinates of the mouse

setMousePos(x,y): sets the mouse position

2)Клавиатура:

isKeyPressed(key) : returns true if the specified key is currently pressed

keyDown(key) : causes the key to go into down state

keyUp(key) :causes the key to go up

doKeyPress(key) : simmulates a key press

Писать код на LUA можно в двух местах.

1) Перейдите в отладчик и нажмите ctrl+L для вызова LUA engine (или вызовите из меню Tools);

2) В главном окне CE зайдите в комментарии к таблице и вы увидите там вкладку LUA.

В последнем случае LUA скрипт будет выполняться, когда вы запускаете файл-таблицу двойным кликом.

Серия моих туторов:

Тутор 1. Пример работы таймера

Получится у нас следующее (ссори за опечатки на скрине,там должно быть слово "Меняется")

post-3-1294732518,05_thumb.png

Я покажу как создавать GUI элементы. Присваивать им название, положение. Покажу как создать таймер и его обработчик. Покажу пример конкатенации с переменной. LUA я знаю очень плохо, но если есть справочные материалы по LUA(сами поищите, я может быть позже приведу ссылки) + откройте справку CE 6.0 , то разобраться не так уж сложно если у вас есть навыки программирования на других языках.

Впишите в окно ввода следующее (в комментариях я написал подробности)

post-3-1294732523,31_thumb.png


--------------------
Пример работы таймера в CE 6.0 LUA
MasterGH 2011
---------------------
]]

-- Создать форму и отобразить её в центре рабочего стола
frmtrainer = createForm()
control_setCaption(frmtrainer,"Пример таймера в действии")
form_centerScreen(frmtrainer)

-- Создать текстовое поле, установить его позицию и значение
lbtimer = createLabel(frmtrainer)
control_setPosition(lbtimer, 100,100)
control_setCaption(lbtimer,"Прошло времени 0000 мс ")

-- Создать таймер, счётчик интервалов и его обработчик

countdelay = 1

function onTimerUpdateValue(timer)
-- Задержка не может быть меньше 10 мс поэтому пришлось дописать нолик
control_setCaption(lbtimer,"Прошло времени " .. countdelay .. "0 мс ")
countdelay = countdelay + 1
end

timer1 = createTimer(frmtrainer)
-- Задержка не может быть меньше 10 мс
timer_setInterval(timer1, 10)
timer_onTimer(timer1, onTimerUpdateValue)
--[[ 

Жаль что нет установки редактирования шрифтов...

Тутор 2. Как работать в отладке с LUA.

Тутор от Дарк Байта. Я просто обязан упомянуть о нём здесь. Мы получаем данные при бряках на функции PeekMessageA в данном случае получаем естественно данные стека по указателю ESP. Также хочу обратить внимание, что мы можем получать данные регистров, данные памяти, кода... анализировать эти данные; ставить по определенным условиям брейкпоинты на адресах и снимать их; получить количество срабатываемых инструкций... Иначе говоря мы можем автоматизировать управление отладкой.

Теперь теоритически можно написать подход поиска инструкций на которые не нужно ставить или нужно ставить фильтры... можно написать трейсер выводящий только нужную информацию. Возможно даже трейсер поднимающийся вверх по коду пытающийся построить цепочку указателей, но почему в LUA нет инструкции дизассемблирования по адресу (или я не нашёл??? :blink: ) Ладно, перейдём к тутору.

Есть такая функция PeekMessage существует у любой программы.


LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
BOOL PeekMessage(          

PeekMessage распределяет входящие отправленные (асинхронные) сообщения, проверяет помещенные в очередь (синхронные) сообщения очереди сообщений потока и извлекает сообщение.... Ну вот наша задача собрать параметры этой функции перед её вызовом. Подключитесь к какому-нибудь поцессу и выполните следующий скрипт:



count=0;

function debugger_onBreakpoint()
if (EIP == pmAddress) then
count = count + 1


ret=readInteger(ESP)
lpMsg=readInteger(ESP+4)
hWnd=readInteger(ESP+8)
wMsgFilterMin=readInteger(ESP+12)
wMsgFilterMax=readInteger(ESP+16)
wRemoveMsg=readInteger(ESP+20)

print(count ..") PeekMessage (lpMsg="..lpMsg.." hWnd="..hWnd.." wMsgFilterMin="..wMsgFilterMin.." wMsgFilterMax=".. wMsgFilterMax.." wRemoveMsg="..wRemoveMsg..") return address="..ret)

if count>=1000 then
debug_removeBreakpoint("PeekMessageA");
end

return 1 --I handled it so dont tell the user
else
return 0 --unexpected breakpoint, show the the user
end
end

debug_setBreakpoint("PeekMessageA");
pmAddress=getAddress("PeekMessageA"); 

Например, результат будет следующий:

1) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=0) return address=4532553

2) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=1) return address=4532628

3) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=0) return address=4532553

4) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=1) return address=4532628

5) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=0) return address=4532553

6) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=0) return address=4532553

7) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=1) return address=4532628

8) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=0) return address=4532553

9) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=0) return address=4532553

10) PeekMessage (lpMsg=1245020 hWnd=0 wMsgFilterMin=0 wMsgFilterMax=0 wRemoveMsg=1) return address=4532628

.... и так далее...

Тутор 5. Обзор GUI - элементов и работа с GUI - элементами.

GUI (или Graphical user interface графический интерфейс пользователя) имеют все оконные приложения Windows. В него входят различные элементы: форма, даилоги, кнопки, панели, лейблы(вывод одной строкой с возможностью возврата каретки и новой строки), едиты (ввод/ввод текста одной строкой), мемо(многострочный ввод/вывод) и т.п.

Перед тем как обратиться к этим элементам стоит рассмотреть управления окнами CE.

closeCE() : just closes ce (закрыть CE)

hideAllCEWindows() : makes all normal ce windows invisible (e.g trainer table) (скрыть все окна CE)

unhideMainCEwindow() : shows the main cheat engine window (показать все окна CE)

Эти команды удобны в тех случаях, когда вы создаёте LUA трейнер чтобы CE не мешался своим видом.

Ну а теперь вернёмся к GUI элементам которые можно создавать с помощью поддержки LUA. Как и положено, классически создаётся форма, затем на ней размещаются компоненты и можно показать эту форму пользователю. Когда мы будем размещать компоненты мы должны указать куда будем их размещать - на форму или другие компоненты. Иначе говоря мы должны определить владельца компонента. Также у нас будет возможность задать параметры как формы так и компонентов.

В арсенале CE 6.0 существуют следующие функции создающие GUI элементы.

createForm(visible OPT)// где можно указать OPT= true, что означает создать и показать форму.

ccreatePanel(owner) // в остальных случаях можно указывать owner - переменную владельца

createGroupBox(owner)

createImage(owner)

createLabel(owner)

createEdit(owner)

createMemo(owner)

Все эти функции возвращают указатель на созданный объект. Например:

formMain = createForm(true)

После того как мы получаем указатель на созданный GUI-объект мы можем установить его свойства или прочитать их.

1. Установка/чтение описания

control_setCaption(control, caption) : sets the text on a control. All the gui objects fall in this category

control_getCaption(control)

2. Установка/чтение позиции

control_setPosition(control, x,y): sets the x and y position of the object base don the top left position (relative to the client array of the owner object)

control_getPosition(contron): returns the x and y position of the object (relative to the client array of the owner object)

3. Установка/чтение размера

control_setSize(control, width,height)

control_getSize(control)

4. Выравнивание

control_align(control, alignmentoption)

5. И ещё

form_centerScreen(form) // расположить форму по центру

form_hide(form) // скрыть форму

form_show(form) // показать формуs

showMessage(text) : shows a messagebox with the given text // показать сообщение

messageDialog(text, type, buttons...) // диалог с кнопками

Я думаю тут всё просто.

Вот мой пример создающий форму:


formMain = createForm(true)
control_setCaption(formMain, "Trainer for Game.exe +1")
form_centerScreen(formMain)
control_setSize(formMain, 500,300)

Как вы могли догадаться можно создать панель и в ней расположить другие созданные элементы. С созданием интерфейса разобрались. Ну что если у нас есть кнопка, она же должна что-то делать?! А если окно трейнера закроется, вдруг мы тоже захотим при этом что-то сделать. Для таких целей существуют обработчики сообщений (или обработчики событий). У нас их для GUI элементов два.

form_onClose(form, function) // закрытие формы

control_onСlick(control, function) // клик на элементе

Когда вы видите приставку "on", то вы видите "обработчик". Так условно обозначаются обработчики событий. В следующем примере, на кнопку "вешается обработчик"


formMain = createForm(true)
control_setCaption(formMain, "Trainer for Game.exe +1")
form_centerScreen(formMain)
control_setSize(formMain, 500,300)

btn_InfHealth = createButton(formMain)
control_setCaption(btn_InfHealth, "Беск. здоровье")
control_setSize(btn_InfHealth, 90, 20)
control_setPosition(btn_InfHealth, 10, 10)

function OnInfHealth()
-- какой-то код
showMessage("Я в функции OnInfHealth")
end
control_onClick(btn_InfHealth, OnInfHealth)

Забыл отменить, что комментарии в LUA обозначаются "--".

Теперь если кликнуть на кнопку, то будет вызов функции fun_OnInfHealth с сообщением об этом.

post-3-1297239308,52_thumb.png

Мы рассмотрели GUI-тему и можно было бы на этом закончить обзор. Однако, вы можете обратить внимание ещё на функции создания элементов любезно предоставлены Wiccaan-ом. Вы сможете вызвать функцию создания элемента с установленными параметрами чтобы не задавать их помногу раз ручным вводом. Так же увидите код LUA в своей красе.



Winmine.exe (Windows XP Minesweeper) Lua Trainer Example
by atom0s [Wiccaan]

This is a demonstrational Lua script showing off
what Cheat Engine 6.0 can do with Lua.

]]--

--
-- DO NOT EDIT BELOW THIS LINE!!
--

local Trainer_Example = { }

----------------------------------------------------------------------------------
-- func: InitButton( .. )
-- desc: Wraps button creation and setup functions for smaller code.
----------------------------------------------------------------------------------
function InitButton( form, caption, x, y, w, h, func )
local button = createButton( form );
if( button == nil ) then
return nil;
end

control_setCaption( button, caption );
control_setPosition( button, x, y );
control_setSize( button, w, h );
control_onClick( button, func );
return button;
end

----------------------------------------------------------------------------------
-- func: InitLabel( .. )
-- desc: Wraps label creation and setup functions for smaller code.
----------------------------------------------------------------------------------
function InitLabel( form, x, y, text )
local label = createLabel( form );
if( label == nil ) then
return nil;
end

control_setCaption( label, text );
control_setPosition( label, x, y );
return label;
end

----------------------------------------------------------------------------------
-- func: Trainer_Example.Main( .. )
-- desc: Prepares script for overall actions.
----------------------------------------------------------------------------------
function Trainer_Example.Main( )
-- Main trainer form pointer.
Trainer_Example.Form = createForm( true );
form_centerScreen( Trainer_Example.Form )

-- Create buttons.
Trainer_Example.btnFlags = InitButton( Trainer_Example.Form, "Toggle Inf. Flags", 1, 1, 150, 30, Trainer_Example.OnFlagsClicked );
Trainer_Example.btnTimer = InitButton( Trainer_Example.Form, "Toggle Unlimited Time", 155, 1, 150, 30, Trainer_Example.OnTimeClicked );

-- Create info label.
Trainer_Example.lblInfo = InitLabel( Trainer_Example.Form, 5, 175,
"This is an example Lua trainer written using Cheat Engine 6.\n" ..
"Coded by: atom0s [Wiccaan]"
);

-- Create option booleans.
Trainer_Example.bFlagsEnabled = false;
Trainer_Example.bTimerEnabled = false;

-- Set form caption.
control_setCaption( Trainer_Example.Form, "Minesweeper Lua Trainer Example" );
return true;
end

----------------------------------------------------------------------------------
-- func: Trainer_Example.OnFlagsClicked( .. )
-- desc: Toggles infinite flags when flag button is clicked.
----------------------------------------------------------------------------------
function Trainer_Example.OnFlagsClicked()
Trainer_Example.bFlagsEnabled = not Trainer_Example.bFlagsEnabled;

if( Trainer_Example.bFlagsEnabled == true ) then
autoAssemble( "winmine.exe+346E:\n" ..
"db 90 90 90 90 90 90"
);
else
autoAssemble( "winmine.exe+346E:\n" ..
"db 01 05 94 51 00 01"
);
end
end

----------------------------------------------------------------------------------
-- func: Trainer_Example.OnTimeClicked( .. )
-- desc: Toggles unlimited time when time button is clicked.
----------------------------------------------------------------------------------
function Trainer_Example.OnTimeClicked()
Trainer_Example.bTimerEnabled = not Trainer_Example.bTimerEnabled;

if( Trainer_Example.bTimerEnabled == true ) then
autoAssemble( "winmine.exe+2FF5:\n" ..
"db 90 90 90 90 90 90"
);
else
autoAssemble( "winmine.exe+2FF5:\n" ..
"db FF 05 9C 57 00 01"
);
end
end

-- Execute our script.
Trainer_Example.Main();
--[[ 

Результат:

post-3-1297242497,93_thumb.png

На этом пока всё.

Тутор 7. Отображение картинок в GUI.

Я написал, такой скрипт и он не работал.


control_setSize(frm, 600,400)
form_centerScreen(frm)

img = createImage(frm)
control_setSize(img, 300,400)
image_loadImageFromFile(img,"D:\qqq\Game.bmp")
frm = createForm(true) 

Я спросил у Dark Byte в чём ошибка. Он любезно написал, что можно заменить image_loadImageFromFile(img,"D:\qqq\Game.bmp") на:

image_loadImageFromFile(img,"D:\\qqq\\Game.bmp")

или

image_loadImageFromFile(img,[[D:\qqq\Game.bmp]])

Так что имейте это ввиду при создании трейнера на LUA. Я задал DB также вопрос как загрузить картинку из текущей директории без указания пути. Ведь если пользователь скопирует файлы .CT и picture.bmp, тогда у него изменится путь...

Тутор 7. Отображение картинок в GUI.

....

Я задал DB также вопрос как загрузить картинку из текущей директории без указания пути. Ведь если пользователь скопирует файлы .CT и picture.bmp, тогда у него изменится путь...

Пришёл ответ. Это возможность пока не поддерживается. Но в будущей версии 6.1 уже поддерживается загрузка картинок с ресурсами форму из IDE LUA. Причем картинки разных форматов, а не только bmp.

Можете посмотреть на результат.

post-3-1297763474,85_thumb.jpg

Картинка весит ~70 кб. Файл скрипта весит в два раза больше. Учитывая то, что кроме картинки больше ничего нет. А знаете почему. Потому что скрипт это xml разметки и картинка преобразуется в нём в текстовый вид байтов.


<CheatTable CheatEngineTableVersion="10">
<Forms>
<UDF1>5450463007544345466F726D0455444631044C65667403A30106486569676874039F0003546F7003C401055769647468034301084175746F53697A65
//.......................... (вырезано мной)
FD9000000</UDF1>
</Forms>
<CheatEntries/>
<UserdefinedSymbols/>
</CheatTable>
<?xml version="1.0"?>

Если картинку удалить, то скрипт занимает 301 байт.


<CheatTable CheatEngineTableVersion="10">
<Forms>
<UDF1>5450463007544345466F726D0455444631044C6566740360010648656967687403F00003546F70038F010557696474680340010743617074696F6E0604554446310000</UDF1>
</Forms>
<CheatEntries/>
<UserdefinedSymbols/>
</CheatTable>
<?xml version="1.0"?>

Также хочу напомнить, что очень важен именно такой тип трейнеров зависящих от установленной Cheat Engine, а не автономно работающих отдельно от CE. Почему спросите вы? Я отвечу:

1) Очень важно - в любой момент можно проанализировать и изменить скрипт.

2) Автономные трейнеры от CE не подходят (хотя их тоже можно открыть в CE и редактировать). Они не подходят и логически и из-за увеличения размера в базе данных, которая будет и без того разрастаться. Логически одна часть программы должна быть на компьютере пользователя и должна строить интерфейс трейнера по разметке. Разметки, т.е. скрипты уже пользователь может качать хоть откуда. Если пользователь не будет размещать в своём трейнере картинок, то его разметка будет меньше 1 кб. Сравните самый маленький трейнер обычно больше 2 кб. А в основном делают трейнеры которые больше 20 кб. Если паковать/распаковывать в огромной базе, то это будет ненужной работой процессора, который будет работать с базой данных.

Одно дело когда мы хотим скачать трейнер пусть даже размером 1 мб. Мы его скачаем попользуемся и удалим. Другое дело быть человеком который хранит множество трейнеров, чтобы другие ими пользовались. И тут встаёт вопрос. Во всех трейнерах есть множество повторяющейся логической информации: код создание окна, обработчики сообщений, PE формат исполянемых файлов. Всё это мусор когда терйнеров больше одного. Мы должны работать только с той информацией которая удобна и которая не повторяется от трейнера к трейнеру. Решение это именно таблицы Cheat Engine: LUA скрипты + автоассемблер + подгрузка dll-ок. Больше ничего геймхакеру и не нужно, он сосредоточиться только на инъекции кода и сможет быстро и удобно изменять её в любой момент и хранить свой труд в максимально лучшей форме - восприятии и компактности.

Какие нам нужны ещё туторы.

0) Работа с вводом/выводом (горячие клавиши, эмуляция клавиш, обработчики нажатий клавиш)

1) Отображения информации из памяти игры в GUI элементах

2) Создание интерфейса как у трейнера с управлением активации и деактивации читов

3) По дизассемблерным инструкциям формирование автоассемблерных скриптов и читов

4) Пользователь вводит адрес параметра, после чего происходит отладка с нахождением инструкций, создаётся чит...

5) Работа в LUA с файлами

6) Создание автоматизированных сценариев в сингловых играх.

и т.п.

Резюме по LUA CE 6.0

Кстати поддержка отдельно функционирующих трейнеров будет в следующих версиях (написал Дарк Байт)

Вообще я считаю не очень стоит заморачиваться на LUA в создании трейнеров. Форму трейнера с GUI элементами по прежнему удобнее создавать в IDE VisulaStudio. Как я писал много раз я придерживаюсь модели: создания программы ланчера TrainerMAx с БД библиотек cheat.dll, которые внедряются в процесс используют базовые функции из SystemCheat.dll. И это самый лушчий способ, который я знаю в создании групп трейнеров. GUI форму можно сменить на DirectX и OpenGL.... НО LUA это хороший эксперимент. И все же он полезен при исследовании игрового кода, т.к. с его помощью можно автоматизировать чтение структур данных, их обработки (сравнении, выводе информации по условию и т.п.).

Ссылка на комментарий
Поделиться на другие сайты

Есть несколько полезных ссылок. Я их отобрал для себя, чтобы потом прочитать. Может кому пригодится:

ЗЫ В Индии есть отель которые называется Lua :)

Ссылка на комментарий
Поделиться на другие сайты

  • 1 месяц спустя...
  • 1 месяц спустя...

Часть1. Пробуем на вкус CE 6.1 Alpha (седьмая альфа от 28 марта)

Читать только опытным.

Модифицированная версия CE 5.6 RUS сгенерировала такой скрипт + я его изменил (код который генерит сигнатуру можно увидеть здесь)


[ENABLE]
aobscan(_faddress,d9xxxxxxd9xxxxxxd8xxxxxxdfxxf6xxxx0fxxxxxxxxxx8bxxe8xxxxxxxx8b)
alloc(_newmem,2048)
label(_returnhere)

_newmem:
mov [ebp+7c],(float)125.0
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]
jmp _returnhere

_faddress: // 005AE415 = Manhunt2.exe+1AE415
jmp _newmem
nop
nop
nop
_returnhere:

[DISABLE]
aobscan(_faddress,909090d8xxxxxxdfxxf6xxxx0fxxxxxxxxxx8bxxe8xxxxxxxx8b)

_faddress-5:
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]

dealloc(_newmem)
//Alt: db D9 45 7C 56 D9 5C 24 10

В этой игре как и в большинстве необязательно второй раз искать сигнатуру для отмены чит-кода (как правильно подметил Xipho). Надо исправить скрипт. Вопрос, как?! Потому что вряд ли это можно сделать на CE 5.6 версии - автоассемблер не позволяет создавать метки с изменяемыми смещениями во время выполнения скрипта. Также задача его переделать под паттерн "инициализация, активация и деактивация"+ создать интерфейс для трейнера под последнюю альфа версию CE 6.1. В ней появились новые фишки с переменными и сканированием сигнатур. Наконец, там появилось дизассемблириование в LUA позволяющее делать автоматическую инъекцию и генерацию сигнатур, но для этого нужно писать много мне до этого.

Нужно помнить, что LUA скрипт выполняется один раз после чего вся его память очищается, поэтому придётся использовать память процесса игры (хотя может быть уже и нет, т.к. не внимательно читал последние изменения в реализах альфа версиях)

План. Всё делаем на LUA+AA.

1. Пользователь кликает дважды на файл трейнера.

2. В нём расположен скрипт скрытия CE с последующей инициализацией.

3. Инициализация включает в себя

3.1 Поиск адреса внедрения по сигнатуре и его запоминание

3.2 Создание скрипта включения/отмены по горячим клавишам

Сделаем пока так в консоли


print(healthInjected)
healthInjected = AOBScan("d9xxxxxxd9xxxxxxd8xxxxxxdfxxf6xxxx0fxxxxxxxxxx8bxxe8xxxxxxxx8b",+X-C-W)

Не работает.

Смотрим пример DB, а он пишет, что работает так:


function _memrec_myCheat_activating(mr)
results = AOBScan("d9xxxxxxd9xxxxxxd8xxxxxxdfxxf6xxxx0fxxxxxxxxxx8bxxe8xxxxxxxx8b","+X-C-W")
if (results ~= nil) then
count = stringlist_getCount(results)
if (count > 1) then
address=stringlist_getString(results, 1)
script=[[
label(aobresult_myCheat)
registersymbol(aobresult_myCheat)
]]..address..[[:
aobresult_myCheat:
]]
autoAssemble(script);
end
object_destroy(results)
results=nil
end
end

Исправляем и запускаем в консоли.


results = AOBScan("d9xxxxxxd9xxxxxxd8xxxxxxdfxxf6xxxx0fxxxxxxxxxx8bxxe8xxxxxxxx8b", "+X-C-W")

if (results == nil) then
return
end

count = stringlist_getCount(results)
if (count >= 1) then
address=stringlist_getString(results, 0)
end

object_destroy(results)
results=nil
print(address)

Ага заработало. Показывает верный адрес 005AE415

Вспомним, что DB писал про новую поддержку переменных в LUA и AA:

e.g in the lua script you have "bla=123"

you can then do "mov [eax],$bla"

same for addresses, but remember that ce uses hexadecimal strings

so:

lua: address="00400500"

aa: mov eax,[$address]

Это информация поможет составить скрипт, где найденный адрес будет встроен в автоассеблерный текст.

Стоп, стоп... Всё это было исследование ключевых моментов. Не стоит углубляться в него слишком сильно. Иначе мы получим множество осколков программы, которые будет сложно соединить.

Вернёмся к началу что должно быть, но теперь уже яснее.

Графического интерфейса трейнера пока не будет.

Инициализация чит-кода не должна быть видна пользователю, в таблице CE должен появиться чит-код когда процесс игры найден и было подключение к нему. Всё вместе выглядит так:

1. Написать скрипт LUA:

1.1) Инициализация

- поиск и подключение к процессу

- поиск сигнатуры байт

- формирование АА скрипта инициализации и его запуск (возможно без АА)

- формирование АА скрипта как записи в таблице и регистрация горячей клавиши

- Написать условие, если процесс игры был закрыт : вывести сообщение, очистить таблицу, ожидать процесс с последующей инициализацией.

После того как это будет сделано.

2. Встроить логику скрытия окна CE

3. Создать интерфейс трейнера

Конец первой части...

Часть2. Пробуем на вкус CE 6.1 Alpha (седьмая альфа от 28 марта пофиксенная из SVN)

Сделаем попытку открытия доступа к процессу и когда это произойдёт обработаем это событие.


-- 1. Активировать циклический поиск процесса
aalist = getAutoAttachList()
stringlist_add(aalist,"Manhunt2.exe");

-- После того как процесс был открыт вызовется этот обработчик
function onOpenProcess(processid)

showMessage("Я в функции onOpenProcess")

-- 2. Формирование и запуск инициализирующего скрипта
-- Поиск адреса внедрения

-- 3. Сделать: формирование скрипта активации и добавление его в таблицу CE

-- 4. Сделать: формирование скрипта деактивации и добавление его в таблицу CE

end

Сохраним этот скрипт в окне "Lua script: Cheat Table" вызываемого по "Ctrl+Alt+L"

Сохраним таблицу.

Выйдем из СЕ.

Дваждый кликнем по файлу сохранённой таблицы. При этом появиться CE с предложением сразу выполнить LUA-скрипт - соглашаемся.

Теперь как только запустим игру, то появиться сообщение "Я в функции onOpenProcess"

Вот ещё один шаблон этой функции, который пригодится например тогда, когда игра есть лаунчер и он запускает другие процессы. Можно скорректировать какой процесс мы будем открывать, если уже открыт какой-то процесс


--check if the user opened firefox.exe (for next version I should implement a getProcessnameFromID() )
wrongProcessID=getProcessIDFromProcessName("firefox.exe")
if (processid==wrongProcessID) then
--is there a plugin-container.exe?
correctProcess=getProcessIDFromProcessName("plugin-container.exe");
if (correctProcess ~=nil) and (correctProcess ~=0) then
openProcess(correctProcess);
end;
end;
end;
function onOpenProcess(processid) 

Имейте ввиду, что onOpenProcess(processid) срабатывает всегда, когда открывается процесс и однажды был выполнен LUA-скрипт аналогичный одному из выше описанных. processid содержит идентификатор процесса с которым можно работать как в примере выше.

Конец 2-ой части...

Важное дополнение к этой части (от 5 апреля 2011 г)

Функция onOpenProcess(processid) оказалась такой "вертлявой", что я сразу не понял в чем дело.

Оказывается она просто сигнализирует о том, что processid был найден, но ещё не присоединён. И в то же время она сработает если вы вызовите OpenProcess. Это весьма странно. Функция уже должна сразу открывать процесс.

Выйти из этого положения позволит следующий код.


aalist = getAutoAttachList()
stringlist_add(aalist,"Manhunt2.exe");
attach = false

-- После того как процесс был открыт вызовется этот обработчик
function onOpenProcess(processid)

if (attach) then return end
-- Здесь код будет срабатывать только один раз если далее не изменить attach на false
attach = true
openProcess(processid)

--- другой код
end
----------
----------

Часть3. Пробуем на вкус CE 6.1 Alpha (седьмая альфа от 28 марта пофиксенная из SVN)

В первой части был скрипт на постоянное здоровье. Приводить его снова не буду. Задача сделать так чтобы чтобы формировался этот же скрипт, но с изменяемыми адресами при подключении к процессу. А позже мы уберём выделением памяти из скрипта автоассемблера.

Сначала создайте пустой автоассемблерный скрипт с названием "Inf. Health". Точнее пустой с директивами


[ENABLE]
[DISBLE]

Итак. Вызваем окно LUA по ctrl+alt+L из главного окна CE. И вбиваем


-- 1. Автоматический поиск процесса и его ожидание
aalist = getAutoAttachList()
stringlist_add(aalist,"Manhunt2.exe");
attach = false

-- После того как процесс был открыт вызовется этот обработчик
function onOpenProcess(processid)

if (attach) then return end
attach = true
openProcess(processid)

-- 2. Поиск адреса внедрения
results = AOBScan("d9xxxxxxd9xxxxxxd8xxxxxxdfxxf6xxxx0fxxxxxxxxxx8bxxe8xxxxxxxx8b", "+X-C-W")
if (results == nil) then
messageDialog("Ошибка. Не найден адрес внедрения.\n\rТрейнер будет закрыт!",1, 2)
closeCE()
return
end

count = stringlist_getCount(results)
if (count >= 1) then
address=stringlist_getString(results, 0)
end

-- 3. Формирование скрипта и добавление его в таблицу CE
script =[[
[ENABLE]
alloc(_newmem,2048)
label(_returnhere)

_newmem:
mov [ebp+7c],(float)125.0
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]
jmp _returnhere

]]..address..[[:
jmp _newmem
nop
nop
nop
_returnhere:

[DISABLE]
]]..address..[[:
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]

dealloc(_newmem)
]]

te_InfHealth = getTableEntry("Inf. Health")
memoryrecord_setScript(te_InfHealth, script)

object_destroy(results)
results=nil

-- 4. ормирование интерфейса

end

Сохраняем и закрываем CE.

Теперь запускаем файл таблицы двойным кликом.

Затем игру (или наоборот игру а затем таблицу это без разницы)

Тут же сформируется скрипт и если вы посмотрите какой там скрипт, то вы увидите теперь там полностью сформированный скрипт.


[ENABLE]
alloc(_newmem,2048)
label(_returnhere)

_newmem:
mov [ebp+7c],(float)125.0
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]
jmp _returnhere

005AE415:
jmp _newmem
nop
nop
nop
_returnhere:

[DISABLE]
005AE415:
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]

dealloc(_newmem)

Как видите здесь нет aobscan, потому что он уже отработал своё.

Следующий шаг нам надо чтобы скрипт формировался где-то так.


[ENABLE]
сode_InfHealth: //005AE415
jmp cave_InfHealth
nop
nop
nop

[DISABLE]
сode_InfHealth: //005AE415
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]

Но об этом и о другом в следующей части...

Важное дополнение к этой части.

Забыл написать про новую фишку переменных со знаком $;

Предыдущий сгенированный скрипт можно получить таким и он будет работать совместно с LUA.


alloc(_newmem,2048)
label(_returnhere)

_newmem:
mov [ebp+7c],(float)125.0
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]
jmp _returnhere

$address:
jmp _newmem
nop
nop
nop
_returnhere:

[DISABLE]
$address:
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]

dealloc(_newmem)
[ENABLE]

Для этого надо изменить LUA


--------
-- 1. Автоматический поиск процесса и его ожидание
aalist = getAutoAttachList()
stringlist_add(aalist,"Manhunt2.exe");
attach = false

-- После того как процесс был открыт вызовется этот обработчик
function onOpenProcess(processid)

if (attach) then return end
attach = true
openProcess(processid)

-- 2. Поиск адреса внедрения
results = AOBScan("d9xxxxxxd9xxxxxxd8xxxxxxdfxxf6xxxx0fxxxxxxxxxx8bxxe8xxxxxxxx8b", "+X-C-W")
if (results == nil) then
messageDialog("Ошибка. Не найден адрес внедрения.\n\rТрейнер будет закрыт!",1, 2)
closeCE()
return
end

count = stringlist_getCount(results)
if (count >= 1) then
address=stringlist_getString(results, 0)
end

-- 3. Формирование скрипта и добавление его в таблицу CE
script =[[
[ENABLE]
alloc(_newmem,2048)
label(_returnhere)

_newmem:
mov [ebp+7c],(float)125.0
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]
jmp _returnhere

$address:
jmp _newmem
nop
nop
nop
_returnhere:

[DISABLE]
$address:
fld dword ptr [ebp+7c]
push esi
fstp dword ptr [esp+10]

dealloc(_newmem)
]]

te_InfHealth = getTableEntry("Inf. Health")
memoryrecord_setScript(te_InfHealth, script)

object_destroy(results)
results=nil

-- 4. ормирование интерфейса

end
---------------

Если посмотреть выше написанный скрипт, то он смотрится несколько громоздко это только для одного чита. А самое главное что писать всё это долго и неудобно.

И тогда я задумался есть ли какой-то вариант нового формата?!

Для создания чит-кода пока на первый раз нужно не многое.

Имя читкода

Адрес

Выполнять ли оригинальный код

Тело чит-кода

Всё это можно вписать в компактный формат наиболее удобный xml файл, который можно поместить в ресурсы с трейнером LUA.

Представьте себе, что достаточно написать вот эти строчки для осуществления задуманного, все остальное за вас сделает LUA.


<process name = "Manhunt2.exe">
<cheat name ="InfHealth" address = "Manhunt2.exe+1AE415" originalcode = "true">
mov [ebp+7c],(float)125.0
</cheat>
</process>

Только для обработки придётся писать специальный LUA-анализатор. Или использовать анализатор совместной с free pascal на котором скомпилирован CE...

Ссылка на комментарий
Поделиться на другие сайты

  • 1 месяц спустя...

Тема. Как подключиться к процессу при загрузке скриптов (CE 6.1)

Когда Вы дважды кликаете по файлу Cheat Engine "имя файла.ct", то там может быть встроен LUA скрит, который сразу может выполнится. Выполниться он или нет это зависит от настроек CE. В скрипте LUA вы можете создать форму трейнера и скрыть CE. Но скрывать CE и создавать форму трейнера мы не будем. Напишем LUA скрипт который автоматически будет искать процесс по его имени.


processName = "Test.exe"

aalist = getAutoAttachList()
stringlist_add(aalist,processName);
attach = false

function onOpenProcess(processid)

if (attach) then
return
end

attach = true
openProcess(processid)

-- ваш код

end

onOpenProcess() это функция обработки события, которая будет вызываться при открытии процесса. attach - это переменная, от которой зависит открывать ли процесс второй раз. Пришлось именно так написать, потому что onOpenProcess срабатывает не один раз. Подробнее почему так происходит сейчас написать не могу.

После того как этот код выполнится можно включить читы в главной таблицы Cheat Engine. Ну или активировать опции Вашего LUA трейнера.

Ссылка на комментарий
Поделиться на другие сайты

Тема. Часть1. Функция генерирующая автоассемблерный скрипт. CE 6.1

Я не раз уже писал у себя в блоге во Флудильне, что существует возможность генерировать скрипты автоассемблера или АА скрипты с помощью LUA при минимальных начальных данных. Я предлагал это сделать по разметке XML. Но пока до неё ещё рановато и я решил сделать простую функцию которой бы требовалось пока самых простых данных:

1) названия скрипта,

2) адрес инъекции

3) отладочный код инъекции

4) булевая переменная (да или нет) означающая генерировать ли разметку оригинального кода, т.к. бывают случаи когда оригинальный код не требуется.

Чтобы получить аналогичный скрипт на тестовой программе:



[ENABLE]
alloc(newmem,2048)
label(originalcode)
label(returnhere)

newmem:
mov [0045B5A4],#10000
originalcode:
jmp returnhere

"Test.exe"+54659:
jmp newmem
db 90 90 90
returnhere:

[DISABLE]
"Test.exe"+54659:

dealloc(newmem)

Можно воспользоваться моей функцией:



function GenerateAAscript( cheatName, address, injectInstruction, originalcode )
local addressInjection = address
local sumBytes = 0
local nopsString = ""
local nopsCount = 0
local originalCodeString = ""

repeat
extrafield, opcode, bytes, address = splitDisassembledString(disassemble(address))

if (originalcode) then
originalCodeString = originalCodeString..[[

]]..opcode
end

local countBytes = math.floor(string.len (string.gsub(bytes, " ", "")) / 2)

sumBytes = sumBytes +countBytes

if (sumBytes>5) then
nopsCount = sumBytes - 5
nopsString = " db"
for i = 1, nopsCount do
nopsString = nopsString.." 90"
end
nopsString = nopsString..[[

]]
break
end

address = string.format("%x", ("0x"..address) + countBytes)
until (countBytes == 5)

script =[[
[ENABLE]
alloc(newmem,2048)
label(originalcode)
label(returnhere)

newmem:
]]..injectInstruction..[[

originalcode:]]..originalCodeString.. [[

jmp returnhere

]]..addressInjection..[[:
jmp newmem
]]..nopsString..[[
returnhere:

[DISABLE]
]]..addressInjection..":"..originalCodeString.. [[


dealloc(newmem)
]]

te = getTableEntry(cheatName)
memoryrecord_setScript(te, script)
end

GenerateAAscript("Inf10000", [["Test.exe"+54659]], [[mov [0045B5A4],#10000]], false )

Как видно входные данные очень короткие (код инъекции может быть большим и в несколько строк):


GenerateAAscript("Inf10000", [["Test.exe"+54659]], [[mov [0045B5A4],#10000]], false )

И всю разметку АА скрипта делает за вас код. Также не забудьте создать в главной таблице CE пустой автоассемблерный скрипт с директивами [ENABLE] и [DISABLE]


[ENABLE]
[DISABLE]
 и назвать его

Подведя итоги можно обратить внимание на следующее.

Когда необходимо сгенерировать аа скрипт, теперь это стало сделать проще благодаря GenerateAAscript(). Обратите внимание, что Ваш автоассемблерный скрипт теперь можно не писать, т.к. эа это сделает LUA. Конечно, это не для всех случаев, но для большинства. Для других случаев нужно усовершенствовать LUA код.

Эта функция должна будет делать в будущем следующее:

1) включить анализ который будет решать ставить pushfd и popfd или нет

2) подключить ассемблер и генерировать разметку более продвинуто по правилу постоянного определенного значения и в этом случае отладочный код инъекции указывать не потребуется (также можно подключить другие правила)

3) подключить сканирование сигнатур

4) подключить скрипт определения цепочки указателей и фильтров используя отладчик.

В аттаче файл AAgen.lua с функцией GenerateAAscript и инструкциями как ей пользоваться.

AAgen.rar

Ссылка на комментарий
Поделиться на другие сайты

Тема. Часть2. Функция генерирующая автоассемблерный скрипт. CE 6.1.

Создайте AAgen.lua: (добавить в директорию со скриптами CE)


function GenerateAAscript( cheatName, address, injectInstruction, originalcode )
local addressInjection = address
local sumBytes = 0
local originalCodeString = ""

repeat
extrafield, opcode, bytes, address = splitDisassembledString(disassemble(address))
local countBytes = math.floor(string.len (string.gsub(bytes, " ", "")) / 2)
originalCodeString = originalCodeString..[[

]]..opcode
sumBytes = sumBytes +countBytes
address = string.format("%x", ("0x"..address) + countBytes)
until (sumBytes >= 5)

local nopsString = ""
local nopsCount = sumBytes - 5
if (nopsCount>0) then
nopsString = " db"
for i = 1, nopsCount do
nopsString = nopsString.." 90"
end
nopsString = nopsString..[[

]]
end

if (originalcode) then
script =[[
[ENABLE]
alloc(newmem,2048)
label(originalcode)
label(returnhere)

newmem:
]]..injectInstruction..[[

originalcode:]]..originalCodeString.. [[

jmp returnhere

]]..addressInjection..[[:
jmp newmem
]]..nopsString..[[
returnhere:

[DISABLE]
]]..addressInjection..":"..originalCodeString.. [[


dealloc(newmem)
]]
else
script =[[
[ENABLE]
alloc(newmem,2048)
label(returnhere)

newmem:
]]..injectInstruction..[[

jmp returnhere

]]..addressInjection..[[:
jmp newmem
]]..nopsString..[[
returnhere:

[DISABLE]
]]..addressInjection..":"..originalCodeString.. [[


dealloc(newmem)
]]
end

local teSlave = getTableEntry(cheatName)

if (teSlave == nil) then
teSlave = addresslist_createMemoryRecord(addresslist)
memoryrecord_setDescription(teSlave, cheatName)
memoryrecord_setType(teSlave, vtAutoAssembler)
end

memoryrecord_setScript(teSlave, script)
end

Напишите этот скрипт для автозапуска (этот скрипт шаблон для примера):


require("AAgen")
processName = "Test.exe"

function Initialize()
GenerateAAscript("Test", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
-- ваши либые GenerateAAscript(...) которых может быть довольно много

end

aalist = getAutoAttachList()
stringlist_add(aalist,processName);

function onOpenProcess(processid)
if (attach) then
return
end
attach = true
openProcess(processid)
Initialize()
end

В данном примере результатом будет добавленный сгенерированный скрипт:


[ENABLE]
alloc(newmem,2048)
label(originalcode)
label(returnhere)

newmem:
mov [0045B5A4],#10000
originalcode:
mov eax,[0045B5A4]
jmp returnhere

"Test.exe"+54650:
jmp newmem
db 90 90 90 90 90
returnhere:

[DISABLE]
"Test.exe"+54650:
mov eax,[0045B5A4]

dealloc(newmem)

Обращаю внимание, что самая важная фишка это упрощение в генерации скриптов и значительное сокращение их объёма:


GenerateAAscript("Test", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
-- ваши либые GenerateAAscript(...)

end
function Initialize()

При этом у пользователя файл AAgen.lua: должен предварительно до запуска файла *.CT находиться в директории со скриптами *.CT.

От геймхакера в большинсве случаев требуется писать только:


GenerateAAscript("Test1", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
GenerateAAscript("Test2", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
GenerateAAscript("Test3", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
GenerateAAscript("Test4", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
GenerateAAscript("Test5", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
GenerateAAscript("Test6", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
GenerateAAscript("Test7", [["Test.exe"+54650]], [[mov [0045B5A4],#10000]], true )
-- ваши либые GenerateAAscript(...) которых может довольно много

end
function Initialize()

Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

В этой теме можно посмотреть как был сделан трейнер на LUA Engine на версии CE 6.1 Beta 3 с методом сканирования сигнатур.

С не большой картинкой он был бы 53 Кб. Без неё он немного меньше 4 кб без упаковки архиватором.

Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

Новый LUA-скрипт генерации АА-скрипта в единую выделенную память

Работать с ним можно так:

bufScript = BeginGenerateAAscriptAggregateMem("allocMem","2048")
bufScript = GenerateAAscriptAggregateMem(bufScript, "infGold", "0045464A", "xor eax, eax", true )
bufScript = GenerateAAscriptAggregateMem(bufScript, "infMana", "00454650", "xor ebx, ebx", true )
bufScript = EndGenerateAAscriptAggregateMem(bufScript)
showMessage(bufScript)
autoAssemble(bufScript)

Происходит следующее. Создаётся один выделенный блок памяти. В нём размещаются все тела читов. При каждом таком размещении в таблицу CE добавляется AA-скрипт с активацией и деактивации ТОЛЬКО прыжков. Т.е. мы имеем созданный блок памяти и нам о нём знать не надо и не надо видеть его АА скрипт. Далее мы работает только включая и выключая прыжки. Названия читов должны быть английскими.

Для того чтобы это дело работало,то выше написанного кода заместите следующий код:


function BeginGenerateAAscriptAggregateMem(newMemDescription, sizeMem)
return [[
alloc(]]..newMemDescription..","..sizeMem..[[)
->>label]]..newMemDescription..[[:

->>newCode
->>adressessInjected
]]
end

-- Функция завершает "инциализирующий" скрипт
function EndGenerateAAscriptAggregateMem(aggregateMem)
local endScript = string.gsub(aggregateMem, "->>label", "")
endScript = string.gsub(endScript, "->>newCode", "")
endScript = string.gsub(endScript, "->>adressessInjected", "")
return endScript
end

local function preSubScript(script, patternWord, newWords)
local index = string.find(script, patternWord)
local newscript = string.sub(script, 1, index - 1)..newWords.."\n"..string.sub(script, index)
return newscript
end

-- Функция объединяет предыдущие скрипты с текущим
function GenerateAAscriptAggregateMem(aggregateMem, cheatName, address, newCode, stateOriginalCode)
--[[
1. Добавляется новый скрипт к общей памяти
Подскрипты сразу же добавляются в таблицу CE, которые можно активировать после активации инциализирующего скрипта.
]]--

-------
local addressInjection = address
local sumBytes = 0
local originalCodeString = " "

repeat
local countBytes = getInstructionSize(address)
extrafield, opcode, bytes, address = splitDisassembledString(disassemble(address))
originalCodeString = originalCodeString.."\n "..opcode
sumBytes = sumBytes +countBytes
addressBehindNops = string.format("%x", ("0x"..address) + countBytes)
until (sumBytes >= 5)

local nopsString = ""
local nopsCount = sumBytes - 5
if (nopsCount>0) then
nopsString = " db"
for i = 1, nopsCount do
nopsString = nopsString.." 90"
end
nopsString = nopsString..[[

]]
end
-- todo: сделать проверку на регистрацию меток которые уже были созданы (подразумеваеться, что пользователь не зарегистрировал метку изначально с именем в cheatName)
--[[ help:
registerSymbol(symbolname, address)
unregisterSymbol(symbolname)
]]--

local script = preSubScript(aggregateMem,"->>label", "label("..cheatName..")")
script = preSubScript(script,"->>label", "registersymbol("..cheatName..")") -- зарегистрируеются во время активации инициализирующего скрипта из внешнего кода
registerSymbol("returnHere_"..cheatName, addressBehindNops)
if (stateOriginalCode) then
script = preSubScript(script,"->>label", "label(originalcode_"..cheatName..")")
local buf = cheatName..":\n "..newCode.."\noriginalcode_"..cheatName..":"..originalCodeString.."\n jmp returnHere_"..cheatName
script = preSubScript(script,"->>newCode", buf)
else
script = preSubScript(script,"->>newCode", cheatName..":\n"..newCode.."\n jmp returnHere_"..cheatName)
end
--script = preSubScript(script,"->>label", "label(returnHere_"..cheatName..")")

-- Скрипт Активации и Деактивации, который будет добавлен в главную таблицу CE
local scriptAddMainTable = "[ENABLE]\n-->>address1[DISABLE]\n-->>address2"
-- регистрировать метку возврата ненадо, т.к. она уже была зарегистрировна выше
-- originalCodeString переносить на новые строки не требуется
-- инструкция активации
scriptAddMainTable = preSubScript(scriptAddMainTable,"-->>address1", addressInjection..":\njmp "..cheatName.."\n"..nopsString)
-- инструкция воостанавления
scriptAddMainTable = preSubScript(scriptAddMainTable,"-->>address2", addressInjection..":"..originalCodeString)
-- очистка меток
scriptAddMainTable = string.gsub(scriptAddMainTable, "-->>address1", "")
scriptAddMainTable = string.gsub(scriptAddMainTable, "-->>address2", "")
-- добавление в таблицу (активировать можно только после того когда выполнится оригинальный код, иначе будет сообщение об ошибки не зарегистрированной метки в cheatname)

local teSlave = getTableEntry(cheatName)
if (teSlave == nil) then
teSlave = addresslist_createMemoryRecord(addresslist)
memoryrecord_setDescription(teSlave, cheatName)
memoryrecord_setType(teSlave, vtAutoAssembler)
end
memoryrecord_setScript(teSlave, scriptAddMainTable)

------------
return script
end

-- Функция генерирует "инциализирующий" скрипт для подготовки с разметкой

Если кто-то не понимает как этим пользоваться пишите вопросы

Ссылка на комментарий
Поделиться на другие сайты

К посту выше. Тоже самое, но со сканированием сигнатуры:

Пример вызова, если CE 6.1 RC2 уже была подключена к процессу :


--...
GenerateAAscriptAggregateMemWithSignature(bufScript,"noRecoilWhenWalk","d9xxd9xxxxd9xxxxd9xxxxxxxxxxdexxdfxxf6xxxx75xxd9xxxxxxxxxxd9",8,"fstp st\nfldz", true)
--...

Этот пример частично для:



processName = "iw3sp.exe"

function ByteScan(signature)
local results = AOBScan(signature, "+X-C-W")
if (results == nil) then
messageDialog("Ошибка. Не найден адрес внедрения.\n\rТрейнер будет закрыт!",1, 2)
closeCE()
return
end

local address = stringlist_getString(results, 0)
return address
end

function Initialize()
--1 Убрать отдачу на руку
local workAddres = ByteScan("e8xxxxxxxx8bxxxxxx83xxxx33xx80xxxxxx75xx8bxxxxxxf7")
local workTemplateAsmText = [[
->>address:

db 90 90 90 90 90]]
workTemplateAsmText = string.gsub(workTemplateAsmText, "->>address", workAddres)
autoAssemble(workTemplateAsmText)

--2 Устойчивый прицел при стрельбе
--3 Устойчивый прицел при беге

--Устойчивый прицел при беге
local workAddres1 = ByteScan("d9xxd9xxxxd9xxxxd9xxxxxxxxxxdexxdfxxf6xxxx75xxd9xxxxxxxxxxd9")
workAddres1 = string.format("%x", ("0x"..workAddres1) + 8)

-- Устойчивый прицел при стрельбе
local workAddres2 = ByteScan("d9xxxxxxxxxx75xxd9xxxxxxxxxxd8xxxxxxd8xxdfxxf6xxxx7axxd9")

workTemplateAsmText = [[
alloc(newmem1,2048)
label(newmem2)
label(returnhere1)
label(originalcode1)
label(returnhere2)
label(originalcode2)

newmem1:
fstp st
fldz
originalcode1:
fstp dword ptr [ebx+00000624]
jmp returnhere1

newmem2:
fstp st
fldz
originalcode2:
fst dword ptr [ecx+00000624]
jmp returnhere2

->>address1:
jmp newmem1
nop
returnhere1:

->>address2:
jmp newmem2
nop
returnhere2:
]]
workTemplateAsmText = string.gsub(workTemplateAsmText, "->>address2", workAddres1)
workTemplateAsmText = string.gsub(workTemplateAsmText, "->>address1", workAddres2)
autoAssemble(workTemplateAsmText)

--4 Бесконечные боеприпасы без перезарядки
-- в будущем
--5 Бесконечное здоровье
-- в будущем

-- Звуковой сигнал об успешности внедрения читов
beep()
end


aalist = getAutoAttachList()
stringlist_add(aalist,processName);

function onOpenProcess(processid)
if (attach) then
return
end
attach = true
openProcess(processid)
Initialize()
form_show(UDF1)
end

function FormClose(sender)
closeCE()
return caHide
end
hideAllCEWindows()

Необходимый код для формирования скрипта по сигнатуре в общий скрипт:


local results = AOBScan(signature, "+X-C-W")
if (results == nil) then
messageDialog("Ошибка. Не найден адрес внедрения.\n\rТрейнер будет закрыт!",1, 2)
closeCE()
return
end

local address = stringlist_getString(results, 0)
return address
end


-- Функция объединяет предыдущие скрипты с текущим по сигнатуре
function GenerateAAscriptAggregateMemWithSignature(aggregateMem, cheatName, signatre, signatureOffset, newCode, stateOriginalCode )
local workAddress = ByteScan(signatre)
workAddress = string.format("%x", ("0x"..workAddress) + signatureOffset)
GenerateAAscriptAggregateMem(aggregateMem, cheatName, workAddress, newCode, stateOriginalCode)
end
function ByteScan(signature)

Ссылка на комментарий
Поделиться на другие сайты

Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.