How I Built a Working Online Poker Bot, Part 6: Guerilla-Style File Monitoring on Windows with C# and C++
Thursday, July 17, 2008   

Introduction

How to write code to detect when any software application on the machine accesses any file on the machine, and how to extract and view any data read from or written to that file in your own application, in real time, using C# and a little bit of C++?

That's the subject of today's article: how to build a file monitor on steroids (or a rough draft of one).

In the above screen shot, we're monitoring the Yahoo Messenger application. And as you can see, it reads and writes quite a bit of data. Public keys, log file snippets, some XML filtering stuff, and so forth. We could have as well chosen to point the tool at Microsoft Word, Grand Theft Auto, or the humble Notepad.exe. But the real power of these techniques is incorporating them into your own applications.

For example, those of you who've been following the online poker botting series can use these exact methods to detect and respond to log file and poker hand history text in real time, as its generated by the poker client. Let's take our tool and point it at the Poker Stars client, POKERSTARS.EXE, by way of example:

Sure enough, we've detected that Poker Stars has opened the log file, and sure enough, we're reading and displaying the log file data as it appears. Which is almost exactly what we'd like our poker bot to do.

But maybe poker's not you're thing. Maybe you're just tired, as I am, of third-rate software treating your hard drive like one of these:

Modern software apps are footloose and fancy-free when it comes to depositing cruft on your machine. I mean the temporary files that never get deleted. The hidden cookies that persist until you reinstall the operating system. And the registry entries which don't get culled when the application that owns them is uninstalled.

Fighting Back

It's a widely-known fact that file I/O on Windows systems is a matter of public record. It's public because, with admin-level access, you yourself can browse to any file on the machine, open the file, edit it, and save it. It's public because you have access to the process address space of every application on the machine (with few exceptions). It's public because applications that read from or write to files do so using a small set of publically available and thoroughly documented functions:

The above functions are formal members of the exclusive country club known as the Windows API. They're called by millions of applications around the world every day. As you're reading this, various applications on your machine are calling these functions, whether they know it or not and whether you know it or not. In fact, it's hard to do much of anything on a Windows box without calling one of the above functions, either directly or indirectly.

But I'm a .NET programmer! you say. I'm a Java programmer! I don't talk to low-level Windows APIs!

Ah, but you do. You won't call these functions directly, but the .NET framework, the JVM implementation, or whatever library or framework you're using will. So the above set of file-manipulation APIs constitute a sort of bottleneck through which a majority of system file I/O must pass. Now: what if we could somehow get Windows to call one of our functions every time a particular application (any application) calls or causes to be called one of the above APIs?

File monitoring would be a cinch in that case. And that's essentially what we're going to do.

Creating the Injection DLL

The above application was written in C#, but it works in conjunction with a small C++ "workhorse" DLL. Inside this DLL, we'll code custom but equivalent versions of whatever Windows APIs we're interested in redirecting. Here's an example showing our custom version of CreateFile, dubbed "Mine_CreateFile" but you can call it whatever you want:

// Our custom version of the CreateFile Windows API, having the same parameters,
// return type, and calling convention as the version of CreateFile provided by the OS.
HANDLE WINAPI Mine_CreateFile(LPCWSTR lpFileName,DWORD dwDesiredAccess,
                              DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecAttr,
                              DWORD dwCreateDisp, DWORD dwFlagsAttr,HANDLE hTemplate)
{
    // First, call the original CreateFile provided by the operating system.
    HANDLE hFile = Real_CreateFile(lpFileName,dwDesiredAccess,dwShareMode,lpSecAttr,
                                    dwCreateDisp,dwFlagsAttr,hTemplate);

    // Now, do whatever we want with the filename (lpFileName)
    // Now, do whatever we want with the file handle (hFile)

    // Return the same value returned to us by the original API
    return hFile;
}

Pretty straightforward. Whenever this function is called, we call the original CreateFile API through the Real_CreateFile function pointer. Once that's done, we can snoop on the parameters (such as the filename) passed to this function by the target application, report them, etc. Now, in order for this code to work, we have to ensure one thing: our code must be running inside the target application's process.

A New Flavor of DLL Injection

I've written about DLL injection before, and there's a lot of information on this topic scattered around the net and in books. So I'll assume you know that DLL Injection is robust and reliable. I'll assume you know it's a carefully-designed operating system facility, not some backdoor hack. And I'll assume you know (or can figure out) how to inject a DLL. After all, it's a one-liner:

HHOOK myHook = SetWindowsHookEx(WH_CBT, (HOOKPROC) MyHookProcedure, hInstance, 0);

For the purpose of monitoring file I/O in real time, any DLL injection technique will work. But there's one which is particularly well-suited for this kind of thing. I mean the DetourCreateProcessWithDLL function, provided by the Microsoft Detours library:

// Initialize some data structures...
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);

// The following call loads the target executable with our DLL attached
BOOL bSuccess = DetourCreateProcessWithDll( targetExePath, NULL, NULL, NULL, TRUE,
                          CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED, NULL, NULL,
                          &si, &pi, "detoured.dll", "XFileMonitor.Hook.dll", NULL);

// DetourCreateProcessWithDll runs the process in a suspended state. Wake it up.
if (bSuccess)
   ResumeThread(pi.hThread);

The benefit of doing it this way, as opposed to Windows Hooks or other methods of DLL injection? It ensures that our DLL will be present during the target application's startup routine, which is traditionally when programmers open a lot of files. Were we to inject using a Windows Hook, we'd miss application startup every time, because it takes several seconds for a Windows Hook DLL to be propagated across the system.

Detouring the System File APIs

It's not enough to simply create a DLL and inject it into the target application. We have to somehow redirect calls to the system-provided file APIs to the equivalent versions we've created. That's easily accomplished, using Microsoft Detours and a few lines of code.

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        if (::GetModuleHandle(L"XFILEMONITOR.GUI.EXE") == NULL)
        {
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourAttach(&(PVOID&)Real_CreateFile, Mine_CreateFile);
            DetourAttach(&(PVOID&)Real_CloseHandle, Mine_CloseHandle);
            DetourAttach(&(PVOID&)Real_WriteFile, Mine_WriteFile);
            DetourAttach(&(PVOID&)Real_ReadFile, Mine_ReadFile);
            DetourTransactionCommit();
        }
        break;

    case DLL_THREAD_ATTACH: break;
    case DLL_THREAD_DETACH: break;
    case DLL_PROCESS_DETACH:
        if (::GetModuleHandle(L"XFILEMONITOR.GUI.EXE") == NULL)
        {
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourDetach(&(PVOID&)Real_CreateFile, Mine_CreateFile);
            DetourDetach(&(PVOID&)Real_CloseHandle, Mine_CloseHandle);
            DetourDetach(&(PVOID&)Real_WriteFile, Mine_WriteFile);         
            DetourDetach(&(PVOID&)Real_ReadFile, Mine_ReadFile);
            DetourTransactionCommit();
        }
        break;
    }
    return TRUE;
}

C and C++ programmers will recognize this is as a standard DllMain function. The DLL_PROCESS_ATTACH notification tells us that this DLL is being mapped into a new process - in this case, the process of the target application we'd like to spy on. That's as good a place as any to perform the redirection. The following lines of code...

DetourAttach(&(PVOID&)Real_CreateFile, Mine_CreateFile);
DetourAttach(&(PVOID&)Real_CloseHandle, Mine_CloseHandle);
DetourAttach(&(PVOID&)Real_WriteFile, Mine_WriteFile);
DetourAttach(&(PVOID&)Real_ReadFile, Mine_ReadFile);

...cause all calls to the original versions of the function (the ones provided by the operating system) to be redirected to our custom versions of those functions. It happens transparently, behind the scenes, and in memory - you're not actually changing the system DLLs as they exist on disk, so you don't have to worry about corruption. And of course, when the DLL is unloaded from the process (DLL_PROCESS_DETACH) we remove the detours.

Simple.

Spying on the Birth and Death of Files

So you've got your DLL containing custom versions of various Windows APIs. You've injected your DLL into the target process using DetourCreateProcessWithDLL or your injection method of choice. And you've redirected all calls to various system APIs such that your code gets called instead. Now it's time to start tracking files as they're opened and closed by the target application. Let's revisit our CreateFile override, and add a little bit of code to do this:

// This data structure stores the files currently opened by the target app.
map<HANDLE, CString> g_openFiles;

// Critical section guards access to the above collection across threads.
// Elsewhere in code we've called InitializeCriticalSection
CRITICAL_SECTION g_CritSec;

// Our custom version of the CreateFile API.
HANDLE WINAPI Mine_CreateFile(LPCWSTR lpFileName,DWORD dwDesiredAccess,
                              DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecAttr,
                              DWORD dwCreateDisp, DWORD dwFlagsAttr,HANDLE hTemplate)
{
    // First, all the original CreateFile provided by the operating system.
    HANDLE hFile = Real_CreateFile(lpFileName,dwDesiredAccess,dwShareMode,lpSecAttr,
                                    dwCreateDisp,dwFlagsAttr,hTemplate);

    // If that was successful... a new file handle has been born
    if (lpFileName && hFile)
    {
        CString sFileName = lpFileName;
        if (!sFileName.IsEmpty())
        {
            // Store it! Multiple threads may call this function at the
            // same time, we we'll use a critical section to ensure that
            // only one of them manipulates g_openFiles at a given time.

            ::EnterCriticalSection(&g_CritSec);
            g_openFiles.insert(pair<HANDLE, CString>(hFile, sFileName));
            ::LeaveCriticalSection(&g_CritSec);
        }
    }
    return hFile;
}

Every time the target application calls (or indirectly causes to be called) CreateFile, we're storing the filename and the HANDLE associated with it. You could also take this opportunity to dump some information in a log file or (in the case of the XFileMonitor) display a notification in your GUI (which is running in a different process) that a particular file has been opened.

Snooping on File Reads and Writes

Similar to the custom version of CreateFile above, we'll create custom versions of the WriteFile and ReadFile APIs. The target application will call our versions of these APIs whenever it tries to read data from or write data to the disk. Let's take a look at our version of ReadFile.

BOOL WINAPI Mine_ReadFile(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,
                            LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
    // Call the original version of ReadFile provided by the OS
    BOOL bSuccess = Real_ReadFile(hFile, lpBuffer, nNumberOfBytesToRead,
                                  lpNumberOfBytesRead, lpOverlapped);

    // See if the handle being written to is one we know about
    map<HANDLE, CString>::const_iterator iter = g_openFiles.find(hFile);
    if (iter != g_openFiles.end())
    {
       // Yes, we collected this handle in an earlier call to CreateFile.
       // Retrieve the file name and do something with it.
       CString fileName = (*iter).second;
    
       // TODO: lpBuffer now contains whatever data the application read. This
       // might be text data (either single-byte or Unicode) or it might be
       // binary.. we're free to examine and/or transmit it as we see fit
    }
    return bSuccess;
}

First we pass the call along to the original API. Then we take the supplied HANDLE and see if it's in the list of handles we created by monitoring calls to CreateFile. If it is, we can easily get the associated filename. If it's not, we can still get the filename, but it's a bit more work, and left as a reader exercise.

Identifying Binary vs. Text Data the Quick and Dirty Way

If you're targeting or trying to monitor a particular file or application, such as monitoring the log file created by the Poker Stars gaming client, you already presumably know about the files you want to snoop on. You know for example, that hand history files are text files. So when you detect a write to a hand history file, you can be sure it's text data that's being written.

But what about when you don't know anything about the files? What if you're trying to write a generic file monitor, such as the one shown above?

The problem is that ReadFile and WriteFile treat everything as a buffer of bytes. It might be text data, that's being written. Then again, it might be binary data. You don't really know, and there's no quick and easy way to tell the difference, because binary data and text data will frequently use some of the same underlying values. For example, if the first byte of data in the buffer has the value 65, it might be the letter "A" (which, in its single-byte form, has the value 65) or it might be that you're reading a binary file, and the 65 indicates something (anything) else entirely. Maybe it means there are 65 records in this particular file, or maybe it means that the average price of a pound of bok choi in Moscow is 65 pennies. You don't know, I don't know. Only the person who wrote the code knows.

So what to do in that case?

Well, we can get a quick-and-dirty approximation of whether a given buffer of data is text or binary by:

  • Checking the file extension. This isn't always reliable, since you can give any file, whether text or binary, any extension. But it's usually reliable.
  • Checking the data being written to see if it falls within normal ranges for text character data. Again, not always reliable, but often reliable.

You already know how to get the file name (by detouring CreateFile). So let's look at some quick-and-dirty code to scan a particular buffer and see if the data in that buffer can successfully be interpreted as ASCII text. Specifically, let's check each byte in the buffer to see if it falls within normal ranges for ASCII (single-byte) character data. Here's a sample function which does that:

// Check the first N bytes of any arbitrary data stream to see
// if they fall within normal printable ranges for ASCII/MBCS
// character data.
bool IsAsciiText(LPCSTR buffer, int testLength)
{
    int validChars = 0;
    for (int index = 0; index < testLength; index++)
    {
        char c = buffer[index];

        // if the value of C is negative, or if it
        // identifies a non-printable ASCII character,
        // then this is probably binary data.

        if (char < 0 || !(isprint(c) || isspace(c)))
            return false;
    }

    // Every character we tested was within the range
    // for printable ASCII characters. This is probably
    // text data.

    return true;
}

Now, this little function is far from complete. It only handles ASCII (single-byte) character text. It doesn't correctly handle embedded NULLs and so forth. But it's good enough for demonstration purposes and pretty easy to enhance with additional, smarter checks.

Transmitting File Read and Write Data to Managed Applications

So you've got your ReadFile and WriteFile detours in place and working. You're able to roughly detect when particular data is text or binary, by examining the file name as well as the data stream itself. Now how do you go about transmitting all this data back to your application, running in a separate process?

Obviously we'll have to use some sort of inter-process communication. The exact flavor is up to you, but the above XFileMonitor tool uses WM_COPYDATA because it's simple and straightforward.

// A little structure to communicate relevant file read/write contextual info
struct FILECONTEXT
{
   HANDLE File;       // the handle of the file read/written
   int OriginalAPI;   // a number uniquely identifying the specific API
                      // (such as ReadFile, etc) that was called.
};

// Transmit file read/write data back to the XFileMonitor (or any) application..
void Transmit(Win32API sourceAPI, HANDLE hFile, LPCWSTR text)
{
    // We're using WM_COPYDATA, which is a window message, so we need a target window.
    HWND hWnd = ::FindWindow(NULL, L"Coding the Wheel - XFileMonitor v1.0");
    if (!hWnd)
        return;

    // Set up the COPYDATASTRUCT expected by Windows.. the length should be the
    // size of our (custom) FILECONTEXTstructure, plus the length of the file
    // data we're sending, expressed in bytes, with enough room for a terminating
    // NULL.

    COPYDATASTRUCT cds;
    ::ZeroMemory(&cds, sizeof(COPYDATASTRUCT));
    cds.dwData = action;
    cds.cbData = sizeof(FILECONTEXT) + ((wcslen(text)+1) * 2);

    // Allocate the outgoing array
    LPBYTE pOutData = new BYTE[cds.cbData];

    // Place a FILECONTEXT structure at the front of this array
    FILECONTEXT ht;
    ht.File = hFile;
    ht.OriginalAPI = sourceAPI;
    memcpy(pOutData, &ht, sizeof(FILECONTEXT));

    // Place the text immediately following the structure..assumes any
    // single-byte text has already been converted to Unicode
    wcscpy((LPWSTR)(pOutData + sizeof(FILECONTEXT)), text);

    // Send it off
    cds.lpData = pOutData;
    ::SendMessage(hWnd, WM_COPYDATA, (WPARAM)::GetDesktopWindow(), (LPARAM)&cds);
    delete [] pOutData;
}

Then in our managed code application .EXE, we can use some simple P/Invoke to access the data. First, let's override OnWndMessage:

// WM_COPYDATA message ID
const int WM_COPYDATA = 0x4A;

// Get access to the WM_COPYDATA message
protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_COPYDATA)
        OnCopyData(ref m);

    base.WndProc(ref m);
}

Now let's define the OnCopyData function. In order to do that, we'll need to create P/Invoke compatible versions of the COPYDATASTRUCT (defined by windows) and FILECONTEXT (defined by us) structures.

// Managed version of Win32 COPYDATASTRUCT (defined by OS)
private struct COPYDATASTRUCT
{
    public int dwData;
    public int cbData;
    public IntPtr lpData;
};

// Managed version of FILECONTEXT struct (defined by us)
private struct FILECONTEXT
{
    public IntPtr Handle;
    public System.Int32 OriginalAPI;
}

// Decode WM_COPYDATA sent from the unmanaged C++ file monitoring DLL
private void OnCopyData(ref Message m)
{
    COPYDATASTRUCT cds = new COPYDATASTRUCT();
    cds = (COPYDATASTRUCT)Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT));

    FILECONTEXTht = (FILECONTEXT)Marshal.PtrToStructure(cds.lpData, typeof(FILECONTEXT));

    string strBufferData;
    int unManagedSize = Marshal.SizeOf(typeof(FILECONTEXT));
    unsafe
    {
        byte* pString = (byte*)cds.lpData.ToPointer();
        pString += unManagedSize;
        IntPtr pManString = new IntPtr((void*)pString);
        strBufferData = Marshal.PtrToStringUni(pManString);

        // TODO: strBufferData contains the text sent over by the
        // file monitor. Display it, analyze it, whatever.
    }
}

 

And that's all there is to it.

I had very little time to throw this code together so I hope you'll excuse some of its obvious warts such as the use of unsafe C# code. Think of it as a quick and dirty example of techniques rather than production botting code. This code will be extended in Part 8 of this series (already written, and due shortly) in a direction you might not have guessed...

Putting It All Together: the XFileMonitor Application

You've seen what it looks like. You've read about the techniques behind it. Time to download the thing and test it out.

The source code contains four projects:

  • An EXE project, implemented in C#
  • A DLL project, implemented in C++
  • 2 Detours projects: detours.dll and detoured.dll.

These projects boil down to essentially two files, which contain all the source code demonstrated above. In addition, there are two projects needed by the Detours Library which you can mostly ignore. Boost is not required for this project, and neither is NMAKE. So those of you that had trouble building the FoldBot will hopefully find this project is a cleaner build.

And as usual, my standard source-code disclaimer applies!

Good luck, and enjoy.


Posted by James Devlin   59 comment(s)

Very impressive article.

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

Thank you for eliminating the need for NMAKE. It was a nightmare on my system.

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

AWESOME

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

So my question is do you think PS is already blocking your app with this file name and banning accounts that have been seen using it? Its easy enough for them to scan and see what programs are running and then close someones account because of it. I have seen programs that change there exe name each time they run but I have wondered if that really will work since they could be tracking what the appication is doing and what it monitors. A bit of a ramble but let me know your thoughts.

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

Poker Stars is so behind the times on this I think if they were to do that, they'd end up teaching a whole new generation of botters how to stealth their applications. Maybe they'd prefer not to make a big stink about it.

Just my .02 great post btw..

Arden on 7/17/2008 6:04 PM (601 days ago)

And I'm guessing this is one of many reasons security experts say 'there's no security on the client' because of techniques like this and actually... your entire series makes me question what applications are really "made of".. in physics we learn that matter is mostly empty space and after reading your site I'm starting to realize that software is basically the same. Not at all what you think it is on the surface.

The single most impressive explanation of this topic I've ever read, by the way. There are tools you can buy to do this kind of thing but I've never seen a detailed explanation of how to do it in code. Also nice to see some C# in the mix this time.

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

"Its easy enough for them to scan and see what programs are running"

Using Windows APIs? Too bad there's no way to hook them and control what they return ;)

Jason on 7/17/2008 11:31 PM (601 days ago)

Leading to a situation in which they try to detect you, you try to detect them, you're both now injecting code into each other's process space and spawning cloven-hoofed injector demon rootkits and next thing you know, BOOM.

The Universe explodes.

JeremyX on 7/18/2008 4:48 AM (601 days ago)

Lets all help out here with this series and do some translation from C++ to C# for the gui part. If you look at XPokerBot you will see that all the code is there and can be accessed from C# the same way that it is done in XMonitorFile just need to convert it to C# from C++ I am going to start and will post what I do but I will say this C++ is not my stronge suite.

Anonymous on 7/18/2008 5:35 AM (601 days ago)

Would you advise using the same technique that you showed for pokertime to actually click the buttons for PS and FTP?

Anonymous on 7/18/2008 5:41 AM (601 days ago)

"Lets all help out here with this series and do some translation from C++ to C# for the gui part."

I'm working on this too, just wondering if maybe the author already has a C# version...?

If anybody else has done this yet speak up! Smile

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

WOW. Amazing post, and tool. Quite eye-opening to see what files a particular program touches. I've been pointing this thing at every EXE on my system having quite a bit of fun.

Does that qualify me for geek status? Wink

Anonymous on 7/18/2008 5:22 PM (600 days ago)

I'm really enjoying these articles, keep it up! Poker bot making is a fun hobby.

http://www.icmbot.com

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

Ok lets start with translating the types of items in the struct's from C++ to C#
So far here is what I have and here are the problems that I have.
Work Correcyly
C++ C#
INT System.Int32
BOOL System.Boolean
double System.Decimal

Not sure of
double Limits[2];
HWND Window;
TCHAR TableName[64];

I will post if i find answers.

Anonymous on 7/20/2008 12:30 PM (599 days ago)

Ok right after posting that I found this
seems to work well for the struct conversions
http://tangiblesoftwaresolutions.com/Demo.htm

Anonymous on 7/20/2008 12:48 PM (599 days ago)

Although when you convert the struct it turns it into a class which C# does not like
So trying to just do struct to struct when I have
public string TableName = new string(new char[64]);
I get the error
cannot have instance field initializers in structs

Anonymous on 7/20/2008 1:39 PM (599 days ago)

Man, you really crazy coder!!!
Thank you for your impressive series of articles.

Anatoliy from Russia on 7/22/2008 4:15 AM (597 days ago)

For the C# part i made some entries on part 5 of this series... got it up and running now with an entire C# frontend... just fast mock-up. Relying on the source from the monitor client (from part 5) to send the data to my C# application, and then letting that application handle the interpretation of the chat (btw only doing full tilt poker venue, where the hole cards is displayed in the game chat).

@anynomous (two posts before this one) i think i solved that problem (char[64]) in the code part i displayed in part 5 of this series.

H4mm3rHead on 7/22/2008 5:11 PM (596 days ago)

Hello all I just built the MonitorBot and the file monitor thingy. I'm seeing some of the poker clients open up some strange files. Stuff in my browser history and so forth, registry entries, and etc. Anybody else seeing the clients opening files they shouldn't be?

Anonymous on 7/29/2008 7:14 AM (590 days ago)

AS mentioned in one of the first parts of this series, the poker software can in some regards be looked upon as spyware, i think thats prbably the best explanation.. try look at that post (think its post 1 or 2)

H4mm3rHead on 7/29/2008 11:10 AM (590 days ago)

No i don't see it oepning any strange files. Just look through the code. YOu should be able to see if there is anything in there that it is accessing that you don't want it to.

LastChance on 7/30/2008 1:27 PM (589 days ago)

Hi LastChance, I mean the **poker software** (Poker Stars) is opening strange files, not the tool. For example I see it (Pokerstars) look at my IE history folder and some other stuff... am researching.

Anonymous on 7/30/2008 7:37 PM (588 days ago)

Very intersting keep us posted. What are you using to track what its opening?
Its probably looking for bot programs running. Have you tried seeing what FTP is looking at?

LastChance on 7/31/2008 12:41 PM (588 days ago)

PStars has a list of prohibited software that players aren't supposed to be using while they play. Stuff like bots and statistics programs that use centralized databases and stuff like that. To enforce "prohibition" they snoop on everyones PCs looking through things like the registry, start-menu, program files, IE history, etc. Apparently running a bot or stat collector is a bad thing but rifling your PC (and mine) is perfectly OK.

DM on 7/31/2008 8:22 PM (587 days ago)

OH BOYSmile anyone running on 64bit Vista?

I am getting this when i try to call InstallHook on any program.

An attempt was made to load a program with an incorrect format.

LastChance on 7/31/2008 8:28 PM (587 days ago)

Fixed that by changing the build properties to x86

LastChance on 7/31/2008 11:21 PM (587 days ago)

But now I no longer get any messages cominb back from C++
Anyone doing this in 64bit?

LastChance on 8/1/2008 12:35 AM (587 days ago)

Hi guys, I've been out of town...

Last time I checked, Detours Express didn't support 64-bit out of the box, so that may be the problem. There are other options though, for example:

code.google.com/p/easyhook-continuing-detours/

James Devlin on 8/1/2008 11:21 PM (586 days ago)

It works on 64 bit just have to change it to build as X86 and it will work fine.

LastChance on 8/4/2008 10:31 AM (584 days ago)

The latest version of the Easyhook library can be found here. The one at google is an old version

www.codeproject.com/KB/dotnet/EasyHook64.aspx

Jerry on 8/15/2008 5:34 PM (572 days ago)

How do we prevent poker sites from detecting
our injected dll when they can simply run
an application like this to detect them?
http://www.edmond-hakmeh.com/hack%20tools/utilities%20and%20hacking%20tools/utils/injected_dll.html

Anonymous on 8/19/2008 9:48 PM (568 days ago)

In a past life I was loosely associated with a poker stat collecting software app. A good product but I'm not here to advertise for anyone. From extensive readings in that forum and others I can tell you that only a few poker-sites state adamantly that bots and stat collection software that used centralized databases are not allowed. Of those... only two (PS & PP) actually use their client app to check your system. They don't disallow all DLL injections on your system... too many of those are required by the system or major apps or for good purposes. They check for specific known products by name or by tell-tell signs of it on your system (registry, file system, IE history). Products that have sold thousands of copies. Now PS is stating that ICM calculators are disallowed as well... so those guys will now have to watch out. The two poker-sites y aren't worried about the little guy building a bot for free that could be under any name today and maybe a different name next week... that may or may not be able to get his bot working, and may or may not be able to break even with it when he does. They're only worried about big bot software companies whose technology could be used in collusion rings, or centralized stat databases where paying members get tons of stat data on players they've never played against or even seen. I know a lot about this... but I'm still here enjoying this fun project. James got me excited about programming again. Keep em coming James.

DM on 8/21/2008 7:49 PM (566 days ago)

How do I determine for what table the hole cards in the log file belong? Any ideas?

Simon Pettersson on 10/7/2008 6:58 PM (519 days ago)

Okay I am struggeling with this. I found that windows get assigned ID's so I can use that to track which window was assigned the hole cards. Not the next problem arises, the log file gets mangled when multitabling. The lines written from the different games overlap eachother.

Simon on 10/12/2008 1:40 PM (515 days ago)

Simon: each line in the log file should be prefaced with the HWND of the window to which it belongs. Take the HWND and use that to find out which table the action occurred on. Have you tried this approach?

Anonymous on 10/12/2008 4:34 PM (514 days ago)

Anyone have any idea how I can get the hand that is dealt to me using the William Hill poker software?

It writes to a log file, but only after the hand is finished. The hand isn't displayed in the chat window (even with all dealer messages being shown), I can't see how I can get my hand in real time!?

Any ideas appreciated! Smile

P.S Got my "bot" working on PokerStars, but would like to get it working for William Hill Poker!

Ricky on 10/12/2008 5:49 PM (514 days ago)

I haven't looked at William Hill but you have (at least) two options:

1) Use a simple pixel test to read the hole cards. I think there was a diagram of this way back in Part 1.

2) Perform some basic disassembly to figure out where in memory the hand values are being stored.



Anonymous on 10/13/2008 1:43 AM (514 days ago)

Nice

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

Great stuff, thanks James, especially liked your section on "Identifying Binary vs. Text Data the Quick and Dirty Way"

Caribbean Poker Classic on 11/5/2008 10:44 AM (491 days ago)

How can I preface log messages with the HWND of the window to which it belongs? (multitabling)

Any piece appreciated!

Walli on 12/13/2008 6:28 AM (453 days ago)

@Walli

You are probably already keeping a list of poker windows and their corresponding table names (if not, you should). When you get a log message just see if it contains the table name.

Anonymous on 12/15/2008 4:32 PM (450 days ago)

Wow; this is the most advanced poker bot website I came across; now to be honest, very very few people can implement such a profitable bot; but if you can, you are the man

Poker Edge on 1/23/2009 9:40 PM (411 days ago)

Nice job!

We did something similar here:
http://www.pokerbot-smart.com/download.html

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

Hello everybody. I`m only 16, i`m from slovakia, i`m interested in programming a bot, but this is too difficult for me, couldn`t somebody write for me please only a simple fukntion, or part of a programme, that will return only the cards (means my hand, flop, turn, river)? this is the death point of my work, cuould somebody help me?
i need only the funtion for reading cards and save it to the variables.
Thank you very much.
Igor jebectt-gmail-com

igor on 3/26/2009 6:55 AM (350 days ago)

*optionaly (im writing) in C++ BUILDER

igor on 3/26/2009 6:56 AM (350 days ago)

*sry for another comment, but i forgot that i need only for pokerstars.THX.Igor

igor on 3/26/2009 6:58 AM (350 days ago)

Pokerstars chat doesn't seem to be working any more, as in it's not capturing the chat Frown.

I checked the log file, and they only write some obscure reference to board cards now:

DealBoardCard 0
DealBoardCard 1
[2009/04/24 17:44:02]
DealBoardCard 2
[2009/04/24 17:44:04]

Frown

Anonymous on 4/24/2009 6:39 PM (320 days ago)

AWESOME BLOG! im not really into building a real bot but this in a great introduction to windows API programmming (i come from Unix)

im trying to figure out all the possible DLL Injection techniques, but im puzzled by that comment on line #314,315 of XFileMonitor.Hook.cpp:

// Install the global window procedure hook, causing this DLL to be mapped
// into the address space of every process on the machine.
(...)


I thought Detour would map the DLL only in the target application and not into the address space of EVERY PROCESS on the machine..?

thanks for enlightening me!! and keep up the AWESOME work! hehe

luis

luis666 on 4/30/2009 10:44 AM (315 days ago)

thats awesome!

Poker Bot on 6/2/2009 6:15 PM (281 days ago)

Awesome and really impressive

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

Does it still working??? It seems to me that it is not. I tested a similar solution with Party Poker but it does not present any log until the end of the round.

Anyone experiencing the same problem??

pisrael on 7/12/2009 6:24 PM (241 days ago)

Wow, this is very impressive. How did you come up with this thing? How big is your programming experience? Also isn't this illegal?

Regards

Poker Reviews on 9/23/2009 1:41 PM (169 days ago)

I'm not a programmer, but some day would like to be. I just want to know what is going on with machine. I want to learn to spy on the spies. When I started this project I went and downloaded microsoft visual C++ 2008 Express Edition.

Unfortunately, neither MFC nor ATL are supported in the Visual C++ 2008 Express Edition. The header files and the related libraries and DLLs are only licensed with the Visual Studio 2008 Standard and additional commercial editions. There is no download source available. Does any one else have this problem and can you help me find alternative libraries for the few cases of ATL that you seem to be depending on.

<atlstr.h> in particular, or maybe even recommend a different compiler.

Thanks in advance

You lead this donkey to the water and I'm damn sure going to drink it.

Noobie on 10/25/2009 3:15 PM (137 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:49 PM (108 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:16 PM (105 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)

It look like difficult to built.

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

Hi,
A tutorial text, thanks for sharing. Do you think that top rated sites are also carry this format? By the way a top rated site now offer a certain [url=http://www.pokerrakeback.com/]rakeback and bonus. Hope it is enjoyable.

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

I tried running this and the fold bot on Vista.

After selecting the pokerStars.exe I get an error saying that the GUI has stopped working.

Any ideas anyone?

Thomo on 3/8/2010 8:44 PM (2 days ago)

Comment on this post:

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