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

Вопрос По Внедрению Кода В Процесс Игры


Рекомендуемые сообщения

Всем доброго времени суток. Не знал куда поместить тему, думаю в этом разделе она будет уместна.

Заранее прошу прощения за огромное количество текста, и так сократил по максимуму, но наплыв вопросов очень большой.(

Абстрактные представления имею, но не могу собрать пазлы в единую картину.

И так.. Просмотрел видеокурс уроков Coder'a по написанию трейнера с нуля, видно, парень очень хорошо шарит, но к сожалению не очень доходчиво объясняет, но даже не смотря на это Огромнейшее ему спасибо! что-то новое я для себя все-таки взял.

1) Допустим, зачем использовать такие библиотеки как AsmJit, beaengine, если можно использовать ассемблерные вставки?

2) Какой метод внедрения кода он использовал CreateRemoteThread или CreateThread ?

 

Я так понял, когда мы используем code injection, то он тупо вешается на срабатывание инструкции, это не обязательно должна быть функция, это может быть даже присвоение значения какой-либо переменной. Самое главное, когда эта инструкция сработает, то сработает и наш код(который мы дописали к этой инструкции). Это хорошо, когда нам в качестве новой инструкции нужно записать одни лишь нопы (о чем очень хорошо пояснил keng, за что ему мега респект!).

Но как быть, если нам в качестве новых инструкций, нужно записать целый листинг команд?

3) Я так полагаю, для этого нужно использовать ассемблерные вставки и этот метод внедрения называется создание удалённого потока(CreateRemoteThread)?

4) Но для для его использование мы уже будем использовать не инструкции, а непосредственно отлаженные функции?

Но что он из себя представляет?

Опять-таки из своих рассуждений предположу, что он не использует никаких прыжков, а в свободной области процесса жертвы выделяется память и туда из нашего процесса  внедряется производная функция от оригинальной, с измененными значениями переменных(допустим). И когда у игры сработает оригинальная функция, она подменится на ту которую мы внедрили.

 

И вообще что такое ддл инжектор + *.dll. Ддл инжектор получает хендл процесса, а сама ддлка является как раз тем самым кодом, который мы внедряем.

От сюда вывод что  ддл инжектор + *.dll = трейнер.exe ? то бишь все в одном флаконе

 

Всем спасибо кто дочитал до конца! Рассчитываю на адекватные ответы.

Изменено пользователем tirion
Ссылка на комментарий
Поделиться на другие сайты

1) Допустим зачем использовать такие библиотеки как AsmJit, beaengine, если можно использовать ассемблерные вставки?

Не всегда компилятор ассемблерную вставку сделает именно в том виде, в каком ты ее пропишешь. И функцию нужно объявлять "голой". С указанными библиотеками ты от этого избавлен.

 

 

2) Какой метод внедрения кода он использовал CreateRemoteThread или CreateThread ?

CreateRemoteThread

 

 

Я так понял, когда мы используем code injection, то он тупо вешается на срабатывание инструкции, это не обязательно должна быть функция, это может быть даже присвоение значения какой-либо переменной. Самое главное, когда эта инструкция сработает, то сработает и наш код(который мы дописали к этой инструкции). Это хорошо, когда нам в качестве новой инструкции нужно записать одни лишь нопы (о чем очень хорошо пояснил keng, за что ему мега респект!). Но как быть, если нам в качестве новых инструкций, нужно записать целый листинг команд?

Для этого и делается инъекция кода - в коде игры выделяется место, туда прописывается весь нужный листинг, а с оригинальной инструкции делается прыжок на выделенное место. Это и называется инъекцией кода.

 

 

3) Я так полагаю, для этого нужно использовать ассемблерные вставки и этот метод внедрения называется создание удалённого потока(CreateRemoteThread)?

Необязательно. CreateRemoteThread используется чаще при инжекте длл-ки, для обычной инъекции кода он не нужен.

 

 

4) Но для для его использование мы уже будем использовать не инструкции, а непосредственно функции?

Опять-таки, инжект библиотеки (длл-ки) и инъекция кода - это несколько разные вещи

 

 

И вообще что такое ддл инжектор + *.dll. Ддл инжектор получает хендл процесса, а сама ддлка является как раз тем самым кодом, который мы внедряем.

длл инжектор - это процесс, который внедрит длл-ку с каким-то кодом в процесс игры. На этом построено большинство хуков.

 

 

От сюда вывод что ддл инжектор + *.dll = трейнер.exe ? то бишь все в одном флаконе

Вывод неверный. Трейнер не внедряет библиотеку (в общих случаях), он лишь "удаленно" делает выделение памяти в целевом процессе и запись туда какого-либо кода.

Ссылка на комментарий
Поделиться на другие сайты


Привет! Я попробую объяснить, но начну немного издалека. Вот есть игра:

******
*Игра*
******

Это - загруженный в оперативную память и запущенный на исполнение бинарный файл.
Или исполняемый, как угодно. 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, пока я писал
вот это вот.

Спасибо всем, кто насладился некоторым количеством букв.
  • Плюс 3
Ссылка на комментарий
Поделиться на другие сайты

Спасибо вам за столь развернутые ответы
 

Не всегда компилятор ассемблерную вставку сделает именно в том виде, в каком ты ее пропишешь. И функцию нужно объявлять "голой". С указанными библиотеками ты от этого избавлен.

Не всегда, но ведь он вполне приемлем и суть одна самое главное?
Потому что я считаю сначала лучше научиться работать с ассемблерными вставками, потом уже с подобными инструментариями типа AsmJit
 

 

Вывод неверный. Трейнер не внедряет библиотеку (в общих случаях), он лишь "удаленно" делает выделение памяти в целевом процессе и запись туда какого-либо кода.

так что же получается трейнер работает  методом CreateRemoteThread? А обычное внедрение библиотечки CreateThread ?

 

 

Для этого и делается инъекция кода - в коде игры выделяется место, туда прописывается весь нужный листинг, а с оригинальной инструкции делается прыжок на выделенное место. Это и называется инъекцией кода.

Т е какая бы не была инъекция, какие бы методы не использовала, она всегда прыгает на наш код и выпрыгивает обратно?

 

 

Опять-таки, инжект библиотеки (длл-ки) и инъекция кода - это несколько разные вещи

А вот тут я немного встал в ступр...

 

 


Я на днях написал статью, которая поверхностно описывает данную технику. 

Да, но ты там описал как записать опкоды в байтовом представлении.. А мне интересно как это сделать ассемблерными вставками

 

 

Просто столкнулся с таким трейнером, который состоит чисто из Exe Файла, но при запуске игры, он внедряет в нее библиотеку, а затем функциями этой библиотеки можно манипулировать непосредственно из трейнера

Изменено пользователем tirion
Ссылка на комментарий
Поделиться на другие сайты

Додумались они до того, что описывал

в своих работах Coder. Можно же не использовать внешнюю программу, а внедрить в

адресное пространство игры свой исполняемый модуль, скажем, DLL! В едином

адресном пространстве будет сильно удобнее - во-первых, не нужны API-функции

записи в память - мы можем напрямую ее изменять и копировать, были бы права на

это. Во-вторых - можно записать нужную ассемблерную вставку прямо в DLL и делать

 инъекцию кода, используя адрес этой вставки, обернув ее в функцию. Это

позволяет писать намного быстрее и не очень заморачиваться со смещениями и

цифрами вместо команд - большинство компилируемых языков программирования

поддерживают ассемблерные вставки в коде программы.

На этом этапе EXE-файл трейнера выполняет роль красивой обертки, цель которой -

показать картинку, написать имя автора и то, какой он крутой и молодец, а затем

внедрить в адресное пространство игры эту самую DLL, которая сделает всю

дальнейшую работу. Такие вот внедрители DLL и называются инжекторами, только не

кода, а DLL.

 

keng, я понял тебя, но уж больно трудно скомпоновать все это в единую мысль

Изменено пользователем tirion
Ссылка на комментарий
Поделиться на другие сайты

DLL можно встроить в EXE различными способами, а сконтачить их вместе можно, например, через общую секцию памяти. Или через сообщения, как это делает подавляющее большинство программ в Windows.
  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

А ты попробуй для начала просто связать два файла вместе. Напиши свой инжектор DLL, научись эту DLL запихивать в чужой процесс, внутри самой DLL - разберись, как работать с памятью изнутри нужного адресного пространства, затем - как писать инъекции кода сразу внутри DLL. После - как работать с shared-секцией памяти или сообщениями, чтобы инжектор и DLL друг друга понимали. А потом уже и объединить можно. Способов, как я уже сказал, масса - их можно и банально нагуглить. Если не понимаешь, как подобраться к крупной задаче - разбивай ее на мелкие и решай их.
  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

А ты попробуй для начала просто связать два файла вместе. Напиши свой инжектор DLL, научись эту DLL запихивать в чужой процесс, внутри самой DLL - разберись, как работать с памятью изнутри нужного адресного пространства, затем - как писать инъекции кода сразу внутри DLL. После - как работать с shared-секцией памяти или сообщениями, чтобы инжектор и DLL друг друга понимали. А потом уже и объединить можно. Способов, как я уже сказал, масса - их можно и банально нагуглить. Если не понимаешь, как подобраться к крупной задаче - разбивай ее на мелкие и решай их.

спасибо за умные мысли=) Пожалуй, так и поступлю

Ссылка на комментарий
Поделиться на другие сайты

Ребята, прошу прощения, но это выше моих сил. Я думал, задав наводящие вопросы, это позволит мне ближе подойти к разрешению моего основого вопроса, но увы нет. Задам вопрос прямо. Этот кусок кода взят из исходника трейнера. Грубо говоря эта функция вытащена из отладчика и переделана на свой лад, НО не могу понять каким образом она вызывается, а именно подменяет оригинальную функцию, тк тут нет никаких джампов и ретурнов, а самое главное вычисления размера. Я предполагаю что она записывается в свободную область памяти при помощи функции VirtualAllocEx, но каким образом она подменяет оригинальную? У меня была мысль, что на этом месте - call edx она возвращается в оригинальный код, но почему тогда после опкода call edx идет восстановление регистров, ведь он никогда не выполнится по сути.

void __stdcall Attack_THREAD() { __asm{pushadmov edx,0x0044FE60 // адрес вызываемой функцииmov ecx,dword ptr ds:[BA]mov ecx,dword ptr ds:[ecx+0x1C]mov ecx,dword ptr ds:[ecx+0x20] // структуру игрока пихаем в ecxpush -1 // понятия не имею, что это за параметрыpush 0 // они просто взяты из OllyDbgpush 0push 0call edxpopad}}
NullAlex: код необходимо обрамлять соответствующим тегом.
Ссылка на комментарий
Поделиться на другие сайты

Судя по коду, он вызывает функцию по адресу 0x0044FE60 с аргументами (0, 0, 0, -1). Пятый аргумент передается прямо через ECX  и это, на мой взгляд, странновато, но мало ли что там себе думает компилятор. Заменяется функция где-то в другом месте. Обычно или заменяется указатель на функцию, или первые 5 байт функции меняются на "JMP 00112233", где 00112233 - адрес функции, которую нужно вызывать хакеру.
  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

Судя по коду, он вызывает функцию по адресу 0x0044FE60 с аргументами (0, 0, 0, -1). Пятый аргумент передается прямо через ecx и это, на мой взгляд, странновато, но мало ли что там себе думает компилятор. Заменяется функция где-то в другом месте. Обычно или заменяется указатель на функцию, или первые 5 байт функции меняются на "JMP 00112233", где 00112233 - адрес функции, которую нужно вызывать хакеру.

Но, как тогда объянить восстановление регистров после возврата на оригинальную функцию с данными аргументами? Ведь опдкод popad никогда не выполнится

 

 

Как бы подсказывает, что это функция назначается потоку, который, в свою очередь, скорее всего, создается удаленно (CreateRemoteThread)

да, ты совершенно прав, автор использует данный прием

hProcThread = CreateRemoteThread(hProc,NULL,NULL,(LPTHREAD_START_ROUTINE)pFunction,pParams,NULL,NULL);

Это означает что в удаленном потоке не используется никаких джампов ретурнов, вычислений размеров инъекции? И чтобы пролить лучик ясности нужно детально изучить  эту  CreateRemoteThread функцию?

Изменено пользователем tirion
Ссылка на комментарий
Поделиться на другие сайты

В приведенном тобой кусочке кода нет оригинальной и неоригинальной функции. Она есть только одна. И она просто вызывается. Без остальной части кода угадывать трудновато. CreateRemoteThread просто создает поток и передает управление на выбранный тобой адрес памяти. Если что-то где-то заменяется - то не здесь.


NullAlex: позволил себе исправить твое сообщение, чтобы оно отображалось именно со шрифтом "Terminus", т.к. была допущена ошибка в теге :)
  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

Всем спасибо! Вроде бы  понял разницу между внедрением кода и удаленным потоком. Удаленный поток вызывается по требованию пользователя, т.е он не вешается ни на какую инструкцию и не ожидает ее выполнения, именно поэтому нужно обязательно передавать функции все ее аргументы

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

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

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