Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: patсher_x86
DF2 :: ФОРУМЫ > Игровые форумы > Heroes of Might & Magic III > HD-мод
Страницы: 1, 2
baratorch
Патчер - это инструмент для модификации любого исполняемого кода (не только героев). Патчер делает модинг максимально стандартизованным, удобным и безопасным. А так же помогает достичь максимальной совместимости между несколькими независимо разрабатываемыми модификациями одного кода (пример - Хд, Хота и ХВ-рулз).


С хд и хотой поставляется уже патчер версии 4.2.2
в нем теперь нет понятия неотменяемого патча.
появились методы WriteAsmPatch и WriteAsmHook, которые позволяют писать конструкции типа:

Код
_PI->WriteAsmHook(0x4F8662,
    "push 1",
    "call %d", Sleep,
    "_ExecDefault",
    0);


Код
            _PI->WriteAsmPatch(0x59D715,
                "MOVZX ESI,BYTE PTR DS:[EAX + EBX - 0x159]",
                "MOV EAX, DWORD PTR DS:[EBX + 0x60]",
                "TEST EAX,EAX",
                "JNZ SHORT 0x59D72A",
                "CMP DWORD PTR DS:[EBX + 0x74], 1",
                "JE SHORT 0x59D735",
                "CMP EAX, 1",
                "JNZ SHORT 0x59D7A9",
                "CMP DWORD PTR DS:[EBX + 0x74], 2",
                "JNZ SHORT 0x59D7A9",
                "PUSH DWORD PTR DS:[EBX + 0x6C]",
                "PUSH DWORD PTR DS:[EBX + 0x68]",
                "MOV ECX, DWORD PTR DS:[EBX + 0x64]",
                "PUSH ESI",
                "CALL 0x4E54B0",
                "MOV ECX, DWORD PTR DS:[EBX + 0x64]",
                "MOVSX ECX, WORD PTR DS:[ECX + 0x18]",
                "CMP EAX, ECX",
                "JG SHORT 0x59D762",
                "MOV EDX, DWORD PTR DS:[EDI + 0x8]",
                "MOVZX EAX, BYTE PTR DS:[EDX + EBX - 0x159]",
                0);


Так же в новом патчере практически устранена разница между Code и Data патчами. Т.е. Code патч всегда пишет код именно так как мы задумали, без неочевидных конвертаций относительных адресов.
И еще ряд мелких правок и багфиксов.

***

Кому-нибудь это вообще интересно?
Расписывать ли мне здесь общую документацию, изменения и документацию к новому функционалу?
Кто-нибудь, кроме меня, Сава и феанора еще пользуется патчером? feanor, тебе это интересно?
Просто у меня весьма ограничено свободное время и совершенно не хочется его тратить впустую.

На вог-форуме есть тема по патчеру, но, к сожалению, цель с которой я эту тему там создавал - не достигнута.
Эра так и не встала на рельсы патчера. Берсу не до эры и тем более не до патчера, поэтому писать туда смысла не вижу.
Etoprostoya
А patcher_x64 сложно будет сделать?
igrik
Я пользуюсь патчером. И да - мне это интересно.
Но пользуюсь я им не настолько глубоко, чтобы делать что-то грандиозное.
baratorch
Цитата(Etoprostoya @ 21 Oct 2016, 16:10) *
А patcher_x64 сложно будет сделать?

У меня лично в нем нет потребности и я не знаю особенностей x64
И если учесть что надо будет писать другие

то да, для меня сделать сложно. Но вообще - реально.

Основные проблемы, короче, - это дизассемблер длин и ассемблер, остальное - уже решаемо мной.
Причем дизассемблер длин нужен компактный и быстрый, писанный на асм/си/си++.
А ассемблер тоже нужен с минимальнейшим функционалом, лишь бы все инструкции понимал. + должен быть написан на си/си++. Я когда искал подходящего кандидата для x86 натыкался либо на каких-то громоздких суперфункциональных чудовищ либо на ущербов, которые даже не все 86ые инструкции переваривали. Ассемблер из OllyDbg - идеален для наших задач, но я нашел его не сразу. Насколько мне известно подобного для х64 - нет (в смысле - открытых исходников).

В принципе можно обойтись и без ассемблера, как без него обходился патчер x86 до версии 4.0. Он нужен только чтобы полностью повторить имеющийся сейчас функционал.
XEPOMAHT
Теоретически не представляю как пользоваться patсher_x86, да хотя бы как просто подключить его к третьим Героям без HD-мода или ХотА. Да и когда отсутствует разобранный диассамблированный код третьих Героев, не знаю, что смогу наменять в Героях, скорее добавлю какие-нибудь баги и вылеты. И на каком языке что-то писать под patсher_x86 (ERM-костылями неудобно, а Си-крест-крест - это слишком высокоуровнево для низкоуровневых представителей человечества) Поэтому для моддинга ERA/MOP only, patсher_x86 is not. И документация к patсher_x86 вряд ли поможет зелёным новичкам типа меня - это уже бесполезно в силу древности игры и минимальному интересу к моддингу у игроков в Героев в целом (может быть patсher_x86 может поднять интерес к моддингу Героев, но вероятность этого крайне низка - эта вещь сделана для программистов/хакеров, а не для обычных моддеров, лично для меня patсher_x86 не представляет интереса, когда есть более понятные и доступные для использования в третьих Героях платформы ERM/WERD), тем более нужно им понадобиться очень подробная документация с примерами "patсher_x86 для чайников" - может ли такую написать профессиональный программист (по себе могу сказать, что у меня ушло много месяцев на расжёвывания документации по ERM, вряд ли получится разобраться в документации patсher_x86, если она всё-таки будет написана)?
baratorch
XEPOMAHT, да, pather_x86 - это инструмент именно для программиста, причем такого, кто готов реверсить код, работая с отладчиками и дизассемблерами.
просто патчер облегчает (ускоряет!) работу, по реверсингу в том числе.
Мне и интересно сколько здесь таких.
t800
Цитата(baratorch @ 21 Oct 2016, 19:55) *
XEPOMAHT, да, pather_x86 - это инструмент именно для программиста, причем такого, кто готов реверсить код, работая с отладчиками и дизассемблерами.
просто патчер облегчает (ускоряет!) работу, по реверсингу в том числе.
Мне и интересно сколько здесь таких.


Здравствуйте! А каким образом Ваш патчер ускоряет работу по ререрсингу? Просто летом в свою IDA поставил плагин от James Koppel https://www.hex-rays.com/contests/2011/index.shtml им можно прямо в дебагере патчить и сразу смотреть что получилось.

ЗЫ Если еще если вы дадите инструкцию с каким-нибудь примерам как ползоваться и что можно интересного сделать Вашим патчером, то если хотете я мог бы мог сделать видеоурок про Ваш парчер. Вот.
Etoprostoya
В составе Object File Converter есть дизассемблер с открытым исходным кодом http://www.agner.org/optimize/#objconv Я с ним детально не разбирался, но там очень широкий (если не полный, вплоть до всяких FMA, AVX, AVX512) диапазон распознаваемых и дизассемблируемых команд.
Вот сейчас взглянул на фрагмент кода (opcodes.cpp):

Ну и вообще, у Агнера Фога много хороших книжек на тему оптимизации ассемблерной, сишной, вместе с примерами и библиотеками.
feanor
Цитата
feanor, тебе это интересно?

В принципе, интересно, но если инклуд будет комментирован на уровне прошлых версий - можно и обойтись, если не до того.
baratorch
Положил на дропбокс патчер 4.2.3 (отличается от 4.2.2 меньшим размером) и обновленный хэдер для Си++:
https://dl.dropboxusercontent.com/u/5667529...r_x86%204.2.zip

Итак, изменения версии 4.2 относительно 2.8:

[!] Переработана структура хранения примененных патчей.
Теперь патчи группируются не по адресу установки, как было, а по динамическим диапазонам (отрезкам): начальный адрес - конечный адрес.
В одном таком динамическом отрезке хранятся все пересекающиеся патчи.
Если применяется один патч поверх другого со смещением или отличающийся по размеру - то этот патч помещается в тот же отрезок, при этом отрезок расширяется (меняются его начальный адрес и конечный адрес)
Пока (в версии 4.2), отрезки при применении патчей - создаются, расширяются. Но не уменьшаются и не удаляются при отмене.
Т.е. возможна ситуация когда в одной стопке хранятся не пересекающиеся патчи (когда патч пересекающий их обоих отменен).

В связи с этим теперь:
- можно отменить любой патч, в том числе и перекрытый другим со смещением. FIXED патчей теперь нет.
- методы, GetLastPatchAt GetFirstPatchAt, все зависимые (UndoAllAt и пр.) теперь ищут патч не по адресу установки, а по попаданию в существующий диапазон (в patcher_x86.cpp хэдере используется термин "в окрестности адреса address" вместо "по адресу address").
(методы BlockAt и BlockAllExcept по прежнему оперируют адресами установки)
- изменились форматы дампа и лога патчера.

дамп:
Код
[ ][ ][ ] 3: (00401510 05 HiHook 0000001843 - HD.HotA), (00401510 05 HiHook 0000001845 - HD.HotA), (00401510 05 HiHook 0000002968 - HD.Af)
[ ][s][t] 2: (00424389 04 Patch  0000003595 - HotA), (00424389 06 LoHook 0000003596 - HotA)
[ ][s][t] 2: (0042440B 03 Patch  0000003597 - HotA), (0042440B 05 LoHook 0000003598 - HotA)
[a][s][t] 2: (005A8048 05 HiHook 0000002731 - HD.HotA), (005A8014 64 Patch  0000005395 - HotA)
[a][s][ ] 2: (005F946E 03 Patch  0000001782 - HD.HotA), (005F9470 01 Patch  0000003184 - HD.True32)

[a] значит в стопке есть патчи не совпадающие по адресам установки
[s] значит в стопке есть патчи не совпадающие по размеру
[t] - по типу
число после типа патча - это общая глобальная очередность применения.

в логе ворнинги теперь выглядят так:

Код
WARNING! [a][s][t]: Applying LoHook (00432E91 10 - HotA) over already applied Patch  (00432E97 04 - HotA).




[!] теперь для установки EXTENDED_(и SAFE_) CDECL_ HiHook-хуков можно использовать и __stdcall функции (возможность использовать __cdecl функции сохранена)
- это было продекларировано для более ранних версий патчера, но с какой-то версии сломалось и не работало.
Таким образом для EXTENDED_ хука хук-функция имеет единый вид: ? __stdcall new_func(HiHook* hook, ?)

[-] исправлен баг с применеием-отменой-применением хуков поверх длинных патчей (размером больше 5, на практике это серии NOP'ов)

[!] изменена часть мостов хай-хуков, позволяющая устанавливать хуки на рекурсивно выполняющиеся функции. Теперь для не рекурсивных вызовов выполняется один (максимально быстрый и безопасный) код, а для рекурсивных - другой. Поскольку не рекурсивных функций обычно 99+% , такой подход очень сильно повлиял на быстродействие и стабильность.

[ ] теперь патчер жрет на ~ 1.2 - 1.5 мегабайта оперативы меньше (в сравнение с версиями 2.7, 2.8, ...).

[ ] практически устранена разница между CODE_ и DATA_ патчами. Т.е. Code патч всегда пишет код именно так как мы задумали, без неочевидных конвертаций относительных адресов. Таким образом вызовы PatcherInstance::Write(a, d, CODE_) и PatcherInstance::Write(a, d, DATA_) теперь идентичны.

[+] добавлены новые методы PatcherInstance: WriteAsmPatch и WriteAsmHook (а так же CreateAsmPatch и CreateAsmHook):


***

в хэдере в методе
virtual LoHook* __stdcall WriteLoHook(_ptr_ address, _LoHookFunc_ func) = 0;
изменен тип func с void* на _LoHookFunc_, чтобы
в MS VC++ компиляторах (в VC++ 2010 и новее) методу можно было скармливать лямбды:

Код
        _PI->WriteLoHook(0xAABBCC, [](LoHook* h, HookContext* c) -> int
        {
            o_MsgBox("Hello from lambda LoHook!");
            return EXEC_DEFAULT;
        });


так же для MS VC++ 2010 и новее
я сделал такой макрос (через лямбду опять же):

чтобы можно было ставить "быстрые инлайн" хайхуки:

Код
                Create_HiHook(0xAABBCC, SPLICE_, EXTENDED_,
                    int, __thiscall, FuncName, (int in, int in2),
                    {
                        //...
                        return DefaultFunc(in, in2);
                    }
                )->ApplyInsert(0);


правда пользоваться ими для написания больших хуков неудобно, потому что IntelliSence не работает как надо внутри.


***

позже буду обновлять первый пост - добавлять в него актуальную информацию, ссылки, примеры.
feanor
Лямбды, ня!
baratorch
Цитата(t800 @ 21 Oct 2016, 19:39) *
Здравствуйте! А каким образом Ваш патчер ускоряет работу по ререрсингу?

Несколько примеров из личного опыта:

1. Дано:
В ИДА мы нашли конструктор интересующего нас объекта, в конструкторе нашли виртуальную таблицу, отреверсили определенную функцию F (т.е. +- поняли что она делает) в этой виртуальной таблице.
Найти: откуда в программе эта функция F вообще вызывается?
Решение: ставим с помощью патчера SPLICE_ EXTENDED_ хук на эту функцию F
Внутри хука делаем запись в лог (например в ХД я использую вывод в консоль) результат hook->GetReturnAddress()
так же при этом мы можем в лог писать значения входных аргументов и результат выполнения функции F, например.
Запускаем программу (Героев), совершаем действия, смотрим лог.



2. Нам нужно в отладчике (OllyDbg) поставить брэйкпоинт на чтение или изменение определенного поля определенной структуры в определенный момент (т.е. после выполнения определенных действий).
Решение: ставим хук на нужное место в коде (это место, понятно, должно быть отреверсено), делаем вывод адреса нтересующего нас поля (обычно адрес - динамический), нужной структуры, например с помощью MessageBox
- что одновременно "ставит на паузу" выполнение программы (Героев), позволяя нам поставить бряк в отладчике на нужный адрес. Постановка на паузу особенно актуальна, если интересующее нас поле ДО интересующего нас момента может меняться в программе хренову тучу раз.
Нажимаем ОК в мессадж-боксе - смотрим где сработал бряк.

3. Установка элементарной точки трассировки:

Этого можно было бы добиться установкой бряка в отладчике. Но бряк останавливает выполнение программы, и в случае когда код выполняется много раз в короткий промежуток времени, такой метод (установка ьряка) совсем не юзабелен.

Думаю, те, кто занимается реверсингом эти методы (о)ценят. Хотя возможно я не все знаю о возможностях Olly и IDA и их инструментами можно достичь того же. Если так, то просветите меня, пожауйста.


Цитата
Просто летом в свою IDA поставил плагин от James Koppel https://www.hex-rays.com/contests/2011/index.shtml им можно прямо в дебагере патчить и сразу смотреть что получилось.
я плагин не смотрел, но позволяет ли он менять оригинальный код на новый большей длины? позволяет ли писать новый код не на асме а на с++, скажем?
Если нет, то этот плагин совсем несравним с патчером.

UPD.
сейчас в сети нашел как поставить брейкпоинт на чтение/зменение памяти программно из нашего кода
- в сочетании с Примером 2 вообще удобнота будет.
igrik
А в чем принципиальная разница Hi и Lo хуков?
И еще вопрос: можно ли как-то вывести диалог, с выбором одного из 7-ми элементов. Элементы - изображения ресурсов (дерево и т.д.). (Хотя думаю вопрос не совсем в теме)
t800
Цитата(baratorch @ 25 Oct 2016, 11:05) *
Цитата
Просто летом в свою IDA поставил плагин от James Koppel https://www.hex-rays.com/contests/2011/index.shtml им можно прямо в дебагере патчить и сразу смотреть что получилось.
я плагин не смотрел, но позволяет ли он менять оригинальный код на новый большей длины? позволяет ли писать новый код не на асме а на с++, скажем?


Нет не позволяет, только каманды asm можно писать


Цитата(baratorch @ 25 Oct 2016, 11:05) *
Цитата(t800 @ 21 Oct 2016, 19:39) *
Здравствуйте! А каким образом Ваш патчер ускоряет работу по ререрсингу?

Несколько примеров из личного опыта:

1. Дано:
В ИДА мы нашли конструктор интересующего нас объекта, в конструкторе нашли виртуальную таблицу, отреверсили определенную функцию F (т.е. +- поняли что она делает) в этой виртуальной таблице.
Найти: откуда в программе эта функция F вообще вызывается?
Решение: ставим с помощью патчера SPLICE_ EXTENDED_ хук на эту функцию F
Внутри хука делаем запись в лог (например в ХД я использую вывод в консоль) результат hook->GetReturnAddress()
так же при этом мы можем в лог писать значения входных аргументов и результат выполнения функции F, например.
Запускаем программу (Героев), совершаем действия, смотрим лог.


Может я что-то не понял, а разве по Блок Схеме в IDA нельзя просто по стрелочке глянуть откуда функция F вызывается?
tolich
Цитата(t800 @ 25 Oct 2016, 21:43) *
Может я что-то не понял, а разве по Блок Схеме в IDA нельзя просто по стрелочке глянуть откуда функция F вызывается?
Только статические вызовы функций. Динамические вызовы, т.е., через указатель на функцию и вызовы виртуальных функций в статике в принципе не отследить.
baratorch
Цитата
А в чем принципиальная разница Hi и Lo хуков?

HiHook - это "высокоуровневый" хук. Он может ставиться только на функцию.
SPLICE_ - на саму функцию.
CALL_ - на ее конкретный явный вызов (call 0xAABBCC или call dword ptr [0xAABBCC])
FUNCPTR_ - на указатель, т.е. например на позицию в виртуальной таблице или таблице импорта, или на колбэк, передаваемый аргуменом в другую функцию).

SLPICE_ - используется если нам всегда и везде нужно измененное поведение функции, откуда бы она ни вызывалась.
СALL_ - если нам нужно измененное поведение функции только в этом конкретном вызове.
FUNCPTR_ чаще применяется когда по-другому функцию не захучить (функция из таблицы импорта)
если мы ставим FUNCPTR_ хук на функцию из таблицы импорта, то в рамках игры получается тот же эффект, что от SPLICE_ хука.
Если поставим FUNCPTR_ хук на функцию в виртуальной таблице, то наша измененная функция будет вызываться только тем объектом - чья виртуальная таблица. Хотя сама функция при этом может входить в другие виртуальные таблицы других объектов, и на их поведение наш FUNCPTR_ хук уже влиять не будет.

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

LoHook - это, соответственно, "низкоуровневый" хук. Его мы можем поставить вообще на любое место в коде. При этом мы получаем возможность работать с регистрами процессора как с переменными, используя их в нашем (допустим С++) коде и можем менять адрес возврата.
LoHook - гораздо более мощный и универсальный инструмент, в сравнение с HiHook'ом. Им можно решать любые задачи (даже те что решают SPLICE_ и CALL_ хайхуки).

Но я в ХД использую в основном именно ХайХуки - ибо с ними код гораздо более читаемый, удобный для правок, безопасный и минимально конфликтен с другими модификациями.
ЛоуХуки стараюсь использовать как можно меньше. Я их использую в крайних случаях, когда это максимально эффективно (в плане объема работ и скорости внедрения изменений).


Цитата
И еще вопрос: можно ли как-то вывести диалог, с выбором одного из 7-ми элементов. Элементы - изображения ресурсов (дерево и т.д.). (Хотя думаю вопрос не совсем в теме)

можно, конечно. Может быть найду время и выложу UI SDK для героев с примерами.
igrik
Цитата(baratorch @ 26 Oct 2016, 08:57) *
Цитата(igrik)
И еще вопрос: можно ли как-то вывести диалог, с выбором одного из 7-ми элементов. Элементы - изображения ресурсов (дерево и т.д.). (Хотя думаю вопрос не совсем в теме)

Можно, конечно. Может быть найду время и выложу UI SDK для героев с примерами.

Это было бы идеально.
AlexSpl
Цитата
Патчер - это инструмент для модификации любого исполняемого кода (не только героев).

Кто-нибудь может привести пример, как использовать патчер для моддинга других игр (например, Героев 2)?
baratorch
Цитата(AlexSpl @ 17 Sep 2017, 22:25) *
Цитата
Патчер - это инструмент для модификации любого исполняемого кода (не только героев).

Кто-нибудь может привести пример, как использовать патчер для моддинга других игр (например, Героев 2)?

Сейчас я в отпуске. Вернусь, покажу пример модинга героев 2. Я же начал делать ХД мод для двойки, но запнулся о локализацию, просто обломало ее делать. И работа дальше не пошла, хотя двойка (экзешник с сигнатурами) - благодатнейшая платформа для модинга.
AlexSpl
Было бы очень здорово! Патчить вручную не самое приятное занятие, а возможности патчера я уже успел оценить smile.gif
Berserker
Цитата
На вог-форуме есть тема по патчеру, но, к сожалению, цель с которой я эту тему там создавал - не достигнута.

Если не ошибаюсь, в то время, как функции установки заплаток Эры с самого начального этапа поддерживали рекурсию, на хуках патчера я столкнулся с багом. После поддержка рекурсии была добавлена, но вместо pushad-подобного вступления была портянка команд на сохранение каждого регистра. Комп у меня был не ахти какой быстрый, решил тогда повременить. А потом не до этого стало. Пишу по памяти ))

Имеется сейчас актуальный pas-файл для подключения патчера и ссылка на сам патчер последней версии?
baratorch
в патчере в мосте лоу-хука до сих пор портянка команд на сохранение каждого регистра
Для все той же обратной совместимости.
Более того, в коде моста много данных в регистр/стек попадают через кучу (временные переменные).
сейча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
Berserker
Спасибо, старого файла хватит вполне, сам добавлю при необходимости.

Самый простой переходник:
Код
pushad
push esp;
mov eax, [адрес обработчика]
call eax
test eax, eax // выполнять ли результат по умолчанию
jz skip_default
popad
add esp, 4
затёртые команды + nop
push адрес возврата
ret

skip_default:
popad
ret


Я так понимаю, обращение к стеку гораздо быстрее с точки зрения процессора (кэш, предсказание), чем к куче в совсем другой области памяти. Но профилирования не проводил.
baratorch
озадачился я вопросом скорости выполнения моста
состряпал тест вот с таким кодом:




скачать тестовый экзешник

результат несколько неожиданный

на моем рабочем ноуте такой:
pushad... : 3074
push edi...: 2386
push eax...: 2590

на лобби сервере для Хоты:
pushad... : 6719
push edi...: 4547
push eax...: 4625

* можно посмотреть в отладчике что тест выполняется честно
* при перестановке местами вариантов результаты теста не меняются

то есть pushad получается самым медленным.
а именно та портянка, которая используется сейчас в патчере - самая быстрая.


***

приведенный мной выше код моста я сократил и убрал перемещение значений esp и регистра флагов через кучу:

но как сделать перемещение адреса возврата не через кучу я не представляю,
кажется что сохранив функционал этого не сделать..

вобщем теперь переживать из-за размера моста можно будет меньше.

кстати в дампе патчера есть записи
bridge memory - это память выделенная патчером под все мосты.
bridges sizes sum - это суммарный размер всех мостов.
Berserker
Привет, спасибо за тест smile2.gif. Удивлён несколько. Хотя в тесте и нет доступа к памяти в куче (.

Дорабатываю сейчас Эру до поддержки скомпилированных map-карт. Вижу, что патчер используется уже давно в качестве обязательного:

(* Remove Erm trigger "BeforeSaveGame" call *)
Core.p.WriteDataPatch($7051F5, ['9090909090']);

Не использовались только переходники на LoHook. Гляну, можно ли безболезненно подменить одни вызовы другими. Если вызываемая функция получает один и тот же контекст в качестве аргумента, имеет соглашение stdcall, то должно сработать.
baratorch
Цитата(Berserker)
Самый простой переходник:...

Ну ты привел код из эры. Я его итак видел.
Но твой код отличается по функционалу:
во-первых, твой код будет портить регистр флагов, а мой нет (причем у меня в хуке можно читать и менять флаги)
во-вторых с твоим мостом нельзя будет внутри хука делать push (на вог-форуме я уже писал об этом)
в-третьих я не очень понимаю как с твоим кодом внутри хука можно работать с адресом возврата.
у меня на мост посылает команда jmp, а у тебя чтоли call раз адрес возврата уже запушен до начала выполнения моста?
В любом случае если делать возможность полноценной работы с esp (push) внутри хука, то обойтись простыми push/pop с адресом возврата не получится.

Цитата(Berserker)
Если вызываемая функция получает один и тот же контекст в качестве аргумента, имеет соглашение stdcall, то должно сработать.

так не один и тот же контекст же.
у меня в обратном порядке регистры, поэтому и отдельными пушами перемещаются.
Berserker
Странно, что я pushfd/popfd не добавил раньше. Если при заменах call-ов флаги априори и не должны сохраняться, то при перехвате обычных инструкций ещё как должны (.
Адрес возврата менялся через структуру контекста. Context.Ret := новое значение. А вот push-подобая работа со стеком за всё время, если честно, не пригодилась.

С отладочными картами работу закончил (возможность получить человекочитаемый адрес по PE-модулю + смещению вида "Era.3E86C (PoTweak.OnBeforeResetErmFunc in PoTweak.pas on line 117)"). Попробую сейчас перенаправить большую часть перехватов на patcher. Сообщу о проблемах, если будут smile2.gif

PS. Модуль VFS у меня тоже на патчере с тех самых пор. Разве что небольшой рефакторинг делаю, уходя от dword в pointer.
Berserker
Пока что вылеты. Я так понимаю, как 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
Berserker
Поскольку у Эры с плагинами тоже вопрос обратной бинарной совместимости, сохранение флагов и манипулирование стеком не нужны, то предлагаю в перспективе добавить в патчер отдельный метод WriteEraBridgeHook или что-то в этом роде, который будет иметь идентичный функционал + возвращать адрес кода по умолчанию (который в мосте, то бишь в куче = затёртые команды).
Остальное на 90% и так использует патчер.

Но спешить не стоит. Как раз думаю дать возможность функциям установки заплаток связывать заплатки с пользовательским dword. Тогда обработчик (callback) становится делегатом или замыканием, описываемым парой: адрес + указатель на данные.

В любом случае в следующем обновлении Эры я включу последнюю версию патчера. На ней уже и работаю.
baratorch
Цитата(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
baratorch
переделал свой тест

добавил в тестируемый код 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 итераций
уж в рамках героев разницей точно можно пренебречь.
Berserker
Цитата
если да, то такое нельзя сделать.

Именно так.

адрес затёртого кода, хранимого в мосте:
[затёртая команда 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. У тебя такие проблемы исключены?
baratorch
Цитата
адрес затёртого кода, хранимого в мосте:
[затёртая команда 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, ...
Ben
Berserker, вы могли бы подсказать, как использовать патчер для моддинга игры, отличной от Heroes 3, например, Heroes 2, либо для моддинга Heroes 3, но без HD мода ?
Berserker
Бара, поделишься самой последней версией патчера, по которой тесты делал? )
И ещё маленький вопрос, у тебя компилятор может детальный map-файл сгенерировать для patcher_x86.dll?

Цитата
Berserker, вы могли бы подсказать, как использовать патчер для моддинга игры, отличной от Heroes 3, например, Heroes 2, либо для моддинга Heroes 3, но без HD мода ?

Вы знаете, это пришлось бы писать очень длинную инструкцию, чего сейчас делать не могу. Обычно требуется сперва навык работы в отладчике, ручной установке патчей и чуть-чуть программной, чтобы вовсю использовать такие инструменты.
Ben
Цитата(Berserker @ 12 Nov 2017, 12:32) *
Вы знаете, это пришлось бы писать очень длинную инструкцию, чего сейчас делать не могу. Обычно требуется сперва навык работы в отладчике, ручной установке патчей и чуть-чуть программной, чтобы вовсю использовать такие инструменты.


Навыки работы в отладчике есть. А если не детальную инструкцию, а какие-то общие принципы/этапы ?
Berserker
У Вас должна быть возможность подгружать свои библиотеки/плагины для изменяемой игры. Поэтому первое, что пишется — это загрузчик. Он либо внедряется в начало исполняемого кода оригинального файла, как Эра:
https://yadi.sk/i/gk4G7ak83PcxrM
(в коде инициализации библиотеки нужно выполнить затёртые команды и вернуться)

Либо загрузчик — внешний исполняемый файл, который вживляет библиотеку в адресное пространство запускаемого процесса оригинальной игры и ждёт, пока библиотека не закончит инициализацию и не вернёт управление, после чего возобновляет основной поток игры. Пример загрузчика:



Вот сам загрузчик, принимающий путь к исполняемому файлу и внедряемой библиотеке через командную строку. Антивирусы на этот файл смотрят с нескрываемым диагнозом «вирус», поскольку можно запустить любой процесс, внедрив в него любую библиотеку, что чревато.
https://yadi.sk/d/zBeSI8uk3Pcy8Q

Далее Ваш плагин, используя patcher_x86 или другое средство для установки заплаток устанавливает множество перехватчиков по коду, которые будут генерировать события — вести на Ваши обработчики. Событие: «щелчок мышью», событие «оценка ИИ стоимости объекта» и т.д. Для этого предназначены прежде всего WriteHiHook (вы перехватываете начало функции, можете вызвать оригинальную, можете весь функционал заменить) и WriteLoHook (установить перехватчик в любом месте кода с возможность вызова затёртого кода перед возвратом). Примеры плагинов можете попросить у feanor, Sav, SyDr. Небольшие их модули точно используют патчер.

Вот и всё. Загрузчик — своя библиотека — установка перехватчиков — обработка событий.

Цитата
я не понимаю зачем давать пользователю LoHook'a возможность обращаться к адресу затертого кода в мосте

Редкие случаи. До перевода части кода на патчер это был функционал SPLICE. Если перехватчик установлен на первые команды функции, то можно вызывать как оригинальную функцию, так и перехваченную.
Если перехвачено место, куда идёт несколько прыжков, то адрес в одном из прыжков можно заменить на адрес выполнения затёртого кода, а в другом оставить адрес перехваченного кода. Будут две разные ветви исполнения. Я реально использовал только SPLICE-подобную механику и то, до перехода на патчер.

Тем не менее, в АПИ Эры функция возвращает указатель на выполнение затёртого кода. Как этот указатель будет использоваться — решает пользователь.

Цитата
проблем c уcтановкой ЛоуХука на команды изменяющие esp - нет.

Значит у тебя более продвинутая реализация, это хорошо.
Ben
Спасибо ! Очень интересно !
Berserker
На здоровье! smile2.gif Успехов Вам )
Ben
Цитата(Berserker @ 12 Nov 2017, 14:40) *
Далее Ваш плагин, используя patcher_x86 или другое средство для установки заплаток устанавливает множество перехватчиков по коду, которые будут генерировать события — вести на Ваши обработчики.



Плагинов я уже написал определенное количество, но для их подгрузки использовался интерфейс HD мода. Чтобы понять, как подгрузку плагинов выполнить с помощью самого патчера, следует внимательно изучить его исходники ?
Ben
Цитата(Berserker @ 12 Nov 2017, 14:40) *
Вот и всё. Загрузчик — своя библиотека — установка перехватчиков — обработка событий.


Ну в общем, понятен принцип. Сами плагины, видимо, могут загружаться либо загрузчиком же, либо библиотекой-патчером, на усмотрение разработчика.
feanor
Цитата
Сами плагины, видимо, могут загружаться либо загрузчиком же, либо библиотекой-патчером, на усмотрение разработчика.
Библиотека-патчер ничего не может загружать по той простой причине, что она библиотека.

Загружать чужой код в общем случае можно либо правкой исходного файла (LoadLibrary куда-то на старт), либо лаунчером с инъекцией кода (разными способами).
(в частном - еще всяческие трюки с поиском уязвимости, типа переполнения буфера)
Berserker
Ben, если речь идёт о системе плагинов, то используйте AngelRun (или любой другой загрузчик dll в чужой процесс), а в самой dll выполните FindFirstFile/FindNextFile/FindClose в определённой папке, загружая все файлы с нужным расширением через LoadLibrary.
baratorch
Здесь последний патчер версии 4.2.9.1, хэдер для C++ версии 4.2 и pas-файл версии 2.1:
скачать патчер SDK
Berserker
Бара, есть возможность скомпилировать ту же DLL с настройкой генерации detailed *.map-файла в linker?
Open the project's Property Pages dialog box. For details, see Setting Visual C++ Project Properties.
Click the Linker folder.
Click the Debug property page.
Modify the Generate Map File property.

P.S. https://yadi.sk/i/CY2S8Ku63PpTzc
Если я верно понимаю, то LoHook не подходит для многопоточных приложений — гонка данных на запись одного и того же значения в оперативной памяти по абсолютному адресу. Такие баги сложно отловить.

P.S.S. В связи с этим у меня вопрос, являются ли потокобезопасными HiHook-перехватчики? У меня модуль виртуальной файловой системы полностью работает на них. Файловые функции с разных потоков дёргаются. Единственное, что спасает — всё защищено критическими секциями.
Ben
Цитата(Berserker @ 16 Nov 2017, 22:46) *
Бара, есть возможность скомпилировать ту же DLL с настройкой генерации detailed *.map-файла в linker?
Open the project's Property Pages dialog box. For details, see Setting Visual C++ Project Properties.
Click the Linker folder.
Click the Debug property page.
Modify the Generate Map File property.


Map файл для версии 2.8:
https://yadi.sk/i/J-ZvmeWu3Pp79m
Berserker
В дополнение к предыдущему посту. Бара, ты используешь VirtualProtect для того, чтобы дать странице памяти права READ_WRITE_EXECUTE, но не возвращаешь оригинальные права обратно после применения патча. А это значит, что любая прямая запись позже в ту же страницу будет успешной, а не приводить к Access Violation. Может быть стоит возвращать странице оригинальные права после внесения правок?

Код
{!} Assert(Utils.IsValidBuf(Dst, Count));
  {!} Assert((Src <> nil) or (Count = 0));
  result := Count = 0;

  if not result then begin
    result := Windows.VirtualProtect(Dst, Count, Windows.PAGE_EXECUTE_READWRITE, @OldPageProtect);
    
    if result then begin
      Utils.CopyMem(Count, Src, Dst);
      result := Windows.VirtualProtect(Dst, Count, OldPageProtect, @OldPageProtect);
    end; // .if
  end; // .if
baratorch
Berserker, в патчере целиком и полностью забито на многопоточность.
Точнее в патчере:
1. нельзя создавать/ставить/отменять патчи/хуки в несколько потоков.
2. нельзя выполнять код с LoHook и HiHook (кроме DIRECT_) хуками в несколько потоков.
можно в несколько потоков выполнять Asm и DIRECT_ HiHook хуки.
3. нельзя пользоваться методами VarInit и VarFind в несколько потоков.

Цитата
В дополнение к предыдущему посту. Бара, ты используешь VirtualProtect для того, чтобы дать странице памяти права READ_WRITE_EXECUTE, но не возвращаешь оригинальные права обратно после применения патча.

Вообще-то обратно возвращаю.
Berserker
Понял, спасибо за разъяснения. Думаю, стоит это указать в первом посте или документации, что при необходимости поддержки многопоточности нужно использовать Asm и DIRECT_ HiHook-хуки.

Цитата
Вообще-то обратно возвращаю.

Отлично, значит переведя свои WriteCodeAt на Write я получу свои патчи в журналах патчера, не потеряв функциональности хуков Эры. С SetUnhandledExceptionFilter уже разобрался (теперь работает и твой, и мой обработчик). Скорее всего, бинарные патчи тоже пропущу через патчер.

А как насчёт map-файла? Можешь скомпилировать библиотеку с генерацией оного?
baratorch
Цитата
А как насчёт map-файла? Можешь скомпилировать библиотеку с генерацией оного?

map сделаю к следующей версии патчера, которая выйдет на днях.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.