You are not logged in.
Увы, никаких баз у меня нет, и работаю я не с гта.
..и не в IDA : D
Я использую OllyDbg. Кстати, как можно приаттачить Иду к процессу игры, чтобы и процесс не был остановлен, и код был в Иде доступен для просмотра (вот прям как в Olly)?
[edit]
А можно __thiscall описывать как __cdecl и передавать this вручную?
Тогда в дополнение к п.1: как найти место, откуда вызывается конструктор?
Вообще примерно. По каким ниточкам идти, пользуясь отладчиком?)
Спасибо за труды.
От меня, как всегда, несколько вопросов (однотипных):
1. Как можно вычислить конструктор определённого объекта? У меня есть лишь начало его структуры. Хотелось бы узнать точный размер этой структуры. Я так понял из статьи, что мне поможет конструктор.
2. Функции-члены класса содержатся в структуре экземпляра класса или отдельно (в описании класса)?
3. Класс это тип данных. Значит у него есть определённый размер. Влияет ли размер функций класса на общий размер класса в памяти? Конечно, если вообще есть такое понятие, как размер функций %)
во многом, кажется, разобрался. основной инструментарий - отладчик, много-много времени и нудный просмотр дизасма : D
насчёт расчёта адреса для JMP - до сих пор не понимаю, что за чудоарифметика, но это и впрямь работает (адрес получается нужным).
так вот, для CALL арифметика выходит другая, хотелось бы узнать, какая именно.
кстати, где можно почитать про байткоды операций? конкретная ссылка была бы не лишней.
ещё нужен совет, как быть в ситуации:
перехватываю управление из функции (которая лежит в DLL) и передаю его своей функции (которая лежит в другой DLL), где добавляю пару операций, затем повторяю оригинальный (заменённый на переход к моей функции) код и возвращаю управление туда, откуда его перехватил.
состояние регистров до и после перехвата должно быть идентичным, но как тогда сделать джамп к оригинальному коду, если адрес этого кода нестатичен (находится в DLL)?
было много идей, все они оказывались провальными:
этот код не работает, ибо почему-то нельзя отправлять в стек значения переменных:
DWORD dwAddrToGo = (DWORD)GetModuleHandle("neededLib.dll")+0x100500; __asm { push dwAddrToGo ret }
этот код подошёл бы, но портит регистр:
DWORD dwAddrToGo = (DWORD)GetModuleHandle("neededLib.dll")+0x100500; __asm { mov edx, dwAddrToGo jump edx }
и действительно, обратно этот регистр уже не вернуть.
был и такой вариант:
DWORD dwAddrToGo = (DWORD)GetModuleHandle("neededLib.dll")+0x100500; __asm { xchg eax, dwAddrToGo push eax xchg eax, dwAddrToGo ret }
но чувствую, что тут вновь проблема в использовании переменной (подозреваю, что xchg пашет только с регистрами).
ещё была идея перед перехватом управления поместить в стек EIP, но с этим регистром вообще ничего нельзя сделать напрямую.
также была идея пихать туда не EIP, а <адрес заменяемой функции+оффсет к нужной точке возврата>; реализовывать пока не пробовал, но думаю, что придётся опять шаманить с сохранением регистров.
сейчас пришёл к выводу, что самым халявным способом будет сделать не JMP на свою функцию, а CALL (а в конце функции будет просто ret). вот в связи с этим и интересуюсь формированием байткода команды типа "CALL 00123458".
кстати, пока танцевал с бубном, раскусил-таки фишку __declsped(naked): если приписать это перед названием функции, содержать она будет (в ассемблерном виде) строго то, что мы записывали при кодинге, а если оставить всё, как есть, поместив в функцию код (лучше всего на ассемблере), то можно заметить в отладчике, что функция выполняет какие-то там дополнительные операции с регистрами (в частности с EBP), а только потом написанный при разработке код.
>Делается элементарно, просто меняешь значение в структуре игрока...
зачем мне менять значение, если я хочу повторить функцию? я, кажется, доступным языком вопрос задал.
и тема, которую ты мне скинул не отвечает на цитированный тобой вопрос.
так или иначе, я уже потихоньку начал разбираться и с выполнением своего кода в пространстве других приложений и с созданием функций (инжект кусков кода).
хм.
есть где-нибудь годный гайд с примерами на тему инжекта кода?
хочу повторить код, который есть в стандартном exe (естественно вызывать его в нужное мне время и с нужными мне параметрами), но я не знаю, как найденный код перенести на C++ (сложность в том, чтобы обозначить границы действительно нужного ассемблерного кода).
найти нужные байты и адреса я смогу (Cheat Engine + Olly Dbg), но нужна мне не замена, а просто повтор.
в итоге должнен получиться "инжект кусков кода".
если нет гайдов, не мог бы кто-нибудь привести пример описания у себя функции с параметрами (здесь уже речь об описании прототипа функции, без использования ассемблерных вставок)?
и ещё. когда копаю эти функции, нередко натыкаюсь на "ненужные" параметры перед собственно вызовом функции по адресу (наряду с важными для функции значениями (передаваемыми параметрами) в стек пихаются какие-нибудь нули или единицы (независимо от параметров)). как у себя описывать такие функции (тут уже не куски кода, а описание функции на C++ с указанием её адреса в пространстве)? в смысле, есть функция void FUNCTION (int, float). я точно уверен, что важны два параметра и могу определить их в отладчике, однако в коде зачем-то в стек пихается больше значений, чем два. в общем, я даже не знаю как сформулировать вопрос как приложение понимает, что функция, вызываемая как FUNCTION (10, 234.55) на самом деле принимает только 2 параметра и как мне описать такую FUNCTION, если я наткнусь на непонятные "лишние" параметры в стеке?
ещё. предположим, я хочу повторить функцию лечения игрока (например, ту, которая срабатывает при покупке газировки). вот я нашёл, какая операция меняет собственно значение здоровья игрока. где-то выше этого кода стопроцентно всегда идёт нужный для работы функции код. как понять, где этот нужный код начинается? неужели нужен весь код до первого встречного выше ret?
ты не поверишь, но именно этот исходник я упомянул, он содержит один экспорт Direct3DCreate9.
: D
спасибо, ребята.
решил-таки заняться хуком D3D9.
так что реквестирую больше подсказок и примеров ;D
пробил экспорт из стандартной d3d9.dll (с помощью своей программки):
There are 14 functions in d3d9.dll: CheckFullscreen D3DPERF_BeginEvent D3DPERF_EndEvent D3DPERF_GetStatus D3DPERF_QueryRepeatFrame D3DPERF_SetMarker D3DPERF_SetOptions D3DPERF_SetRegion DebugSetLevel DebugSetMute Direct3DCreate9 Direct3DShaderValidatorCreate9 PSGPError PSGPSampleTexture
скомпилил свой вариант, написанный на базе кода из поста #13 (listener), пробил в той же программке экспорт:
There are 10 functions in d3d9.dll: _D3DPERF_BeginEvent _D3DPERF_EndEvent _D3DPERF_GetStatus _D3DPERF_QueryRepeatFrame _D3DPERF_SetMarker _D3DPERF_SetOptions _D3DPERF_SetRegion _Direct3DCreate9 _Direct3DShaderValidatorCreate9 ___CPPdebugHook
в связи с этим вопросы:
1) что за подчёркивания? как экспортировать нормальные имена (в исходниках у них оригинальные названия, а не эти)?
2) что за ___CPPdebugHook? моя либа явно этого не экспортирует, но использую я C++ Builder (мало ли, чего там напридумали?).
3) я нигде не нашёл следующие функции: CheckFullscreen, DebugSetLevel, DebugSetMute, PSGPError, PSGPSampleTexture. что это такое и как бы его у себя описать?
4) (главный вопрос) нужно ли экспортировать что-либо кроме Direct3DCreate9? видел в каких-то исходниках, что экспортируют только одну её, правда через .def-файл, как в посте #17 (Alien).
5) может, где-то уже были все эти вопросы? проще конечно же дать мне ссылку, если были. гугл не помогает: пара иностранных сайтов с минимумом комментариев по коду, половина из которых естественно непонятна. прям как-то неловко изучать что-то в сетях..
хм. вроде всё довольно легко. спасибо.
а можно сделать так?
это:
IDirect3D9 * WINAPI Direct3DCreate9(UINT SDKVersion);
описать у себя так:
void* WINAPI Direct3DCreate9(UINT SDKVersion);
я не собираюсь использовать устройство, а указатель вроде как всегда одного размера.
не думаю, что это особо сложно, только вот зачем? он и так работает через обычный bass.dll (в этом-то и всё удобство).
можешь продемонстрировать наглядный пример, как сделать подмену стандартной библиотечной функции на функцию из своей библиотеки?
в смысле, чтобы моя экспортируемая функция из фейковой библиотеки вызывала оригинальную функцию настоящей библиотеки (и желательно так, чтобы не нужно было повторять полные прототипы).
прям на примере одной функции.
а что, обязательно описывать их полные прототипы?
предположим, что в оригинальной библиотеке есть функция с параметрами:
void Function (int i);
у себя я её описываю вот так:
extern "C" __declspec(dllexport) void Function ();
а приложение у себя вновь описывает, как положено изначально:
void Function (int i);
разве так не будет работать?
интересно.
ещё несколько вопросов ;D
1. как правильно выполнить "обёртку" функции?
так пойдёт?
extern "C" __declspec(dllexport) void ProcessSuicide() { ... }
2. в чём разница между dllexport и dllimport? вопрос, вроде как глупый, однако следующий код демонстрирует, словно пофиг, что из этого использовать:
#ifdef XLIVELESS_EXPORTS #define XLIVELESS_API extern "C" __declspec(dllexport) #else #define XLIVELESS_API extern "C" __declspec(dllimport) #endif
3. что такое __declspec(naked) void Function() {} ?
честно, я гуглил и читал это, но не понял, зачем делать функцию "без обрамления функции" (и, как это вообще понимать, я тоже не понял).
4. можно ли программно экспортировать динамический набор функций?
план таков: просматриваем секцию экспорта оригинальной DLL на предмет имён функций, дальше подставляем имя в цикле с префиксом __declspec и бла-бла-бла.
ведь вместо самой функции можно подставить указатель на неё?
while(int i=0; i<functionsCount; i++) { ... // тут копаем структуру экспорта DLL на предмет имён name = ... ; // сюда запихиваем очередное имя функции extern "C" __declspec (dllexport) void* GetProcAddress(hOriginalDLL, name); }
хотя, сомневаюсь, что так можно. а если сделать программку, которая загрузит в себя DLL, запишет все адреса и имена функций и, в соответствии с полученными данными, сгенерирует мне код, в котором будет обёртка всех функций, который уже я откомпилирую, это будет нормальным ленивым решением задачи?
5. предположим, что мне нужно сделать поддержку нескольких версий (скажем, трёх разных версий одного приложения), как различить версии? наверняка есть программка, которая вернёт первый адрес памяти (одинаковый адрес для всех приложений), по которому лежат разные значения? так их можно было бы различить.
если JMP требует лишь адрес, куда нужно прыгнуть, то почему нельзя просто указать адрес функции?
вот так:
*patch = 0xE9; // JMP
*(DWORD *)(patch+1) = pfnReplacement; // первый байт - опкод, следующие 4 байта - операнд.
и ещё, если мы меняем код, разве не нужно юзать VirtualProtect?
а что ты подразумеваешь под обёрткой? типа такого?
создать в своей библиотеке функции (а точнее прототипы. нужно же?), затем сделать примерно такое:
MyProcedure1 = GetProcAddress(hOriginalDLL,"MyProcedure1");
а потом нагло экспортировать их?
ещё вопрос. да, я смотрел исходники твоего xliveless, мне понравилась эта функция:
XLIVELESS_API void injectFunction (DWORD dwAddress, DWORD pfnReplacement) { dwAddress += dwLoadOffset; BYTE * patch = (BYTE *)dwAddress; *patch = 0xE9; // JMP *(DWORD *)(patch+1) = (pfnReplacement-(dwAddress+5)); }
объясни, пожалуйста, что здесь происходит.
dwAddress, как я понимаю, это оффсет от Image Base процесса?
pfnReplacement - указатель на свою функцию?
вот тут я собственно не понимаю:
*(DWORD *)(patch+1) = (pfnReplacement-(dwAddress+5));
(DWORD*) - меняем размер (чтобы писать 4 байта), это понятно. а что за рассчёты такие? что за +5?
в общем, как я понимаю, просто вставляется джамп на свой адрес. а как потом нормально вернуть управление (джамп на свою функцию, там нужные нам действия, затем продолжение с того джампа) ?
прокатит, если сперва сделать джамп к себе, там первым делом вернуть оригинальный код (вместо джампа), выполнить свой код, затем в конце джампнуться на тот оригинальный код?
кстати, ещё вопрос. как вообще делаются плагины? так, чтобы правильно работали. не то, чтобы прям официально, но чтобы это было действительно расширение возможностей программы (например, QIP). в смысле, плагины это библиотеки DLL. они делаются с использованием специальных API или разработчик сам копает память и добавляет новенькое?
привет, друзья!
есть где-нибудь литературка на тему создания загрузчиков библиотек?
я сделал свой загрузчик в виде библиотеки. если внедрить его в процесс, то он тут же поищет файлы определённого расширения рядом с запускаемым файлом этого процесса и заинжектит их в программу, но минус в том, что сам загрузчик нужно внедрять вручную (в остальном работает на пять с плюсом).
пробовал переименовать загрузчик в DLLку, нужную процессу, чтобы приложение самостоятельно скушало мою библиотеку, а заодно и кучу других библиотек (в том числе и оригинальную заменённую DLL, причём в первую очередь). проблема оказалась в том, что приложение пытается извлечь нужные ему функции из моей либы, а не из оригинала.
план был такой: при инжекте загрузчика из него вызывается функция, останавливающая все потоки (кроме собственного), загружался бы оригинальный файл, после чего менялась бы таблица импорта, лежащая в PE-хидере процесса. чувствую, что достаточно отловить, куда пихается хендл либы, которую процесс запускает сам и запихать туда хендл либы, которая инжектится загрузчиком. ну а дальше потоки возвращаются в исходное состояние и грузится остальное добро моего расширения.
прокатит ли это? если да, то как это сделать? инжектор должен быть таким, чтобы мог заменить вообще любую библиотеку (или какую-нибудь чертовски важную, типа user32).
p.s. как всегда, интересует не конкретный код, а "отправная точка".
пожалуйста:
http://www.megaupload.com/?d=ULESG7UJ
обязательно расскажите друзьям, если понравится.
и куда его тут можно положить, чтобы его заметили или нашли из поиска?)
куда позволительно залить архив с плагином, дабы скинуть сюда линк?
должно быть, я немного зазнался, поставив перед собой настолько сложную цель.
давно же я не посещал этот форум.. впрочем, благодарю всех за ответы.
а всем безумцам, решившим создать свой скриптовый язык (или прочие подобные вещи) я хочу сказать, что ВСЁ реализуется в C++, причём довольно просто.
пожалуй, я познал это для себя, открыв возможности си ;D
прикрепляю к посту свою последнюю на сегодняшний день разработку - проигрыватель онлайн-радиостанций для gta sa. может похвастаться некоторыми полезными настройками.
выполнен в виде .asi-плагина. написал на C++ за одну ночь)
всегда мечтал о подобном плагине и скорее всего я такой не один.
подробнейший ридми аж на двух языках прилагается к архиву, настоятельно рекомендуется прочесть его перед использованием.
p.s. Админы, если можно, переименуйте меня в мой нынешний ник: BritishColonist.
p.p.s. К сожалению ничего не прикрепилось. как заливать-то?
прости, должно быть, мой ответ уже будет неактуальным, но меня давненько не было на форуме
начнём с того, что не рекомендуется выполнять код в функции DllMain, лучше создай отдельный поток в приложении и позволь ему (приложению) окончательно запуститься и загрузить остальные библиотеки.
при инжекте библиотеки ul_reason_for_call равняется DLL_PROCESS_ATTACH, а не DLL_THREAD_ATTACH.
а вот тебе 0AB0 на C++:
bool KeyPressed(BYTE key) { return ((GetAsyncKeyState(key)&(1<<16))!=0); }
Доброго времени суток, ребята. А особо в этой теме приветствуются люди знающие и/или участвовавшие в разработке скриптовых движков, в частности CLEO/SunnyBuilder.
/* немного автобиографии
Давно пишу скрипты для SA, и последние мои разработки сводились к сплошной работе с адресами памяти напрямую.
И я подумал, раз так, то почему бы не использовать вообще отдельную программу? Тогда юзеру даже не придётся ставить клео, качать скрипт. Тем более, включение и выключение будет максимально удобным (крестик в окошечке рулит, как никак). Так я подумал, когда хотел сделать себе сохранялку координат на клео. Расставлял тачки в сампе, а когда заглянул в окно СанниБилдера, понял, что получать координаты из сампа он категорически отказывается. А с файлами через скрипты я как работать не умел, так и не умею (кстати, тема для мануала). В то время я считал, что команда сохранения координат пашет только в режиме дебага самп-сервера (а оказалось, что работает она везде, просто не отображает какой-либо результат). Так что выход был один - использовать отдельную программку. И мне пришлось много париться, т.к. знал намного меньше, нежели сейчас. Изобретал велосипед, но учился...
В данный момент я как-то редко захожу в SA и ещё реже в Билдер, т.к. игра наскучила. Разве что, когда просят, делаю людишкам скрипты. Вообще, всё, что можно, придумано и реализовано (хотя не совсем, я лично создал пару новинок). И вот пал мой взгляд на другие игры, где можно было бы весело издеваться над игровой механикой. А для этого я решил сперва поискать инфу (ничего не нашёл), а потом и взять дело "в свои руки". Я вовсе не стремлюсь к славе и распространению своих наработок, мне лишь нужен удобный инструмент, который будет самостоятельно раскапывать бездонные структуры, добираясь до того, с чем удобно будет работать непосредственно скриптеру. Весьма вероятно, что Seemann когда-то думал точно так же.
*/
Это было, так сказать, предисловие. Собственно, основное:
Возможно эти слова будут слишком громкими, но всё же.
Планирую создать нечто вроде клео, но не для гта. Значит конкурентом данному проекту я не буду никоим образом.
Но для начала мне нужна отправная точка. Имеются некоторые вопросы относительно создания скриптового движка:
* На чём удобнее создавать? С++ или Delphi? // Изначально я учил паскаль/дельфи, так что лично мне удобнее работать с ним, но слышал, что Си наиболее гибок, среди языков высокого уровня. Стоит ли полностью переходить на Си?
* Каким образом можно внедрить код отдельных скриптов в процесс игры? //Неужто через построчное считывание скрипта, проверку в каждой строчке на имя команды (в СанниБилдере - опкод) и дальнейшую работу с переданными параметрами (в соответствии с командой)?
* Чем копать EXE? // Я, например, юзал как иду, так и олли дебаг. Ещё круто ворочает ассемблерный код программка TSearch. Я даже много чего находил и записывал всё в блокнотик. Проблема в том, что этой информации много, хранится она в неудобном виде и достаётся оттуда с трудом.
* Неужели вся работа клео-опкодов постороена на прямой работе с памятью? // В смысле, как происходит работа скрипта? Внутри игры или посредством "топорных" чтения и записи в адреса памяти?
Естественно разжёвывать всё это я не прошу. Тут возможно огромное кол-во подробностей, я уверен. Прошу лишь описать что-то вкратце, на что-то дать ссылки.
Опыт показывает, что пока за дело не возьмёшься лично ты, оно не тронется ничуть, несмотря на то, касается ли оно других. И сейчас я думаю о том, насколько хорошо то, что когда-то Seemann взялся за создание CLEO. Вот я, например, многое узнал благодаря работе в Билдере. Я благодарен автору. Я, будучи начинающим программером, нашёл применение навыков в деле, полезном не одному мне. Я создал десятки скриптов, за которые мне были благодарны люди. Но самое главное: ко мне обращались за помощью. У меня спрашивали, что и как. И, чёрт возьми, я учил их и показывал. Разве это не прекрасно? Благое дело, как никак. Вот ещё одна причина, по которой я уж очень хочу создать скриптовый язык, движок.
И, что бы вы тут не написали, спасибо за внимание.
Оу. А если делать в виде asi, то можно будет вызывать функцию процесса игры?
Да, найти получается.
Теперь тогда ещё вопрос. В саннике используются хэндлы, т.е. переменные, могут хранить в себе игрока/машину/объект/любое значение. Подобный хэндл - просто число?
А то нужно передать теперь параметры в дельфях. Мне следует найти адрес начала струкруты этого игрока/машины/объекта?
var StoreActorPos: Procedure (player: dword; var x,y,z:single; _x,_y,_z: single);cdecl; ... var x,y,z : single; PLAYER_ACTOR: dword; begin GetWindowThreadProcessId(Wnd,@PId); //внедрение в процесс игры PHandle:=OpenProcess(PROCESS_ALL_ACCESS,false,PId); // <- ALL_ACCESS @StoreActorPos:=Ptr($0048BA97); //адрес процедуры ReadProcessMemory(PHandle,Ptr(CPed),@PLAYER_ACTOR,4,rw); //считываем указатель на адрес структуры игрока ReadProcessMemory(PHandle,Ptr(PLAYER_ACTOR),@PLAYER_ACTOR,4,rw); //читаем этот адрес If PLAYER_ACTOR<>0 Then Begin StoreActorPos(PLAYER_ACTOR,x,y,z,0.0,0.0,0.0); // <- здесь вылет Edit4.Text:=Format('%8.4f %8.4f %8.4f',[x,y,z]); End; Closehandle(PHandle); end;
AcessViolation при вызове процедуры. Разве что-то не так?
Где можно надыбать точные адреса памяти, используемые в опкодах санни билдера? Такая информация где-то вообще есть?
Хочется перенести пару скриптов на удобный мне Delphi.
Собственно, интересующие опкоды:
04C4:
86BD:
Нужны собственно адреса функций, то есть, такие, чтобы заново можно было создать эти функции в дельфях и использовать, как обычные функции дельфи (я видел, так делали и на дельфи, и на си).
Замечательно, спасибо!