Wake of Gods Forum | Форум Во Имя Богов
patcher_x86.dll - Printable Version

+- Wake of Gods Forum | Форум Во Имя Богов (http://wforum.heroes35.net)
+-- Forum: Герои Меча и Магии 3.5 WoG/ERA (/forumdisplay.php?fid=99)
+--- Forum: Моды (/forumdisplay.php?fid=104)
+--- Thread: patcher_x86.dll (/showthread.php?tid=3648)


patcher_x86.dll - baratorch - 11.02.2012 13:20

Дабы облегчить жизнь себе и потенциально другим мододелам,
а также в целях повышения совместимости независимых разработок
состряпал полезную штуку
перевел на нее ХД 3
и переводится на нее ХотA

готова версия 2.8:
библиотека + SDK + исходники


изменения:
Code:
Изменения нарушающие обратную совместимость: [!]
Исправления багов: [-]
Расширение функциональности: [+]
Несущественные изменения/комментарии: [*]

2.8

[-] теперь EXTENDED_ и SAFE_ HiHook хуки могут быть установлены на рекурсивно выполняемый код.



2.7

[+] patcher_x86 теперь полностью совместим с DEP (Предотвращением выполнения данных)



2.6

[+] Добавлен метод PatcherInstance::BlockAllExceptVA(_ptr_* va_args)
    блокирует для данного  PatcherInstance установку патчей для всех адресов кроме указанных.
      Добавлен метод Patcher::PreCreateInstance(char* name)
    нужен для применения методов PatcherInstance::BlockAt и PatcherInstance::BlockAllExceptVA
     до того как PatcherInstance будет полноценно создан с помощью Patcher::CreateInstance
[-] Исправлена возможность краша на старте из-за DEP.



2.5

[+] Функциии PatcherInstance::CreateCodePatch и PatcherInstance::WriteCodePatch
    теперь умеют корректно воспринимать записанные прямо опкоды с относительной адресацией.
    Например 0x639C40: call 0x447799 раньше можно было записать только в виде
    WriteCodePatch(0x639C40, "c%", 0x447799); или
    WriteCodePatch(0x639C40, "E8 ~d", 0x447799);
    (+ еще можно: WriteDataPatch(0x639C40, "E8  54 DB E0 FF");  чего, в принципе, было достаточно)
    Теперь можно и прямо WriteCodePatch(0x639C40, "E8  54 DB E0 FF") ;  (т.е. теперь можно просто скопировать патч с OllyDbg и не париться с преобразованием)
    


2.4

[-] баг: установка LoHook на длинную последвательность NOP'ов приводила к ошибкам.



2.3

[+] добавлены функции
    Patcher::VarInit
    Patcher::VarFind
    и тип Variable для работы с общими "переменными" для всех текущих клиентов patcher_x86.dll
[+] функция SaveDump сохраняет в файл после списка всех установленных патчей список всех "переменных" с их текущими значениями.


2.2

[-] LoHook не сохранял регистр флагов


2.1

[-] чтение/изменение esp регистра в LoHook хуке работало неверно

[+] добавлены функции
    PatcherInstance::WriteHexHook
    PatcherInstance::CreateHexHook
    
    эти функции позволяют установить самый примитивный хук
    а именно LoHook без высокоуровневой оболочки-функции.
    тело хука пишется прямо в вызове WriteHexHook или CreateHexHook
    таким же образом как пишется патч с помощью WriteCodePatch или CreateCodePatch

[+] В любом LoHook хуке теперь доступен для чтения/изменения регистр флагов процессора (расширена структура HookContext)

[+] LoHook теперь хранит экземляр HookContext в стеке, а не по фиксированному адресу, что позволяет корректно рекурсивно выполнять LoHook хук.

[+] теперь внутри функции сработавшей по LoHook хуку можно использовать операции с помещением данных в стэк контекста хука, аналогичные команде процессора PUSH.
    размер памяти, доступной для такого помещения в стэк контекста ограничен 128 байтами для хука, созданного с помощью методов PatcherInstance::WriteLoHook и PatcherInstance::CreateLoHook, и произволен для хука, установленного с помощью новых методов
    PatcherInstance::WriteLoHookEx
    PatcherInstance::CreateLoHookEx.

[+] добавлена функция:
    PatcherInstance::BlockAt
    которая запрещает данному экземпляру PatcherInstance устанавливать по заданному адресу патчи/хуки

[+] добавлены функции
    HiHook::SetUserData
    HiHook::GetUserData

[+] расширен функционал PatcherInstance::WriteDataPatch и PatcherInstance::CreateDataPatch
    - добавлен формат символ %s (string) - копирует си-строку из аргумента


1.7

[+] для HiHook добавлен новый subtype: SAFE_ = 2
    - то же самое что и EXTENDED_, однако перед вызовом GetDefaultFunc() восстанавливаются (только на время вызова)
    значения регистров процессора EAX, ECX (если не FASTCALL_ и не THISCALL_),
    EDX (если не FASTCALL_), EBX, ESI, EDI, бывшие на момент вызова замещенной функции

1.6

[+] Добавлены функции:

    PatcherInstance::Write
    PatcherInstance::CreatePatch

    (см. SDK)

1.5

[-] критический баг при попытке поставить CALL_ HiHook на неверный опкод при выключенном логировании


1.4

[-] баг из-за которого невозможно было использовать FUNCPTR_ EXTENDED_ HiHook


1.3

[-] Исправлены ошибки:
    - отмена хуков во время выполнения их хук-функций приводила к непредвиденным результатам
    - неверно работал формат-символ %a в функции Patcher::WriteComplexData

[+] Функция Patcher::CreateInstance теперь, принимая в качестве аргумента нулевую и пустую строки,
    создает PatcherInstance с именем модуля вызвавшего функцию.

[+] Функции Patcher::GetInstance можно передавать в качестве аргумента не только
    уникальое имя PatcherInstance, но так же имя модуля, создавшего PatcherInstance.
    Однако, имейте ввиду что в одном модуле можно создать несколько экземпляров PatcherInstance,
    в этом случае Patcher::GetInstance с именем модуля в качестве аргумента будет
    возвращать первый созданный PatcherInstance в этом модуле.

[!] Теперь невозможно создавать патчи посредством экземпляра PatcherInstance созданным не в вашем модуле.
    Возможность отменять и применять эти патчи сохранена.

[!] Функция Patch::Apply() теперь возвращает индекс (c 0) патча/хука в последовательности примененных патчей по данному адресу
    в случае успеха, -2 если патч/хук уже применен и -1 в случае неуспеха из-за неведомой хрени.

[!] Функция Patcher::DestroyAllAt заменена на Patcher::UndoAllAt

[*] Функции
    PatcherInstance::WriteComplexPatch
    PatcherInstance::CreateComplexPatch
    переименованы в
    PatcherInstance::WriteCodePatch
    PatcherInstance::CreateCodePatch
    для лучшего отражения сути их работы

[!] Теперь все патчи разделены на 2 типа: DATA_ и CODE_
    DATA_ патчи устанавливаются посредством простого копирования памяти
    CODE_ - посредством MemCopyCode
    произвольно выбрать тип патча нельзя, он устанавливается автоматически.
    DATA_ : BytePatch, WordPatch, DwordPatch, HexPatch, FUNCPTR_ HiHook, DataPatch
    CODE_:  JmpPatch, CodePatch (бывший ComplexPatch), LoHook, CALL_ и SPLICE_ HiHook
    ОЧЕНЬ ВАЖНО понимать разницу!

[+] Добавлены/реализованы функции:

    Patch::ApplyInsert

    PatcherInstance::WriteDataPatch
    PatcherInstance::CreateDataPatch
      PatcherInstance::GetLastPatchAt,
    PatcherInstance::UndoAllAt,
    PatcherInstance::GetFirstPatchAt.

    Patcher::GetFirstPatchAt,
    Patcher::MemCopyCodeEx,


[!] Теперь невозможно уничтожить неотмененный Хук (т.е. чтобы уничтожить, нужно сначала отменить).
[!] Возможность корректно уничтожить хук во время выполнения хук-функции ликвидированна
    (из-за бага ее и так не было, но теперь ее нет официально)

[*] Теперь Хук любого типа можно безопасно отменить в любой момент во время выполнения
    его хук-функции (соответственно исключая т.н. FIXED хуки, которые вообще нельзя отменить).
    Хук отменяется сразу (т.н. понятие отложенной отмены теперь неактуально).

[+] Функция Patcher::MemCopyCode теперь помимо CALL (E8) и JMP (E9) опкодов корректно копирует
    опкоды 0F 80 - 0F 8F (длинные условные прыжки);
[!] теперь в функции Patcher::MemCopyCode переходы E8, E9, 0F 80 - 0F 8F трансформируются
    только если направляют за границы копируемого блока.

[!] Функция Patcher::MemCopyCode теперь копирует всегда целое количество опкодов.
    (т.е. размер скопированного блока кода теперь >= size (был строго = size, т.е последний опкод мог быть обрезан).

[*] Функция Patcher::MemCopyCodeEx отличается от Patcher::MemCopyCode тем что если
    в копируемом блоке короткие прыжки EB, 70 - 7F направляют за границы копируемого блока,
    то они замняются на соответсвующие длинные прыжки (E9, 0F 80 - 0F 8F)

[*] Таким образом, теперь появилась возможность устанавливать SPLICE_ хуки и низкоуровневые хуки на код,
    содержащий любые корткие и длинные относительные переходы.
    А еще очевидна возможность скопировать целую функцию или ее часть от начала
    и вызывать копию как оригинал.

[+] Низкоуровневый хук теперь не сбивает содержимое регистров флагов, соответственно его
    теперь можно ставить на любой код в функции.

[!] Jmp патч, созданный посредством PatcherInstance::CreateJmpPatch и PatcherInstance::WriteJmp
    теперь закрывает целое количество опкодов, т.е. размер патча теперь >= 5 (был строго = 5),
    разница заполнятеся NOP'ами.

[+][!] Расширена функциональность Patcher::WriteComplexData, PatcherInstance::WriteCodePatch и PatcherInstance::CreateCodePatch
    - добавлена возможность ставить метки (до 10) и осуществлять переход к ним
      с помощью всех длинных и коротких относительных переходов (E8, E9, 0F 80 - 0F 8F, EB, 70 - 7F)
      (формат символы #0: , #1: , ... , #9: и #0 , #1 , ... , #9)
    - добавлен формат символ ~b - берет из аргументов абсолютный адрес и
      пишет относительное смещение до него размером в 1 байт для опкодов EB, 70 - 7F
    - добавлен формат символ ~d - берет из аргументов абсолютный адрес и
      пишет относительное смещение до него размером в 4 байта для опкодов E8, E9, 0F 80 - 0F 8F
    - добавлен формат символ %o (offset) - помещает по адресу из аргумента смещение позиции в Complex коде,
      относительно начала Complex кода.
    - добавлен формат символ %n (nop) - пишет nop опкоды, количеством указанным в аргументе
    - формат символ %m теперь копирует посредством MemCopyCodeEx а не MemCopyCode,
      то есть размер скопированного кода может оказаться больше копируемого
      из-за трансформации коротких прыжков в длинные.
    - формат символы %a и %h будут оставлены для обратной совместимости, но упоминания о них будут убраны из SDK;
      вместо этих символов рекомендуется использовать %o и %%

Code:
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ОПИСАНИЕ.
//
// ! библиотека предоставляет:
//        - удобные унифицированные централизованные
//          инструменты для установки патчей и хуков
//          в код целевой программы.
//        - дополнительные инструменты: дизассемблер длин опкодов и функцию
//          копирующую код с корректным переносом опкодов jmp и call c
//          относительной адресацией
// ! библиотека позволяет
//        - устанавливать как простые так и сложные патчи.
//          с методами по установке сложных патчей почти так же удобно работать
//          как с ассемблером (пока не хватает только меток и прыжкам к меткам)
//        - устанавливать высокоуровневые хуки, замещая оригинальные функции в
//          в целевом коде на свои, не заботясь о регистрах процессора,
//          стеке, и возврате в оригинальный код.
//        - устанавливать высокоуровневые хуки один на другой
//          не исключая а дополняя при этом функциональность хуков
//          установленных раньше последнего, тем самым реализуется идеология сабклассинга
//        - устанавливать низкоуровневые хуки с высокоуровневым доступом к
//          регистрам процессора, затертому коду и адресу возврата в код
//        - отменять любой патч и хук, установленный с помощью этой библиотеки.
//        - узнать задействован ли определенный мод, использующий библиотеку
//        - узнать какой мод (использующий библиотеку) установил определенный патч/хук
//        - получить полный доступ ко всем патчам/хукам, установленным из других модов
//          с помощью этой библиотеки
//        - легко и быстро обнаружить конфликтующие патчи из разных модов
//          (использующих эту библиотеку) 1) отмечаяв логе такие конфликты как:
//                                - устанавливаются патчи/хуки разного размера на один адрес
//                                - устанавливаются патчи/хуки перекрывающие один другого со смещением
//                                - устанавливаются патчи поверх хуков и наоборот
//          а так же 2) давая возможность посмотреть дамп (общий листинг) всех патчей
//          и хуков установленных с помощью этой библиотеки в конкретный момент времени и лог использования библиотеки.
////////////////////////////////////////////////////////////////////////////////////////////////////////////



RE: patcher_x86.dll - Berserker - 11.02.2012 15:48

Хорошая работа, будет интересно глянуть. Я, кстати, для дизассемблирования взял готовую компактную (600 байт) Hacker Disassembler Engine (hde32), правда использовал только для определения минимального безопасного размера перехвата (5 байт и более).


RE: patcher_x86.dll - kostya_76 - 11.02.2012 22:59

baratorch' Wrote:Сейчас идет тестирование и готов SDK для Cи

А эта штука будет в билдере компилиться?

А то SDK Berserker'а для GNU C++ билдер не понимает, а как это исправить - придумать не получается.


RE: patcher_x86.dll - baratorch - 13.02.2012 18:42

должна компилиться везде )

SDK для C++ представляет 1 хидер-файл, в котором объявлены нужные структуры и интерфейсы, сама библиотека экспортирует всего 1 функцию

итак версия 0.1
http://sites.google.com/site/heroes3hd/files/patcher_x86%200.1.7z?attredirects=0&d=1

в архиве:
длл : patcher_x86.dll
и SDK С++:
patcher_x86.h - хидер совсем без комментариев (выглядит не таким страшным и большим как комментированный)
patcher_x86_commented.h - тот же хидер с ооччень подробными комментариями


RE: patcher_x86.dll - Berserker - 13.02.2012 20:09

Тогда уж либо хэдер, либо заголовочный файл )


RE: patcher_x86.dll - baratorch - 13.02.2012 21:36

ну простите )


RE: patcher_x86.dll - kostya_76 - 13.02.2012 21:42

(13.02.2012 18:42)baratorch Wrote:  patcher_x86_commented.h - тот же хидер с ооччень подробными комментариями

   Не хватает примера создания хука. Ab


RE: patcher_x86.dll - Berserker - 13.02.2012 22:04

Кстати, из-за ООП и fastcall вызовов переходник SDK на Object Pascal или C вряд ли возможен.


RE: patcher_x86.dll - baratorch - 13.02.2012 22:56

ну fastcall я запросто переделаю в stdcall
а для си, если заморочиться, можно написать SDK
   объявив таблицу виртуальных методов в структурах
   и описать функции через эту таблицу

Quote:Не хватает примера создания хука.

Я ж написал в 1 посте, примеры - будут. позже


RE: patcher_x86.dll - Berserker - 13.02.2012 23:25

Кстати, а почему бы автора патча не определять автоматически по адресу возврата? VirtualQuery на адрес, получить базовый адрес, GetModuleFileName на него?


RE: patcher_x86.dll - baratorch - 15.02.2012 13:53

хорошая идея, воспользуюсь ей позже

версия 0.2
http://sites.google.com/site/heroes3hd/files/patcher_x86%200.2.7z?attredirects=0&d=1

переделал функцию выполняющуюся при срабатывании LoHook хука из __fastcall в __stdcall
скорректировал SDK для C++
написал SDK для С и для Delphi, но комментарии пока есть только для С++

коментарии к C и Делфи а так же примеры для C++, C, Делфи будут позже


RE: patcher_x86.dll - baratorch - 17.02.2012 02:15

итак версия 1.0:
http://sites.google.com/site/heroes3hd/files/patcher_x86%201.0.7z?attredirects=0&d=1

скорректировал и закрепил функционал для версии 1.0
исправил баги
добавил логирование и функцию сохранения лога в файл

написал коментированный SDK и коментированный пример для делфи
написал коментированный SDK и коментированный пример для С++
для извращенцев SDK для С (коментарии и пример смотреть в SDK C++)
примеры - плагины для эры/ХД (в ХД кидать в папку _HD3_Data\Common)
примеры - протестированы, компилируются, работают

в комплекте так же идет _HD3_.dll версии 3.0 бета 7 (та же бета 6, но переведенная на patcher_x86.dll)
сочетание клавиш [SHIFT]+[CTRL]+[ALT]+[DEL] в ХД вызывает стоп-ошибку, при этом кроме
крашлога в папку с игрой пишутся дамп и лог патчера:
patcher_x86 dump.txt и patcher_x86 log.txt
надеюсь все это будет интересно и полезно.

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


RE: patcher_x86.dll - baratorch - 20.02.2012 08:49

готова версия 1.1:
http://sites.google.com/site/heroes3hd/files/patcher_x86%201.1.7z?attredirects=0&d=1
[*] сильно смягчены правила установки патчей, теперь все конфликтные патчи применяются, WARNING сообщение о конфликте пишется в лог.
[*] логирование по умолчанию теперь отключено, чтобы включить его, необходимо в той же папке создать файл patcher_x86.ini c единственной записью: Logging On = 1 (Logging On = 0 - отключает логирование)
[*] исправлены ошибки в библиотеке и файлах SDK
[*] скорректированы описания-коментарии в SDK
[*] изменена форма записей в дампе и в логе


RE: patcher_x86.dll - baratorch - 28.02.2012 09:38

готова версия 1.2:
http://sites.google.com/site/heroes3hd/files/patcher_x86%201.2.7z?attredirects=0&d=1
исправил ошибки
убрал _HD3_.dll из сборки, т.к. выпускаю HD 3.0 beta 8 (на базе patcher_x86 1.2)


RE: patcher_x86.dll - Sav - 18.03.2012 19:04

Похоже, высокоуровневый хук CALL_, EXTENDED_, FASTCALL_ для функции с одним аргументом работает неправильно. При каждом вызове он уменьшает ESP на 4. Если заменить на THISCALL_, то всё нормально.


RE: patcher_x86.dll - Sav - 24.03.2012 23:36

SPLICE_ хук очень плохо себя ведёт, когда в затираемом им коде содержится локальный переход. Вызов оригинальной функции в этом случае невозможен.


RE: patcher_x86.dll - gamecreator - 25.03.2012 01:37

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


RE: patcher_x86.dll - Sav - 25.03.2012 09:10

Поставь splice хук и вызови в нём оригинальную функцию до собственного кода.


RE: patcher_x86.dll - baratorch - 26.03.2012 09:14

(18.03.2012 19:04)Sav Wrote:  Похоже, высокоуровневый хук CALL_, EXTENDED_, FASTCALL_ для функции с одним аргументом работает неправильно. При каждом вызове он уменьшает ESP на 4. Если заменить на THISCALL_, то всё нормально.
да, так и есть. Для меня это - само собой, поэтому даже не подумал обратить на это внимание в комментариях. Хотя
Code:
если   int __fastcall orig(int a1, int a2, ?)    то    int __stdcall new_func(HiHook* h, int a1, int a2, ?)
из комментов мною читается однозначно: fastcall c 1 аргументом не поддерживается в EXTENDED_ FASTCALL_.
Действительно, этот недостаток можно обойти используя EXTENDED_ THISTCALL_
Ну а для читабельности и логичности можно использовать пару констант типа:
FASTCALL_1_ = THISCALL_ и FASTCAL_2_ = FASTCALL_
Просто мне не представляется возможным, чтобы хук сам мог определять сколько аргументов передается замещаемой функции, поэтому EXTENDED_ FASTCALL_ всегда пушит и ECX и EDX в стек.

Quote:SPLICE_ хук очень плохо себя ведёт, когда в затираемом им коде содержится локальный переход. Вызов оригинальной функции в этом случае невозможен.
Дело в функциональности MemCopyCode, сейчас она копирует корректно только CALL и далекий JMP опкоды. После твоего поста добавлю поддержку далеких JE, JNE, JL, JG, JLE, JGE, JA, JB, JBE, JNA, ....
Впринципе короткие прыжки можно в замещаемом коде при копировании заменять на длинные, однако тогда скопироавнный код будет длиннее оригинального, что еще добавит работы...

Короче, сделаю в следующем релизе.


RE: patcher_x86.dll - baratorch - 04.04.2012 14:17

1.3

http://sites.google.com/site/heroes3hd/files/patcher_x86%201.3.7z?attredirects=0&d=1

старался как можно сильнее сохранить обратную совместимость. но полностью не получилось.
ХД 3 написанный с patcher_x86 SDK 1.2, например прекрасно пошел на новой 1.3
(а вот последнюю редакцию ХоТы придется подправить)

чейнджлог:
Code:
Изменения нарушающие обратную совместимость: [!]
Исправления багов: [-]
Расширение функциональности: [+]
Несущественные изменения/комментарии: [*]

1.3


[-] Исправлены ошибки:
    - отмена хуков во время выполнения их хук-функций приводила к непредвиденным результатам
    - неверно работал формат-символ %a в функции Patcher::WriteComplexData

[+] Функция Patcher::CreateInstance теперь, принимая в качестве аргумента нулевую и пустую строки,
    создает PatcherInstance с именем модуля вызвавшего функцию.

[+] Функции Patcher::GetInstance можно передавать в качестве аргумента не только
    уникальое имя PatcherInstance, но так же имя модуля, создавшего PatcherInstance.
    Однако, имейте ввиду что в одном модуле можно создать несколько экземпляров PatcherInstance,
    в этом случае Patcher::GetInstance с именем модуля в качестве аргумента будет
    возвращать первый созданный PatcherInstance в этом модуле.

[!] Теперь невозможно создавать патчи посредством экземпляра PatcherInstance созданным не в вашем модуле.
    Возможность отменять и применять эти патчи сохранена.

[!] Функция Patch::Apply() теперь возвращает индекс (c 0) патча/хука в последовательности примененных патчей по данному адресу
    в случае успеха, -2 если патч/хук уже применен и -1 в случае неуспеха из-за неведомой хрени.

[!] Функция Patcher::DestroyAllAt заменена на Patcher::UndoAllAt

[*] Функции
    PatcherInstance::WriteComplexPatch
    PatcherInstance::CreateComplexPatch
    переименованы в
    PatcherInstance::WriteCodePatch
    PatcherInstance::CreateCodePatch
    для лучшего отражения сути их работы

[!] Теперь все патчи разделены на 2 типа: DATA_ и CODE_
    DATA_ патчи устанавливаются посредством простого копирования памяти
    CODE_ - посредством MemCopyCode
    произвольно выбрать тип патча нельзя, он устанавливается автоматически.
    DATA_ : BytePatch, WordPatch, DwordPatch, HexPatch, FUNCPTR_ HiHook, DataPatch
    CODE_:  JmpPatch, CodePatch (бывший ComplexPatch), LoHook, CALL_ и SPLICE_ HiHook
    ОЧЕНЬ ВАЖНО понимать разницу!

[+] Добавлены/реализованы функции:

    Patch::ApplyInsert

    PatcherInstance::WriteDataPatch
    PatcherInstance::CreateDataPatch
      PatcherInstance::GetLastPatchAt,
    PatcherInstance::UndoAllAt,
    PatcherInstance::GetFirstPatchAt.

    Patcher::GetFirstPatchAt,
    Patcher::MemCopyCodeEx,


[!] Теперь невозможно уничтожить неотмененный Хук (т.е. чтобы уничтожить, нужно сначала отменить).
[!] Возможность корректно уничтожить хук во время выполнения хук-функции ликвидированна
    (из-за бага ее и так не было, но теперь ее нет официально)

[*] Теперь Хук любого типа можно безопасно отменить в любой момент во время выполнения
    его хук-функции (соответственно исключая т.н. FIXED хуки, которые вообще нельзя отменить).
    Хук отменяется сразу (т.н. понятие отложенной отмены теперь неактуально).

[+] Функция Patcher::MemCopyCode теперь помимо CALL (E8) и JMP (E9) опкодов корректно копирует
    опкоды 0F 80 - 0F 8F (длинные условные прыжки);
[!] теперь в функции Patcher::MemCopyCode переходы E8, E9, 0F 80 - 0F 8F трансформируются
    только если направляют за границы копируемого блока.

[!] Функция Patcher::MemCopyCode теперь копирует всегда целое количество опкодов.
    (т.е. размер скопированного блока кода теперь >= size (был строго = size, т.е последний опкод мог быть обрезан).

[*] Функция Patcher::MemCopyCodeEx отличается от Patcher::MemCopyCode тем что если
    в копируемом блоке короткие прыжки EB, 70 - 7F направляют за границы копируемого блока,
    то они замняются на соответсвующие длинные прыжки (E9, 0F 80 - 0F 8F)

[*] Таким образом, теперь появилась возможность устанавливать SPLICE_ хуки и низкоуровневые хуки на код,
    содержащий любые корткие и длинные относительные переходы.
    А еще очевидна возможность скопировать целую функцию или ее часть от начала
    и вызывать копию как оригинал.

[+] Низкоуровневый хук теперь не сбивает содержимое регистров флагов, соответственно его
    теперь можно ставить на любой код в функции.

[!] Jmp патч, созданный посредством PatcherInstance::CreateJmpPatch и PatcherInstance::WriteJmp
    теперь закрывает целое количество опкодов, т.е. размер патча теперь >= 5 (был строго = 5),
    разница заполнятеся NOP'ами.

[+][!] Расширена функциональность Patcher::WriteComplexData, PatcherInstance::WriteCodePatch и PatcherInstance::CreateCodePatch
    - добавлена возможность ставить метки (до 10) и осуществлять переход к ним
      с помощью всех длинных и коротких относительных переходов (E8, E9, 0F 80 - 0F 8F, EB, 70 - 7F)
      (формат символы #0: , #1: , ... , #9: и #0 , #1 , ... , #9)
    - добавлен формат символ ~b - берет из аргументов абсолютный адрес и
      пишет относительное смещение до него размером в 1 байт для опкодов EB, 70 - 7F
    - добавлен формат символ ~d - берет из аргументов абсолютный адрес и
      пишет относительное смещение до него размером в 4 байта для опкодов E8, E9, 0F 80 - 0F 8F
    - добавлен формат символ %o (offset) - помещает по адресу из аргумента смещение позиции в Complex коде,
      относительно начала Complex кода.
    - добавлен формат символ %n (nop) - пишет nop опкоды, количеством указанным в аргументе
    - формат символ %m теперь копирует посредством MemCopyCodeEx а не MemCopyCode,
      то есть размер скопированного кода может оказаться больше копируемого
      из-за трансформации коротких прыжков в длинные.
    - формат символы %a и %h будут оставлены для обратной совместимости, но упоминания о них будут убраны из SDK;
      вместо этих символов рекомендуется использовать %o и %%



RE: patcher_x86.dll - Berserker - 04.04.2012 19:33

baratorch, круто, но, кажись, журнал изменений продублирован.


RE: patcher_x86.dll - baratorch - 25.06.2012 08:23

готова версия 1.4:
http://sites.google.com/site/heroes3hd/files/Patcher%20x86%201.4.zip?attredirects=0&d=1

изменения:
Code:
1.4
[-] баг из-за которого невозможно было использовать FUNCPTR_ EXTENDED_ HiHook



RE: patcher_x86.dll - baratorch - 27.07.2012 09:43

готова версия 1.6:
http://sites.google.com/site/heroes3hd/files/Patcher%20x86%201.6.zip?attredirects=0&d=1

изменения:
Code:
1.6

[+] Добавлены функции:
    PatcherInstance::Write
    PatcherInstance::CreatePatch
    (см. SDK)

1.5

[-] критический баг при попытке поставить CALL_ HiHook на неверный опкод при выключенном логировании

так же в архиве плагин для эры (wog_patcherizer.era), который засталяет Эру делать все инициализационные патчи ВоГ через patcher_x86 (версии не ниже 1.6)

***

надо бы еще сделать чтобы эра делала все патчи через patcher_x86.
будет очень зашибись..
в логе patcher_x86 будут сразу видны все потенциальные конфликты


RE: patcher_x86.dll - Berserker - 27.07.2012 14:04

Quote:надо бы еще сделать чтобы эра делала все патчи через patcher_x86.
   будет очень зашибись..
   в логе patcher_x86 будут сразу видны все потенциальные конфликты
Хм, глянул, появились переходники на Паскаль. В таком случае посмотрю, что можно сделать к следующей версии. Имеет ли смысл использовать один объект заплатки на все изменения? Или на каждый чих свой объект?


RE: patcher_x86.dll - baratorch - 27.07.2012 17:30

не очень понял вопрос, но попытаюсь ответить.
установка патча с помощью patcher_x86 в виде непрерывной последовательности байт - это всегда создание нового отдельного объекта TPatch

ХД+ХотА ставят более 3000 патчей/хуков
ХД+ВоГ - более 2200
ХД - более 1200
никаких промедлений при запуске не заметно, тыщи патчей ставятся 'мгновенно'.
ну, подумаешь, сколько-то там сотен килобайт памяти расходуется на это дело
зато удобств куча.

как мне видится использование patcher_x86 в эре:
патчи из самой эры (если таковые имеются) устанавливаются через TPatcherInstance c именем "ERA"
а скажем каждая bin заплатка через TPatcherInstance c именем "ERA:<имя_заплатки.bin>"

и, bers, обрати внимание на Хуки в patcher_x86. посмотри SamplePlugin в SDK Delphi. Хоть пример и не охватывает весь функционал patcher_x86, но некоторое представление о хуках дает .
Было бы вообще супер, если бы ты полностью перешел на них, отказавшись от своей реализации в Эре.

ну реально удобно видеть где кто какие патчи поставил,.. где какой патч пересек другой..


RE: patcher_x86.dll - Berserker - 27.07.2012 19:18

Да, я понял. Попробую.


RE: patcher_x86.dll - baratorch - 24.09.2012 17:36

готова версия 1.7:
https://sites.google.com/site/heroes3hd/files/Patcher%20x86%201.7.zip?attredirects=0&d=1

Code:
1.7

[+] для HiHook добавлен новый subtype: SAFE_ = 2
    - то же самое что и EXTENDED_, однако перед вызовом GetDefaultFunc() восстанавливаются (только на время вызова)
    значения регистров процессора EAX, ECX (если не FASTCALL_ и не THISCALL_),
    EDX (если не FASTCALL_), EBX, ESI, EDI, бывшие на момент вызова замещенной функции

SAFE_ subtype создан прежде всего для установки хуков поверх воговских, т.к. воговские хуки функций Moral часто не ограничиваются аргументами, но используют значения из регистров процессора в контексте вызова в геройском коде.

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


RE: patcher_x86.dll - baratorch - 21.11.2012 06:37

Исходники patcher_x86 (MS VC++ 2008):
http://sites.google.com/site/heroes3hd/files/patcher_x86%201.7%20src.7z?attredirects=0&d=1

Berserker Wrote:Наконец, если там вдруг ошибка или ошибка в SDK, или нужно глянуть реализацию, потому что мой код не работает, исходников нет.
Примеры SDK для всех языков билдились и тестировались успешно.
В самом патчере 1.7 пока ни ХотА ни ХД не нарвались на баг.

Berserker Wrote:Помимо прочего я не помню (поправьте если не прав), чтобы клиенты патчера могли делать то же самое: получить список всех заплаток, например (а ради чего ещё остальным такая централизация?). Наконец, если там вдруг ошибка или ошибка в SDK, или нужно глянуть реализацию, потому что мой код не работает, исходников нет.
Список всех заплаток клиент получить не может. Было бы не плохо если б было можно. Еще лучше была бы функция: получить патчи в указанном диапазоне адресов.

Что сейчас может клиент. Может отменить/применить все патчи, может отменить применить все патчи указанного PatcherInstance (т.е. мода или части мода). Можно получить все патчи по конкретному адресу.
А еще клиент может сохранить в файл дамп - карту всех наложенных патчей в данный момент времени; и лог, показывающий очередность применения/отменения патчей с деталями и потенциальные конфликты. Разработчик смотрит дамп и лог и, руководствуясь увиденным, делает изменения в коде мода.
Вот ради возможности посмотреть такие дамп и лог (которые кроме всего прочего показывают какой мод (PatcherInstance) установил определнный патч/хук) и возможности включать отключать патчи установленные из других модов и нужна централизация.

Все патчи хранятся в упорядоченном двоичном дереве, каждый лист которого - адрес с динамическим массивом всех наложенных по этому адресу патчей. Правда в каждом PatcherInstance хранится еще и связанный список всех его патчей.
Возможно это всё лишние нагромождения, и можно б было обойтись динамическим массивом вместо дерева, но дерево сделано для скорости доступа к патчу по конкретному адресу (хотя я, крайне редко использую эту возможность).

(20.11.2012 06:56)GrayFace Wrote:  
(15.11.2012 03:03)baratorch Wrote:  Было бы здорово, если б 3.59 тоже перешел на патчер, ибо никакие узкоспециализированные API и скриптовые языки не смогут решать такие задачи, какие может патчер
Я сейчас использую удобную систему хуков на Луа. По сути, любые задачи с помощью моего модуля хуков/структур/работы с памятью можно решать. Одно исключение - когда нужна скорость, тогда придется грузить dll или байт-код писать строкой. Да, еще ограничение - код должен исполняться в основном потоке. Патчер я глянул только чуть-чуть и, как я понял, есть сходства с моими хуками в высокоуровневости.
Главная фича хуков патчера - это то, что разные моды независимо друг от друга могут хучить одну и туже функцию или ее конкретный вызов сколько угодно раз, не перекрывая а дополняя её код при желании.
Ну и описанные выше возможности по управлению чужими патчами/хуками и просмотр дампа и лога - очень важная и полезная вещь.

вот пример дампа:
Quote:instances(7): 'HD', 'HotA', 'spellbook.patch', 'HD.Af', 'HD.TE', 'HD.IG', 'HD.Pack.SuperRus',
addresses count: 3460
    patches count: 3637


00401448: count = 1 [Patch , size: 04, owner: HD]
0040144F: count = 1 [Patch , size: 04, owner: HD]
00401510: count = 2 [HiHook, size: 05, owner: HD] [HiHook, size: 05, owner: HD.Af]
00402480: count = 1 [Patch , size: 04, owner: HotA]
00402E70: count = 1 [HiHook, size: 05, owner: HD]
00402F00: count = 1 [Patch , size: 04, owner: HD]
00402F49: count = 1 [Patch , size: 04, owner: HD]
...

и лога:
Quote:log records count: 3769

Patch 00599B31/06 ->Apply (HD), z-order = 0
...
HiHook 00479853/05 ->Apply (HotA), z-order = 0
HiHook 0047999B/05 ->Apply (HotA), z-order = 0

WARNING! 00493E31: Applying Patch (size: 32, owner: HotA), which overwrites a part of already applied HiHook (size: 5, owner: HD) at 00493E4C! Older one becomes FIXED!

Patch 00493E31/32 ->Apply (HotA), z-order = 0
...



RE: patcher_x86.dll - Berserker - 21.11.2012 13:38

baratorch, спасибо за исходники и разъяснения. Мне осталось только разобраться в конфликтах виртуальной файловой системы Эры и Wine.

Quote:Главная фича хуков патчера - это то, что разные моды независимо друг от друга могут хучить одну и туже функцию или ее конкретный вызов сколько угодно раз
Подавляющее большинство моих перехватов находится в произвольных частях. Правку call/jmp смещений в затёртых командах я добавил, так что и пять заплаток в одно место в режиме "мост" сработают. Но в целом направление верное, будем туда и копать.


RE: patcher_x86.dll - Berserker - 01.01.2013 22:27

Патчер 1.7, SDK C++, patcher_x86.hpp:
225:2 [Warning] no newline at end of file


RE: patcher_x86.dll - gamecreator - 01.01.2013 23:00

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


RE: patcher_x86.dll - Berserker - 04.01.2013 21:51

Code:
SAFE_ - то же самое что и EXTENDED_, однако перед вызовом (на время вызова) GetDefaultFunc() восстанавливаются
    //                значения регистров процессора EAX, ECX (если не FASTCALL_ и не THISCALL_),
    //                EDX (если не FASTCALL_), EBX, ESI, EDI, бывшие на момент вызова замещенной функции
Мне в корне не ясен смысл SAFE/NOT SAFE. Неужели мост по умолчанию портит регистры? В Эре перед вызовом оригинальной функции контекст восстанавливается полностью, вплоть до флагов (POPAD, POPFD)..


RE: patcher_x86.dll - Sav - 04.01.2013 22:01

В HiHook'ах регистры сохраняются до перехвата и восстанавливаются после. А когда делаешь CALL от GetDefaultFunc никакие регистры не восстанавливаются: ведь вызываемая функция не должна работать ни с чем, кроме своих параметров. Однако, по-видимому, в Воге кое-где в вызываемой функции используются регистры, значение которых было присвоено в вызывающей функции и, не будучи параметром, волею случая при компиляции, не затёрто в вызываемой функции. Судя по описанию, SAFE_ отвечает за восстановление регистров именно перед вызовом функции по GetDefaultFunc.


RE: patcher_x86.dll - Berserker - 04.01.2013 22:43

Спасибо, что-то подобное и предполагал.


RE: patcher_x86.dll - Berserker - 05.01.2013 00:05

.hpp]ISO C++11 переработанный вариант заголовочного файла патчера
  • Код оформлен в основном по руководству гугла.
  • Модуль помещён в пространство имён x86patcher.
  • Поскольку патчер полностью пропитан 32-битовой арифметикой, ссылаться на типы Студии (__int8 и т.д) смысла нет. Поэтому использованы типы byte, dword, longbool, addr.
  • Табы заменены двумя пробелами.
  • Добавлены обобщённые функции ptr(адрес) и ptr_ofs(адрес, смещение). В качестве адреса первая принимает что угодно из 4-х байт (размер проверяется на этапе компиляции). Вторая принимает любые указатели и возвращает адрес, равный базе + смещение.
  • Макросы кроме CALL_ убраны. Константы оформлены в виде именованных перечислений.
  • EXTENDED стал kBridge, SAFE стал kSafeBridge. Антонимом к Direct ("прямой") является посреднический, а "расширенный" и "безопасный" вообще сбивают с толку.
  • Аргументы, в которых предполагалось использование констант, стали типизированными.
  • Символ указателя привязан к самим переменным. Позволяет избежать ошибок при парном объявлении и в целом распространённая практика. Вместо dword* x используется dword *x.
  • Ряд inline процедур стали обычными. Они объявлены в стиле CDECL с переменным количеством аргументов и используют прямую адресную арифметику для получения указателя на аргументы. Такая вещь определённо не встраивается толком.
  • NULL => nullptr
  • Исправлена весьма странная функция получения указателя самого патчера. Во-первых, функция была встроенной (inline), но содержала среднего размера тело. Во-вторых, любой вызов после первого должен был без особого смысла возвращать NULL. Теперь функция стала обычной. Если есть запомненный указатель на патчер, то он возвращается немедленно. Иначе происходит загрузка DLL и т.д. nullptr возвращается только при ошибке.
  • Код оформлен по границе в 100 символов.
  • char* => const char*
  • Ряд мелких, косметических и оформительных изменений.



RE: patcher_x86.dll - Berserker - 06.01.2013 03:10

ППЦ, чувствую себя магом.

Code:
template<typename T> struct to_dword
{
  typedef dword type;
};

template
<
  typename    RetType,
  kCallConv   call_conv,
  typename    Addr,
  typename... Args
>
inline RetType call(Addr addr, Args... args)
{
  static_assert
  (
    (call_conv == kStdcall)   ||
    (call_conv == kThiscall)  ||
    (call_conv == kFastcall)  ||
    (call_conv == kCdecl),
    "Unknown calling convention!"
  );

  switch(call_conv)
  {
    case kStdcall:
    {
      typedef RetType (__stdcall *func)(typename to_dword<Args>::type...);
      return ((func) addr)((dword) args...);
      break;
    }
    case kThiscall:
    {
      typedef RetType (__thiscall *func)(typename to_dword<Args>::type...);
      return ((func) addr)((dword) args...);
      break;
    }
    case kFastcall:
    {
      typedef RetType (__fastcall *func)(typename to_dword<Args>::type...);
      return ((func) addr)((dword) args...);
      break;
    }
    case kCdecl:
    {
      typedef RetType (__cdecl *func)(typename to_dword<Args>::type...);
      return ((func) addr)((dword) args...);
      break;
    }
    default:
    {
      return RetType();
    }
  }
}

Code:
patching::call<HMODULE, patching::kCdecl>(0x887668, L"Hello", L"World");

Code:
MOV [DWORD ESP+4],60103086
MOV [DWORD ESP],60103092
MOV EAX,887668
CALL NEAR EAX

Всё без макросов )))


RE: patcher_x86.dll - hippocamus - 09.01.2013 12:59

(05.01.2013 00:05)Berserker Wrote:  Антонимом к Direct ("прямой") является посреднический, а "расширенный" и "безопасный" вообще сбивают с толку.
Relative ("относительный") же!


RE: patcher_x86.dll - Berserker - 09.01.2013 18:11

В контексте функций-перехватчиков, перехват либо прямой, либо через посредника.


RE: patcher_x86.dll - Berserker - 28.06.2013 14:16

Меня в Делфийском SDK смущает строчка:
Code:
vtString: dword_args[i] := _dword_(PAnsiChar(AnsiString(VString^ + #0)));
Сложение строки с символом даёт временную строку в куче, которая уничтожается по завершению вычисления выражения. В итоге сохраняется указатель на опасный участок памяти.
Правильнее, наверное, будет оставить строку как есть.


RE: patcher_x86.dll - Berserker - 28.06.2013 15:21

Quote:// заранее объявлены переменные _P и _PI для использования во всех модулях пректа



RE: patcher_x86.dll - Berserker - 02.07.2013 19:50

Методы GetOriginalFunc, GetReturnAddress не потокобезопасны.
В Делфийском SDK не потокобезопасен Call (глобальный массив аргументов).


RE: patcher_x86.dll - baratorch - 16.08.2013 09:06

Bersy Wrote:.hpp]ISO C++11 переработанный вариант заголовочного файла патчера
ссылка, помоему, с самого начала мертвой была, ну или пару дней продержалась, так и не смог скачать посмотреть. Вот пишу об этом только сейчас.

насчет DIRECT_ и EXTENDED_
c DIRECT_ - все понятно.
а в EXTENDED_ - основной смысл не в том что мы функцию через мост вызываем, а в том что из тела функции можем получить аргументом указатель на экземпляр хука. В этом и расширенность хука.
А то что эта расширенность невозможна без моста, так это побочный эффект. Вот такая логика у меня была.
SAFE_ назван мной так для краткости, по моей логике должен называться SAFE_EXTENDED_ а еще правильнее WOG_SAFE_EXTENDED ))


Quote:ППЦ, чувствую себя магом.
...
Всё без макросов )))
к сожалению пишу в MS VS 2008, а в ней не поддерживается ISO C++ 11
в полной мере оценить красоту решения не могу.


Bersy Wrote:Меня в Делфийском SDK смущает строчка:
Code:
vtString: dword_args[i] := _dword_(PAnsiChar(AnsiString(VString^ + #0)));
Сложение строки с символом даёт временную строку в куче, которая уничтожается по завершению вычисления выражения. В итоге сохраняется указатель на опасный участок памяти.
Правильнее, наверное, будет оставить строку как есть.
Quote:В Делфийском SDK не потокобезопасен Call (глобальный массив аргументов).
Вы уж простите меня, но мой опыт работы с паскалем составлен из 3х этапов: 10класс школы, перевод исходников Хота на C++, написание SDK для патчера. Здесь лучше заняться исправлением улучшением и оптимизацией, опытному паскалисту/делфисту.

Bersy Wrote:Методы GetOriginalFunc, GetReturnAddress не потокобезопасны.
тогда получаются непотокобезопасны
методы Apply, ApplyInsert и Undo (у Patch/LoHook/HiHook)
т.к. может случится так что когда один поток будет стирать патч, другой будет ставить в это же место (а ставя он читает 'старый' код)
и вообще в это время может до этого места дойти выполнение модифицируемой программы.

Потокобезопасностью никогда не занимался.
Поможет ли создание одной единственной критической секции для всего патчера, и вход в нее когда пишем в код и в default_func???
если я правильно понял проблему....


RE: patcher_x86.dll - Sav - 22.08.2013 21:02

В LowHook`ах следует сделать pushf после сохранения всех регистров - тогда внутри хука можно будет свободно работать с c->esp. Сейчас же это невозможно, т. к. он меняется до хука и используется после.


RE: patcher_x86.dll - Berserker - 05.09.2013 21:11

Quote:Поможет ли создание одной единственной критической секции для всего патчера, и вход в нее когда пишем в код и в default_func???
   если я правильно понял проблему...
Думаю, не стоит. Пусть пользователи сами оборачивают.
Залил два своих файла:
http://wikisend.com/download/439718/Patcher.rar


RE: patcher_x86.dll - Berserker - 25.09.2013 19:27

Ужаснулся, глянув в код низкоуровневого перехватчика. Регистры не в стеке сохраняются и передаются, а в глобальной памяти. Это значит, что рекурсивный вызов перезатрёт их подчистую!




RE: patcher_x86.dll - Berserker - 26.09.2013 18:44

EXEC_DEFAULT = 1;
NO_EXEC_DEFAULT = 0;

Проверка в коде патчера на 1 не есть хорошо. Нужно сравнивать с нулём. Так работает тип bool: 0 = ложь, остальное - истина.


RE: patcher_x86.dll - Berserker - 10.10.2013 19:43

Ещё не хватает возможности в параметрах записи высокоуровневого перехватчика пользовательский указатель, который будет сохранён в структуре перехватчика и по которому можно было бы определить, с какой целью ставился перехват и кто его клиент. Что=то вроде HiHool->UserData.

Не хочется также изобретать велосипед по поводу генерации журнала о вылете. Может быть оформить эту часть хд в виде плагина?


RE: patcher_x86.dll - baratorch - 24.10.2013 21:32

Quote:Ужаснулся, глянув в код низкоуровневого перехватчика. Регистры не в стеке сохраняются и передаются, а в глобальной памяти. Это значит, что рекурсивный вызов перезатрёт их подчистую!

Code:
MOV [DWORD 4978BC8],EAX
   MOV [DWORD 4978BCC],ECX
   MOV [DWORD 4978BD0],EDX
   MOV [DWORD 4978BD4],EBX
   MOV [DWORD 4978BD8],ESP
   MOV [DWORD 4978BDC],EBP
   MOV [DWORD 4978BE0],ESI
   MOV [DWORD 4978BE4],EDI
   MOV EAX,74C6FC
   MOV [DWORD 4978BE8],EAX
   PUSH 4978BC8
я так понял речь о мосте LoHook хука.

во-первых, регистры сохраняются не в глобальной памяти а в памяти выделенной для конкретного объекта LoHook.
точно так же и память под мост выделена для каждого конкретного LoHook объекта и сам мост уникален для каждого экземпляра LoHook.
То есть получается что каждый мост каждого LoHook объекта работает со своей областью памяти для хранения значений регистров.

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

в-третьих, один патч (с собственным одним мостом и объектом HookContext) невозможно применить дважды ни к одному и тому же адресу ни к разным адресам. то есть рекурсивное выполнение кода моста невозможно.

На практике все прекрасно, что подтверждает правильность реализации.

т.е. HookContext передаваемый в высокоуровневую LoHook функцию на самом деле является частью LoHook, но доступен он только из высокоуровневой функции, ибо в другое время доступ к нему не имеет смысла.


Quote:Проверка в коде патчера на 1 не есть хорошо. Нужно сравнивать с нулём. Так работает тип bool: 0 = ложь, остальное - истина.
поправил в версии 2.1

Quote:Ещё не хватает возможности в параметрах записи высокоуровневого перехватчика пользовательский указатель, который будет сохранён в структуре перехватчика и по которому можно было бы определить, с какой целью ставился перехват и кто его клиент. Что=то вроде HiHool->UserData.
не совсем понял зачем это нужно. можно абстрактный пример?...
ни внутри хд, ни в нутри хоты, ни во взаимодействии до сих пор не понадобилось ничего подобного.


позже опубликую версию 2.1


RE: patcher_x86.dll - Berserker - 24.10.2013 22:38

Quote:во-вторых, я не понимаю о какой рекурсии может идти речь.
Процедура Х (...)
команда 1
низкоуровневый перехватчик №1
команда 2...

Низкоуровневый перехватчик №1(...)
вызвать процедуру Х(...); рекурсия !!!
другие команды...

Многие АПИ и часть алгоритмов требует рекурсии. Нельзя, чтобы перехватчик по одному адресу сохранял контекст в одной и той же области памяти. В такой перехватчик невозможно зайти дважды, не затерев предыдущий контекст. В Эре контекст сохраняется через PUSHAD и таких проблем нет.

Quote:не совсем понял зачем это нужно. можно абстрактный пример?...
Положим, есть скриптовый язык, для примера будет ЕРМ, но только для примера. Ему предоставляется АПИ для установки перехватчика SetHook(Addr, ERM_FUNC_ID).

Реально вызывается WriteHiHook на одну общую функцию: CallErmFunc. Но так как это именно функция без привязки к данным, она не может определить, какой ERM_FUNC_ID вызывать в свою очередь ей. Для этого в ЯП используют делегаты - связку: адрес объекта + адрес метода объекта.

Иначе говоря, одна функция обслуживает десятки перехватов. Чтобы определить, кому дальше передавать управление, ей нужно проанализировать какое-то поле данных, но ничего передать не получается. А с другой стороны, всегда передаётся-таки объект Hi/LoHook. Однако он бесполезен, в нём нельзя сохранить пользовательский указатель (h->UserData = ...).


RE: patcher_x86.dll - baratorch - 25.10.2013 08:11

Quote:Процедура Х (...)
команда 1
низкоуровневый перехватчик №1
команда 2...

Низкоуровневый перехватчик №1(...)
вызвать процедуру Х(...); рекурсия !!!
другие команды...

Многие АПИ и часть алгоритмов требует рекурсии. Нельзя, чтобы перехватчик по одному адресу сохранял контекст в одной и той же области памяти. В такой перехватчик невозможно зайти дважды, не затерев предыдущий контекст. В Эре контекст сохраняется через PUSHAD и таких проблем нет.
понял о какой рекурсии идет речь, но это изврат - вызывать из лоухука код, в который установлен этот лоухук.
хочу реального примера. Но посмотрю, конечно, что можно сделать.

В HiHook UserData добавлю

А так к версии 2 в патчере появились две вкусные вещи
WriteLoHookEx и WriteHexHook.


WriteLoHookEx работает так же как WriteLoHook, но позволяет полноценно работать со стеком контекста, т.е. возможно выполнение такой штуки:
Code:
int __stdcall LoHook_Func(LoHook* h, HookContext* c)
{
   ....
   c->Push(x)
   // или то же самое:
   с->esp -= 4;
   IntAt(c->esp) = x;
   ...
   return NO_EXEC_DEFAULT;
}
вот на такое эровский лоу-хук точно не способен.



а WriteHexHook устанавливает LoHook без использования высокоуровневой функции, тело хука пишется прямо в вызове WriteHexHook в машинных кодах, подобно тому как патч пишется с помощью WriteCodePatch
пример:
Code:
_PI->WriteHexHook(0x335577, NO_EXEC_DEFAULT, "FF 35 %d", &x); // push [&x]

Вот, кстати, с помощью WriteHexHook и можно самостоятельно написать свой мост к высокоуровневой функции любого содержания, в том числе и сохраняющего регистры в стеке.


RE: patcher_x86.dll - Berserker - 25.10.2013 17:42

Quote:понял о какой рекурсии идет речь, но это изврат - вызывать из лоухука код, в который установлен этот лоухук.
   хочу реального примера. Но посмотрю, конечно, что можно сделать.
Например, FindFileInLod. А в нём if FindFileInLod(файл с альтернативным расширением или изменённым именем, то вернуть его), иначе действие по умолчанию. У меня были и другие примеры, но нужно лезть в код и искать те, где была рекурсия. При чём порой рекурсия была косвенной, когда ты вызываешь АПИ, которое дёргает ту же функцию, в которой перехват. Это нормально, ибо рекурсия не бесконечная, а ограничена условиями и проверками.

Quote:с->esp -= 4;
IntAt(c->esp) = x;

Я обычно пишу так: dec(Context.esp, 4) и pinteger(context.esp)^ := x; Вряд ли можно чем-то упростить работу с обычными адресами в памяти, но посмотрим.

Quote:В HiHook UserData добавлю
Спасибо.

Quote:Но посмотрю, конечно, что можно сделать.
PUSHAD PUSH ESP? ))


RE: patcher_x86.dll - baratorch - 25.10.2013 20:20

(25.10.2013 17:42)Berserker Wrote:  
Quote: хочу реального примера.
Например, FindFileInLod. А в нём if FindFileInLod(файл с альтернативным расширением или изменённым именем, то вернуть его), иначе действие по умолчанию. У меня были и другие примеры, но нужно лезть в код и искать те, где была рекурсия. При чём порой рекурсия была косвенной, когда ты вызываешь АПИ, которое дёргает ту же функцию, в которой перехват. Это нормально, ибо рекурсия не бесконечная, а ограничена условиями и проверками.
в приведенном примере напрашивается использование хай хука на FindFileInLod. Но в любом случае я уже сделал сохранение регистров в стэке.

(25.10.2013 17:42)Berserker Wrote:  
Quote:с->esp -= 4;
IntAt(c->esp) = x;

Я обычно пишу так: dec(Context.esp, 4) и pinteger(context.esp)^ := x; Вряд ли можно чем-то упростить работу с обычными адресами в памяти, но посмотрим.
ты не понял о чем речь.
мой и твой код идентичны, дело не в простоте.
В твоем хуке по адресу (Context.esp - 4) храниться значение одного из регистров. Т.е. с каждым таким push в хуке ты будешь портить значения регистров, которые возможно соберешься использовать после такого пуша, и которые потом заPOPADишь в мосте, а за регистрами пойдут указатель на экземпляр THookHandlerArgs и адрес возврата в мост. Надеюсь, проблема понятна.

(25.10.2013 17:42)Berserker Wrote:  
Quote:Но посмотрю, конечно, что можно сделать.
PUSHAD PUSH ESP? ))
не так все просто.
В структуре HookContext патчера регистры в обратном порядке нежели в THookHandlerArgs и чтобы сохранить обратную совместимость
пушить регистры надо в обратном PUSHADу порядке.
плюс в новой версии патчера в HookContext еще и доступен для чтения/изменения регистр флагов, а он для той же обратной совместимости добавлен после поля return_address.

сама длл 2.1 и хэдер для с++ с комментариями уже готовы.
дополню твой PatchApi.pas и опубликую.


RE: patcher_x86.dll - Berserker - 25.10.2013 22:04

Отлично.


RE: patcher_x86.dll - baratorch - 26.10.2013 21:47

версия 2.1

библиотека + SDK + исходники

чейнджлог:
Code:
2.1

[-] чтение/изменение esp регистра в LoHook хуке работало неверно

[+] добавлены функции
    PatcherInstance::WriteHexHook
    PatcherInstance::CreateHexHook
    
    эти функции позволяют установить самый примитивный хук
    а именно LoHook без высокоуровневой оболочки-функции.
    тело хука пишется прямо в вызове WriteHexHook или CreateHexHook
    таким же образом как пишется патч с помощью WriteCodePatch или CreateCodePatch

[+] В любом LoHook хуке теперь доступен для чтения/изменения регистр флагов процессора (расширена структура HookContext)

[+] LoHook теперь хранит экземляр HookContext в стеке, а не по фиксированному адресу, что позволяет корректно рекурсивно выполнять LoHook хук.

[+] теперь внутри функции сработавшей по LoHook хуку можно использовать операции с помещением данных в стэк контекста хука, аналогичные команде процессора PUSH.
    размер памяти, доступной для такого помещения в стэк контекста ограничен 128 байтами для хука, созданного с помощью методов PatcherInstance::WriteLoHook и PatcherInstance::CreateLoHook, и произволен для хука, установленного с помощью новых методов
    PatcherInstance::WriteLoHookEx
    PatcherInstance::CreateLoHookEx.

[+] добавлена функция:
    PatcherInstance::BlockAt
    которая запрещает данному экземпляру PatcherInstance устанавливать по заданному адресу патчи/хуки

[+] добавлены функции
    HiHook::SetUserData
    HiHook::GetUserData

[+] расширен функционал PatcherInstance::WriteDataPatch и PatcherInstance::CreateDataPatch
    - добавлен формат символ %s (string) - копирует си-строку из аргумента

Добавил SetUserData и GetUserData к HiHook, но функцию установки HiHook хука с одновременным указанием UserData писать не стал.
Ее можно быстро написать самому, используя PatcherInstance::CreateHiHook, HiHook::SetUserData и HiHook::Apply

Есть один нюанс.
В хота 1.2 и в ХД+ версий ниже 3.59 есть критический баг в использовании патчера, который прощали старые версии, но не прощает 2.1


RE: patcher_x86.dll - Berserker - 26.10.2013 22:12

Quote:Добавил SetUserData и GetUserData к HiHook, но функцию установки HiHook хука с одновременным указанием UserData писать не стал.
   Ее можно быстро написать самому, используя PatcherInstance::CreateHiHook, HiHook::SetUserData и HiHook::Apply
Согласен. Не стал писать это же предложение.

Quote:В хота 1.2 и в ХД+ версий ниже 3.59 есть критический баг в использовании патчера, который прощали старые версии, но не прощает 2.1
Если есть двоичная несовместимость, можно было и в LoHook использовать PUSHAD (что быстрее ручного порегистрового сохранения). А с другой стороны, это нужно кучу всего обновлять. Меня просто всё время точит червь оптимизации )))

Кстати, использовал уже несколько раз исходники ХД при поиске информации. Так что открытые материалы имеют таки синергетический эффект.


RE: patcher_x86.dll - baratorch - 28.10.2013 12:46

версия 2.2

библиотека + SDK + исходники

чейнджлог:
Code:
2.2
[-] LoHook не сохранял регистр флагов



RE: patcher_x86.dll - baratorch - 03.12.2013 16:39

версия 2.3

библиотека + SDK + исходники

чейнджлог:
Code:
2.3
[+] добавлены функции
    Patcher::VarInit
    Patcher::VarFind
    и тип Variable для работы с общими "переменными" для всех текущих клиентов patcher_x86.dll
[+] функция SaveDump сохраняет в файл после списка всех установленных патчей список всех "переменных" с их текущими значениями.

patcher_x86.hpp для С++ дополнен еще и методами Patcher::VarGetValue(name, default_value) и Patcher::VarValue(name), которых
нет в PatchApi.pas
но подобные методы можно написать самому.

HD версии 3.660 теперь использует патчер 2.3 и использует его общие "переменные".
Экспортируемые _HD3_.dll методы HdCommon_Get и HdCommon_Set оставлены для обратной совместимости, но теперь являются оболочками новых методов патчера.
напомню, с помощью [SHIFT]+[CTRL]+[ALT]+[DEL] в ХД вызывается краш c генерацией логов,
и в сгенерированном patcher_x86 dump.txt можно посмотреть все общие "переменные" и их значения
Теперь чтобы использовать "переменные" ХД, не нужно грузить _HD3_.dll и импортировать ее функции.

еще один шаг для взаимодеийствия и дружбы модов.


RE: patcher_x86.dll - Berserker - 03.12.2013 19:21

Аналог общей библиотеки-реестра выходит (хранение ключей-значений с интерфейсом доступа к ним).


RE: patcher_x86.dll - Arseniy - 31.03.2014 12:28

У меня, может, немного нубский вопрос, но я пока не понял, как выглядит система с патчером в действии.
Вот если я хочу использовать вместе свой мод, который будет прописывать какие-то значения в игре с помощью патчера, и HD Mod.
Как, что и к чему нужно подключать?
И второй вопрос про jump хуки в код. Если я правильно понимаю, то функции патчера просто позволяют извлечь текущий контекст в ассеблеровском виде, то есть нужно доподлинно знать, что именно сейчас лежит в регистрах и за что отвечает?


RE: patcher_x86.dll - Berserker - 31.03.2014 21:39

Погляди что-ли плагины Феанора с исходным кодом в качестве примеров.


RE: patcher_x86.dll - baratorch - 03.11.2014 02:19

версия 2.5

библиотека + SDK + исходники

чейнджлог:
Code:
2.5

[+] Функциии PatcherInstance::CreateCodePatch и PatcherInstance::WriteCodePatch
    теперь умеют корректно воспринимать записанные прямо опкоды с относительной адресацией.
    Например 0x639C40: call 0x447799 раньше можно было записать только в виде
    WriteCodePatch(0x639C40, "c%", 0x447799); или
    WriteCodePatch(0x639C40, "E8 ~d", 0x447799);
    (+ еще можно: WriteDataPatch(0x639C40, "E8  54 DB E0 FF");  чего, в принципе, было достаточно)
    Теперь можно и прямо WriteCodePatch(0x639C40, "E8  54 DB E0 FF") ;  (т.е. теперь можно просто скопировать патч с OllyDbg и не париться с преобразованием)
    


2.4

[-] баг: установка LoHook на длинную последвательность NOP'ов приводила к ошибкам.



RE: patcher_x86.dll - baratorch - 03.11.2014 02:49

Bers, продолжим здесь.

baratorch Wrote:
Berserker Wrote:На мой взгляд, указанная тобой, Бара, проверка, неэффективна. Мало ли из какого кода какого модуля вызывается запись патча? Из ЕРМ? Из сгенерированного в оперативной памяти обработчика? Из модуля, который использует функционал другого?

Patcher := Core.GlobalPatcher.CreateInstance(pchar(GetUniquePatchName(BinPatchSource)));
Я создавал по экземпляру патчера для каждого файла-заплатки под уникальным именем и тут же пробовал через этот объект вносить изменения в цикле:
Patcher.Write(cardinal(Patch.Addr), cardinal(@Patch.Bytes), Patch.NumBytes, CODE_PATCH);
В итоге ошибки и, как следствие, вылеты.
Берс, я хочу чтобы у тебя там все работало с патчером.
Но что ты предлагаешь, убрать эту проверку совсем?
Мне это сделать не сложно. Я задавал вопросы, чтобы понять, можно ли решить твою проблему не трогая патчер, но ситуацию ты не прояснил.

Я пока не представляю себе случая когда модулю нужно создать патчер_инстанс, но не писать патч, а модулю не создававшему использовать чужой патчер_инстанс чтобы создавать патч; и чтобы требуемую задачу нелзя было полностью решить с помощью того что есть сейчас.
Покажи мне конкретный пример (из твоих слов о цикле и заплатках мне ничего не понятно).

Если убрать эту проверку, то (как мне кажется) теряется вообще смысл в классе PatcherInstance. И как тогда отслеживать авторов патчей в логе и дампе (одна из важнейших фич патчера), если автором патча может быть кто угодно, вместо обозначенного?


Quote:Patcher := Core.GlobalPatcher.CreateInstance(pchar(GetUniquePatchName(BinPatchSource)));
Я создавал по экземпляру патчера для каждого файла-заплатки под уникальным именем и тут же пробовал через этот объект вносить изменения в цикле:
Patcher.Write(cardinal(Patch.Addr), cardinal(@Patch.Bytes), Patch.NumBytes, CODE_PATCH);
Вот я написанное понимаю так, что и инстанция и патч через эту инстанцию создаются в одном куске кода, т.е. без вариантов в одном модуле (pe-файле... dll/exe).
Поэтому та проверка (на идейном плане), считаю, не может быть помехой (разве что в ее реализации косяки).

вот код из ХД:

Code:
void LoadBinPatch(char* file_name)
{
    DWORD fr;
    _dword_ patches_count = 0;
    _dword_ patch_size = 0;
    _byte_ patch_data[2048];
    _ptr_ address = 0;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    hFile = CreateFile(file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        PatcherInstance* _PIbin = _P->CreateInstance(Base::GetShortFileName(file_name));
        SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
        if (ReadFile(hFile, (LPVOID)&patches_count, 4, &fr,  NULL))
            for (int i = 0; i < patches_count; i++)
                if (ReadFile(hFile, (LPVOID)&address, 4, &fr,  NULL))
                    if (ReadFile(hFile, (LPVOID)&patch_size, 4, &fr,  NULL))
                    {
                        if (patch_size > 2048) BREAKPOINT;
                        if (ReadFile(hFile, (LPVOID)&patch_data, patch_size, &fr,  NULL))
                        {
                            _PIbin->Write(address, (_ptr_)patch_data, patch_size);
                        }
                        
                    }
        CloseHandle(hFile);
    }
}

...

void LoadBinPatches(char* dir)
{
    WIN32_FIND_DATA file_find_data;
    HANDLE h_search;
    BOOL finished = FALSE;

    Base::dir_set(dir);
    h_search = FindFirstFile("*.bin", &file_find_data);
    if (h_search == INVALID_HANDLE_VALUE)
    {
        Base::dir_restore();
        return;
    }

    while (!finished)
    {
        //////////////////////////////////////
        LoadBinPatch(file_find_data.cFileName);
        //////////////////////////////////////

        if (!FindNextFile(h_search, &file_find_data))
            if (GetLastError() == ERROR_NO_MORE_FILES)
                finished = TRUE;
            else
            {
                FindClose(h_search);
                Base::dir_restore();
                return;
            }
    }

    FindClose(h_search);
    Base::dir_restore();
}

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

...

Я понял в чем дело.

Нужно заменить

Patcher.Write(cardinal(Patch.Addr), cardinal(@Patch.Bytes), Patch.NumBytes, CODE_PATCH);

на

Patcher.Write(cardinal(Patch.Addr), cardinal(@Patch.Bytes), Patch.NumBytes, DATA_PATCH);

Это не очевидно, но я объясню почему.

Я понимаю что, практически все сочиняемые пачти подразумевают содержание кода. Однако CODE_PATCH в Методе Write
означает не то что 'мы пишем код', а то что данные по адресу @Patch.Bytes будут восприниматься как код.

Возмем пример из ченджлога к патчеру 2.5.
Допустим мы хотим поставить патч 0x639C40: call 0x447799.
В бин-файле это будет записано в прямом виде:
   0x639C40, 5, E8 54 DB E0 FF
после считывания в Patch.Bytes будет E8 54 DB E0 FF
но ведь @Patch.Bytes не равен 0x639C40!!!
а значит если мы подразумеваем что по адресу @Patch.Bytes код, а не данные, то E8 54 DB E0 FF будет вызывать не 0x447799, а функцию заданную адресом относительным к @Patch.Bytes.
Ну вот представь, что мы процессору передали на выполнение адрес @Patch.Bytes: будет вызвана функция не 0x447799, а совершенно левая.

***

Еще раз повторю вопрос: что выдает лог патчера?

создаем рядом с patcher_x86.dll - patcher_x86.ini c записью Logging = 1
и после установки всех патчей вызываем Patcher::SaveLog(char* file_name).

если в логе отсутствуют записи
ERROR! Can not create ... at ... (...): Wrong Patcher Instance!
то проверка на верный модуль ни при чем, а дело в CODE_PATCH

жду ответа.


RE: patcher_x86.dll - Berserker - 03.11.2014 20:14

Привет, только прочитал сообщение, отвечу дома. Извини, первые три месяца работы после универа, ещё вхожу в ритм.


RE: patcher_x86.dll - Berserker - 03.11.2014 20:21

Если я верно понимаю, при CODE_PATCH патчер считает, что я копирую блок существующего кода, в котором нужно исправить относительные смещения? То есть реальный безопасный патч без обработки — это всегда DATA_PATCH?


RE: patcher_x86.dll - baratorch - 03.11.2014 20:22

да, именно так


RE: patcher_x86.dll - Berserker - 03.11.2014 22:18

Откатил в Git для тестов до версии с Patcher, поменял на DATA_PATCH, вылеты исчезли. Значит перенесу потом изменения, вопрос решён, спасибо за помощь.


RE: patcher_x86.dll - baratorch - 07.11.2014 16:12

версия 2.6

библиотека + SDK + исходники

чейнджлог:
Code:
2.6

[+] Добавлен метод PatcherInstance::BlockAllExceptVA(_ptr_* va_args)
    блокирует для данного  PatcherInstance установку патчей для всех адресов кроме указанных.
      Добавлен метод Patcher::PreCreateInstance(char* name)
    нужен для применения методов PatcherInstance::BlockAt и PatcherInstance::BlockAllExceptVA
     до того как PatcherInstance будет полноценно создан с помощью Patcher::CreateInstance
[-] Исправлена возможность краша на старте из-за DEP.



RE: patcher_x86.dll - baratorch - 21.08.2015 11:25

версия 2.8

библиотека + SDK + исходники

чейнджлог:
Code:
2.8
[-] теперь EXTENDED_ и SAFE_ HiHook хуки могут быть установлены на рекурсивно выполняемый код.

2.7
[+] patcher_x86 теперь полностью совместим с DEP (Предотвращением выполнения данных)



RE: patcher_x86.dll - Bes - 10.09.2015 20:04

вопрос к Отцам, кто может теперь адаптировать ERA2.47 под новый патчер, ибо сейчас они перестали дружить - новый HD-мод и последняя (2,46 и 2,47) ERA Sorry
Владыка судя по всему не скоро вернётся к ERA (или никогда ((( )


RE: patcher_x86.dll - gamecreator - 11.09.2015 00:57

Почему это перестали? Они не пересекаются вроде. Может просто wog_patcherizer отвалился, или как там его?