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

srg91

Администраторы
  • Постов

    289
  • Зарегистрирован

  • Победитель дней

    22

Весь контент srg91

  1. Ну, если есть возможность или делаешь не для себя, то конечно. Но если чисто игру пройти, то мне кажется можно и так.
  2. Да, слушай, можешь без фильтров - гномы умирают, улитки засыпают, хп не уменьшается. Просто записывай в [eax] после fstp 50 во флоате и будет отлично: Да, через следующую запись хп не уменьшается, но нужно использовать фильтры, чтобы выйти на хп игрока, а не на иную характеристику: fstp dword ptr [eax] // записываем всегда 50, ничего не вычитаем push (float)50 pop [eax]
  3. Норм, на всяких Пивзах и Волан-де-Мортах работает без фильтрации. А есть уровни с мобами? У меня какой-то супер дебаг режим включен, он и в окне запускается и могу по уровням прыгать. Попробую поискать еще отдельных мобов, не боссов.
  4. Эти инструкции работают со стеком FPU, стеком хранящим и оперирующим значениями с плавающей точкой. В CE его можно увидеть во время дебаггинга - справа в окне регистров будет кнопка со стрелкой ">": Собственно значения в этом окне и будут st(0), st(1) и т.д. Подебажив увидишь как они изменяются. Инструкция fstp dword ptr [eax] которую ты нашел - это запись здоровья из регистра st(0) в Health структуры игрока. Причем можно заметить, что попадает он в st(0) буквально на инструкцию выше - из fld dword ptr [esp+08] - эта инструкция берет значение из стека и пушит его в стек FPU. Из этого следует, что где-то раньше взяли твое текущее здоровье, отняли его и записали в [esp+08]. Можно попытаться найти это место, а можно просто после fstp (в целом я думаю можно даже не делать fstp) сделать push [MAX_HEALTH] и pop [eax]. Увы mov [eax],[MAX_HEALTH] сделать нельзя, поэтому приходится через push. Но ты можешь сделать и fld [MAX_HEALTH] и fstp [eax]. Возможно фильтры будут нужны, но у меня есть сохранение только уже на Волан де Морте, поэтому проверить быстро не могу Быстроскрипт без aobscan и фильтров:
  5. Исправил, спасибо. Я тоже так думаю, но предыдущие статьи показали, что это продвинутый уровень
  6. Спасибо, исправлено. Исправил картинки нескольких других статьях.
  7. Готово. Да, так даже удобнее, я что-то машинально. Спасибо. Спасибо за отзывы. Если есть ошибки - пишите, я исправлю.
  8. @Garik66 спросил - как использовать createthread, чтобы он не крашился. Начал отвечать и это слегка вышло за рамки простого ответа в тему, поэтому решил выделить это в отдельный топик. Я не очень хорошо знаю assembler, но попробую описать возможные причины. createthread создает поток и просит его выполнить call с адресом переданной функции. Например в данном случае createthread(my_function) в отдельном потоке выполнит call my_function: И тут вступает в дело стек, да. Вот эта штука (в Memory View вызывается через ПКМ на правом нижнем окне и выборе пункта Full stack): После выполнения call my_function мы попадаем в нашу функцию, а в стек записывается адрес, куда нужно вернуться после выполнения этой функции (my_function). Собственно за возврат куда нужно и отвечает ret. Он берет первую запись из стека и делает jmp в правильное место. Собственно сам адрес возврата виден в стеке на скриншоте - это KERNEL32.BaseThreadInitThunk+24. Без ret ассемблер вывалится за пределы функции и попытается выполнить команду - add [eax],al , хотя для нас это просто участок пустой памяти - 00 00. Ну а так как в EAX ничего приличного нет, то программа выдает ошибку - сорян, не могу записать в данный участок памяти и падает. Поэтому ret обязателен. Из этого вытекает то, что при подходе к ret в последней записи в стеке (адрес в регистр ESP) должна быть с правильным адресом возврата, который был нам передан из call my_function. И основная причина крашей в том, что регистр ESP указывает не на тот адрес при выполнении команды ret. Программа выполняет jmp на неизвестный нам адрес и крешится из-за невозможности выполнить команды, на которые она попала (как с add [eax],al). А что собственно двигает стек и адрес регистра ESP? А двигают стек наши любимые команды push и pop. Собственно, когда мы пытаемся вызывать функцию и передаем параметры через push #100, на самом деле выполняются две команды - sub esp,4 и mov [esp],#100. Первая уменьшает адрес вершины стека на 4 байта, а вторая записывает на вершину стека значение 100. А pop eax сдвигает вершину в другую сторону - делает mov eax, [esp] и дальше сдвигает вершину стека назад add esp,4. Поэтому после того, как мы сдвинули стек с помощью push при передаче параметров в функцию - после её выполнения нужно сдвинуть стек обратно! Собственно следующий пример почти 100% скрешится: Мы ввели новую функцию second_function, которая берет переданный аргумент и записывает его в EAX. Вроде бы всё просто, передали в функцию 100, получили в EAX ответ, а всё равно креш. Почему? А это видно на следующем скриншоте - на вершине стека теперь хранится значение 0x64 (=100): Теперь когда выполнится ret он попытается прыгнуть на адрес 0x00000064, а не на 0x760E8744 как должен был. Как же этого избежать? Есть несколько путей: сохраняем значение регистра esp и возвращаем его перед ret внимательно следим за стеком по мере выполнения нашей функции и к концу программы он сам будет в правильном месте В целом первый способ очень неплох. Мы можем сохранить адрес регистра esp в памяти и вернуть его прямо перед вызовом ret. Например: Но это не совсем assembler-way, поэтому я предпочитаю следить за стеком самостоятельно. Для этого нужно обращать внимание на то, как со стеком работает вызываемая функция. Например, наша функция second_function не следит за стеком. Поэтому если мы делаем push для передачи параметров, то мы так же должны сдвинуть его назад. Это можно сделать через pop указав неиспользуемый регистр или просто добавить к адресу ESP 4 байта (размер адреса в 32 битной системе, в 64-битной - это 8 байт): Собственно таким образом мы двигаем стек за функцией. И в зависимости от количества переданных push - на такое же количество нужно сдвинуть и регистр ESP. Например для 3-х аргументов нужно выполнить 3 pop или добавить к esp 3 * 4 (3 - количество сдвигов, 4 - размер инструкции): В результате мы получим следующий код: Единственное, есть небольшое исключение - функции, которые сами заботятся о стеке, после их вызова не нужно сдвигать регистр esp, просто делаем push и call. Их довольно просто отличить, посмотрим на примере следующего кода: Основное отличие - после call функции которая сама двигает стек не идет ни каких add esp,8 или не выполняются pop: Так же можно обратить внимание на код самой функции, а точнее на ret 8. Параметр 8 указывает программе, что нужно не просто взять адрес из вершины стека и прыгнуть на него, а так же после этого сдвинуть стек на 8 байт, т.е. выполнить ret и add esp,8. Собственно и получается, что add делаем не мы, а сама функция. Вот и получается, что следя за стеком и тем, как вызываемые функции заботятся о стеке - мы не получим крашей в вызове createthread.
  9. Привет. Ты можешь открыть окно AutoAssembler и нажать F1 - откроется справка по командам AutoAssembler с примерами. Минимальная информация так же есть на Wiki. По самому ассемблеру наверное ничего не могу сказать (максимум что это Intel-синтаксис), но форуме есть пара тем про асм:
  10. Sleep предоставляется CE (из main.lua): sleep(milliseconds): pauses for the number of specified milliseconds (1000= 1 sec...)
  11. Второе задание - готово. Не открывал не IDA, ничем. Использовал небольшой, как со вторым TrainMe и датой ) Под спойлером напишу как взламывал.
  12. ну тут и так уже всё рассказали, я просто дампнул процесс через PETools и открыл через ILSpy => cкопировал строку с результатом и проверил. После этого прочитал, что дампить нельзя )) Собственно, если код не обфусцирован - то после ILSpy всё как на ладони. Да и после обфусцирования тоже - Knock-knock более менее ломается даже так:
  13. Ну, раз задание с не дампить - подождем решения ) Могу скинуть в личку.
  14. Упс, сначала читай - потом делай. Сдампил, посмотрел код, после этого внимательно прочитал задание ) Эх ))
  15. Спасибо Надеюсь следующее будет чуть более удачным. Будем пробовать встроить получившийся скрипт в LUA форму с выбором машинки из выпадающего списка.
  16. Записал видео со спавном машин рядом с игроком. Получилось ужасно долго, тихо и в конце похоже пропал звук. Я решил всё равно выложить, я вряд ли смогу переснять, но если получится исправить - исправлю ссылку на видео. Плюс если что есть текстовая статья - можно выложить её (но она как и видео - огромна). В видео мы узнаем какие функции вызываются при создании автомобиля через чит-коды и набросаем небольшой скрипт, который будет создавать требуемый автомобиль перед игроком: Итоговый скрипт:
  17. [offtop]Это было далекие года назад, вряд-ли Плюс в основном писал не я, я больше по скриптам - зеркало там забабахать, сохранение инвентаря, etc. У меня упорства никогда не хватает - закончить хоть одну игру )[/offtop]
  18. у меня что-то не получается может кто еще подскажет. в целом нужно выйти на move_speed, но это всё ruby-объекты и я не уверен как они выглядят в памяти. Опять же, получилось сделать небольшой скрипт, который можно использовать в любом месте игры. Просто добавь его через Memory Viewer - Tools - AutoAssembler, вставляешь текст и жмешь - File - Assign to current cheat table.
  19. Спасибо 1. Да, конечно, можно и перенести. Просто мне кажется, такое ощущение из-за того, что это всё текстом. В целом ничего продвинутого вроде бы не происходит, но возможно я не правильно оцениваю. Поэтому мне кажется нет проблем, если перенесем - главное, что информация где-то есть ) 2. В целом я структурировал текст, возможно попробую на досуге еще раз записать видео. Если получится - дозалью в "Видео" и добавлю ссылочку в начало статьи.
  20. Привет. Получилось сделать через Ruby, лови скрипт: Собственно пример использования: Сделал через переопределение реальных классов, поэтому сработает только при включении чита в главном меню и только с новой игрой. А так же будущие сохранения можно будет загрузить только предварительно включив чит. Я думаю можно это обойти, но увы, на это у меня не было времени и скрипт превращается в кракозябры. Возможно немного позже Если надоест звук - просто убери строчку: db ' $game_system.se_play($data_system.escape_se)' #13 #10 Плюс расписал подробно, что делал - в статье. Спасибо тебе, что поднял эту тему - было интересно.
  21. Всем привет. Спасибо Vasabist, который поднял тему RPG Maker. Он попросил сделать возможность ускорения персонажа при нажатии клавиши Shift. Мы как-то давно с другом создавали игры на RPG Maker XP и я даже писал какие-то скрипты, расширяющие возможности персонажа. Тогда уже был VX, но он нам не нравился из-за мелких моделей персонажей. В общем увидел тему, нахлынуло Сами игры на RPG Maker XP - по сути большой набор Ruby-скриптов, которые выполняются движком RPG Maker с помощью Ruby-интерпретатора. Сначала я предложил Vasabist использовать фильтры, так как повторяется ситуация с lua-играми - один код работает со множеством адресов. Но потом вспомнил, что RPG Maker есть такой класс объектов как Events, который так же позволяет выполнить произвольный код при срабатывании определенных условий. Решил по-исследовать - думал выйти на функцию eval, которой скорее всего и выполняется пользовательский код. Но всё оказалось даже проще, движок RPG Maker XP предоставляет функцию RGSSEval, которая выполняет произвольный код переданный в аргументах с доступом к глобальным переменным игры. Собственно на RGSSEval выйти оказалось очень просто. Я скачал RPG Maker XP, создал на карте Event и попросил при взаимодействии с ним вывести строку "Hello, 1234444567": После запустил игру и попробовал найти данную строку. И она нашлась! Ставим бряк на access и активируем Event. И в памяти мы видим следующее: Да, ребят, это оказался memcpy. Но, это не главное. Главное что мы видим смещения адресов как RGSS104E.regex_error_code_to_str+52018, а это значит в движке RGSS104E присутствуют публичные функции, которые он любезно предоставляет нам. И открыв Memory View - View - Enumerate DLL's and Symbols мы видим RGSSEval: Осталось понять как её использовать. В целом на это довольно просто выйти, жмем два раза на функцию данном окне или просто прыгаем на неё и выполняем Memory View - Tools - Dissect Code. В появившемся окне жмем старт и видим всех, кто использует RGSSEval: Двойным нажатием на один из (Call) под RGSSEval прыгаем на место использования и видим, что у данной функции один аргумент (один push с выполняемой строкой перед вызовом) и она не двигает за собой стек (add esp,8 - восемь из-за того, чтобы не двигать стек два раза): Собственно, можем копировать эти call-ы в наш AutoAssembler и пробовать вызывать. Для тестирования я набросал следующий скрипт: Запускаем и видим следующее: Ура, функция работает как мы и ожидали. Проверить доступные глобальные переменные мы можем через print global_variables, либо обратившись к какой-нибудь из известных нам (или пока только мне..) переменных напрямую, например print $game_player. Можем творить! Собственно весь код игры на Ruby можно увидеть в меню Tools - Script Editor в RPG Maker. И скорее всего код в вашем текущем проекте будет совпадать в большом количестве игр на данном движке. Поэтому можем попытаться написать обертки для стандартных функций для выполнения наших хитрых задач: Для начала рекомендую пробовать написать обертку в своем проекте, а после внедрять её в Cheat Engine (еще лучше для начала прочитать краткий мануал по Ruby, но я так рвался закончить код что пропустил этот пункт). И сразу оговорюсь, обертки над классами будут работать только до того, как эти классы станут объектами. Поэтому наш будущий чит можно будет активировать лишь раз - до начала игры. Это поправимо, Ruby позволяет определять функции у инстансов на лету, но код становится менее лаконичным, поэтому я пошел по простому пути. Собственно для того, чтобы ускорить нашего персонажа мы должны воздействовать на атрибут move_speed класса Game_Player. Он является приватным, поэтому для взаимодействия с ним лучше сделать его видимым для всех. Опять же нам повезло и у Game_Player есть метод update, в котором мы сможем проверять нажата ли клавиша Shift, но если бы его не было, пришлось бы менять move_speed из вне. Приступим. Проще всего будет исправить Game_Player создав класс Game_Player унаследовав его от оригинала: class Game_Player < Game_Player Для изменения move_speed нам понадобится сохранять его оригинальное значение и назначать скорость бега, выделим для этого две переменные - walk_speed и run_speed. Ну и заодно сделаем move_speed публичным: attr_accessor :move_speed attr_accessor :walk_speed attr_accessor :run_speed Добавляем функции переключения скоростей и простую проверку на то, бежит ли персонаж: def start_run @move_speed = @run_speed end def start_walk @move_speed = @walk_speed end def running? return (@move_speed == @run_speed) end Осталось установить начальные значения переменным и добавить проверку на нажатый SHIFT. Для этого обернем функции initialize и update класса Game_Player с помощью функции alias: alias orig_initialize initialize def initialize orig_initialize @walk_speed = @move_speed @run_speed = 5 end Видите? Мы попросили обозначить родительский initialize как orig_initialize и вызывали его в нашей функции initialize. Скорость бега я установил на 5 (оригинальная скорость 4). Если увеличивать больше - персонаж просто летает и им неудобно управлять. Тоже самое сделаем с update. На нажатый SHIFT проверить очень просто - движок предоставляет объект Input, который знает нажата требуемая клавиша или нет (её я нашел в оригинальных скриптах игры): def update if Input.press?(Input::SHIFT) if !running? start_run end else if running? start_walk end end orig_update end Собственно и всё - скрипт готов. Если вы добавите его в оригинальные скрипты проекта, при нажатии клавиши SHIFT персонаж будет идти быстрее. Осталось просто заменить наш print "Hello, World" на получившийся скрипт. Выглядеть это будет следующим образом - каждую строку обрамляем в db 'xxxxx', где xxxx - строчка с кодом. В конце добавляем #13 #10 (каждое число через пробел) - это обычный Enter в конце строки (\n\r - если так привычнее). Я использовал для этого Sublime Text и его мультикурсоры, но можно воспользоваться простой заменой. Ну и не забываем 0 в конце, как конец текста. В итоге мы получим: Теперь просто заменяем db 'print "Hello, world!"' на получившийся код и можем запускать в главном меню игры Итоговый скрипт: Итоговый скрипт на Ruby: Видео работы скрипта: Итого (или рубрика "О проблемах"): Скрипт прекрасно работает если включать его в меню, до запуска игры. Но не получится загрузить уже существующие сохранения, так как оно выполняет через дампы и нашему коду там просто не откуда взяться С этим можно бороться подключая код на-лету, но это уже в следующей серии. Так же скрипт ломает будущие сохранения - игра не сможет загрузиться, если чит не был включен. Увы, это частая проблема игр использующих моды, а мы фактически этим и занимались. Вот и всё, народ )
  22. Собственно это я и хотел предложить Для затравки кину данный скрипт, ну и пока сам попробую накидать изменение move_speed скриптом:
  23. Не уверен, что подскажу про разные координаты, но с SHIFT сделать довольно просто. Сначала думал написать про хоткеи на скрипт, но они переключают скрипт каждые сколько-то миллисекунд. В общем если ты сделаешь правильную фильтрацию, предлагаю тебе вынести значение скорости в отдельную "переменную" (в скрипт speedUp). После чего по таймеру проверять - нажат ли SHIFT:
  24. Привет. Я думаю ты прав и эта инструкция работает с несколькими адресами. Возможно тебе поможет видео от Garik66, в котором он объясняет как можно написать фильтр для такой инструкции:
  25. Всем привет. Сегодня я расскажу о том, как создавать нужный автомобиль с помощью CE в GTA Vice City. Как и говорил partoftheworlD, это очень просто, но раз возникают вопросы, я решил написать небольшой гайд. Собственно, выйти на функции создания автомобилей очень просто - в игре есть читы, которые позволяют "вызывать" себе танк, катафалк и другие автомобили. Поэтому выйдя на функцию обработки чита - мы найдем функции для создания автомобиля. Приступим. Для начала вспомним несколько читов для создания автомобилей. Я выбрал чит-код создания катафалка - THELASTRIDE , но так же можно использовать наш любимый чит создания танка - PANZER. Собственно ввод кодов - это операция по сохранению текущего введенного символа в строку и последующая проверка получившейся строки на соответствие. Каждый введенный символ записывается как в стек, поэтому в реальности эти читы выглядят как REZNAP и EDIRTSALEHT. Но если поискать данный текст, мы его не найдем. Как же так спросите вы? На самом деле функция обработки чит-кодов срабатывает на вводе последней буквы, поэтому проще всего просто искать часть введенного кода. Выйдем в меню игры, введем чит-код THELASTRIDE и попробуем поискать подстроку TSALEHT (не забываем переворачивать чит-код) в игре: Ура, мы нашли текст по адресу 00A0F94F. Не будем его запоминать, он нам не пригодится, а сразу посмотрим, что же творится в памяти. Нажмем на адрес правой кнопкой мыши и выбираем Browse this memory Region. В памяти сразу поднимемся на пару строк вверх, обычно строки длиннее чем мы искали: Ура, видим часть введенного нами кода. Так же могут присутствовать и другие куски текста, например на скришоте я немного побегал перед вводом чит-кода. Теперь мы видим, что в реальности строка ввода чит-кода больше чем мы ожидали, а её начало находится там, где присутсвуют первые символы DW DIRTSAL, т.е. по адресу 00A0F94A. Добавим этот адрес в табличку (размер строки вычислен эмпирическим путем - посчитал не нулевые байты): Теперь если в игре мы побегаем или введем другие чит-коды, увидим как они укладываются в памяти. Посмотрим же, что использует этот буфер - попросим CE найти всех, кто использует данный адрес при вводе чит-кода Сначала жмем ПКМ на адресе в главном окне CE и выбираем Find out what accesses this address: Теперь переключимся в игру и введем любое слово, например HELLOGHL. Этим мы отсеим инструкции, которые используют наш адрес при обычном вводе. В окне появились следующие инструкции, мы будем их игнорировать: Теперь введем чит-код на создание автомобиля - THELASTRIDE. Внимательно следим за окном с инструкциями, т.к. в процессе ввода у меня появились еще несколько инструкций, которые используют адрес, но не влияют на появление машины. После ввода последней буквы видим следующую картину: Уже можно заметить несколько функций, выполняющихся один раз. Для того, чтобы отсеять лишние инструкции, попробуем ввести другой чит-код, не относящийся к созданию автомобилей, например LEAVEMEALONE: Ура, из предыдущих инструкций у нас осталось одна - инструкция mov byte ptr [00A0F94A] по адресу 004AC84A. Давайте выделим её и нажмем Show disassembler, чтобы увидеть, где она выполняется: Собственно мы видим некое условие, после которого в наш буфер записывает 0x20, он же 32 он же пробел и вызывается некая функция gta-vc.exe+AE7C0 с аргументом AC. Заочно можно нас поздравить, потому что мы нашли нужную нам функцию, осталось только убедиться в этом. Попробуйте тем же способом найти чит-функцию для создания танка. Я её уже нашел, давайте посмотрим: Видно, что функция находится немного в другом месте, но отличие состоит только в том, какой аргумент передается в функцию - push записывает в стек A2 , вместо AC. В остальном код условия идентичен и я предлагаю попробовать просто вызывать функцию gta-vc.exe+AE7C0 с разными параметрами. Для вызова функции мы будем использовать AutoAssembe и функцию createthread. Зайдем в AutoAssembler (откроем Memory Viewer, меню Tools - AutoAssemble) и набросаем следующий скрипт: Добавим его в таблицу через File - Assign to current cheat table и можно закрывать окно Auto assemble. Скрипт выделяет в памяти игры 64 байта для себя, после чего записывает туда инструкции, которые мы скопировали из оригинальной игры и просит createthread выполнить их. Обратите внимание, что аргументы в данную функцию передаются как push AC. В оригинальной игре после call мы можем заметить инструкцию pop ecx. Это значит, что функция не трогает стек и мы сам должны двигать его за ним (т.е. откатить push AC). Поэтому после функции выполняется pop и мы обязательно должны его скопировать, иначе ret будет пытаться вернуть поток не куда-то по адресу 0xAA123456, а по адресу 0x000000AC. Почему pop использует ecx? В данном случае - потому что левая пятка компилятора так решила и это ни на что не влияет (ecx спокойно можно заменить на eax и другие регистры). Ура, скрипт в нашей таблице, давайте же активируем его! Смотрим на результат и...: Наш скрипт вызвал чит-функцию создания катафалка! Наверное вы уже догадались, что если мы заменим push AC на push A2 мы увидим заспаунившися танк? Получается, что AC - это катафалк, а A2 - танк. Поискав GTA Vice City Vehicle IDs, мы найдем, что действительно, A2 = 162 - это Vehicle ID танка, а AC = 172 - катафалк. Попробуем указать свой ID, один из списка, например 168 (такси). Передача аргумента превратится в push #168: Можно сказать, что наш скрипт работает! Осталось вынести ID модели как переменную: Я удалил из скрипта функцию вызова потока, чтобы переместить её в отдельный скрипт: Теперь можно добавить в таблицу адрес our_vehicle_id, в который после записи скрипта будет записано число 206 (ID наикрутейшего Sabre Turbo). Это число отвечает за то, что мы передаем в чит-функцию и меня его мы будем менять ID заспаунившегося автомобиля: Результат: И о проблемах Скрипт фактически вызывает чит-код, поэтому мы начинаем слыть читерами, рейтинг в игре уменьшается и т.д. Это довольно легко обходится, т.к. если мы провалимся в функцию gta-vc.exe+AE7C0, перед самым ret есть две инструкции - add [gta-vc.exe+5B4F94],000003E8 и mov byte ptr [gta-vc.exe+60FB37],01 . Первая накидывает очки читерства (они хранятся отдельно) и устанавливает флаг - "этот парень использовал коды". Если их занопить - игра никогда не узнает о ваших проделках. И вновь, потому что мы просто вызываем функцию чит-кода, она спаунит машины только на дорогу. Она находит ближайшую RoadPoint, прибавляет по оси Z несколько метров и спаунит авто. Поэтому создать авто прямо перед игроком так просто не получится. Ну и основная проблема - это то, что в данной функции использует конструктор только для автомобилей. Он так же используется для вертолетов, но вот байки и лодки с ним заспаунить не получится (заспаунится, но крешится при посадке и выглядит, кхм...): О том, как обойти эти проблемы и вызывать уже конструкторы классов мы узнаем в следующем уроке С видеоформатом гайдов мне сложно работать (довольно тяжело как смотреть, так и записывать их), поэтому уроки в текстовом формате.
×
×
  • Создать...

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

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