MasterGH Опубликовано 11 января, 2011 Поделиться Опубликовано 11 января, 2011 1) туторы LUA с форума CE - здесь;2) последние изменения/добавления в LUA можно увидеть здесь;3) константы здесь; 4) как работают LUA функции в коде - здесь;О LUA в CE 6.0LUA движок используется в Cheat Engine 6.0, а также и в множестве игр. Вспомните что у большинства компьютерных игр под Windows есть вызов игровой консоли так вот я точно не знаю, но вроде в большинстве случаев для этой консоли используется LUA движки. Например, в игре СТАЛКЕР точно есть LUA. Я всё пишу к тому, что LUA может вам пригодиться для модинга, спавна и т.п. Но сейчас речь не об этом.О достоинствах LUA я писал в нескольких темах на форуме, одна из них.Привожу справочный материал из справки CE 6.0Cheat engine also has a script engine in which you can execute simple lua scriptsМоя переработанная справка по LUA-поддрежке в CE 6.0List of CE specific functions:readBytes(address,bytecount) : Reads the bytes at the given address and returns itwriteBytes(address, x,x,x,x) : Write the given bytes to the given addressreadBytesLocal(address,bytecount) : See readBytes but then it's for Cheat engine's memorywriteBytesLocal(address, x,x,x,x) : See writeBytes but then it's for Cheat Engine's memoryreadInteger(address) : Reads a integer from the specified addressreadFloat(address) : Reads a single precision floating point value from the specified addressreadDouble(address) : Reads a double precision floating point value from the specified addressreadString(address, maxlength) : maxlength is just so you won't freeze for too long, set to 6000 if you don't care too muchwriteInteger(address,value) : Writes an integer to the specified address. Returns true on successwriteFloat(address,value) : Writes a single precision floating point to the specified address. Returns true on successwriteDouble(address,value) : Writes a double precision floating point to the specified address. Returns true on successwriteString(address,string) : Write a string to the specified address. Returns true on successgenerateAPIHookScript(address, addresstojumpto, addresstogetnewcalladdress OPT) : Generates an auto assembler script which will hook the given address when executedautoAssemble(text) : runs the auto assembler with the given text. Returns true on successshowMessage(text) : shows a messagebox with the given textsleep(milliseconds): pauses for the number of specified milliseconds (1000= 1 sec...)getProcessIDFromProcessName(name) : returns a processidopenProcess(processid) : causes cheat engine to open the given processidopenProcess(processname): causes cheat engine to find and open the given processpause : pauses the current opened processunpause: resumes the current opened processgetPixel(x,y) : returns the rgb value of the pixel at the specific screen coordinategetMousePos: returns the x,y coordinates of the mousesetMousePos(x,y): sets the mouse positionisKeyPressed(key) : returns true if the specified key is currently pressedkeyDown(key) : causes the key to go into down statekeyUp(key) :causes the key to go updoKeyPress(key) : simmulates a key pressmessageDialog(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 successCheat table functions:createTableEntry: creates an generic cheat table entry and add it to the list. Returns a tableentry pointer you can use with memrec routinesgetTableEntry(descriptionname): returns a tableEntry pointer for use with memrec functionsmemrec_setDescription(te, description): sets the specified description for this entrymemrec_getDescription(te): gets the current description of this entrymemrec_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 needmemrec_getType(te) : returns the Variable type. (vtByte to vtCustom)memrec_setType(te, vartype) : sets the type of the entrymemrec_getValue(te): returns the current value of the cheat table entry as a stringmemrec_setValue(te, value): sets the value of a cheat table entrymemrec_getScript(te) : If the entry is of type vtAutoAssembler then you can get the script with this routinememrec_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 decreasememrec_unfreeze(te) :unfreezes an entrymemrec_setColor(te, colorrgb): Sets the color of the entrymemrec_appendToEntry(te,te) : Adds the entry to another entrymemrec_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 parameterExample: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 variablesEAX, EBX, ECX, EDX, EDI, ESP, EBP, ESP, EIPRAX, EBX, RBX, RDX, RDI, RSP, RBP, RSP, RIP, R8, R9, R10, R11, R12, R13, R14, R15 : The value of the registerDebug 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 inReturn 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 entrypointdebugProcess(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=Kerneldebugdebug_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 welldebug_removeBreakpoint(address) : if the given address is a part of a breakpoint it will be removeddebug_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 happenSo automatically setting the new value would surely cause unpredictable behaviour if the target app uses higher valueshasChangedARegister : 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 cehideAllCEWindows() : 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 changedform_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 GroupboxThe x,y position will be based on the client array of the ownercreatePanel(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 categorycontrol_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. Пример работы таймераПолучится у нас следующее (ссори за опечатки на скрине,там должно быть слово "Меняется")Я покажу как создавать GUI элементы. Присваивать им название, положение. Покажу как создать таймер и его обработчик. Покажу пример конкатенации с переменной. LUA я знаю очень плохо, но если есть справочные материалы по LUA(сами поищите, я может быть позже приведу ссылки) + откройте справку CE 6.0 , то разобраться не так уж сложно если у вас есть навыки программирования на других языках.Впишите в окно ввода следующее (в комментариях я написал подробности)--------------------Пример работы таймера в CE 6.0 LUAMasterGH 2011--------------------- ]]-- Создать форму и отобразить её в центре рабочего столаfrmtrainer = createForm()control_setCaption(frmtrainer,"Пример таймера в действии")form_centerScreen(frmtrainer)-- Создать текстовое поле, установить его позицию и значениеlbtimer = createLabel(frmtrainer)control_setPosition(lbtimer, 100,100)control_setCaption(lbtimer,"Прошло времени 0000 мс ")-- Создать таймер, счётчик интервалов и его обработчикcountdelay = 1function onTimerUpdateValue(timer) -- Задержка не может быть меньше 10 мс поэтому пришлось дописать нолик control_setCaption(lbtimer,"Прошло времени " .. countdelay .. "0 мс ") countdelay = countdelay + 1endtimer1 = createTimer(frmtrainer)-- Задержка не может быть меньше 10 мсtimer_setInterval(timer1, 10)timer_onTimer(timer1, onTimerUpdateValue)--[[ Жаль что нет установки редактирования шрифтов...Тутор 2. Как работать в отладке с LUA.Тутор от Дарк Байта. Я просто обязан упомянуть о нём здесь. Мы получаем данные при бряках на функции PeekMessageA в данном случае получаем естественно данные стека по указателю ESP. Также хочу обратить внимание, что мы можем получать данные регистров, данные памяти, кода... анализировать эти данные; ставить по определенным условиям брейкпоинты на адресах и снимать их; получить количество срабатываемых инструкций... Иначе говоря мы можем автоматизировать управление отладкой.Теперь теоритически можно написать подход поиска инструкций на которые не нужно ставить или нужно ставить фильтры... можно написать трейсер выводящий только нужную информацию. Возможно даже трейсер поднимающийся вверх по коду пытающийся построить цепочку указателей, но почему в LUA нет инструкции дизассемблирования по адресу (или я не нашёл??? ) Ладно, перейдём к тутору.Есть такая функция 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 categorycontrol_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) // показать формуsshowMessage(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")endcontrol_onClick(btn_InfHealth, OnInfHealth)Забыл отменить, что комментарии в LUA обозначаются "--".Теперь если кликнуть на кнопку, то будет вызов функции fun_OnInfHealth с сообщением об этом.Мы рассмотрели 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();--[[ Результат:На этом пока всё.Тутор 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.Можете посмотреть на результат. Картинка весит ~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 это хороший эксперимент. И все же он полезен при исследовании игрового кода, т.к. с его помощью можно автоматизировать чтение структур данных, их обработки (сравнении, выводе информации по условию и т.п.). Ссылка на комментарий Поделиться на другие сайты Поделиться
live_4_ever Опубликовано 11 января, 2011 Поделиться Опубликовано 11 января, 2011 Есть несколько полезных ссылок. Я их отобрал для себя, чтобы потом прочитать. Может кому пригодится: Использование LUA в играхСайт про LUAЯ люблю LUAСкрипты на Lua в С++ЗЫ В Индии есть отель которые называется Lua Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 15 февраля, 2011 Поделиться Опубликовано 15 февраля, 2011 Весьма интересен размер трейнеров, которые будут создаваться в СЕ. Или же компиляции в экзешник уже больше не будет? Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 15 февраля, 2011 Автор Поделиться Опубликовано 15 февраля, 2011 Она может быть и будет. Но размер будет около 15 мб (это писал Дарк Байт) Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 29 марта, 2011 Автор Поделиться Опубликовано 29 марта, 2011 Часть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 endcount = 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. Встроить логику скрытия окна CE3. Создать интерфейс трейнераКонец первой части... Часть2. Пробуем на вкус CE 6.1 Alpha (седьмая альфа от 28 марта пофиксенная из SVN)Сделаем попытку открытия доступа к процессу и когда это произойдёт обработаем это событие.-- 1. Активировать циклический поиск процессаaalist = getAutoAttachList()stringlist_add(aalist,"Manhunt2.exe");-- После того как процесс был открыт вызовется этот обработчикfunction onOpenProcess(processid) showMessage("Я в функции onOpenProcess")-- 2. Формирование и запуск инициализирующего скрипта -- Поиск адреса внедрения-- 3. Сделать: формирование скрипта активации и добавление его в таблицу CE-- 4. Сделать: формирование скрипта деактивации и добавление его в таблицу CEendСохраним этот скрипт в окне "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 _returnhere005AE415: 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... Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 1 мая, 2011 Автор Поделиться Опубликовано 1 мая, 2011 Тема. Как подключиться к процессу при загрузке скриптов (CE 6.1)Когда Вы дважды кликаете по файлу Cheat Engine "имя файла.ct", то там может быть встроен LUA скрит, который сразу может выполнится. Выполниться он или нет это зависит от настроек CE. В скрипте LUA вы можете создать форму трейнера и скрыть CE. Но скрывать CE и создавать форму трейнера мы не будем. Напишем LUA скрипт который автоматически будет искать процесс по его имени.processName = "Test.exe"aalist = getAutoAttachList()stringlist_add(aalist,processName);attach = falsefunction onOpenProcess(processid) if (attach) then return end attach = true openProcess(processid) -- ваш кодendonOpenProcess() это функция обработки события, которая будет вызываться при открытии процесса. attach - это переменная, от которой зависит открывать ли процесс второй раз. Пришлось именно так написать, потому что onOpenProcess срабатывает не один раз. Подробнее почему так происходит сейчас написать не могу.После того как этот код выполнится можно включить читы в главной таблицы Cheat Engine. Ну или активировать опции Вашего LUA трейнера. Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 1 мая, 2011 Автор Поделиться Опубликовано 1 мая, 2011 Тема. Часть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)endGenerateAAscript("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 Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 5 мая, 2011 Автор Поделиться Опубликовано 5 мая, 2011 Тема. Часть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(...) которых может быть довольно многоendaalist = 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(...)endfunction 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(...) которых может довольно многоendfunction Initialize() Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 19 мая, 2011 Автор Поделиться Опубликовано 19 мая, 2011 В этой теме можно посмотреть как был сделан трейнер на LUA Engine на версии CE 6.1 Beta 3 с методом сканирования сигнатур.С не большой картинкой он был бы 53 Кб. Без неё он немного меньше 4 кб без упаковки архиватором. Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 31 мая, 2011 Автор Поделиться Опубликовано 31 мая, 2011 Новый 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 endScriptendlocal 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 newscriptend-- Функция объединяет предыдущие скрипты с текущим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 scriptend-- Функция генерирует "инциализирующий" скрипт для подготовки с разметкойЕсли кто-то не понимает как этим пользоваться пишите вопросы Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 1 июня, 2011 Автор Поделиться Опубликовано 1 июня, 2011 К посту выше. Тоже самое, но со сканированием сигнатуры:Пример вызова, если 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 addressendfunction 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 fldzoriginalcode1: fstp dword ptr [ebx+00000624] jmp returnhere1newmem2: fstp st fldzoriginalcode2: fst dword ptr [ecx+00000624] jmp returnhere2->>address1: jmp newmem1 nopreturnhere1:->>address2: jmp newmem2 nopreturnhere2:]] workTemplateAsmText = string.gsub(workTemplateAsmText, "->>address2", workAddres1) workTemplateAsmText = string.gsub(workTemplateAsmText, "->>address1", workAddres2) autoAssemble(workTemplateAsmText)--4 Бесконечные боеприпасы без перезарядки -- в будущем--5 Бесконечное здоровье -- в будущем-- Звуковой сигнал об успешности внедрения читов beep()endaalist = getAutoAttachList()stringlist_add(aalist,processName);function onOpenProcess(processid) if (attach) then return end attach = true openProcess(processid) Initialize() form_show(UDF1)endfunction FormClose(sender) closeCE() return caHideendhideAllCEWindows()Необходимый код для формирования скрипта по сигнатуре в общий скрипт: 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 addressend-- Функция объединяет предыдущие скрипты с текущим по сигнатуре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) endfunction ByteScan(signature) Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения