tirion Опубликовано 16 марта, 2015 Поделиться Опубликовано 16 марта, 2015 (изменено) Всем доброго времени суток. Не знал куда поместить тему, думаю в этом разделе она будет уместна.Заранее прошу прощения за огромное количество текста, и так сократил по максимуму, но наплыв вопросов очень большой.(Абстрактные представления имею, но не могу собрать пазлы в единую картину.И так.. Просмотрел видеокурс уроков Coder'a по написанию трейнера с нуля, видно, парень очень хорошо шарит, но к сожалению не очень доходчиво объясняет, но даже не смотря на это Огромнейшее ему спасибо! что-то новое я для себя все-таки взял.1) Допустим, зачем использовать такие библиотеки как AsmJit, beaengine, если можно использовать ассемблерные вставки?2) Какой метод внедрения кода он использовал CreateRemoteThread или CreateThread ? Я так понял, когда мы используем code injection, то он тупо вешается на срабатывание инструкции, это не обязательно должна быть функция, это может быть даже присвоение значения какой-либо переменной. Самое главное, когда эта инструкция сработает, то сработает и наш код(который мы дописали к этой инструкции). Это хорошо, когда нам в качестве новой инструкции нужно записать одни лишь нопы (о чем очень хорошо пояснил keng, за что ему мега респект!).Но как быть, если нам в качестве новых инструкций, нужно записать целый листинг команд?3) Я так полагаю, для этого нужно использовать ассемблерные вставки и этот метод внедрения называется создание удалённого потока(CreateRemoteThread)?4) Но для для его использование мы уже будем использовать не инструкции, а непосредственно отлаженные функции?Но что он из себя представляет?Опять-таки из своих рассуждений предположу, что он не использует никаких прыжков, а в свободной области процесса жертвы выделяется память и туда из нашего процесса внедряется производная функция от оригинальной, с измененными значениями переменных(допустим). И когда у игры сработает оригинальная функция, она подменится на ту которую мы внедрили. И вообще что такое ддл инжектор + *.dll. Ддл инжектор получает хендл процесса, а сама ддлка является как раз тем самым кодом, который мы внедряем.От сюда вывод что ддл инжектор + *.dll = трейнер.exe ? то бишь все в одном флаконе Всем спасибо кто дочитал до конца! Рассчитываю на адекватные ответы. Изменено 16 марта, 2015 пользователем tirion Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 16 марта, 2015 Поделиться Опубликовано 16 марта, 2015 1) Допустим зачем использовать такие библиотеки как AsmJit, beaengine, если можно использовать ассемблерные вставки?Не всегда компилятор ассемблерную вставку сделает именно в том виде, в каком ты ее пропишешь. И функцию нужно объявлять "голой". С указанными библиотеками ты от этого избавлен. 2) Какой метод внедрения кода он использовал CreateRemoteThread или CreateThread ?CreateRemoteThread Я так понял, когда мы используем code injection, то он тупо вешается на срабатывание инструкции, это не обязательно должна быть функция, это может быть даже присвоение значения какой-либо переменной. Самое главное, когда эта инструкция сработает, то сработает и наш код(который мы дописали к этой инструкции). Это хорошо, когда нам в качестве новой инструкции нужно записать одни лишь нопы (о чем очень хорошо пояснил keng, за что ему мега респект!). Но как быть, если нам в качестве новых инструкций, нужно записать целый листинг команд?Для этого и делается инъекция кода - в коде игры выделяется место, туда прописывается весь нужный листинг, а с оригинальной инструкции делается прыжок на выделенное место. Это и называется инъекцией кода. 3) Я так полагаю, для этого нужно использовать ассемблерные вставки и этот метод внедрения называется создание удалённого потока(CreateRemoteThread)?Необязательно. CreateRemoteThread используется чаще при инжекте длл-ки, для обычной инъекции кода он не нужен. 4) Но для для его использование мы уже будем использовать не инструкции, а непосредственно функции?Опять-таки, инжект библиотеки (длл-ки) и инъекция кода - это несколько разные вещи И вообще что такое ддл инжектор + *.dll. Ддл инжектор получает хендл процесса, а сама ддлка является как раз тем самым кодом, который мы внедряем.длл инжектор - это процесс, который внедрит длл-ку с каким-то кодом в процесс игры. На этом построено большинство хуков. От сюда вывод что ддл инжектор + *.dll = трейнер.exe ? то бишь все в одном флаконеВывод неверный. Трейнер не внедряет библиотеку (в общих случаях), он лишь "удаленно" делает выделение памяти в целевом процессе и запись туда какого-либо кода. Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 16 марта, 2015 Поделиться Опубликовано 16 марта, 2015 Привет! Я попробую объяснить, но начну немного издалека. Вот есть игра:*******Игра*******Это - загруженный в оперативную память и запущенный на исполнение бинарный файл.Или исполняемый, как угодно. 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 Ссылка на комментарий Поделиться на другие сайты Поделиться
tirion Опубликовано 16 марта, 2015 Автор Поделиться Опубликовано 16 марта, 2015 (изменено) Спасибо вам за столь развернутые ответы Не всегда компилятор ассемблерную вставку сделает именно в том виде, в каком ты ее пропишешь. И функцию нужно объявлять "голой". С указанными библиотеками ты от этого избавлен.Не всегда, но ведь он вполне приемлем и суть одна самое главное?Потому что я считаю сначала лучше научиться работать с ассемблерными вставками, потом уже с подобными инструментариями типа AsmJit Вывод неверный. Трейнер не внедряет библиотеку (в общих случаях), он лишь "удаленно" делает выделение памяти в целевом процессе и запись туда какого-либо кода.так что же получается трейнер работает методом CreateRemoteThread? А обычное внедрение библиотечки CreateThread ? Для этого и делается инъекция кода - в коде игры выделяется место, туда прописывается весь нужный листинг, а с оригинальной инструкции делается прыжок на выделенное место. Это и называется инъекцией кода.Т е какая бы не была инъекция, какие бы методы не использовала, она всегда прыгает на наш код и выпрыгивает обратно? Опять-таки, инжект библиотеки (длл-ки) и инъекция кода - это несколько разные вещиА вот тут я немного встал в ступр... Я на днях написал статью, которая поверхностно описывает данную технику. Да, но ты там описал как записать опкоды в байтовом представлении.. А мне интересно как это сделать ассемблерными вставками Просто столкнулся с таким трейнером, который состоит чисто из Exe Файла, но при запуске игры, он внедряет в нее библиотеку, а затем функциями этой библиотеки можно манипулировать непосредственно из трейнера Изменено 16 марта, 2015 пользователем tirion Ссылка на комментарий Поделиться на другие сайты Поделиться
tirion Опубликовано 16 марта, 2015 Автор Поделиться Опубликовано 16 марта, 2015 (изменено) Додумались они до того, что описывалв своих работах Coder. Можно же не использовать внешнюю программу, а внедрить вадресное пространство игры свой исполняемый модуль, скажем, DLL! В единомадресном пространстве будет сильно удобнее - во-первых, не нужны API-функциизаписи в память - мы можем напрямую ее изменять и копировать, были бы права наэто. Во-вторых - можно записать нужную ассемблерную вставку прямо в DLL и делать инъекцию кода, используя адрес этой вставки, обернув ее в функцию. Этопозволяет писать намного быстрее и не очень заморачиваться со смещениями ицифрами вместо команд - большинство компилируемых языков программированияподдерживают ассемблерные вставки в коде программы.На этом этапе EXE-файл трейнера выполняет роль красивой обертки, цель которой -показать картинку, написать имя автора и то, какой он крутой и молодец, а затемвнедрить в адресное пространство игры эту самую DLL, которая сделает всюдальнейшую работу. Такие вот внедрители DLL и называются инжекторами, только некода, а DLL. keng, я понял тебя, но уж больно трудно скомпоновать все это в единую мысль Изменено 16 марта, 2015 пользователем tirion Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 16 марта, 2015 Поделиться Опубликовано 16 марта, 2015 DLL можно встроить в EXE различными способами, а сконтачить их вместе можно, например, через общую секцию памяти. Или через сообщения, как это делает подавляющее большинство программ в Windows. 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 16 марта, 2015 Поделиться Опубликовано 16 марта, 2015 А ты попробуй для начала просто связать два файла вместе. Напиши свой инжектор DLL, научись эту DLL запихивать в чужой процесс, внутри самой DLL - разберись, как работать с памятью изнутри нужного адресного пространства, затем - как писать инъекции кода сразу внутри DLL. После - как работать с shared-секцией памяти или сообщениями, чтобы инжектор и DLL друг друга понимали. А потом уже и объединить можно. Способов, как я уже сказал, масса - их можно и банально нагуглить. Если не понимаешь, как подобраться к крупной задаче - разбивай ее на мелкие и решай их. 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
tirion Опубликовано 16 марта, 2015 Автор Поделиться Опубликовано 16 марта, 2015 А ты попробуй для начала просто связать два файла вместе. Напиши свой инжектор DLL, научись эту DLL запихивать в чужой процесс, внутри самой DLL - разберись, как работать с памятью изнутри нужного адресного пространства, затем - как писать инъекции кода сразу внутри DLL. После - как работать с shared-секцией памяти или сообщениями, чтобы инжектор и DLL друг друга понимали. А потом уже и объединить можно. Способов, как я уже сказал, масса - их можно и банально нагуглить. Если не понимаешь, как подобраться к крупной задаче - разбивай ее на мелкие и решай их.спасибо за умные мысли=) Пожалуй, так и поступлю Ссылка на комментарий Поделиться на другие сайты Поделиться
tirion Опубликовано 17 марта, 2015 Автор Поделиться Опубликовано 17 марта, 2015 Ребята, прошу прощения, но это выше моих сил. Я думал, задав наводящие вопросы, это позволит мне ближе подойти к разрешению моего основого вопроса, но увы нет. Задам вопрос прямо. Этот кусок кода взят из исходника трейнера. Грубо говоря эта функция вытащена из отладчика и переделана на свой лад, НО не могу понять каким образом она вызывается, а именно подменяет оригинальную функцию, тк тут нет никаких джампов и ретурнов, а самое главное вычисления размера. Я предполагаю что она записывается в свободную область памяти при помощи функции 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: код необходимо обрамлять соответствующим тегом. Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 17 марта, 2015 Поделиться Опубликовано 17 марта, 2015 Судя по коду, он вызывает функцию по адресу 0x0044FE60 с аргументами (0, 0, 0, -1). Пятый аргумент передается прямо через ECX и это, на мой взгляд, странновато, но мало ли что там себе думает компилятор. Заменяется функция где-то в другом месте. Обычно или заменяется указатель на функцию, или первые 5 байт функции меняются на "JMP 00112233", где 00112233 - адрес функции, которую нужно вызывать хакеру. 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
Xipho Опубликовано 17 марта, 2015 Поделиться Опубликовано 17 марта, 2015 Attack_THREAD()Как бы подсказывает, что это функция назначается потоку, который, в свою очередь, скорее всего, создается удаленно (CreateRemoteThread) Ссылка на комментарий Поделиться на другие сайты Поделиться
tirion Опубликовано 17 марта, 2015 Автор Поделиться Опубликовано 17 марта, 2015 (изменено) Судя по коду, он вызывает функцию по адресу 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 функцию? Изменено 17 марта, 2015 пользователем tirion Ссылка на комментарий Поделиться на другие сайты Поделиться
keng Опубликовано 17 марта, 2015 Поделиться Опубликовано 17 марта, 2015 В приведенном тобой кусочке кода нет оригинальной и неоригинальной функции. Она есть только одна. И она просто вызывается. Без остальной части кода угадывать трудновато. CreateRemoteThread просто создает поток и передает управление на выбранный тобой адрес памяти. Если что-то где-то заменяется - то не здесь.NullAlex: позволил себе исправить твое сообщение, чтобы оно отображалось именно со шрифтом "Terminus", т.к. была допущена ошибка в теге 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
tirion Опубликовано 18 марта, 2015 Автор Поделиться Опубликовано 18 марта, 2015 Всем спасибо! Вроде бы понял разницу между внедрением кода и удаленным потоком. Удаленный поток вызывается по требованию пользователя, т.е он не вешается ни на какую инструкцию и не ожидает ее выполнения, именно поэтому нужно обязательно передавать функции все ее аргументы Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения