» » » Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

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

Про онлайн-игры

Для начала предлагаю разобраться, на какие же категории можно разделить все онлайн-игры с точки зрения их уровня безопасности. Первый тип – это платформозависимые игры с собственным движком, который обычно разрабатывается с нуля. Это такие игры, как World of Warcraft, Lineage, Warhammer и другие. Как правило, вся информация от клиента к серверу передается по собственному протоколу игры, и степень ее защищенности определяется только фантазией разработчиков.

Второй тип – это кроссплатформенные игры с собственным движком и протоколом обмена данными. Это всевозможные бойцовские клубы, игры типа TimeZero и прочие. В отличие от игр первого типа, они обычно выполнены на базе Flash и Java. Их плюс – в кроссплатформенности, а небольшой минус – в безопасности. Конечно, с безопасностью все не критично, но явно хуже, чем у первого типа игр, в силу ограниченности возможностей используемых технологий, особенно Flash. Тем не менее, у игр третьего типа маневров для поддержания достаточно хорошего уровня безопасности еще меньше.

Третий тип – это подмножество второго типа, то есть браузерные онлайн-игры, построенные на схожем протоколе обмена данными, API либо движке. К этому типу относятся игры внутри таких небезызвестных сервисов, как Mail.ru, Yahoo и ВКонтакте. Последнему и посвятим наше внимание.

Приложения ВКонтакте завоевали популярность в массах практически сразу же после своего появления. Фермы, покеры и рисовалки на стенах в кратчайшие сроки набрали сотни тысяч пользователей в свои ряды, а их создатели получили неплохую финансовую подпитку за счет любителей поднять свой рейтинг (и, как следствие, виртуальный общественный статус) путем оплаты тех или иных услуг со счета ВКонтакте. Как будет показано далее, с помощью несложных манипуляций в некоторых приложениях можно добиться желаемого результата, не тратя много времени, а всего лишь написав автоматизатор (бот), который будет выполнять всю работу за тебя. Так я и сделал в один прекрасный день.

Идея!

С чего же все началось? А с того, что я, будучи любителем английского языка, нашел ВКонтакте приложение LinguaMania. Это тренажер для изучения языков. Чтобы обучение проходило веселее, разработчики реализовали систему поощрений в виде букв, из которых можно собирать слова, а затем "покупать" за них одежду и одевать свою виртуальную аватарку. Ну и, само собой, рейтинг. А вот о нем подробнее. Рейтинг дается не только за обучение по виртуальным "учебникам", но и за игру в викторину, которая сразу меня заинтересовала.

Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

Так выглядит игровой процесс

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

Первым делом я полез смотреть трафик приложения: что куда передается, что приходит обратно. Для анализа HTTP-трафика я еще не встречал инструмента лучше, чем приложение HTTPWatch для Internet Explorer'а. Итак, я запустил игру, нажал кнопку "Record" в аддоне для записи логов… и вот тут мне, как говорится, и "пришли 2 туза на префлопе". Я увидел, что приложение передает все свои данные как есть, в открытую.

Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

Трафик "глазами" HttpWatch

Изучив трафик я обнаружил, что переменная state принимает следующие значения: PHASE1, PHASE2, PHASE3, GUESS, LOSE. Это соответствует стадиям игры в каждом раунде: все буквы скрыты, одна буква открыта, две буквы открыты, слово угадано, слово не угадано. Как видишь, в открытую передается question_id, что, как несложно догадаться, есть номер вопроса. В случае первых трех фаз в поле "question_mask" передается маска слова. Например, если state=PHASE3, первая буква C, вторая буква A, то маска слова - ??С?A. Если текущая фаза – GUESS или LOSE, то слово будет отображено на экране целиком, чтобы показать пользователю ответ. Соответственно, в переменной question_mask это также отобразится: там будет слово полностью, без знаков вопроса.

Смотри внимательно. В конце каждого раунда мы получаем номер вопроса и ответ на него. Именно это открытие и навело меня на идею автоматизировать процесс. Ведь каждый раз, когда мы получаем слово, мы заносим его в нашу базу данных слов. Если же такой question_id там уже есть, то мы берем слово и вписываем его в окошко программы, за что получаем очки. Само собой, что проверку на наличие слова в базе нужно делать в тот момент, когда state будет PHASE1, PHASE2 или PHASE3. Таким образом, наш бот будет самообучающимся: нет слова – запоминаем, есть слово – отвечаем на вопрос.

Кодинг

Как же это все реализовать на практике? Для начала нужно научиться самим перехватывать трафик. Рассмотрим доступные нам варианты. Их будет три.

Первый вариант – это перехват пакетов сетевого интерфейса. Достаточно низкоуровневый способ, требующий навыков взаимодействия с внешними библиотеками, сетевыми интерфейсами и, в целом, умения работать с операционной системой на низком уровне. Тебе пригодится библиотека WinPCap, которая позволяет получать пакеты в чистом виде прямо с сетевого интерфейса. В остальном – Гугл в помощь.

Второй путь – это работа с Internet Explorer. Этот способ, пожалуй, самый высокоуровневый, так как мы получаем уже готовый, обработанный трафик. Для перехвата трафика таким образом необходимо владеть COM-программированием: уметь подключаться к COM-объектам (каковым является компонент IWebBrowser2 внутри окна Internet Explorer’а), реализовывать интерфейсы и работать с ними. Способ требует серьезных знаний в вышеуказанной области, поэтому я решил делать "как дед учил", третьим способом. А это у нас реализация прокси-сервера. Точнее говоря, Socks 4/5 сервера. На этом способе заострим наше внимание. Заострим немного, так как статья все же не о создании Socks-сервера, поэтому нагружать ее кодом не буду.

Начать написание Socks-сервера стоит с изучения вот этого мануала. Для ленивых и англонемых будут мои пояснения на русском. Но сперва реализуем серверное приложение. Есть несколько способов сделать приложение-сервер, начиная со старого…

SOCKET mysocket

sockaddr_in local_addr;

local_addr.sin_family = AF_INET;

local_addr.sin_port = htons(MY_PORT);

local_addr.sin_addr.s_addr = 0;



mysocket = socket(AF_INET, SOCK_STREAM, 0);

bind(mysocket, (sockaddr *)&local_addr, sizeof(local_addr));

listen(mysocket, 1080);



SOCKET client_socket;

sockaddr_in client_addr;

int client_addr_size = sizeof(client_addr);



while ((client_socket = accept(mysocket, (sockaddr *)&client_addr, &client_addr_size)))

{

DWORD thID;

CreateThread(NULL, NULL, ClientThread, &client_socket, NULL, &thID);

}


...и заканчивая вполне современным способом реализации серверов под Windows, каковым является работа с сетевыми сообщениями при помощи функции WSAAsyncSelect(). Рамки статьи не позволяют детально описать процесс создания Socks-сервера, поэтому я ограничусь лишь пояснением структуры программы и основных методов, которые я использовал.

Начать написание такого приложения стоит примерно c такого кода:

SOCKET server_socket;

WSADATA wsaData;

int server_port = 3128;

int queue_size = 5;

struct sockaddr_in server_address;

#define SERVER_ACCEPT WM_USER + 1

#define CLIENT_EVENT WM_USER + 2

#define TARGET_EVENT WM_USER + 3

#define SOCKET_OPENED WM_USER + 4

#define SOCKET_CLOSED WM_USER + 5

int ServerStart(HWND hWnd)

{

int rc;

WSACleanup();

WSAStartup(0x0101, &wsaData);

server_socket = socket(AF_INET, SOCK_STREAM, 0);



server_address.sin_family = AF_INET;

server_address.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

server_address.sin_port = htons(server_port);



bind(server_socket, (LPSOCKADDR)&server_address, sizeof(server_address));

listen(server_socket, queue_size);

/* !!! */ rc = WSAAsyncSelect(server_socket, hWnd, SERVER_ACCEPT, FD_ACCEPT);

return 0;

}


Чем же этот код отличается от первого? А тем, что в данном случае нам не нужно возиться с многопоточностью для обработки каждого подключения к серверу. Благодаря выделенной строчке мы будем в нашем окне hWnd получать сообщения SERVER_ACCEPT при каждом новом подключении клиента.

Теперь рассмотрим момент подключения. В процедуре обработки сообщений окна мы ловим сообщение SERVER_ACCEPT и работаем с ним. Как только клиент подключился, вызывается функция accept(), и далее регистрируется событие CLIENT_EVENT, которое будет приходить главному окну нашей программы, как только в сокет поступят данные либо сообщение о его закрытии:

client_socket = accept(server_socket, (LPSOCKADDR)&socket_record->client_address, 
&len);

rc = WSAAsyncSelect(socket_record->client_socket, hWnd, CLIENT_EVENT, FD_READ |
FD_CLOSE);


Вернемся опять в нашу оконную процедуру. В ней мы будем обрабатывать вышеозначенное событие CLIENT_EVENT. Значение lParam в данном случае будет либо FD_READ (если событие было вызвано как сигнал о поступлении данных в сокет), либо FD_CLOSE (если сокет был закрыт).

if(WSAGETSELECTEVENT(lParam) == FD_READ) 

{

c = recv(socket_record->client_socket, &bf[0], 1,0);

...

}

if(WSAGETSELECTEVENT(lParam) == FD_CLOSE)

{

...

}


Таким образом, мы уже научились принимать подключения и читать данные с сокета. Но вернемся к реализации Socks-протокола, упоминавшейся выше. Здесь я покажу, как работает Socks5, хотя в моей программе реализована и 4-ая версия. Вот что говорит нам RFC.

После подключения клиент посылает Socks-серверу два байта: номер версии (4 или 5) и количество методов аутентификации: N. После этого нам придет еще N байт с номерами этих самых методов. Обычно приходят 3 целых числа: 05h, 01h и 00h. То есть пятая версия и первый метод, значение которого 00h, означающий, что клиент желает работать без аутентификации. В ответ мы должны вернуть два байта. Первый – это номер версии, то есть 05h, второй – значение выбранного нами (сервером) метода аутентификации из того списка, что нам предложил клиент. Так как клиент обычно предлагает только 00f, то именно это мы обратно и вернем. Скушав наш ответ, клиент пришлет запрос следующего формата: первый байт – номер версии, второй – команда, третий – резервный байт, четвертый – тип адреса. После чего будет идти группа байт непосредственно с самим адресом хоста-цели, а завершат пакет два байта с номером порта хоста-цели. Формат группы байт с адресом хоста-цели будет зависеть от того, какой тип адреса (четвертый байт) задается. Мы будем работать с привычным IPv4, что соответствует значению 01h для четвертого байта запроса. В этом случае группа байт будет размером 4, то есть стандартного формата IP-адреса из четырех целых чисел. Номер порта хоста-цели вычисляется как первый байт * 256 + второй байт.

Итак, у нас есть запрос от клиента, который нужно выполнить. Подключаемся к хосту-цели по данному нам адресу и порту, ждем данных. Ждать данных будем как из сокета нашего клиента, так и из только что созданного сокета для хоста-цели, с которым клиент желает посредством нашего Socks-сервера пообщаться. Все, что придет от клиента, посылаем хосту-цели. Все, что от хоста-цели, уходит клиенту. Так и работает Socks-сервер.

В моей программе реализована структура, содержащая данные обо всех подключениях, созданных нами, чтобы при принятии сообщения FD_READ мы знали, какой сокет что и куда посылает, клиент ли это или хост-цель, и в каком статусе находится процесс общения с ним. Рамки статьи не позволяют детально описать эту структуру, так что смотри исходный код.

Итак, мы научились перенаправлять трафик и, следовательно, слушать его. Теперь, думаю, тебе не составит труда написать парсинг необходимых данных. Обрабатывая данные мы, как я уже обозначил ранее, смотрим, есть ли в нашей базе данных записи для искомого id. Если нет, то в момент наступления фазы LOSE или GUESS мы заносим это слово в базу данных. Если же запись имеется, то настало время сообщить игре правильный ответ. Это можно сделать двумя способами.

Первый "очень высокоуровневый" и одновременно сложный. Заключается он в том, что мы подключаемся к COM-объекту IWebBrowser2 внутри Internet Explorer’a, в котором у нас загружена игра. Получаем интерфейс Flash-ролика и уже непосредственно с ним работаем через Flash API, что позволит нам вбить слово прямо в текстовое поле и нажать кнопку ввода. Это очень круто. И очень сложно :).

А что если ты не пользуешься Internet Explorer’ом? Да и реализовывать все вышеописанное не хватает сил и желания? Есть старый добрый метод эмуляции событий мышки и клавиатуры. Заключается он в использовании системной функции SendInput. Это модернизированная версия keydb_event()/mouse_event(), которая рекомендована производителем (Microsoft). Вот пример нажатия клавиши F5:

INPUT pInput;

pInput.type = INPUT_KEYBOARD;

pInput.ki.wVk = VK_F5;

pInput.ki.time = 0;

pInput.ki.wScan = 0;

pInput.ki.dwFlags = KEYEVENTF_EXTENDEDKEY;

SendInput(1, &pInput, sizeof(pInput));


Ура! Теперь мы умеем слушать трафик через наш Socks-сервер, самообучаться, расширяя словарь при каждом новом слове, и отвечать на вопросы, которые нам уже встречались. Цель достигнута!

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

Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

Вот что получилось в итоге

И это все?

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

-Лингвамания;
-Викторина;
-Угадай мелодию;
-Фотовикторина;

А также некоторые другие.

Поверхностный поиск среди приложений ВКонтакте дал еще несколько плодов. Первый из них – это приложение "Пузыри".

Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

Игра "Пузыри"

Если ты хороший алгоритмист и можешь написать достойную программу для набора максимального количества очков на заданном поле, то для тебя есть отличная новость. Расклад "поляны" перед вашими глазами:

Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

Вот что увидел Нео в Матрице

Любителям судоку ВКонтакте есть свое местечко.

Взлом игр ВКонтакте: исследование приложений ВКонтакте под микроскопом

Такой вот расклад

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

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

Подводя итоги

В завершение поразмыслим о том, кто виноват и что делать. Виновными признаются лень и жажда наживы разработчиков приложений, а также эгоизм тех, кто пользуется этими приложениями, сидя ночами напролет и отращивая "виртуальный половой орган". В ином случае эти приложения просто не разрабатывались бы для широкой публики. Из путей решения проблемы, какие вижу лично я, предложу следующие.

Разработчикам. Внимательнее изучать вопрос безопасности использования приложений. Внедрение простого "шифрования" сдвигом на пару байт уже должно отбить у значительной доли "взломщиков" желание копаться в трафике, не говоря уже о более совершенных методах защиты данных, которые, в зависимости от твоей фантазии, в той или иной степени обезопасят приложение от неадекватных действий пользователей.

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

Тем, кто все еще играет. Перестань. Это всего лишь точки на экране. Во многих играх есть вполне реальная возможность автоматизации процесса игры и все твои многочасовые, а в редких клинических случаях и многодневные, усилия будут смотреться скудно по сравнению с результатами работы компьютерной программы. Программа не хочет ни спать, ни есть. Она не устает и не сдается. Не нужно соревноваться с программой. Живи реальной жизнью, а не жизнью "эльфа 80-го уровня". Испытав все это в свое время на личном опыте, я бросил этим заниматься. И вот, осознав, что знанием стоит поделиться с другими, я открыл текстовый редактор и написал первые строки этой статьи.

Яблоков Станислав

Другие публикации по теме:

Комментарии