Deconstructing the Poker Client, Part 1

Friday, October 03, 2008

Introduction

When I was nine years old, my uncle (who once caught a seven-foot hammerhead shark off a crowded Padre Island beach) taught me how to gut and clean a fish. It was a visual, hands-on introduction to the idea, pervasive in software development, that implementation details are messy. These days, I can hardly hear the word encapsulation without thinking about that fish. Anyone who's ever taken a biology class knows that one of the ways we learn how things (such as frogs) work is by dissecting them. Indeed, to analyze something is to take it apart; that's the root meaning of the word.

Anyway, today we're going to talk about how to gut (analyze) a different sort of fish.

I put the picture together in Photoshop, as a lark. Those of you who've read Exploiting Online Games: Cheating Massively Distributed Systems will recognize the four exploit vectors for modern client/server software: above, beneath, inside, and way outside. If you've been following the poker botting series, you've already played with some (basic, but hopefully illustrative) code that achieves a minor exploit along one vector or another. And of course, we use the word "exploit" but what we really mean is a reaffirmation of your basic electronic rights.

  • The right to know exactly what a given third-party piece of software is doing on and to your machine.
  • The right to run whatever software you like on your personal computer, provided it doesn't hurt or defraud someone.
  • The right to aggressively defend your property and your privacy against an army of spyware, adware, and other classes of malware.

Most online gambling software (not just online poker; I mean the entire online money gaming industry) can technically be classified as spyware.

Spyware is computer software that is installed surreptitiously on a personal computer to intercept or take partial control over the user's interaction with the computer, without the user's informed consent.

These programs, operating without oversight under the broad umbrella of "game integrity", and propped up by draconian, completely unenforceable EULAs, scan and monitor your system for a hundred and one telltale clues. They install browser add-ons without your express permission. They scan your list of running processes. They perform full-text, key-by-key searches through your Registry. They touch foreign directories on the local filesystem. They take screenshots. And so forth.

Hello, Big Brother.

Whether or not these techniques are justified for fraud prevention and/or operational efficiency is a discussion we can have when the online gambling sites stop trying to sneak these techniques in through the electronic back door. Just because we mindlessly click the "I Accept" button on the software EULA does not give a fly-by-night, often dubiously legal online casino (which would quickly go bankrupt without our patronage) a carte blanche to do whatever the hell it pleases to your machine, across international waters no less.

Call me crazy, but I still believe in electronic privacy. And so should you.

Fighting Back

Today and in future installments we're going to talk about how to spy on the software that spies on you. In today's article, you'll find a handful of basic techniques, some of which you'll probably be familiar with. In future installments, we'll progress to full-on disassembly of online gaming client source code. We're interested in three things:

  • How to figure out what kinds of data a given online poker/gambling client is reading or depositing on your machine.
  • How to protect your privacy by hiding or obstructing access to this data.
  • How to publicize this information so that it reaches the casual player.

The online gambling industry has proven repeatedly that it's not capable of regulating itself. Whether it's the Ultimate Bet and Absolute Poker super-user scandals, the seizing of player deposits (anyone remember PokerSpot?), the arrogance and dismissiveness of online gambling support staff, or the generally shoddy state of modern gambling software, the industry is due for a large and painful correction.

So without further ado, here are some basic "software reconaissance 101" techniques that every poker player, and certainly every poker programmer, should know about. Consider it self-protection.

Monitoring Your Local Filesystem

In How I Built a Working Online Poker Bot, Part 6: Guerilla-Style File Monitoring, we talked about how to build a simple Windows file monitor: a small application which tracks, in real time, the files opened, written, and read by a particular application (in our case, an online poker client application). That was useful from a poker botting perspective, because online poker clients typically write relevant game information to hand history files and log files. But this sort of thing is also useful from a privacy perspective: because of all the ways in which software applications violate your privacy, two of the most common are:

  • By depositing cookies, unique IDs, and other identifying cruft on your hard drive.
  • By scanning and perhaps opening the files present on your hard drive.

The only problem with the custom file monitoring approach is that it requires you to write some code. Not everybody is a programmer, and not every programmer has the time or the inclination to write code to investigate what may or may not be a privacy violation. So what we want is a free, easy to use, and robust tool that can monitor file accesses generically, for any application on the machine.

The freeware SysInternals Process Monitor, formerly known as FileMon, is just such a tool. Let's open up the Ultimate Bet and Full Tilt poker executables and see if we can turn up something interesting.

Well, okay. I'm cheating a bit. I've already blogged (just a few minutes ago) about ieSnare, a piece of pseudo-spyware which online poker sites use to track machine identities. So nothing new here. But the point is, when confronted with an unknown third-party application for which you have zero source code, one of the best things you can do is run it with file monitoring in place. You're looking for anything out of the ordinary. When you find something out of the ordinary, Google it. Then blog about it. Complain. Post it in the forums. Or leave me a comment and I'll blog about it. Or head over to pokerai.org and start up a thread.

It's that simple.

Viewing (and Modifying!) Embedded Resources with Visual Studio

Windows applications often ship with embedded resources: images, string tables, dialog layouts, icons, etc. An embedded resource is any resource which is placed inside a binary executable file such as an .EXE or a .DLL. Embedded resources are public. We can see a list of the resources embedded within a particular file by opening that file in an application that understands how to view embedded resources. One such application is Visual Studio. Let's open the Full Tilt poker client executable (FULLTILTPOKER.EXE) and see what we find.

The Full Tilt development team, following common practice, makes heavy use of embedded resources. We can see accelerators, dialogs, icons, a manifest, and a version resource. Let's open up one of the dialog resources. Specifically, let's open the Full Tilt Login dialog in the Visual Studio designer. We don't have access to the Full Tilt source code, but it doesn't matter. And Full Tilt would probably prefer we don't go mucking around with their resources, but there is nothing illegal or unethical about examining the software which runs on your machine.

Although there's no text (the text will be filled in at runtime), you can see two edit boxes; one for the user name and one for the password. The dark gray areas are custom controls. Let's right click one of these custom controls and pull up its properties. (Yes, we can edit these controls and their properties, even though we don't have the Full Tilt source code.)

The highlighted control is a custom control of class FTCSkinButton. That, of course, is a window class, not a C++ (or other language) class. This is the same information you'd get if you launched Full Tilt and pointed Spy++ at the above dialog, which looks like this at run time:

A little-known fact is that you can edit binary resources of any application, with or without the source code. Let's go in and rearrange the Full Tilt login dialog, just because we can. We'll move some controls around and (using Copy/Paste) add a new button at the top of the window:

We can then launch the Full Tilt software, and we'll see our modified version of the login dialog. This dialog is fully functional, though it looks a little cockeyed. We've changed the locations of some of the controls and added a couple new ones, but otherwise everything works the same.

This is a little bit of software sleight-of-hand, perhaps not too useful in the grand scheme of things, but illustrative. Now let's look at another application: the Cake poker client (CAKE.EXE).

Here we see some some other resources: string table, type library, RCDATA. Let's open the string table resource, which is a way of storing strings without "hard-coding" them into application source code.

If you've ever used printf, these strings should make sense. The %s and %d symbols are printf-style formatting flags, used when compositing a string. Which means that if we were to install a detour around the printf family of functions (there are several varieties on Windows), we'd expect our detour to be called with some of the above strings. Your mental gears should now be churning.

Snooping on Windows with Spy++

Windows applications typically display one or more windows—such as a "lobby" window, along with a dedicated window for each table. Spy++ allows you to spy on those windows in real time via a handy graphical interface. Simply drag the "Find Window" crosshairs around the screen, and the window beneath the tool will be highlighted.

There are several kinds of information you should be looking for when using this tool. First, Spy++ tells you whether or not a given on-screen widget—a button, a listbox, an image—is backed by an actual Windows window. In the following screenshot of the PokerStars cashier window, there are exactly seven windows: the dialog itself, and each of the buttons ("Buy Chips", "Cash Out", etc.) along the right.

For each window encountered, Spy++ will give you some basic information, such as the window's class, it's handle, the address of its window procedure, and so forth. If we drag the Find Window crosshairs to the "Buy Chips" button (a button many of you will be familiar with) we get the following: 

We can see at a glance that this is not a standard Windows button. A standard Windows button is always of class "BUTTON" whereas PokerStars buttons are of class "PokerStarsButtonClass". In order to make sense out of this, of course, you need to understand what a window class is, and how you'd go about creating a custom window class in code. But for now, suffice to say that once you know a window's class name you can programmatically search for that window using APIs such as FindWindowEx.

That's pretty handy, but the real power of Spy++ has always been its ability to snoop on window message traffic in real time. In order to do that, let's point Spy++ at a PokerStars table window. 

If you squint, you can see that PokerStars table windows are of class PokerStarsTableFrameClass. So again, we're dealing with a custom window class. In the Find Window dialog box, check the "Messages" radio button and hit OK to view window messages received by the PokerStars table window: 

If you've worked with window messaging before, most of these messages will make sense to you. We can see WM_PAINT, WM_TIMER; all the usual suspects. But if you take a closer look you'll see several non-standard messages: 

These messages are custom window messages which have a special meaning for the PokerStars application. The meaning of messages like WM_PAINT and WM_TIMER is standardized across all windows. But registered and user-defined messages can have any meaning whatsoever. Once you know that these messages are being sent, it's a simple matter to a) inject a DLL into the PokerStars address space and b) subclass the PokerStars table window so that you can c) take an in-depth look at these messages and their parameters in code.

That's really the value of Spy++. It's not that the tool gives you so much useful information in and of itself; but it gives you a basic navigational awareness of the windowing and messaging behavior of a particular piece of software, which knowledge you can then use to decide what sort of investigative code to write: which windows to subclass, which message pumps to spy on, and so forth.

Viewing DLL Dependencies

As software developers, and especially as developers of poker botting software, we're very interested in the dynamic link libraries (DLLs) loaded by a particular application. We can examine these DLLs to determine what libraries and subsystems the application relies on, and we can (perhaps) use that knowledge to replace these libraries with our own customized versions. Even if we're not interested in this sort of "trojan horse" approach, we still want to know what DLLs the application is loading, as a matter of principle. There are a number of tools you can use to do this. One of my favorites is the Dependency Walker (DEPENDS.EXE).

Let's use it to track the DLLs loaded by the Cake online poker client:

Nothing out of the ordinary here. Cake is using a standard set of operating system DLLs, and you'll see that most poker clients follow a similar pattern. But once in a while, you'll stumble across something interesting. To illustrate, let's crack open the Full Tilt poker executable (FULLTILTPOKER.EXE):

Will wonders never cease. You see, in addition to tracking DLL dependencies, the Dependency Walker also extracts functions exported by a particular binary. These are functions intended to be called by other EXEs and/or DLLs. Above, we see a list of functions provided by the Full Tilt poker client, most of them prefaced with "FTCG" which is probably short for "Full Tilt Client Something". And you can infer the likely purpose of many of these functions from the name. In fact, with a little detective work, you could try to call these functions yourself or, better yet, detour these functions such that whenever Full Tilt code attempts to call one of them, your own code is executed instead.

This information is publically available, using run-of-the-mill development tools, so here's the full list of functions:

  • void FTCG_LastHand_Header(struct HFTCGAME__ *,struct HFTCG_LASTHAND__ *,unsigned long,unsigned long,int,void (*)(unsigned long,int,wchar_t *,unsigned long,long),long)
  • int FTCG_TableEx_Submit_Accept(struct HFTCG_TABLEEX__ *,unsigned long,unsigned char)
  • CentsFromString
  • FTCG_BuyIn_Create
  • FTCG_BuyIn_Destroy
  • FTCG_BuyIn_HasEnoughFunds
  • FTCG_BuyIn_Init
  • FTCG_BuyIn_Submit
  • FTCG_Casino_Create
  • FTCG_Casino_DCS_AdvCurrent
  • FTCG_Casino_DCS_AdvInterval
  • FTCG_Casino_DCS_Create
  • FTCG_Casino_DCS_Current
  • FTCG_Casino_DCS_Current2
  • FTCG_Casino_DCS_ExitScreenCurrent
  • FTCG_Casino_DCS_GeoCountryIx
  • FTCG_Casino_DCS_ProChat
  • FTCG_Casino_DCS_PromoCurrent
  • FTCG_Casino_Destroy
  • FTCG_Casino_Get_LobbyIxNext
  • FTCG_Casino_Get_LobbyIxToLobby
  • FTCG_Casino_Get_PlayerIx
  • FTCG_Casino_Get_Stat
  • FTCG_Casino_IsBelongLobbyIx
  • FTCG_Chat_AddText
  • FTCG_Chat_ConvertType
  • FTCG_Chat_Create
  • FTCG_Chat_Destroy
  • FTCG_Chat_Get_Line
  • FTCG_Chat_Get_LineInfo
  • FTCG_Chat_Get_NextEvent
  • FTCG_Chat_Get_NextEvent2
  • FTCG_Chat_Get_PlayerIx
  • FTCG_Chat_Get_TableId
  • FTCG_Chat_Get_TournSummary
  • FTCG_Chat_IsDisplay
  • FTCG_Chat_IsPlayerNoteAvail
  • FTCG_Chat_Submit
  • FTCG_Create
  • FTCG_Data_Get_HappyHour
  • FTCG_Data_Get_MixedGames
  • FTCG_Data_IsMoneyGram
  • FTCG_Destroy
  • FTCG_FormatError
  • FTCG_Format_Comma
  • FTCG_Format_Game
  • FTCG_Format_GameURL
  • FTCG_Format_Money
  • FTCG_Format_MoneyW
  • FTCG_Format_TableTitle
  • FTCG_GetGeoCountry_Tmp
  • FTCG_GetGeoState_Tmp
  • FTCG_Get_GeoCountry
  • FTCG_Get_GeoState
  • FTCG_Get_PlayerInfo
  • FTCG_Get_VersionConnect
  • FTCG_IsMessage
  • FTCG_LastHand_AddHand
  • FTCG_LastHand_Create
  • FTCG_LastHand_Destroy
  • FTCG_LastHand_Footer
  • FTCG_LastHand_Get_CardState
  • FTCG_LastHand_Get_Count
  • FTCG_LastHand_Get_FirstHand
  • FTCG_LastHand_Get_Hand
  • FTCG_LastHand_Get_PlayerActive
  • FTCG_LastHand_Get_PlayerId
  • FTCG_LastHand_Get_Winners
  • FTCG_LastHand_Output
  • FTCG_Legacy_UpdateStamp
  • FTCG_ListView_CmpLobbyMixed
  • FTCG_ListView_CmpLobbyTables
  • FTCG_ListView_CmpProChat
  • FTCG_ListView_CmpSatellite
  • FTCG_ListView_CmpSitNGo
  • FTCG_ListView_CmpTablePlayers
  • FTCG_ListView_CmpTablePlayers2
  • FTCG_ListView_CmpTableWaiters
  • FTCG_ListView_CmpTourn
  • FTCG_ListView_CmpTournTablePlayers
  • FTCG_ListView_CmpTournTables
  • FTCG_ListView_LobbyMixed
  • FTCG_ListView_LobbyTable
  • FTCG_ListView_LobbyTourn
  • FTCG_ListView_ProChat
  • FTCG_ListView_TablePlayer
  • FTCG_ListView_TableWaiter
  • FTCG_ListView_TournPlayer
  • FTCG_ListView_TournTable
  • FTCG_ListView_TournWaiter
  • FTCG_Lobby_Create
  • FTCG_Lobby_Destroy
  • FTCG_Lobby_GetCached_Table
  • FTCG_Lobby_GetCached_TableNext
  • FTCG_Lobby_GetCached_TableTitle
  • FTCG_Lobby_GetCached_Tourn
  • FTCG_Lobby_GetCached_TournDetail
  • FTCG_Lobby_GetCached_TournID
  • FTCG_Lobby_GetCached_TournInfo
  • FTCG_Lobby_GetCached_TournNext
  • FTCG_Lobby_Get_Tourn
  • FTCG_Lobby_Get_TournClose
  • FTCG_Lobby_Get_TournGameInfo
  • FTCG_Lobby_Get_TournNext
  • FTCG_Lobby_Get_TournState
  • FTCG_Lobby_Get_TournSummary
  • FTCG_Lobby_Get_TournTitle
  • FTCG_Lobby_Get_TournTokenPrize
  • FTCG_Lobby_IsLoaded
  • FTCG_Lobby_IsTableProChat
  • FTCG_Lobby_IsTableRingGame
  • FTCG_Note_Get
  • FTCG_Note_GetPlayer
  • FTCG_Note_Init
  • FTCG_Note_Set
  • FTCG_Note_SetIgnoreChat
  • FTCG_Note_Submit_Metrics
  • FTCG_Playback_GetText
  • FTCG_Playback_Update
  • FTCG_Player_Create
  • FTCG_Player_Decrypt
  • FTCG_Player_Destroy
  • FTCG_Player_Encrypt
  • FTCG_Player_GetCached_Balance
  • FTCG_Player_Get_Address
  • FTCG_Player_Get_AffiliateInfo
  • FTCG_Player_Get_Avatar
  • FTCG_Player_Get_Balance
  • FTCG_Player_Get_BonusHappyHour
  • FTCG_Player_Get_Bonus_OptIn
  • FTCG_Player_Get_BuyInPrefs
  • FTCG_Player_Get_DepositBonuses
  • FTCG_Player_Get_DisconnectTime
  • FTCG_Player_Get_Email
  • FTCG_Player_Get_Language
  • FTCG_Player_Get_Login
  • FTCG_Player_Get_LoginFailure
  • FTCG_Player_Get_Name
  • FTCG_Player_Get_OfferPlayer
  • FTCG_Player_Get_OptIns
  • FTCG_Player_Get_ParamsURL
  • FTCG_Player_Get_PlayerIx
  • FTCG_Player_Get_PromoURL
  • FTCG_Player_Get_SecureURL
  • FTCG_Player_Get_Tables
  • FTCG_Player_Get_TournRegistered
  • FTCG_Player_Get_Tournaments
  • FTCG_Player_Get_TransferHistory
  • FTCG_Player_Get_Transfers
  • FTCG_Player_Get_UserName
  • FTCG_Player_IsAddressValid
  • FTCG_Player_IsAllowTournRegister
  • FTCG_Player_IsCountryValid
  • FTCG_Player_IsEmailVerified
  • FTCG_Player_IsExcluded
  • FTCG_Player_IsNameValid
  • FTCG_Player_IsPhone
  • FTCG_Player_IsRightAvailable
  • FTCG_Player_IsStatusAvailable
  • FTCG_Player_IsTournRegistered
  • FTCG_Player_IsTransferPending
  • FTCG_Player_Redeem_GetCommission
  • FTCG_Player_Submit_Address
  • FTCG_Player_Submit_AddressName
  • FTCG_Player_Submit_Affiliate
  • FTCG_Player_Submit_Avatar
  • FTCG_Player_Submit_BonusOptIn
  • FTCG_Player_Submit_BuyInPrefs
  • FTCG_Player_Submit_Chat
  • FTCG_Player_Submit_Create
  • FTCG_Player_Submit_Data
  • FTCG_Player_Submit_Deny
  • FTCG_Player_Submit_Deposit
  • FTCG_Player_Submit_Disconnect
  • FTCG_Player_Submit_Email
  • FTCG_Player_Submit_EmailNotification
  • FTCG_Player_Submit_EmailRequest
  • FTCG_Player_Submit_EmailVerify
  • FTCG_Player_Submit_Exclude
  • FTCG_Player_Submit_Find
  • FTCG_Player_Submit_Language
  • FTCG_Player_Submit_Login
  • FTCG_Player_Submit_Password
  • FTCG_Player_Submit_PasswordRequest
  • FTCG_Player_Submit_PasswordReset
  • FTCG_Player_Submit_PlayReload
  • FTCG_Player_Submit_PlayerExists
  • FTCG_Player_Submit_Redeem
  • FTCG_Player_Submit_RemoveAllWaitlist
  • FTCG_Player_Submit_Store_BuyProduct
  • FTCG_Player_Submit_TableDecline
  • FTCG_Player_Submit_Transfer
  • FTCG_Player_Submit_WaitingListAdd
  • FTCG_Player_Submit_WaitingListRemove
  • FTCG_Player_Submit_Withdraw
  • FTCG_Player_Submit_WithdrawVoid
  • FTCG_ProChat_Create
  • FTCG_ProChat_Destroy
  • FTCG_ProChat_Get_Info
  • FTCG_ProChat_Submit
  • FTCG_ProChat_Submit_End
  • FTCG_ProChat_Submit_Leave
  • FTCG_ProChat_Submit_Return
  • FTCG_ProChat_Submit_Start
  • FTCG_ProChat_Submit_Topic
  • FTCG_ProcessObjectMessage
  • FTCG_Reload
  • FTCG_Set_Language
  • FTCG_Set_Notification
  • FTCG_Start
  • FTCG_Stat_GetData
  • FTCG_Stat_GetGameHiLo
  • FTCG_Stat_NotifySet
  • FTCG_Stat_Reset
  • FTCG_Stop
  • FTCG_Submit_System_ClientTracking
  • FTCG_Submit_System_IESnare
  • FTCG_TableCheckHappyHour
  • FTCG_TableCreate
  • FTCG_TableDestroy
  • FTCG_TableEx_Create
  • FTCG_TableEx_Destroy
  • FTCG_TableEx_GetCached_CommunityCards
  • FTCG_TableEx_GetCached_HandInfo
  • FTCG_TableEx_GetCached_HandPlayers
  • FTCG_TableEx_GetCached_HappyHour
  • FTCG_TableEx_GetCached_Info
  • FTCG_TableEx_GetCached_IsPlayerPro
  • FTCG_TableEx_GetCached_IsWaiterPro
  • FTCG_TableEx_GetCached_Players
  • FTCG_TableEx_GetCached_TablePlayerInfo
  • FTCG_TableEx_GetCached_WaiterPlayerInfo
  • FTCG_TableEx_GetCached_Waiters
  • FTCG_TableEx_Get_TitleInfo
  • FTCG_TableEx_Get_WaitListState
  • FTCG_TableEx_Submit_Bet
  • FTCG_TableEx_Submit_BuyIn
  • FTCG_TableEx_Submit_Chat
  • FTCG_TableEx_Submit_Emotion
  • FTCG_TableEx_Submit_Fold
  • FTCG_TableEx_Submit_Leave
  • FTCG_TableEx_Submit_Muck
  • FTCG_TableEx_Submit_RequestTime
  • FTCG_TableEx_Submit_Return
  • FTCG_TableEx_Submit_Show
  • FTCG_TableEx_Submit_Sit
  • FTCG_TableEx_Submit_Stand
  • FTCG_TableFTM
  • FTCG_Time_GetServerTime
  • FTCG_Time_SetTimeDiffProc
  • FTCG_Tourn_Create
  • FTCG_Tourn_Destroy
  • FTCG_Tourn_Get
  • FTCG_Tourn_Get_AllPlayers
  • FTCG_Tourn_Get_BuyIns
  • FTCG_Tourn_Get_GameNameInfo
  • FTCG_Tourn_Get_Level
  • FTCG_Tourn_Get_Levels
  • FTCG_Tourn_Get_Payouts
  • FTCG_Tourn_Get_PlayerTable
  • FTCG_Tourn_Get_PlayerWaitStatus
  • FTCG_Tourn_Get_SngTimeLeft
  • FTCG_Tourn_Get_StackInfo
  • FTCG_Tourn_Get_State
  • FTCG_Tourn_Get_TablePlayerInfo
  • FTCG_Tourn_Get_TablePlayers
  • FTCG_Tourn_Get_TournIxRef
  • FTCG_Tourn_Get_Winner
  • FTCG_Tourn_IsFreeroll
  • FTCG_Tourn_IsPlayerPro
  • FTCG_Tourn_IsRegistered
  • FTCG_Tourn_IsTablePlayerPro
  • FTCG_Tourn_IsTablePro
  • FTCG_Tourn_IsTableValid
  • FTCG_Tourn_IsTwoBlinds
  • FTCG_Tourn_SetTable
  • TCG_Tourn_Submit_BuyIn
  • FTCG_Tourn_Submit_Rematch
  • FTCG_Tourn_Submit_Withdraw
  • FTCM_Browser_Attach
  • FTCM_Browser_GetFormData
  • FTCM_Browser_InsertHTML
  • FTCM_Browser_InsertURL
  • FTCM_Browser_Resize
  • FTCM_Browser_UnAttach
  • _WndProcHTML@16

Eureka! That's quite a laundry list. Maybe the Full Tilt development team will hide these exports at some point so we don't have to stare at them.

Monitoring Your System Registry

Whether you're a programmer or not, you've heard of the Windows Registry: a massive database of operating system and application data and metadata, used to store everything from COM object registrations to user settings. I am here to tell you that many applications (and online poker clients specifically) quite simply do not respect your privacy when it comes to the Registry. A software application can infer a LOT about you and your machine by examining the Registry, and yet, you'll find that companies, by and large, feel they have the right to do whatever sort of snooping your Registry they desire.

What you need is oversight: the ability to determine what data an application is reading from, and writing to, your Registry. If you've been following the botting series, you already know how to detour the Registry APIs and insert your own logging code to determine what registry keys are being read and written. But you needn't write code to do this; you can also use a tool like the SysInternals Process Monitor, formerly known as RegMon.

Let's open up the PokerStars executable (POKERSTARS.EXE) using the Process Monitor, and filter out everything except registry-related accesses:

Well, we can see that PokerStars is a busy bee. Indeed, if you run the registry monitor against five or six online poker clients, I think you'll be surprised at just how busy they are. What are they reading and to what end?

Viewing Embedded Strings with a Hex Editor

When you use string literals in code...

void someFunction()
{
   string encryptionPassword = "SuperSecretPassword";
   encrypt_something(encryptionPassword);
}

...those string literals are (by default) embedded in the final executable (whether EXE or DLL) you produce, and can be read by anyone with a decent hex editor.

Now, there are a ton of hex editors available for Windows and other operating systems, and they all work roughly the same: by presenting an unobstructed view of the byte-by-byte data of a particular file (could be a data file, could be an executable file, could be a text file). Typically, you'll see the raw data presented in hexadecimal notation in one column, and in another column, you'll see what the data looks like when you try to interpret it as text character data. Note that some (most) numeric values have no printable character equivalents, and for these you'll see a dot or other symbol.

To illustrate, let's open up the PokerStars client (POKERSTARS.EXE) inside the freeware HEdit hex editor.

We can see various embedded strings, some of which leak implementation details. We notice a C++ source file, formathistory.cpp. We see various internal and external messages. We see various printf format specifiers, and so forth. There are no hard and fast rules about what to look for when you crack open a particular application in a hex editor. Typically, you'll be looking for strings which give you clues as to the application's implementation. With a little detective work you can also identify meaningful non-string data; but in order to do so, you'll probably have to perform some basic disassembly and/or instrumentation in order to figure out that X piece of data exists at Y memory location which corresponds to the Zth byte of the application's executable file. You can then go in and modify that particular byte and produce behavior which is different from what the original developers intended.

I'm not going to explain how to "crack" legitimate commercial software, but suffice to say this is where it begins: by understanding that software integrity is a myth, AT LEAST ON THE CLIENT SIDE. Every byte of every of every application is public, both on disk and later, when loaded into memory. You can't hide the underlying binary instructions from themselves. You can't hide the hard-coded strings, the resources, the data structures these instructions use. The best you can do is obfuscate.

Conclusion

Today we covered the easy stuff: the low-hanging fruit. Later we'll talk about disassembling, debugging, and full-on instrumentation of poker client software. We'll look at encrypted socket connections, and we'll look at how to hook client-side decryption/encryption routines, not that it will do us a whole lot of good. And as a bonus, we might even talk about how to figure out what those pesky browser add-ins (you know, the ones that have access to every web page you visit and every form you fill out including credit card forms) installed by the poker sites actually do.

Good luck in your own investigations of poker and gambling client software and remember, none of this is rocket science. There is absolutely nothing special about poker client software. In fact, poker client software can often be described as a sort of celebration of worst-practices software development: perpetually rushed, in an effort to get those features to market and start collecting that rake!!

Until next time...

Tags: poker bot, Process Monitor, C++, online poker, poker

74 comment(s)

Awesome post James! Keep up the good work!

A very nice article... I will have to dig into what the various clinents I have are really doing on my computer...

thanks for the good work!

James,

does the UAC limit access to the registry when apps are run not as administrator?

i assume (can't remember) that the poker clients require running as admin....

Nice to see you back on the PB track! Thanks man!

Just a little hint for grabbing just the strings from a binary, use sysinternals Strings.exe from the MS website. In fact I suggest grabbing the entire Sysinterals Suite, that way you'll grab everything and then just plop it in to a handy directory.

I'm sure James you knew this all ready. ;-)

bcd, the uac virtualizes access to the registry / file system, so non-elevated processes get directed to a sandbox and certain reads may fail altogether. at least that's what im seei

at least that's what i'm seeing. james, i like where you're going with this article, but you really need to bring preview or edit back for the comments!!!

More crap.

Looks like Full Tilt has been caught with their pants down yet again.

I saw the full list of functions you published and...wow. Are these guys just ASKING us to bot or what? I seriously think these sites include tie-ins for frikkin' bots. They can't talk about them, can't publicize them, and when someone's caught botting they have to put him in the penalty box but come on.

These sites want bots, which is why they include all this crap.

Either that or they know nothing, zilch, zip, squat about security.

And either way it's kinda scary.

@ "Eureka! That's quite a laundry list. Maybe the Full Tilt development team will hide these exports at some point so we don't have to stare at them."

Haha, you had me laughing quite a lot with that line. Fulltilt, you have been burned! :D

Actually some of these techniques are useful. Like robotdog said, pretty much everything from Sysinternals is good. Download the whole Sysinternals package, and run every tool against every poker client you have installed.

I'm not techy enough to understand a lot of abbreviations you use but I definitely found this interesting. Thanks :)

Every post reinvigorates my desire to code.

"Every post reinvigorates my desire to code."

Well, I don't know if I feel the same. I have made a basic bot following these articles (just pre-flop for now, but in a week or less it will be working in the other stages too). And every time I think I'm coming to an end (or a "first end" from which I could add more functionalities, and debug mistakes, and improve intelligence) James writes something that makes me realize that there is still a long path before me.

Three months ago, I was a JEE programmer, knowing just the basics of C++. Since then, I've learnt to use C++ with Windows API, I've learnt to use Detours, I've learnt to program desk applications with Visual Studio, I've learnt C#, I've learnt to make DLL's and import DLL's, I've learnt to import unmanaged structures into managed code, I've learnt to use Hooks... That's quite a lot of things. But it never seems to be enough. :)

Hi James,

Nice post. I am just wondering why poker sites dont try to do something to protect their software from this kind of reverse engineering. As you say they are already observing a great deal about what you are doing on your computer. Couldn't they just scan the running applications and decide that they are not going to allow their client to run on a system that has Visual stuido or Spy ++ or any other application running at the same time?

Although I do aggre with the post above that sites (especially Fulltilt) want bots given that they don't care who is playing as long as a rake is being taken.

A lot of software developers just think their clients are idiots and take no precautions against their snooping.

I agree though - you have the right to do anything you want with any data on your computer.

Anyone else get an email from PokerStars? :)

We at PokerStars have noticed that recently you have tried out a program called "XPokerBot" by Coding The Wheel". We would like you to know that among this program's features are automated robot playing that makes it against the rules of PokerStars.

....

Please don't protest that you didn't use XPokerBot, or that it did not actually play any poker. We are aware that codingthewheel.com has not yet published a complete working bot capable of poker logic. Even the degree of experimentation you have done thus far is against our terms of service, and cannot be permitted. Therefore, any reply that doesn't include something like "I understand that bots are against the rules and I won't use or experiment with them on PokerStars" will only delay the re-opening of your account.

Not with a bang, but with a wimper.

@Kaput: Oh please. The PokerStars "list of disallowed software" is here:

http://www.pokerstars.com/poker/room/prohibited/

That includes totally innocuous sites like www.sharkscope.com and Sit and Go EGT. Just rename the XPokerBot binaries, rename the public function exports, and add some other code to change the binary image. Make sure there's no easily identifiable pieces and you're good to go. I've been running since Day 1 with nary a word from PStars.

thank you. good advice.

I've been running XPokerBot as-is, with no modifications, and haven't gotten any such email. Maybe that's because I do everything through a virtual machine...? Or is this some sort of comment hoax?

Obviously a comment hoax. Either that or PokerStars writes really unprofessional emails.

Why does codingthewheel delete comments from people who oppose your efforts to steal and cheat poker players?

Surely the morality of this is as important as your self-righteous claims to be defeating poker sites, when in reality you're trying to steal money from other players - real humans.

I don't think it's a hoax. I read a lot of Mails from the Poker Stars Support so far and these often sound like this one. I wouldn't call it unprofessional, it's just individually written.

I posted about the email and it is not a hoax..

Here is the entire email:

We at PokerStars have noticed that recently you have tried out a program called "XPokerBot" by Coding The Wheel". We would like you to know that among this program's features are automated robot playing that makes it against the rules of PokerStars.

Perhaps you were unaware that XPokerBot was against the rules, or were just curious about the program, but use of this software under any circumstances is against our terms of service. Those terms of service are located here:

http://www.pokerstars.com/poker/room/tos/

Among them are these terms:

5.5. AUTOMATIC PLAYERS (BOTS). The use of artificial
     intelligence including, without limitation, "robots" is 
     strictly forbidden in connection with the Service. All 
     actions taken in relation to the Service by a User must 
     be executed personally by players through the user
     interface accessible by use of the Software.
5.6. You agree that PokerStars may take steps to detect and
     prevent the use of prohibited EPA Programs. These steps 
     may include, but are not limited to, examination of 
     software programs running concurrently with the PokerStars
     Software on the User's computer.
5.8. FRAUDULENT BEHAVIOR. In the event that PokerStars deems 
     that a User has engaged .... in any of the activities set 
     forth above or any other game manipulation ... PokerStars 
     shall be entitled to take such action as it sees fit, 
     including immediately blocking access to the Service,
     terminating such User's account with PokerStars, seizing
     all monies held in the User's PokerStars account... 

PokerStars takes its obligation to the integrity of its games seriously, and cannot permit such programs as XPokerBot to operate.

We realize that you probably were unaware of the forbidden nature of this program, and therefore we will not take any of the above stern reactions due to this detection. Instead, all we ask is that you reply to this Email stating that you're now aware of the rule against bots, and that you won't use such programs on PokerStars again in the future.

Until we receive such an acknowledgement, we have temporarily suspended your PokerStars account. The account closure isn't intended as punishment nor anything other than to get your attention and to have your acknowledgement of the rules on record. Once you've acknowledged the above rules and agreed to refrain from the use of "bots", we will be pleased to restore your account to good standing.

Please don't protest that you didn't use XPokerBot, or that it did not actually play any poker. We are aware that codingthewheel.com has not yet published a complete working bot capable of poker logic. Even the degree of experimentation you have done thus far is against our terms of service, and cannot be permitted. Therefore, any reply that doesn't include something like "I understand that bots are against the rules and I won't use or experiment with them on PokerStars" will only delay the re-opening of your account.

Thank you for your cooperation in this matter.

Best Regards,

Jeff PokerStars Game Security

lol...oh WOW. POKERSTARS SAID THIS???

"Please don't protest that you didn't use XPokerBot, or that it did not actually play any poker. We are aware that codingthewheel.com has not yet published a complete working bot capable of poker logic. Even the degree of experimentation you have done thus far is against our terms of service, and cannot be permitted. Therefore, any reply that doesn't include something like "I understand that bots are against the rules and I won't use or experiment with them on PokerStars" will only delay the re-opening of your account."

It's the 'please don't protest that you didn't use XPokerBot....' malarkey that floors me. XPokerBot was never a working bot. but since when do multi-million dollar online poker sites indulge in this KIND OF LANGUAGE??? wowowow

I shit you not.. That is the real deal. Mail came from support@pokerstars.com

yeah i just got the same email yesterday.

Well it was bound to happen sooner or later. James, you think now is maybe a good time to talk about stealth??

?

?

Ah but Ed, to talk about stealth, is to make it no longer stealth.

Then what?

thank you james... keep writing please :)

This is dumb, I guess, but could you explain in more detail how to use VS to look at the resources in an .exe file? I loaded the file into VS, but I couldn't figure out how to display the resources. I'm using VS2005 with SP1 and the Vista update under Windows Vista. Thanks

Back in May (You): Well, I'm here to tell you that online poker bots are 100% real, and I know this because I've built one. And if I can build one, well. Anybody can build one. What's more, over the course of this multi-part article, I'll show you how.

Don't get me wrong - I have enjoyed your blogs! I have been with you all the way James, although I am not a strong programmer, I have faithfully followed your blogs and attempted every trial. Even downloaded Visual Studio and studied C++.

I am still ready to build that poker bot. I probably need a refresher as the span of attention is working against me.

Would it be possible if you could give me a short (or long) flow chart or task list or something so I can disregard the superfluous and try to get a working bot going? I seem to be lost in the tangle of Electric Guitars, Monty Hall, Harry potter etc.

@James "To play the blues, you've got to pay your dues"

@James, First I would like to say that I love to read your posts!

Could you share your thoughts and experience about the below "delegate.exe" solution for getting the data from the client?

a way for reading some data is using the delegate proxy and then connecting the program which use SSL to it, for example: delegate.exe -v SERVER=tcprelay://<PokerServerIP>:26002 STLS=mitm FTOSV=-tee-n ADMIN=test -P127.0.0.1:<PokerServerPort>

Why not use the above? It seems much - easier to me, although it needs off course some extra code... - safer to me, especially because you don't need to take precautions for spyware if you run it outside for example a VMWare workstation?

I hope you can quickly respond to my question!

I'm new to Dependency Walker. If I open FullTiltPoker.exe in D.W., I don't see any of its FCTG_ functions as listed in this article. I only see any functions in D.W.'s "PI" or "E" function areas if I select one of the dependencies, not FullTiltPoker.exe itself. What am I missing? I'm running D.W. 2.2.6000, downloaded from dependencywalker.com because VS2008 doesn't seem to include it anymore.

Has anyone gotten anywhere with the input stage on Ultimate Bet? Collecting hole cards and betting activity?

@Rodge

I think that you're right, I don't see them either. Maybe FT saw they were exposed and changed their function names...

I'm very lost on how to intercept the text Ultimate Bet is displaying in their client.

I modified the MonitorBot downloadable in the Part 5 (Deciphering Poker Stars and Full Tilt) to support Ultimate Bet. I had the program detour DrawText, TextOut, ExtTextOut, and DrawTextEx, since I wasn't sure which UB would be using. I also had it detour CreateFile, WriteFile, CreateProcess, and OpenProcess.

My modified MonitorBot successfully shows the UB venue and the tables open. It successfully shows the detoured function calls, of which there are very many of them.

Unfortunately, nothing useful seems to be trapped. Even if chat is set to Full Dealer Chat, nothing displaying in the chat window is trapped. Besides seeing UB flipping through its various banner adds in its lobby Window, and seeing UB call DrawTextEx to change the poker table window's table, there's nothing useful in there. If I hover over the pot, it successfully shows a DrawText & DrawTextEx call showing the "Main Pot is $XX after $YY rake". If I right click in the chat window, it successfully shows the DrawTextEx call showing the "Options" text.

Anyone get anywhere with UB?

James, any chance you would post your UB input code - perhaps in another release of your MonitorBot?

Below is a link to my VS2008 project, which is the MonitorBot downloadable in Part 5 of this series, with code I added to handle Ultimate Bet. Note that I didn't spend a lot of time on UltimateBetCaptionDecoder.cpp -- I have no idea if it parses the caption correctly. I was mainly focusing on trapping the API calls to see there was something there I could use.

You can download the modified MonitorBot VS2008 project at:

http://sigma.homeunix.com/XPokerBot-Monitor-WithUB.zip

James! Thank you for a very interesting and educational article.

When I found your site about 3 weeks ago, I was a professional programmer with ZERO experience coding Windows applications or using VS. I had not used C/C++ in about 10 years (switched to Java and never looked back) and have never had anything but general replies "How do they make viruses" when asked by my non-technical friends/family. Thanks to you, that's all changed!

I have since installed VS 2005, gathered lots of the interesting software you mentioned, gone through your examples and finally started a project I've been wanting to do for 2 years now - write my own helper-app... and eventual bot.

Thanks for taking the time to write this all up. This should go without saying, but with the hope that at least one person will remember and be influenced, thank you for using proper English. It's such a pleasure to read compared to the grammatical, punctual and spelling nightmares often found in forums.

Hi all,

I've been following this saga very closely and I can only tell James to keep up the good work!

Thanks to the series I'm building my own just-for-fun-bot and there is one matter i haven't been able to solve:

If you resize a PokerStars table through code (using MoveWindow or SetWindowPos) its client area won't get repainted. I've spent nearly 20 hours already with this, calling a lot of apis, using Spy++ to see what messages the window receives when you resize it with the mouse (in which case it obviously gets repainted). It won't update itself if you minimize it, move it, etc. The simplest way (I think) is by clicking on an edge of it, so I've tried sending that kinda messages but with no luck...

Has anyone addressed this?

Gabriel

Hi,

2Gabriel:

Try to insert InvalidateRect(hWndOfPSClient, NULL, TRUE) after the call of MoveWindow/SetWindowPos

Regards, Andrew

First of all thanks for a great series. It started me down a path of long nights and endless despair, when I get hopelessly confused over the Windows interface. :-) I was wondering if anyone has a pointer to how to solve the following problem.

I am trying to intercept the SetWindowText function but it crashes the Stars as well as the FT clients even in the case of not doing anything. My definitions are as follows:

BOOL (WINAPI * Real_SetWindowTextW)(HWND hWnd, LPCWSTR lpString) = SetWindowTextW;

BOOL MineSetWindowTextW(HWND hWnd, LPCWSTR lpString) { return RealSetWindowTextW(hWnd, lpString); }

and the usual mumbo jumbo to attach the detour, any other of the detour functions work. I am perplexed?

Andrew,

Thanks for your reply, really. But it didn't work, its like pokerstars knows if you are "injecting" the event rather than actually using the mouse to resize...

Gabriel

[quote] BOOL (WINAPI * Real_SetWindowTextW)(HWND hWnd, LPCWSTR lpString) = SetWindowTextW;

BOOL MineSetWindowTextW(HWND hWnd, LPCWSTR lpString) { return RealSetWindowTextW(hWnd, lpString); } [/quote]

Unless I'm missing something thats right, so if you program crashes its you are either not calling the detours library as you should, or the bug is somewhere else.

The problem seems to have been in the calling convention there is a missing WINAPI specification and the compiler defaults to cdecl. It should be:

BOOL (WINAPI * Real_SetWindowTextW)(HWND hWnd, LPCWSTR lpString) = SetWindowTextW;

BOOL WINAPI MineSetWindowTextW(HWND hWnd, LPCWSTR lpString) { return RealSetWindowTextW(hWnd, lpString); }

"Unless I'm missing something thats right"

I missed the missing WINAPI...

I made a Poker Bot by my self using some of those methods. You can find it on my website. winpokerbot.com

I received this e-mail:

Hello Julio,

We at PokerStars have noticed that you have recently been developing and testing a custom program designed to play poker without human intervention, commonly known as a "bot".

Perhaps you were unaware that such programs were against the rules, but use of this software under any circumstances is against our terms of service.

[bla bla bla]

PokerStars takes its obligation to the integrity of its games seriously, and cannot permit such programs as yours to operate.

We realize that you probably were unaware of the forbidden nature of this program, and therefore we will not take any of the above stern reactions due to this detection. Instead, all we ask is that you reply to this Email stating that you're now aware of the rule against bots, and that you won't use such programs on PokerStars again in the future.

Until we receive such an acknowledgement, we have temporarily suspended your PokerStars account. The account closure isn't intended as punishment nor anything other than to get your attention and to have your acknowledgement of the rules on record. Once you've acknowledged the above rules and agreed to refrain from the use of "bots", we will be pleased to restore your account to good standing.

Please don't protest that you didn't use bots in your response or otherwise object to this mild warning. Any reply that doesn't include something like "I understand that bots are against the rules and I won't use them on PokerStars" will only delay the re-opening of your account.

So I answered the following:

I understand that bots are against the rules and I won't use them on PokerStars.

However, I would like to say that my so called "bot" doesn't and wasn't intended to play for itself nor interact with PokerStars. Its only purpose was to retrieve hand and hole cards and to perform statistical calculations on top of that, just to aid MYSELF (wasn't intended to be distributed in any way) at the time of making decisions. There are a lot of tools for this out there and I have not made use of any of them, so I would like to know if there are any rules against building a custom and personal tool for this. If so, i won't continue with this project.

This is true, I hadn't done any approaches on auto-playing, however they answered the following:

Unfortunately, we cannot accept your explanation, as it does not match many of the observations we have made regarding your account and play.

I regret that I cannot detail for you exactly what evidence we have that contradicts your statement below, but we cannot reveal our methods of tracking bots. To do so would be to teach bot developers how to avoid being detected.

Suffice to say, we know with certainty that you were developing a fully capable automated bot, rather than a mere odds calculator.

Normally such an offense would result in instant barring from PokerStars, but as we had only warned you yesterday, we will stick with that warnning. As such, we accept your acknowledgement and have re-opened your account.

Please note that any further evidence of bot development or bot use will result in the closure of your account and the confiscation of any funds within it.

James Thnx for this series, we all can not wait for the next article. But in the meantime i have a question and maybe some one can help me. I am trying to use these techniques on PokerTime, but can some one give us a list of all the WinApi calls that he is detouring so we help the entire human race including me. Cause i am stuck at this point, and aint wanne rely on a screen scraper.

Thnx

Im pretty much at the same position as HappyProgrammer. Im trying to "redo" my screen scrapping classes and im kinda stuck at this part here. "a) inject a DLL into the PokerStars address space and b) subclass the PokerStars table window so that you can c) take an in-depth look at these messages and their parameters in code.".

You make it sound so easy. I dont get b). Subclass the window? exactly what does this mean? Is there anyone around here that can point me in a direction? Thanks a lot

@Gabriel: do not check this website from the machine you play in PokerStars ;-)

are you implying this site is like run by pokerstars? lol

No, I'm saying they track the websites you visit through crap they install in your computer. If you do not disable it (i.e. browser add-ins), they will know you've been here.

Nice work! And at the moment i play at stars argh :)

Wow. This is a real eye-opener. Thanks for adding to my privacy arsenal against online poker sites. It is important that we do not have foreign programs snooping on our machines. Who knows the real intentions of these gambling companies. I sure don't claim to.

Great series, lots of information and well written (which is nice these days).

I'm having the same problems as some of the others; I open the FT exe in dependency walker but I don't see the FTCG_ functions that are shown here. Have they (FT) changed their coding or are we doing something wrong here? Thanks so much for these articles and keep up the humor. The part about "They are busy bees..." made me laugh so hard I woke up the damn wife at 2:30 AM, which is only good for one thing but not while I'm studying poker....

To misquote quote Fahrenheit 451 - "To learn how to hide something, you must first learn how to find it."

If you have grasped enough so far to code enough to be dangerous, you should be able to use those same techniques to find your own bot. GMER is a great free tool that shows loaded modules, including DLLs they don't belong. Possibly SysInternals does the same thing.

Try enumerating all the processes on your machine, see what you find, and then work out a game plan. Try playing with the Process.GetProcesses Method in .NET, then consider the viability of hooking the underlying WIN32 functionality so you can be notified whenever anything goes snooping.

After you have done that, find a way to detect that you've done it - eg the Registry entry you may have used for a global hook.

Repeat process until insane.

@451 - Awesome. "Repeat process until insane." Youve just described my state of mind!!!

I played at P Stars a few days ago on a VMware virtual machine containing W2k and logged in as admin. Logged out from PS. Surfed the Webb from host (Vista) using Firefox and visited WinHoldem homepage (didn't download anything). I then moved to the virtual machine again and started P Stars again. Now P Stars tells me that I'm running/using (don't remember) WinHoldem. How could they identify what I had in my browser on my host? It scares me!

Clipboard contents?

Haha your fucking idiot. Glad to see Pokerstars suspended your account.

Wow, cool site... ran across it looking for tips on using windbg, but this is excellent stuff. And just to piss off PokerStars, Hoagland's book 'Exploiting Online Games' gets a bit into how to mask one's self from the prying eyes of the security goons

After reading all this I am now never playing online poker again. Truly you cheating clowns should be putting all that knowledge into something more important then creating ways to cheat online gaming sites. This is what our education system has done. We are now raising corrupt punks who are only interested in the quick dollar and not ever having to actually work for it.

Nobody cares if you play online poker, you ignorant hack. Except maybe the people you keep dropping money on when you pretend you can handle the high stakes. How's it feel to know that the world considers you living proof of variance?

Impressive post! I like reading it. adjustable beds

Here is a fix for SetWindowPos issue: WinApi.SendMessage(Handle, WinApi.WmEntersizemove, 0, null); WinApi.SetWindowPos(Handle, IntPtr.Zero, Left, Top, Width, Height, 0); WinApi.SendMessage(Handle, WinApi.WmExitsizemove, 0, null);

I love my ghd outlet Glattetang! It heats up very quickly and works well. My glamour hair is annoyingly thick and poofy, but ghdstraighteneroutletaustralia works wonders! I have never been so fully satisfied with just ghd straightener outlet supplier! What a pleasure shopping at this ghd outlet australia! Thank you very much for this wonderful shopping experience. I will be shopping ghd outlet very very often.

ghd rettetang heat up really quick, and they leave your hair shiny and soft! Also your hair stays straight for ages! I loved them! They are the next best thing. Great ghd glattetang performers and even the next day your hair looks freshly straightened - and I'm talking about someone with really frizzy hair! Best billig ghd ever bought and I've had all the big named ones before I bought these ghd rettetang i norge!! I got ghd slettetang today. My girlfriend very like it. it make us very happy. thanks for the surprise gift. I love my ghd rettetang! It heats up very quickly and works well. My hair is annoyingly thick and poofy, but this works wonders!

I grew up playing sport and computer lingo can sometimes be a foreign language to me. So let's cut to the chase, is it possible to get access to information that current tracking software such as Holdem Manager and PockerTracker cannot ( ie - view opponents hands in real time ) ?

辞任したのは牧義夫厚生労働副大臣、森裕子文部科学副大臣、黄川田徹総務副大臣、主浜了総務政務官の4人。4日の持ち回り閣議で了承された。小沢元代表は同日夜、東京.赤坂の料理店で牧氏らと会食した。

 辞任を認めた理由について、藤村官房長官は4日の記者会見で「辞意が固かった」と説明した。しかし、民主党内では「続投させれば国会の空転を招くと考えたからだろう」との受け止めが支配的だ。

Use the form below to leave a comment.






Coding the Wheel has appeared on the New York Time's Freakonomics blog, Jeff Atwood's Coding Horror, and the front page of Reddit, Slashdot, Digg.

On Twitter

Thanks for reading!

If you enjoyed this post, consider subscribing to Coding the Wheel by RSS or email. You can also follow us on Twitter and Facebook. And even if you didn't enjoy this post, better subscribe anyway. Keep an eye on us.

Question? Ask us.

About

Poker

Coding the Wheel =
Code, poker, technology, games, design, geekery.


Hire

You've read our technical articles, you've tolerated our rants and raves. Now you can hire us anytime, day or night, for any project large or small.

Learn more

We Like

Speculation, by Edmund Jorgensen.