Demon
Register | User Profile | Member List | F.A.Q | Privacy Policy | New Blog | Search Forums | Forums Home
Doomworld Forums : Powered by vBulletin version 2.2.5 Doomworld Forums > Classic Doom > Source Ports > Verifying a client binary (c/s ports)
 
Author
All times are GMT. The time now is 09:10. Post New Thread    Post A Reply
Ladna
Member


Posts: 271
Registered: 04-10


As many of you know, cheating can be an issue in open source games, and many such games have come up with various methods of fighting it. I've come up with a method that, while not effective against all cheats, is effective at stopping certain classes of attacks.

This requires a couple prerequisites:

- An account system, with a unique username and any password
- A clientside launcher program, separate from the game executable

This is how it works:

- Launcher authenticates to the account system with user's info
- Account System generates and compiles a program (referred to as the "verifier"), and sends it to Launcher
- Launcher executes Verifier in a separate process, and assumes its work is done

= Verifier:

Verifier is important in two ways, execution and composition.

== Verifier Execution:

Verifier contains in it a randomly generated token. Verifier reads the game exe into memory and creates an SHA-1 hash of the data combined with the randomly generated token (referred to as the ticket). It then sends this hash to Account System, which knows what the hash ought to be. Next, it creates a randomly-named folder and writes the game exe's data there. It executes the new game exe, passing the ticket as an argument (like -ticket "sdf90sdflksdf90sdf09wlk2309"), and finally removes the temporary folder when the program exits.

== Verifier Composition:

Verifier programs are fundamentally "obfuscated" programs. They are compressed (UPX) and contain junk instructions and data, thus they are not easily disassembled. They also have no library dependencies, so traditional DLL-injection is harder.

= Account System

Another important component of the system is the account system. It keeps track of when tickets are generated because tickets have a short lifespan and can only be used once. This mitigates brute force and forgery attacks.

= Weaknesses

There are, of course, things this scheme doesn't handle:

- Attaching a debugger to the game process afterwards
- High-level DLL-injection (say user32.dll or kernel32.dll)
- VirtualAllocEx/CreateRemoteThread calls on Windows
- Hooking
- Indirect attacks (driver hacks, video buffer scanning, etc.)

Some of these are mitigated by the nature of c/s, if the attack is not fast enough, the client will timeout and they will have to get a new ticket, restart the client, and retry the attack. Others are problems that any native game has (open source or not), therefore I'm not too worried about them.

Old Post 09-27-11 18:40 #
Ladna is offline Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
chungy
Senior Member


Posts: 1809
Registered: 06-05


On Windows, it's actually possible, and has been used for cheats int he past, to exploit a hot-patch point in Windows binaries.

Old Post 09-27-11 22:51 #
chungy is online now Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
fraggle
Filled with the code of Doom


Posts: 7287
Registered: 07-00


This can be trivially circumvented by a user simply using their own binary instead of the "verified" binary, and telling everyone else they *are* using the "verified" binary where necessary.

It's a losing game you're trying to play. There is no solution the problem - on a fundamental level it's impossible to know that the information you're receiving over the network, from an opponent you're playing against, is generated by a particular binary or not.

Your suggestions of using "obfuscated" code really just boil down to a futile attempt at security through obscurity. It's a complicated waste of time that doesn't even solve the problem it's supposed to address.

If you want to avoid cheaters, what you really need is a social solution to the problem - make sure you play with friends that you trust not to cheat.

Old Post 09-27-11 23:16 #
fraggle is offline Profile || Blog || PM || Email || Homepage || Search || Add Buddy IP || Edit/Delete || Quote
Ladna
Member


Posts: 271
Registered: 04-10


That's why the verifier reads the exe into memory, creates a random temporary folder, and writes the exe there before executing it. This does create a race condition; I can solve it on POSIX platforms but I don't know about Windows... probably there is a way though.

If you mean just re-using a ticket after they get a good one, tickets only work for a single connection.

= EDIT 1:

And yeah I know that hooking & hot-patching aren't really addressed with this, but any native game will have these issues too. I'm really only trying to mitigate problems that might arise from being open source, everything past that is bonus points.

= EDIT 2:

Maybe this would work on Windows: http://blogs.msdn.com/b/larryosterm.../19/116084.aspx

Last edited by Ladna on 09-27-11 at 23:48

Old Post 09-27-11 23:33 #
Ladna is offline Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
DaniJ
Forum Staple


Posts: 2055
Registered: 08-03


The second you let go of your verified executable it can be swapped out by another process behind your back.

Old Post 09-28-11 00:44 #
DaniJ is offline Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
Ladna
Member


Posts: 271
Registered: 04-10


If the attacking process knows the path to the new game exe, which it won't. Also I wonder if I can CreateProcess() on Windows while having the exe file open O_EXCL.

Old Post 09-28-11 00:48 #
Ladna is offline Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
DaniJ
Forum Staple


Posts: 2055
Registered: 08-03


Of course it knows the location of your exe, why wouldn't it? You can easily peek at the handles opened by another process, this is how many worms work.

Old Post 09-28-11 00:53 #
DaniJ is offline Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
Ladna
Member


Posts: 271
Registered: 04-10


Fair point, but you'd have to be extremely lucky to be able to swap the file in the 18ns between when the new exe is finished being written, and when the verifier executes it.

Old Post 09-28-11 01:03 #
Ladna is offline Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
fraggle
Filled with the code of Doom


Posts: 7287
Registered: 07-00



Ladna said:
That's why the verifier reads the exe into memory, creates a random temporary folder, and writes the exe there before executing it. This does create a race condition; I can solve it on POSIX platforms but I don't know about Windows... probably there is a way though.

If you mean just re-using a ticket after they get a good one, tickets only work for a single connection.

= EDIT 1:

And yeah I know that hooking & hot-patching aren't really addressed with this, but any native game will have these issues too. I'm really only trying to mitigate problems that might arise from being open source, everything past that is bonus points.

And all these efforts will be in vain, because they ignore the fundamental problem - the program receives data from the network; it is impossible to tell what sent it.

When you try and go down this road, you just end up with an arms race between hackers and game designers, and the result is horrible overly-intrusive programs like PunkBuster that everyone hates. And you still don't solve the problem, either.

Old Post 09-28-11 01:14 #
fraggle is offline Profile || Blog || PM || Email || Homepage || Search || Add Buddy IP || Edit/Delete || Quote
DaniJ
Forum Staple


Posts: 2055
Registered: 08-03



Ladna said:
Fair point, but you'd have to be extremely lucky to be able to swap the file in the 18ns between when the new exe is finished being written, and when the verifier executes it.

I beg to differ; you can wait-on-release and then lock the other process out while you swap in your nefarious executable. However you don't even need to do that, given the other already mentioned approaches.

You *may* be able to implement a completely secure system, even on Windows, however is it really worth all the effort? Personally, I'd spend my time on effective anti-cheat measures (like denying clients the information they need to aim-/wall- hack in the first place).

Old Post 09-28-11 01:16 #
DaniJ is offline Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
Ladna
Member


Posts: 271
Registered: 04-10


Yeah, after doing some research, I don't think there's a way to write to a file and then execute it while being sure of its contents. You might be able to achieve the ultimate goal of executing a known good binary blob by messing up your own stack, but that's obviously not a solution. This goes for Windows and Linux, Linux won't let you execute a file that's open for writing either, and its file locking mechanism is purely "advisory". On Windows, it might be possible to "lock" a file (or part of a file) and execute it, but those locks are write locks and probably prevent the file from being executed as well. That's pretty disappointing. I thought at least on Linux you could create a file descriptor, unlink it, and still execute it, but you can't execute a file descriptor; it has to exist on disk.

Back to the drawing board... :)

Old Post 09-28-11 02:34 #
Ladna is offline Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
hex11
Forum Staple


Posts: 2237
Registered: 09-09


Some years ago I had to "secure" a piece of code running in a consumer appliance type machine, where everything about the environment could be considered known and stable (unlike PC where everything changes at user's whims). It actually ran a very minimal and customized version of Debian Linux. Anyway, the boss was afraid the chinese would copy our code and then mass-produce the appliances for much cheaper, and I tried to convince him it wasn't possible to stop that but ultimately still had to do something. So I encrypted the code, modified the kernel to disable user-level (root) raw access to /dev/mem & friends, etc. and had the decrypting process check some things (like kernel, libs, etc.) to see if the environment was "sane". And also I used various anti-reverse-engineering tricks I found in books to make life harder for people who wanted to disassemble the code or run it in a debugger. But there was one attack vector I couldn't find any way to defeat: if someone ran the whole system in a VM, they would be able to step through the decrypted code, save memory dumps, and do whatever else they want...

Ultimately there's no sure way to stop someone from hacking something that's running on a machine they physically control. You can try to make things more difficult, jump through a lot of hoops and waste lots of time, but it only takes one person to hack the system and then release his exploit... Maybe some hardware DRM would make securing code easier, but then again that can also probably be defeated with the right equipment.

Old Post 09-28-11 12:32 #
hex11 is offline Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
Gez
Why don't I have a custom title by now?!


Posts: 10672
Registered: 07-07


Write your program so that is bytecode is a huge prime number (like DeCSS). Then make it all self-modifying code so that on startup it will regenerate itself by computing this prime number and overwriting itself with it.

:p

Old Post 09-28-11 13:09 #
Gez is online now Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
DaniJ
Forum Staple


Posts: 2055
Registered: 08-03


Thats still deterministic, therefore its "merely" obfuscation in this context :p

Old Post 09-28-11 13:17 #
DaniJ is offline Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
fraggle
Filled with the code of Doom


Posts: 7287
Registered: 07-00



hex11 said:
Ultimately there's no sure way to stop someone from hacking something that's running on a machine they physically control.
Exactly. I expect these kind of futile tactics from commercial games, and companies like Valve can get away with it because they can exact a costly price on people they catch cheating (eg. locking out their CD key so they can't play again) so it's expensive to reverse engineer. I don't expect open source games to waste their time with this nonsense, and there's basically no disincentive at all against hacking it.

Old Post 09-28-11 16:38 #
fraggle is offline Profile || Blog || PM || Email || Homepage || Search || Add Buddy IP || Edit/Delete || Quote
chungy
Senior Member


Posts: 1809
Registered: 06-05


Plus... scenes like this make for much entertainment :)

Old Post 09-28-11 20:40 #
chungy is online now Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
AlexMax
Senior Member


Posts: 1114
Registered: 01-03



chungy said:
Plus... scenes like this make for much entertainment :)


That whole scene was set up.

Old Post 09-29-11 00:16 #
AlexMax is offline Profile || Blog || PM || Email || Search || Add Buddy IP || Edit/Delete || Quote
wesleyjohnson
Forum Regular


Posts: 935
Registered: 04-09


Sauerbraten dropped their anti-cheating code, it is now solely detect and kick the cheater out.

It will be an attraction to someone to break the protection, they cannot resist trying.

If you really feel the need to do something, then just detect impossible play by a player and notify the other players. If it keeps happening then they can kick the player out.

A few companies that tried too hard to protect their code got sued when they went too far. One installed a root kit ...

Any code from a central server can be downloaded into a sandbox or VirtualMachine or just a modified loader, where it can be copied and attacked. The tools for doing this have existed from when AppleII and DOS games came on protected boot disks, and they broke most of them within days.

Last edited by wesleyjohnson on 09-29-11 at 04:12

Old Post 09-29-11 04:05 #
wesleyjohnson is offline Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
Maes
I like big butts!


Posts: 11781
Registered: 07-06



AlexMax said:
That whole scene was set up.


"$2000 1337 machine?" :-p

More like a mid 90s throwaway beige tower pre-filled with loose junk ;-)

Only Doom Marine knows for sure, though.

Old Post 09-29-11 11:26 #
Maes is offline Profile || Blog || PM || Homepage || Search || Add Buddy IP || Edit/Delete || Quote
wesleyjohnson
Forum Regular


Posts: 935
Registered: 04-09


You cannot rely upon Linux system calls for anything when dealing with a hostile remote system. Cheaters can recompile Linux to defeat any mechanism.
Cheaters can modify Linux to detect your code (by several methods), then set a flag to activate other hooks. They can then intercept the system calls to defeat anything you try. Because the system automates the defeat of your tests, 18 ms is plenty of time.

They really only have to substitute one prearranged binary for a final downloaded one. Once they have a copy of your code, they can devise a test for detecting the final download and have a the linux system call you use substitute binaries there.

Old Post 10-01-11 21:01 #
wesleyjohnson is offline Profile || Blog || PM || Search || Add Buddy IP || Edit/Delete || Quote
All times are GMT. The time now is 09:10. Post New Thread    Post A Reply
 
Doomworld Forums : Powered by vBulletin version 2.2.5 Doomworld Forums > Classic Doom > Source Ports > Verifying a client binary (c/s ports)

Show Printable Version | Email this Page | Subscribe to this Thread

 

Forum Rules:
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is OFF
vB code is ON
Smilies are OFF
[IMG] code is ON
 

< Contact Us - Doomworld >

Powered by: vBulletin Version 2.2.5
Copyright ©2000, 2001, Jelsoft Enterprises Limited.

Message Board Statistics