Автор: joaquimandrade
Библиотека это набор байтов. Миллионов байтов. Они могут представлять числа, функции (в машинном коде), массивы и строки. Эта статья попытается объяснить вам, как найти функции в библиотеках и как их использовать.
Найти функцию значит определить ее местоположение в библиотеке, которое обычно называется оффсет. По сути, это число байтов, которое необходимо пройти, чтобы достигнуть функции, начиная с первого байта библиотеки.
В библиотеках, откомпилированных GCC (Linux), оффсеты помечены символьными именами, с помощью которых Вы можете легко распознать, что находится по данному оффсету.
В библиотеках, откомпилированных VC++, оффсеты, в целом, не имеют никаких меток, так что приходиться обращаться к таким методам, как поиск строки в библиотеке. Это происходит потому, что Вы можете легко связать строки в события, например, если Вы видите надпись “Terrorists Win”, то Вы знаете, что имеете дело с функцией, связанной с концом раунда.
При поиске оффсетов, мы можем использовать следующие методы:
- Нахождение функции путем поиска строк.
- После нахождения функции, использовать ее для нахождения других функций, например, если Вы знаете, что ваша функция вызывает или вызывается другой функцией.
- Применять изложенные выше действия в обратном порядке (комбинированный)
Что облегчить данный процесс, вам следует делать его с помощью библиотеки Linux.
Таким образом, вам необходимо иметь библиотеки вашего мода для Linux и Windows, а также заполучить “IDA Pro Disassembler”.
Выберете библиотеку и откройте ее.
Нажмите CTRL+F5. Это откроет диалог, где Вы можете выбрать место сохранения файла. Этот файл – декомпилированная версия вашей библиотеки (особенность IDA, в отм что она конвертирует машинный код обратно в код С (не очень читабельный код и не точно повторяющий оригинал, однако более читаемый, чем машинный код)).
В декомпилированной версии библиотеки Linux, у вас будет примерно такой код:
//----- (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(708);
result = __18CHalfLifeMultiplay(v2);
}
else
{
v1 = __builtin_new(728);
result = __17CHalfLifeTraining(v1);
}
return result;
}
Это функция InstallGameRules, где легко можно увидеть, что используется строка “exec game.cfg\n”.
Теперь взглянем на декомпилированную версию библиотеки для Windows:
//----- (10088530) --------------------------------------------------------
int __cdecl sub_10088530()
{
long double v0; // fst7@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 в файле Counter Strike для Windows будет давать нам функцию InstallGameRules. Вы можете увидеть это в псевдо-метке “sub_10088530” (не обращаем внимание на sub_10). Теперь это число представляется нам, как шестнадцатиричное. Обозначим его 0x88530.
Чтобы продемонстрировать другой метод, обратимся к декомпилированной 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 *result; // eax@25
int v12; // [sp-10h] [bp-58h]@16
float v13; // [sp-Ch] [bp-54h]@16
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 на InstallGameRules. Ищем InstallGameRules.
int __usercall sub_100DD350<eax>(int a1<ecx>, int a2<esi>, long double a3<st0>)
{
int v3; // ebp@1
int v4; // eax@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
v3 = 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 *)InstallGameRules();
И мы находим CWorld__Precache (offset 0xDD350).
В основном это все. При применении этих знаний и маленькой толики вашего мозга, Вы можете найти, в принципе, любую функцию.
Теперь о типах, которые может использовать функция. Вы можете проверить их, просмотрев список функций в Linux-версии библиотеки в окне IDA под именем Names. Я не знаю, существует ли простой путь для типа Return, однако вы всегда можете предположить его или проверить Half Life SDK, если это, конечно, имеет смысл.
Для данного случая:
{
"name" : "InstallGameRules",
"library" : "mod",
"return" :
{
"type" : "CHalfLifeMultiplay *"
},
"identifiers":
[
{
"os" : "windows",
"mod" : "cstrike",
"value" : 0x88530
}
]
}
Это должно находиться в файле InstallGameRules в папке configs/orpheu/functions. Помните, что если бы функция принадлежала классу, то вам бы пришлось создать папку с именем класса.
Следует знать еще кое-что. Оффсет гарантированно будет всегда одинаков каждый раз, когда библиотека загружается, однако он может с легкостью измениться, если библиотека обновилась. Однако эта проблема решается созданием сигнатур функции.
В общем, сканирование сигнатур означает следующее: вместо того, чтобы определять оффсет, определяется набор байтов, который можно найти в этом оффсете (те, что представляют функцию). Этот набор байтов может легко изменить свое местоположение, однако до тех пор, пока он существует в виде блока, вы можете легко найти его.
Перевод: Dani Minch
Источник: http://forums.alliedmods.net/showthread.php?t=118934
Статьи по теме:
Модуль Orpheu (v2.5.1)
Сигнатуры функций.
Еще у нас есть:
Тот парень. Что-то пишу, когда не лень.








Sorry, the comment form is closed at this time.