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

keng

Ветераны
  • Постов

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

  • Посещение

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

    55

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

  1. Эх, вот была бы просто мини-гоночка без навешанного сверху СЕ-упаковщика - тогда да, а иначе слишком долго выйдет ковыряться. Я предположил, что цифр в памяти нет, а по окну туда-сюда гоняются картинки, их координаты сравниваются и строится табличка. Понадобятся координаты окна, этих картинок да функция BitBlt из gdi32.dll, а дальше - чуть раскопать. Больше меня ужаснул даже не размер бинарника в 16.5 Мегабайт, ну мало ли там картинки в bmp или звук в wav, фиг с ним, а то, что оно жрет 15-20% загрузки ЦПУ каким-то циклом (видимо, отрисовкой этих самых машинок) и 400+ Мегабайт оперативной памяти. Это вообще, гм, "слов нет". Зачем, кстати, внутрь трейнера запихивается сборка СЕ? Я давно не смотрел код и не помню, как это там устроено, но неужели действительно это СЕ + .cetrainer + упаковщик? Это же жесть полная.
  2. Обсуждать личку на всеобщем обозрении - не очень прилично.
  3. Выведет, если одна и та же функция используется и для врагов и для твоего персонажа. Во-вторых, если поставить брейкпоинт на значение патронов. Третий вариант - немножко изучить, как работают игры в целом. Скажем, для работы с клавиатурой есть WinAPI-функции. А еще есть DirectInput - библиотека для устройств ввода-вывода в составе DirectX. И еще есть всякое разное. Туда тоже можно ставить брейкпоинты и отлаживать.
  4. Привет! Когда ты отлаживаешь игру и в отладчике ставишь брейкпоинт на, скажем, адрес координаты по оси X персонажа, то отладчик тебя отправляет в эту самую функцию. Просто на уровне ассемблера там возникает достаточно глубокая вложенность функций одна в другую (т.к. в ассемблере по факту функций нет) и внешне это выглядит примерно так: Инстанс класса игрока -> Указатель на таблицу его методов -> Указатель на функцию "походить" -> Функция "походить" (ось1, ось2, ось3) -> Подфункция "поменять значение оси в свойствах персонажа" В последней ты и приземляешься, когда отлаживаешь игру. Что делать? Внимательно прочитать написанное на экране (чаще всего функции самого низкого уровня очень короткие - в 1-2 экрана), а дальше выполнить код до первого возврата (ret) и читать опять. И потом еще. В конце концов ты наткнешься на что-нибудь в духе: PUSH EAXPUSH EAX + 0x4PUSH EAX + 0x8CALL 0x123456 Это и будет та самая функция. Скажем, что в EAX хранится массив координат персонажа, тогда 0x123456 - адрес функции "походить". Как-то так.
  5. Может быть, я не совсем правильно понял вопрос. Есть игра, в ней есть некоторое значение, адрес которого нужно найти скриптом, а не указателем. Сначала я удивился, что не ищутся указатели. Ищутся, пусть их и много. Это еще не самый крутой пример и уровней вложенности, если я правильно помню, всего три. Потом я написал скрипт, который приведу чуть ниже. Взял адрес, поставил на него брейкпоинт на чтение, отладчик выдал мне две инструкции. Выбрал первую. Дальше скрипт: [ENABLE]aobscanmodule(INJECT,PlantsVsZombies.exe,03 82 * * * * 39 44 24 * 0F 9E C0 C2 04 00)alloc(newmem,$1000)registersymbol(INJECT)globalalloc(addr_suns,4)newmem: ADD EAX,[EDX+00005578] // Оригинальная инструкция PUSH EAX // Сохраняем регистр EAX в стеке LEA EAX,[EDX+5578] // Загружаем в него адрес нужного нам значения MOV [addr_suns],EAX // Кладем его в переменную POP EAX // Восстанавливаем регистр EAX RET // И выходимINJECT: CALL newmem NOP[DISABLE]INJECT: db 03 82 78 55 00 00 unregistersymbol(INJECT) dealloc(newmem) dealloc(addr_suns)
  6. Привет! Насколько я помню, в том движке фон - картинка и есть, так что тебя интересуют строчки 227 (загрузка картинки из ресурсов), 245-247 (где создается копия экранного буфера окна и на него копируется загруженная картинка) и, наконец, строчка 296, где этот буфер выводится в окно. Все это - в файле [trn.asm]. Как сделать кнопку с картинкой можно посмотреть, например, [тут]. Автор использует MASM.
  7. Настольный компьютер - 1080p, ноутбук - 1280х800. Смотрю в окошке, потому что привык - на ноутбуке все обернуто в тайловый wm.
  8. Ты статью-то мою прочитал? Там даже готовый пример есть.
  9. Обычно тема или содержит N вопросов и закрывается, получив на них вопросы. Второй вариант - когда тема более собирательная - например тема SER[G]ANT'а о сборках Cheat Engine - она пополняется всякий раз, когда релизится новая версия. Третий вариант - темы-карты, которые постепенно наполняются ссылками и материалами по какому-то более общему вопросу. Одним, иногда - многими людьми. Мне кажется, что данная тема принадлежит к первой категории, так что нет особого смысла держать ее открытой. У кого возникнет похожий вопрос - прочитает эту тему, не найдет в ней ответ, создаст новую. Вопрос будет похожий, но уже другой. На него и будут отвечать. А если вопросов по проигрыванию звука в СЕ вдруг накопится большое количество - то или все темы объединятся в одну, или будет создана тема-карта со ссылками на них.
  10. keng

    TimeShift

    Крутая игра, крутой трейнер, так еще и на ассемблере! Просто все, как я люблю. (:
  11. Если вопрос исчерпан - можно ли закрывать тему?
  12. В приведенном тобой кусочке кода нет оригинальной и неоригинальной функции. Она есть только одна. И она просто вызывается. Без остальной части кода угадывать трудновато. CreateRemoteThread просто создает поток и передает управление на выбранный тобой адрес памяти. Если что-то где-то заменяется - то не здесь. NullAlex: позволил себе исправить твое сообщение, чтобы оно отображалось именно со шрифтом "Terminus", т.к. была допущена ошибка в теге
  13. Судя по коду, он вызывает функцию по адресу 0x0044FE60 с аргументами (0, 0, 0, -1). Пятый аргумент передается прямо через ECX и это, на мой взгляд, странновато, но мало ли что там себе думает компилятор. Заменяется функция где-то в другом месте. Обычно или заменяется указатель на функцию, или первые 5 байт функции меняются на "JMP 00112233", где 00112233 - адрес функции, которую нужно вызывать хакеру.
  14. Во-первых, вызов Sleep не нужен. Во-вторых, дочитай документацию хотя бы по [этой] функции. Читать - это круто! Нет, правда.
  15. Ну ты такой снял снэпшот всех тредов, используя TH32CS_SNAPTHREAD, а потом побежал в цикле через Thread32First, которая возвращает тебе THREADENTRY32, у которой есть поле th32OwnerProcessID. Его-то ты и сравниваешь с идентификатором нужного тебе процесса. Там так и написано.
  16. Привет! Отвечу пока что на второй вопрос. При запуске программе передаются аргументы командной строки. Нулевой аргумент - это полный путь до исполняемого файла. Еще можно, как вариант, положить dll рядом с исполняемым файлом игры или в папку system32.
  17. Блин, точно! Как-то я забыл про этот пункт. А ведь раньше уже находил его. Спасибо.
  18. А ты попробуй для начала просто связать два файла вместе. Напиши свой инжектор DLL, научись эту DLL запихивать в чужой процесс, внутри самой DLL - разберись, как работать с памятью изнутри нужного адресного пространства, затем - как писать инъекции кода сразу внутри DLL. После - как работать с shared-секцией памяти или сообщениями, чтобы инжектор и DLL друг друга понимали. А потом уже и объединить можно. Способов, как я уже сказал, масса - их можно и банально нагуглить. Если не понимаешь, как подобраться к крупной задаче - разбивай ее на мелкие и решай их.
  19. DLL можно встроить в EXE различными способами, а сконтачить их вместе можно, например, через общую секцию памяти. Или через сообщения, как это делает подавляющее большинство программ в Windows.
  20. Люблю позалипать в список пользователей! Можно ли как-то его упорядочить? Не очень удобно мотать 20 страниц пользователя "Гость".
  21. Привет! Я попробую объяснить, но начну немного издалека. Вот есть игра: ****** *Игра* ****** Это - загруженный в оперативную память и запущенный на исполнение бинарный файл. Или исполняемый, как угодно. EXE, в общем. Он состоит из модулей - помимо самого исполняемого файла в его адресное пространство подгружены всякие разные DLL, нужные для его работы. Так как написан он был на компилируемом языке, то на выходе бинарник получился состоящим из так называемых машинных кодов. Если их записать на более понятном для человека представлении, то получится ассемблерный код. Обратно из машинных кодов исходник не получить, потому что все имена функций, переменных и все такое прочее было выброшено компилятором за ненадобностью - ему гораздо быстрее и удобнее ориентироваться в цифрах. Дизассемблированный же листинг мы получить вполне можем. Дизассемблированный - это для архитектуры нашего процессора, то есть он все равно не совсем точный, а примерный. Работать, само собой, работает, тут без вопросов. Адресное пространство, которое я уже выше упомянул - это довольно абстрактная область оперативной памяти, которая зарезирвирована за программой. Она ее видит в виде простыни из 4Гб или чуток побольше адресов, идущих подряд, все это делится на блоки, а блоки - на страницы, которые в нужный момент при помощи магии операционной системы адресуются на физическую память. Такие вот дела. Для того, чтобы изменить ход работы игры, мы можем менять только доступное нам - адресное пространство и код, который мы получили из дизассемблера и\или отладчика. Первый способ - быстрый и удобный, чего уж. Сканер памяти запустил, адрес нужный нашел, меняй - не хочу. А если адрес динамический - посидел 5 минут и нашел указатель. Беда в том, что подходит этот способ далеко не всегда и не для всего. Второй же - куда более муторный, но куда более крутой. Ты сидишь, ковыряешься в отладчике в тщетных попытках понять, что же делает игра. Сидеть и рассматривать примерный дизассемблерный листинг, в котором есть только команды, регистры и адреса - это жесть, как утомительно. Особенно если делать это по 10-12 часов в сутки. Но результат стоит того. При должной сноровке и должном терпении можно хоть всю игру переписать на свой лад. Опции доступны _любые_, без шуток. На что фантазии и времени хватит - то и будет. Менять код игры можно прямо из отладчика, тут же получая результат (или вылет), можно - из Cheat Engine или аналогичной программы, делая скрипты на местном диалекте ассемблера, а можно написать свою программу, которая будет все это делать под твоим руководством. Последний вариант - самый крутой. К счастью, чудо-фирма Microsoft в некоторой мере адекватна и выпустила для своей линейки операционных систем набор API - то есть набор функций, позволяющих приказать этой самой оси сделать нечто полезное. Среди сотен этих функций есть парочка очень необходимых нам - например, возможность читать и менять память в чужом адресном пространстве. Есть в игре, допустим, такая инструкция: DEC EBX Которая при каждом своем срабатывании уменьшает текущее значение регистра EBX на единицу. Можно теперь включить фантазию и нафантазировать, что это - часть функции, ответственной за стрельбу. И срабатывает она каждый раз, как игрок в игре стреляет, отнимая у него патроны. Нас такая ситуация не очень устраивает, так что мы в нашей крутой программе вызываем API-функцию для записи в память и записываем по адресу, где лежит инструкция, опкод инструкции NOP, то есть число 0x90. Почему число? Потому что в памяти лежат цифры, а не DEC или NOP. Их нам выдают отладчик и дизассемблер исключительно потому, что они - добрые и щедрые ребята. В общем, записали, патроны не меняются - круто! Через пару недель мы понимаем, что можно не просто выключать те или иные куски кода, но и дописывать их или переписывать. В какой-то момент сталкиваемся с тем, что у нас доступно для изменения 5 байт, а нужно нам их 25. Тут на помощь приходит техника под названием code injection, которая, как видно из двух английских слов, внедряет кусок кода. Удивительное дело, но при ее использовании нам необходимо всего 5 (а иногда - совсем всего 2) байта. Мы выделяем (или находим) кусок памяти, который игра не использует, записываем туда наши 25 байт, а на месте игровой инструкции записываем переход на нашу память. В итоге игра послушно выполняет то, что мы задумали. Я на днях написал статью, которая поверхностно описывает данную технику. Там как раз используется внешняя программа и вызовы API-функции для записи в память. А писать нужно, как мы помним, цифры. Аж в 16-ричной системе счиления, которую компьютер любит, но не всякий человек. Со временем трейнеростроители поняли, что это не очень уж и удобно, и стали думать. Додумались они до того, что описывал в своих работах Coder. Можно же не использовать внешнюю программу, а внедрить в адресное пространство игры свой исполняемый модуль, скажем, DLL! В едином адресном пространстве будет сильно удобнее - во-первых, не нужны API-функции записи в память - мы можем напрямую ее изменять и копировать, были бы права на это. Во-вторых - можно записать нужную ассемблерную вставку прямо в DLL и делать инъекцию кода, используя адрес этой вставки, обернув ее в функцию. Это позволяет писать намного быстрее и не очень заморачиваться со смещениями и цифрами вместо команд - большинство компилируемых языков программирования поддерживают ассемблерные вставки в коде программы. На этом этапе EXE-файл трейнера выполняет роль красивой обертки, цель которой - показать картинку, написать имя автора и то, какой он крутой и молодец, а затем внедрить в адресное пространство игры эту самую DLL, которая сделает всю дальнейшую работу. Такие вот внедрители DLL и называются инжекторами, только не кода, а DLL. С этой частью, пожалуй, немного разобрались. Осталась самая капелька. Во-первых, Coder использует всякие библиотеки - почему так? Ну, еще он использует C++. А я вот использую С и библиотеки использую, когда мне совсем уж лень писать собственный велосипед. Ему удобнее так, мне - эдак. Дело вкуса. Не требование. Во-вторых, по поводу метода внедрения кода тебе уже ответил Xipho, пока я писал вот это вот. Спасибо всем, кто насладился некоторым количеством букв.
  22. А теперь сходи и почитай про представление отрицательных чисел в 16-ричной системе счиления. (;
  23. Гляди: JMP 1 то же самое, но в опкодах: E9 01 E9 - опкод команды, 01 - смещение для прыжка. В 16-ричной системе счисления. Как его считать? Арифметикой.
×
×
  • Создать...

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

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