views:

29

answers:

1

I am trying to extend the legacy code of an online game to provide a reasonable assurance that the resource files associated with the game are the latest version, and not tampered with. I'd like to do this without DRM, without going into kernel mode, and without hardware assistance. What I ultimately settle on should ideally be about as secure as user-space gets. If they can get around these mechanisms then I'd say they've earned the right to be naughty -- for a while at least, then we ban them. :)

The obvious thing to do is take a cryptographic hash of each of the files, and compare the hash output to pre-computed hashes. But care must be taken to assure that the user can not easily tamper with the pre-computed hashes that are used for comparison against the currently-computed hashes. Here are the measures I had in mind, at an architectural level:

  1. Use OS facilities to lock all the resource files for writing when the process is started, so no other processes can overwrite the files after the fact.
  2. Calculate the MD5 sums of each resource file.
  3. Connect to an https server, requiring verification against a particular signed certificate stored in the client executable.
  4. Download a file over https containing the "correct" hashes, storing it in RAM (not to disk).
  5. Compare the computed hashes to the ones received over https.
  6. Exit if the hashes don't match.

This is more secure than storing the hashes client-side, because as long as they are stored client-side, it is usually possible for someone to modify them. Using this scheme, the attacker would have to figure out how to modify the executable in a way that changes the embedded public certificate so that it actually contains the attacker's certificate, which verifies against the attacker's https web server containing the poisoned hashes that match the attacker's tampered resource files. This is certainly feasible, but it's much more difficult than it would be if the program were using pre-computed keys stored on the client, right?

The executable is native code, so it may be possible to wrap it in a packer that makes it even more difficult to edit the binary and replace the public key.

Now, granted that I just described two paragraphs ago how to potentially attack the scheme I devised: is there any way other than what I described to attack this scheme?

My second question is, what is a free (as in freeware OR freedom) library that can be called from Win32 C++ (Visual Studio 2010 compiler) to accomplish these tasks? Can they be done with built-in Windows APIs or is a third party solution required?

So in summary, the questions: 1. Is the scheme I suggested robust enough to baffle most script kiddies; and 2. What dependencies or libraries would I use to implement it?

Since this is just a game and not a matter of national security or even monetary risk (the game is 100% freeware with no in-game economy), the solution I implement should be pretty good, but it doesn't have to be industrial-strength.

Feel free to answer one or both parts of my question as you see fit, and do correct me if you think I'm asking the wrong questions to begin with.

Thanks!

+1  A: 

I like your solution. It is well thought out. You essentially answer your own questions. I have a few suggestions to point out.

The Win32 libraries most appropriate for your task are the Windows Crypto API for hashing. It also supports SHA-1, which I'd recommend (as MD5 is considered vulnerable and crackable these days). You may also want to look at using HMAC with SHA1 such that the calculated hash includes a secret key (embedded in code).

Wininet is an appropriate library for downloading. I believe it supports HTTPS with certificate validation.

The Windows API CreateFile for opening the file can essentially lock the content files. Just open all the files without specifying any FILE_SHARE_* flags.

Let me throw out a couple of other ideas that I learned during my brief time in the games industry.

Most games only have a short lifetime (a few months to a year) before the cheaters move on to the next fun game. As such, given that there really isn't a perfect solution to stop a determined hacker with nothing better to do, you are essentially looking for solutions that can buy time. That is, the more protection hacks you put into the game, the longer it will take a determined hacker to break your solution. Even if your all your protections aren't cryptographically secure, it buys you more time.

If you are not concerned with hackers who will try to disassemble and modify your your game's EXE, you likely are done given your proposed solution. But consider the following.

1) Here's a simpler attack. Somewhere in your code, you will likely have a function that decides whether or not it is acceptable to proceed with the game based on the hash validation solution you described. All a hacker has to do is find where the end of the final validation occurs and force your validation code to always succeed. That is, if you have a function like this in your code:

bool IsContentValid()
{
   // compare the local hash with the hash returned from the https response
   return (0 == memcmp(local_hash, network_hash, sizeof(HASH)));
}

Then it becomes INCREDIBLY EASY for a determined hacker to modify his own EXE to make this code path always succeed. The hacker simply waits for the code to get to the point where it does the final check and replaces the assembly to flip a false bit to true.

One obvious workaround is to have different variations of this function linked into different areas of the game code and are called periodically during game play. Implement this check with a #define C macro instead of a function. Implement 10 different variations of the code as 10 different macros. Call these macros at 50 different places of the code - such that the hacker will have to find them all (and patch them) in order to succeed. Make it such that if the content is deemed to be invalid, the game behaves buggy. For example, if his content files are invalid, the game player's health and ammo value randomly go to zero such that he's any easy kill!

2) Patching the game more often (every few months), even if there's isn't any reason to other than to generate new hashes of the content and a new layout of the EXE is another protection that buys time. Imagine every month you patch the game and simply update the crypto hashing algorithm and some slight tweaks to the content. The hacker who released the original crack has to start over again. This takes him time and more time to distribute his updated crack if he's successful again.

3) I would do both local validation of the content as well as network validation. They should use different hashing algorithms.

4) Another cheezy hack that buy time: Validate the HASH of the EXE itself as well as the content (both locally and on the network).

5) Use another hash of the content and exe to be a simple crypto key for digitally signing all network messages to the server. You can simply append to the network message: your_simple_fash_hash(content_hash, exe_hash, network_message). The server does the same thing for all received network messages. Implement different versions of this hash/signature code for different network message types. Look at the Murmur hash as a fast hash function example. Now the hacker has to hack the networking code too!

6) Make it hard for the hacker to disassemble your game code using Softice or any other sort of kernel debugger. Any variables used to store content validation hashes (or any crypto keys) should be encrypted as well. One cheezy protection - XOR all important variables with 0xAAAAAAAA (or some other noise) when storing them in RAM. When you need to use these values, XOR them back into a temp var, use them appropraite, then zero out the result as soon as you are done with the computation. In addition to content file hashes, variables for the players health, fuel, and ammo should be treated this way as well.

7) If possible, find a clever way to move important variables around in RAM such that they never persist in the same memory location for the entirety of the game.

Ok, that's a brain dump. More than I like to write for someone that only has 1 reputation point on stackoverflow. I hope it helps.

selbie