Break my network hack protection idea...

Well, I’,m diving into the multiplayer programming scene, and though I conside it unnecessary, I did some thinking on hack prevention. So here’s my idea that conceals a minimum of scripts/data from the user.

There are game files that should not be changed, such as the model files and script files, and there are ones that do change, such as player stats files. When the game is started, it creates a hash of the contents of any files that should not be changed. When the player tries to join a server, one of the first packet includes this hash. The host will then automagically kick him if it doesn’t match his own hash.

Thus the only functions that needs to be concealed from the user is:
sendHashPacket(connection)
and it’s supporting functions to generate the hash. This would probably be in a pyc file, and because it doesn’t involve bge, it isn’t GPL’ed.

Right, now break my hypothetical hack prevention.

Give me a minute whilst I look up the hash on an internet forum :wink:

To clarify, seeing your later conditions:

The concept of network security is really that any data that does not originate from the server cannot be considered trustworthy. It is likely that if a user can modify the game assets, especially after any additional packaging, they can intercept outgoing data from their application. Reverse engineering network protocols isn’t usually as difficult as it might seem, as programmers tend to align data in order to maintain space and performance efficiency gains.

So, you cannot consider the client’s hash value to be valid. However, this is where CD key’s are useful.
If you want to (for example) sell DLC as separate products, you can optionally have a two-step encryption technique to generate unique identifiers (of the CD key and the hash of the content). In reality, you’d just combine the two as a hash, i.e hash((key, content_hash)). Buying the DLC would generate a hash for the server that is valid for your game. Whenever you connect you send that hash, and the server keeps track of which hashes are currently in use- a disincentive to share your valid login details.

Or just use login details, because there’s no reason to check the content on the server, only whether the user owns the game.

Your application of protecting content might seem to be ineffectual, because:

  1. If you want to prevent them seeing files, you’ve already failed.
  2. The client shouldn’t have any trusted state it can trick the server with.

Decompile the pyc, modify sendHashPacket to send the expected hash, regardless of what the files actually contain.

Or:

Set up a software proxy for the local network interface, to rewrite the hash packet (or packets) with the expected data.

Or:

Use a memory scanner/editor to change file content in process memory, along with anything else.

No matter what you do, you can never, ever create an environment in which the client can be trusted. So, if you care about cheat prevention, you need to use a server centric approach, where the simulation runs on machines that you can actually trust, and where clients are little more than dumb input/output terminals.

My view on this:

Any client side measures you take are merely an exercise in obfuscation. Yes, there are many ideas you can come up with that will make it more difficult and tedious to crack into your code and files, but ultimately anyone who is determined enough will eventually break through. At that point they can share their hack with everyone else without others having to put in any of the work.

While client-side checks can still be useful, you should mainly rely on server-side verification. Depending on how your game is designed, there may be many ways in which the game server can detect when a client is cheating. For example, checking how fast the player is moving can counter speed hacks. Making sure a player takes damage and dies when they’re supposed to prevents invincibility hacks. Not sending clients information about other players they shouldn’t be able to see prevents wall/fog hacks, etc. The server should usually be in control of these types of things anyway, so it often isn’t even necessary to implement extra code.

The problem is that it is pretty easy to capture the hash from the original file and return it to the server while still generating the network communication with a different file (man in the middle attack).

You can still use this method to determine version differences and give the user the option to download the latest client.

To protect the communication input, you can let the server decide if the input is plausible. You need a way to solve conflicting results anyway. E.g. Character A at client 1 gets killed, Character A at client 2 survives as the last hit missed. Which one is right? Let it be decided by the server.

Indeed this would not work in a peer to peer environment as it consists of clients only and you do not know which one to trust.

If you would like to strengthen this up a bit you will have to prevent the replay attacks as others have noted. You probably want to mix the current system time in with the file contents and have the client report the hash + time to the server to validate.

So the server knows that there has not been a replay attack and that the hash is fairly recent. However, that doesn’t stop someone from being able to let the client hash the valid files, suspend the process, and swap out the .PY files. There are also other attacks that I can think of with swapping files out between the validation phase and the start of the game.

If you want to go really crazy, then you could have all your .py files zipped up. Your client loads the zip file into memory and validates that, then expands the zip in memory and ‘evals’ that data to load your clients and models in.

There are still attacks that can be done on that system and it comes with big drawbacks around having to load all the game data at the same time.

At the end of the day, if somebody has physical access to a computer it can be hacked. It is a trade off between when more effort to lock things down will cost you more time/money than you are trying to protect.

Pure awesomeness redacted,

sdfgeoff is a super cool guy…

again awesome awesomeness bro-chica

@Blueprint, please only contribute if you know something about what you are talking about.

@Everyone else:
These days everyone says clients need to be ‘dumb’ and do everything server-side. Well, I’ve got a story for you, about two identical games, both which involve dodging as a major game-play element.

One is Descent (1995), it does everything client-side, only movement, shots fired, and your deaths are sent over the network.
The other is Talon (2013), it does movement client-side, damage server-side. No idea what is sent over the network.

I live a long way from anyone else who plays these games/hosts servers. On a good day the ping is 180, normally 250, sometimes 350, and once … 1200. (yup, >1 second ping!).
Descent (1995) can be played all the way up to 350 ping without a problem. You simply account for it in how you play. When it came out, multiplayer was upwards of 500 ping, and people played it.
Talon (2013) can only be played up to about 200 ping, even then you get killed when behind cover, or by random projectiles you never saw.

So the network system I’m developing (because dodging is an important aspect), everything is done client side. No way am I making it so I can’t play this game with other people.

With that in mind, client hack protection is a little more important than normal.

Yes, I am aware that any client-side measures can be broken. The aim isn’t to make a perfect system, simply an inconvenient one. Any game can be hacked with cheatengine or scanmem. That’s not my problem. I just want to make it hard for normal people to edit game-critical files and join normal games. (Interestingly, with this system, if to people have the same hacked version, it will let them play together!).

So seeing as the flaws you’ve seen here are the same ones I spotted, and none of them are great glaring things that could be done in a hurry, I think this security level fits my needs. (After all, a game doesn’t need to be as secure as a bank!)

I’m not quite sure what you’re saying here. Do you mean the game server does nothing other than route clients’ packets to each other or that your game is peer-to-peer with no server at all?

Either way, I would say you can still implement some “server-side” checks, just just have the other clients implement them. For example, if you’re in an 8 player online game and 1 player is using speed hacks, there’s no reason the other 7 clients wouldn’t be able to recognize this and agree to kick the offending player from the game.

You just have to also be aware of malicious clients who may falsely accuse others of hacking. It’s safe to assume that the majority of players aren’t malicious, so in general you could just wait for a sufficient number of clients to flag the suspected hacker before he is kicked.

It’s still got a server, just the server is very simple.
What I mean by ‘everything is done client side’ is that: There is no one game version that tells the other versions what is happening (ie not host-authoritive)

So:

  • any damage from collisions is calculated locally
  • any movement is calculated locally

The data that comes in from the server is:

  • other player’s positions
  • objects created/destroyed by other clients
  • animations/sounds playing on objects caused by other clients

Data that goes to the server is:

  • whether the player has died or not (note: not what he’s collided with or his health, only if he’s died)
  • the inverse of what comes in from other clients

So yeah, the server is prety dumb, just cocatenates data from other players, perhaps filter out unnecessary ones (ie things a long way away), and relays it to everyone else.

With this sort of network model, worrying about hacking is really a question of how responsible the users are. Then again, that’s nearly always the question either way.

Despite the benefits of a server centric design, the logistics of managing p2p games can be really interesting. However, the server-as-router model is somewhat in between.

How you want to resolve conflicts? E.g. Two objects collide at one client but not at the other?

Even in a peer-to-peer network, an arbitrary node is typically selected to perform server-like duties.

Without something that resembles central authority, resolving conflicts between clients is a nightmare.

I agree with Goran’s model of having the clients only give input letting the server make all the calculations. I don’t understand the benefit of having the game happen on multiple computers THEN having the server compile data from all of those connections on top of that. Doesn’t that amount to the same amount of work, with some additional task multiplying the server’s workload by the total player count?

@Nuances:
So as I said, I have ~200 ping to pretty much anywhere. Let’s say I forward all keyboard commands, and the server sends back movement info.
200ms ping will make navigation next to impossible. call it a quarter of a second. How far does a player move in a quarter of a second? A long way.

I also said that the game features dodging as a key element. So if I do damage calculations on the server, the player will have moved several times his hitbox length before he registers as getting hit. And a quarter of a second is easily long enough for projectiles to be created and hit the player: the player will take damage from things he never sees. All this equals not much fun.

@Monster:
There is no conflict resolution. What the player sees on his screen relating to him is what is happening. What the player sees on his screen relating to other players isn’t. But you can account for that in either the software (extrapolate speed, position etc based on ping and opponents commands 200ms ago) or wetware (skill). I’ll probably go the wetware route.
So if the player collides with something, he collides with it. If he shoots a projectile at another player, if it hits him or not is determined by the other players computer. Everything directly relating to the player is local.
For reference: there are no hitscan weapons. If there were, I’d keep the same theory: send the ray on the other players client as well, rather than sending a ‘hit’ from the player who shoots it.

The only possible conflicts come from interaction with non-player objects, such as doors/lights/powerups. In 200ms, two people can pick up the same powerup, toggle a switch, or do anything. With powerups, who get’s it? Both? None? The first one? Both is the easiest, as you simply send a ‘remove object’ command along the network, and if the object is already gone, don’t do anything. Will it break the game? I don’t think so. It’s not racing where you have to know who got over the finish line first. Two weapon powerups aren’t going to break things, as there are more on the level. If I wanted to be uber-fussy, a check to the server may be required before confirming a powerup pickup. But I’m not sure if it’s worth it.

With a switch, you’ll both be switching it the same direction 90% of the time, so there’s no problem if you send an absolute state. The other 10% of the time, the packet that get’s through most recently will be the one all the clients end up getting anyway, so you just have to send state commands back to the player they came from as well as everyone else.

With an external object that takes damage (eg destructable doors), you don’t send the remaining health, you send the change in health, so that both players weapons have an effect, even if the order of packets is different for different clients. These can be concatenated by the server as well to reduce bandwidth.

And so on. Are there any possible conflicts I’ve left out?

Keep these questions coming, I’m enjoying fleshing out my solution before I get too far into implementing it.

What sdfgeoff is describing does exist in todays games. I play the game Phantasy Star Online 2 (MMO) that completely trusts the users dodging abilities. The way the incoming attacks are made, you end up often clutching the controller/keyboard tighter because of how close you were to taking a fatal blow. It wouldn’t be playable/fun in PSO2 without trusting the client.

As far as I’m aware, only two groups in that game have the ability to break the encryption on the game files. One of them uses that ability to translate the program from Japanese to English which brings in a lot more players. The other group, well, they made a small less than safe for work tweak to one of the costumes, but only people that copied the assets they distributed could see the change.

@superflip
I’ve also noticed the local delay, and, while I can’t confirm it, believe it comes from the way most computer handle non-blocking ports. As a result my current tests are using non-blocking ports, and talking to BGE via threading and a queue system. The actual network module runs frame-independant. I’m hoping this will cut out the lag, I’ll do some testing when I next get time to work on this system.

Be aware that linux handles rapid file io really well (caching), butnon windows, reading/writing a file at 60fps can be really slow, I learned this when working on DEEP Space, developing an input system for a custom controller that would write co-ordinates to a text-file. Linux=fast, Windows=slow. If you just want to chuck things in different thread check the resources forum, I added a thread there recently with a basic threading system.

About vote-kick, I think it’s someting that should be decided by the software. Each computers client can evaluate speed/fire-rate etc. The person doesn’t vote for the kick, each client sends a packet to the server saying that they think another player is cheating, and if the server gets enough results, it can kick the person itself. That way the server doesn’t need to be running the game to decide if a players cheating.

My current idea for networking is to use UDP, and to send events. I have a text file outlining the varous packets that it can send. (Ie add this object, change this, move that). The server can then take all the add object packets, join them together, and send only a single add object packet to the other clients.

Warning, wall of text below


Notes:
some terms are used interchangeably here. P2P assumes non-lockstep p2p. The issues with lockstep are those of scalability and necessity of determinism of game logic.

We’ve covered quite a complex topic without going into a lot of details. A number of the suggested solutions here only address one part of the issue, whilst introducing new problems that compromise their effectiveness.

Clients need to be dumb for as long as we cannot guarantee the following to be true:

  • All data that the server / clients handle originated from the code written by the developers, on the machines represented by the connection.
  • All data is sent as soon as it is generated.

At the present time, we cannot make such assumptions when writing networked games. The passage below is written with the assumption that you consider network security to be of concern.

The issues you describe are that of latency. Latency will always be a problem for games that require realtime parallel simulations. The issues addressed by different network models only hide the latency where it can be hidden with rare user-observation, sometimes by introducing latency that looks deliberately a part of the game mechanics.

Let’s first consider the Descent model. All data relating to player state is generated on the client representing that player. This means that the client is the authority on the actions of the player which may involve other players. There are a large number of means to manipulate the state of the player to give them an advantage over other players:

  • Write simple application to determine valid hit vectors to target other players, and send attack information to the other members of the simulation.
  • Disable physics collisions for the player model and negate the impact of terrain geometry upon the movements of the player.
  • Remove limitations such as ammunition count, weapon unlocks, weapon range, visible obstructions like smoke, upon actions of the player.

There are so many other means of cheating but many are described by the above bullet points. All of these modifications are possible because the client’s total authority on anything relating to the state of the player. To be fair, the clients wouldn’t even be cheating, because how can you cheat if you’re just playing by your own rules?

Now, Talon. This is a common tradeoff for security issues, because it reduces the set of possible state modifications to that of player location, delta location and delta of that (i.e position, velocity, acceleration). It is possible to relatively carefully prevent absurd movements like flying and speed modifications, but other more precise forms of cheating are difficult to detect without knowing the inputs used to produce the movement. If the player’s movement options are extensive, this accuracy of detection falls.

Before we look at the solution most commonly used, let’s identify one of the issues with these models. Giving the client the authority over their state in order to avoid waiting RTT for move confirmation does not reduce the influence of latency from other players. This means that players will still see other players at different positions depending upon the latency from the source to the target. Methods such as extrapolation can only somewhat alleviate this. If two players are looking at one another, and they each move forwards at the same time, they both will see the other player in the past, idle. In a dodging example, by the time you fire a missile at another player, they may well have already moved.

In p2p networking, this is difficult to resolve, because player’s cannot accept the concept that the game state is not running in realtime, in parallel. These conflicts represent fundamental issues with the influence of latency upon games, and so the designer has to introduce a means of simply saying, tough. Now, in server-hosted games, it’s easier to resolve. The server manages resolution of such actions, and whilst it still doesn’t remove the problems caused by latency, the server can produce more consistent consequences when conflicts arise. Getting “shot around a corner” is still a pain in the ass, but it’s something players can deal with, much more so than being shot from the other side of the map by invisible players, and leading by latency varying per player. With a client server design, the latency issues are consistently resolved according to the single latency to/from the server, and you have a single, referenceable game state.


Game networking is relatively young as programming is considered, as even is the internet. In fairness to Descent, it was published in 1994-1995 before QuakeWorld, the game which changed game networking. The original Quake used a client server design, but didn’t consider the issues that plagued most people’s internet connections. Quake was predominantly resigned to LAN games, because it required consistent, low latency connections for consistent and enjoyable game play. Given that broadband was a very novel concept for the majority of internet users at the time, this wasn’t possible over remote networks.

QuakeWorld introduced the concept of prediction. It removed the cleanliness of a pure client server model, whereby only the server has any knowledge of the game state, and clients send inputs and render graphics. Prediction is implemented in different means for different use cases, but typically it requires that the client simulate the game as though it were the authority on its own state. It sends the results to the server, which then performs the same calculations, and compares the two states. This way, the entire client state is derived from the server simulation, apart from the raw inputs themselves, but the client is able to move without waiting for the reply from the server. If there are differences between the server and the client state, such as a players moving in the path of other players on the server, the server has to correct the client to account for this change. Hence, it replies with a correction which sets the state of the player. However, this state represents the client’s state at time T (now) - round trip time. So, rather than directly setting the existing state to the corrected state, which would always introduce jarring transformations, the clients resimulate by applying the successive moves to the state that was received and producing a new state for time T. This will still result in visual discrepancies in the case of collisions, but the client will receive the colliding player state at the time of receiving the correction anyway, so it becomes apparent that it would have in fact collided. This correction is unavoidable​, and would otherwise manifest itself in the inconsistency of two player’s simulations, i.e other players would keep running into you and keep trying to move until the data from that client reflects the fact that they see you.

As soon as you start considering writing sanity checks for client state, the entire simulation becomes more and more bloated with unnecessary code. Whenever you add a new feature, the first question needs to be “How could a client abuse this feature?”. For P2P, the clients start looking more and more like servers, except that they have to communicate the results of these sanity checks with the other clients. And what happens if one client is cooperating with another to benefit one another by providing false sanity check results? For server designs, your server starts looking more and more like an authoritative server. Except, now you’re writing multiple implementations of features rather than a single reproducible one. You start adding more and more checks which would be provided by default using an authoritative server model.

To be clear there are two parts to this discussion. We’ve just discussed the security aspect, there’s also lag compensation, which is what you’re more interested in. This isn’t a result of how clients send data, but how the server handles it. Typically, lag compensation is easier done with authoritative servers given that they have a greater notion of state at any given time.

With Talon vs Descent, Assuming descent is pre-quakeworld, and therefore pre lag compensation, Descent favours low ping players whilst Talon is more balanced. Descent demands that you be able to determine the offset of your shooting, your lead, due to latency before you fire, which inevitably means missing several shots for more lagged players. With lag compensation, Talon rewards accuracy of shooting as one might expect in a shooting game, but introduces the possibility for players to be “shot around corners”. This is still possible without lag compensation if hit detection is done client side anyway. However, it still rewards low ping connections with winning a firefight because they see the other player first. Lag compensation addresses accuracy, not responsiveness.

Valve spends quite a lengthy paragraph explaining why they thought Lag Compensation was a worthwhile investment for Half Life here.

Talon (2013) can only be played up to about 200 ping, even then you get killed when behind cover, or by random projectiles you never saw.

As opposed to firing a projectile at a less lagged player, and never hitting them because they always move during the time to the server.

Lag compensation for projectiles is usually a more difficult decision, because they typically cause singular, critical and visual damage, and thus being killed by an apparent miss is much more obvious than, say, a sniper, which you cannot easily see being fired. In addition to this, projectiles live for longer than instant hit weapons, thus are more demanding for simulation. One must also consider what space the projectile lives in - should we move all players back by the RTT to the client, or move the projectile forwards by the same amount?

If we don’t lag compensate, well, it’s back to leading shots with unreliable lead times, and you’ll still die from apparent misses due to varying latencies amongst clients.

Any damage from collisions is calculated locally…

I also said that the game features dodging as a key element. So if I do damage calculations on the server, the player will have moved several times his hitbox length before he registers as getting hit. And a quarter of a second is easily long enough for projectiles to be created and hit the player: the player will take damage from things he never sees. All this equals not much fun.

That’s fine for the defender, but it also means aggressors will miss perfectly valid shots, according to their screens. Who do you favour?

you don’t send the remaining health, you send the change in health

Assuming this is Client->server direction, that’s fine, albeit vulnerable to infinite damage cheating.

The issues with doing things on a per client basis is that no-one has a true gamestate. It’s like having a wall that is a different colour according to the viewer. The server compromise is, yes networking introduces artifacts, but we can trust the server to resolve them and produce a consistent result. Giving the clients this responsibility leads to inconsistent, subtle or otherwise, gameplay experience which is noted to be worse for players.

Equally, servers are usually able to accept more data /second than clients. Rather than flooding home internet connections with packets, every simulation step you send the current simulation state to the client. This is smaller, and easier to handle.

Networking is much more reliable if you send larger, regular packets than smaller, numerous and frequent ones.

A ping of anything above 500ms is truly awful, and there’s no way to step around that. Most of the quotes pertaining to networking involve some reference to the fact that we hide latency, not remove it

Ultimately, there is always a tradeoff.

Concerning the lag in Josip’s setup is simply we only ask for one packet per tick. Sending more than one means that the socket will return older and older packets from the socket buffer. To fix it, encapsulate the


 try:
    received_packet = logic.client.recv(1024)
    logic.memory = loads(received_packet)
except:
    pass

with a while loop (while True), add “, error as SOCK_ERROR” to the import statement from socket, remove the colon after except, add “SOCK_ERROR:” and replace pass with break.

from socket import AF_INET, SOCK_DGRAM, socket, error as SOCK_ERRORfrom pickle import dumps, loads
import urllib.request as urlReq
from time import time


def client(controller):
	from bge import logic
	scene = logic.getCurrentScene()
	objects = scene.objects
	ob = controller.owner
	
	if not hasattr(logic, 'user_id'):
		logic.remote_users = {}
		logic.memory = {}
		logic.user_id = time()
		logic.client = socket(AF_INET, SOCK_DGRAM)
		logic.client.setblocking(0)   


	player = objects['Player']
	data = [logic.user_id, player.worldPosition[:], player.getAxisVect((0,1,0))[:],player.getAxisVect((0,0,1))[:]]
	packet = dumps(data)


	logic.client.sendto(packet, (ob['server'], 35107))
	while True:
		try:
			received_packet = logic.client.recv(1024)
			logic.memory = loads(received_packet)
		except SOCK_ERROR:
			break
	
	
	active = []
	for user_id in logic.memory:
		user_pos, yVect, zVect = logic.memory[user_id]
		
		if user_id in logic.remote_users:
			logic.remote_users[user_id].worldPosition = user_pos
			logic.remote_users[user_id].alignAxisToVect(yVect, 1)
			logic.remote_users[user_id].alignAxisToVect(zVect, 2)
		else:
			ob.worldPosition = user_pos
			new_user = scene.addObject('RemoteUser', ob)
			new_user.worldPosition = user_pos
			new_user.alignAxisToVect(yVect, 1)
			new_user.alignAxisToVect(zVect, 2)
			logic.remote_users[user_id] = new_user
		
		active.append(user_id)
	
	for uid in [uid for uid in logic.remote_users if uid not in active]:
		logic.remote_users[uid].endObject()
		del logic.remote_users[uid]
	
class netServer:
	def __init__(self):
		print('''
		[ Simple Python Server - BGMC Edition ]
		
		Make sure that port 35107 is forwarded
		and players will be able to join at:
				''', str(urlReq.urlopen("http://icanhazip.com/").read()).split("'")[1][:-2], '
')
		
		self.memory = {}
		self.history = {}


		self.socket = socket(AF_INET, SOCK_DGRAM)
		self.socket.bind(('', 35107))
		self.socket.setblocking(0)
		self.serve()
	
	def serve(self):
		while True:
			try:
				packet, address = self.socket.recvfrom(256)
				data = loads(packet)
				
				user_id = data[0]
				user_sd = data[1:]
				
				if not user_id in self.memory: print('User %s joined' %user_id)
				self.memory[user_id] = user_sd
				self.history[user_id] = time()
				
				data = {_id:self.memory[_id] for _id in self.memory if _id != user_id}
				packet = dumps(data)
				self.socket.sendto(packet, address)
			except:
				pass


			inactive = [ uid for uid in self.history if time() - self.history[uid] > 1]
			
			for uid in inactive:
				del self.memory[uid]
				del self.history[uid]
				print('User %s left' %uid)
			
if __name__ == '__main__':
	bgmcServer = netServer()



Good articles on the subject for those who’ve not read them yet,

https://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/Server_In-game_Protocol_Design_and_Optimization#fnote6
http://udn.epicgames.com/Three/NetworkingOverview.html

http://courses.cms.caltech.edu/cs145/2011/robustgames.pdf

Thanks very much agoose, going to take me a while to process those posts…