Май 182013
 

Автор: joaquimandrade

Библиотека это набор байтов. Миллионов байтов. Они могут представлять числа, функции (в машинном коде), массивы и строки. Эта статья попытается объяснить вам, как найти функции в библиотеках и как их использовать.

Найти функцию значит определить ее местоположение в библиотеке, которое обычно называется оффсет. По сути, это число байтов, которое необходимо пройти, чтобы достигнуть функции, начиная с первого байта библиотеки.

В библиотеках, откомпилированных GCC (Linux), оффсеты помечены символьными именами, с помощью которых Вы можете легко распознать, что находится по данному оффсету.

В библиотеках, откомпилированных VC++, оффсеты, в целом, не имеют никаких меток, так что приходиться обращаться к таким методам, как поиск строки в библиотеке. Это происходит потому, что Вы можете легко связать строки в события, например, если Вы видите надпись “Terrorists Win”, то Вы знаете, что имеете дело с функцией, связанной с концом раунда.

При поиске оффсетов, мы можем использовать следующие методы:

  1. Нахождение функции путем поиска строк.
  2. После нахождения функции, использовать ее для нахождения других функций, например, если Вы знаете, что ваша функция вызывает или вызывается другой функцией.
  3. Применять изложенные выше действия в обратном порядке (комбинированный)

Что облегчить данный процесс, вам следует делать его с помощью библиотеки Linux.
Таким образом, вам необходимо иметь библиотеки вашего мода для Linux и Windows, а также заполучить “IDA Pro Disassembler”.

В IDA
IDA Start
Нажмите New

Для библиотеки Linux:
IDA Linux

Для библиотеки Windows:
IDA Windows

Выберете библиотеку и откройте ее.

Нажмите 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)
Сигнатуры функций.

Извините, форма комментирования закрыта в данный момент.