Gratt

Модостроитель
- Регистрация
- 14 Ноя 2014
- Сообщения
- 3.428
- Благодарности
- 4.766
- Баллы
- 625
RU EN
Структура PATCH-файла
Что такое PATCH-файл
PATCH-файл — это сценарий, который изменяет участки памяти процесса без компиляции.
Все файлы с расширением .patch, находящиеся в директориях игры или в VDF-томах, автоматически выполняются при запуске.
Скрипт регистронезависим. Каждая строка содержит один оператор или блок.
Отключение патчей:
Инструкция всегда имеет наивысший приоритет — перечисленные патчи не выполнятся нигде.#disable
Union:#disable [Reference] #disable [Cast]Блоки движков:
Код внутри блока выполняется только для указанных exe.#engine
Поддерживаются:
- CRC32 исполняемого файла
- теги: G1, G1A, G2, G2A
Можно перечислять несколько значений через запятую или не указывать ничего (выполняется всегда).
Union:#engine [0x2BCD7E30, G2A] ... #/engineБлоки патчей:
Каждый блок выполняется автоматически.#patch
ИмяGLOBAL DATAделает переменные видимыми во всех патчах.
Union:#patch [GLOBAL DATA] INT GlobalInteger = 100 #/patchТипы данных
Базовые типы:
- INT → 12345, 0x12345
- FLOAT → 123.45
- BOOL → True / False
- HEX → "строка" или 'байты через пробел'
Размер — через GetHexSize, SetHexSize, SetHexAutoSize.
HEX + HEX работает как конкатенация.
Ссылочные типы (
Ссылка указывает на адрес памяти (или параметр INI) и автоматически снимает защиту на запись.@)
Union:INT @IntegerRef = 0x12345678 HEX @HexRef = 0x87654321 IntegerRef = 800 HexRef = '12 34 56'
Адрес можно задать выражением или напрямую:
Union:FLOAT @0x12345678 = 50.5 FLOAT @(0x12345678 + 400) = 35.3
Ссылки на INI:
Union:INT @SystemPack:Core:ShowDebugWindow = trueПриведение типов (Cast)
Бывает автоматическим и явным:
Union:INT Value = 5 FLOAT AutoCast = 10.0 + Value * 5.3 FLOAT ExplicitCast = 10.0 + FLOAT Value * 5.3
HEX снимает тип и превращает значение в байты:
Union:INT SourceInt = 65535 HEX CastedHex = HEX SourceInt INT RestoredInt = INT CastedHexУсловия:
Выражение должно приводиться к BOOL:IF / ELSE / END
Union:IF BOOL @SystemPack:Core:ShowDebugWindow != true MessageBox("Debug window disabled") ELSE MessageBox("Debug window enabled") ENDСтраницы памяти
Выделение и работа с выделенной страницей:
Union:AllocPage(15, 1024) HEX @15x00000000 = ... HEX @15x00000111 = ...Статические патчи:
Для патчей без условий и динамики — загружаются быстрее и кешируются.#patch static
Union:#patch static [Example] HEX @0x007524A6 = 'E9 EB C2 F1 FF' #/patch
Встроенные функции
Математика и значения
Sqrt(value)
Возвращает квадратный корень числа (INT или FLOAT).
Min(value1, value2)
Возвращает минимальное значение.
Max(value1, value2)
Возвращает максимальное значение.
Lim(minValue, currentValue, maxValue)
Ограничивает значение диапазоном.
Работа с HEX / памятью объектов
GetHexSize(value)
Возвращает текущий размер HEX.
SetHexSize(value, size)
Устанавливает размер HEX вручную.
SetHexAutoSize(value)
Автоматически подбирает размер (строки выравниваются по \0).
HexViewBox(bytePtr, size = auto)
Показывает содержимое памяти как последовательность байтов.
Сообщения и вывод
MessageBox(args …)
Отображает диалоговое сообщение.
PrintScreen(args …)
Печатает текст в игровую консоль.
Страницы памяти
AllocPage(index, size)
Выделяет страницу памяти (начиная с 1).
FreePage(index)
Освобождает ранее выделенную страницу.
Библиотеки и модули
LoadLibrary(libName)
Загружает библиотеку в память.
ModuleBase(moduleName)
Базовый адрес модуля или -1.
ModuleSize(moduleName)
Размер модуля или -1.
Поиск/замена и операции с памятью
FindAndReplace(from, to, startAddress, length)
Ищет последовательность байтов и заменяет.
MemSet(startAddress, bytePtr, length)
Заполняет память шаблоном.
MemCopy(startAddress, placeAddress, length)
Копирует память.
JMP(addressFrom, memTo)
Создаёт переход (заменяет 5 байт).
CALL(addressFrom, memTo)
Создаёт вызов (заменяет 5 байт).
Работа с INI и опциями
OptionDef(iniParameterRef, value)
Создаёт параметр в INI и задаёт значение, если его ещё нет.
Файловые операции
RenameFile(oldName, newName)
Переименовывает файл.
CopyFile(oldName, newName, replace = true)
Копирует файл (с заменой по умолчанию).
MoveFile(oldName, newName)
Перемещает файл.
DeleteFile(fileName)
Удаляет файл.
FileExists(fileName)
Проверяет существование файла.
Работа с плагинами и списками
LoadPlugins(pluginNames)
Загружает перечисленные плагины, возвращает число успешных загрузок.
ShowFunctionList()
Печатает список всех патч-функций.
Concat(args …)
Объединяет аргументы в одну строку.
Окна и процессы
ShowCmd() / HideCmd()
Показывает / скрывает консоль.
FindWindowHandle(className, windowName)
Возвращает хэндл окна.
GetProcessID(procName)
ID процесса по имени.
StartProcess(procName, cmdLine)
Создаёт процесс и возвращает его ID (или -1).
Система и игра
GetUnionVersion()
Возвращает версию Union.
FindSteamDirectory()
Находит путь к Steam.
Restart()
Перезапускает игру.
GetScreenSizeX() / GetScreenSizeY()
Текущее разрешение экрана.
GetLanguage()
Язык Union (1–8: Rus/Eng/Deu/Pol/Rou/Ita/Cze/Esp).
Ссылки, адреса и исполнение кода
GetRefAddress(reference)
Адрес по ссылке или значению.
ImportSymbol(moduleName, symName)
Адрес символа в модуле (иначе -1).
ExecAsm(code)
Исполняет ассемблерный байткод и возвращает результат.
Ассемблерные вставки
Общие сведения
Встроенные ассемблерные вставки создают участок байткода в памяти с правами чтения, записи и исполнения.
Каждая вставка автоматически создаёт:
- Переход к своей области памяти.
- Обратный переход в исходную функцию (если адрес возврата не указан вручную, возвращаемся к следующей действительной инструкции после джампа).
- Сохранение затёртых или частично затёртых инструкций в
orgcodeс пересчётом всех смещений. - Байткод вставки дополнен
nopна конце для удобного дизассемблирования и отладки.
Определение вставки
В квадратных скобках указывается адрес места, где будет вставлен 5-байтный jmp к байткоду вставки.
Все инструкции, которые будут затёрты полностью или частично этим джампом, сохраняются вorgcodeс корректным пересчётом смещений.
Пример:
Union:#assembler [0x0068CF02] ; байткод вставки #/assembler
Если не указывать адрес возврата вручную, вставка автоматически возвращается к следующей действительной инструкции после джампа:
Union:#/assembler [0x0068CF08]
ВАЖНО!
Переход к ассемблерной вставке должен полностью находиться внутри базового блока, то есть 5-байтовый джамп не должен частично пересекать действующую метку перехода.
Пример: мы не можем создать вставку в диапазоне адресов0x006C8767-0x006C876A, так как длина инструкцийadd + retnравна 4 байтам. Попытка вставить джамп здесь приведёт к пересечению с меткой переходаloc_6C876B:
Union:.text:006C875B pop ebx .text:006C875C mov ecx, [esp+3Ch+var_C] .text:006C8760 mov large fs:0, ecx .text:006C8767 add esp, 3Ch .text:006C876A retn .text:006C876B ; --------------------------------------------------------------------------- .text:006C876B .text:006C876B loc_6C876B: ; CODE XREF: oCGame::Render(void)+28↑j .text:006C876B ; oCGame::Render(void)+34↑j .text:006C876B mov ecx, ?zrenderer@@3PAVzCRenderer@@A ; zCRenderer * zrenderer .text:006C8771 mov eax, [ecx] .text:006C8773 call dword ptr [eax+4]
По той же причине ассемблерная вставка должна начинаться с начала инструкции, а не с её середины.
Orgcode
Командаorgcodeвставляет ранее затёртые или частично затёртые инструкции обратно в байткод.
- Если
orgcodeне вызывается, затёртые инструкции никогда не выполняются. - Оператор
+используется, если затёрто несколько инструкций. Он позволяет пропускать ненужные инструкции и выполнять только те, которые нужны.
Пример использования
Исходный код:
Union:0068CF02 imul eax, [edi+40h] 4 bytes 0068CF06 add ecx, eax 2 bytes 0068CF08 mov [esp+24h+var_8], ecx 4 bytes 0068CF0C lea esp, [esp+0] 4 bytes
Вставка:
Здесь:Union:#patch [Close alpha-lines on multipage documents] #assembler [0x0068CF02] imul eax, [$multiplier] mov ebx, [edi+48h] imul ebx, -2 add eax, ebx orgcode +1 #/assembler #/patch
orgcode +1выполняет все инструкции, кроме первой затёртой (imul eax, [edi+40h]), пропуская её.- Остальные затёртые инструкции выполняются с пересчётом всех смещений.
Передача переменных
Переменные, передаваемые во вставку, автоматически остаются в памяти до конца выполнения патча, если используются как хранилище данных.
- Для HEX и переменных, участвующих как хранилище данных, берётся адрес.
- Для остальных типов берётся значение.
Пример практического патча
Выравнивание NPC относительно лестницы и использование переменнойvector:
Union:#engine [G2A] #patch [Aligning npc by ladder] HEX vector = '00 00 00 00 00 00 00 00 00 00 00 00' ; хранилище данных для вставки #assembler [0x00727E45] cmp dword ptr [ebp], 83CF2Ch ; проверка класса oCMobLadder jnz return mov ecx, ebp lea eax, [$vector] ; передача переменной vector push eax call 52DCB0h ; GetAtVectorWorld mov ecx, edi test ecx, ecx jz return push eax call 61CBC0h ; SetHeadingAtWorld return: orgcode #/assembler [0x00727E4A] #/patch #/engine- Переменная
vectorслужит хранилищем данных, сохраняется на протяжении всего патча. - Ассемблерная вставка использует её адрес для передачи данных внутрь байткода.
PATCH File Structure
What is a PATCH file
A PATCH file is a script that modifies process memory without compilation.
All .patch files found in game directories or VDF archives run automatically on startup.
Case-insensitive. One operator/block per line.
Disabling patches:
Highest priority: listed patches never execute.#disable
Union:#disable [Reference] #disable [Cast]Engine blocks:
Code runs only for specified executables.#engine
Supports:
- executable CRC32
- tags: G1, G1A, G2, G2A
Multiple values allowed, or none (runs always).
Union:#engine [0x2BCD7E30, G2A] ... #/enginePatch blocks:
Executed automatically.#patch
GLOBAL DATAshares variables across patches.
Union:#patch [GLOBAL DATA] INT GlobalInteger = 100 #/patchData types
Built-ins:
- INT → 12345, 0x12345
- FLOAT → 123.45
- BOOL → True / False
- HEX → "string" or 'bytes separated by spaces'
Size via GetHexSize, SetHexSize, SetHexAutoSize.
HEX + HEX concatenates.
References (
Point to memory (or INI parameter) and unlock write automatically.@)
Union:INT @IntegerRef = 0x12345678 HEX @HexRef = 0x87654321
Direct addressing:
Union:FLOAT @0x12345678 = 50.5 FLOAT @(0x12345678 + 400) = 35.3
INI references:
Union:INT @SystemPack:Core:ShowDebugWindow = trueCasting
Automatic and explicit:
Union:INT Value = 5 FLOAT AutoCast = 10.0 + Value * 5.3 FLOAT ExplicitCast = 10.0 + FLOAT Value * 5.3
HEX converts to/from raw bytes:
Union:INT SourceInt = 65535 HEX CastedHex = HEX SourceInt INT RestoredInt = INT CastedHexConditionals:
IF / ELSE / ENDUnion:IF BOOL @SystemPack:Core:ShowDebugWindow != true MessageBox("Debug window disabled") ELSE MessageBox("Debug window enabled") ENDMemory pages
Union:AllocPage(15, 1024) HEX @15x00000000 = ... HEX @15x00000111 = ...Static patches:
Optimized for pure bytecode patches (no conditions).#patch static
Union:#patch static [Example] HEX @0x007524A6 = 'E9 EB C2 F1 FF' #/patch
Built-in Functions
Math
Sqrt(value)
square root.
Min(a, b)
minimum.
Max(a, b)
maximum.
Lim(min, value, max)
clamp to range.
HEX / object memory
GetHexSize(value)
get size.
SetHexSize(value, size)
set size.
SetHexAutoSize(value)
auto size (null-terminated).
HexViewBox(bytePtr, size = auto)
show bytes as text.
Messages & output
MessageBox(args …)
popup message.
PrintScreen(args …)
print to console.
Memory pages
AllocPage(index, size)
allocate.
FreePage(index)
free.
Libraries & modules
LoadLibrary(libName)
load library.
ModuleBase(moduleName)
base address or -1.
ModuleSize(moduleName)
module size or -1.
Search/replace & memory ops
FindAndReplace(from, to, startAddress, length)
search & replace bytes.
MemSet(startAddress, bytePtr, length)
fill memory.
MemCopy(startAddress, placeAddress, length)
copy memory.
JMP(addressFrom, memTo)
insert jump (5 bytes).
CALL(addressFrom, memTo)
insert call (5 bytes).
INI options
OptionDef(iniParameterRef, value)
create parameter if missing and set value.
Files
RenameFile(oldName, newName)
rename.
CopyFile(oldName, newName, replace = true)
сopy.
MoveFile(oldName, newName)
move.
DeleteFile(fileName)
delete.
FileExists(fileName)
check existence.
Plugins & utilities
LoadPlugins(pluginNames)
load plugins, return count.
ShowFunctionList()
print available functions.
Concat(args …)
concatenate to string.
Windows & processes
ShowCmd() / HideCmd()
show/hide console.
FindWindowHandle(className, windowName)
window handle.
GetProcessID(procName)
process ID.
StartProcess(procName, cmdLine)
start process, return ID or -1.
System & game
GetUnionVersion()
Union version.
FindSteamDirectory()
Steam path.
Restart()
restart game.
GetScreenSizeX() / GetScreenSizeY()
resolution.
GetLanguage()
language (1–8).
References, addresses, execution
GetRefAddress(reference)
pointer address.
ImportSymbol(moduleName, symName)
symbol address or -1.
ExecAsm(code)
execute assembly and return result.
Assembler Inserts
Overview
Built-in assembler inserts create a block of bytecode in memory with read, write, and execute permissions.
Each insert automatically provides:
- A jump to its bytecode area.
- A return to the original function (if the return address is not specified manually, it returns to the next valid instruction after the jump).
- Preservation of overwritten or partially overwritten instructions in
orgcode, with all offsets recalculated. - The insert’s bytecode is padded with nop at the end for easier disassembly and debugging.
Insert Definition
Inside the square brackets, specify the address where a 5-byte jmp to the insert will be placed.
All instructions fully or partially overwritten by this jump are saved inorgcodewith offsets recalculated.
Example:
Union:#assembler [0x0068CF02] ; insert bytecode #/assembler
If no return address is provided manually, the insert automatically returns to the next valid instruction after the jump:
Union:#/assembler [0x0068CF08]
IMPORTANT!
A jump to assembler insert must be fully contained within a basic block, meaning the 5-byte jump must not partially overlap an existing label.
Example: you cannot create an insert in the address range0x006C8767-0x006C876A, because the length of the instructionsadd + retnis 4 bytes. Attempting to insert a jump here would overlap the jump labelloc_6C876B:
Union:.text:006C875B pop ebx .text:006C875C mov ecx, [esp+3Ch+var_C] .text:006C8760 mov large fs:0, ecx .text:006C8767 add esp, 3Ch .text:006C876A retn .text:006C876B ; --------------------------------------------------------------------------- .text:006C876B .text:006C876B loc_6C876B: ; CODE XREF: oCGame::Render(void)+28↑j .text:006C876B ; oCGame::Render(void)+34↑j .text:006C876B mov ecx, ?zrenderer@@3PAVzCRenderer@@A ; zCRenderer * zrenderer .text:006C8771 mov eax, [ecx] .text:006C8773 call dword ptr [eax+4]
For the same reason, an assembler insert must start at the beginning of an instruction, not in the middle of it.
Orgcode
Theorgcodecommand reinserts previously overwritten or partially overwritten instructions into the bytecode.
- If
orgcodeis not called, the overwritten instructions are never executed. - The + operator is used if multiple instructions were overwritten. It allows skipping unnecessary instructions and executing only the ones needed.
Example
Original code:
Union:0068CF02 imul eax, [edi+40h] 4 bytes 0068CF06 add ecx, eax 2 bytes 0068CF08 mov [esp+24h+var_8], ecx 4 bytes 0068CF0C lea esp, [esp+0] 4 bytes
Insert:
Here:Union:#patch [Close alpha-lines on multipage documents] #assembler [0x0068CF02] imul eax, [$multiplier] mov ebx, [edi+48h] imul ebx, -2 add eax, ebx orgcode +1 #/assembler #/patch
orgcode +1executes all overwritten instructions except the first one (imul eax, [edi+40h]), skipping it.- Other overwritten instructions are executed with offsets recalculated.
Variable Passing
Variables passed into the insert automatically remain in memory until the end of the patch if used as a data storage.
- For HEX and variables used as data storage, their address is passed.
- For other types, the value is passed.
Practical Patch Example
Aligning an NPC to a ladder and using thevectorvariable:
Union:#engine [G2A] #patch [Aligning npc by ladder] HEX vector = '00 00 00 00 00 00 00 00 00 00 00 00' ; data storage for insert #assembler [0x00727E45] cmp dword ptr [ebp], 83CF2Ch ; check for oCMobLadder class jnz return mov ecx, ebp lea eax, [$vector] ; pass the vector variable push eax call 52DCB0h ; GetAtVectorWorld mov ecx, edi test ecx, ecx jz return push eax call 61CBC0h ; SetHeadingAtWorld return: orgcode #/assembler [0x00727E4A] #/patch #/engine- The
vectorvariable acts as data storage and remains valid for the entire patch. - The assembler insert uses its address to pass data into the bytecode.
Последнее редактирование:


