patсher_x86 |
Здравствуйте, гость ( Вход | Регистрация )
patсher_x86 |
08 Nov 2017, 16:38
Сообщение
#21
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Цитата На вог-форуме есть тема по патчеру, но, к сожалению, цель с которой я эту тему там создавал - не достигнута. Если не ошибаюсь, в то время, как функции установки заплаток Эры с самого начального этапа поддерживали рекурсию, на хуках патчера я столкнулся с багом. После поддержка рекурсии была добавлена, но вместо pushad-подобного вступления была портянка команд на сохранение каждого регистра. Комп у меня был не ахти какой быстрый, решил тогда повременить. А потом не до этого стало. Пишу по памяти )) Имеется сейчас актуальный pas-файл для подключения патчера и ссылка на сам патчер последней версии? -------------------- |
|
|
09 Nov 2017, 22:23
Сообщение
#22
|
|
Immortal Сообщений: 2 412 Спасибо сказали: 4617 раз |
в патчере в мосте лоу-хука до сих пор портянка команд на сохранение каждого регистра
Для все той же обратной совместимости. Более того, в коде моста много данных в регистр/стек попадают через кучу (временные переменные). сейчаc код моста вот такой: Порядок регистров, адреса возврата и регистра флагов здесь продиктован лишь обратной совместимостью. Да, можно изменив порядок этого всего внутри HookContex сделать короче и красивее. Но мне лично лень ломать голову как это сделать я не ASM-нинзя, здесь скорее кто-то вроде MasterOfPuppets нужен. Предложите ваше решение и я добавлю в патчер "NewFastLoHook" хук. а старый LoHook останется для обратной совместимости. Но вообще от этой страшной медленной портянки никто, кроме тебя не страдает. Да и начиная с версии 4 используя PatcherInstance::WriteAsmHook или Patcher::WriteAsmCode можно легко написать низкоуровневый хук точно и тонко самому определив код его моста. Цитата Имеется сейчас актуальный pas-файл для подключения патчера и ссылка на сам патчер последней версии? актуального pas-файла нет, так как он уже несколько лет (!) никому не нужен. можно использовать старый, но, очевидно, не будет доступен добавленный позднее функционал (например Asm-патчи и Asm-хуки) Здесь последний патчер версии 4.2.8, хэдер для C++ версии 4.2 и pas-файл версии 2.1: скачать патчер SDK -------------------- |
|
|
10 Nov 2017, 23:33
(Сообщение отредактировал Berserker - 11 Nov 2017, 13:38)
Сообщение
#23
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Спасибо, старого файла хватит вполне, сам добавлю при необходимости.
Самый простой переходник: Код pushad push esp; mov eax, [адрес обработчика] call eax test eax, eax // выполнять ли результат по умолчанию jz skip_default popad add esp, 4 затёртые команды + nop push адрес возврата ret skip_default: popad ret Я так понимаю, обращение к стеку гораздо быстрее с точки зрения процессора (кэш, предсказание), чем к куче в совсем другой области памяти. Но профилирования не проводил. -------------------- |
|
|
11 Nov 2017, 16:13
(Сообщение отредактировал baratorch - 11 Nov 2017, 16:18)
Сообщение
#24
|
|
Immortal Сообщений: 2 412 Спасибо сказали: 4617 раз |
озадачился я вопросом скорости выполнения моста
состряпал тест вот с таким кодом: скачать тестовый экзешник результат несколько неожиданный на моем рабочем ноуте такой: pushad... : 3074 push edi...: 2386 push eax...: 2590 на лобби сервере для Хоты: pushad... : 6719 push edi...: 4547 push eax...: 4625 * можно посмотреть в отладчике что тест выполняется честно * при перестановке местами вариантов результаты теста не меняются то есть pushad получается самым медленным. а именно та портянка, которая используется сейчас в патчере - самая быстрая. *** приведенный мной выше код моста я сократил и убрал перемещение значений esp и регистра флагов через кучу: но как сделать перемещение адреса возврата не через кучу я не представляю, кажется что сохранив функционал этого не сделать.. вобщем теперь переживать из-за размера моста можно будет меньше. кстати в дампе патчера есть записи bridge memory - это память выделенная патчером под все мосты. bridges sizes sum - это суммарный размер всех мостов. -------------------- |
|
|
11 Nov 2017, 16:18
(Сообщение отредактировал Berserker - 11 Nov 2017, 16:20)
Сообщение
#25
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Привет, спасибо за тест . Удивлён несколько. Хотя в тесте и нет доступа к памяти в куче (.
Дорабатываю сейчас Эру до поддержки скомпилированных map-карт. Вижу, что патчер используется уже давно в качестве обязательного: (* Remove Erm trigger "BeforeSaveGame" call *) Core.p.WriteDataPatch($7051F5, ['9090909090']); Не использовались только переходники на LoHook. Гляну, можно ли безболезненно подменить одни вызовы другими. Если вызываемая функция получает один и тот же контекст в качестве аргумента, имеет соглашение stdcall, то должно сработать. -------------------- |
|
|
11 Nov 2017, 16:49
Сообщение
#26
|
|
Immortal Сообщений: 2 412 Спасибо сказали: 4617 раз |
Цитата(Berserker) Самый простой переходник:... Ну ты привел код из эры. Я его итак видел. Но твой код отличается по функционалу: во-первых, твой код будет портить регистр флагов, а мой нет (причем у меня в хуке можно читать и менять флаги) во-вторых с твоим мостом нельзя будет внутри хука делать push (на вог-форуме я уже писал об этом) в-третьих я не очень понимаю как с твоим кодом внутри хука можно работать с адресом возврата. у меня на мост посылает команда jmp, а у тебя чтоли call раз адрес возврата уже запушен до начала выполнения моста? В любом случае если делать возможность полноценной работы с esp (push) внутри хука, то обойтись простыми push/pop с адресом возврата не получится. Цитата(Berserker) Если вызываемая функция получает один и тот же контекст в качестве аргумента, имеет соглашение stdcall, то должно сработать. так не один и тот же контекст же. у меня в обратном порядке регистры, поэтому и отдельными пушами перемещаются. -------------------- |
|
|
11 Nov 2017, 18:08
(Сообщение отредактировал Berserker - 11 Nov 2017, 18:09)
Сообщение
#27
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Странно, что я pushfd/popfd не добавил раньше. Если при заменах call-ов флаги априори и не должны сохраняться, то при перехвате обычных инструкций ещё как должны (.
Адрес возврата менялся через структуру контекста. Context.Ret := новое значение. А вот push-подобая работа со стеком за всё время, если честно, не пригодилась. С отладочными картами работу закончил (возможность получить человекочитаемый адрес по PE-модулю + смещению вида "Era.3E86C (PoTweak.OnBeforeResetErmFunc in PoTweak.pas on line 117)"). Попробую сейчас перенаправить большую часть перехватов на patcher. Сообщу о проблемах, если будут PS. Модуль VFS у меня тоже на патчере с тех самых пор. Разве что небольшой рефакторинг делаю, уходя от dword в pointer. -------------------- |
|
|
11 Nov 2017, 18:57
(Сообщение отредактировал Berserker - 11 Nov 2017, 18:57)
Сообщение
#28
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Пока что вылеты. Я так понимаю, как LoHook нет метода, возвращающего адрес в рамках моста для вызова оригинальной (замещённой функции) или оригинальных команд с последующим продолжением исполнения, как если бы патча не существовало?.
У меня эти несколько лет Data-патчи уже используют патчер, равно как в достаточно HiHook вызовов. По вылетам смотрю, пока не ясно. Лог выше не актуален, ниже использую уже THookContext патчера. Вот явно кусок патча в куче: https://yadi.sk/i/KGgPuvxG3Pc9LY Но возвращает он по адресу в куче куда-то не туда: https://yadi.sk/i/5vAcbI2y3Pc9Pk Что я сделал: THookContext сделал синонимом PatchApi.THookContext + подправил ApiHook: Код function ApiHook (HandlerAddr: pointer; HookType: integer; CodeAddr: pointer): {n} pointer;
begin if HookType = HOOKTYPE_BRIDGE then begin p.WriteLoHook(CodeAddr, HandlerAddr); end else begin result := Hook(HandlerAddr, HookType, CalcHookSize(CodeAddr), CodeAddr); end; end; // .function ApiHook -------------------- |
|
|
11 Nov 2017, 19:19
(Сообщение отредактировал Berserker - 11 Nov 2017, 20:25)
Сообщение
#29
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Поскольку у Эры с плагинами тоже вопрос обратной бинарной совместимости, сохранение флагов и манипулирование стеком не нужны, то предлагаю в перспективе добавить в патчер отдельный метод WriteEraBridgeHook или что-то в этом роде, который будет иметь идентичный функционал + возвращать адрес кода по умолчанию (который в мосте, то бишь в куче = затёртые команды).
Остальное на 90% и так использует патчер. Но спешить не стоит. Как раз думаю дать возможность функциям установки заплаток связывать заплатки с пользовательским dword. Тогда обработчик (callback) становится делегатом или замыканием, описываемым парой: адрес + указатель на данные. В любом случае в следующем обновлении Эры я включу последнюю версию патчера. На ней уже и работаю. -------------------- |
|
|
11 Nov 2017, 21:01
(Сообщение отредактировал baratorch - 11 Nov 2017, 21:24)
Сообщение
#30
|
|
Immortal Сообщений: 2 412 Спасибо сказали: 4617 раз |
Цитата(Berserker) Я так понимаю, как LoHook нет метода, возвращающего адрес в рамках моста для вызова оригинальной (замещённой функции) или оригинальных команд с последующим продолжением исполнения, как если бы патча не существовало?. Я что-то не понял вопроса. Всмысле можно ли поставить LoHook который ничего не делает (просто выполняет затертый код)? Да, можно: Код int __stdcall NopFunc(LoHook* h, HookContext* c) { return EXEC_DEFAULT; } ... _PI->WriteLoHook(0xAABBCC, NopFunc); или нужно что-то вроде: Код int __stdcall Handler(LoHook* h, HookContext* c) { какой-то код... ... выполнить затертое ... какой-то код... } если да, то такое нельзя сделать. такое есть в WriteAsmHook: _PI->WriteAsmHook("какие-то команды....; _ExecDefault; какие-то команды...", 0); Цитата LoHook нет метода, возвращающего адрес в рамках моста для вызова оригинальной (замещённой функции) для LoHook нет понятия оригинальной (замещенной) функции. LoHook устанавливается просто на код. Что там за код под ним - не имеет значения. Я из кода эры не очень могу понять как устанавливается HOOKTYPE_BRIDGE хук. В самом мосте адрес возврата у тебя не пушится. Но в контексте он есть, так? Значит от попадает в стек до выполнения моста, так? Значит перенаправление на сам мост из оригинального кода идет посредством call? У меня же перенаправление на мост идет посредством jmp а адрес возврата помещается в контекст внутри моста. Второе. Я правильно понимаю что в твоем HOOKTYPE_BRIDGE мосте, если Handler возвращает EXEC_DEFAULT, то мост игнорирует адрес возврата из контекста и прыгает обратно туда, куда должен по умолчанию? У меня же в мост прыгает по адресу возврата из контекста , который мы возможно изменили внутри Handler, в любом случае: вернул ли Handler SKIP_DEFAULT или EXEC_DEFAULT -------------------- |
|
|
11 Nov 2017, 21:21
Сообщение
#31
|
|
Immortal Сообщений: 2 412 Спасибо сказали: 4617 раз |
переделал свой тест
добавил в тестируемый код pushfd - popfd и добавил еще 2 варианта Код pushad... : 10172 push edi...: 9781 push eax...: 9906 old... : 12172 new... : 10984 Код pushad... : 5912 push edi...: 5506 push eax...: 5398 old... : 7566 new... : 6053 old - это то как реализовано перемещение регистров в стек и обратно в последнем опубликованном patcher_x86 4.2.8 через кучу, т.е. некоторые значения проходят путь: регистр -> куча -> стек -> куча -> регистр. new - это новая реализация: регистр -> стек -> стек -> регистр По моему очень неплохо для новой, да и старая показывает результат того же порядка. И это 500 000 000 итераций уж в рамках героев разницей точно можно пренебречь. -------------------- |
|
|
11 Nov 2017, 23:35
(Сообщение отредактировал Berserker - 12 Nov 2017, 00:17)
Сообщение
#32
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Цитата если да, то такое нельзя сделать. Именно так. адрес затёртого кода, хранимого в мосте: [затёртая команда 1] [затёртая команда 2] [возврат на адрес после затёртых команд] Цитата В самом мосте адрес возврата у тебя не пушится. У меня мост реализован в виде CALL. Адрес возврата автоматически попадает в стек. Его можно изменить в обработчике через Context.RetAddress := новое значение. Цитата Значит перенаправление на сам мост из оригинального кода идет посредством call? Да. Цитата У меня же перенаправление на мост идет посредством jmp а адрес возврата помещается в контекст внутри моста. Да, видел. Цитата Второе. Я правильно понимаю что в твоем HOOKTYPE_BRIDGE мосте, если Handler возвращает EXEC_DEFAULT, то мост игнорирует адрес возврата из контекста и прыгает обратно туда, куда должен по умолчанию? Да, именно так. Затёртый код мог содержать команды, изменяющие ESP, я уже сталкивался с такими багами. Поэтому адрес возврата жёстко забит в MOV EAX, [адрес] при EXEC_DEFAULT. Цитата У меня же в мост прыгает по адресу возврата из контекста , который мы возможно изменили внутри Handler, в любом случае: вернул ли Handler SKIP_DEFAULT или EXEC_DEFAULT Были баги, кода перезаписал места вида ADD ESP, XXX или PUSH XXX или POP XXX. У тебя такие проблемы исключены? -------------------- |
|
|
12 Nov 2017, 06:48
Сообщение
#33
|
|
Immortal Сообщений: 2 412 Спасибо сказали: 4617 раз |
Цитата адрес затёртого кода, хранимого в мосте: [затёртая команда 1] [затёртая команда 2] [возврат на адрес после затёртых команд] я не понимаю зачем давать пользователю LoHook'a возможность обращаться к адресу затертого кода в мосте и зачем вызывать такое внутри Handler, ведь на момент вызова этого будет неактуальным и непредсказуемым содержимое регистров. после return EXEC_DEFAULT в Handler в мосте происходит следующее: 1. значения всех регистров из контекста (HookContext::eax, ...) копируются в соответствующие регистры 2. выполняется затертый код (затертая команда 1, затертая команда 2, ...) 3. прыжок на HookContext::return_address. Если внутри Handler он не изменялся, то это будет адрес после затёртых команд в исходном коде. Что тут тебе еще может быть нужно, я не понимаю. Цитата Были баги, кода перезаписал места вида ADD ESP, XXX или PUSH XXX или POP XXX. У тебя такие проблемы исключены? проблем c уcтановкой ЛоуХука на команды изменяющие esp - нет. Цитата(baratorch) Код int __stdcall Handler(LoHook* h, HookContext* c) { какой-то код... ... выполнить затертое ... какой-то код... } Здесь главное то, что после выполнения затертого еще можно выполнить еще какой-то свой код внутри Handler. Под затертым я имею в виду реально затертое (это может быть джамп другого хука, о котором мы не знаем), А так-то мы всегда можем в Handler продублировать затираемый оригинальный код манипуляциями с HookContext::eax, ... -------------------- |
|
|
12 Nov 2017, 10:11
Сообщение
#34
|
|
Power Member Сообщений: 197 Спасибо сказали: 132 раза |
Berserker, вы могли бы подсказать, как использовать патчер для моддинга игры, отличной от Heroes 3, например, Heroes 2, либо для моддинга Heroes 3, но без HD мода ?
|
|
|
12 Nov 2017, 12:32
Сообщение
#35
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
Бара, поделишься самой последней версией патчера, по которой тесты делал? )
И ещё маленький вопрос, у тебя компилятор может детальный map-файл сгенерировать для patcher_x86.dll? Цитата Berserker, вы могли бы подсказать, как использовать патчер для моддинга игры, отличной от Heroes 3, например, Heroes 2, либо для моддинга Heroes 3, но без HD мода ? Вы знаете, это пришлось бы писать очень длинную инструкцию, чего сейчас делать не могу. Обычно требуется сперва навык работы в отладчике, ручной установке патчей и чуть-чуть программной, чтобы вовсю использовать такие инструменты. -------------------- |
|
|
12 Nov 2017, 13:34
Сообщение
#36
|
|
Power Member Сообщений: 197 Спасибо сказали: 132 раза |
Вы знаете, это пришлось бы писать очень длинную инструкцию, чего сейчас делать не могу. Обычно требуется сперва навык работы в отладчике, ручной установке патчей и чуть-чуть программной, чтобы вовсю использовать такие инструменты. Навыки работы в отладчике есть. А если не детальную инструкцию, а какие-то общие принципы/этапы ? |
|
|
12 Nov 2017, 14:40
(Сообщение отредактировал Berserker - 12 Nov 2017, 14:33)
Сообщение
#37
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
У Вас должна быть возможность подгружать свои библиотеки/плагины для изменяемой игры. Поэтому первое, что пишется — это загрузчик. Он либо внедряется в начало исполняемого кода оригинального файла, как Эра:
https://yadi.sk/i/gk4G7ak83PcxrM (в коде инициализации библиотеки нужно выполнить затёртые команды и вернуться) Либо загрузчик — внешний исполняемый файл, который вживляет библиотеку в адресное пространство запускаемого процесса оригинальной игры и ждёт, пока библиотека не закончит инициализацию и не вернёт управление, после чего возобновляет основной поток игры. Пример загрузчика: Вот сам загрузчик, принимающий путь к исполняемому файлу и внедряемой библиотеке через командную строку. Антивирусы на этот файл смотрят с нескрываемым диагнозом «вирус», поскольку можно запустить любой процесс, внедрив в него любую библиотеку, что чревато. https://yadi.sk/d/zBeSI8uk3Pcy8Q Далее Ваш плагин, используя patcher_x86 или другое средство для установки заплаток устанавливает множество перехватчиков по коду, которые будут генерировать события — вести на Ваши обработчики. Событие: «щелчок мышью», событие «оценка ИИ стоимости объекта» и т.д. Для этого предназначены прежде всего WriteHiHook (вы перехватываете начало функции, можете вызвать оригинальную, можете весь функционал заменить) и WriteLoHook (установить перехватчик в любом месте кода с возможность вызова затёртого кода перед возвратом). Примеры плагинов можете попросить у feanor, Sav, SyDr. Небольшие их модули точно используют патчер. Вот и всё. Загрузчик — своя библиотека — установка перехватчиков — обработка событий. Цитата я не понимаю зачем давать пользователю LoHook'a возможность обращаться к адресу затертого кода в мосте Редкие случаи. До перевода части кода на патчер это был функционал SPLICE. Если перехватчик установлен на первые команды функции, то можно вызывать как оригинальную функцию, так и перехваченную. Если перехвачено место, куда идёт несколько прыжков, то адрес в одном из прыжков можно заменить на адрес выполнения затёртого кода, а в другом оставить адрес перехваченного кода. Будут две разные ветви исполнения. Я реально использовал только SPLICE-подобную механику и то, до перехода на патчер. Тем не менее, в АПИ Эры функция возвращает указатель на выполнение затёртого кода. Как этот указатель будет использоваться — решает пользователь. Цитата проблем c уcтановкой ЛоуХука на команды изменяющие esp - нет. Значит у тебя более продвинутая реализация, это хорошо. -------------------- |
|
|
12 Nov 2017, 14:56
Сообщение
#38
|
|
Power Member Сообщений: 197 Спасибо сказали: 132 раза |
Спасибо ! Очень интересно !
|
|
|
12 Nov 2017, 15:55
Сообщение
#39
|
|
Immortal Сообщений: 1 468 Спасибо сказали: 1151 раз |
На здоровье! Успехов Вам )
-------------------- |
|
|
12 Nov 2017, 17:08
Сообщение
#40
|
|
Power Member Сообщений: 197 Спасибо сказали: 132 раза |
Далее Ваш плагин, используя patcher_x86 или другое средство для установки заплаток устанавливает множество перехватчиков по коду, которые будут генерировать события — вести на Ваши обработчики. Плагинов я уже написал определенное количество, но для их подгрузки использовался интерфейс HD мода. Чтобы понять, как подгрузку плагинов выполнить с помощью самого патчера, следует внимательно изучить его исходники ? |
|
|
Текстовая версия | Сейчас: 18 April 2024 - 14:32 |
Copyright by Алексей Крючков
Programming by Degtyarev Dmitry |