Перевод и редактирование: DJ_WEST
1. Вступление.
Данная статья поможет вам находить функции в библиотечных файлах, для их дальнейщего использования в скриптинге с помощью модуля Вы должны зарегистрироваться, чтобы видеть ссылки.. Поиск функции означает найти ее позицию в библиотечном файле, что обычно называют смещением (offset). Смещение - это количество байтов, начиная с начала библиотеки, которые необходимы пройти, чтобы найти функцию.
В библиотеках, скомпиленных с помощью gcc (linux), смещения помечены символическими именами, с помощью которых можно легко понять, что в них находится. В библиотеках, скомпиленных с помощью VC++ (windows), смещения не помечены именами, поэтому необходимо использовать поиск строк в библиотеке. Потому что с помощью строк вы сможете понять, что выполняет та или функция. К примеру, строка "Terrorists Win" уже говорит нам о том, что мы нашли функцию, которая работает с окончанием раунда.
2. Начало работы с программой.
Итак, для простоты поиска функций мы будем пользоваться библиотекой игры из linux. Для дальнейщего выполнения данной статьи нам понадобится программа IDA Pro Disassembler (не бесплатная). Бесплатная версия данной программы нам не подходит. Просьба не спрашивать на форуме, где можно найти/скачать данную программу, ищите сами с помощью поисковиков.
Запускаем IDA Pro. Затем, как появится данное окно:
Нажимаем New.
Если мы открываем linux библиотеку, то выбираем закладку Unix, затем опцию ELF/COFF Dynamic Library:
Если мы открываем windows библиотеку, то выбираем закладку Windows, затем опцию PE Dynamic Library:
Затем выберите библиотечный файл (к примеру, cs_i386.so или mp.dll) и откройте его.
Нажмите Ctrl + F5. Откроется новое диалоговое окно, в котором можно выбрать директорию, куда нужно сохранить декомпиленный файл библиотеки. Основная возможность программы IDA - это конвертация машинного кода обратно в C код, далеко от читаемого и не всегда схожего с оригинальным, но лучше понимаемого, чем машинный.
3. Поиск функции.
В декомпиленной версии linux библиотеки (в данном случае файл cs_i386.c) можно найти следующий код (он может немного отличаться от вашего, потому что в разных версиях библиотек код мог быть изменен разработчиками):
- Код: Выделить всё
//----- (000B3C10) --------------------------------------------------------
int __cdecl InstallGameRules()
{
int result; // eax@2
int v1; // eax@2
int v2; // eax@3
(*(void (__cdecl **)(_DWORD))&g_engfuncs[156])("exec game.cfg\n");
(*(void (**)(void))&g_engfuncs[160])();
if ( *(float *)(gpGlobals + 20) == 0.0 )
{
v2 = __builtin_new(0x2C4u);
result = __18CHalfLifeMultiplay(v2);
}
else
{
v1 = __builtin_new(0x2D8u);
result = __17CHalfLifeTraining(v1);
}
return result;
}
Данная функция называется InstallGameRules, в ней мы можем найти использование следующей строки:
- Код: Выделить всё
exec game.cfg\n
Теперь используя данную строку для поиска функции в декомпиленной версии windows библиотеки (в данном случае файл mp.c), мы найдем следующий код (он может немного отличаться от вашего, потому что в разных версиях библиотек код мог быть изменен разработчиками):
- Код: Выделить всё
//----- (10088530) --------------------------------------------------------
int __cdecl sub_10088530()
{
double v0; // st7@1
int v1; // eax@2
int v3; // eax@4
dword_101623DC("exec game.cfg\n");
dword_101623E0();
v0 = *(float *)(LODWORD(dword_101625B8) + 20);
if ( v0 == 0.0 )
{
v1 = (int)operator new(0x2E8u);
if ( v1 )
return sub_100C5EF0(v1, v0);
}
else
{
v3 = (int)operator new(0x2D0u);
if ( v3 )
return sub_10093D80(v3, v0);
}
return 0;
}
Из данного наблюдения понятно, что мы имеем дело с той же самой функций, так как больше такую строку в коде мы не встретим.
Это означает, что по смещению 88530 в windows библиотеке игры Counter-Strike мы найдем функцию InstallGameRules. Из кода видно, что функция имеет псевдо-имя sub_10088530. Игнорируя часть имени sub_10, мы получим HEX число - 0x88530, которое и будет нашим смещением.
4. Поиск вызова функции.
В качестве бонуса, найдем в декомпиленной версии linux библиотеки вызов функции InstallGameRules:
- Код: Выделить всё
//----- (0011163C) --------------------------------------------------------
int *__usercall CWorld__Precache<eax>(long double a1<st0>, int a2)
{
unsigned int v2; // edi@8
__int16 v3; // fps@15
long double v4; // fst6@15
char v5; // c0@15
char v6; // c2@15
char v7; // c3@15
int v8; // eax@16
int v9; // ecx@18
int v10; // eax@19
int v11; // ST1C_4@23
int *result; // eax@25
int v13; // [sp-10h] [bp-58h]@16
float v14; // [sp-Ch] [bp-54h]@16
int v15; // [sp-8h] [bp-50h]@22
g_pLastSpawn = 0;
g_pLastCTSpawn = 0;
g_pLastTerroristSpawn = 0;
(*(void (__cdecl **)(_DWORD, char[4]))&g_engfuncs[240])("sv_gravity", "800");
(*(void (__cdecl **)(_DWORD, char[4]))&g_engfuncs[240])("sv_maxspeed", "900");
(*(void (__cdecl **)(_DWORD, _DWORD))&g_engfuncs[240])("sv_stepsize", "18");
(*(void (__cdecl **)(_DWORD, _DWORD))&g_engfuncs[240])("room_type", "0");
if ( g_pGameRules )
__builtin_delete((void *)g_pGameRules);
g_pGameRules = InstallGameRules();
Из последней строки данного кода видно, что функция InstallGameRules вызывается из CWorld__Precache (реальное имя функции CWorld::Precache). Теперь вернемся снова в декомпиленную версию windows библиотеки. Делаем поиск по sub_10088530 и находим:
- Код: Выделить всё
//----- (100DD350) --------------------------------------------------------
int __usercall sub_100DD350<eax>(int a1<ecx>, double a2<st0>)
{
int v2; // ebp@1
int v3; // eax@3
int v4; // ecx@3
int v5; // esi@4
int v6; // eax@6
int v7; // ecx@7
int v8; // eax@8
signed int v9; // esi@17
int v10; // eax@28
int v11; // ecx@29
int result; // eax@33
float v13; // [sp-4h] [bp-10h]@25
signed int v14; // [sp-4h] [bp-10h]@31
v2 = a1;
dword_10162EFC = 0;
dword_10163D00 = 0;
dword_10163D04 = 0;
dword_10162430("sv_gravity", "800");
dword_10162430("sv_maxspeed", "900");
dword_10162430("sv_stepsize", "18");
dword_10162430("room_type", L"0");
if ( dword_10162304 )
operator delete(dword_10162304);
dword_10162304 = (void *)sub_10088530();
И мы находим функцию CWorld__Precache (смещение 0xDD350).
5. Получаем необходимые данные.
После изучения и понимания этой статьи вы сможете найти необходимые вам функции. Также возможно вам понадобится знать тип функции. Вы можете узнать его в IDA из списка функций linux библиотеки:
Для того чтобы узнать тип, который функция возвращает, можно воспользоваться HL SDK. Из файла gamerules.cpp:
- Код: Выделить всё
CGameRules *InstallGameRules( void )
{
SERVER_COMMAND( "exec game.cfg\n" );
SERVER_EXECUTE( );
if ( !gpGlobals->deathmatch )
{
// generic half-life
g_teamplay = 0;
return new CHalfLifeRules;
}
else
{
if ( teamplay.value > 0 )
{
// teamplay
g_teamplay = 1;
return new CHalfLifeTeamplay;
}
if ((int)gpGlobals->deathmatch == 1)
{
// vanilla deathmatch
g_teamplay = 0;
return new CHalfLifeMultiplay;
}
else
{
// vanilla deathmatch??
g_teamplay = 0;
return new CHalfLifeMultiplay;
}
}
}
Видно, что в данном случае возвращаемый тип CHalfLifeMultiplay.
6. Использование функции в Orpheu.
Теперь нужно применить полученные данные к модулю Orpheu. В директории configs/orpheu/functions создаем файл InstallGameRules со следующим содержимым:
- Код: Выделить всё
{
"name" : "InstallGameRules",
"library" : "mod",
"return" :
{
"type" : "CHalfLifeMultiplay *"
},
"identifiers":
[
{
"os" : "windows",
"mod" : "cstrike",
"value" : 0x88530
}
]
}
Не забывайте, что если функция принадлежит классу, то необходимо создать папку с именем класса и поместить файл туда.
7. Заключение.
Полученные смещения гарантирует нам то, что функции будут всегда по указанному адресу, каждый раз при загрузке библиотеки сервером. Но при обновлении версии библиотечных файлов смещение может поменяться, поэтому прийдется искать его снова или нужно находить сигнатуры функций, которые позволяют находить необходимое смещение даже при обновленных библиотеках.