Автор: 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.