How I Built a Working Online Poker Bot, Part 7: Extracting Text from 3rd-Party Applications
Thursday, July 17, 2008   

Introduction

Lately I've been doing some programmatic graffiti.

Maybe I have too much time on my hands, but sooner or later every programmer wonders:

  • How to extract text displayed by other applications.
  • How to draw your own text in the windows of external applications.

Maybe you've even wondered:

  • How to hide the text displayed by other applications.

That's fun, too. Minimalism.

I like it.

But why stop with a single application? Let's replace every piece of text drawn anywhere on the system with our tag, creating a harmless "graffiti bomb". Scroll down if you're curious.

Why Are You Vandalizing Your Computer Screen?

To illustrate a technique.

In How I Built a Working Online Poker Bot, Part 4: The Poker Botting Erector Set, we looked at some code to extract text from the Poker Time online poker client software. That was relatively easy because Poker Time windows are, for the most part, standard Windows controls. That means the text they contain is wide-open, to our bot and a hundred other tools, such as Spy++.

Poker Stars and Full Tilt are a little different. Poker Stars and Full Tilt are special. Pull up Spy++ or a similar tool, point it at the Poker Stars game chat window. What do you see? 

Empty text, that's what. Because the Poker Stars game chat window isn't a standard Windows control, with well-defined behavior. It's a custom window, likely implemented using MFC (we know this because of the characteristic "Afx:" window class). Custom windows are allowed to do whatever they want, within reason. Such as return an empty string in response to the WM_GETTEXT message.

"I don't contain any text! Scout's honor!"

Are we fooled?

Today I'm going to show you one way to extract this text. There are others. We'll leave those for future posts, or for your own investigations.

The Text Output Bottleneck

Almost every poker client features a game chat window which contains game actions, chat text, and dealer spam: 

But sometimes getting the text out of this window is a little more complicated than just sending the window a WM_GETTEXT. The method we're going to discuss today is very similar to the method we use to snoop on 3rd-party file I/O. Only this time, instead of detouring the CreateFile/WriteFile/ReadFile functions, we're going to detour the various text-drawing APIs provided by Windows. DrawText and ExtTextOut in particular.

System-Wide Graffiti?

We're going to take advantage of the fact that, by and large, every piece of text drawn by the Windows operating system is routed through a small handful of public APIs. No, I mean virtually EVERY piece of text drawn by your Windows PC is routed through a small handful of functions that display text.

By detouring those functions and replacing them with our own versions (a matter of a few lines of code) we can do things like create a system-wide graffiti bomb. Below, I've replaced every piece of text drawn by every application on the system with a single phrase:

Coding the Wheel was here

This required tens, not hundreds, of lines of code.

Hey, did I mention that VIRTUALLY EVERY PIECE OF TEXT ON THE SYSTEM IS ROUTED THROUGH A HANDFUL OF TEXT-OUTPUT APIs?

Okay. I'm glad we got that straight.

Situated GDI Hooking

If you've been following the botting series, you already know about detouring text-output functions. But if you haven't, understand that whenever an application calls a system API like DrawText or ExtTextOut in order to paint text on the screen, it's going to be calling our code instead. Our code then does four things:

  1. Calls the original version of the function provided by the Windows operating system.
  2. Examines the HDC provided with the call, and gets the associated window as an HWND value. This tells us which window the text is being drawn to.
  3. Examines the location of the text, as provided by point or rectangle coordinates. This tells us what part of the window the text is being drawn to.
  4. Examines the text itself, and does something with it.

I like to call this "situated GDI hooking" because it's installs detours around text-drawing APIs provided by the Graphic Device Interface (GDI) and uses the device context (HDC) along with the X,Y location of the text to establish the meaning of the text: is this game chat text? Miscellaneous window text? Button text? And so forth. 

Injecting the DLL

First, inject a DLL into the target application's process using any DLL injection mechanism.

This has been covered on the Internet, in books, and here on Coding the Wheel, endlessly, so I won't rehash it.

Just get your DLL into the target application's process by any means necessary.

Detouring the Text Output Functions

If you've used the Microsoft Detours library before, you already know the score on this one. For each function you want to detour, you'll need to create an equivalent version taking the same parameters and return type and place it in a DLL. Let's take a look at some sample code for a simple DrawText detour for Poker Stars:

// Function pointer to the original (un-detoured) DrawText API
int (WINAPI * Real_DrawText)(HDC a0, LPCWSTR a1, int a2, LPRECT a3, UINT a4) = DrawTextW;

// Our custom version of DrawText
int WINAPI Mine_DrawText(HDC hdc, LPCWSTR text,  int nCount, LPRECT lpRect, UINT uOptions)
{
    int rv = Real_DrawText(hdc, text, nCount, lpRect, uOptions);
   
    HWND hWindow = WindowFromDC(hdc);
    if (!hWindow)
       return rv;

    if (IsPokerStarsChatWindow(hWindow))
    {
       // Text has been written to the Poker Stars chat window.
       HWND hPokerTable = ::GetParent(hWindow);
       // hPokerTable now stores the HWND of the poker table that
       // the chat window belongs to.
    }

    return rv;
}

And of course, we need to actually install the Detour. The typical time to do this is during the DLL_PROCESS_ATTACH notification.

// Install the DrawText detour whenever this DLL is loaded into any process...
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved  )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)Real_DrawText, Mine_DrawText); // <- magic
        DetourTransactionCommit();
        break;

    case DLL_PROCESS_DETACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)Real_DrawText, Mine_DrawText);
        DetourTransactionCommit();
        break;
    }

    return TRUE;
}

The other crucial text-drawing API you'll probably want to detour is ExtTextOut (not shown).

How do I tell which window a particular DrawText or TextOut call is drawing to?

As I've said, the text APIs we're detouring get called all over the place. In the case of online poker clients, we're only interested in the text that gets painted in the game chat window. We'd like to ignore all the calls which draw text to other areas of the window.

Here's the basic procedure:

  1. Every call to DrawText, TextOut, and similar APIs includes a device context (HDC) parameter.
  2. Figure out the window associated with this HDC by calling the WindowFromDC function, which returns a window handle (HWND).
  3. Take that window handle and test it to see if it "equals" the window we're interested in. Typically, look at the window's class name and, if necessary, its parent window.

In order to figure out what the window class name is for a particular window, use a tool like Spy++. Here we see that the Poker Stars chat window has a class of "Afx:400000". We also notice that it's a child window of the poker table window. 

Putting all that together, we might have some code that looks like this. The following function returns true if the specified window is the Poker Stars chat window.

bool IsPokerStarsChatWindow(HWND hWindow)
{
    // The Poker Stars chat window is a child of the poker table window, so
    // if the window doesn't have a parent, it's not the chat window.
    HWND hParent = ::GetParent(hWindow);
    if (hParent)
    {
        // Okay, the window has a parent. Now get the (child) window's class name.
        TCHAR className[255];
        GetClassName(hWindow, className, 255);

        // Compare the window's class name with the PS chat window class name.
        // We know from Spy++ that the Poker Stars chat window has a class of
        // 'Afx:400000:20'. _tcsicmp returns 0 if they're equal.
        return 0 == _tcsicmp(className, _TEXT("Afx:400000:20") );
    }
    return false;
}

Ultimately, we'd prefer not to hard-code window class names or the parent/child relationships. This information can be passed in from an XML or other settings file, allowing for generic, cross-venue code.

Filtering Out Redundant ExtTextOut and DrawText Calls

As soon as you start to hook the text output APIs you'll notice one thing: TextOut and ExtTextOut get called a lot. Redundantly. Sometimes with empty strings.

In the comments to my last post, a poster by the name of Robert noted:

I have a question for anyone reading... How are you handling the case in which a message is duplicated? Here is an example from a hooked ExtTextOutW to illustrate...

Hooked Function - Table Name = Message
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: The flop is [Ts 7s 8h]
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: Pritt95 bets $5
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: bordos calls $5
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: Pritt95 bets $5
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: bordos calls $5
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: The turn is [3c]
MyExtTextOutW-Alto (6 max) - $5/$10 - Limit Hold'em=Dealer: SwimmingPool raises to $10
MyExtTextOutW-Alto (6 max) - $5/$10 - Limit Hold'em=Dealer: gambleallday folds
MyExtTextOutW-Alto (6 max) - $5/$10 - Limit Hold'em=Dealer: MGAMW calls $5
MyExtTextOutW-Alto (6 max) - $5/$10 - Limit Hold'em=Dealer: The flop is [Js 5d 6s]
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: bordos calls $5
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: The turn is [3c]
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: Pritt95 bets $10
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: The turn is [3c]
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: Pritt95 bets $10
MyExtTextOutW-Alexandra (6 max) - $5/$10 - Limit Hold'em=Dealer: bordos calls $10

Notice how the turn is "dealt" several times? Also how several bets are duplicated. How can we handle this sort of thing?

Another reader, nickname The Lone Ranger, replied:

Looks like you will need to write a filtering routine. The extent of the filtering will be determined by the frequency and extent of the duplicates. Do the duplicates occur on every hand? Is it always the turn?

I happened to notice both comments shortly after they were posted, so I chimed in.

Yes and one quick and dirty way is to filter by Y value. Provided you don't resize the window or touch the scroll bar, new text tends to enters the window at a constant Y because of the glue effect. Or at least, for any give X,Y associated with a TextOut call, you can answer the question "is this text at the bottom of the window?" The Y may or may not be constant, but it will always be higher than the Y of duped, pushed-upward text.

That's a quick solution to the problem, which is one of DrawText/ExtTextOut spoofing. As new text enters the window, the poker client calls DrawText, ExtTextOut, etc., to draw the text. But it draws that text not only when it's first added to the window, but successively, as new text enters the bottom of the window, forcing the lines above it upward. And depending on exactly how the paint procedure is implemented, the poker client might redraw text at other, seemingly random times.

Another solution would be to get at the actual text stored by the poker client—an approach we'll look at later.

For now, just realize that you'll have to write a little bit of code to answer the question: is the text passed in this particular call to DrawText or TextOut redundant? Here are some ways to do that:

  • First of all, on Poker Stars, don't worry about it. We can bypass redundant messages thanks to a quirk in the drawing logic. This technique was demonstrated in the source code accompanying How I Built a Working Online Poker Bot, Part 5: Deciphering Poker Stars and Full Tilt.
  • On other venues, you can filter (as noted above) by the Y-value passed to the DrawText or ExtTextOut call.
  • You can also store a queue containing the most recent X unique text string you've received via a particular drawing API. When a new call is made, you can check the text against the text you have stored in the list. If it's a dupe, ignore it. Otherwise add it to the queue, and pop the oldest element off. This leverages the fact that it's rare for two identical lines of text to appear in the chat window except in heads-up situations.
  • You can also parse each line of text as it comes in and see if it makes sense logically, according to the state of the current game. If you get a line of text "Player A bets 40" followed by "Player B calls 40" followed by "Player A bets 40" you can tell that the last message is redundant, because we never saw the text for Player C's action.

Also keep the following in mind:

  • Text output functions call each other! TextOut calls ExtTextOut internally, and DrawText calls DrawTextEx. Focus your hooking on DrawTextEx and ExtTextOut.
  • You can mostly ignore calls made with an HDC that correlates to a NULL window. That is, if WindowFromDC returns NULL, you can most likely ignore the call as these calls are associated with a screen DC, and a screen DC is not (usually) the kind of DC that people draw to in these situations. Instead, they'll draw to a window DC associated with the specific window.
  • Obviously, you can ignore calls the poker client makes with empty text.

You now have, believe it or not, everything you need to extract any piece of text from any application which draws text using standard APIs.

Conclusion—Parlor Tricks

I'd like to leave you today with one last picture. Look closely.

It looks like a foreign language version of the Internet Explorer "blank" page. This is what happens when we reverse the text passed to our detour, just for fun:

BOOL WINAPI Mine_ExtTextOut(HDC hdc, int X, int Y, UINT options, RECT* lprc, LPCWSTR text, UINT cbCount, INT* lpSpacingValues)
{
    if (!text)
        return TRUE;

    // Make a copy of the supplied string..safely
    LPWSTR szTemp = (LPWSTR)LocalAlloc(0, (cbCount+1) * 2);
    memcpy(szTemp, text, cbCount*2); // can't use strcpy here
    szTemp[cbCount] = L'\0'; // append terminating null

    // Reverse it..
    wcsrev(szTemp);
   
    // Pass it on to windows...
    BOOL rv = Real_ExtTextOut(hdc, X, Y, options, lprc, szTemp, cbCount, lpSpacingValues);

    // Cleanup
    LocalFree(szTemp);

    return TRUE;
}

Not too useful, but interesting, and illustrative. By interposing your code between the target application and the operating system drawing APIs, you can snoop on, modify, hide, replace, or cosmetically tweak the vast majority of text drawn on your system, in any process, any application whatsoever. This is how the images above (at the top of the article) were generated.

I hope I've made the point: extracting text from Poker Stars or any other application whatsoever is not only possible, it's easy. Let's add it to our toolbox and move on.

...emit txen litnU. sliated eht ni s'lived eht...rebmemer dna ,gnidaer rof sknaht ,syawla sA


Posted by James Devlin   208 comment(s)

Brilliant work JD...

Really enjoyed the entire botting series.

LogicalAI on 7/17/2008 12:42 PM (602 days ago)

Great article. Well written.

Anonymous on 7/17/2008 1:17 PM (602 days ago)

well worth the wait

Anonymous on 7/17/2008 2:28 PM (602 days ago)

A great series! Keep up the good posts!

Anonymous on 7/17/2008 2:30 PM (602 days ago)

Awesome post James. I look forward to more in the future.

Andrew G. on 7/17/2008 2:36 PM (602 days ago)

Anonymous on 7/17/2008 3:15 PM (602 days ago)

Agree !

It was worth the wait !

Hope the 8 will come sooner than we think ;)

Anonymous on 7/17/2008 3:21 PM (601 days ago)

Amazing.
I have enjoyed your poker bot series since day one.
Because of you I am going to start my own bot... not for the financial aspects, but for purely academic reasons. I need a pet project to keep my coding skills sharp.

Trace A. on 7/17/2008 4:18 PM (601 days ago)

Does anybody know whether these same techniques work under WPF or is this strictly GDI? And is there any way to accomplish this purely in C#?

James: you should submit this over at one of the MS forums they would probably get a kick out of that last pic..

Ed K. on 7/17/2008 7:23 PM (601 days ago)

I'm curious whether or not these techniques can be performed using a language other than C# or C++. Like Java? Anybody?

Jam Ends Evil on 7/18/2008 4:29 AM (601 days ago)

Am I going mad or did 3 new articles just appear at once after a 2-week drought?! Very interesting as usual James.

Paul on 7/18/2008 5:53 AM (601 days ago)

you're going mad - these articles appeared days ago.

no, just kidding. but i think some guy in the previous post broke his F5 key, so maybe author felt bad about that and decided to release a couple extra posts. or maybe it was going to be another one of his endlessly long technical "code the known universe" diatribes and he decided to make good on his promise to start doing shorter pieces.

good set of posts though, except he chickened out and got rid of the boost. you people.. i swear.. what's so hard about building a couple hundred thousand lines of source code using an obscure nmake command and a twirling flux cogillitator? sheesh.

Anonymous on 7/18/2008 8:11 AM (601 days ago)

The term is 'flux capacitor' The cogillitator is for tranversing the interdimensional gateway, not time travel.

What I like about this post "Coding the Wheel" scrawled even on the system Start button. I was able to get it to work on my machine but I've shut my little app down and the text is still showing up on my Start menu. Weird. But interesting. Will have some fun with this one.

JeremyX was here on 7/18/2008 8:27 AM (601 days ago)

I downloaded your latest sample code and tried running it on pokerstars and it kept giving me errors. Namely, "The procedure entry point ?Detoured@@YGPAUHINSTANCE__@@XZ could not be located in the dynamic link library detroued.dll".

First question: Am I supposed to build this on my system or just open it (that's what I did) and if so - I've been trying to get a hold of visual studio but have been having lots of problems downloading from microsoft. any suggestions?

thanks! Great posts btw! This will open up a whole new area of programming for me.

jay on 7/18/2008 11:21 AM (601 days ago)

Wow, they all came out at once! Great reading James, thanks.

Poker League on 7/18/2008 12:30 PM (601 days ago)

Great series and I enjoyed each article very much. I hope now you've got a whole batch of articles off your chest you could get in touch about a possible advertising deal? http://www.pokerisrigged.com has plenty of space to advertise this site for minimal effort on your part.

Cheers,
Nick.

Poker League on 7/18/2008 12:35 PM (601 days ago)

Nice, I really want to see this poker bot series continue. Solid articles for poker players, programmers, and poker bot enthusiasts alike.

http://www.icmbot.com

Poker Bot on 7/18/2008 10:51 PM (600 days ago)

I have been looking at this and it has me excited to program again. I used to program professionally, but stopped after taking a new position. So, this has me learning new things, re-learning old things, and generally excited to be in front of a compiler.

Thanks again.

chipset on 7/19/2008 11:36 PM (599 days ago)

Agree with chipset. I would like to see more managed code however, done in this style.

Does anyone know a good resource for hooking the graphics driver? I've heard this is possible but I've never seen an example of how it's done or what it might be used for. A few people over in the poker botting series were talking about this.

Anonymous on 7/20/2008 5:37 AM (599 days ago)

Hi dude,
This is one great work you've done her. Impressive! keep on doing the good job!

I tried to look at many hooking examples, but none are compiled properly on VS8.
I don't wanna sound too rude, but it would be great if you publish the code for the text replacement thingy.

Keep on doing that great job, RESPECT!

Benda

Benda on 7/20/2008 10:20 AM (599 days ago)

Nice, nice, nice. I was looking for this. Great!. I'm programming a bot on delphi for poker tables with RichEdit20W or similar controls that answer to the wm_gettext message. My hook only gets the hwnd of the RichEdit20W control and puts on my application(sometimes you go to another window with new controls and new hwnd.-obviously-, my hook capture the new hwnd if exist). My main application is out from hook .dll and capture all messages(the RichEdit control displays everything). I can parse everything. But, NOW, perhaps I can build the bot for the rest of poker rooms, thanks guy, you shown me a new way.

Great job!.
PD: Awaiting your next article.

Berto on 7/20/2008 8:04 PM (598 days ago)

How would you hook the names of the other players and their chips counts?

Anonymous on 7/21/2008 6:11 PM (597 days ago)

I have a question that relate back to the foldbot. In the code you hard coded the X,Y coords to push the Fold button. My question is how did you came up with that? I looked at Spy++ but I didn't see much there that I could use. Was it a program you used or trial and error? Any help would be apprecaited!

Adam on 7/22/2008 11:01 AM (597 days ago)

The chips are the reason for I need this article, the names...Get all text, twice, @buffer1-@buffer2=new lines, parsing the new lines,
example: 'Anonymous raise 600'
pos:=ansipos('raise',sentence);//position of r
playerstr:=midstr(sentence,1,pos-1);// name of player
...
I put it on stringgrid, if the player action changes and player exist on stringgrid, I change the status of player. If not exist then add player to stringgrid.
The name of players is not the problem, but the chips... yeah!. Then I found this article, that is a great job. If the detours working for me in delphi, dont need more. Ocr's and Screen scrap is difficult work but an option.

Berto on 7/22/2008 3:43 PM (596 days ago)

I forget it. My hook. I have a hook.dll and a hooklauncher with a button to start and edit box. The hook.dll drops the hwnd of the richedit control of the poker window in the editbox of hooklauncher, if window poker changes the hwnd changes(obviously, I know), but, my hooklauncher always have the same name then I have the main app, get the hwnd of hooklauncher window by name and with findwindowex gets the edit box control and with wm_getmessage gets the text of edit box. Drops the hwnd number in a memory file. The dll is not heavy. When I found this article my project was in advanced development, then only few changes have been implemented. In fact, I dont use the em_streaming capture method. This last article is very useful for me, and when my actual project ends, I will try the drawtext detour method that combined with x and y coordinates maybe tell to me everything.

Berto on 7/22/2008 5:36 PM (596 days ago)

Question:
When I'm using Spy++ and the "Find window" and drops the finder tool on my poker clients chat window (I'm using Svenska Spel poker client) it shows nothing on Class and the Caption only displays the caption of the window the "chat box" belongs to. Any thoughts anyone?



jonny on 7/23/2008 5:08 PM (595 days ago)

I've had this same question. Is it possible for a window to have an empty or null class name? Jonny can you browse to the window in the spy++ list of windows?

Keith on 7/24/2008 9:25 AM (595 days ago)

I copy pasted the Mine_ExtTextOut code to reverse text into the code for the pstars / fulltilt bot. After I edited the OnBnClickedBtnStars function to allow iexplore.exe it did not reverse the text as expected, and as seen on your screenshot.

Could someone point out what I missed?

KS on 7/27/2008 3:09 AM (592 days ago)

Fixed it!

KS on 7/27/2008 12:36 PM (592 days ago)

I'm using a bot doing fix limit six handed and can't seem to come up with a winning strategy. I've done an interesting approach. I have the program playing the hand about one thousand times from that point on, for instance if I have pocket aces and 3 people are in the hand it will find the odds that I will win based on one thousand hands. So I am able to come up with a percentage as to whether or not i should play the hand. I still can't win efficiently over 1000 hands at the lower limits. Any advice?

Anonymous on 7/29/2008 12:53 PM (590 days ago)

Read HPFAP (Holdem Poker for Advanced Players), apply the ruleset, hook your bot up to Poker Tracker, and look at way more than 1000 hands?

1000 hands isn't a large enough sample set for anything (whether computing equities or measuring your own results) but even if it were, the "my hand is X% to win against 2 random hands" statistic is only useful as a general indicator. What you really want is to assign a hand range to your opponents, compute the equity, and refine that range as the hand progresses.

Even then, a lot of the play is tactical (I've flopped top two pair, so I'm going to go with this hand regardless of the equities) rather than strategic (I'm an X% favorite vs. villain's range).

We'll be discussing this in detail so stay tuned!

James Devlin on 7/29/2008 5:25 PM (589 days ago)

Thanks for the response James. What are possible ways to see the villain's range? Doing it the way i've done I can stay alive in a room for hours without going broke but it is a very slow loss of money as time progresses. I'm guessing other players see how tight I am and play against me accordingly. Right now i'm trying to calculate the other players agression factors, V$IP, and PR, then im going to modify my range to either call or reraise the weak players who are just cont. betting me. When can we expect an article on this meaty stuff?

Gen_Poker on 7/29/2008 7:13 PM (589 days ago)

hi, you honestly rock! thanks for your botting series so far!

can anybody help me with this issue? i'm searching a windows api call, which is called everytime a new window is created (e.g. for getting a new opned pokertable).
i dont want to use the cbthook. i already tried to hook CreateWindowExA(and W), but the poker application kind a crashs (on ub). also i tried to hook EnableWindow, but it seems that not all new opened windows are shown (it's weird, only no limit tables are shown).
ohter hooks i tried are showwindow and registerclass.
so if somebody have any suggestions, pleas let me know!
thanks

Anonymous on 8/1/2008 12:13 PM (587 days ago)

Gen_Poker: How are you making the determination to keep/fold a hand? Preflop, and on/after the flop? Example: Villain (TAP) raises UTG, you 3-bet with QQ. All fold. Flop A K 2. Villain bets. What does your bot now do and why? Are you already incorporating PT stats? And can the bot compute hand vs. hand range equities for multiple opponents?

Anonymous: Can you post your CreateWindowExA (and W, both versions) detour code?

James Devlin on 8/2/2008 12:26 AM (586 days ago)

hi james, yea well i'm using the exactly the same from the traceapi sample of the detour project. here my code i'm testing: http://rafb.net/p/ucBp4P19.html.
ub won't start at all wenn i attach both of these calls and if i only attach the createwindowexa call to detours, ub starts but fails early with an unhandled win32 exception. it's really weird, but i'm looking forward you could give me a hint. thanks!

Anonymous on 8/2/2008 3:42 AM (586 days ago)

This is great stuff. You must have considered rolling all this into something that we can pay money for?

I'm primarily interested in limit poker AI and do all my testing against the Poker Academy bots. I've never actually tried to hook anything up to a real poker site so what I'd really like to see is some middleware (with or without it's own bots) that handles all the data extraction and user click emulation and which provides me with a slick interface where I can insert my own poker playing logic (C++ in my case).

I'd be happy to good pay money for this and I'd also be happy to pay for regular updates to keep pace with any changes that poker sites might make to their client applications.

From the response you've had to the articles so far, I would guess that if you throw some half-decent bots into the package you'd have rather more people than just myself banging on your door with credit cards in hand!

KayEmm on 8/2/2008 9:54 PM (585 days ago)

For me as a computer geek and a poker player it's been a set of most interesting lessons on a great topic and great techniques. In fact I can't remember any programming topic ever that captured my interest as much as this has. Great work James. Every day I look for more...

DM on 8/4/2008 4:45 AM (584 days ago)

What are some ways that people are making there bot realize that its there turn to act? My first idea was keep track of who is on your left but this starts to become a mess when people stand up or are not in the hand.

LastChance on 8/5/2008 11:02 AM (583 days ago)

This is what i am thinking of using now.

string playerToLeft = "";
int playerToLeftIndex = 0;
for( int i = 0; i < newGame.Players.Count; i++ ) {
if( newGame.Players[i].Name == currentPlayer ) {
break;
}
if( newGame.Players[i].IsInTheHand ) {
playerToLeftIndex = i;
playerToLeft = newGame.Players[i].Name;
}

}
if( playerToLeft.Length > 0 ) {

LastChance on 8/5/2008 11:40 AM (583 days ago)

Anyone get this working on Vista 64?

Paul on 8/5/2008 7:23 PM (582 days ago)

Yes its working on vista 64 for me. Let me know if you have questions.

LastChance on 8/6/2008 3:36 PM (581 days ago)

hi, i have another problem on ub. everytime textouta is called to give out a dealer notification in the chat window, the hdc adress is always different. and it's also weird that the WindowFromDc HWND handle is always 0 then. i would appreciate it, if anyone could help me. thanks!

Anonymous on 8/7/2008 3:00 PM (581 days ago)

Hi all !

Great articles James

For those of you who prefer C# and are struggling with Dll Injection and API Hooking,
take a look at EasyHook (http://www.codeplex.com/easyhook)
Download the binaries, read the small doc, and youre on your way injecting dlls and hooking from managed code.

I did preliminary tests and it seems to be as good as promised

FreakNguyen on 8/12/2008 8:27 PM (575 days ago)

Hey,

Very interesting work!!

I have starting doing my homework with a PokerStars bot.
One question for any people working on same subjet:

What is the best way to get accurate information about position and stack of each player around the table.
The information is given in the history file :

Seat 1: player1(1500 in chips)
Seat 2: player2(1500 in chips)
Seat 3: player3(1500 in chips)
Seat 4: player4(1500 in chips)
Seat 5: player5(1500 in chips)
Seat 6: player6(1500 in chips)
Seat 7: player7(1500 in chips)
Seat 8: player8(1500 in chips)
Seat 9: player9(1500 in chips)


but it may not be 100% accurate in case a player move to another table. The only place to get 100% accurate information seems to be the table window itself but It did not seem to call DrawText or TextOut to display it.

Thanks
Fab

Fab on 8/13/2008 9:44 AM (575 days ago)

I made my own working bot for PokerStars, just run and enjoy!

www.torrentparty.com/...f5e64b6d0601af9f54360.html

Anonymous on 8/13/2008 11:28 AM (575 days ago)

In Part-6 there was a blurb about Part-8 already being written... but it's been almost a month since then.

Is this series going to continue?

DM on 8/13/2008 1:32 PM (575 days ago)

That's what I'm wondering...

Anonymous on 8/13/2008 4:28 PM (574 days ago)

hi,

- ¿Because detoured.dll create your own, because we do not use that comes with Detours ?
- ¿Detourcreatewithdll have to be in a dll.?

alex on 8/14/2008 2:31 PM (574 days ago)

James,

I have followed your BOT expositions since the beginning, and even through the BOOST phase, successufully, ( and I am not a strong programmer). You have even inspired me to learn C++.

Since we haven't seen an article for a month now, I am beginning to wonder if something has discouraged further discourse on the subject.

Please let us know all is well, and PART 8 is on the way.

James on 8/14/2008 4:49 PM (573 days ago)

Hey , great Work so far to bad its so recent and all the articles of the series are not yet published. I look forward to your version of a trashbot. i have tried many attempts of making the foldbot a trashbot but my skills are nowhere near what i would like theme to be in c++. Anyway keep up the good work

Mark on 8/15/2008 10:17 PM (572 days ago)

Hi,

Thank you for this article, but my english is very bad and my only known language is c#.
I understand the logic behind it but i cant do anything in c++. It will be very very nice
if somebody compile a dll to use in c#.

Jim on 8/20/2008 10:26 PM (567 days ago)

Hi,

I've some Questions. I hope someone know the solutions. I work with Poker Stars.

#1 : I send over SendMessage a MouseClick on the Buttons to Call, Raise etc...
It work well but only if the TableWindow is bigger or equals the beginning size. If i change the size to smaller by resizing the TableWindow nothing hapend if i click with SendMessage on the Buttons. I have checked the x, y Coordinates by Drawing a Point where i click over SendMessage.

#2 : I need to know the Chips and the Player Count on the Table. And what is importent my Cards. With this Methode i only gets the Bets of my Opponents.

I hope someone can help me.

ayke on 8/22/2008 8:13 PM (565 days ago)

I found a Solution for the #2 Problem. PokerStars logs the hand in realtime in the PokerStars.log.0
But the first Problem is still open.

ayke on 8/22/2008 8:42 PM (565 days ago)

I'm not seeing DrawText, ExtTextOut, etc functions being called to draw player names, balances, etc. Just calls to output text to the chat window. Am I missing something? How could you make the first image above?

dave on 8/24/2008 10:53 AM (564 days ago)

And just to clarify my question above... I'm not filtering based upon window classes. I'm looking at every call FullTiltPoker.exe makes to the DrawTextEx and ExtTextOut functions, and don't see anything for player names, balances, etc.

dave on 8/24/2008 10:55 AM (564 days ago)

First I want to thank you for the tutorials here; really great stuff!

I tried your code on Titan (IPoker Network).
The function ExtTextOut shows the dealer messages.
But when I tried to get the HWND WindowFromDC(hdc); it returns NULL.
Then I enumerated all child windows of the table and know that the last child is the chat window.
Is there a possibility to only hook into that child window?

Thanks,
Roy

Roy on 8/24/2008 3:22 PM (563 days ago)

First of all, on Poker Stars, don't worry about it. We can bypass redundant messages thanks to a quirk in the drawing logic. This technique was demonstrated in the source code accompanying How I Built a Working Online Poker Bot, Part 5: Deciphering Poker Stars and Full Tilt.

Hi

I'm again. You said something about technique fix the Problem with the redundant messages. I searched in your Source Code for the Solution. In witch File i can find it ????

mfg ayke

ayke on 8/25/2008 4:13 AM (563 days ago)

In my detour of the ExtTextOut-function I do this:
HWND hWindow = WindowFromDC(hdc);

It sometimes returns NULL for me and sometimes it works. For example it works in FullTilt chat-window, but only on some controls on the lobby window (works in the column headers but not on the rows of the table). MSDN says it returns NULL when control is not attached to any window. As a win32 n00b I do not undestand what this really means. All I know is that the IPoker-client I'm detouring it is not working and I do not know any other approach.

JMK on 8/27/2008 9:30 AM (561 days ago)

Hi,

We find our hole cards by parsing PokerStars.log, but how do we determine which table the hole cards belong to. If I have three PokerStars tables running, all them write to PokerStars.log. In the example below is 001F01C6 the table handle?

MSG_TABLE_PLAYERCARDS 001F01C6
::: 8d
::: 13s

Is 0201F060 a sub window of 00260100..

[2008/09/01 23:30:38]
MSG_TABLE_ADVANCED_BET 00260100
Table::AdvActions::config() 0201F060

Thank you in advance for the clarification.

Anonymous on 9/2/2008 3:06 AM (555 days ago)

Correct, Use Spy++ or a similar tool and you'll understand.

Nyx on 9/2/2008 8:46 AM (555 days ago)

"
In the example below is 001F01C6 the table handle?

MSG_TABLE_PLAYERCARDS 001F01C6
::: 8d
::: 13s
"

Yes it is. If you want to know the HWND object, you could grab it as a CString and then convert it to a number. I made it yesterday and it works - although casting to a HWND raises a warning:

CString handleS = _T("001F01C6");
ULONG handleL = wcstoul(handleS, NULL, 16);
HWND handle = (HWND)handleL; //<--Now you have the handle!!

Anonymous on 9/2/2008 11:11 AM (555 days ago)

hello James,
First of all i want to say thanks for inspiring me... you did a better job then my teachers at the university Smile
thanks to your posts i' m learning a lot....

As Dave asked, can you please tell us which API method did you use to ovverride player's names, balances, etc....

I have tried my shareware programs that monitors which APIs are called from a process, but none of them works how it should... do you have any suggestion?

thanks a lot, James and good luck for your blog.

myro on 9/9/2008 3:59 AM (548 days ago)

I tried adding Poker Academy Pro support. I can launch PAPro, and the function calls show up - but PAPro hangs before the splash screen and just sits there. I at least expected it to run OK even if I didn't get the expected results. Anyone tried to work on Poker Academy?

M on 9/10/2008 10:31 AM (547 days ago)

Nevermind, just seems to be a little unstable.

M on 9/10/2008 10:41 AM (547 days ago)

I wasn't able to get any text out of Poker Academy so far unfortunately. Any hints on what calls to hook into? History files, GDI texts? How does a Sun AWT frame output text?

Titan is kind of interesting, because it seems you have to assemble table infromation from a finished hand's log first, and then apply the dealer chat for deltas. Any other way for Titan to create a table state structure? Any way to get to the text written to the buttons or the seat lables for example?

Jan on 9/17/2008 7:00 AM (540 days ago)

Addendum: Found the Java API for Poker Academy. THat seems to be an easy way to interface it...

Fortunately I built my BotBrain - Client interface over a socket connection, so it doesn't matter whether the client interface is written in Java or C++.

Anyway, any smarter way to interface Titan Poker other than reading the dealer chat for current events and reading the hand history WriteFile call for table state?

Jan on 9/17/2008 9:56 AM (540 days ago)

Thanks for great articles. These have taught me several new tricks I didn't knew even possible (like Detours). And I'm a professional programmer and have been working on a poker bot project on/off over a year. Also, you can write very well, easy to follow.

I would like to share some knowledge about OCR. And by this, I mean like taking screenshots of poker client window and analysing all the information from the bitmap. I've coded a program that can do this, although it's not real OCR, but just comparing character pixels to already known comparison pictures. You have pointed at some article, how difficult OCR can be, and I can say you're quite right. All possible characters have to be gathered from the poker client to a separate comparison bitmap. I have done this to one poker client, and the amount of characters is as big as 150, because all font sizes, capital letters etc. have to have seperate pictures. On the plus side, table and hole cards can be captured using this same method.

Some things considering speed. I first thought the speed would be problem with OCR, because the pixel comparison algorithm is quite complex. However, I'm a bit surprised how fast it is. After all, real changes doesn't happen so often in poker window. 90% of the time it is enough just to take a small screencapture from the message box to see if nothing has happened since last capture. When the whole table is completely captured (messagebox, table cards, all players names and stacks, player cards) it takes about 5 ms to take the screenshot and 25 ms to analyze with my 2.13 GHz processor. And this is C# code. I have also a c++ algorithm that is better optimized in many ways, which manages to analyze in about 10 ms.

How I see OCR:
+to my opinion, no way to get caught, because screenshots are the only way used to read information
+fits to all types of poker clients whatsoever
-hard to code, lots of debugging
-lots of work with every new poker window type
-takes a bit CPU time
-a little delay before information if gathered

John D. on 9/25/2008 1:01 PM (532 days ago)

Hey John,

you don't happen to have been studying computer science in Southern Germany with a room-mate named Jan in the appartment in the first year, do you? Otherwise I assume we've been coding some network stuff (starting with a serial 38,4 k connection, eventually moving onto an ARCnet drilling holes through the walls) together...

Jan on 9/25/2008 1:17 PM (532 days ago)

Hi all,

I've been looking at this on and off for a while as I've been planning on making a "Poker Bot". However, mine would really act as an "auto-folder". I wanted a program which read my hands in real time, worked out the current position I'm in and then read from a list of hands I wish to play. If the hand doesn't exist then fold then hand automatically for me. I finally achieved this. Smile

The program I coded only works for PokerStars as that is the only poker software I use, but if any one is interested I could post some code on how I achieved this. (It is written in C# however). I didn't use an DLL injections or read the window text etc, and I've been using it for around two days now and it works great.

I retrieved the hands from the PokerStars log file, worked out how many players etc are playing and on the current table etc all from this file. However, I manually have to type in one thing, and that's where my player is sitting on the table. I can't see where to get this information from, other than reading the text in the window, but didn't want to go down that route just for one small problem.

Ricky on 10/12/2008 11:02 AM (515 days ago)

Great Post!

http://www.poker-bots.com/

Poker Bot on 10/13/2008 4:59 AM (514 days ago)

"First of all, on Poker Stars, don't worry about it. We can bypass redundant messages thanks to a quirk in the drawing logic. This technique was demonstrated in the source code accompanying How I Built a Working Online Poker Bot, Part 5: Deciphering Poker Stars and Full Tilt. "


sorry to say, but it's not true. as you can see Poker stars MonitorBot duplicates messages from the chatwindow when some one wins a hand:

Dealer: Game #2118426xxxx: PLAYERNAME wins pot (9700)
Dealer: Game #2118426xxxx: PLAYERNAME wins pot (9700)
Dealer: Game #2118426xxxx: PLAYERNAME wins pot (9700)

I didn't find any code snippet that handle this in the MonitorBot source code.

am i missing something?

myro on 10/14/2008 7:39 AM (513 days ago)

In reply to Jay (7/18/2008). I know that comment was a long time ago, but I have been struggling with the same problem and it was driving me mental. Everything worked on one pc, and I kept getting a very similar error as Jay ("The entry point could not be located in the dynamic link library detoured.dll") on a second pc. Same visual studio, same project, same build settings etc.

I finally figured it out after searching my machine for other instances of detoured.dll - there was a program which I guess was doing its own bit of detouring already (?) "Dell\EMBASSY Trust Suite by Wave Systems\Embassy Trust Suite\Document Manager Lite" when I ran setup, uninstalled it and restarted my pc, magically - the FileMonitor sample worked.

Just putting this out here in case anyone has the same problem and is pulling their hair out like I was.

ton on 10/16/2008 10:34 AM (511 days ago)

Hi, maybe a bit late for this but... I am trying to hook into Poker Academy (PA) as some others have on this thread (Jan and M). I am having similar problems with hooking the standard apis for windows - DrawText etc - that were used in the examples which do not seem to be called by PA and also figured that it must have something to do with the fact it is java. Jan mentions "Found the Java API for Poker Academy". Is there anyone here that has successfully hooked to this and is getting information from PA? If so can they elaborate on the specifics? Is there any way to find what apis a program is calling? Is this a stupid question because should spy++ show this information? Thanks for any help.

Piggy on 11/3/2008 9:39 AM (493 days ago)

So much scrumptious code, no but seriously and awesome multi-post article on creating a poker bot!

Poker Basics on 11/5/2008 10:10 AM (491 days ago)

i made it!
finally i arrived to do a poker robot.
If you want to try it or have a look on it, I put it in my website.
Enjoy!

Rami on 11/14/2008 10:54 PM (481 days ago)

Has anybody written code to get text from the pokerstars lobby?

I simply wish to get the text in the listview of the tables, ie table name, limit, stakes, players so I can make an automatic table opener.

Any clue as to how to do this, anyone?

Anon on 11/22/2008 11:35 AM (474 days ago)

I'm trying to get text from titan windws too but it semms to use the mentioned apis only in few parts ...the main part of text isn't captured by our hook...
Someone solved this problem ? wich api titan poker uses to print text to screen?

frengo on 11/25/2008 2:37 AM (471 days ago)

Amazing series of articles, just from the point of a programer.

I have spent a lot of time on global system hooks and hook-injection after reading your articles. But there is one important question to be answered:

If i place my code between target application and operating system, wont the c++.dll will be loaded in every application? So wont for example pokerstars will be able to checksum the dll-manipulation?

I have found information on loading dll before let the target application generate its initialize-compare-checksum, but how is your solution working not to be detected?

Thx in advance.

Anonymous on 11/26/2008 1:45 PM (470 days ago)

I'm trying to modify the XMonitor code to be able to monitor an ipoker client.

Does anyone know how to handle PTIODEVICE windows and get caption from them?
Does anyone know how to get the text from the chat ?

Thanks

DoubleD on 1/14/2009 7:36 PM (420 days ago)

Hello,

I tried the API-Hooking method with the uallCollection for Delphi on PartyPoker.
It works, but after maybe 20 seconds, PartyPoker is closing the table and I'm back in the lobby!
Do you know if there is something like a detection method against API-Hooking?
Maybe there is also some error in the code that forces PartyPoker to close, but all the other applications like Firefox etc. are working stable!

Thomas on 1/18/2009 4:03 PM (416 days ago)

Hi all,
also for me the window retrieved by WindowFromDC is ALWAYS null, and so I'll never know which window has called the TextOut (or DraText) API. Any suggestion? Anyone with the same problem? Thanks in advance. Just a question, is the hdc valuable outside the process that has created it?

Antonhy

Antonhy on 2/19/2009 10:23 AM (385 days ago)

Nice job!

We did something similar too, you can download the free trial and check it out!
http://www.pokerbot-smart.com/

Poker Bot on 3/1/2009 12:05 PM (375 days ago)

Cool

Free Poker bot on 3/9/2009 7:15 AM (367 days ago)

You can use eazy way to get message from chat window.

1.Call SendMessage function to send mouse event to select text that you want.
2.Use SendMessage function to send Ctrl+C key event that poker stars will copy text into clipboard.
3.Use hook and inject your code into SetClipboardData function and take chat text.

Teco Li on 3/27/2009 6:18 PM (348 days ago)

detoured.dll was not found

I get this error when trying to open one of the poker clients!

any help on this one?

asdf on 3/31/2009 1:22 PM (345 days ago)

XPOKERBOTHOOK_API InstallHook(LPCWSTR pokerClientPath, bool installCBTHook)

DetourCreateProcessWithDll(

ADDED FULL PATH TO BIN FOLDER HOLDING DLLS

)

this fixed my issue

asdf on 3/31/2009 1:37 PM (345 days ago)

Although this is a different implementation, it reminds me of the OPenGL hacks used for CounterStrike, such as seeing through walls, etc..

pokerstars on 4/21/2009 11:09 PM (323 days ago)

I wonder where your ideas come from...

They must be a combination of a deep rooted curiosity and an insatiable urge to discover what is possible. Anyways, it's a great quality to have, but as I said before, I would like to see your intelligence/genius applied to other fields and blogged about. Lots of things can be reverse engineered.

Poker in California on 4/25/2009 10:55 PM (319 days ago)

Super post wanted to get in to this!

Greetings
Ralph
www.freerollsoftware.com

Freerollsoftware on 4/28/2009 6:00 PM (316 days ago)

This is pretty sick, I didn't know like 10% of what's written here. Kinda scares me that there may be some whiz kids trying to get an edge in this zero sum game.

Aced Poker on 5/10/2009 2:32 PM (305 days ago)

Well I am using Vista, and when I detour both text functions on Poker Stars I can replace and edit the text, but not the text of the chat window.

I think Poker Stars has smartened up and taken another approach to drawing the chat window text.

That or it is vista. Anyone have evidence contrary to this?

John on 5/29/2009 8:48 PM (285 days ago)

those images rocks, your unbelievable!

Great job

Poker Bot on 6/8/2009 9:35 AM (276 days ago)

I also cannot monitor the chat by detouring the text draw functions in the way described above in Poker Stars using Vista... Are there any other possible functions which I could detour to have more success?

Jeff on 8/15/2009 10:19 AM (208 days ago)

Hi everyone,

I would like to congratulate James Devlin because your web site is really amazing. From the technical point of view, it is 7 stars, not 5. It is outstanding.

Right now, I would like to find someone to cooperate with me on a poker software project whose goal is to improve SNG table selection (Pokerstars). As James Devlin states, table selection is very, very important and I need to improve that area. What I am looking for is someone that is able to develop a software that retrieves the content of any listbox on Pokerstars. I have already developed all the software needed to evaluate if we should or should not register on each specific tournament taking into account some statistic criteria ....

If you are interested, please post your email on a comment and I will contact you afterward.

Poker of Aces on 11/22/2009 9:52 PM (108 days ago)

Hi james.
Fisrt of all I would like to thank you for all those wonderful articles !

But I have a problem.
The same problem as Jeff and John, that is to say that I have successfully detoured the function DrawTextEx and ExtTextOut(and DrawText and TextOut to be sure Smile ) and some of the information in pokerstars or fulltilt are well changed by my own function, but the chat (mainly) is not changed at all.
I was so wondering if it exists other function that I should detoured to get the chat text.
Thank you one more time for your articles Smile

ghostichou on 11/24/2009 8:36 PM (106 days ago)

Hey Poker of Aces,
I work on another Poker project. I focus on cash games. But maybe we could exchange some knowledge and how to investigate new Poker rooms.

Feel free to write me an email: Klabautermann176@web.de

Klabautermann on 11/25/2009 8:18 PM (105 days ago)

is it possible to build a bot for facebook texas holdem to winfacebook poker chips

facebook poker chips on 12/9/2009 10:10 PM (91 days ago)

I would still like to know what Poker Rooms the technique works more effectively on? And how much testing have you done with Rakeback sites?

James on 12/11/2009 10:06 AM (90 days ago)

What a amazing idea.

Unibet on 12/14/2009 1:13 AM (87 days ago)

To all those people asking stupid questions like "is it possible to create this bot on facebook". Did you read anything or are you just one of those lazy, silver spoon fed, wastes of oxygen? This guy takes all this time to write an amazing read and the only shit you can ask is that? GO AWAY.

Ryan on 12/23/2009 4:44 PM (77 days ago)

To those of you who struggle trying to extract the chat text from the FullTilt software, know that since some months ago, their software is built using QT (v4.5.2 if I remember correctly).

QT is an application and UI framework available here: http://qt.nokia.com/

Since you can download their SDK (c++ source files) and access all the documentation, It becomes very easy to reach our goal.

In a nutshell, our injected dll should:
1- find the chat window's handle
2- call QT's QWidget::find() to get a QT Widget object from the handle
3- cast it to a 'QListWidget'
4- check the 'items' property
Bingo ! you get all the text in the chat window !

F.Nguyen on 1/21/2010 6:52 PM (48 days ago)

@ugg boots online
The way I do it is I check every X milliseconds if the Fold button is visible

F.Nguyen on 1/26/2010 1:53 PM (44 days ago)

Hello F.Nguyen on 1/21/2010 6:52 PM (10 days ago)

Do you have some functional code to hook Full Tilt on QT platform?

--------------------------------
I couldn't get anything to work. Also tried getting evaluation copy of Test environment from squish. They replied:

------------------------------------
Hello John,

thanks for your evaluation request. Before I create the trial account I better check one point to save yourself some time: is your goal to test the Fulltilt Poker Client? We know its recent versions are Qt-based so Squish for Qt would theoretically is a good fit. To protected unwanted usage the makers of the client have build in some protection that make it impossible to let it be driven by a test tool like Squish.

If you are after testing something else just let us know and we'll issue a license key.


John on 2/1/2010 5:37 PM (37 days ago)

@[John on 2/1/2010]

If youre only talking about the hooking process, I use Easyhook (http://www.codeplex.com/easyhook) since the greater part of my work is in C#.

If youre talking about finding the windows handle in the FullTilt application but just seeing one big QWidget (with Spy++ and such), try putting QT_USE_NATIVE_WINDOWS=1 in your environment variables (doc says it has a performance drawback, but didn't see it)

If you can be more precise on your question, I may help you more, perhaps with some bits of code (entirely C# and managed C++) ...

F.Nguyen on 2/2/2010 1:06 PM (37 days ago)

Thanks for the reply F. Nguyen.

I want to hook chat text and player names from the Full Tilt table. If I run spy++ on the table with QT_USE_NATIVE_WINDOWS=1, no Paint or Text Draw API's are called from the chat control. On any of the controls, the only useful API's are related to mouse movement.

My main question is "How do I get to the QT properties" that you mentioned?

If you have some code which does that in current Full Tilt environment, please send.

I used to have a nice working native C++ DLL for Full Tilt, but their switch to Qtool killed it.

I still have a nicely working hook DLL for i_Poker and Cereus and would be willing top trade if you could send me some working code for Full Tilt.

My email is jbrown@insight-pokerhound.com

Thank you.

John on 2/2/2010 11:24 PM (36 days ago)

@John

TextDraw or other text API's are in fact no longer used by the FullTilt Software.

The starting point to get QT Properties is to use the QWidget::find() method.
It takes a window handle as parameter and returns a QWidget (base class for controls) which you can cast to the correct QT type.

I dont have the code with me but here is an example as I remember it which clicks the fold button using QT: (hwnd is button's handle)

QWidget* widget = QWidget::find((WId)hwnd);
QPushButton* button = (QPushButton*)widget;
// button->animateClick();
return QMetaObject::invokeMethod(button, "animateClick", Qt::QueuedConnection);

note: I use QMetaObject::invokeMethod() instead of directly calling button->animateClick() because I call it from a worker thread

F. Nguyen on 2/3/2010 12:05 AM (36 days ago)

Have you tried extracting text, because Full Tilt has placed counter measures into their application.

Even Squish, which is a fully functional real-time test environment for QT, won't work.

If you can send me some functional code for Full Tilt, I'll trade source code for i_Poker and Cereus.

My DLL for i_Poker and Cereus has three easy to use functions and I'll exchange source code for functioning source code on Full Tilt.

The Full Tilt mouse events I can get, but I can not access any text, because I believe Full Tilt has blocking mechanisms built into their application.

If you want to see a demo of my i_Poker and Cereus DLL before trading code view demo: www.insight-pokerhound.com/.../...d_hook_demo.html

or download test app: www.insight-pokerhound.com/ubuild/Demo_Textbox.zip


I hope you can send some working code for Full Tilt, as I urgently need it. Otherwise, I'll have to resort to screen-scrape for text.

John on 2/3/2010 1:31 AM (36 days ago)

QWidget* widget = QWidget::find((WId)hwnd);
QListView* listview = (QListView*)widget;
QAbstractItemModel* model = listview->model();

for (int i = 0; i < model->rowCount(); ++i)
{
QString s = model->index(i, 0).data(Qt:LaughingisplayRole).toString();
System::String^ string = gcnew System::String((wchar_t*)s.utf16());
chatLines->Add(string);
}

F. Nguyen on 2/3/2010 9:39 AM (36 days ago)

@F. Nguyen

Are you using it in a project with Easy Hook? Is it possible to send entire project to my email. I haven't used easy hook at all. My prior hooking has been via Native C++ dll. I can trade source for my C++ DLL which you could add to your project.

I'll have to go through Easy Hook documentation.

Thank you

John on 2/3/2010 12:00 PM (36 days ago)

@John

I'm not comfortable sending my whole project but I'm willing to extract the core (dll injection, QT stuff, etc).
I'll try to make a basic working application with what I've got.
Don't know when I'll be abble to do it, but I'll keep you posted.

F.Nguyen on 2/3/2010 1:07 PM (36 days ago)

@F. Nguyen

Thank you - I look forward to trying it.

I'm looking into easy hook in mean-time -

John

John on 2/3/2010 1:47 PM (36 days ago)

@F. Nguyen
Please - what libraries do I need to include, to access QT functions in .Net project.

I downloaded QT SDK and .NET extension, but can't get .Net accessing it.

John on 2/3/2010 5:16 PM (35 days ago)

Hi John

For QT, I used the normal QT SDK v4.5.2 (if I remember correctly (the same FullTilt uses))

My .Net project interfacing QT is a C++ Managed Project so it can be called from other C# project and it can call QT stuff.
You'll need to add '...\QT4.5.2\includes\' to the Additional Include Directories and add '...\QT4.5.2\lib\' to Additional Library Directories

Do I answer your question right ?

F.Nguyen on 2/4/2010 4:28 PM (34 days ago)

Yes - Thank you F. Nguyen

I'll go in and try that - see if I can get it working...

John on 2/4/2010 10:52 PM (34 days ago)

Hello F. Nguyen

I got the QT SDK compiled for Visual Studio 2005 after many attempts and much research on nmake error codes -- that SDK doesn't compile and integrate easily.

Tried the

QWidget* widget = QWidget::find((WId)hwnd);

using hwnd for chat control from non-injected code - but QT function returns that expression cannot be evaluated.

Do the QT calls have to be done from code injected into Full Tilt? Or is there something else I need to do. - nothing is working very well. -- please help

Anonymous on 2/7/2010 10:12 AM (32 days ago)

Hello John, your 2 problems are related:

First, you don't need to compile QT. You just need the includes and lib files for compiling and linking. At runtime, you will use the same QT dlls the FullTilt app uses.

Second, you need to call QT from your injected code. It should resolve the QT dlls to the ones in your FullTilt installed directory (hence my first point).

Hope this helps

F. Nguyen on 2/7/2010 2:21 PM (32 days ago)

Hello F. Nguyen

Thanks - Are you interested in the code exchange, I suggested earlier. My C++ DLL works great on i_Poker and Cereus. It also contains old Poker Stars section that could (with some detective work) be adapted to their new chat control. It contains three public functions that can be easily called from managed code.

I really need an example project for the Full Tilt QT hook, as I have little experience with QT and Easy Hook. The example project would be a huge time saver for me. And if you need to hook i_Poker or Cereus, my project would be a huge time saver for you.

John

John on 2/7/2010 7:00 PM (31 days ago)

@John

See the email I sent you

F. Nguyen on 2/7/2010 10:46 PM (31 days ago)

The combination of text and screen shot pictures is just amazing. Thanks for sharing. Oh! I also want to share that recently a top rated site offers ultimate bet rakeback games with 60% rakeback offer. Enjoy it!

Poker on 2/8/2010 3:05 PM (31 days ago)

Hi!

@F. Nguyen
Thank you very much for your hints!
I understand the theory, but... it seems like I need some more hints, please!

QWidget::find() needs a handle as an argument, but since the handle of the Chat's widget is never the same, how can I automatically retrieve that handle for each poker table? (yes, I'm a beginner... lol)

I was thinking about eventually disassembling with OllyDbg in order to get directly the objectName property of the Chat's widget, but then I can't find a way to use that objectName to reach my goal...

So, can you help me a little bit please? Smile

PoWaZ on 2/10/2010 2:26 PM (29 days ago)

@PoWaZ

The easy way (maybe not the more effective, but the more comprehensible) is to have another worker thread watching the opening and closing of poker table windows.

For example, say we have a thread whose work is to keep track of all the windows on the desktop and trigger events when it finds a new poker window (and its handle) (and when a window closes).

Hint: have a look at these win32 api methods: GetDesktopWindow , FindWindow, GetWindowText, GetWindowInfo, etc.
Determining wheter a window is really a poker window can be achieved just by getting its text (window's title)

Good luck !

F.Nguyen on 2/10/2010 5:30 PM (28 days ago)

@PoWaZ

... and when you have poker window handle, finding the chat window handle is just a matter of enumerating the poker window's child windows to find the chat, given the chat window's size per exemple.

F.Nguyen on 2/10/2010 7:03 PM (28 days ago)

@F.Nguyen

Thank you so much for helping me so quickly! Smile
I thought there was a special trick for QWidget::find() to directly find the Chat's widget ^^

I use Qt 4.6.1 with VS2008 and the VS2008 Qt Add-in, and I code in C++.

Finally, I'll try that code:
HWND hwnd = FindWindow(L"QWidget", NULL);
QWidget* widget = QWidget::find((WId)hwnd);
QListWidget* chatBox = (QListWidget*)widget;
// I've found that QListWidget is easier to use than QListView

Then I'll use chatBox->addItem() in order to display each line of the chat into my chatBox. Later I'll be able to filter out only the important chat lines.

Once again, thank you F.Nguyen, it is time to code now! Smile
Bye !

PoWaZ on 2/10/2010 8:50 PM (28 days ago)

@PoWaZ

I don't understand what you're trying to do here. So let me comment on some points.

1. HWND hwnd = FindWindow(L"QWidget", NULL)
There will be plenty of windows of class QWidget. In fact, all controls in a QT `form` are QWidgets, and so is the chat window.

2. QListWidget* chatBox = (QListWidget*)widget
You cannot cast the QWidget to something it is not. The reason why I cast it in a QListView is because it is a QListView

3. chatBox->addItem()
Do you want to read the chat window's text or do you want to add text to it ? I dont see why you would add you own text to the chat in the Fulltilt app.

I tell you all this because it took me some weeks to succeed in getting the chat text, but reading your post tells me that it could take you even more Smile. Don't want to offend the beginner you say you are here, but if you could tell me what you are trying to achieve, I could give you some more insightful hints.

Peace




F.Nguyen on 2/11/2010 11:30 AM (28 days ago)

@F.Nguyen

Yeah, I agree it is hard for a beginner to find a QWidget with injected code and use Detours to intercept its content... lol
It is even more difficult when there are so few articles about using Detours, and using models in Qt 4, that's why I use a QListWidget instead of QListView ;)

1. I thought a good starting point would be to list all the windows of class QWidget, and then to sort them and identify the poker tables

2. Hum... it seems I have to learn how models work in Qt in order to use QListView with Qt 4.

3. In fact, chatBox is my QListWidget where I wanted to "paste" the content of the FullTilt chat.

I'm lost, because of the use of Qt by FullTilt... I understand this article James wrote, but it seems the use of Qt changes a lot of things.
I don't even know what Detours has to attach: before Qt, it was the DrawText function Detours has to attach, but now?

I'm sure it is (I hope) less complicated than I think, anyway I'll go on searching and practicing everyday until I manage to reach the goal! Laughing

Again, thanks for your help!!

PoWaZ on 2/11/2010 2:41 PM (28 days ago)

It seems we don't need to use Detours anymore, for FullTilt, thanks to Qt.
Is that true?

PoWaZ on 2/12/2010 5:58 AM (27 days ago)

In fact, we need Detours to inject our dll, but we don't need to detour any function.

PoWaZ on 2/12/2010 7:14 AM (27 days ago)

@PoWaz

Indeed we don't need to hook any api to get the chat text.

But we still need to inject our dll, with Detours or other.
I personnaly use EasyHook (I work with C#)

F.Nguyen on 2/12/2010 12:06 PM (27 days ago)

Greetings to you all...

First of all, let me say: excelent thread here !

I'm the author of a well known GUI configurable pokerbot, fully written in Borland Assembler (TASM), (for maximum performance, and downsized system resource consumptions).
I prefer not to reveal the project name, because i've managed to keep my methods stealth from the "anti-cheating" site measures, taken against most of the commercial bots out there.
I have my project divided on 2 major classes:
The Client: Without using any dll, i managed to inject code on the main window of the poker client, this way the code will stay stealth and actually part of the poker client, avoiding detection, it will be responsible for collecting data and send it through TCP to the server.
The Server: Usualy placed on a remote machine, once again to avoid detection, it collects data sent from the client, calculates decisions according to user configurations, and finally returns it through TCP back to client.

Now for the challenge...
I had it working for years now on most of the known poker sites. Because i'm no fan of screen OCR and the algorithms are heavy / hard to implement in assembly, i've always used the principles of "detouring" winapi calls like TextOutA, TextOutW, ExtTextOutA, ExtTextOutW, DrawText and whatever needs...
The relevant fact is, i do this before the poker client starts, on a global wide level, this way, the poker client IAT (Import Address Table) will point to my api functions and no further hacking is done.
As mentioned back on this thread, Full Tilt now uses QT from (Trolltech/Nokia), i've managed to use their own poison against them, and with simple TASM code in combination with QtCore4.dll and QtGui4.dll managed to read the all the data needed.
But this aproach is not, by far, my chosen one, what i would like you guys familiar with QT to point me in, is:

How in the hell QT engine is writting text on DCs avoiding API calls like TextOut and whatever?

And what are the function names on QtCore responsible for such a thing?

Assuming they must be there, i've been getting nuts on the past few weeks targeting them...

Any help apreciated...feel free to query for any extra explaining.

Thx, Again

Xembly on 2/12/2010 2:00 PM (27 days ago)

@F. Nguyen

Thanks for confirming my thoughts :p

And finally, I will use: GetWindowTextLength, GetWindowText, EnumWindows, and strstr() in order to find the poker tables' windows.

Also, I've understood how to use a QListView thanks to a model ;)


I've never programmed in C#, and there are more examples, more books, and more articles for Detours than EasyHook.
But anyway, one day I'll move to C# and EasyHook.

PoWaZ on 2/12/2010 5:11 PM (26 days ago)

@Xembly

Did you use code caves in order to inject your code on the main window?
If that's the way you did it, is the use of code caves really safe and not detectable?

I'm sorry but I can't help you about the function used by Qt...

PoWaZ on 2/12/2010 5:36 PM (26 days ago)

@F. Nguyen

I've found all the names of the QWidgets used by FullTilt, so it's even easier than using QWidget::find() Laughing

The name of the QListView corresponding to the chat window is "chatview".

PoWaZ on 2/15/2010 9:20 AM (24 days ago)

@PoWaZ

Yes you're right thank you !

My method for finding the window is quite old, didn't came back to improve it. Now is the time !

Thanks for sharing

F.Nguyen on 2/15/2010 10:57 AM (24 days ago)

@F. Nguyen

The following widgets' name might interest you... ;)

"playerName1" to "playerName9"
"playerStack1" to "playerStack9"
"totalStack"

See you!

PoWaZ on 2/15/2010 4:10 PM (23 days ago)

@PoWaZ

It might be very interesting indeed ... I was getting the hand history file and replaying the previous hand to obtain the current stacks !

This will same me a whole lot of code ... and deleting code is paradoxically always a rewarding part of writing code.

Thanks

F.Nguyen on 2/15/2010 5:23 PM (23 days ago)

This will save me a whole lot of code
of course

F.Nguyen on 2/15/2010 5:28 PM (23 days ago)

@F. Nguyen

In fact, we can find the name of any widget by disassembling FullTiltPoker.exe with OllyDbg 2.0 :p Spy++ is useless now...

I wanted to ask you something: do you think there is a way to list all the children of a given QWidget's name (window title)?
This way, we could easily find the name of all the widgets present in each FullTilt's window, it would be very useful you guess it Laughing

PoWaZ on 2/15/2010 5:39 PM (23 days ago)

@PoWaZ

Didn't see how to list the child widgets of a widget. I think there should be a way . I really don't know QT that much. A learn its features as I need them ...

I now there is an easy way to list all the child windows of a window (EnumWindows etc.) but I don't think it would gice the same results.

F.Nguyen on 2/15/2010 7:09 PM (23 days ago)

@F. Nguyen

Ok... nevermind Smile
Also, I wanted to know if you managed to create your own bot?
And I'd like to know what were the difficulties you encountered?

It is just in order to imagine how much time it will take me... ^^ Thanks!

PoWaZ on 2/16/2010 11:27 AM (23 days ago)

@PoWaZ

Didn't spend much time on AI yet so the botting capability of my software is limited to 'All-in or fold'.

I'm working more on the decision making helper side of it.

Anonymous on 2/16/2010 6:18 PM (22 days ago)

was me !

F. Nguyen on 2/16/2010 6:18 PM (22 days ago)

@F. Nguyen

I found a way to list all the children of a QWidget: we can use QObject::children() which can return a list of all the children of any QWidget.

I'll create a testapp and see if it would work for finding the names of all the widgets in FullTilt. Then we could get any information that interests us, hide what does not interest us, ...

PoWaZ on 2/17/2010 10:32 AM (22 days ago)

@PoWaZ

Code caves is one way of code injection, but if you ask, IMHO, it's not fail safe, and easy to detect, the block of memory you found maybe reserved for later use by the process, a simple crc check may warn the client that something isn't as it should, and more, memory blocks change so often, and there are many other things which may happen that will mess up those "unused" memory blocks. So my advice to you, take a look at win api functions like WriteProcessMemory and CreateRemoteThread. Write the code, make sure it runs at a specified fixed address you provided, use WriteProcessMemory to copy the code to the remote process using the same address your code has, use CreateRemoteThread to start the remote thread and... voilá !!! You got yourself your thread running inside the address space of the remote process.
Clean and easy as it sounds, no dll injection needed, no ghost windows, making detection almost impossible, (if you know what your doing!)... of course i'm doing this with pure x86 Intel Assembly and TASM, it gives me much more control over fixed memory addresses, but this can also be achieved with C and others.

Good luck.

Xembly on 2/17/2010 10:57 AM (22 days ago)

@Xembly

I've never used WriteProcessMemory with CreateRemoteThread, since we need to know the assembler language in order to debug, etc. I'm a beginner! ^^

But that's an interesting technique that I'll use later, I think Smile

For now, in my project I use DetourCreateProcessWithDll in suspended mode, then a SetWindowsHookEx, and a CBT hook.

PoWaZ on 2/17/2010 3:02 PM (22 days ago)

@Xembly

I don't like code caves too for the same reason of you. I made a good work in injecting my code in a poker client that hooked WriteFile function in this way :

- allocate a small memory block (1Kb is more than enough) in the process memory with VirtualAllocEx with RWE permission because i hold there even variables.
- find with GetProcAddress all the functions I had to call and put them inside the "placeholders" in my function.
- WriteProcessMemory of variables and function inside the block I allocated before
- find the proper place for hook (in my case, i found WriteFile function inside kernel32.dll and found the call for ZwWriteFile, then replaced it with a push <hookaddress> - ret. 6 bytes just like "CALL DWORD PTR:ZwWriteFileAddress").

With this method, I injected code that is good even for future updates of the client Smile As a matter of fact, the poker client was updated at least two times since i wrote my simple app and still works like a charm.

Last but not least :

1) I didn't need to change a single byte of the poker client code, since the only modification to existing code is made in kernel32.dll.

2) My choice to place the hook deep inside the WriteFile function instead of hook it at the beginning make the detection of the hook more difficult.

My code works on XP, to make it work on Vista and 7 it would need, probably, some tweaking.

Obviusly for a secure injection I need to suspend the threads before the injection and resume them immediately after that.

The little snippet of code injected is in assembly for size and handiness.

Bye!

PushItNow!

PushItNow! on 2/18/2010 7:43 AM (21 days ago)

@PushItNow!

It's fabulous to be able to discuss so many opinions and methods of aproach, like the one you mentioned above.
I'm assuming you must be hooking writings to the log file, please correct me if i'm wrong, however, before i've made a life with botting, i was sleeping with the "enemie", and hunting down targets like us, thats right, i was a member of a development team of a regular poker site and my experience at the time lead me to one of my first counter-measures, delaying more than enough the dumping to the log file of the current hand, (the chat window was fully owner draw, therefore API unreadable), so my advice to you is: concentrate your programing skills in analysing the DCs, believe me, if your eyes see it...thats because it's really there! And most of the known sites are daily learning with each other to enforce protection against bots, so it's just a matter of time.
Another neat approach, a bit harder to implement (at least in pure x86 assembly), that i'm considering to work on, relies on back-propagation neural networks, with the correct training (learning curve path), my guess it will be somehow between the most steady and generalistic ever solution made for this subject, and i'm taking the necessary steps to provide it. Imagine a self configured scrapper that educates himself to most of the changes sites do to their GUI, all a child must do is answer a few questions each time something has changed on the GUI.

Keep up the good work,

Cheers

Xembly on 2/18/2010 11:58 AM (21 days ago)

@Xembly

Hi!

Yes, i was abviusly dumping the log Smile. In that particular venue, anyway, the log was written in real time, so i could see my hole cards istants before they were actually draw on the client. Anyway I'm focusing right now (like many of us here Smile) on capturing the majority of information from FT client... By now i'm in the phase of "know your enemy" Laughing, so i injected a little dll to dump all the info i need from the widgets displayed (classname, object name, screen coords, size, labels etc. - still working on it), and next step is dumping the chat...

all the info we need (i think) is :

- hole and board cards (from the chat)
- player names and stacks (and i think this could be taken from the widgets)
- dealer button position (still working on it)
- position of variuos buttons and sliders for actually perform actions (widgets)

Once we have this i'll focus on all the intel needed Smile

Very interesting the prospective of use a "learning client" for the GUI changes... Sure it's hard, but there's even to consider that (at least i think) it would be unusual for a poker venue to make big changes to the gui just for "disguise" against automated software... many regular players would complain for "too much intrusive" changes to the gui.

Bye!

PushItNow!

PushItNow! on 2/19/2010 6:54 AM (20 days ago)

@PushItNow!

If you want to know the names of the different widgets used in FullTilt, you have 2 choices :
- using OllyDbg 2.0, you can find any name by searching for all the referenced strings (chatview, playerName1, playerName2, ..., playerStack1, playerStack2, ..., totalStack, controller1, controller2, etc)
- or using QObject::children() which returns, you guess it, a list of all the children of any QObject.

I've tested these 2 solutions, and it works like a charm Smile

I have to work more on the injection, that's my only problem actually: finding the right injection method... So I test, I test, I test, ...

I can't do it in assembler...

PoWaZ on 2/19/2010 8:36 AM (20 days ago)

@PoWaZ

Hi!

Eheh, i had already digged thru the code of the client in the debugger and found all the names you state, and my little injected .dll do exactly what you say :
- Send to the .dll a HWnd of a table window
- QWidget *widg = QWidget::find(HWnd);
- QObject *qobj = (QObject *)widg;
- QObjectList objl = qobj->children();

Then I dump all the info of the items in the list (at this time I get classname, superclassname,objectname, and x,y,h,w if it's a visible widget), and on any object i obtain (if available) and dump an objectlist, so i get all the nested objects.

This way a get a "tree" of all the objects on the table.

The strange thing is that i get plenty of info (coords and classnames of the chat window, of all the buttons etc.) but i don't get anything with the name "namePlayerX","stackPlayerX" and all the names i got from ollydbg.

I tried to do QObject::findChild() too but nothing!

Can you give me (if you have it) some info like which class are the "namePlayerX" widgets ?

Regarding your tests, I suggest you to read a very good book, that's the same that the author of this blog suggested all of us to read, that is :

Windows via C/C++ by Jeffrey Richter.

It covers nearly evertthing in Windows System Programming, starting from processes and kernel objects, and it has chapters on the various methods for injecting code.

Best Regards!

PushItNow!

PushItNow! on 2/20/2010 7:59 AM (19 days ago)

@PushItNow!

It is not "namePlayerX" but playerNameX, and playerStackX ! :p

playerNameX is a QLabel, and is a child of a GridLayout (I don't know its name at the moment)
playerStackX is also a QLabel, and a child of the same GridLayout.

In fact, all the QWidgets of the poker table (names, chips, pods, avatars, ... ) are the children of that GridLayout.


Thank you for your suggestion about Windows via C/C++, I tested the CreateRemoteThread technique for loading my code into the memory of the target process. It worked for some simple apps, like Notepad, but it didn't work for mIRC, Firefox, ...

But I wanted to know: how can a process know that you injected a "bad" DLL?
I mean... we know that they can't block the injection of DLLs, and if the name of the DLL is ordinary or the same of a famous antivirus, how can they know that your DLL is related to the use of a bot?

I understand that probably the best technique is "CreateRemoteThread/WriteProcessMemory" but if we inject our DLL using the Detours lib (after the removal of detoured.dll), in a virtual machine, are there more or less risks?

PoWaZ on 2/20/2010 11:26 AM (19 days ago)

Is seems that the objectName of the GridLayout is simply "table_layout".

PoWaZ on 2/20/2010 2:02 PM (19 days ago)

@PoWaZ

Hi there, seems you're progressing well on decoding FT... i'm glad you do.

Regarding some of your questions,

"...It worked for some simple apps, like Notepad, but it didn't work for mIRC, Firefox, ..."

It should work with any win32/64 process, believe me i use this technique for years now with my commercial bots in several clients and does the job like a champ. You mentioned earlier not to be familiar with assembler, that's no problem, i only use assembly language because i'm a resource saver maniac!! and a bit crazy though!! It makes my life funnier!!
So i'll give you some hints:

.Of course a bit of assembly knowledge is needed for detouring, but i guess you all ready know that.
.You need to open the target process with some privileges - (PROCESS_ALL_ACCESS)
.Grab some memory info about your source module - (GetModuleInformation)
.This will fill out a (MODULEINFO) structure, where you will find the (EntryPoint) and (SizeOfImage) of your module.
.Now this is very important - You must allocate (SizeOfImage) memory on the target process address space at the exact value of your module (EntryPoint), this will assure successful code reallocation (assembly jmps, calls, whatever...), so it's your responsibility to force your process to run on non-colliding source/target memory addresses.
.Ok, now we have everything we need to inject our source thread on the target process, for that we will use WriteProcessMemory on the target process passing our (EntryPoint) as source address and our (SizeOfImage) as size. This assures that everything our thread needs will be copied, (constants, variables, whatever...)
.And now we can call (CreateRemoteThread) passing the thread address that we want to remote trigger.
.That's it... you'll have your code running undetected inside the remote process.
No dll injection, code caving, whatever... and when the process terminates your injected code will terminate too.
.You can wait for your thread remote start using (WaitForSingleObject) and passing the handle resulting from your previous call to (CreateRemoteThread) and timed for 0 milisecs of wait (forever).
.Don't forget to close your process handle to avoid leaks.

As for your second question:
"...how can a process know that you injected a "bad" DLL?..."
With my experience at the time of "sleeping with the enemy" that's the easy part, i remember one involving scrolling down the process's loaded library list, getting the path of each one, getting the file version, calculating the crc check of the file and finally cross-comparing the result with a database we had, this technique was used to blacklist a lot of "injections".
Other involved fixed memory crc checks of some suspicious processes found running on several machines.
On virtual environments like VMWare it's even easier, there's an exposed function from a VMs library i don't recall who logs the history of everything ran and still running on the session, so there's no point.
So this is why i left "dll injections", parallel processes and virtual environments for good!

And i've done well on the past 3 years.

Hope to have helped some.

See you.

Xembly on 2/21/2010 12:23 AM (18 days ago)

@Xembly

Thank you very much for helping me!
Now I better understand how it works Smile ...and I better understand the power of this technique too.

I'm progressing slowly, but I prefer to take the time and use the best method to inject my code. ;)

PoWaZ on 2/21/2010 1:04 PM (18 days ago)

@PoWaZ

Hi!

I see that Xembly gave you an excellent answer to your thoughts (anyway better that what would have been mine Smile )
Anyway, i'm keeping on my efforts for getting info from FT client.

Seems like (and I would be glad to be wrong) that all that widgets you listed me are actually not used, at least on my tests... I placed many breakpoints on the code that creates all that widgets and the apps never encountered them while running, when open tables (play money, real money, sng, tourn lobbies and hand history). I don't know the reason for that code been there but looks like it was never called. From my little app that inject a "info-dump" .dll i got many infos... Just to tell, this is the initial part of the dump of a table window I had :

Class : CTableViewGamePlay
SClass : CTableViewRenderer
Name :
X: 291 Y: 79 H: 572 W: 831
Class : QWidget
SClass : QObject
Name :
X: 0 Y: 0 H: 572 W: 831
Class : QWidget
SClass : QObject
Name : qt_scrollarea_hcontainer
X: 1 Y: 451 H: 28 W: 610
Class : QScrollBar
SClass : QAbstractSlider
Name :
X: 0 Y: 0 H: 28 W: 610
Class : QBoxLayout
SClass : QLayout
Name :

And so on... The indentation (that I hope it is well rendered with the font used on this blog) is due to parent-child relation of widgets. All buttons are listed in the remaining of the dump, as well as the chat window, but no reference to QLabels, players and stack widgets... This is frustrating, but the road is taken, and let's see where it will take us...

Best Regards!

PushItNow!

PushItNow! on 2/21/2010 3:57 PM (17 days ago)

Ok, the indentation is not visible Smile I post it again to show better :

Class : CTableViewGamePlay
SClass : CTableViewRenderer
Name :
X: 291 Y: 79 H: 572 W: 831

--Class : QWidget
--SClass : QObject
--Name :
--X: 0 Y: 0 H: 572 W: 831

--Class : QWidget
--SClass : QObject
--Name : qt_scrollarea_hcontainer
--X: 1 Y: 451 H: 28 W: 610

----Class : QScrollBar
----SClass : QAbstractSlider
----Name :
----X: 0 Y: 0 H: 28 W: 610

----Class : QBoxLayout
----SClass : QLayout
----Name :

And so on...

PushItNow! on 2/21/2010 4:00 PM (17 days ago)

@PushItNow!

Maybe the widgets I listed are used by another window :/ (tournament, heading, ...)
In OllyDbg, I think if we search for the number "9" (for example - 9 players) in the referenced strings, we should find the correct names of the widgets that interest us. I've just found: Xth, pdPayoutLabelX, pdPayoutX, payoutLabelX, payoutX, chipRatioX, icmX, customPreX, customPostX, customX, playerX.


Once again, I've got a question about the injection... :p
I read some articles about the use of CreateRemoteThread with VirtualAllocEx, WriteProcessMemory, ... but they always use this technique to inject a DLL using LoadLibraryA, and this is not what we want.

In Jeff Richter's book, he also uses this technique to inject a DLL :/


I think the best thing to do would be to start from scratch ("good luck!")

PoWaZ on 2/21/2010 8:34 PM (17 days ago)

@Powaz

Jeff's article (at least the one i read), concerning dll injection and CreateRemoteThread shows a technique on how to bypass the blocking of external blacklisted dll injections, by using an injected thread that actually does the job (loadlibrary), so there's no point, once you gain access to the external process's address space and being able to inject your own thread, all you have to do is copy the necessary code starting at (EntryPoint), sized by (SizeOfImage), reallocate it (necessary if the target address differs from the source address) and fire it up!
Unfortunately this technique is also used by some of the worst malware threats ever made, so if your having trouble on implementing it, my guess and advice to you is:
.Check your AV to see if it blocks suspicious code execution, if it does, add an exception for your exe.
.Check DEP (Data Execution Prevention), add an exception to your exe.

You wont find a lot of information about this technique on the web because it's power and stealthiness may lead to misbehaviors in the wrong hands.
Windows API is far from being bullet proof, and this type of coding exploits security gaps to the extreme, one of my customers called it "The Invisible Man On The Middle".

But in time, with some patience my guess is you'll get there.

Call back if you need.

See you.

Xembly on 2/21/2010 10:27 PM (17 days ago)

@Xembly

Thanks to you, I've googled a lot and I've just found some articles about code injection. One article is about the making of a rootkit... -_-' so, I think I'm the right way! lol

Thank you, and see you!

PoWaZ on 2/22/2010 8:59 AM (17 days ago)

... I'm ON the right way

PoWaZ on 2/22/2010 10:13 AM (17 days ago)

@Powaz

Happy to help you!

Root-kits aren't the kind of approach i'd advise you to use. The basic need of a root-kit is to load everytime your system boots and remain stealth, it involves driver development skills, if i were you, i wouldn't go that far...
Of course some of them use the described technique, but only to inject them selves for the first run, so my advice to you is to master the injection skill mentioned above, it will be more than enough to make your code stealth, and it will load only on user demand, keep your system clear from moding because "Worse Things Happen At Sea".
Anyway, if you're stick to the idea of a "friendly root-kit", i would advise you to get "Windows Internals, Fifth Edition" book, very intensive and gutter look inside the kernel, actually one of the best i've read.

Keep me informed of your progress.

See you.

Xembly on 2/22/2010 12:58 PM (17 days ago)

@PoWaZ

Hi!

Just an update to let you save some time.

The widgets we were talking about (playerNameX, playerStackX and so on...) are ALL part of a widget named "CTournDealDlg". Havn't seen that window, but I can say with good confidence that it is the windows that popups whenever the players of a final table in a tournament agree to make a deal... So, it's useless to our goal Frown . Better focus on something else.

Bye!

PushItNow!

PushItNow! on 2/23/2010 5:37 AM (16 days ago)

@Xembly

I'm progressing quite well now, thanks to your explanations ^^
My code doesn't work yet (some errors), but I know how to make it work, so it's just a question of time ;) Thanks

@PushItNow!
Sorry, I thought we found a good way to know the objectNames used by FT, but I was wrong :/
So, as Xembly said earlier, now we have to concentrate on the DCs...

I read that Qt uses QPainter::drawText() to display text on screen, maybe it can help.

PoWaZ on 2/23/2010 2:55 PM (16 days ago)

@Xembly

My code works! I'm very impressed by this technique... I can inject my code in any process now Laughing

But I need your advice regarding stealth. My code works like this:
- I use CreateProcess to launch the poker client, in suspended mode
- I inject my code using VirtualAllocEx, WriteProcessMemory, and CreateRemoteThread
- then I resume the process.

My question is: do you think it is better to open the target process (OpenProcess) and then inject the code, or to launch the target process in suspended mode, and then resume it after the injection?

I think the second solution might be the best one, but I'm not sure, that's why I'm appealing to your experience ;)

See you!

PoWaZ on 2/24/2010 9:20 PM (14 days ago)

@Powaz

I'm delighted with your progress.
Regarding your question, my advice to you, as odd as it may look is to use the first approach, why?...
Because it's easy through several API methods, or even by querying the registry, to find out who "owns the created process" or "who called the creation of process" or even "who opened the process".
One should try to act as normal as possible. I think your project should not be the trigger of the poker client, he should remain stealth attached to an innocent, randomly, and well known chosen process, wait for something to happen... like detecting a poker client presence, then he should take the necessary steps to inject himself on the target process (OpenProcess approach), this way even if the poker client tries to determine where the event came from, all it will see is a perfect, standard, well known process like an AV, firewall, browser, whatever...
Even if it checks for consistency/integrity on the caller process, like getting the exe/dll name for later crc check, it would still get the expected crc value, all of this assuming you have correctly injected your code through our technique on an innocent parallel process, used only as a host.
That said, i guess you understand now why i've been able myself to keep my bots running undetected for all this time. With this techniques and some skillful coding your bot will run virtually undetected!

Happy to help, keep on posting progress!

See you.

Xembly on 2/24/2010 10:30 PM (14 days ago)

@Powaz

Just a tiny add-on:

We don't expect a poker client process to close himself on a shelter avoiding memory scans from AVs, tcp packet inspections from firewalls, and so on...

That's why i stated "Virtually Undetected". Wink

Cheers Wink

Xembly on 2/24/2010 10:42 PM (14 days ago)

@Xembly

I agree with you! I made some tests yesterday, after I posted my message, and it is very easy for a poker client to know if the process was "normally" created.
It just has to get its parent PID, and compare it to the explorer's PID. If it doesn't match, then the poker client can easily identify the process who created it, and get as much as information as it can... :/

For your solution, I think my host will be cmd.exe, so I can get some feedback with just some couts (for testing), then later my host will be an antivirus (the best host app I think) :p

See you later!

PoWaZ on 2/25/2010 7:16 AM (14 days ago)

@Powaz

Yes, that's why i advised you to use (OpenProcess) from a randomly picked running host, preferably one that was trigged on boot, then i cross-compare it with a table of my creation, containg names of several well known processes, if it's there then i use it.
Randomization makes it even harder to detect.

Good job.

See you.

Xembly on 2/25/2010 1:08 PM (14 days ago)

@Xembly

I'm still amazed by your technique! Smile
Now I need to study how does IPC work. I don't think it will take a long time since there are a lot of tutorials available about IPC.

Here I go! See you next time Smile

PoWaZ on 2/26/2010 6:25 PM (12 days ago)

@Powaz

Way to go! Happy i could help. Wink
About IPC, as you all ready might know, there are several techniques, Pipes, Memory, WM_COPYDATA, whatever...
Personally, and if you're trying to notify your "decision center" about events on the poker client, i advise you to develop one on your own, you could use MapViewOfMemory to share data between processes, but keep in mind that those are easy targets... In fact i don't advise you to have a separated "decision center" on the same machine you're running the bot...
I use another machine as a decision center, so everything goes through sockets, however, if i choose so, and because all my client/server/monitor code share the same executable, i may:

.Tell my "monitoring center" injected on the innocent process, and always searching for poker clients, to act also as a "decision center" (server through TCP)
.Tell my "control center" injected on the poker client to communicate with the "decision center" server locally, by using (127.0.0.1 - localhost).

So basically, one executable that besides screen scrapping has everything i need!
But usually i have a second machine acting as a server.
Remember: same executable, different parameters = different behavior.

But again, it's your call, only wanted to bring some light on darkness...

Keep it up!

Cheers!

Xembly on 2/26/2010 9:52 PM (12 days ago)

@Powaz

Just a small edit:

What i meant here "same executable, different parameters = different behavior" is more like:
same executable, different thread = different behavior Wink

See you!

Xembly on 2/26/2010 9:56 PM (12 days ago)

@Xembly

Thanks for turning the light on! Laughing
I was thinking about using some shared memory, as it is the simplest and fastest technique, but I trust you. I'll use sockets Smile

Do you use a second machine for more stealth or because of some performance issues?
Just to know if I should install the server side on another machine...
Also, do you use some kind of encryption between the decision center and the control center?

PoWaZ on 2/27/2010 7:39 AM (12 days ago)

@Powaz

I use localhost server only for debugging purposes, i run a windows 2008 server as a "decision center". Because all my comercial bots can do "team play mode", it's the place where they store "team play rules".
Using SSL through TCP sockets provides the final barrier against poker sites counter-measures, it's not as fast as memory IPCs, but surely very secure and useful.

See you.

Xembly on 2/27/2010 11:00 AM (12 days ago)

@Xembly

I see... I didn't think your bot was so powerful ^^ You must make a good profit Smile
Ok, so my 2 modules will communicate through TCP sockets with SSL encryption, on 1 machine. Then later... I could adapt the bot for "teamplay".

I've still a lot to learn, but I think the making of a pokerbot is a very good project even for a beginner like me, and I really appreciate your help.

The actual step for me is to inject a server and a client into 2 different process and send some messages using SSL encryption.

Here I go! See you Smile

PoWaZ on 2/27/2010 8:11 PM (11 days ago)

@Powaz

Good luck, Wink then.

Post back if you need, i'll be happy to help.

See you.

Xembly on 2/27/2010 11:03 PM (11 days ago)

@Xembly

Now I've got a working multithreaded TCP server (1 thread for each client), and a simple TCP client. It works fine Smile
I've got to add SLL support now. It doesn't seem complicated, but maybe I'm wrong... let's see!

Have a nice day!

PoWaZ on 3/1/2010 9:21 AM (10 days ago)

@Powaz

You're doing fine! All the work you're having will result in a very stealth and stable solution.

Keep up!

See you.

Xembly on 3/1/2010 11:35 AM (10 days ago)

@Xembly

As I understand, you're advocating copying code into the target process address space. I have a couple of questions:

1. What form is the code you inject? You say its not a DLL so it must be an executable?

2. How do you ensure that the addresses match up between the source module and the target process (or if they don't how to you fix it?).

Thanks

Anonymous on 3/1/2010 12:56 PM (10 days ago)

@Anonymous

1. I'm using an executable that injects himself on an innocent host process (read my previous posts), and waits for interesting processes to reinject himself.
2. That depends on the directives your compiler accepts, in TASM you can achieve this by using "{$IMAGEBASE $XXXXXXXX}", where $XXXXXXXX is the virtual hex address where your PE (Portable Executable) will load. I always use values that won't conflict with target's used addresses, from time to time i change my base address for security reasons. And yes, of course this is valid for any valid win32 process (whether DLL or EXE).

See you.

Xembly on 3/1/2010 3:07 PM (10 days ago)

@Xembly

How do you ensure that the values you use dont conflict with the target process?

Anonymous on 3/1/2010 8:29 PM (9 days ago)

I just finished reading the thread between Xembly and PoWaZ and F.Nguyen and I'm super intrigued. I spent this last weekend trying to read some information from FTP (I'm trying to create a very simple non-playing bot just to see if I can) and just learned I was going about it the wrong way (I was hooking the Windows API calls) and just learned I really need to be looking at the QT libraries.

I'm new to C# development so it's been a challenge to say the least. I'm using C# and EasyHook. I really wish I had something useful I could share so that others might share with me, but you guys are so far ahead of me that I'm useless.

Anyway, this was VERY helpful and hopefully I'll get something built eventually.

Thanks!

MrM on 3/1/2010 10:54 PM (9 days ago)

@Anonymous
You should have a look at this article : www.codeproject.com/KB/threads/processinject.aspx It's the only article I've found about that injection technique. It might help you Smile

@MrM
I've created a fake poker client in Qt, for testing purpose... I'll let you know if I find a way to guess the objects names and read any content using code injection. Smile Anyway, it's just a matter of time!

PoWaZ on 3/2/2010 5:37 AM (9 days ago)

@Anonymous

By using "Process Explorer" from SysInternals you'll get about 80% of the used virtual addressing, the remaining 20% are used at runtime for data allocation, use a hook on each call to (VirtualAlloc) and (VirtualAllocEx) and write a log file with the results, you'll get a better picture of the target's memory.

@MrM

As i stated before, i'd advise you to stick with DC analysis, it's just a matter of time before more poker clients sabotage the use of API hooking. If you're interested on my injection method, i've posted a very clear description on how to implement it some posts back.

See you all. Wink

Xembly on 3/2/2010 7:46 AM (9 days ago)

@Xembly
Did you use OpenSSL for your IPC?

PoWaZ on 3/2/2010 11:31 AM (9 days ago)

@Xembly
I think I'll use SSPI Smile

PoWaZ on 3/2/2010 11:59 AM (9 days ago)

@Powaz

Both will do fine because you will develop your own client and server. However for compatibility issues, and because some other bot developers rented a connection to my "decision center" i'm currently using OpenSSL, you see, the supported cipher algorithms from SSPI are not as "wide" as those in OpenSSL. :-(

But, as i said both will do fine for you because you're in full control of the client and the server. Wink

See you.

Xembly on 3/2/2010 1:20 PM (9 days ago)

@Xembly

Got any info on DC analysis? I can't find anything online.

Anonymous on 3/2/2010 2:33 PM (9 days ago)

@Xembly

I've spent a whole deal of time with my teacher reading and testing your methods and all i can say is...you must the einstein of poker botting!!! Your skills, knowledge, and the way you planned your bot are enough reasons for a case study!
Great help you gave us! And we are finnaly going somewhere here... Thx to you!

See you too... and thx again!

PS: Are you using OCR, and if so, could you point us in the right direction?

Anonymous on 3/2/2010 2:38 PM (9 days ago)

@Anonymous on 3/2/2010 2:33 PM

DC stands for: Device Context, every visual window has one attached to it. Implementation of a poker bot (in a windows platform) envolves a lot of API knowledge, i advise you to google a little bit for this: "Screen Scraping" or "Data Scraping".

@Anonymous on 3/2/2010 2:38 PM

Glad to help. Wink
About OCR i use it only on FT, my own feature extraction algorithm written in assembly, i'm tweaking it right now to work with backpropagation neural networks for further use with all my bots.

See you.

Xembly on 3/2/2010 3:41 PM (8 days ago)

Hi there,

I have been trying around with that QT stuff lately. I was wondering how you bypassed the fact that the libs from 4.5.2 differ from the dlls FullTilt uses. Because some Functions do not execute because of wrong entry Points when you use the dll Files FullTilt uses.

Anonymous on 3/2/2010 7:52 PM (8 days ago)

@Anonymous on 3/2/2010 7:52 PM

Sorry, can't help. I'm not using QT, but for what i've heard you can always download previous versions from the TrollTech/Nokia site. That should do.

See you.

Xembly on 3/2/2010 8:51 PM (8 days ago)

@Anonymous on 3/2/2010 7:52 PM

That and some other issues left me no option but to forget QT. For a short period after FT's major update i was able to grab enough data, but that soon ended. My guess is some libraries may have been altered and recompiled on purpose, currently i'm OCRing FT, but also working on a clever way of doing this to avoid updating everytime FT GUI does.
About function entry points there are plenty of alternatives for you to get them right, "Process Monitor", "Dll Export Viewer" and many others may do the job.
Sorry, but as i said before, left QT for good!

See you.

Xembly on 3/2/2010 11:16 PM (8 days ago)

@Xembly
Yeah, I agree we don't need Qt at all... it seems we don't even need to code it in C++ or C#: Calculatem Pro is coded in VB and works great. -_- I'd really like to know how it works, maybe they use OCR...

PoWaZ on 3/3/2010 7:44 AM (8 days ago)

@Xembly
Ok, now I'm sure Calculatem Pro uses OCR: http://www.calculatempro.com/support.html#100

PoWaZ on 3/3/2010 8:15 AM (8 days ago)

@Powaz

I'll take a look "inside" Calculatem Pro, but my guess is OCR, on the last months i've discussed this subject with a whole deal of bot developers and they all state to be OCRing FT.

See you later.

Xembly on 3/3/2010 8:19 AM (8 days ago)

@Powaz

Yep, i took a "deep inspection", and you are obviously right, Calculatem Pro uses "BitBlt" and "PrintWindow" to grab a window snapshot, it then parses the necessary data using OCR techniques.

So, scrapping FT for now is all about DCs. Wink

See you.

Xembly on 3/3/2010 11:20 PM (7 days ago)

@Xembly
I managed to secure my sockets with SSPI, it works well... not as secured as if I used OpenSSL, but it does what it's meant to do ^^
Anyway, I'm still at the very beginning of a pokerbot, so I'll move to OpenSSL later. Smile

Now, I've got to inject the server and the client, and I'll check if everything is OK.

In my next step, I'll concentrate on the DCs.

Wow, I'm learning a lot of things thanks to you! lol

Thanks again, and see you later Smile

PoWaZ on 3/4/2010 10:27 AM (7 days ago)

@Powaz

Glad to know! Wink
SSPI is a very good choice too, the only diference you'll find to OpenSSL relies on the supported cipher algorithms, it lacks some of them, this is only important for compatibility reasons, but as i said before, you're in control of both your client and your server, so they'll always be compatible, you shall do fine.

Keep it up!

See you.

Xembly on 3/4/2010 10:40 AM (7 days ago)

@Xembly
Everything is ok! I injected my server in a notepad, and injected the client in the calculator, it worked fine Smile Now I've got a secured IPC.

Tonight I'm going to start analyzing how we can get any data from a Qt program, using DCs or using Qt functions... anything that can help us...

I hope I'll find something interesting ^^ Of course, I'll let you know about my progress!

See you Xembly!

PoWaZ on 3/6/2010 10:37 AM (5 days ago)

@Powaz

Great, i advise you to take a look at the nearest neighbour OCR method, it is easier to implement than the feature extraction method, both can be future adapted to work with back propagation neural nets, (if one day you decide to go that way Wink ), see you mate !

Keep it up.

Xembly on 3/6/2010 3:28 PM (4 days ago)

@Xembly
Last question about code injection, and stealth in particular: do you think that my server and my client can work in console mode (for getting some feedback) or not?
Or maybe my bot's "command center" should connect to the "decision center" to get all the feedback I need...

Which solution is the best?
- console mode
- a connection to the "decision center"
- or logging everything to a file

PoWaZ on 3/6/2010 8:13 PM (4 days ago)

@Powaz

Well, consoles are "readable" through pipes, snapshots, etc... you can redirect the output from any console using pipes, which says about everything, if you can, so do the "others"!
I'd use blind mode, use a remote machine to get feedback of what's happening, don't write files on the client machine, those are traceable events too.
If for debugging purposes you may have everything running on the same machine, when you get in serious botting you should split them... never, ever, have any kind of GUI that the "others" can see, read, snapshot, whatever... because they will do it from time to time, and you'll get that account blacklisted...
My "decision center" allows me to interact with the game role, add exceptions, add new temporary rules, and make them permanent if i decide to do so.
I use GUIs on the client machine only in debug mode.

See you.

Xembly on 3/6/2010 10:30 PM (4 days ago)

@Xembly

Ok, thank you for answer!
I'm sure I will have to debug A LOT, so I'll split the different parts of my bot later... later... later... ^^
In James' tutorials, he doesn't talk about botting from a venue's point of view: that they can know what's displayed on your screen, scan for injected libraries, ... so I prefer to ask you ;)

I was wondering if you were a "pro botter", I mean... does your bot is good enough for you to stay at your home and just check if everything works fine?
It's hard for me to imagine how much money a bot can generate in 1 day...

Last question (sorry... ^^) about OCR this time: is there a good library available that I could use, or is it preferable that I code it all by myself? (for more stealth, for example).

I ask you so many questions... you are very patient! Thank you for that Smile

See you!

PoWaZ on 3/7/2010 6:25 PM (3 days ago)

@Powaz

Ok, while debugging/scrapping/testing use a "play-money" account, when you're done, renew/release your router's IP address so that you get a new one before you login with your "real-money" account.
About "venue's point of view", as i said before, thanks to the time a spent "sleeping with the enemy", i was able to understand why they worry so much with botting, it's all about money of course... bots scare most of the "fish", with "fish" poker sites win a whole deal of money (rake), bots have no feelings, don't go on tilt, and are intended to evolve close to perfection, imagine how scary this can be if you own a poker site, and because you don't take actions against botting you understand you're loosing players to your rivals, who happen to be unfriendly to bots. Notice that Anti-Spyware triggers most of poker clients, that's because they actually spy on your computer, justifying such behaviour with some vicious license agreement explaining that this is for your own protection.
One could even question about statistics, that was one hell of a case study posted some years ago by Poker Academy, the percentage of online poker RNGs (Random Number Generators) that causes 6 or more players to be calling the river, was estimated to be twice compared to human drawings, this study was done using more than 600000 hands, and has not be questioned so far, i'm sure you understand how this affects the rake "site's proffit", which leaves with another question, are they interfering with RNG's? And another... the entities who supervise these sites are taking the necessary measures to assure 100% RNG's?
Even with my experience at the time, i could not figure out if this was or not the case, my low-level and API programming skills were used only to hunt down guys like us, i didn't had access to the left-over code of the client, one thing was for sure, for the "site's venue" bots should die!
About your interest if one can make a living by "pro-botting", well the answer is obviously yes, IMHO there are two types: the ones who build them (that's me!), the ones who spend hours and hours tunning them (my customers). I'm not a very good player, i don't even try to be one, i leave that to the real serious players. My bot's are expensive, but very secure and trustable, i rent anual licenses to all my bots, all the profiles, rules of engagement and whatever... are stored in my server, so i've absolute control of everything, that includes updates of course, and yes, ever since i left my previous job, i do make a reasonable living, better than the one i had before, one of my customers builted a huge poker war-room, and he's actually getting averages around $5k per day.
About your last question, related to OCR, i'd advise you to look for "tesseract", those libraries, if you're not a "resource saver maniac/assembly fan" like me, and don't intend to develop your own, this might be what your looking for, open-sourced, originally developed by HP, they are now public domain, with lots of info wide-spread, this might be a good starting point.

Happy to help Wink

Feel free to query back!

See you.

Xembly on 3/8/2010 9:48 AM (3 days ago)

@Xembly

Yeah, I understand very well why poker venues hate bot users... ^^
I didn't know about that study, that's very interesting ! Smile
If poker venues want to kill the bots, I think the only way might be to stop online poker.
No need to be a very good programmer to know that everything is hackable on a PC, and easier to hack because of the Internet (tutorials, libraries, forums, ...)
It's a never-ending story ! Smile

About my research concerning Qt and FT, I found that Qt uses a DC called QPaintDevice to draw text. I will have more time the next days.

About OCR, I've read on PokerAI that Tesseract was full of memory leaks, I don't know if this is true...? If so, maybe it's negligeable... I'll see!

Now about your customer, $5k a day... wow... he's "lucky"! ;)

I'm actually at work, so I can't talk too much... as usual, I thank you for your help!

See you later!

PoWaZ on 3/9/2010 10:06 AM (2 days ago)

@Powaz

I'll take a deeper look on "QPaintDevice" to see how it works inside, l'll tell you later my results.
I'm not using Tesseract so i can't say if that's true or false, but google code is supporting the development of "ocropus", which is, from what i've heard built around tesseract.
About my customer, i've no means to tell if that is "de facto" true, just for the record... i don't monitor the earnings of the clients on my server, that amount of money is what he claims to be his daily average, one thing is for sure, i detect around 20 to 30 different IPs connected simultaneous using his license number, when i got in touch with him about a possible license number leak, he claimed to have a huge poker war-room running collusion tables, so i must assume that's probably true, at least for the time beeing.
I'm also at work, but this is my work Wink so, feel free to query back anytime.

See you.

Xembly on 3/9/2010 10:43 AM (2 days ago)

@Powaz

I took a deeper look at "QPaintDevice", in win32, and if you're using the environment variable (QT_USE_NATIVE_WINDOWS = 1), it renders a device GUI by calling (BeginPaint -->> BitBlt -->> EndPaint), notice that no api text related functions were used, i also advise you to take a look at "QString setText", because every GUI text object use at least one QString.

See you.

Xembly on 3/9/2010 11:05 AM (2 days ago)

@Xembly

Ok, thx for the info Smile
I had a look at "ocropus", I think it's a litte too heavy, it's a big project whereas "tesseract" is lighter and if there are some memory leaks, if they are not negligeable for a pokerbot then I could try to fix some of them, nevermind ;)

I wanted to know something about poker venues... can they ban when a player is suspicious, or do they ban ONLY when they've got some proofs?
Are they so obsessed by the death of pokerbots that they can blacklist as soon as something is strange but they can't find anything?

I think I won't use a bot all the time, it will be more like a scenario: the bot will play during the day, and at night I will play... "it is late at night, I played all the day, I'm tired, I don't play very well..." something like this ^^
Or I could just create a set of rules for this scenario, yeah.
I'll work on it later!

See you !

PoWaZ on 3/10/2010 7:41 AM (1 day ago)

@Powaz

About OCRing, it's not hard to implement the matrix algorithm, look in google or wikipedia, you'll find plenty of info, the only "if" of this method, it really needs a lot of trainning with character combinations without a space between.
About the "kickout" policies, usually before banning they blacklist, this means that they suspect of an "un-human" behaviour, like acting always by clicking the exact "xy" pixel of a button, fast bet adjustments, clicking without mouse moving (using window messaging), same time distance between "mousedown" and "mouseup", screenshots containing poker related software, whatever...
When you get blacklisted your account will eventually be monitored by an admin to gather more proof, if he/she suspects the customer behaviour to be abnormal then some sites may issue an email informing you about suspicious activity and threat you with an account suspension and funding withdraw, others imediate suspend you and notify you should get in touch with support.
There have been some reports even of bot players who claim to be unfavoured by RNGs, (this issue was also pointed by several bot players on a Poker Academy old thread), this could be "de facto" the best polite measure to kick bots out, by enforcing the idea "your bot will never play like a human, so don't use it".
Overall, they have all means to kick you out if they intend so, even if you're not using bots and use only helper software like PokerTracker.
My advise is too have at least 2 accounts for bot playing, never play more than 5 to 6 continuous hours with each, and renew your ip when changing from one to another, even though this might not be enough (they can always get some kind of unique hardware ID and trace you that way, i used this method on the past Wink).
This is why i'm so paranoid with stealth and security... 8-)

See you.

Xembly on 3/10/2010 10:48 AM (1 day ago)

Comment on this post:

Thanks for your interest in Coding the Wheel. All fields are optional.