Программирование Аллодов 2

Часть 1. Исправление спидхака

Первая часть написана exc!ton (Vladimir Chebotarev aka ex-lend). Благодаря ей можно понять как внедрять любые фичи в Аллоды 2. Отдельное спасибо ZZYZX за помощь с восстановлением заголовков статьи. Данные принципы разработки действуют и на “Хэте Тангара“.

Статья рассчитана на людей, знакомых с программированием на Ассемблере.
Здесь я попытался показать, как добавлять собственный код в произвольное место исполняемых файлов при помощи add_dll на примере исправления спидхака.

Принцип работы add_dll
add_dll записывает по заданному RVA в исполняемом файле 6 байт – инструкцию безусловного перехода (jmp) или вызова (call) подпрограммы из библиотеки (dll).В случае безусловного перехода (самый полезный режим) после собственного кода нужно выполнить затёртые вызовом команды и вернуть управление в исходную программу, например, через mov edx, 0xADDRESS / jmp edx.

Спидхак
Багом спидхака называется многократный выход из здания без входа в него. Из-за ошибки игры персонаж в этом случае многократно копируется в список персонажей и обрабатывается опять же много раз вместо одного. Поэтому при попытке атаки будет наноситься во много раз больший урон. Чтобы исправить этот баг необходимо было найти обработку пакета выхода из здания и вставить проверку на нахождение в здании в этот момент.

Пример исправления
Для сборки dll потребуется Microsoft Visual Studio 2003/2005/2008 или Microsoft Visual C++ Toolkit 2003 (мало весит, зато без среды разработки).
В директории сборки должны быть следующие файлы:

  • a2server_orig.exe – оригинальный a2server.exe
  • add_dll.exe

Откройте server_utils.cpp – исходный код библиотеки:

void __declspec(naked) add_player_to_map()
{
        __asm
        {
                push    ebp
                mov     ebp, esp
                sub     esp, 0x0C
                mov     eax, [ecx + 0x4C] // unit
                test    eax, 8
                jz      aptm_already_on_map
                mov     edx, 0x052C40F
                jmp     edx
aptm_already_on_map:
                mov     edx, 0x052C47B
                jmp     edx
        }
}

#define DLL_PROCESS_ATTACH 1
#define DLL_PROCESS_DETACH 2

int __stdcall DllMain(void *hModule, unsigned long ul_reason_for_call, void *lpReserved)
{
	switch(ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		break;
		case DLL_PROCESS_DETACH:
		break;
	}

	return 1;
}
 
  • Откройте server_utils.def – определение библиотеки и список экспортируемых подпрограмм:

    LIBRARY server_utils
    EXPORTS
    add_player_to_map       @1
     

    Здесь задаётся номер подпрограммы add_player_to_map in dll (1).

  • Откройте server_utils.mapping – список адресов экзешника в exe-файле, которые будут переписаны при помощи вызовов dll
     
    0 1 52C409 // fix speedhack
    ////////// 0: jmp, 1: call
     

    По адресу 0x52C409 в исполняемый файл записывается вызов подпрограммы из dll номер 1 с использованием команды jmp (0).

  • Откройте compile.bat – скрипт для компиляции и прикрепления dll
     
    @echo off
    
    if exist "%VS90COMNTOOLS%vsvars32.bat" (
    call "%VS90COMNTOOLS%vsvars32.bat"
    ) else (
    if exist "%VS80COMNTOOLS%vsvars32.bat" (
    call "%VS80COMNTOOLS%vsvars32.bat"
    ) else (
    if exist "%VS71COMNTOOLS%vsvars32.bat" (
    call "%VS71COMNTOOLS%vsvars32.bat"
    ) else (
    if exist "%VS70COMNTOOLS%vsvars32.bat" (
    call "%VS70COMNTOOLS%vsvars32.bat"
    ) else (
    if exist "%ProgramFiles%\Microsoft Visual C++ Toolkit 2003" (
    call "%ProgramFiles%\Microsoft Visual C++ Toolkit 2003\vcvars32.bat"
    )
    )
    )
    )
    )
    
    cl /c server_utils.cpp /nologo
    if errorlevel 1 goto error
    link server_utils.obj /nologo /dll /def:server_utils.def /out:server_utils.dll
    if errorlevel 1 goto error
    copy /y a2server_orig.exe a2server.exe
    add_dll a2server.exe server_utils.dll server_utils.mapping
    
    echo Successfully completed!
    goto exit
    
    :error
    
    echo Shit happens
    
    :exit
     

После успешного выполнения скрипта появляются файлы server_utils.dll и a2server.exe.

Часть 2. Записки разработчика: поиск формулы дистанции телепорта

// Автор: ZZYZX; статья является монологом в дискорде

У меня есть примерное понимание где она должна быть
Где-то рядом с инициализацией информации про заклинание из свитка
Как бы объяснить
В общем там есть такой тип данных “применяемое заклинание”
Именно такие фиговины создаются внутри свитков, посохов, а также для наложения эффекта на лету (пример: обкаст персонажа при входе на сервер)
Массивом этих хреней является книга заклинаний у игрока
Скорее всего, можно найти по обработке параметра castSpell, например
Создание эффекта: sub_541046
Применение эффекта (заклинания) к юниту: sub_53EFB2, конкретно sub_53FA2B
Структура эффекта:
ID (тип) = *(uint8_t*)(effect+60)
заклинание = *(uint16_t*)(effect+64)
сила = *(uint16_t*)(effect+66)

При применении параметра, если ID=41 (castSpell) то зачем-то вызывается (*(int (__thiscall **)(int))(*(_DWORD *)a2 + 0x58))(a2). a2=юнит, втейбл в off_60B5D0.
То есть это sub_4684FB. По содержимому, там создаётся проджектайл какой-то. По ходу чё-то не то я нашёл тут.

Как же новая ида любит массивы. Любой непонятный тип? Массив!
а я понял очень простую вещь
У нас в сервере есть т.н. GM Cast
И она делает очень простую вещь.
Увеличивает длину каста до бесконечной
Уж не помню как я это нашёл)
Чё-т смотрю в книгу вижу фигу
С одной стороны длина каста-то увеличивается
Но вот как и почему — чё-т хз
По ходу проще смотреть по старому дальше )))
В общем я нашёл нечто sub_539F5A.
Я честно говоря не уверен что она делает
Но по-моему всё же кастует, только я не совсем понял что куда и как
Предположительно если *(DWORD*)(a2 + 104) это итем, то кастуется первое заклинание с итема
И тогда навык — да, берётся от заклинания с итема +66
Иначе он берётся из *(signed __int16 *)(a2 + 2 * v247 + 168) + *(signed __int16 *)(a2 + 136) – 30;
Я не совсем понял почему -30
Но возможно это подразумевается разум
да.
unit+136 (он же 0x88) это разум
Итого стартовый навык для заклинания изначально берётся как скилл+разум-30. И всё это уравнивается к 0-255
Ничего себе у телепорта логика сложная, я уже не уверен что это каст заклинаний
В общем рассчёт чего-то относительно заклинаний и скилла находится тут: sub_539541
Последний аргумент — навык
Но дальность находится не тут. Тут находится дамаг и другие параметры а-ля “насколько отхилить” и “надолго ли инвиз”. Тут кстати время кп от навыка тоже, только в каких-то попугаях указано (480 * навык / 100, судя по всему 1сек=32)
Логика телепорта (case 22) просто невообразимо длинная, занимает половину функции. Хз зачем
а, ёть, это не телпорт, это перерод
Конечно сложный
хд
Там с единицы же
Ну в общем применение заклинаний тут, но оно применяется явно после проверки на дальность, каковая находится где-то не тут
хм
пошёл открывать а2
так
В общем
Номер раз: расчёт всех параметров заклинания которые меняются от уровня — таки да в sub_539541
Номер два: я проглядел где там телепорт, но вообще-то он вот:

+9 это дистанция
Если интересует значение других параметров в заклинаниях, ищем по этому xref’у

sub_539541
Самый первый — это отрисовка подсказки от заклинаний)))
Которой на сервере нету, но вот функция отрисовки есть
В общем обнаружено два момента
Во-первых, формул дистанции телепорта две.

1) база + скилл / 3
2) база + скилл / 15 (в случае если режим сервера арена или дезматч — во всяком случае я так понял)

Для всех остальных заклинаний формула база + скилл / 30
Что такое база, щас скажу. Надо смотреть
Создание заклинаний по ID: sub_538FDD, sub_5391AB
32 * *(unsigned __int8 *)(v14 + 8) + dword_6D0748 — заклинания из data.bin. Это 0x6D0668+E0, если что. Парсинг датабина — в sub_50E0F0

В общем мне лень дальше курить. Я не нашёл где проставляется конструктор для массива заклинаний, а соответственно (не имея дебаггера) не могу найти функцию которая читает заклинания из датабина (там вызов по втаблу)
Во всяком случае сегодня
“1” — в датабине.
“навык/3” — внутри sub_539541. Но только не навык, а конечно же — 1 + (навык+разум-30) / 3

Итого для телепорта выходит дистанция 8
к слову)
У мага вроде разум 50
1 + 20 / 3
И единичка на счёт кривизны а2

Для свитка формула такая же только без участия разума
Чисто навык прописанный в свитке
Разум учитывается только если каст не из итема, а из книги
Для палки аналогично
А вот для мобов вроде так же как и нормально)

Добавить комментарий

Ваш адрес email не будет опубликован.