Bot Fighting 101: Don’t Feed the Hacker

 
Author:  Follow: TwitterFacebook
Job Title:Sarcastic Architect
Hobbies:Thinking Aloud, Arguing with Managers, Annoying HRs,
Calling a Spade a Spade, Keeping Tongue in Cheek
 
 
Don't Feed the Hacker
#DDMoG, Vol. VIII

[[This is Chapter 29(c) from “beta” Volume VIII of the upcoming book "Development&Deployment of Multiplayer Online Games", which is currently being beta-tested. Beta-testing is intended to improve the quality of the book, and provides free e-copy of the "release" book to those who help with improving; for further details see "Book Beta Testing". All the content published during Beta Testing, is subject to change before the book is published.

To navigate through the "1st beta" of the book, you may want to use Development&Deployment of MOG: Table of Contents.]]

In previous instalment of this loooong chapter on Bot Fighting, we discussed how attackers are likely to approach your game, and categorized all the attacks on MOGs into “Attacks on Silly Deficiencies”, “Specific Attacks”, and “Brute-Force Attacks”.

Now, we know enough to start discussion on “what MOG devs can do to fend off attackers”. First, and most obviously, we have to avoid those “silly deficiencies” which quite a few of those attacks are based on.

Our operational phrase for our Bot Fighting 101 crash course is “don’t reveal information beyond absolutely necessary”. In practice, while each of the efforts to stop providing unnecessary information isn’t rocket science, making sure that you covered all of them, will require quite a bit of diligence.

The following is a (non-exhaustive) list of things to be done in this regard:[[TODO: convert from text to prose]]

  • Judging hare:DO use Authoritative ServersDO use Authoritative Servers
    • It was discussed at length in Vol. I’s chapter on Authoritative Servers; in short – if you’re not doing Authoritative Servers, your game is very likely to become a cheater-fest, with the only way to handle it being moved towards Authoritative Servers. Quite a few companies already learned it in a painful way, and are in the middle of a cumbersome-and-error-prone migration of their game logic from Clients to Servers.
  • DO use Interest Management (see Vol. I’s chapter on Communications for details). As discussed in Vol. I, without Interest Management you will be distributing all the information to all the Clients, which in turn enables the whole families of attacks, including wallhacks (=”seeing through walls”) and maphacks (=”lifting fog of war”).
  • [[TODO: Linux]]
  • When you’re using system calls, consider all the information fed to the system calls, as “information already in the hands of the attacker”.1 To minimize negative impact of such leaks on the game, make sure that you DON’T reveal more than absolutely necessary, in system calls and their parameters.
    • Sure, we DO need to use system calls. In particular, there is no way to avoid the following:
      • Network calls (most of the time – socket calls)
      • Graphics calls
      • User input calls
    • Inquisitive hare:Some of the system calls are not absolutely necessary, and using them will significantly simplify life of the attackerOTOH, the following system calls are not absolutely necessary, and using them will significantly simplify life of the attacker:
      • System-level encryption calls.
        • Whatever encryption we’re doing, can be lifted trivially (revealing our bare protocol and enabling all kinds of bots) – as soon as we’re using system-level calls to encrypt.
        • Mind you, encryption is a Really Good Thing™ to deal with hackers, it is just that it should be done by our own monolithic executable, and not by system-level DLLs.
      • System calls which do both encryption and sockets
        • The reason is the same as for pure encryption.
      • Any system-level call which deals with character-based output – at least, for any non-constant data which is related to current game state.
        • This includes such stuff as using standard OS controls for mere display purposes (Windows controls are particularly nasty in this regard), and goes all the way up to using functions such as DrawText() and ID3DXFont::DrawText().
        • The reason is that whenever you call DrawText() to show something related to current state of the game, you have already leaked significant information about current game state to the attacker.
          • The worst real-world case I know about, was a whole bunch of poker games, using standard Windows controls to show “chat window” – which, by tradition, for poker apps has all the history of the hand up-to-now. It means that attackers can get current state of the game in an easily-parseable form, by merely sending WM_GETTEXT message to the “chat window”. And as soon as they have it – they can write all kinds of “data mining” programs (which are usually prohibited by ToC as upsetting the balance of the game and scaring players), automated advisors (also prohibited by ToC for the same reasons), and just need to add sending WM_MOUSEUP messages at fixed positions within the game window to enable writing a full-scale automated bot player.
        • Kerning Pair In digital typography, kerning is usually applied to letter pairs as a number by which the default character spacing should be increased or decreased— Wikipedia —To address this class of vulnerabilities – the only way I know is to owner-draw everything.
          • You need a control? Write it yourself (or at least use a 3rd-party static library which does it without using system-level calls which take character input).
          • You need to draw text (for the control above or otherwise)?
            • At least – use your own bitmap fonts2 to combine your bitmapped characters into a line of text. BTW – for quite a few games, simplistic bitmaps-with-per-character-width (but without kerning pairs) often look “good enough”.
            • At most – you can use something like FreeType library to render your TTF/OTF/…-based text into bitmap – and then render the bitmap to screen via the system-level API call.
            • In any case, all the relevant calls to Windows API will look as a bunch of BitBlt() calls, which are much more difficult to reverse engineer than reading parameters of a simple call to DrawText().
          • Hare with While using system DLLs is inevitable (to the extent discussed above), using non-system DLLs within your Client is a Big No-No™.While we’re on a subject of 3rd-party calls: DON’T reveal information by using 3rd-party DLLs. While using system DLLs is inevitable (to the extent discussed above), using non-system DLLs within your Client is a Big No-No™. Very briefly, the reasoning goes along the following lines:
            • There is no reason to use DLLs on the Client-Side (except for official 3rd-party plug-ins for your app). For detailed discussion – see, for example, [NoBugs2010].
            • with each DLL, we’re splitting our monolithic executable, and they are providing lots of information to the attacker. And as using DLLs in 2017 (beyond some very narrow cases) doesn’t make any sense – replacing all-DLLs3 with static libs clearly qualifies as a Very Good Thing™.
              • I cannot even count the number of games which are using openssl.dll – and had their protocols hacked using this attack vector as a result.
            • Note that with F.L.I.R.T., even 3rd-party static libraries are vulnerable – but dealing with F.L.I.R.T. is a subject of a separate discussion in [[TODO]] section below.
              • For the time being – just make sure that you’re compiling 3rd-party libraries from source; in addition, when doing so, you SHOULD:
                • Use your own compiler settings (different from those used in default compile)
                • Use all the library-provided #defines to switch off all the features you don’t need within the library.
                  • This tends to play very nicely with TLS libs such as OpenSSL: as for MOG, we’re controlling both sides of communication – we can (and SHOULD) disable all the algorithms except for one we’re using (for discussion on which-exactly-TLS-algo-to-use – please refer to Vol. IV’s chapter on Basic Security); and the best way to disable unnecessary stuff is via library-provided #define
    • DO encrypt your traffic. If you’re not doing it – you just make your game vulnerable to fundamentally-undetectable proxy bots. While you’re implementing encryption:
      • DO check the certificate on the Client-Side (and DON’T use Anonymous Diffie-Hellman for encryption)
      • DO store certificate-for-checking within your executable (and DON’T use system-level storage of root certificates for this purpose)
        • DO obfuscate the certificate within your executable.
      • Along the lines discussed above – DON’T use system-level libs for encryption; neither use 3rd-party DLLs for encryption (though statically-linked OpenSSL-or-whatever-TLS-lib-you-fancy – is fine).
      • Hare thumb up:Scrambling will help to protect your protocol even if the attacker manages to F.L.I.R.T. with your TLS libraryDO scramble your traffic right before feeding it to encryption library; DO it using your own method. We’ll discuss more on scrambling (and more generally – on obfuscation) starting from [[TODO]] section below, but for the time being – let’s note that such scrambling will help to protect your protocol even if the attacker manages to F.L.I.R.T. with your TLS library,4 and to get your unencrypted traffic.
        • It doesn’t matter how insecure your scrambling is (=”don’t bother to use an anywhere-standard encryption library”), the only real requirements for scrambling/obfuscation are the following:
          • It MUST be reversible for obvious reasons.
          • It SHOULD be non-standard (though it MAY be constructed out of standard primitives, more on it in [[TODO]] section below).
            • If you’re using a standard library for scrambling – it can in turn be F.L.I.R.T.-ed with, which goes against the goal of scrambling discussed right above.
          • It SHOULD be convoluted (~=”simple XOR with a constant byte is not good enough”).
            • Think of it as if you’re designing a very lightweight crypto algorithm yourself; in this process, there is a 99.(9)% chance that whatever-you5-design, won’t be secure – but it can easily be a good scrambler (and a unique one too, which is important).
          • It SHOULD have some means of integrity checking (such as “message checksum calculated in your own way and/or scrambled”). If such a checksum doesn’t match on the receiving side – it is a serious indication of being hacked,6 so it has to be reported, and probably have corresponding player flagged.
          • Ideally – your scrambler SHOULD be randomly generated for each new build out of reversible primitives (for a long discussion on it, including versioning – see [[TODO]] section below).
  • In C++, DO disable RTTI. RTTI is a nice feature of C++; however – it turns out to be  nicer to hackers than to gamedevs. If you don’t disable RTTI, than for each and every object of every-class-that-has-at-least-one-virtual-function, hacker will be able to know not only the VMT pointer for this class (and VMT pointer identifies the class7),  but also class name. Actually, this is the only case when information about C++ source-code names leaks to the executable, and it happens to be of enormous value for the attacker. Without RTTI, all attacker has is merely a “it is an instance of some class” (and has to guess about what-the-class-does); with RTTI, for the same class attacker can see “hey, it is an instance of the class which author has thoughtfully named ‘PlayerHealth’ or ‘UnitPosition'”.
    • To deal with it, three approaches are possible:
      • disable RTTI (“/GR-” switch in MSVC). This comes at a cost of losing ability to make dynamic_cast<> (which are rather easily replaced with DIY kinda-dynamic-cast based on DIY virtual functions); as for all the other goodies of RTTI such as typeid() (which might be usable to make maps of object types), I yet to see any real-world uses for them (and even if they do exist – they’re very few and far between, so replacing them with DIY virtual functions won’t be a problem).
      • in your production build, run a pre-processor which replaces all the class names with their hashes (or something similar). TBH, I don’t really like this approach (it is cumbersome, doesn’t deal with 3rd-party libraries, etc.); also – it may be interpreted as an insult to the attacker (and believe me, we don’t want to make attacker a personal enemy). If you still decide to go this route – make sure to randomize names on every build.
      • Obfuscate VMT pointers as discussed in [[TODO]] section below; as RTTI is inherently based on VMT pointer – hiding it will make debugger rather unhappy.
      • Personally, I would argue to both disable RTTI, and obfuscate VMT pointers. My rationale goes as follows: (a) hassle due to disabled RTTI happens to be very limited (at significant anti-hacker benefit); and (b) obfuscating VMT pointers is certainly a Good Thing(tm), but it can’t be applied across the board, so there going to be non-obfuscated VMT pointers in our program (and that’s when disabling RTTI at least won’t make the job of attacker easier).

Phew. The list above is certainly non-exhaustive, but following all the advice above is a de-facto prerequisite to any realistic protection. Otherwise – according to the weakest-link principle – attackers won’t even bother with hacking your other defenses (so they won’t even know how clever your other defenses are <sad-wink />).


1 As it will be discussed later – there is no good way to hide system calls anywhere reliably, which means that “we should treat them as easily-interceptable”; in other words – we should treat these calls as residing beyond-our-defence-perimeter.
2 Of course, properly obfuscated as we’ll discuss in [[TODO]] section below
3 except for those which are system-level-only; still, we SHOULD statically link msvcrt.lib etc. etc. For Mac OS, make sure to read [Kulesza]
4 =”identify functions within the library using F.L.I.R.T. feature of IDA Pro”
5 or me for that matter
6 errors in outermost encryption can be just due to packets being corrupted in transit over the Internet; however, for the data protected by outermost encryption, chances of any problem at packet level propagating below, are 2-128 ~= 3e-39. Even if we consider all the packets for all the players for a million-player game running for 10 years, we get merely 1e6 players * 20 packets/player/second * 86400 seconds/day * 365 days / year * 10 years ~= 6e17 packets, so chances of crypto randomly failing even for one single packet during this time is miniscule 2e-27 (~=”failures due to bugs etc. are much more likely”).
7 in [[TODO]] section below, we’ll discuss how to deny attacker easy access to information whether two objects are of the same class, or of different classes

 

[[To Be Continued…

Tired hare:This concludes beta Chapter 29(c) from the upcoming book “Development and Deployment of Multiplayer Online Games (from social games to MMOFPS, with social games in between)”.

Stay tuned for Chapter 29(d), where we’ll get to real real obfuscation <wink />]]

Don't like this post? Comment↯ below. You do?! Please share: ...on LinkedIn...on Reddit...on Twitter...on Facebook

[+]References

Acknowledgement

Cartoons by Sergey GordeevIRL from Gordeev Animation Graphics, Prague.

Join our mailing list:

Comments

  1. says

    “it looks as an insult to the attacker”

    Isn’t all of these steps an insult to the attacker? How is “running a pre-processor which replaces all the class names with their hashes” an insult where other steps to stop the hacker is not?

    • "No Bugs" Hare says

      > Isn’t all of these steps an insult to the attacker?

      Depends on point of view, but in general, “defending against” doesn’t equal to “insulting”. If I remove RTTI information, I am defending – and when a serious attacker sees it, the reaction is like “oh, those defending guys know enough to remove RTTI, good for them”; it tends to build respect, not a being-insulted feeling. OTOH, if I am leaving RTTI in place, but making it effectively useless – it is often more insulting, because when attacker can see that RTTI is present, an anticipation of “hey, I can use RTTI” builds, but then there is a quick disappointment; such creating-false-pretense with quick-disappointment-afterwards is known to cause “being insulted” feelings (regardless of real intentions of whoever-creates-them).

Leave a Reply

Your email address will not be published. Required fields are marked *