For the 10th Insomni’Hack anniversary, new hacking challenge categories were available during the CTF. They consisted of social engineering, hacking room, and a multiplayer FPS game.
This article will cover several write-ups for this last category. It is a great occasion to understand quickly some basis of modern game hacking.
The game was compiled with Unity Engine, methods demonstrated here could work on other games compiled by the same engine. The binaries and installation instructions will be available at the end of the article.
The following mention was written into every challenge description: “You will find almost all of the logic code into Data\Managed\Assembly-CSharp.dll.“
A .NET assembly editor and decompiler are needed as CSharp uses .NET platform. The decompiler has to feature a decent search method (a game .dll to analyze is likely to have a large number of classes). For this write-up, the dnSpy tool was used. It is available on Github.
The game developers are implementing a tutorial level called “Basement”. They tried to block players from accessing the level by setting obstacles. Show them you are still able to reach the main room.
This is the first and only challenge to do on the “Basement” map.
Once connected, we spawn into a small room. There seems to be only one way out :
Our jump power is way too low to go there. So we could try to implement a fly hack but it may be too overkill for this first challenge. A smarter way would be to find a variable that increases our jump enough to go through this way.
We launch dnSpy and decompile the earlier mentioned .dll.
We should start by searching for the “Jump” string into DnSpy. At this point, we already note that a lot of our search results are located in the vp_FPController class and see some interesting names containing the JumpForce string which sounds like an interesting name. We refine our search:
(On dnSpy, a string being referenced several times in the same file will only be shown once into the search assemblies (bottom window) results. You must then use the other search bar in the editor window at the upper right corner to navigate through the several references from the same file.)
The MotorJumpForce is the only candidate for a jump variable that is located in this vp_FPController class that we see so often in the results. Let’s start from there and if it doesn’t work we will check the ones in the other classes.
This variable is only referenced into the OnStart_Jump callback whose code is below:
(Note the usage of the class member MotorFreeFly on the second line whose name is interesting. In this method, this class member would cancel the jump if set to true and if the player isn’t grounded, this will be useful for the next challenge).
Now that we found this function, let’s multiply by 10 our jump force. Edit Method (Ctrl+Shift+E) and replace the next to last line for this one:
Now press the Compile button at the bottom-right of the window to close the method edition and Save All (Ctrl+Shift+S) to overwrite the existing .dll.
We’ve succeeded to through this way, but at some point, there are mines blocking the way. If we listen carefully we hear a trigger sound before the explosion occurs. If the explosion occurs after a delay, and that we succeed to trigger it running fast enough, we can avoid damage.
Now that we patched the jump force, we should try to patch the player’s acceleration or movement speed by searching some related words like “speed” or “acceleration” in the same vp_FPController class.
We fall in two methods coming from the same caller UpdateMotor() :
If we suppose the MotorFreeFly variable is not set, we have to edit the last line of the UpdateThrottleWalk() method, where m_MotorThrottle.z get reassigned. Z-axis corresponds to the W and S controller keys.
Once we increased this variable, we are able to pass this obstruction, we get the flag and can finally move on the next map.
Reach the Top
Positioning is the key to any tactical operation, especially if you are alone. Prove you are the best player by reaching the top of the building:
(Using the previous jump hack doesn’t directly help to solve this challenge because you need to be alive for several seconds to get the flag).
The first idea is to remove gravity from our player. m_GravityMultiplier and PhysicsGravityModifier look good candidates especially the second one since he’s located in vp_Controller.
Once again we find some references in the vp_Controller class, in a method called UpdateForces that is not taking any parameter.
This method is called every frame and calculates the falling speed the player.
We can replace the 0.002f coefficient to 0f, and recompile. The plan is to jump and see if we could go above the building.
In this demo, the player is getting stuck after his first jump. The player cannot jump a second time because the controller checks whether you are grounded or not before allowing your next jump.
Remember the MotorFreeFly variable we found several times earlier? Let’s inspect it a little more, and find references to it. Right-click on it and choose “Analyze” (Ctrl + Shift + R).
Again, we see that the functions come from the same vp_FPController class we edited earlier. The MotorFreeFly variable is used 4 times in it. In the CanStart_Jump and OnStart_Jump functions, this variable is used to check if the player should perform a jump, but the UpdateJump and UpdateMotor functions, that are called every frame, reveal something more interesting:
As we saw earlier, we understand that the controller will handle walk and jump functions differently depending on whether the MotorFreeFly variable is set to “True” or not.
A lot of FPS games have an advanced “free fly” mode implemented (called “no clipping” and prevents the first person camera from being obstructed by other objects and also permits the camera to move in any direction). The code implemented for this feature could be triggered by a player to cheat without having to understand the game physics.
To solve this part, we should find all references to MotorFreeFly in any conditional expression, remove the ‘!’ negation operator when prefixed, and conversely append it when not present.
A bug was reported to the developers about an unstable toasting feature. They patched the toaster to restrict players from triggering it by adding a keypad. Once the toaster is activated, you don’t have much time to enter the password. Could you show them that you can still make a toast?
Let’s search for the “Toast” keyword:
We find an interesting function name, it takes a string in an argument called “code”.
This function seems to be a wrapper for a RPC call destined to the Master (second argument) that checks if the submitted code matches. The RPC function code is the following one, it’s executed server-side:
No anti-brute force mechanism is implemented, then, surrounding the RPC call with a basic loop will brute-force the PIN:
Finally, trying any combination in-game results in defusing the toaster and receiving the flag.
The game was not designed to be publicly released. It is still far away from a real game and this is why won’t find it on any game platform. Nevertheless, it has a great educational value and we want to share it for these purposes.
Run the PhotonControl application, and set the Server IP to 127.0.0.1.
Then, start the LoadBalancing application.
Finally, launch the game depending on your platform: