Создать структуру программно
-
Создать структуру программно
Пример, что будет в конце записи
Нужен поинтер и процесс.Запустим туториал из Cheat Engine из меню Health->Cheat Engine Tutorial.
Прохождение туториала подробно описано здесьПодключаемся к процессу идем на 8-мой шаг. В руководстве есть поинтер
[[[["Tutorial-i386.exe"+XXXXXX]+C]+14]+0]+18
Вместо XXXXXX может быть любое смещение. Поэтому надо бы поискатьНашил адрес.
Поставили бряк.
Нашли esi
Поставили брякВышли сюда
0042595E - A1 60D65F00 - mov eax,[005FD660] { [0183E9E8] }
00425963 - 8B 40 0C - mov eax,[eax+0C]
00425966 - 8B 40 14 - mov eax,[eax+14]
00425969 - FF 30 - push [eax]
//...
00425F3C - 89 46 18 - mov [esi+18],eax// Вот такой получили поинтер
[[[[005FD660]+C]+14]+0]+18
Поинтер получили, дальше пойдет Lua.Открываем Lua консоль и проверяем поинтер
Выведем адрес и его значение
\-- getAddress позволяет получить адрес. Если есть квадратные скобки, то и значение адреса local address = getAddress("[[[[005FD660]+C]+14]+0]+18") local value = getAddress("[[[[[005FD660]+C]+14]+0]+18]") local text = string.format('%X = %s',address,value) -- Показать диалог. В defines.lua можно посмотреть переменные mtInformation, mbok messageDialog(text, mtInformation, mbok) --> Вывод "018250E0 = 3289"
Поинтер верный.
Другой вариант читать значение поинтера примерно такой
local address = getAddress("game.exe") address = readPointer(address + 0x123) address = readPointer(address + 0x456) local value = readFloat(address + 0x789)
Есть и такие варианты
value = readInteger("[[[[[[[[[witcher3.exe + 028F3F60] +0] +18] +20] +40] +40] + 1c0] +10] +28]") value = readFloat("[[[[[[[[[witcher3.exe + 028F3F60] +0] +18] +20] +40] +40] + 1c0] +10] +28]") value = readDouble("[[[[[[[[[witcher3.exe + 028F3F60] +0] +18] +20] +40] +40] + 1c0] +10] +28]")
Теперь самое интересное — создание структур с помощью Lua
Построим структуру [[[[005FD660]+C]+14]+0]+18. На +18 будет наш адрес.
Сначала построим один уровень —"005FD660"\--Создаем пустую структуру и добавляем её в глобальный список myStructure = createStructure('MyStructure') myStructure.addToGlobalStructureList() -- Автоматически заполняем -- Праметры структуры: адрес, смещение и размер myStructure.autoGuess('005FD660', 0, 4096) -- Создать окно и внести поле адреса local structureFrm = createStructureForm('005FD660') -- Выбрать структуру на форме. Через UI клик по индексу последней созданной структуры local structureIndex = getStructureCount() - 1 structureFrm.Menu.Items[2][structureIndex+2].doClick()
Если выполнить скрипт выше, то мы построим структуру одного уровня.
Построим теперь структуру двух уровней "[005FD660]+C". Второй уровень нужно развернуть
Для этого после создания структуры создам дочернююmyStructure2 = createStructure('MyStructure2') myStructure2.autoGuess('[005FD660]', 0, 50) -- И поместим её по индексу local offset = 0 myStructure.getElementByOffset(offset).setChildStruct(myStructure2) -- Чтобы развернуть список поитеров. К сожалению разворачивается только два уровня structureFrm.Menu.Items[1][6].doClick()
Итого получается такой скрипт до второго уровня
\-- Создаем пустую структуру и добавляем её в глобальный список myStructure = createStructure('MyStructure') myStructure.addToGlobalStructureList() -- Автоматически заполняем -- Праметры структуры: адрес, смещение и размер myStructure.autoGuess('005FD660', 0, 4096) \------------------ myStructure2 = createStructure('MyStructure2') myStructure2.autoGuess('[005FD660]', 0, 50) local offset = 0 myStructure.getElementByOffset(offset).setChildStruct(myStructure2) \------------------ -- Создать окно и внести поле адреса local structureFrm = createStructureForm('005FD660') -- Выбрать структуру на форме. Через UI клик по индексу последней созданной структуры local structureIndex = getStructureCount() - 1 structureFrm.Menu.Items[2][structureIndex+2].doClick() structureFrm.Menu.Items[1][6].doClick()
Создадим и развернем весь указатель [[[[005FD660]+C]+14]+0]+18
Не будем делать через цикл, чтобы не усложнятьmyStructure2 = createStructure('MyStructure2') myStructure2.autoGuess('[005FD660]', 0, 50) local offset = 0x0 myStructure.getElementByOffset(offset).setChildStruct(myStructure2) myStructure3 = createStructure('MyStructure3') myStructure3.autoGuess('[[005FD660]+C]', 0, 50) offset = 0xC myStructure2.getElementByOffset(offset).setChildStruct(myStructure3) myStructure4 = createStructure('MyStructure4') myStructure4.autoGuess('[[[005FD660]+C]+14]', 0, 50) offset = 0x14 myStructure3.getElementByOffset(offset).setChildStruct(myStructure4) myStructure5 = createStructure('MyStructure5') myStructure5.autoGuess('[[[[005FD660]+C]+14]+0]', 0, 50) offset = 0x0 myStructure4.getElementByOffset(offset).setChildStruct(myStructure5)
Полный скрипт
\-- Создаем пустую структуру и добавляем её в глобальный список myStructure = createStructure('MyStructure') myStructure.addToGlobalStructureList() -- Автоматически заполняем -- Праметры структуры: адрес, смещение и размер myStructure.autoGuess('005FD660', 0, 50) \------------------ myStructure2 = createStructure('MyStructure2') myStructure2.autoGuess('[005FD660]', 0, 50) local offset = 0x0 myStructure.getElementByOffset(offset).setChildStruct(myStructure2) myStructure3 = createStructure('MyStructure3') myStructure3.autoGuess('[[005FD660]+C]', 0, 50) offset = 0xC myStructure2.getElementByOffset(offset).setChildStruct(myStructure3) myStructure4 = createStructure('MyStructure4') myStructure4.autoGuess('[[[005FD660]+C]+14]', 0, 50) offset = 0x14 myStructure3.getElementByOffset(offset).setChildStruct(myStructure4) myStructure5 = createStructure('MyStructure5') myStructure5.autoGuess('[[[[005FD660]+C]+14]+0]', 0, 50) offset = 0x0 myStructure4.getElementByOffset(offset).setChildStruct(myStructure5) --[[[[005FD660]+C]+14]+0]+18 \------------------ -- Создать окно и внести поле адреса local structureFrm = createStructureForm('005FD660') -- Выбрать структуру на форме. Через UI клик по индексу последней созданной структуры local structureIndex = getStructureCount() - 1 structureFrm.Menu.Items[2][structureIndex+2].doClick() structureFrm.Menu.Items[1][6].doClick()
Результат
Ну и на закуску.
Допустим мы знаем что по адресу X будет всегда тип float (как X,Y,Z координаты). Но расструктуризация будет показывать другой тип, дизассемблер будет показывать другой тип — 4 байта. Что делать?
И сразу еще допустим double тип всегда хотим как float тип (в виде комментов в дизассемблере или в расструктуризации)
Воспользоваться следующей функцией
-- Может менять тип адреса x в окне дизассемблере в комментариях, когда в инструкции существует адрес
-- Может менять тип адреса в окне расструктуризации
-- Изменив тип, будет ображаться значение другого типа
onAutoGuess(function) :
Registers an function to be called whenever autoguess is used to predict a variable type
function override (address, ceguess): Return the variable type you want it to be. If no change, just return ceguess
Ну и вот два примера-- Пример замена типа по типу function iKnowBetter1(address, ceguess) if (ceguess==vtDouble) then return vtDword else return ceguess end end -- Пример замена типа по адресу function iKnowBetter2(address, ceguess) local someAddress = 0x1424AC6D0 if (address == someAddress) then return vtSingle else return ceguess end end onAutoGuess(iKnowBetter1) onAutoGuess(iKnowBetter2)
-
Решил попробовать написать CE Lua скрипт, который прочесывает структуру и в Dissect data/structures окне создавал бы структуру только со смещениями, с которыми код работает, т.е. читает или пишет.
Например, мой персонаж прогуливается по городу, а каждые 200 мс ставится брейкпоинт на смещение +1 до гипотетической N границы структуры (например до 4096).
Я не успел сделать определение типа, но смещения внутри структуры получить я успел.
Итак, находим начало структуры любой. Запускаем Lua скрипт и просто что-то делаем в игре. Потом вылазит текст с дизассемблированными инструкциями и смещениями. Уже по этим логам можно определить вручную
-
к каким смещением было обращение (адреса по ним мы и будем менять или сравнивать структуры между собой позже)
-
по виду инструкции и соседнему смещению уже примерно можно определить тип данных и их размер
Осталось сделать определение типа, а это не так уж долго сделать и осталось взять код из предыдущих записей в блоге формирования структуры в окне dessect data. Так мы получим структуру только с активными смещениями, (а зачем нам пассивные?) и определим в них тип, я надеюсь определим правильно
Пример кода, который я использовал
addressStructure1 = 0x412E0200 --> адрес начала структуры в любой игре sizeStructure = 100 --> гипототический размер структуры 100 для быстрых поисков, по умолчанию 4096 indexStructure = 0 --> индекс внутри структуры, который будет перемещаться вместе с breakPointAddress breakPointAddress = 0 --> адрес, на который сейчас постален брейкпоинт waitTimeTillBreak = 300 --> частота активности смещеиня resultText = '' --> конкатенация частей текста в этой переменной \-- Функция пытается поставить брейкпоинт на следующий байт в структуре function TryNextSetBreakPointToAddress() debug_removeBreakpoint(breakPointAddress) indexStructure = indexStructure + 1 if indexStructure > sizeStructure then debugTimer.Interval = 1000 debugTimer.Enabled = false debugTimer.destroy() debug_continueFromBreakpoint(co_run) print(resultText) --> вывод результата с завершением отладки return end breakPointAddress = addressStructure1 + indexStructure debug_setBreakpoint(breakPointAddress, 1, bptAccess, bpmDebugRegister) debug_continueFromBreakpoint(co_run) end \-- Любимая функция снятия отладочных данных function debugger_onBreakpoint() -- проверить обращение к структуре prevAddress = getPreviousOpcode(RIP) resultText = resultText..string.format('Offset: + %X : %s', indexStructure, disassemble(prevAddress)) .. '\r\n' TryNextSetBreakPointToAddress() return 1 end \-- Простой таймер debugTimer = createTimer(nil, false) debugTimer.OnTimer = function(timer) TryNextSetBreakPointToAddress() end debugTimer.Interval = waitTimeTillBreak debugTimer.Enabled = true breakPointAddress = addressStructure1 + indexStructure debug_setBreakpoint(breakPointAddress, 1, bptAccess, bpmDebugRegister)
-