It's time go deeper, peeps. We'll start diving into the more involved/technical game hacking strategies moving forward. Today, i'm focusing on games built with Unity in specific. For the unenlightened, Unity is a cross-platform game engine that is used to create a lot of games. Kerbal Space Program, Cuphead, Doom, and Escape from Tarkov are just a few of the big names created with this engine. With so many big name games we now have a nice target to where we can create a certain attack flow based on what we want to from the game. Let's tear the face off this engine and start ripping it apart.
Tools we'll need to take with us:
DnSpy for - decompiling some code
Unity Asset Bundle Extractor - for extracting and replacing assets
Unity Asset Studio - for looking at and extracting assets
Harmony - for patching games
Cheat Engine - for live function calls
In order to access our assets, we gotta find the source folder of our game. Since this is a Steam game, we can find it in Steam>Steamapps>common>Risk of Rain 2. For a first look, I prefer to use Asset Studio because it can provide a preview to meshes before you extract them. It's better than the guess and check method of extracting assets. From the file path previously mentioned you're going to want to enter the directory Risk of Rain 2_Data. This a pretty standard as most Unity games store their assets in a base directory then store supporting DLLs and other resources in the subsequent directories that we'll get to later.
So we've got some assets, but what we do with it? We have a few options. As said earlier, we can steal it or sell it OOORRR we can make a mod. That's right you can make any edits to this model in Blender and use the import raw data in Unity Studio bundle asset extractor as shown below.
I'm not downloading Blender and modifying the mesh because that's just not my cup of tea. However, if you wanna make a mesh of a goose and run around bonking aliens messing with the honk, go right ahead. We can do the same with textures, audio files, and any asset you can find with your extractors. Just a reminder, this isn't as easy in other games like Resident Evil where you have to step through the memory and find the address character models are loaded into. This is Unity which doesn't require a ton of skills to create a game. Since we can customize the look, let's customize the experience.
Using DnSpy, we can decompile variables and functions for our target game. The directory that contains the target DLL is one directory further in called "Managed." The target file is Assembly-CSharp.dll.
Before you start making crazy edits and being a mad lad, create a copy of this file in case you need to restart. It's very easy to crash your game off of a single changed value. Once we have a back up, we can start exploring. This method is not only good for hacking the game, but also data mining for information about future patches and updates.
So one good thing about this game being in early access, we know there is unreleased content in the game. In the top image we can see that the devs left both functions and meshes related to an unreleased character named HAN-D in their code/assets. Theoretically, I could go ahead and add this character to the selection to play it in-game, but that would also require me to go in to the code and add all of it's assets into all the proper locations that refer to any character game objects related to HAN-D. This could take a while and I don't really want to spoil the character for myself since I do actually enjoy new content for this game. SPOILER: this character is a short-ranged, heavy-damage melee character. Moving forward, let's actually mess with some in-game functionality.
After sifting through the dev's half-decent code, I found the function that calculates how much money to take away from the player's character money stack. If we break it down, it starts by making an instance of the cost type and getting the player's money amount. Then, the this.paycost method sets the respective values of each field for both the item bought as well as the player's money stack. I went ahead and changed the value for the cost, so let's see how it affects the in-game player's money value.
If you watch the top left corner you can see the player has an initial stack of $68 then purchases a chest for $25, which should subtract from that value and give me $43 left over. However, as you can see, there was no effect on the player's money value. Nice! Harmony can be used to apply a patch in this situation as well, but i'm not going to go into it. Here is a link that does explain how to use harmony though: https://gist.github.com/pardeike/c02e29f9e030e6a016422ca8a89eefc9
Last step, let's take a look at Cheat Engines live mono injections. Go ahead and start your game, then start CE and hook the game. On the toolbar at the top there should be a tab named "Mono." This will inject a DLL automatically for the Mono Data Collector the game uses already and set up a pipe to communicate with (as per CE docs). What does this do you ask? Well, we've essentially attached a debugger where we can run functions at a high level or we can step through the ASM at a low level. This gives us access to change address values as we hit each individual command called. If you don't understand how this transition, I suggest you do a bit of research on how compilers and assemblers work to execute programs. Once the mono dissector is open, find a function you'd like to tackle.
In the image above, I have found a function that handles the timer on a player's run. We can see that if the stop watch is paused, it'll return the offset from the fixed time pointer. In the game, there is a level where you enter a "world between worlds" and time stops, which is probably why they created this function.
After running the mono dissector, I located the same function from before and used the "jit" command to take a look at the ASM. Immediately it is evident that the je instruction is most likely indicative of the if statement checking if the stop watch is paused. The test instruction does a bitwise and and sets the sign flag to 1 which is then checked with the next je. Since je is checking if 0 = 1, it jumps to the end of the function and returns.
Above is the result of changing the first je instruction to a nop and returning the function. Great! In-game time has been frozen and the difficulty scaling has stopped as it is based on how long you've been in a match. Since this is done in assembly real-time, there isn't any need to attach a debugger. With CE, we can also create a LUA script to automate this, then distribute it to anyone who wants it.
In the end, if your Unity game is using mono instead of IL2CPP, your game is open to being ripped off (if you care). I could see not caring if you just used assets from the Unity store, but if you're creating all of your assets at home, you're doing yourself a disservice. For those who don't know, IL2CPP ensures Unity converts IL code from scripts and assemblies to C++. This will allow for other types of attacks, sure, but your assets will be harder to pull than with Mono. These methods still aren't super in-depth and could 100% be done by people who don't have much of any experience in programming or modding. Nonetheless, jump in, fam, go break some games.