Карты, деньги, компилятор. Пишем крутой бот для игры в интернет-покерНиколай Байбородин (baiborodin@gmail.com) Aaloha, брат! Позади чумная череда новогодних праздников. Ты, как и я, наверняка, находишься на мели. В такое голодное время любая денежная идея ценится дороже золота. В плане денежных идей в условиях кризиса наш журнал традиционно находится на гребне волны, поэтому встречай крутое решение - online-геймблинг. Вся наша жизнь - играЧто такое? Ты противник азартных игр и это не для тебя? Понимаю, я тоже считаю людей, подсевших на гамблинг, гм… мягко говоря, неудачниками. Но когда, совсем недавно, я случайно познакомился с покером, – то с удивлением узнал, что между покером и той же рулеткой пролегает огромная пропасть. Если в рулетке или в BlackJack от тебя ничего не зависит, то в покере победа или поражение зависят в первую очередь от твоих же действий. Покер просчитывается! И это, в отличие от BlackJack, разрешено и поощряется. Можно сказать, что покер – игра статистики и терпения. Более того, во многих странах покер – официально признанный вид спорта. Вот еще одна приятная новость: в списке таких стран значится и Россия, в которой официально зарегистрирована Федерация Спортивного Покера.
Игра настолько популярна, что в нее вовлечены сотни миллионов людей по всей планете. Регулярно проводятся чемпионаты по покеру с призовым фондом, зашкаливающим за десятки миллионов долларов. Ну да ладно, довольно лирики, ближе к телу. Будучи такой популярной игрой, покер широко представлен и в Сети. Начиная от сайтов и форумов, посвященных обсуждениям игровой стратегии, и заканчивая Online покер-румами, в которых можно поиграть в покер на условные или настоящие деньги с соперниками со всего света. Все, что для этого нужно – зайти на сайт одного из покер-румов, скачать оттуда клиентское ПО, установить, открыть свою учетную запись, закинуть туда немного лавандоса и наслаждаться игрой. Кстати, все крупнейшие покер-румы работают с нашими веб-манями (проверено на собственном опыте). Придерживаясь одной из выбранных стратегий и начав играть на низких лимитах (PokerStars – минимальная ставка 1 цент), можно оставаться в стабильном плюсе, медленно, но верно увеличивая свой банк. Как такое может быть, если игра просчитывается и все стратегии уже давно известны? Все просто, мой юный Че! Больше половины посетителей покер-румов - это буржуи, цель которых убить время и развлечься. Они пришли не за выигрышем. Они понятия не имеют ни о каких стратегиях, ограничиваясь только знанием базовых правил. В покерной терминологии они – «рыба». Русские, скорее, назвали бы такого игрока коровой, потому что его можно доить на предмет лавандосов. В общем, большинство таких игроков просто ищут, кому бы отдать свои денежки. Почему бы тебе не стать тем добрым самаритянином, который поможет им расстаться с грузом лишней зелени? Согласись, это уже само по себе хорошая новость. Но это не конец истории, а только самое ее начало. Немного поиграв, однажды ты обнаружишь, что занятие это требует терпения и усидчивости. К тому же, не такое уж оно и прибыльное - два-три президента в день, это хорошо, но на серьезный заработок не тянет. Причина в том, что игра с минимальным риском предполагает, что ты будешь сбрасывать не менее 90% своих карманных карт. Многие решают эту проблему одновременной игрой на нескольких столах. Но в этом случае весь процесс превращается из легкого развлечения в тяжелый труд, где нужно уметь быстро анализировать ситуацию и принимать единственно верные решения. Не всякому человеку под силу! Стоп! А кто сказал, что играть обязательно должен человек? Если игра просчитывается, если есть готовые стратегии, – что мешает перевести их в нули и единицы и заставить компьютер играть в покер за тебя? Вот та самая мысль, которая и послужила основой для написания статьи. Первоначальный замысел был такой – разобрать протокол общения клиента с игровым сервером и написать собственного клиента, играющего на нескольких столах одновременно, без участия человека. Не знаю, как у тебя, а у меня первые мысли – обычно самые бредовые. Так было и сейчас. Как и следовало ожидать, оказалось, что все общение клиента с сервером происходит внутри SSH-сессии. План (не тот, другой)Затем в моей голове всплыли рассуждения Криса Касперски, что как бы ни наворачивали защиту своего клиента разработчики WebMoney, система все равно остается уязвимой, ведь можно элементарно эмулировать взаимодействие пользователя с графическим интерфейсом. Чем не вариант? Будем пробовать.
Идея такая – отслеживать запуск одного из клиентов online покер-румов, по хендлу инклудить в свою прогу контрол, в котором в режиме реального времени прокручивается лог игры, парсить этот лог, передавать полученные данные модулю, реализующему принятие решения, и, в итоге, эмулировать действия пользователя по нажатию соответствующих батонов! Сразу скажу, статья не резиновая, поэтому не жди детальной пошаговой инструкции. Мы рассмотрим наиболее сложную часть, реализующую автоматизацию. Ну а перевести одну из приглянувшихся тебе стратегий в логический модуль программы ты сможешь и без моей помощи. Неуправляемое управлениеВ качестве теоретического минимума будем осваивать технику DLL-инъекции. А поскольку .Net безгранично царствует на просторах Windows, сделаем это с оглядкой на управляемый код в .Net сборках. Допустим, у тебя есть некий управляемый код, который ты хотел бы поместить в процесс–жертву. Для большей интриги – пусть процесс-жертва будет организован в виде нативного неуправляемого кода. Оформим его в виде обычной библиотеки классов .Net. Для тех, кто обделен фантазией, ниже приведен архисложный пример:
В приведенном примере содержится всего один метод, принимающий строковый параметр и возвращающий целочисленное значение. Метод играет роль точки входа в управляемый код – вызвав его из нативного кода, мы как по волшебству перенесемся в царство .Net. Итак, у нас есть процесс – жертва и наш, готовый к внедрению, код. Следующий шаг – написать загрузчик библиотеки. Внимание! Мы не будем ломиться вперед сломя голову и делать инъекцию управляемого кода в неуправляемый. Вместо этого мы выполним инъекцию нативной DLL'ки, которая в свою очередь способна взаимодействовать со средой .Net и загружать наш управляемый код. Дело за малым - побряцать немного по клаве и написать простенькую DLL-ку:
В основе нашего загрузчика лежит последовательный вызов определенных методов CLR API.
Именно последний из перечисленных методов и выполняет основную работу, ради которой все, собственно говоря, и затеивалось. Думаю, с этим полезным методом тебе уже не раз приходилось иметь дело. Но на всякий случай, для тех, кто впервые взял в руки наш журнал, я напомню, что ExecuteInDefaultAppDomain принимает обязательный строковый параметр, а в качестве своего значения возвращает число типа integer. Гы, а ты думал, что я от нефиг делать в первом примере использовал метод со строковым параметром и целочисленным возвращаемым результатом? Вызов метода ExecuteInDefaultAppDomain будет успешно работать в большинстве приложений. Но есть одно исключение, которое тебе обязательно нужно знать. Дело в том, что если целевой процесс также является частью .Net-приложения, придется воспользоваться альтернативными методами, которые мы не будем здесь рассматривать в силу ограниченности журнального пространства. Хочешь подробностей – кури MSDN. Давай подведем промежуточные итоги. Что мы имеем на текущий момент? У нас есть программный код, выполняющий основную работу, есть DLL, осуществляющая его инициализацию. Осталось выполнить инъекцию этой самой библиотеки в процесс клиента выбранного заранее покер-рума: Сестра, шприц!Займемся DLL-инъекцией, которая поможет нам порулить клиентом покер-рума. «Почему именно техника DLL Injection?», – спросишь ты. Все просто. Софт для покер-румов создают не пионеры, а серьезные дядьки. Поэтому любая модификация клиента моментально палится сервером, и твоя учетная запись будет забанена без малейшей перспективы получить обратно свое бабло. А если нельзя сделать легкий тюнинг клиента заранее, стало быть, будем тюнинговаться на лету. Для этой цели DLL Injection – то, что доктор прописал. Технике DLL Injection не один год. Она широко известна в наших узких кругах и неплохо документирована на многих специализированных веб-ресурсах (несколько интересных ссылок ты найдешь на полях). Поэтому рассмотрим лишь самую суть, имеющую отношение к созданию бота для игры в покер. Вообще, DLL Injection – достаточно обширное понятие, под которым подразумевается несколько родственных технологий. Самая, пожалуй, популярная – внедрение в адресное пространство чужого процесса – неоднократно описывалась на страницах нашего журнала. Правда, это больше относится к соседней рубрике «Взлом», мы же люди мирные, нам бы бабла поднять. Поэтому будем осваивать менее воинственные техники – Windows Hook и CBT Hook. Покурив MSDN на тему Windows Hook, ты обнаружишь, что в основе техники лежит метод SetWindowHookEx из Windows API. Ниже – демонстрация того, как мы можем использовать это для своих грязных целей:
С использованием глобальных CBT-хуков есть одна досадная проблема (это справедливо и для других глобальных хуков – на то он и глобальный, чтобы никому сладко не было). DLL в этом случае будет загружена в адресное пространство ВСЕХ процессов. И если эта библиотека весит более чем «Hello, World!» (а так оно и будет после того, как ты напишешь логику своего бота), приличные тормоза системе обеспечены. Поэтому мы с тобой поступим следующим образом: напишем легкую библиотечку, которая будет выполнять только одно действие – проверять, в адресном пространстве какого процесса она находится. И если этот процесс принадлежит клиенту online покер-рума, то – подгружать основную библиотеку, зарабатывающую бабло. Закатаем рукаваДовольно лирики, пора браться за дело. Начинаем с DLL Injection:
Следующий шаг – создание механизма обнаружения открытия и закрытия интересующих нас окон клиента покер-рума:
И – пара комментариев. В момент активации главного окна приложения посылается сообщение HCBT_ACTIVATE. Соответственно, в момент закрытия окна посылается сообщение HCBT_DESTROYWND. Остальной код достаточно прост для понимания. Он просто-напросто получает контроль над окном или освобождает его от своей опеки. Да, чуть не забыл. Мы не можем использовать стандартное сообщение HCBT_CREATEWND для ответа на вопрос о том, создано ли уже окно клиента. Поскольку к процессу клиента мы еще не подключились, то не можем и захватить управление окном. А значит, HCBT_CREATEWND здесь не подходит. Вместо этого мы воспользуемся сообщением HCBT_ACTIVATE для поиска активного окна. Напомню, что исходные данные для анализа мы будем получать, парся текст из окна чата игроков текущего стола. Чтобы получить текст и передать впоследствии парсеру, отловим сообщение EM_STREAMING, заменив callback функцию клиента своей собственной. Чтобы не загружать пример кода лишней ботвой, перехват сообщений начинается в момент инициализации бота. На практике тебе придется внести некоторые коррективы. Дело в том, что если открыто окно стола с игрой, это еще не значит, что ты уже в игре. Ты можешь просто наблюдать за игрой или, только что вступив в игру, дожидаться, когда до тебя дойдет очередь ставить большой блайнд. Поэтому бот должен включиться в игру в тот момент, когда ты садишься за стол. Или еще проще – после нажатия кнопки «Бабло», управляющей активностью бота. Концепт данного модуля будет таким:
Теперь, док, давай научимся взаимодействовать с окном чата. Здесь конкретные действия будут зависеть от того, на основе какого элемента управления оно реализовано. В частности, клиент для такого покер-рума, как PokerTime, использует для этого компонент RichEdit. Для отображения текста в окне чата используется стандартное сообщение EM_STREAMIN, которое мы и будем перехватывать:
Если тебе уже приходилось раньше сталкиваться с EM_STRAEMING, то ты должен помнить, что вместе с ней необходимо использовать callback-функцию EDITSTREAMCALLBACK. Нам нужно заменить исходную callback-функцию (А) своей собственной (В). Ну а мы уже передадим управление функции А после того, как сделаем свои темные делишки, чтобы клиент не потерял связи с реальностью и не выпал в глубокую нирвану :).
Финальный аккордОстались сущие пустяки – разобрать полученный текст, вытащив из него информацию, необходимую для принятия решений. Ключевые строки, на которые будем ориентироваться, это:
Регулярные выражения – твой лучший друг:
Обработаем полученные данные и симулируем действия пользователя. В первом нет ничего сложного, второе – тоже из серии «для чайников», поэтому отдельно останавливаться на этих моментах мы не будем. Если вдруг возникнут трудности – грызи исходники, которые есть на диске. Ахтунг!Так, чтобы все гладко и без единого сучка, бывает только в кино. В реальной жизни за каждым углом нас поджидает лохматый писец. Не хочу капать на мозг (он и так изрядно загажен нытьем о всемирном экономическом кризисе, раздающимся из каждой подворотни), но ты это должен знать. Иначе было бы просто не честно. Отсюда – несколько маленьких финальных советов:
Удачи в игре, гринго! Покерные комбинации составляются из пяти карт (при этом у игрока на руках может быть от двух до семи карт, в зависимости от типа игры), дающих игроку сильнейшую из возможных в данной ситуации покерных комбинаций. Общим для всех разновидностей клубного покера считается правило, по которому банк забирает игрок, имеющий лучшую покерную комбинацию на момент завершения сдачи и вскрытия карт. Ранжир комбинаций – общий для всех разновидностей покера (кроме специально оговоренных случаев) и определяется вероятностью собрать ту или иную комбинацию. CDСледуя незыблемой традиции, на диске ты найдешь исходный код простого бота, который ты можешь взять за основу своего денежного пылесоса. WARNINGМы настоятельно рекомендуем тебе не использовать полученные из статьи знания на практике. В противном случае ни автор, ни редакция не несут ответственности за твои действия. Будь умницей :). WWW
|