Hacking Unity games (.NET disassembling, patching)— Nullcon CTF HackIM 2020, Zelda Adventures.

Vaibhav Choudhari
5 min readFeb 12, 2020

It’s fascinating how in the world of programming/hacking, one single problem statement can have multiple solutions. While Tushar Kulkarni has already posted a thorough write-up on the same challenge, I want to show a different approach which I took to solve the same challenge.

This year a new miscellaneous category of challenges was seen which was — Zelda Adventures.

The category had three challenges:

  1. Zelda and the Zombies
  2. Zelda at the Swamp
  3. Zelda crossing the land’s end

All the 3 challenges involved downloading the same zip file which contained a binary named “zeldaadventure.exe” which when executed resulted in a good ol’ 2D game.

Zelda Adventures

The instant instinct for anybody who has hacked/patched games in the past using Lucky patcher, Game Killer or Cheat Engine would be to approach the challenge using the same patching techniques. However, I wanted to take a different approach for this challenge.

Let's dive into understanding the approach.

When we execute the binary, we can see a logo of “Unity” popping up before we could start playing the actual game. Which apparently meant that the game was programmed using Unity.

Unity Logo

Whenever a game is compiled using Unity engine for a Windows platform, it is packed with a folder named <gamename>_Data which contains a file called Assembly-CSharp.dll which stores all the compiled resources of the game.

And that is where the juice lies.

The Assembly-CSharp.dll is nothing but a .NET assembly which can be easily disassembled by any .NET disassembler like dnSpy, ILSpy, dotPeek etc. Once disassembled, one can explore the in-game functions.

I opened up the Assembly-CSharp.dll in dnSpy and explored the de-compiled code which was apparently pretty straight forward.

All of the 3 challenges revolved around patching certain methods and just recompiling the dll.

De-compiled Assembly-CSharp.dll

Solving the challenges:

1. Zelda and the Zombies
- The task was to kill a NPC (Non playable character).

As per the game controls, you could use SPACE bar to hit a NPC. However, hitting an NPC in-game didn’t kill him. I tried exploring the map to look for some other non-hacky ways to kill the NPC but couldn’t find any. So I resorted to disassembling the code to kill a NPC.

I went on exploring the classes in the disassembled dll looking for health/damage related methods and I came across a class named “Enemy”. Looking at the methods of the class it was quite obvious that the class was programmed for NPC behavior.

Our goal was to kill a NPC so I focused on the TakeDamage method:

private void TakeDamage(float damage)
{
this.health -= damage;
if (this.health <= 0f)
{
base.StartCoroutine(this.ShowSome());
base.gameObject.SetActive(false);
}
}

Just what we wanted! As you can see, the method takes damage as a parameter and reduces the value from health. Further if the health is found to be ≤ 0 a call to ShowSome() is made which can be a function call to display our flag.

I edited the method and set the value of health to 0f. ;)

Compiled the dll from within dnSpy, saved the module and restarted the game! And as expected..

U touch me U die

The flag was — “revolutionstartswithme”

2. Zelda at the Swamp
The task — “Head to FlagTown and cross the pond to get to the flag!”

Collision is the property which prevents the game characters to pass through rigid bodies inside the game for eg: walls. For this challenge, the flag was placed inside a pond which was a rigid body that prevented Zelda from passing through the pond.

Alright, so in the disassembly, we should probably look for methods related to movement. After exploring the PlayerMovement class, I came across a method named — MoveCharacter.

private void MoveCharacter()
{
this.change.Normalize();
this.myRigidbody.MovePosition(base.transform.position + this.change * this.speed * Time.delataTime);
}

As it appears, Zelda’s movement is determined by this particular function. Our goal for this particular challenge was to get access to the flag which was surrounded by rigid bodies which meant that we had to somehow surpass the rigid bodies to get our flag. Kicks in — SPEED HAX!

I multiplied Zelda’s speed by 5 which basically enabled our character to cover more distance per unit on both x and y axes per move than the original logic.

Increasing the speed by 5f

Which led Zelda to cross the pond and get to the flag.

The flag was “bendtherules42pirate”

Zelda just Flash-in around!

3. Zelda crossing the Land’s end
The task read— “Zelda is on the quest to find other survivors and is now standing at the land’s end trying to cross over.”

For this challenge as well, our flag was at an unreachable area surrounded by rigid bodies. During the CTF, I could see a lot of players banging their heads over this one! :P The players were able to see a tiny part of the flag but the walls restricted them from crossing over to the flag area.

With our speed hax enabled, it was a piece of cake to explore the whole map. I re-started the game and explored the whole map like a pirate on steroids!

BA DUM TSS!

The flag was “explorerforlife”

The challenges were a good refresher for the good old days of dirty patching! Even though fairly simple, they did consume a fair amount of time. Especially in identifying the right dll file to tinker with. Now we know where to head on if we ever come across a Unity game. ;)

Thanks for the amazing challenges Aneesh Dogra.

--

--