Ru-MaNGOS

Ru-MaNGOS (http://mangos.ytdb.ru/index.php)
-   Прочая документация (http://mangos.ytdb.ru/forumdisplay.php?f=33)
-   -   Разбор пакетов WoW (http://mangos.ytdb.ru/showthread.php?t=95)

DeusModus 07.03.2010 16:14

Разбор пакетов WoW
 
Разбор структуры пакетов из wow.exe это просто! :)

Нужно:
1. Интуиция.
2. Знание "СИ".
3. IDA+HexRays.
4. Терпение. :)

Качаем IDA+HexRays.
нацеливаем IDA на wow.exe
декомпилируем, потом сохраняем всё в .c файл с помощью HexRyas:)

ищем в файле строчку типа
Код:

(461, (int)
находим её примерно в этом месте
Код:

  sub_5F9870(459, (int)sub_401220, 0);
  sub_5F9870(461, (int)sub_401330, 0);

итак
Код:

sub_5F9870() это что то типа OpcodeHandler()
для удобства переименовываем
Код:

sub_5F9870->OpcodeHandler
получаем
Код:

  OpcodeHandler(459, (int)sub_401220, 0);
  OpcodeHandler(461, (int)sub_401330, 0);

sub_401220() это функция обработки пакета под номером 459(SMSG_NOTIFICATION)
ищём по коду эту функцию sub_401220().
......
находим что то типа
Код:

//----- (00401220) --------------------------------------------------------
signed int __usercall sub_401220<eax>(__int64 a1<edi:ebp>, int a2, int a3, int a4, int a5)
{
....................

  v5 = (int)&v9;
  v10 = 1;
  v11 = 0;
  sub_422B90(a5, (int)&v9, 0x1000u);
  if ( !sub_6A6BF0(&v9, "DEBUGACTION: ", 0xDu) )
    v5 = (int)&v12;
  if ( *(_BYTE *)v5 == 126 )

sub_422B90() это функция для чтения строки(СИ строки с нулём на конце) из опкода.
переименовываем её в ReadString()

находим в начале файла.
int __thiscall ReadString(int this, int a2, unsigned int a3);


и переименовываем все рядом стоящими функции типа так...
Код:

int __thiscall ReadByte(int this, int a2);
int __thiscall ReadInt8(int this, int a2);
int __thiscall ReadInt16(int this, int a2);
int __thiscall ReadInt32(int this, int a2);
int __thiscall ReadInt64(int this, int a2);
int __thiscall ReadFloat(int this, int a2);
>int __thiscall ReadString(int this, int a2, unsigned int a3);
int __thiscall ReadString2(int this, int a2, int a3);
int __thiscall callReadString2(int this, int a2, int a3);
int __thiscall ReadPascalString(int this, int a2, int a3);


теперь мы знаем какие функции какой тип данных считывают из пакета :)


ИТАК. теперь мы хотим разобрать структуру какого либо SMSG пакета.
Обратите внимание, что CMSG пакеты разбираются по другому и пока что я это описывать не буду.:)
И ещё, некоторые пакеты SMSG которые есть в Opcodes.h(Мангос) их нету в wow.exe
это связанно с тем что эти пакеты только для GM клиента и в обычном клиенте их просто нет:)

Так вот... к примеру мы хотим узнать что за структура у пакета
SMSG_LOOT_LIST = 0x3F9,
переводим 0x3F9 в десятичное число и ищём по нашему коду.
OpcodeHandler(1017, (int)

нашли к примеру
OpcodeHandler(1017, (int)sub_65E100, 0);

переходим к sub_65E100

Код:

//----- (0065E100) --------------------------------------------------------
signed int __cdecl sub_65E100(int a1, int a2, int a3, int a4)
{
  int v4; // eax@1
  int v5; // esi@1
  __int64 v7; // [sp+4h] [bp-8h]@1

  v5 = a4;
  ReadInt64(a4, (int)&v7);
  v4 = sub_46DB20(v7, 8);
  if ( v4 )
    sub_65D6D0(v4, v5);
  return 1;
}

Видим что считывается только ReadInt64() int64, скорее всего этого GUID чего либо.:)
всё, структуру опкода мы знаем:)

все адреса приведённые здесь sub_65E100, sub_401220 и т.д. меняются с каждой версией клиента,
так что нужно полагаться на интуицию ;)



и ещё... клиент бывает считывает Пакованный гуид из пакета...
функция считывания выглядит примерно так

Код:

//----- (006A5870) --------------------------------------------------------
int __cdecl sub_6A5870(int a1, int a2)
{
  int v2; // ebx@1
  unsigned int v3; // edi@1
  int v4; // esi@1
  int v6; // ecx@1
  __int64 v7; // qax@3
  unsigned __int8 v8; // [sp+Fh] [bp-1h]@3

  v6 = a1;
  v4 = a2;
  v2 = 0;
  *(_DWORD *)a2 = 0;
  *(_DWORD *)(a2 + 4) = 0;
  ReadInt8(v6, (int)((char *)&a2 + 3));
  v3 = 0;
  do
  {
    if ( (unsigned __int8)(1 << v2) & BYTE3(a2) )
    {
      ReadInt8(a1, (int)&v8);
      v7 = v8 << (char)v3;
      *(_DWORD *)v4 |= v7;
      *(_DWORD *)(v4 + 4) |= *((_DWORD *)&v7 + 1);
    }
    v3 += 8;
    ++v2;
  }
  while ( v3 < 0x40 );
  return a1;
}

можно найти быстро её если искать строку
ReadInt8(v6, (int)((char *)&a2 + 3));
но!!!!!! строка не уникальна и в других версиях клиента может меняться...


Автор: Дерека.

Grinder 28.04.2011 12:46

Хорошая статья. Скажите, а где можно более детально узнать о разборе пакетов?

Я так понимаю что опкоды (например такой 0x3F9) берется из снифов! Я правильно понял?
В статье говорится о разборе SMSG пакетов, а как разбирать СMSG?

ПыСы
Может подобные вопросы и покажутся нубскими, но я думаю не только у меня есть большое желание научиться этому....

Chameleon 28.04.2011 13:21

Я делал мини гайдик на английском по поиску опкодов и структур для 4.0. Но в принципе и для 3.3.5 некоторая инфа тоже может быть полезна.

Lordronn 28.04.2011 15:58

Я прочитал его, но возник вопросик: мы ищем в дампе иды результат сложения оффсета и 8080? или ищем само выражение т.е 1111 + 8080(к примеру)?

Chameleon 28.04.2011 22:34

Цитата:

Сообщение от Lordronn (Сообщение 21579)
Я прочитал его, но возник вопросик: мы ищем в дампе иды результат сложения оффсета и 8080? или ищем само выражение т.е 1111 + 8080(к примеру)?

Нет, в Кате мы ищем текст "+ оффсет", где оффсет вычисляется например в OpcodeTools. 8080 это просто пример, который надо заменить оффсетом под нужный опкод.

TOM_RUS 29.04.2011 03:43

Я осуществляю поиск в IDB с помощью скрипта на питоне (findinstructions.py). Можно найти в блоге разработчиков IDA.

Astromancer 28.05.2012 20:56

Цитата:

Сообщение от DeusModus (Сообщение 349)
ищем в файле строчку типа
Код:

(461, (int)
находим её примерно в этом месте
Код:

  sub_5F9870(459, (int)sub_401220, 0);
  sub_5F9870(461, (int)sub_401330, 0);


Пытался разобрать сейчас 4.3.4, вышеуказанные пункты выполнил, но ни не нашол данных ни по одному ныне известных опкодов, и вообще
sub_5F9870(459, (int)sub_401220, 0); подобных строк нет, точнее в "субах" нет вложенных субов. Собственно говоря вопрос, что делать? (база idb взята с форума)

Dereka 29.05.2012 20:59

Цитата:

Сообщение от Astromancer (Сообщение 27449)
Пытался разобрать сейчас 4.3.4, вышеуказанные пункты выполнил, но ни не нашол данных ни по одному ныне известных опкодов, и вообще
sub_5F9870(459, (int)sub_401220, 0); подобных строк нет, точнее в "субах" нет вложенных субов. Собственно говоря вопрос, что делать? (база idb взята с форума)

в 4х клиенте все круто поменялось ( так что данная документация устарела

HuntsMan 03.06.2012 18:19

Цитата:

Сообщение от Dereka (Сообщение 27467)
в 4х клиенте все круто поменялось ( так что данная документация устарела

Для 4х сейчас все просто до невозможности:
1. Качаем xml с адресами почти всех обработчиков от LordJZ либо Камиллу.
2. Находим нужный опкод, смотрим нужные адреса.
3. ???
4. PROFIT!

Astromancer 03.06.2012 18:27

Ну понимаеш, LordJZ допустим не всегда будет снабжать нас всем необходимым, поэтому хотелось бы быть более независимым и сделать что то самому

HuntsMan 03.06.2012 19:18

А толку то? В бете панд уже опять все изменилось кардинальным образом.

Astromancer 03.06.2012 22:02

вот собственно мы и незаметно перешли к той теме о которой я и хотел спросить)) Так кто-нибудь уже декомпилил панд? Как оно?

Amaru 25.06.2012 11:54

Вроде бы разобрался на основе 14333 с Jam и Auth опкодами, с ними все прозрачно.
Использовал Opcode Calculator от Chameleon.

Но не понятно, что делать с regular opcodes, согласно его гайду
Цитата:

So to find a client handler for a particular SMSG opcode you can convert it to an offset using tools and
then search full decompile for ‘+ 8080’ (replaced by correct offset).
Получены оффсет 4844 (0x12EC) и converted 867 на примере опкода SMSG_NOTIFICATION 30375.
Текстовый поиск по + 4844 или + 0x12EC ничего не дает, кроме
Код:

void __usercall ClientDestroyGame(int a1<ebx>, double a2<st6>, double a3<st5>, double a4<st4>, double a5<st3>, double a6<st2>, double a7<st1>, double a8<st0>, int a9, int a10, int a11)
{
  int v11; // eax@2
  int v12; // eax@2
  int v13; // eax@2
  int v14; // eax@2
  int v15; // eax@2
  int v16; // eax@2
  int v17; // eax@2
  int v18; // eax@2
  void *v19; // edi@3
  int v20; // ecx@4
  void *v21; // edi@9

  if ( dword_CFA6A4 )
  {
    EventUnregister(5, (int)ClientIdle);
    v11 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 4844) = 0;
    *(_DWORD *)(v11 + 13036) = 0;
    v12 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 2272) = 0;
    *(_DWORD *)(v12 + 10464) = 0;
    v13 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 1840) = 0;
    *(_DWORD *)(v13 + 10032) = 0;
    v14 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 3836) = 0;
    *(_DWORD *)(v14 + 12028) = 0;
    v15 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 1800) = 0;
    *(_DWORD *)(v15 + 9992) = 0;
    v16 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 5324) = 0;
    *(_DWORD *)(v16 + 13516) = 0;
    v17 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 7116) = 0;
    *(_DWORD *)(v17 + 15308) = 0;
    v18 = g_clientConnection;
    *(_DWORD *)(g_clientConnection + 6024) = 0;
    *(_DWORD *)(v18 + 14216) = 0;
    dword_E0C6CC = 0;
    dword_E0C650 = 0;
    dword_E0C600 = 0;
    dword_E0C864 = 0;
    if ( dword_CFA754 )
    {
      v19 = dword_CFA754;
      sub_4011F0((int)dword_CFA754);
      SMemFree(v19, (int)"delete", -1, 0);
      dword_CFA754 = 0;
    }
    FriendList__Destroy();
    AreaListShutdown();
    CGItem_C__Shutdown();
    CGPlayer_C__Shutdown();
    PlayerClientShutdown();
    LootDestroy();
    CGUnit_C__Shutdown(a2, a3, a4, a5, a6, a7, a8);
    CGGameObject_C__Shutdown();
    CGObject_C__Shutdown();
    MovementDestroy(v20, a2, a3, a4, a5, a6, a7, a8);
    LootRollDestroy();
    EventUnregister(5, (int)MovementIdleMoveUnits);
    Spell_C_StopTargeting(a2, a3, a4, a5, a6, a7, a8);
    ClntObjMgrDestroyStd(a2, a3, a4, a5, a6, a7, a8);
    CGUnit_C__PostShutdown();
    CGObject_C__PostShutdown();
    SpellVisualsClear(a2, a3, a4, a5, a6, a7, a8);
    CEffect__Destroy(a2, a3, a4, a5, a6, a7, a8);
    CGWorldFrame__SetScreenEffect(a7, a8, 0);
    if ( dword_E96728 )
      CGWorldFrame__UnitClear((void *)dword_E96728);
    World__UnloadMap();
    WeaponTrailsShutdown();
    PlayerNameShutdown();
    WorldTextShutdown();
    CGGameUI__ShutdownGame();
    ClientDestroyGameTime();
    SI3__ShutDownAmbienceFlavor();
    SI3__ShutdownZoneSoundsHandler();
    SI3__FreeZoneIntros();
    SE3__StopAllSounds(-2, 0);
    dword_DE74F0 = 0;
    ClntObjMgrDestroyShared();
    AccountDataDestroy(0);
    dword_CFA6A4 = 0;
    LoadingScreenDisable();
    if ( dword_CFA6B0 )
    {
      dword_CFA6B0 = 0;
      dword_CFA6AC = 0;
    }
    BattlenetUI_LeaveRealm();
    CAnimKitManager__UninitSystem();
    CAnimReplacementSetDef__UninitSystem();
    if ( dword_CFC8D0 )
    {
      v21 = dword_CFC8D0;
      CCameraManager___CCameraManager((int)dword_CFC8D0);
      SMemFree(v21, (int)"delete", -1, 0);
      dword_CFC8D0 = 0;
    }
    if ( !a9 )
      ClientDBDisconnect();
    if ( a10 )
    {
      CGlueMgr__Resume(a1, a9);
      if ( a11 )
        dword_E7A798 = 11;
      CGlueMgr__SetScreen();
    }
  }
}

Что я не так делаю?

Chameleon 25.06.2012 12:40

Цитата:

Сообщение от Amaru (Сообщение 27769)
Получены оффсет 4844 (0x12EC) и converted 867 на примере опкода SMSG_NOTIFICATION 30375.
Текстовый поиск по + 4844 или + 0x12EC ничего не дает, кроме

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

Цитата:

Сообщение от HuntsMan (Сообщение 27515)
А толку то? В бете панд уже опять все изменилось кардинальным образом.

Что там серьезного поменялось? Я глубоко не смотрел и только мак версию, что с дебаг символами, но особых различий в обработке опкодов клиентом я не заметил.

Amaru 25.06.2012 13:08

Спасибо за быстрый ответ.

Для 14333 есть функция,
Код:

protected override bool NormalCheck(uint opcode)
        {
            return (opcode & 0x2322) == 8738 && opcode != 57919 && opcode != 26159;
        }

Нахожу место, где чекается в IDA получаю
Код:

//----- (00485940) --------------------------------------------------------
char __thiscall NetClient__ProcessMessage(void *this, int a2, int a3, int a4)
{
  int v4; // ebx@1
  void *v5; // edi@1
  unsigned int v6; // esi@1
  char result; // al@2
  unsigned int v8; // eax@3
  int v9; // ecx@6

  ++dword_D36C28;
  v4 = a3;
  v5 = this;
  CDataStore__GetInt16(a3, (int)&a3);
  v6 = (unsigned __int16)a3;
  if ( (a3 & 0x2399) == 0x301 )
  {
    result = NetClient__JAMClientDispatch(v5, 0, a2, a3, v4);
  }
  else
  {
    (*(void (__thiscall **)(void *, _DWORD))(*(_DWORD *)v5 + 72))(v5, (unsigned __int16)a3);
    v8 = v6 & 1 | ((v6 & 0x1C | (((unsigned __int8)(v6 & 0xC0) | ((v6 & 0x1C00 | (v6 >> 1) & 0x6000) >> 2)) >> 1)) >> 1);
    if ( (v6 & 0x2322) == 0x2222 && v6 != 0xE23F && v6 != 0x662F && (v9 = *((_DWORD *)v5 + v8 + 344)) != 0 )
      result = ((int (__cdecl *)(_DWORD, unsigned int, int, int))(v9 - ((v6 | (v6 << 16)) ^ 0x62A3A31D)))(
                *((_DWORD *)v5 + v8 + 2392),
                v6,
                a2,
                v4);
    else
      result = (*(int (__thiscall **)(int))(*(_DWORD *)v4 + 24))(v4);
  }
  return result;
}

в v8 походу считается наш оффсет
Правильно ли, что в *((_DWORD *)v5 + v8 + 2392) будет как раз адрес обработчика?

TOM_RUS 25.06.2012 14:45

Разве обработчик не
Код:

v9 - ((v6 | (v6 << 16)) ^ 0x62A3A31D)
где v9 зашифрованный оффсет, v6 - номер опкода
?

Код:

char __thiscall NetClient::ProcessMessage(ClientConnection *this, int a2, int opcode, int a4)
{
  CDataStore *data; // ebx@1
  ClientConnection *_this; // edi@1
  unsigned int op; // esi@1
  char result; // al@2
  unsigned int condensedId; // eax@3
  void *jamOffs; // ecx@6

  ++dword_D36C28;
  data = opcode;
  _this = this;
  CDataStore::GetInt16(opcode, &opcode);
  op = opcode;
  if ( (opcode & 0x2399) == 0x301 )
  {
    result = NetClient::JAMClientDispatch(_this, 0, a2, opcode, data);
  }
  else
  {
    (*(_this->vTable + 18))(_this, opcode);
    condensedId = op & 1 | ((op & 0x1C | (((op & 0xC0) | ((op & 0x1C00 | (op >> 1) & 0x6000) >> 2)) >> 1)) >> 1);
    if ( (op & 0x2322) == 0x2222 && op != 0xE23F && op != 0x662F && (jamOffs = _this->Handlers[condensedId]) != 0 )
      result = ((jamOffs - ((op | (op << 16)) ^ 0x62A3A31D)))(_this->HandlersState[condensedId], op, a2, data);
    else
      result = data->vTable->IsRead(data);
  }
  return result;
}


Amaru 25.06.2012 17:03

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

Код:

    //unsigned int op = 30375; //smsg notification
    //unsigned int * handle = (unsigned int *)0x00401230;
    unsigned int op = 43638; //smsg whois
    unsigned int * handle = (unsigned int *)0x004CF590;
    unsigned int * jamOffs = (unsigned int*)((unsigned int)handle + ((op | (op << 16)) ^ 0x62A3A31D));
    unsigned int condensedId = op & 1 | ((op & 0x1C | (((op & 0xC0) | ((op & 0x1C00 | (op >> 1) & 0x6000) >> 2)) >> 1)) >> 1);

    std::cout << std::hex << "Jam: " << jamOffs << std::endl;

    unsigned int * _this = (unsigned int *)(jamOffs - 344 - condensedId);
    std::cout << std::hex << "This: " << _this << std::endl;

Есть идеи как такое может быть?

Chameleon 26.06.2012 10:38

Цитата:

Сообщение от Amaru (Сообщение 27772)
Правильно ли, что в *((_DWORD *)v5 + v8 + 2392) будет как раз адрес обработчика?

До 4.1 в v5 + v8 + 344 был адрес обработчика. Теперь там зашифрованный адрес, который восстанавливается по формуле, которую написал TOM_RUS: v9 - ((v6 | (v6 << 16)) ^ 0x62A3A31D)

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

Основная проблема для поддержки новых и будущих версию, как я вижу - это создание автоматизированного парсера структуры Jam bitstream пакетов. Чтобы в ядре порядок данных в структуре пакетов мог оставаться стабильным между версиями, а порядок сериализации этих данных для конкретной версии основывался бы на информации сгенерированной этим чудесным парсером. Имея такую штуку можно было бы думать о переходе между новыми версиями за разумное время, без траты уймы времени на ручную перестановку порядка филдов в куче JAM пакетов.

Amaru 26.06.2012 11:10

Я пытаюсь получить handle для конкретного опкода
Как выяснилось, надо дополнительно знать либо _this, либо jamOffs
Решая обратную задачу, располагая найденным адресом какого либо хендлера, можно получить и _this и jamOffs, а далее использовать их для получения хендлеров всех остальных окодов.
Но то ли я что-то не понимаю, либо что-то не так считаю, но адреса остальных хендлеров не получаются
При этом посчитанный в начале jamOffs совпал с адресом, указанным LordJZ в его xml, что означает, что хендлер, из которого он считался, был найден правильно

Amaru 27.06.2012 02:01

Запарившись разбираться, написал патч, который считает все auth и special оффсеты, а также перехватывает адрес хендлера обычных пакетов, когда идет к нему обращение
https://github.com/Zakamurite/OpcodeHandleDumper
Офсет выдается относительно IDA, для известных опкодов адреса корректны, но можно найти и левые адреса, но среди них использующихся/известных опкодов не находил, скорее всего это несуществующие опкоды

Chipleo 27.04.2014 20:04

"обработки пакета под номером 459(SMSG_NOTIFICATION)" - подскажите откуда берется символьное значение опкода SMSG_NOTIFICATION=459, из клиента?

Lordronn 27.04.2014 21:16

из сниффа

Chipleo 27.04.2014 22:38

Это не ответ, тот кто писал сниф - выдумал названия, или гдето в клиенте есть точные названия?

Lordronn 27.04.2014 22:54

насколько я знаю было, но давно, очень давно

TOM_RUS 27.04.2014 23:17

Цитата:

Сообщение от Chipleo (Сообщение 32780)
Это не ответ, тот кто писал сниф - выдумал названия, или гдето в клиенте есть точные названия?

В ранних билдах клиента, а так же internal и прочих "особых" билдах есть названия опкодов. От туда они и были получены.

Chipleo 28.04.2014 13:48

Я начал собирать снифом с оффа информацию с билда 18019. Парсер пакетов как оказалось устарел и неисправен - опкоды изменились. Где можно взять последнюю возможную информацию как теперь в вов 5.4 искать среди дезасемблированого кода нужную информацию по обработке пакетов а также соответствие опкода - номеру. Выложеный вариант файла для IDA 6.1 у меня не открывается.

Chipleo 06.05.2014 17:24

Я так понял, что вов 5.4 - табу? никто так и не ответил.

Amaru 06.05.2014 18:09

Цитата:

Сообщение от Chipleo (Сообщение 32785)
Я начал собирать снифом с оффа информацию с билда 18019. Парсер пакетов как оказалось устарел и неисправен - опкоды изменились. Где можно взять последнюю возможную информацию как теперь в вов 5.4 искать среди дезасемблированого кода нужную информацию по обработке пакетов а также соответствие опкода - номеру. Выложеный вариант файла для IDA 6.1 у меня не открывается.

Искать так же как и раньше искали, начиная с каты.
Номера опкодов пока что одинаковые для всех билдов 5.4.7
Если ида не открывает - качай другую ида

Chipleo 08.06.2014 14:30

Я разобрался с SMSG_..., остался вопрос как в клиенте 18291 искать подготовку пакетов CMSG_... ?

Chipleo 25.09.2014 19:58

Поделитесь .pkt или .bin с оффа для 4.3.4?


Текущее время: 08:06. Часовой пояс GMT +3.

ru-mangos.ru - Русское сообщество MaNGOS