Find the hidden Golden Eggs
TL;DR
After digging through classes, we find that we need to list all the actors in the game. Using the in-game chat to interact with the code, we extract a list of all actors and teleport to each of their coordinates in search of golden eggs. We also find out another method of tracking down each egg's location so we can pick up the Flag of the Egg Hunter.
The Video
Introduction
In the last article, we discussed how we could use teleporting and hovering to get the Unbearable Revenge flag. We're making progress towards collecting them all! This time, we decided to skip the Overachiever quest, as it involves completing each achievement in the game, and "Egg Hunter" sounds more interesting anyways. So, in this episode of the Pwn Adventure 3: Pwnie Island video series, we'll work towards finding all of the golden eggs in the game and completing the Egg Hunter quest!
The Egg
You might've noticed a shiny, golden egg in previous episodes when we tried flying out of the cave at the beginning of the game.
What's up with this? While we were reverse engineering and debugging the libGameLogic.so
library, we actually stumbled across the GoldenEgg
class, which implies the presence of eggs somewhere in the game. This was effectively confirmed when we realized we'd gone past one of them when trying to fly out of the cave!
So, we know where one egg is. What about all the other ones? Is there a way that we can just get all of the egg locations from the code, so we can quickly collect them all and complete the quest?
Let's Get Digging
... not in the game, but into the code! If the game can place an egg in a location, surely the egg has a position, or a set of coordinates (x, y, z).
Checking The Classes
Let's start with the GoldenEgg
class. This class is an Item
(as can be seen below), which in turn is an IItem
...
The functions contained in each class actually didn't really point us in any particular direction. In fact, there is no GoldenEgg
object. So, let's look at this the other way: we need to find something that gives us a reference to a golden egg.
To do so, we looked for any piece of code that returns or otherwise handles golden eggs. In our search, we saw that the GameAPI
class contains a GetGoldenEggList();
function, which returns a vector of item pickups.
The ItemPickup
class (which is also an Actor
, by the way) holds a reference to IItem
objects, which we know is what our GoldenEgg
class is. So, we're in a good position. Note also that this ItemPickup
class also has a couple of lines allowing the player to use the object, basically interacting with it in-game. This is a good sign!
virtual bool CanUse(IPlayer *);
virtual void PerformUse(IPlayer *);
If we can get a reference to the GameAPI
, then we can just call the GetGoldenEggList()
function and, well, get the list of locations of all the eggs. By the way, remember the global GameWorld
variable? We found another global variable while exploring around, called, Game
, which is in fact a GameAPI
object. So let's just call it using GDB and get the locations. Well, it didn't work.
It seems to be empty. How about p g_eggs
? Nope, doesn't work either.
Thinking About Our Approach
Let's think about what we've learned so far. We tried to see if the GoldenEgg
class returned a position. It doesn't do so, but we learned while reading the class that it is effectively an Item
object, which is in turn an IItem
object. Finding a reference to the eggs got us to a couple of functions which we thought would give us the answer, but they instead returned empty vectors. We did all of this on the client side, since it's where we're working from to hack the game.
Hacking requires understanding what's going on at both a high level and a more in-depth level. In the case of Pwn Adventure 3, the server and client talk to each other (a lot). It's possible that the golden eggs are not handled client-side, but only server-side, with the egg position information then fed to the client at the appropriate time. This method makes sense! However, we tested it out by going to the cave with the egg located halfway to the opening in the cave ceiling, all the while checking the output of p Game.GetGoldenEggList()
and p g_eggs
. Once again, they both return empty vectors.
This is, as the youths would say, "wack", but it makes sense if we think about it. Both the server and the client share the libGameLogic.so
shared object. Since they share this, it is totally possible to make the client calculate something or interact with the shared object just like the server would. Effectively, if the server has the list and the client doesn't, maybe we can find a way to make the list available to the client, allowing us to quickly and efficiently track down all the golden eggs.
Getting the Egg Coordinates
If we're standing in front of an egg in-game, surely its position has to be listed somewhere in the code. This one took us a while to figure out, but it's pretty nifty. Remember the ItemPickup
class? The code also tells us that the GoldenEgg
object is also an actor via the very same ItemPickup
class.
The GameWorld
object can give us a list of all the actors in the in-game world. Can you see where we're going with this yet? Maybe the eggs are in the actor list given by the GameWorld
object. Let's check it!
To do so, we wrote a short function that we could input in the in-game chat (it's like having our own terminal in the game, how cool is that?!). We appended it to the code we wrote in the previous article. The code for the actor list looks like this:
else if(strncmp("actors", msg, 6)==0) {
// get the address of the global variable GameWorld
ClientWorld* world = *((ClientWorld**)(dlsym(RTLD_NEXT, "GameWorld")));
// loop over all actors in the world
for (ActorRef<IActor> _iactor : world->m_actors) {
Actor* actor = ((Actor*)(_iactor.m_object));
Vector3 pos = actor->GetPosition();
printf("[actor] %s: %.2f %.2f %.2f\n", actor->GetDisplayName(), pos.x, pos.y, pos.z);
}
}
What this code does, is if we type "actors" in the in-game chat and hit the return key, we get all the addresses of the GameWorld
global variable, which gives us the list of each actor in the game world. Then, the position of each actor is obtained from the game, and output to the terminal where we can read the coordinates in their (x, y, z) format.
If you pair that with the teleporting command we set up in the last article, you're in business!
Remember, you can checkout GitHub for the complete code. The code for the golden eggs is available here.
Since we had a list of all the actors but no idea which was which, we needed to cycle through them one after the other. As you saw in the screenshot above, this has led to some untimely deaths in-game... but it doesn't matter. Very quickly, we found our first egg! We also unlock a new achievement, the Chamber of Secrets, and start the Egg Finder quest.
We then continue to cycle through the list and work our way through to each egg! We skipped over teleporting to the NPCs, because the eggs are probably not around them. Collecting the eggs is easy at this point, and we're about to be done, until we get to the last egg at Ballmer Peak, and it's nowhere to be found! Time to investigate some more.
Ballmer Peak
Teleporting to the next actor's position gets us in front of an xkcd about Ballmer Peak. Interesting... We took a look into the disassembled libGameLogic.bndb
with Binary Ninja, searched the file for the Egg
term and found this line
BallmerPeakEgg::~BallmerPeakEgg()
If we check the CanUse
function from the BallmerPeakEgg()
function, we find that it checks for a BallmerPeakSecret
.
This secret is only referenced in other places in the code such as BallmerPeakPoster::damage
. So, what's this damage about? Looking at the relationships within BallmerPeakPoster
, there seems to be a check having to do with "CowboyCoder"
before we can reach the BallmerPeakPoster::damage
point. Cowboy Coder is in fact a weapon that we picked up as part of our Cow King article where the Cow King dropped a revolver as loot when we defeated it. That revolver is called the Cowboy Coder!
Okay, let's try something. We used the Cowboy Coder revolver to shoot the poster. After firing our six shots, the poster showed no visible damage.
We decided to go back to where the actor coordinates were for the last egg, on the balcony of Ballmer Peak, and lo and behold, the final egg! And with it, the Flag of the Egg Hunter! That would've been worth 250 points in the Ghost in the Shell 2015 CTF competition. Not bad at all!
But wait, there's more!
Alternate Method
In retracing our steps to write the procedure for the video, we realized that we'd missed some pretty interesting content. There is in fact another way of finding all the eggs' locations. If we look at all the initialization methods that happen for the game, we realize that they follow a linear structure.
For instance, GameAPI::InitObjects()
is a function that creates the objects in the game, one after the other, in a linear fashion. As you might imagine, it's huge. We decided to write a bit of Python code to help us parse through and find the information that is relevant to us. We implemented it in Binary Ninja using its API console.
This loop goes over every instruction (line 1), and looks for a call (line 2). It gets the call target (line 4), which is a function bv.get_function_at(il.operands[0].value.value)
. What we're interested in is its name, so we grab that by appending .name
at the end of the function, and assigning it to the fname
variable. Finally, if the Egg
string is in the function name, the hex address is returned, which gives us the entire list of golden eggs. Bingo!
We can check this by looking in the disassembler again; for example, the hex address 0x1751d1L
gets us to a place where the constructor of the class is called, which calls the operator new
, basically creating a new golden egg object. Rinse and repeat for all the other eggs.
At some other point in the code, a 3-dimensional vector is created with three parameters xmm0
, xmm1
, and xmm2
. These are registers that are used for floating-point operations... but the hardcoded values that are given are of type int32_t
, a type which is evidently not a float
. What's this all about?! We changed their data type to float
, and it gives us the in-game location of the eggs! Pretty nifty indeed.
So there you have it! Two different methods that you can use to finish the Egg Hunter quest and pick up the Flag of the Egg Hunter. Eggselent work!