книги хакеры / журнал хакер / 121_Optimized
.pdfT.DLLXCh |
|
ge E |
|
|
|
INJECT.DLL |
INJECT.DLL |
|||||||
|
- |
|
an |
|
|
|
d |
|
|
|
|
|||
|
F |
|
|
|
|
|
|
|
t |
|
|
|
|
|
|
D |
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
|
r |
|
|
|
||
P |
|
|
|
|
|
|
NOW! |
o |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|
|
|
||||
|
|
|
|
to |
|
|
|
|
|
|
|
|
||
w Click |
|
|
|
|
|
|
m |
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
o |
|
|
|
|
|
. |
|
|
|
|
|
|
.c |
|
P |
.DLL |
|
||
|
|
p |
|
|
|
|
|
g |
|
|
|
|||
|
|
|
df |
|
|
|
n |
e |
|
|
|
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
C |
E |
|
|
|||
|
|
X |
|
|
|
|
|||
|
- |
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
NOW! |
o |
|||
>> codingto BUY |
|
|
|||||||
|
|
|
|
|
|||||
w Click |
|
|
|
|
|
m |
|||
w |
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
g |
|
|
|
|
|
|
df |
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
IEX
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Full Tilt Poker. Главное окно клиента |
|
|
|
|
Клиент PokerStars |
|
|
|
|
|
|
|
|
||
|
|
Сестра,шприц! |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
return (LRESULT) theInjector. |
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
||||||||
|
Займемся DLL-инъекцией, которая поможет нам порулить клиентом по- |
|
|
HandleIt(Hook_Create, (HWND)wParam); |
|
|
|
|
||||||||
|
кер-рума. «Почему именно техника DLL Injection?», — спросишь ты. Все |
|
|
else if (nCode == HCBT_DESTROYWND) |
_Destroy,.DLL |
|
|
|
|
|||||||
|
просто. Софт для покер-румов создают не пионеры, а серьезные дядьки. |
|
|
return theInjector.HandleIt(HookP |
|
|
|
|
||||||||
|
Поэтому любая модификация клиента моментально палится сервером, |
|
|
(HWND)wParam); |
|
|
|
|
|
|||||||
|
и твоя учетная запись будет забанена без малейшей перспективы полу- |
|
} |
|
|
|
|
|
|
|
|
|||||
|
чить обратно свое бабло. А если нельзя сделать легкий тюнинг клиента |
|
|
return 0; |
|
|
|
|
|
|||||||
|
заранее, стало быть, будем тюнинговаться на лету. Для этой цели DLL |
|
} |
|
|
|
|
|
|
|
|
|||||
|
Injection — то, что доктор прописал. |
|
|
bool OPCHOOK_API InstallHooks() |
|
|
|
|
|
|||||||
|
Технике DLL Injection не один год. Она широко известна в наших узких |
|
{ |
|
|
|
|
|
|
|
|
|||||
|
кругах и неплохо документирована на многих специализированных веб- |
|
|
g_hHook = SetWindowsHookEx(WH_CBT, (HOOKPROC) |
|
|
|
|||||||||
|
ресурсах (несколько интересных ссылок ты найдешь на полях). Поэтому |
|
|
AutoCBTProc, hInstance, 0); |
|
|
|
|
|
|||||||
|
рассмотрим лишь самую суть, имеющую отношение к созданию бота для |
|
|
return g_hHook != NULL; |
|
|
|
|
|
|||||||
|
игры в покер. |
|
} |
|
|
|
|
|
|
|
|
|||||
|
Вообще, DLLInjection— достаточно обширное понятие, под которым |
|
|
|
|
|
|
|
|
|
|
|
||||
|
С использованием глобальных CBT-хуков есть одна досадная проблема |
|
|
|||||||||||||
|
подразумевается несколько родственных технологий. Самая, пожалуй, |
|
|
|
||||||||||||
|
популярная — внедрение в адресное пространство чужого процесса — |
|
(это справедливо и для других глобальных хуков — на то он и глобаль- |
|
|
|||||||||||
|
неоднократно описывалась на страницах нашего журнала. Правда, это |
|
ный, чтобы никому сладко не было). DLL в этом случае будет загружена |
|
|
|||||||||||
|
больше относится к соседней рубрике «Взлом», мы же люди мирные, нам |
|
в адресное пространство ВСЕХ процессов. И если эта библиотека весит |
|
|
|||||||||||
|
бы бабла поднять. Поэтому будем осваивать менее воинственные техни- |
|
более чем «Hello, World!» (а так оно и будет после того, как ты напишешь |
|
|
|||||||||||
|
ки — Windows Hook и CBT Hook. Покурив MSDN на тему Windows Hook, |
|
логику своего бота), приличные тормоза системе обеспечены. Поэтому |
|
|
|||||||||||
|
ты обнаружишь, что в основе техники лежит метод SetWindowHookEx |
|
мы с тобой поступим следующим образом: напишем легкую библиотечку, |
|
|
|||||||||||
|
из Windows API. Ниже — демонстрация того, как мы можем использовать |
|
которая будет выполнять только одно действие — проверять, в адрес- |
|
|
|||||||||||
|
это для своих грязных целей: |
|
ном пространстве какого процесса она находится. И если этот процесс |
|
|
|||||||||||
|
|
|
|
|
|
|
принадлежит клиенту online покер-рума, то — подгружать основную |
|
|
|||||||
|
|
LRESULT CALLBACK PokerBotCBTProc(int nCode, |
|
библиотеку, зарабатывающую бабло. |
|
|
|
|
|
|||||||
|
|
WPARAM wParam, LPARAM lParam) |
|
|
|
|
|
|
|
|
|
|
|
|||
|
{ |
|
|
|
|
|
Закатаемрукава |
|
|
|
|
|
||||
|
|
if (nCode < 0) |
|
Довольно лирики, пора браться за дело. Начинаем с DLL Injection: |
|
|
||||||||||
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return CallNextHookEx(g_hHook, nCode, |
|
|
bool XPOKERBOTHOOK_API InstallHook() |
|
|
|
|
|
||||||
|
|
wParam, lParam); |
|
{ |
|
|
|
|
|
|
|
|
||||
|
} |
|
|
|
|
|
g_hHook = SetWindowsHookEx(WH_CBT, |
|
|
|
|
|
||||
|
|
else if (theInjector.getVenue() != |
|
|
(HOOKPROC) CBTProc, g_hInstance, 0); |
|
|
|
|
|
||||||
|
|
Venue_Unknown) |
|
|
|
|
|
|
|
|
|
|
|
|||
|
{ |
|
|
|
|
|
return g_hHook != NULL; |
|
|
|
|
|
||||
|
|
if (g_bFirstTime) |
|
} |
|
|
|
|
|
|
|
|
||||
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Следующий шаг — создание механизма обнаружения открытия и закры- |
|
||||||||||||||
|
|
theInjector.inject(); |
|
|
||||||||||||
|
|
bFirstTime = false; |
|
тия интересующих нас окон клиента покер-рума: |
|
|
|
|
|
|||||||
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LRESULT CALLBACK CBTProc(int nCode, |
|
|
|
|
|
|||
|
|
if (nCode == HCBT_ACTIVATE) |
|
|
WPARAM wParam, LPARAM lParam) |
|
|
|
|
|
||||||
|
|
return (LRESULT) theInjector.HandleIt |
|
{ |
|
|
|
|
|
|
|
|
||||
|
|
(Hook_Activate, (HWND)wParam); |
|
|
if (nCode < 0) |
|
|
|
|
|
||||||
|
|
else if (nCode == HCBT_CREATEWND) |
|
|
return CallNextHookEx(g_hHook, nCode, wParam, |
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xàêåð 01 /121/ 09 |
|
|
|
|
|
|
099 |
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
E |
|
|
||||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
NOW! |
o |
|||||
|
|
|
|
|
||||||
|
|
|
|
|
|
|||||
w Click |
to BUY |
|
>> |
|||||||
|
|
|
|
|
|
m |
||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
n |
e |
|
|||
|
|
|
|
-xcha |
|
|
|
|
|
NOTEPADcoding .EXE
XYZ.DLL
PHOTOSHOP.EXE POKERSTARS.EXE
XYZ.DLL XYZ.DLL
|
|
|
|
|
|
hang |
e |
|
|
|
|
||
|
|
|
|
|
C |
|
|
E |
|
|
|||
|
|
|
|
X |
|
|
|
|
|
|
|||
|
|
|
- |
|
|
|
|
|
|
d |
|
||
|
|
|
F |
|
|
|
|
|
|
|
t |
|
|
|
|
|
D |
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
r |
||
|
|
P |
|
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
|
|
|
||||
|
IEXPLORE.EXE |
|
BUY |
|
|
||||||||
|
to |
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
w Click |
|
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|
|
|
|||||
|
w |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
w |
|
|
|
|
|
|
|
|
o |
|
|
XYZ.DLL |
|
. |
|
|
|
|
|
|
.c |
|
||
|
|
|
p |
|
|
|
|
|
g |
|
|
||
|
|
|
|
|
df |
-x c |
|
n |
e |
|
|||
|
|
|
|
|
|
ha |
|
|
|
|
T.DLL INJECT.DLL
.DLL
EXE
Клиент Full Tilt Poker |
INJECTINJECT.DLL.DLL |
|
lParam); |
|
|
|
в тот момент, когда ты садишься за стол. Или еще проще — после нажа- |
|
else if (!g_pClient) |
|
|
|
тия кнопки «Бабло», управляющей активностью бота. Концепт данного |
|
return 0; |
P |
.DLL |
|
модуля будет таким: |
|
HWND hWnd = (HWND)wParam; |
|
|
|
||
if (!hWnd) |
|
|
|
|
PokerTimeTableWindow::PokerTimeTableWindow |
return 0; |
|
|
|
|
(HWND hWnd, PokerTimePokerClient* client) : |
|
|
|
|
|
OnlineTableWindow(hWnd, client) |
if (nCode == HCBT_ACTIVATE) |
|
|
|
{ |
|
{ |
|
|
|
|
HWND hwndChat = ::FindWindowEx(hWnd, NULL, |
if (!g_pClient->IsRegisteredWindow(hWnd)) |
|
|
_T("RichEdit20W"), NULL); |
||
g_pClient->TryRegisterWindow(hWnd, NULL); |
|
|
if (hwndChat) |
||
} |
|
|
|
|
{ |
else if (nCode == HCBT_DESTROYWND) |
|
|
|
PokerTimeTableWindow::OldRichWndProc = |
|
{ |
|
|
|
|
(WNDPROC)::GetWindowLongPtr(hwndChat, |
if (g_pClient->IsRegisteredWindow(hWnd)) |
|
|
GWL_WNDPROC); |
||
g_pClient->UnregisterWindow(hWnd); |
|
|
|
::SetWindowLongPtr(hwndChat, GWL_WNDPROC, |
|
} |
|
|
|
|
(LONG_PTR)PokerTimeTableWindow::MyRichWndProc); |
return 0; |
|
|
|
} |
|
} |
|
|
|
|
} |
|
|
|
|
|
|
И—паракомментариев.Вмоментактивацииглавногоокнаприложения посылаетсясообщение HCBT_ACTIVATE.Соответственно,вмоментзакрытияокнапосылаетсясообщениеHCBT_DESTROYWND.Остальнойкод достаточнопростдляпонимания.Онпросто-напростополучаетконтроль надокномилиосвобождаетегоотсвоейопеки.Да,чутьнезабыл.Мыне можемиспользоватьстандартноесообщение HCBT_CREATEWND для ответанавопросотом,созданолиужеокноклиента.Посколькукпроцессу клиентамыещенеподключились,тонеможемизахватитьуправление окном.Азначит, HCBT_CREATEWND здесьнеподходит.Вместоэтогомы воспользуемсясообщением HCBT_ACTIVATE дляпоискаактивного окна.Напомню, что исходные данные для анализа мы будем получать, парся текст из окна чата игроков текущего стола. Чтобы получить текст и передать впоследствии парсеру, отловим сообщение EM_STREAMING, заменив callback функцию клиента своей собственной. Чтобы не загружать пример кода лишней ботвой, перехват сообщений начинается в момент инициализации бота. На практике тебе придется внести некоторые коррективы. Дело в том, что если открыто окно стола с игрой, это еще не значит, что ты уже в игре. Ты можешь просто наблюдать за игрой или, только что вступив в игру, дожидаться, когда до тебя дойдет очередь ставить большой блайнд. Поэтому бот должен включиться в игру
Теперь, док, давай научимся взаимодействовать с окном чата. Здесь конкретные действия будут зависеть от того, на основе какого элемента управления оно реализовано. В частности, клиент для такого покеррума, как PokerTime, использует для этого компонент RichEdit. Для отображения текста в окне чата используется стандартное сообщение EM_STREAMIN, которое мы и будем перехватывать:
LRESULT PokerTimeTableWindow::MyRichWndProc
(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
EDITSTREAM* es = (EDITSTREAM*) lParam; if (msg == EM_STREAMIN)
{
PokerTimeTableWindow::OldRichEditCB = es->pfnCallback;
es->pfnCallback = PokerTimeTableWindow::MyEditStreamCallback;
PokerTimeTableWindow::CurrentChatWindow = hWnd;
}
LRESULT lRet = ::CallWindowProc(
100 |
xàêåð 01 /121/ 09 |
NOTEPAD.EXE
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|||
|
|
X |
|
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
|
||
|
F |
|
|
|
|
|
|
t |
|
||
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
||||
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
XYZ.DLL |
||||||
|
|
|
|
to |
BUY |
|
|
|
|
|
|
w Click |
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
m |
||||
w |
|
|
|
|
|
|
|
|
o |
|
|
|
w |
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
||
|
|
|
df |
|
|
n |
e |
|
|
||
|
|
|
|
-xcha |
|
|
|
|
|
INJECT.DLL
XYZ.DLL XYZ.DLL
INJECT.DLL INJECT.DLL
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
C |
E |
|
|
|||
|
|
|
|
|
X |
|
|
|
|
|||
|
|
|
|
- |
|
|
|
|
d |
|
||
|
|
|
|
F |
|
|
|
|
|
t |
|
|
|
|
|
|
D |
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
r |
||
XYZ.DLL>> |
P |
|
|
|
|
NOW! |
o |
|||||
|
|
|
|
|
|
|||||||
codingto BUY |
|
|
||||||||||
|
|
|
|
|
||||||||
|
|
|
w Click |
|
|
|
|
|
m |
|||
|
|
|
w |
|
|
|
|
|
|
o |
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
.c |
|
||
|
|
|
|
|
p |
|
|
|
g |
|
|
|
|
|
|
|
|
|
df |
|
n |
e |
|
||
|
|
|
|
|
|
|
-x cha |
|
|
|
|
INJECT.DLL
На форуме «2+2» есть и горячие обсуждения ботов
PokerTimeTableWindow::OldRichWndProc, hWnd, msg, wParam, lParam);
if (msg == EM_STREAMIN)
{
es->pfnCallback = PokerTimeTableWindow::OldRichEditCB;
}
return lRet;
}
Еслитебеужеприходилосьраньшесталкиватьсяс EM_STRAEMING,тоты долженпомнить,чтовместеснейнеобходимоиспользоватьcallback-фун- кциюEDITSTREAMCALLBACK.Намнужнозаменитьисходнуюcallbackфункцию(А)своейсобственной(В).Нуамыужепередадимуправление функцииАпослетого,каксделаемсвоитемныеделишки,чтобыклиентне потерялсвязисреальностьюиневыпалвглубокуюнирвану:).
PokerTimeTableWindow::OldRichEditCB = es->pfnCallback;
es->pfnCallback = PokerTimeTableWindow::MyEditStreamCallback;
|
HOTOSHOP.EXE |
POKERS |
|
XYZ.DLL |
XYZ |
INJECT.DLL |
INJECT.DLL |
INJE |
string sCard2 = what[2]; |
|
|
ApplicationProxy::TransmitHoleCards |
|
|
((sCard1 + sCard2).c_str(), hPokerTable); |
|
}
else if (boost::regex_match(line, what, P regWakeUp, boost::match_default|boost::match_single_line) && what.size() == 3)
{
string theActor = what[1];
if (theActor == g_pClient->LoggedInAs) OnlinePokerExecutor::PerformAction(
hPokerTable);
}
}
}
Обработаем полученные данные и симулируем действия пользователя. В первом нет ничего сложного, второе — тоже из серии «для чайников», поэтому отдельно останавливаться на этих моментах мы не будем. Если вдруг возникнут трудности — грызи исходники, которые есть на диске.
|
Финальныйаккорд |
Ахтунг! |
|
||
Остались сущие пустяки — разобрать полученный текст, вытащив из него |
Так, чтобы все гладко и без единого сучка, бывает только в кино. В ре- |
|
информацию, необходимую для принятия решений. Ключевые строки, |
альной жизни за каждым углом нас поджидает лохматый писец. Не хочу |
|
на которые будем ориентироваться, это: |
капать на мозг (он и так изрядно загажен нытьем о всемирном экономи- |
|
• «DealingHoleCards(Ah,Ad)» (нам раздали карманные карты и можно |
ческом кризисе, раздающимся из каждой подворотни), но ты это должен |
|
начинать анализ игровой ситуации); |
знать. Иначе было бы просто нечестно. |
|
• «Meowt,youhave10secondstorespond» (наш ход, пора действовать). |
Деловтом,чтовсебезисключенияпокер-румыприлагаютмаксимум |
|
Регулярные выражения — твой лучший друг: |
усилийктому,чтобыпресечьнакорнюразведениепокер-ботов.Этимзани- |
|
|
|
маютсяотдельныелюди,именуемыеаналитиками.Основнойихинструмент |
|
DWORD CALLBACK PokerTimeTableWindow::MyEditStrea |
—анализисторийигровыхсессий.Главноепалево,выдающееботаспот- |
|
mCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG |
рохами—шаблонностьипредсказуемостьигры.Человекможетошибиться, |
|
numberOfBytes, LONG* actualBytes) |
войтивазартиотступитьсяотлюбойсистемы,какойбыоннипридерживал- |
{ |
ся.Бот—никогда.Унегожелезные,точнее,кремниевыенервы. |
|
|
DWORD dwRet = PokerTimeTableWindow::OldRichEditCB |
Отсюда — несколько маленьких финальных советов: |
|
(dwCookie, pbBuff, numberOfBytes, actualBytes); |
1.Не жадничай — много мелких банков привлекут меньше внимания, |
|
if (0 == dwRet && actualBytes && *actualBytes > 0) |
чем несколько крупных. |
{ |
2.Стабильный плюс — повод внимательнее приглядеться к игроку. Поэ- |
|
|
boost::smatch what; |
тому позволь своему боту иногда проигрывать. |
|
if( boost::regex_match(line, what, regHoleCards, |
3.Как в той рекламе («Теплее, Виктор, еще теплее…»), добавь боту |
|
boost::match_default|boost::match_single_line) && what. |
немного человечности. Пусть он время от времени удивляется своим |
|
size() == 3) |
удачам или огорчается проигрышам, отпуская соответствующие замеча- |
{ |
ния в окне чата. |
|
|
string sCard1 = what[1]; |
Удачи в игре, гринго!z |
|
|
101 |
xàêåð 01 /121/ 09 |
||
|
|