MasterGH Опубликовано 2 февраля, 2015 Поделиться Опубликовано 2 февраля, 2015 (изменено) В данной теме будут ответы на вопросы по извлечению информации из дизассемблера при помощи Lua Engine в Cheat Engine 6.4Может быть много ситуаций, когда мы получаем много адресов инструкций отладочного кода и перебирать вручную, не упустив при этом мелочей, требует много времени и энергии. Представьте себе, что можно извлечь практически любую информацию из дизассемблера в режиме отладки и автоматически провести сравнения и анализ для поиска нужной информации. Можно автоматически проанализировать от нескольких сотен, до десятка тысяч строк дизассемблерного кода в атоматическом режиме. Основные данные адресов инструкций мы будем получать из окон: 1) из функции debugger_onBreakpoint, которая срабатывает при брейкпоинте2) из окна Ultimap3) из окна Tracer Log4) из окна прервавшихся инструкций5) из окна установленных Брейкпоинтов5) из окна Code ListОкно1. Главное окно CE->Table->Show Cheat Table Lua Script (Ctrl+Alt+A)Окно2. Главное окно CE->Кнопка Memory View->Tools->Lua Engine (Ctrl+L) Разница между этими окнами в том, что Окно2 не сохраняет информацию и при исполнении кода выводит его полностью. Окно1 сохранет код в таблицу CE.Писать можно в двух окнах.local lineDissassemble = disassemble('7FEEFBC143A')print(lineDissassemble)--Вывод: "7FEEFBC143A - FF 15 4888B000 - call qword ptr [7FEF06C9C88]"Допустим нужно прочитать инструкцию по адресу 7FEEFBC143Aprint(getNameFromAddress('7FEEFBC143A'))--Вывод: "gamedll_x64_rwdi.dll+5C143A "local lineDissassemble = disassemble('7FEEFBC143A')local extrafield, opcode, bytes, adressReturnHere = splitDisassembledString(lineDissassemble)print(extrafield)print(opcode)print(bytes)print(adressReturnHere)Вывод:"пустая строка"call qword ptr [7FEF06C9C88]FF 15 4888B0007FEEFBC143Aгде extrafield - комментарии к инструкции, если их нет, то будет пусто opcode - дизассемблированная инструкцияbytes - байт код инструкии adressReturnHere - адрес инструкцииlocal lineDissassemble = disassemble('7FEEFBC143A')local extrafield, opcode, bytes, adressReturnHere = splitDisassembledString(lineDissassemble)local _,_,x = string.find(opcode, '%[(.*)%]')print(opcode)print(x)Вывод:call qword ptr [7FEF06C9C88] 7FEF06C9C88 Бывают инструкции с квадратными скобками. Например, call qword ptr [7FEF06C9C88] . Чтобы вывести 7FEF06C9C88 можно использоваться следующий примерif(targetIs64Bit()) thenprint('64bit')elseprint('32bit')endEFLAGS - регистр флагов для обоих режимов (результат от сравнение, прыжков и других инструкций)32-bit: EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP, EIP64-bit: RAX, RBX, RCX, RDX, RDI, RSI, RBP, RSP, RIP, R8, R9, R10, R11, R12, R13, R14, R15Это можно сделать тремя способами.Вручную из главного окна CE, вручную в окне Дизассемблера и програмным способом.local addressCode = '7FEEFBC143A'local size = 1 -- количество байт (обычно от 1 до 8, для bptExecute обычно 1)debug_setBreakpoint(addressCode, size, bptExecute) -- поставить бряк на адрес на выполнение--debug_setBreakpoint(addressCode, size, bptAccess) -- поставить бряк на адрес на чтение и запись--debug_setBreakpoint(addressCode, size, bptWrite) -- поставить бряк на адрес на запись-- также можно не указывать размер и тип--debug_setBreakpoint(addressCode, size)--debug_setBreakpoint(addressCode)Это можно сделать тремя способами.Вручную нажав на кнопку Stop в окне срабатывания бряков. Вручную в окне Брейкпоинтов, которое можно вызывать из Окна дизассемблера (Главное окно CE-> Кнопка MemoryView->View->BreakPointList(Ctrl+ ) и програмным способом.addresslocal addressCode = '7FEEFBC143A'debug_removeBreakpoint(addressCode)Примерfunction debugger_onBreakpoint() if(targetIs64Bit()) then print(RIP) -- выведет значение регистра RIP в десятчиной системе print(EFLAGS)-- выведет значение регистра EFLAGS в десятчиной системе return 1 -- не показывать дизассемблер else print(EIP) -- выведет значение регистра EIP в десятчиной системе print(EFLAGS)-- выведет значение регистра EFLAGS в десятчиной системе return 1 -- не показывать дизассемблер end return 0 -- показывать дизассемблер с остановкой процесса (код сейчас сюда не дойдет, но обычно по умолчанию стоит 0)endlocal addressCode = '7FEEDFD143A'debug_setBreakpoint(addressCode)Вывод:8791495873594 659 Регистры RIP и EIP для режима 64- и 32-бит. У этих режимов свои регистры. Они всегда в десятичной форме. Для сравнений регистров в десятичной системе со hex значениями в строках нужен дополнительный перевод.return 1 не покажет какие-либо окна в CEreturn 0 обычно показывает окно дизассемблера с прерыванием на адресе (придет вручную отпускать процесс по F9)Также существует возможность прерываться на следующей инструкции со входом в call или без входа в негоДля этого можно исползовать функцию continueFromBreakpoint передавая в аргумент фукнцииco_run=0 - обычное продолжение отладкиco_stepinto=1 - прерваться на следующей инструкции с вхождением в callco_stepover=2 - прерваться на следующей инструкции не входя в callПример (из темы)pmAddress=getAddress("1F72152C")max = 10count = 0function debugger_onBreakpoint() -- срабатывает всегда, когда срабатывает брейкпоинт count = count + 1-- если понадобятся все регистры или некоторые, то вы можете исправить код ниже s = string.format("%3d) 0x%08X: EAX = 0x%08X EBX = 0x%08X ECX = 0x%08X",count, EIP, EAX, EBX, ECX) print(s) if count>=max then debug_removeBreakpoint(pmAddress); end --Breakpoint continue methods: co_run=0, co_stepinto=1, co_stepover=2 debug_continueFromBreakpoint(co_run) -- return 1 --I handled it so dont tell the user -- return 0 --unexpected breakpoint, show the the userendДля этого нужно объявить функцию debugger_onBreakpoint и поставить брейкпоинтУсловный бряк можно делать вручную и программным способом.Вручную нужно поставить на инструкцию F5 бряк на выполнение. Зайти в окно брейкпоинтов. И изменить условие в полях вводаНапример RIP==0x7FEEDFD143A или RIP==8791495873594function debugger_onBreakpoint() if(targetIs64Bit()) then if(RIP==0x7FEEDFD143A) then print('RIP==0x7FEEDFD143A') end end return 0 -- показывать дизассемблер с остановкой процесса (код сейчас сюда не дойдет, но обычно по умолчанию стоит 0)endlocal addressCode = '7FEEDFD143A'debug_setBreakpoint(addressCode)Вывод:RIP==0x7FEEDFD143AРегистры RIP и EIP для режима 64- и 32-бит. У этих режимов свои регистры.return 1 не покажет какие-либо окна в CEreturn 0 обычно показывает окно дизассемблера с прерыванием на адресе (придет вручную отпускать процесс по F9)print(string.format('0x%X', 8791495873594)) -- вывод 0x7FEEDFD143Aprint(string.format('0x%s', 8791495873594)) -- вывод 0x8791495873594print(string.format('%d', '0x7FEEDFD143A')) -- вывод 8791495873594print(tonumber('0x7FEEDFD143A', 16)) -- вывод 8791495873594print(getAddress('7FEEDFD143A')) -- вывод 8791495873594print(getNameFromAddress('7FEEDFD143A')) -- вывод gamedll_x64_rwdi.dll+5C143AОписание класса Disassembler Class (Inheritance: Object) createDisassembler() - Creates a disassembler object that can be used to disassemble an instruction and at the same time get more data getDefaultDisassembler() - Returns the default disassembler object used by a lot of ce's disassembler routines getVisibleDisassembler() - Returns the disassembler used by the disassemblerview. Special codes are: {H}=Hex value {R}=Register {S}=Symbol {N}=Nothing special registerGlobalDisassembleOverride(function(sender: Disassembler, address: integer, LastDisassembleData: Table): opcode, description): Same as Disassembler.OnDisassembleOverride, but does it for all disassemblers, including newly created ones. Tip: Check the sender to see if you should use syntax highlighting codes or not This function returns an ID you can pass on to unregisterGlobalDisassembleOverride() 6.4+ unregisterGlobalDisassembleOverride(id) properties LastDisassembleData : Table OnDisassembleOverride: function(sender: Disassembler, address: integer, LastDisassembleData: Table): opcode, description syntaxhighlighting: boolean : This property is set if the syntax highlighting codes are accepted or not Methods disassemble(address): Disassembles the given instruction and returns the opcode. It also fills in a LastDisassembleData record decodeLastParametersToString() : Returns the unedited "Comments" information. Does not display userdefined comments getLastDisassembleData() : Returns the LastDisassembleData table. The table is build-up as follow: address: integer - The address that was disassembled opcode: string - The opcode without parameters parameters: string - The parameters description: string - The description of this opcode bytes: table - A table containing the bytes this instruction consists of (1.. ) modrmValueType: DisAssemblerValueType - Defines the type of the modrmValue field (dvtNone=0, dvtAddress=1, dvtValue=2) modrmValue: Integer - The value that the modrm specified. modrmValueType defines what kind of value parameterValueType: DisAssemblerValueType parameterValue: Integer - The value that the parameter part specified isJump: boolean - Set to true if the disassembled instruction can change the EIP/RIP (not ret) isCall: boolean - Set to true if it's a Call isRet: boolean - Set to true if it's a Ret isConditionalJump: boolean - Set to true if it's a conditional jumpПример работы с классом после подключения к процессуfunction t2aob(t,sep) return table.concat(imap(t,byte2aob),type(sep)=='string' and sep or ' ') endfunction PrintValueType(caption, iValue) if(iValue == 0) then print(caption.." dvtNone = 0") end if(iValue == 1) then print(caption.." dvtAddress = 1") end if(iValue == 2) then print(caption.." dvtValue = 2") endendfunction PrintData(data) print(string.format('address 0x%X',data["address"])) print('opcode '..data["opcode"]) print('parameters '..data["parameters"]) print('description '..data["description"]) print('bytes '..t2aob(data["bytes"])) PrintValueType('modrmValueType ', data["modrmValueType"]) -- dvtNone=0, dvtAddress=1, dvtValue=2 print(string.format('modrmValue 0x%X', data["modrmValue"])) PrintValueType('parameterValueType ', data["parameterValueType"]) -- dvtNone=0, dvtAddress=1, dvtValue=2 print(string.format('parameterValue 0x%X',data["parameterValue"])) if(data["isJump"]) then print("Is Jump") end if(data["isCall"]) then print("Is isCall") end if(data["isRet"]) then print("Is isRet") end if(data["isConditionalJump"]) then print("Is isConditionalJump") end print('')enddisassembler = createDisassembler()print(disassembler.disassemble("0045465C")) -- is callPrintData(disassembler.getLastDisassembleData())print(disassembler.disassemble("00454684")) -- is retPrintData(disassembler.getLastDisassembleData())print(disassembler.disassemble("0045468A")) -- is jmpPrintData(disassembler.getLastDisassembleData())print(disassembler.disassemble("00454612")) -- is jePrintData(disassembler.getLastDisassembleData())print(disassembler.disassemble("00454650")) -- is mov eax,[address]PrintData(disassembler.getLastDisassembleData())print(disassembler.disassemble("0045464A")) -- is inc [address]PrintData(disassembler.getLastDisassembleData())print(disassembler.disassemble("00454661")) -- is mov edx,[ebp-04]PrintData(disassembler.getLastDisassembleData())Вывод:0045465C - E8 B73EFBFF - call 00408518 address 0x45465C opcode call parameters 00408518 description call procedure bytes E8 B7 3E FB FF modrmValueType dvtNone = 0 modrmValue 0x0 parameterValueType dvtAddress = 1 parameterValue 0x408518 Is Jump Is isCall 00454684 - C3 - ret address 0x454684 opcode ret parameters description near return to calling procedure bytes C3 modrmValueType dvtNone = 0 modrmValue 0x0 parameterValueType dvtNone = 0 parameterValue 0x408518 Is isRet 0045468A - EB F0 - jmp 0045467C address 0x45468A opcode jmp parameters 0045467C description jump short bytes EB F0 modrmValueType dvtNone = 0 modrmValue 0x0 parameterValueType dvtAddress = 1 parameterValue 0x45467C Is Jump 00454612 - 74 44 - je 00454658 address 0x454612 opcode je parameters 00454658 description jump short if equal bytes 74 44 modrmValueType dvtNone = 0 modrmValue 0x0 parameterValueType dvtAddress = 1 parameterValue 0x454658 Is Jump Is isConditionalJump 00454650 - A1 A4B54500 - mov eax,[0045B5A4] address 0x454650 opcode mov parameters eax,[0045B5A4] description copy memory bytes A1 A4 B5 45 00 modrmValueType dvtNone = 0 modrmValue 0x0 parameterValueType dvtAddress = 1 parameterValue 0x45B5A4 0045464A - FF 05 A4B54500 - inc [0045B5A4] address 0x45464A opcode inc parameters [0045B5A4] description increment by 1 bytes FF 05 A4 B5 45 00 modrmValueType dvtAddress = 1 modrmValue 0x45B5A4 parameterValueType dvtNone = 0 parameterValue 0x45B5A4 00454661 - 8B 55 FC - mov edx,[ebp-04] address 0x454661 opcode mov parameters edx,[ebp-04] description copy memory bytes 8B 55 FC modrmValueType dvtValue = 2 modrmValue 0xFFFFFFFFFFFFFFFC parameterValueType dvtNone = 0 parameterValue 0x45B5A4 Случаются инструкции на брейкпоинтах, в которых значение перезаписывается. Например. mov eax, [eax]. Чтобы решить эту проблему можно:1) Изменить тип брейкпоинта на память в настройках CE. Он будет выполниться до записи. Соответсвенно, если ставить аппаратные брейкпионты, то определить можно уже после записи. Подробноее об этом в этой теме2) Перейти в дизассемблер и определить проскакивающие адреса на инструкции. Если там будет один адрес, то определите. Но бывает много адресов.3) Провести пошаговую отладку. Т.е. перети в дизассемблер. Поставить на инстурукцию выше F5 и подсмотреть eax до записи. Если же адресов много, то смотреть по ситуации.Когда попадаются инструкции работающие со стеком, то может быть несколько способов как узнать какое значение там было.1. Один из самыйх простых способов это дважды кликнуть на инстуркцию в окне инструкций и в далоге MemInfo нажать на кнопку "S". Подробнее этой теме2. Перейти в дизассемблер и через контекстное меню с функцией "Break and trace instruction" и провести трассеровку кода установив в опциях количество выполняемых инструкций и поставив в опциях снимание стека.Продолжение следует... Изменено 7 июня, 2015 пользователем MasterGH 3 Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 2 февраля, 2015 Поделиться Опубликовано 2 февраля, 2015 Здорово. Подписался. Нужно начать изучать Lua Engine. Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 20 мая, 2015 Автор Поделиться Опубликовано 20 мая, 2015 Добавлен 14 пункт "Как работать с классом Disassembler?" 1) Можем узнать является ли инструкция: прыжком, условным прыжком, вызовом, ret-ом 2) Можем узнать opcode, например "mov" 3) Можем узнать parameters, например "edx,[ebp-04]" 2) Можем узнать modrmValueType (dvtNone=0, dvtAddress=1, dvtValue=2) 3) Можем узнать parameterValueType (dvtNone=0, dvtAddress=1, dvtValue=2) 4) Можем узнать значение modrmValue 5) Можем узнать значение parameterValue Другие функции класса Disassembler не рассмотрены 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
DarkPower2 Опубликовано 6 апреля, 2016 Поделиться Опубликовано 6 апреля, 2016 Можно ли в CE сохранить измененные инструкции в exe как в Олли? Ссылка на комментарий Поделиться на другие сайты Поделиться
Гость valeriyanka Опубликовано 16 апреля, 2020 Поделиться Опубликовано 16 апреля, 2020 Дампну тему поскольку хотелось бы узнать, возможно ли сделать такое решение чтобы можно было зациклить выполнение Lua кода при срабатывании инструкции в скрипте ? например как только увеличилось значение (inc [ebx]), то выполнить функцию Lua print('Debug value = ' + ebx) Ссылка на комментарий Поделиться на другие сайты Поделиться
imaginary Опубликовано 16 апреля, 2020 Поделиться Опубликовано 16 апреля, 2020 45 минут назад, valeriyanka сказал: возможно ли сделать такое Устанавливай точку отладчика на нужную инструкцию, потом получай регистр (хранится в переменной с таким же названием, например EBX), а затем запускай выполнение кода дальше, и так каждый раз. Все необходимые функции описаны вот в этом местечке. 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения