Разбор протокола World Of Tanks

Часть первая: инструментарий мелкосерийного изобретения велосипедов

Почему и зачем: длинная и необязательная преамбула

Хорошо, что опыта игрового модостроительства у меня было немного — так, пару кастомных прицелов для Deer Hunter 2005 и «нелицензионный» недоклиент VATSIM/FSD с сопутствущим «взломом» протокола последнего. Ещё лучше, что ни разу не приходилось с головой погружаться в сколь-нибудь трудоёмкую и длительную отладку и дизассемблирование. То есть, с IDA и OllyDBG я поверхностно знаком, но не как с ежедневными рабочими инструментами.

В WOT играю с начала 2011 года. Не запоем, а, скорее, набегами — по 5-6 боёв вечером. Было время 2 года назад, наш клан состоял в Красном Альянсе, ходил на глобалку по ночам, выполнял какие-то тактические задачи на европейском ТВД, устраивал тренировки и спарринги, вовсю бурлили внутриигровые политические страсти, отпочковывались учебные кланы. Сейчас всего этого уже нет, и наш золотой ёжик превратился в табличку над «Домом Ветеранов».

Впадать в ересь сравнения танков с другими MMO не буду, так как хорошо знаком только с танками. Тем более не знаком ни с одним другим проектом, использующим BigWorld, поэтому искренне верю WarGaming'у на слово, что существуют и (не)тривиально (не)преодолеваются различные техномагические ограничения движка — на размер карты, на максимальную скорость юнита, на численность команд и прочее. Оставаясь в рамках внутренней критики, я также понимаю, что, с точки зрения целевой аудитории танков вообще, и их активного игрового коммьюнити в частности, каждое нововведение из очередного патча, безусловно, гораздо более востребовано и обосновано, сколь бы малым оно ни было. И что оптимизировать Motion Blur на несколько процентов это, безусловно, важнее, чем отменить принципиальную неизменяемость привязки действий на кнопки мыши для тех, кто привык на них ставить движение вперёд-назад (DOOM-стайл, да).

Итак, я уверен, что в обозримом будущем никаких планов по введению полноценного режима спектатора в WOT нет и не будет. Под полноценным режимом спектатора я понимаю множественные подключения игроков в сеанс боя изначально как невзаимодействующих на игру «привидений»-наблюдателей, а не на технике. Это тот самый режим, из-за отсутствия которого комментаторы на чемпионатах WOT вынуждены заходить в бой 15-м танком, убиваемым своими на базе. Это тот самый режим, из-за которого появились моды «командирского zoom» и «кинематографической камеры» — по сути, просто костыли. А нужен такой режим затем, чтобы командир роты занимался командованием, а не скакал впереди на лихом танке по-чапаевски, чтобы он видел ситуацию на карте в целом непрерывно, а не отвлекался на неё в пылу нападения из засады. В идеале, командиру даже не нужны красоты трёхмерного мира — достаточно одной большой карты на весь монитор с игровой ситуацией в реальном времени — HP, повреждениями модулей, членов экипажа, направлениями стволов и прицелов союзной техники, засвеченных в каждый момент вражеских юнитов и прочей вспомогательной информации.

WOT предоставляет широкие возможности модостроительства, но такая идея выходит за рамки классического «заменить пару swf-файлов на свои». Потребуется перехват и разбор самого игрового протокола для того, чтобы иметь возможность передать на командирский планшет своё видение игровой ситуации.

Обход шифрования 0x0A-й дорогой

Первые робкие попытки пролезть в протокол танков я предпринял ещё в 2011 году. Начинать это, как и везде в подобных задачах, стоит с хорошего сниффера (кстати, в случае с VATSIM/FSD на этом можно и остановиться — внезапно оказалось, что протокол там текстовый), и я, вооружившись Microsoft Network Monitor'ом, ринулся в бой. За отчётные 3 года в схеме входа ничего кардинально не поменялось, кроме количества игровых кластеров. Сервис авторизации у каждого кластера живёт на одном IP, к нему уходит один пакет с логином-паролем, и от него приходит один пакет — как минимум, с идентификатором конкретного игрового сервера из этого же кластера, на который клиенту надо переключиться, и с чьим IP происходит весь дальнейший обмен. Выглядит это всё приблизительно так

image

Помеченные октеты, судя по тому, что изменяются они мало и более-менее предсказуемо — заголовок пакета. После них идёт равномерный битовый шум, то есть там уже включается шифрование. Тут можно было вспомнить, что записи боёв в WOT шифруются BlowFish'ем, и что ключ шифрования не изменялся с того момента, как стал известен широкой публике, и покопаться глубже — но смысла в этом особого не вижу. Для задуманного гораздо важнее то, что ходит между клиентом и сервером в процессе игры, чем то, как он авторизуется при входе.

Итак, если трафик проходит потоковое шифрование, логично предположить, что где-то в глубинах программы есть функция наподобие SendToServer(), в которой есть вызов вроде EncryptBuffer() и в которой, в конечном счёте, выполнение доходит до конкретного sendto(). Наша задача, для начала — найти, где это происходит. Загружаем танки в OllyDbg и перед нажатием на кнопку «Войти» ставим логирующий брейкпоинт на sendto().

image

Через несколько десятков срабатываний, уже в ангаре, вызовы sendto() становятся более монотонными, в том плане, что адрес буфера для отправки данных не меняется:

77062E14  Call to WS2_32.sendto from WorldOfTanks.00BA5B97
            000004D8  Arg1 = 4D8
            04436A75  Arg2 = 4436A75
            00000010  Arg3 = 10
            00000000  Arg4 = 0
            0018E2DC  Arg5 = 18E2DC
            00000010  Arg6 = 10

Чтобы узнать, где шифруется этот буфер, я сначала пошёл по неправильному пути — начал изучать графы вызовов в IDA и отслеживать вручную возвраты из функций. Где-то на 15-й такой функции мне это надоело, я полностью заблудился в коде и поставил брейк на запись по адресу буфера.

image

Брейк сработал внутри вот такой чудесной функции по адресу 0x00BAF76F, чудесность которой состоит в том, что в неё передаются 3 параметра — два адреса буфера и ещё один — их одинаковая длина. Тот самый EncryptBuffer(ptr_src,ptr_dest,len), который нам нужен. Что там лежит в незашифрованном виде, мы пока смотреть не будем, об этом позже.

image

Это что касается отправки данных. Как быть с приёмом? Немного сложнее, но, в целом, так же, поэтому не буду утомлять вас большими и страшными скриншотами отладчика. Схема такая — ставим брейк на recvfrom(), смотрим адрес буфера, куда складывается принятый зашифрованный пакет. Ставим брейкпоинт на чтение с адреса буфера и тут нам в очередной раз повезло — брейк срабатывает на вызове функции по адресу 0x00BAFB79, которая занимается расшифровкой блоков по 8 байт и находится, в свою очередь, в теле функции по адресу 0x00BAFB30. А уже эта функция почти такая же чудесная, как и та, что мы нашли выше: она принимает 4 параметра — два адреса буфера, их длину и какой-то флаг.

image

Назовём её DecryptBuffer(ptr_src,ptr_dest,len,flag). Адреса буферов, как видно, совпадают. Что полностью логично, так как при потоковом шифровании стоит ожидать одинаковой длины блоков исходного и шифротекста.

Осталась одна тонкость. Если функцию EncryptBuffer() достаточно перехватывать непосредственно перед её вызовом (т.е. ставим INT3-брейкпоинт вместо CALL) вытаскивая память по адресу ptr_src длиной len, то в момент вызова DecryptBuffer() оба указателя покажут на один и тот же блок, который ещё пока зашифрован. Поэтому перехватывать эту функцию надо перед самым возвратом, который у неё происходит командой RETN 10 по адресу 0x00BAFBA8. В этот момент на стэке лежат те же параметры, за исключением того, что ptr_src равен нулю (это нововведение версии 0.8.11, в предыдущей указатели как-то оставались равны) и адрес возврата. И ptr_dest, конечно, показывает на расшифрованный буфер. Теперь, когда мы знаем, где в клиенте WOT сообщения ещё не зашифрованы и где уже расшифрованы, нужно их оттуда автоматически вытаскивать для дальнейшего анализа.

Что же там внутри?

Здесь матёрые гуру реверс-инжиниринга, в духе «There's an emacs command to do that» подумают: «ага, ну теперь можно написать на питоне\эзотерическом языке вот такой скрипт\плагин для ольки\иды, который будет делать с этими данными всё что хочешь и даже за пивом сбегает». Но мы пойдём другим путём. Я хочу сразу ориентироваться на то, что ещё до того, как дело дойдёт до разработки командирского планшета, то есть даже для самого полноценного разбора протокола WOT мне понадобятся помощники и тестировщики, далёкие от мира программирования. Им нужен будет простой инструмент с понятным интерфейсом, выдающий легко читаемые данные.

Так что предвидя скептические ухмылки, я засел за Lazarus и набросал в нём специализированный win32-отладчик, основной функцией которого является поставить два INT3-брекпоинта в нужных местах и по их срабатыванию вытаскивать данные по адресу и длине буфера, лежащим на стеке по известным смещениям. Ещё он умеет вести txt.gz лог с hexdump'ами пакетов и записывать все прошедшие пакеты так, что их потом можно заново «проиграть» через парсер. Вот что получилось. Так WOT клиент только начинает входить в ангар.

image

А вот так он себя ведёт уже находясь в ангаре.

image

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

Во-первых, видно, что все пакеты начинаются либо с 0x48, либо с 0x58, либо с 0x78; какой-нибудь закономерности на этот счёт я ещё не уловил, кроме того, что пакеты с уже известной функцией свой значащий байт не меняют.

Во-вторых, почти в каждом пакете есть один или несколько счётчиков сообщений; например, в keep-alive и ответах на них этим занимается третий и четвёртый байт в заголовке, кроме того сам пакет содержит ещё счётчик сообщений общий (включая keep-alive) и какой-то специфический (считающий пакеты без учёта количества keep-alive); всё это имеет отношение к контролю доставки пакетов, который пришлось прикрутить к UDP, вероятно, для оценки потерь и пересылки BLOBов (об этом позже).

Размеры всех пакетов выравнены по границам 8 байт, что ненавязчиво указывает нам на размер блока всё того же BlowFish; пока я до этого догадался, прошло немало времени в попытках обьяснить странную «контрольную сумму» в конце, да ещё и переменной длины. В конце концов, получив в этом паддинге вместо простого мусора слово «Flags» я, наконец, прозрел.

Ну и в лучших хакерских традициях, в конце каждого пакета находится сигнатура мёртвой говядины; кто бы мог подумать где мы её найдём :)

После получения Session ID от сервера приезжает пакет, содержащий номер игрока в неожиданно текстовом формате (792067). А вот пакет, который начинается на 0x78 0x00 на первом скриншоте — особо интересен. Сочетание сигнатуры 0x80 0x20 вкупе с тем, что перед каждым строковым литералом в нём стоит 0x55 и байт длины строки, а после каждого 0x71 находится возрастающий номер, должно насторожить опытных питонщиков — это же, чёрт побери, Python Pickle со своим запихиванием всего подряд в мемо! Вот он такой:

Pickle
Dct[15]:(xmpp_host = wot-ru.loc
captchaKey = 6Lc8GcASAAAAAKffZdxeZZvOvmSTNXbZvsy6CgBR
voipDomain = www.wotp.vivox.com
file_server = Dct[6]:(clan_emblems_small = Dct[1]:(url_template = http://ce.worldoftanks.ru/dcont/clans/emblems/%d/emblem_32x32.png)
clan_emblems_big = Dct[1]:(url_template = http://ce.worldoftanks.ru/dcont/clans/emblems/%d/emblem_64x64.png)
rare_achievements_images_big = Dct[1]:(url_template = http://ce.worldoftanks.ru/dcont/achievements/medals/180x180/%d.png)
clan_emblems = Dct[1]:(url_template = http://ce.worldoftanks.ru/dcont/clans/emblems/%d/emblem_64x64_tank.png)
rare_achievements_images = Dct[1]:(url_template = http://ce.worldoftanks.ru/dcont/achievements/medals/67x71/%d.png)
rare_achievements_texts = Dct[1]:(url_template = http://ce.worldoftanks.ru/dcont/achievements/medals/medals_%s.xml))
newbieBattlesCount = 100
roaming = Lst[4]:(1,1,Lst[3]:(Lst[4]:(1,1,499999999,RU),Lst[4]:(2,500000000,999999999,EU),Lst[4]:(3,1000000000,1499999999,NA)),Lst[0]:())
xmpp_enabled = True
jdCutouts = 0
xmpp_port = 5222
isTutorialEnabled = True
wallet = Lst[2]:(True,True)
xmpp_connections = Lst[1]:(Lst[2]:(xmppcs.worldoftanks.net,5222))
xmpp_resource = wot
regional_settings = Dct[2]:(starting_day_of_a_new_week = 0
starting_time_of_a_new_day = 0)
reCaptchaParser = )



В следующей части, если она заинтересует уважаемых обитателей Хабра, я расскажу о том, как в протоколе WOT передаются файлы, размеры которых намного больше реалистичного размера UDP пакета и MTU. И о том, что эти файлы оказывается сжатыми zlib'ом а внутри у них всё тот же Python Pickle с разными неожиданными вещами.

Спасибо за внимание!

UPD. Свежие новости! По агентурным данным из самого сердца КВГ, мой лёгкий намёк на потенциал коммерческого использования командирского планшета поднял «небольшой бугурт» (дословно), в результате чего кровавые модераторы в полнейшей панике наконец заметили и слили в мусорник тему проекта на официальном форуме танков, а мне было доверительным шёпотом посоветовано замылить данные своего аккаунта на скриншотах.

Я, имярек, находясь в здравом уме и трезвой памяти, торжественно клянусь, что разработка эта имеет исключительно академический характер, клянусь никогда в жизни не покупать компанию WarGaming.net за несметные миллиарды, вырученные от продажи ещё даже не существующей разработки либо иным способом покушаться на финансовое благополучие любимой фирмы! Аминь.

UPD2.
«Видеопрезентация» из ныне убиенной темы на форуме танков
Поделиться публикацией

Комментарии 90

    0
    картинок нет :(
      +7
      Но! Как плейбой без фоток!
      0
      Простите, это мой первый пост, я пытаюсь разобраться почему habrastorage не даёт вставлять картинки
        0
        Habrastorage лёг, половина всех картинок умерло временно. Повезло вам ;)
        (залитые на beta.hstor.org/getpro/habr-sb)
          0
          Видимо, новый хабрасторэдж глючит, так как вот тоже пост из песочницы и тоже не грузятся картинки habrahabr.ru/post/212347/
            0
            Решение там же в комментах.
              +1
              Я перезалил на собственный сервер
                0
                Да, я его туда написал, когда увидел, как починились картинки в этом посте.
            0
            Так в итоге получился «командирский планшет» или нет?
              +11
              Ещё нет, но принципиальных препятствий к этому не вижу. Вход в бой — серия тех же самых pickle, боевая ситуация передаётся в коротких пакетах, где номера игроков и события\параметры техники. Хотелось просто закрепить приоритет публикацией и радовать общественность новыми открытиями по мере их поступления.
                0
                принципиальных препятствий к этому не вижу

                В том же QuakeWorld клиенту не передаются координаты игроков и айтемов, которые он не видит.
                Поэтому даже внутри программы нет полной картины.
                  +1
                  Здесь тоже не передаются состояния вражеских юнитов, которые пропали из засвета. Однако, миникарту с отметками мест, где их последний раз видели, это сделать не помешало.
              +4
              С нетерпением жду продолжения.
                +2
                Очень интересно будет узнать продолжение :)
                Хм, а как интересно отнесется КВГ к разбору протокола, после публикации вашего командирского планшета?
                  0
                  На официальном форуме КВГ уже неделю пылится тема этого проекта, и пока за мной никто не гонялся.
                  А потом, чем чёрт не шутит, сделают это собственной фишкой, как произошло с OTM и таймером КД.
                    0
                    на самом деле это противоречит пользовательскому соглашению :) Так что вопрос очень такой… Сами пытались сделать такой планшет, но более простой — перехватывая во флешке интерфейса отрисовку мини-карты, но потом кануло в небытье из-за сильных ограничений среды.
                      +1
                      Якобы противоречит оно в одном-единственном пункте ПС, а именно п.4.4 о запрете декомпиляции и дизасма Проекта. Однако представить себе существующий огромный зоопарк модов без дизасма и декомпиляции довольно сложно (ведь официального модостроительного API нет), поэтому пункт дополнился пояснением о «за исключением модификации Контента с разрешения правообладателя». Короче говоря, что не запрещает администрация, то и разрешено.
                        0
                        Потому и пишу, что очень так… как минимум еще п. 5.3 и 4.4, в котором есть несколько частей.

                        Мы столкнулись с проблемами даже в более благих целях — например варгейминг не только не хочет дать возможность продавать премиум-товары за криптовалюты но и мешает появлению легальных посредников, которые бы взяли на себя эту роль :(
                          0
                          То что КВГ не хотят ввязываться в продажу цацок за крипту, это очень даже понятно, в свете вот этого, например, да и общих неясностей с правовым статусом крипты.

                          А насчёт посредников — идея интересная. Чисто теоретический вопрос — кто вам мешает стать «нелегальным» посредником? Принял битки, отправил голду через WM и хрен что докажешь.
                            0
                            Их никто не заставляет ввязываться. Но, к примеру, их конкуренты это допускают :) Тем более мы готовы были даже заранее выкупать и гарантировать свою деятельность финансовым обеспечением.

                            Понимаете — второй ваш способ дает бОльшие риски — в свете того, что хотели бы строить легальный бизнес, бренд и все такое. А это бы несло риски и для нас и для клиентов.

                            Ссылка не применима к этой ситуации, ибо ни мы ни варгейминг не являемся, как минимум, субъектами в правовом поле РФ.
                              0
                              ни мы ни варгейминг не являемся, как минимум, субъектами в правовом поле РФ

                              Мне не кажется, что можно рассчитывать на снисхождение к (и, тем паче, легализацию) крипты в более других странах, где даже на WM с принципиально отсутствующей анонимностью транзакций оказывается систематическое давление и наезды.
                                0
                                Поговорим, например, про Сингапур?
                                  0
                                  Вы с Украины, Wargaming.net LLP из Лондона, давайте про Сингапур. Только это уже оффтопик и лучше в личку.
                                    0
                                    Я про то, что создать компанию, если надо другая юрисдикция, это не проблема :)
                      0
                      Эх, уже потерли…
                        0
                        Там ничего особенного и не было. Без технических деталей, «видеопрезентация» предыдущей версии, для 0.8.10.
                        wotpad

                        Здесь ещё нет уже сейчас написанного нативного парсера Pickle, о котором я расскажу в следующей статье и нет записи пакетов.
                          0
                          Очень круто) Интересно, заставит ли ваша работа ВГ менять протокол или хотя бы усиливать шифрование
                            0
                            Я сомневаюсь. Слишком много придётся переделывать ради непонятно чего. Ибо сказано: «Работает — не трогай!»
                    +5
                    Хех, как мне это напоминает мой разбор протокола EVE Online :)
                    Ждем ботов! Удачи!
                      +3
                      Не напишите ли статью? — интересно было бы почитать. Или если уже — ссылку не дадите? А то чертовски интересно…
                        +6
                        Нет, увы, статьи нет. Давно это было, сейчас только исходники остались :)
                        Сейчас садиться и опять вспоминать как разбирал пакеты, пожалуй, уже и не возьмусь.
                        А о жизни эволюции ботов (копатели, файтеры, выполнители миссий), боюсь, что тут будет не очень интересно.
                          +4
                          Судя по количеству плюсов и комментов к топикам с новостями из Евы — многим будет интересно.
                        0
                        Боты есть и очень даже ничегошные. TankLeader самый продвинутый из них.
                        0
                        это же, чёрт побери, Python Pickle

                        Этого следовало ожидать, ибо там половина клиента с Python скриптами (достаточно поглядеть в любой продвинутый мод типа DM).
                        Думается мне, проще было пойти по этому пути (судя по всему swf-ки в GUI уже имеют доступ к распакованным данным из клиента), чем пытаться реверсить клиент
                          0
                          Да знаю я насколько сильно питон интегрирован в танки. «Чёрт побери» тут имеет, скорее, саркастический смысл.
                          В рамках «замены swf» идея «командирского планшета» не реализуется, да и необходимых скиллов для написания swf у меня нет.
                            +1
                            Про сарказм я понял. Как человек играющий в WoT, я вижу же скрипты в клиенте ( \res\scripts\common + \res\scripts\client ) — одни *.pyc файлы.
                            Да и наличие в папке с игрой стандартного набора из (libcurl, libeay32,libsndfile-1,ssleay32,zlib1, а также чудесного python.log), что скорее всего все это из под Python'a и пользуется.

                            Камера для спектатора, кстати вроде появилась, насколько я видел она реализована специальным «танком» с названием Камера
                              0
                              Но она все так же занимает слот в бою, поэтому их не может быть сколько угодно.
                          +1
                          github.com/Camelion/jts-tanks
                          уже давно разобрали и пилится эмулятор
                            0
                            Я буквально минуту назад разговаривал с автором того, на что вы выложили ссылку.
                            Ничего там не пилится, человек сделал эмуль только процесса авторизации и забил на всё остальное, с форматом обмена внутри игры он не разбирался и не собирается.
                              –9
                              Крипт есть, начальная логика есть — все остальное допилить не составляет проблемы.
                              Дамп трафика снимать — не велика наука и не думаю, что заслуживает статьи.
                                –1
                                offtop: где-то я уже видел вашу аватарку… ЗГ?
                                  –2
                                  Странно что именно на аватарку вы обратили внимание, а не на никнейм :)
                                    –2
                                    Нравится мне она просто уж оч. ))
                                      –1
                                      Вы про полную версию, надо думать? :)
                                        0
                                        Все то вы знаете :) Я ее не видел 2 года, пока у меня стоял этот аватар :D
                                        *Привет минусующим за бог весть что*
                                          +1
                                          Я не минусовал, но попробую пояснить. Дело в элементарном уважении к чужому труду. Вот в моей научной практике встречались два характерных явления:

                                          1) Некто, обсуждая какую-то научно-техническую проблему, заявляет, что это очень сложно, требуется такое-то финансирование (большое), такой-то срок (большой) и такой-то штат (тоже большой) для её решения. Когда же ему через 10 минут показывают в соседнем помещении решающий эту проблему лабораторный макет, кое-как слепленный из чего попало 3-4 аспирантами, этот же человек не моргнув глазом, заявляет «а, ну так это же элементарно и тут вообще делать нечего!».

                                          И обратный пример

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

                                          Это, конечно, не прямая аналогия; пожалуйста, не воспринимайте это буквально и лично. Но примеры характерные и вызывают у собеседников оправданные негативные эмоции. Я поэтому воздержался от комментариев, а кто-то другой, видимо, не воздержался.
                                            0
                                            Я объясню свою позицию. В реверсе\восстановлении\реализации всевозможных протоколов, крипталгоритмов и прочего имею довольно-таки богатый опыт и мне, с позволения сказать, несколько неясен смысл сей статьи в таком состоянии. Я бы на вашем месте сперва релизнул бы бета версию сей утилиты с минимальным функционалом и приурочил бы к ней выход некого обзора\how-to статьи.
                                            Сугубое ИМХО. Против вас лично ничего не имею, как и против минусующих кемрадов.
                                              0
                                              несколько неясен смысл сей статьи в таком состоянии

                                              Оценка интереса сообщества к этой тематике;
                                              Удовлетворение собственного тщеславия;
                                              Закрепление приоритета (слишком уж часто в последнее время я обнаруживал анонсы реализаций того, до чего кто-то додумался тоже)
                                              и т.п.
                            +1
                            Очень интересно, жду продолжения.
                            В свое время потратил некоторое время на попытки расшифровать водяные знаки в скриншотах из танков.
                              +1
                              Можете развернуть пункт?
                              набросал в нём специализированный win32-отладчик

                              Я например вообще понятия не имею как писать отладчики. Если я хочу вмешаться в процесс, я подсовываю ему свою ДЛЛку взамен какой-нибудь системной, или вроде того (DLL Proxy метод).

                              Вот ваш метод весьма интересен.
                                +4
                                Конкретно в этом пункте ничего особенного, всё было написано на чистом WinAPI строго по MSDNу. Я думаю, примеров минимальных отладчиков везде полно, нужен CreateProcess() с DEBUG_PROCESS, и WaitForDebugEvent() с ContinueDebugEvent() в цикле. На удивление простое и понятное API.

                                Но если вас интересует конкретная реализация (с брейкпоинтами и вытягиванием памяти) я могу поделиться исходником своего класса отладчика. Только он на FPC, если вас это не смутит.
                                  +1
                                  Мне главное понять принцип действия API, на каком он языке не столь важно :) Если не затруднит, я бы посмотрел.
                                +2
                                По поводу шифрования. Я увидел там RSA реализуемый библиотекой OpenSSL.

                                Вот тут идет вызов виртуальной функции, которая по факту есть rsa_public_encrypt
                                IDA Proc
                                image


                                * Что-то спойлер у меня получился саморазворачивающийся…

                                  +2
                                  Я писал эту статейку в день релиза 0.8.11, поэтому разбираться с конкретным механизмом шифрования не было времени, а в 0.8.10 однажды в процессе погони за функциями брейкпоинт на чтение из буфера сработал прямо в середине BF_ecb_encrypt(). И действительно, выбор BlowFish, как мне кажется, более оправдан — это наиболее быстрый блочный шифр, в то время как RSA — не быстрый и не блочный, и даже не потоковый. Его использование для игровых целей очень сомнительно, и, ИМХО, вызвало бы неслабый оверхед по вычислительной мощности на игровом кластере.
                                  Возможно, тот код, что вы привели, имеет отношение к авторизации, которая выполняется однократно за сессию.
                                    0
                                    Не исключено. Я занимался именно авторизацией.
                                  0
                                  Немножко не в тему, но не встречался ли кому либо снифер для RakNet протокола? Или описание самого протокола на уровне UDP пакетов?
                                    0
                                    Так он же опенсорс вроде, можно, как вариант, заглянуть в исходники да посмотреть как формируются пакеты.
                                    +5
                                    Извиняюсь за нескромный вопрос, но это не вы танк этот… того..? :)
                                      0
                                      Скажите, почему был выбран метод отладки как способ перехвата пакетов, а не например тот-же dll proxy/injection + сплайсинг, разве наличие int 3 не замедляет выполнение программы?
                                        +1
                                        Исключительно из соображений трудоёмкости реализации.
                                        Прокси-DLL это вообще какой-то костыль, который мне не нравится из эстетических соображений.
                                        Я не так давно в рамках выполнения другого проекта писал внедрение невидимой DLL в процесс, и помню, что даже если не париться с невидимостью, мороки всё равно намного больше, чем от написания простого отладчика.
                                        Нет, int3 не замедляет, я проверял. Даже с учётом всех танцев с откатом контекста и поиском именно того потока, который трапнулся на брейкпоинте, достаточно просто не заниматься в отладочном потоке ничем, кроме сброса вытащенных буферов памяти в очередь другого потока (который и занимается анализом — такая себе «двойная буферизация»), и тогда ни FPS ни пинг не проседает. Субъективно — по игре не заметно, идёт она под такой отладкой или нет.
                                          0
                                          Ну вогнать dll-ку в чужой процесс не так уж и сложно
                                          вот как вариант, тут, правда, не хватает пары проверок
                                          program Injector;
                                          
                                          uses
                                            shellapi,
                                            Windows;
                                          
                                          type
                                            TInject = packed record
                                              PushLoadLibraryCommand: BYTE;
                                              LoadLibraryArg: DWORD;
                                              CallLoadLibrary: word;
                                              CallLoadLibraryAddr: DWORD;
                                              PushExitThread: BYTE;
                                              ExitThreadArg: DWORD;
                                              CallExitThread: word;
                                              CallExitThreadAddr: DWORD;
                                              AddrLoadLibrary: pointer;
                                              AddrExitThread: pointer;
                                              LibraryName: array [0 .. MAX_PATH] of Char;
                                            end;
                                          
                                          {$R *.res}
                                          
                                            { Внедрение Dll в процесс }
                                          function InjectDll(Process: DWORD; ModulePath: PChar): boolean;
                                            function Offset(struct, field: pointer): cardinal;
                                            begin
                                              Result := cardinal(field) - cardinal(struct);
                                            end;
                                          
                                          var
                                            Memory: pointer;
                                            Code: DWORD;
                                            BytesWritten: DWORD;
                                            ThreadId: DWORD;
                                            hThread: DWORD;
                                            hKernel32: DWORD;
                                            Inject: TInject;
                                          begin
                                            Result := false;
                                            Memory := VirtualAllocEx(Process, nil, sizeof(Inject), MEM_TOP_DOWN or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                                            if Memory = nil then
                                              Exit;
                                          
                                            Code := DWORD(Memory);
                                            // инициализация внедряемого кода:
                                            FillChar(Inject, sizeof(Inject), 0);
                                          
                                            Inject.PushLoadLibraryCommand := $68;
                                            Inject.LoadLibraryArg := Code + Offset(@Inject, @Inject.LibraryName);
                                            Inject.CallLoadLibrary := $15FF;
                                            Inject.CallLoadLibraryAddr := Code + Offset(@Inject, @Inject.AddrLoadLibrary);
                                          
                                            Inject.PushExitThread := $68;
                                            Inject.ExitThreadArg := 0;
                                            Inject.CallExitThread := $15FF;
                                            Inject.CallExitThreadAddr := Code + Offset(@Inject, @Inject.AddrExitThread);
                                          
                                            hKernel32 := GetModuleHandle('kernel32.dll');
                                            Inject.AddrLoadLibrary := GetProcAddress(hKernel32, 'LoadLibraryW');
                                            Inject.AddrExitThread := GetProcAddress(hKernel32, 'ExitThread');
                                            lstrcpy(@Inject.LibraryName, ModulePath);
                                            // записать машинный код по зарезервированному адресу
                                            WriteProcessMemory(Process, Memory, @Inject, SIZE_T(sizeof(Inject)), SIZE_T(BytesWritten));
                                            // выполнить машинный код
                                            hThread := CreateRemoteThread(Process, nil, 0, Memory, nil, 0, ThreadId);
                                            if hThread = 0 then
                                              Exit;
                                            WaitForSingleObject(hThread, INFINITE);
                                            CloseHandle(hThread);
                                            VirtualFreeEx(Process, Memory, 0, MEM_RELEASE);
                                            // надо-надо умываться по утрам и вечерам
                                            Result := True;
                                          end;
                                          
                                          procedure StartHook;
                                          var
                                            ShellInfo: SHELLEXECUTEINFO;
                                          begin
                                            FillChar(ShellInfo, sizeof(ShellInfo), 0);
                                            ShellInfo.cbSize := sizeof(ShellInfo);
                                            ShellInfo.lpFile := PChar('hoockedexe.exe');
                                            ShellInfo.nShow := SW_NORMAL;
                                            ShellInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
                                            ShellExecuteEx(@ShellInfo);
                                            InjectDll(ShellInfo.hProcess, 'inject.dll');
                                          end;
                                          
                                          begin
                                            StartHook;
                                          
                                          end.
                                          
                                          А со сплайсингом помогает вот эта либа MinHook.
                                            0
                                            Ну вогнать dll-ку в чужой процесс не так уж и сложно

                                            Я в курсе. Делал инжектор невидимой DLLки (т.е. без файла на диске) для win32/win64.
                                            Но пока подход с дебагом работает, можно кодить основное направление.
                                              0
                                              Согласен, подход в данном случае не важен, а вот по поводу невидимой dll-ки не поделитесь информацией?, это ведь придётся вручную разворачивать все её структуры в памяти, довольно сложная работа.
                                                +1
                                                На самом деле ничего сложного там нет. Если предварительно снять образ уже загруженной обычным порядком DLL, то потом после внедрения этого образа в целевой процесс нужно только пройти таблицу relocations и расставить защиту памяти по атрибутам секций.
                                                Для первого есть чудесная функция LdrProcessRelocationBlock() в NTDLL, для второго — несколько раз дёрнуть VirtualProtect(). Всё вместе около сотни строк со всеми проверками.
                                                  0
                                                  LdrProcessRelocationBlock, ого, не знал о такой штуке, спасибо.
                                        0
                                        Интересно насколько законно реверс-инжинирить танки?
                                          0
                                          Вопрос сложный, об этом ветка комментариев выше. Как мне кажется по совокупности последних событий, КВГ не против этого проекта до тех пор, пока он не предназначен для продажи и пока он не светится на оф.форуме и не провоцирует настроения в духе «читы возможны а власти скрывают».
                                          0
                                          записи боёв в WOT шифруются BlowFish'ем, и что ключ шифрования не изменялся с того момента, как стал известен широкой публике

                                          Можно поподробнее на этом моменте? Или может где-то уже написано?
                                          Одно время хотел написать грамотный анализатор реплеев…
                                            0
                                            сейчас (с выходом 8.11) я вот не уверен, что есть надобность — в открытых заголовках там сейчас лежит куча инфы, раза в два больше, чем раньше (знаю так как написали и используем тот самый продвинутый анализатор реплеев, хоть и для специфических нужд, так что достаточно только открытой в нем инфы)
                                              0
                                              В 8.11 добавили новую открытую инфу? То что написали открыто или..?
                                                0
                                                Посмотрите внутрь файла реплеев от 0.8.11 — там открытым текстом лежит JSON со статистикой боя (той, что показывается в клиенте после боя). В шифрованном виде только хронология событий.
                                                  0
                                                  Ну так открытая часть она всегда была, интересуют именно события
                                                    –1
                                                    Все пакеты, проигрываемые клиентом с реплея имеют json формат, покрытый Pickle. Для нормального разбора реплея нужно знать структуру исходного пакета.
                                                      0
                                                      А про какой blowfish с известным ключем тогда идет речь в статье?
                                                        0
                                                        А это уважаемый AN3O «забыл» сказать, что эти самые Pickle в реплеях таки шифруются именно blowfish'ем. Да вы всё-таки посмотрите внутрь реплея, там видно.
                                              0
                                              Я имел в виду вот это, и хотя это для 0.7.3, по слухам, ключ с тех пор не менялся.
                                              0
                                              Приветствую,

                                              А по ватсиму вы свои разработки куда-нибудь выкладывали?
                                                0
                                                отписал в лс.
                                                –2
                                                Автор, не верным путём вы идёте.
                                                Получать игровые данные надо чисто, иначе бан не заставит себя ждать. Ваше вмешательство должно быть невычислимо. Единственный способ этого достичь (и конкретно для вашей задачи — получить данные не вмешиваясь в игру — он подходит идеально) — снифер.
                                                Посмотрите в сторону библиотеки pcap. В итоге вы будете разбирать трафик игры параллельно с клиентом, никак не вмешиваясь в его работу.
                                                Для этого метода придётся приложить чуть больше усилий — потребуется разобраться как осуществляется шифрование трафика и обмен ключами. Зато вам не надо будет при каждом обновлении игры искать куда переехали адреса функций, в которые вы ставите брекпоинты.
                                                  +1
                                                  Во-первых, при грамотно сделанном шифровании с обменом ключей сниффер не поможет в принципе.
                                                  А решать проблемы буду по мере их поступления. Кстати, по опыту — NMAPI более вменяемый, чем pcap.
                                                    0
                                                    Я пока не сталкивался с проектами в которых было бы грамотно сделано шифрование. Хорошие надёжные шифры требуют много ресурсов.
                                                    Фогейм когда только запустил сервера Ла2 проводил эксперименты с шифрованием. Включили на пару дней. Сервера стали лагать адски. Шифрование выключили и не включали уже до фроста. Чего в фросте в итоге сделали — не знаю.
                                                    Вам стоит разобраться как игра формирует ключи. Как минимум из академического интереса :)
                                                      0
                                                      Надёжность шифра и количество ресурсов тут совершенно ни при чём. Вот простая схема, которую можно за пару часов реализовать на штатном crypto winapi: алиса и боб каждый у себя генерят пару публичный-приватный ключ RSA; отправляют каждый другому свой публичный ключ; шифруют не своим ключом новый сгенеренный ключ, допустим, от RC5 или того же блоуфиша; передают его на ту сторону, там его расшифровывают приватным. Всё, теперь в обе стороны может ехать поток шифротекста, закрытый двумя независимыми ключами RC5/BF, которые любой MITM принципиально не сможет узнать за время актуальности данных даже при минимальном размере ключа RSA. Можно даже обойтись и одной стороной — пусть даже только один боб передаёт алисе свой потоковый ключ закрытый парой алисовых rsa. И всё тоже самое, только проще в два раза.
                                                      У меня такое делают студенты на лабораторках, я ж думаю, товарищи из КВГ как минимум не глупее. И даже если так не сделано, сделать это легко и просто и на нагрузке игровых серверов не отразится вообще никак, потому что сессию стартует сервер авторизации, а не игровой, а его нагрузка — около десятка юзеров в секунду.
                                                        0
                                                        Теоретически да. Но если рассуждать в таком ключе — отрезать отладчик так же легко как снифер. Смотреть надо как сделано.
                                                        Я считаю что по возможности надо использовать методы не трогающие процесс игры. А внедрение в код, это уже крайние меры.
                                                          0
                                                          В любом случае в клиенте будет место, где этот «шифрованный» трафик будет декриптиться — что мешает сплайснуть эту функцию и слать\получать трафик например через свой локальный SOCKS5 прокси, где вы при желании спокойно будете видеть его (трафик) кристально чистым?

                                                          Учитывая, что клиенты КВГ вообще ничем и никак не защищены, для экспериментов там поле непаханое.
                                                            +1
                                                            Ничего не мешает — именно так всё и сделано, за исключением того, что вместо сплайса применяется гораздо более простой в реализации дебаг.
                                                          0
                                                          Не в шифровании было дело а в убогости модуля обнаружения потенциально нежелательного ПО, проблему которого в данный момент решили введением WhiteList'ов, постоянно собираемых с пользователей.
                                                            0
                                                            Shildconsole запущенная на ОБТ. Отработала пару недель. Потом прошло обновление после которого стало всё дико лагать. Через пару дней очередное обновление и все проблемы как рукой сняло.
                                                            На тематических форумах обсуждалось. Проводился эксперимент с дополнительным шифрованием трафика. Неудачный. Остальные системы Shieldconsole работали всё время. И только после ОБТ, если мне не изменяет память, появился фрост. Какие проблемы были у фроста и как решались я не знаю.
                                                      +1
                                                      — (Как относится WG к расшифровке на Хабре протокола игры?)
                                                      — Посмеялись, спасибо.
                                                      © Storm (Wargaming)
                                                        0
                                                        Было бы там что расшифровывать.
                                                        0
                                                        Даже не сказали какие порты используются.

                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.