Many people know how to find multi-level pointers, but few of them actually know what they look like at the high(C/C++) and low(ASM) levels.
Personally, this happened to me, before someone taught me.
That’s why I decided to write this small post, to show you what you’ve been playing with.
Small Recap: What are Pointers of 1 Level in the high level (C/C++)
If you come from a programming background, you know that a pointer is a variable that points to another variable by having its memory address.
Let’s take this code(x86) as an example:
int abc = 1337; int* ptr = &abc; // declare an int pointer and assign the address of abc printf("Address of abc: %p\n", &abc); // %p is used to show the address printf("Address of ptr: %p\n", &ptr); getchar(); // avoid exiting
Now if we hop into cheat engine we can check the value behind ptr.
As you can see, ptr simply contains the address of abc.
Now that you understand a level 1 pointer, it’s time for the real the deal: Multi-Level Pointers.
Level 2 Pointers Example
One instance where these are used is for classes member variables.
Let’s take a look at this example:
#include class Player { public: int health = 100; int power = 50; }; Player* p = new Player; int main() { while(true) { p->power++; printf("Player x is: %i\n", p->power); getchar(); } }
Note: I didn’t put the variable p inside main to avoid it being referenced by ebp, so we can see its real address.
I made it so when you press ENTER the health changes to ease finding the pointer.
Now onto CE:
What I did: Searched for 51, found 5 addresses, pressed ENTER and searched for 52, then I right-clicked->”Find out what writes to this address” and press ENTER again.
Now to find the multi-level pointer you could either do pointer scan, manually going through the each “what writes to the address” or just read the assembly.
I’m always about the later, since it’s quicker, so let’s head over to the “Show disassembler”.
Note: Your assembly might be different, no worries, that depends on the compiler you’re using.
Since this example is pretty simple we can already see that our multi-level pointer (in this case 2 level) is:
Base - BaseModule + 0x1B2E0 1st Offset - 0x4
But why is that?
So, if you look at the code, you have a pointer to an instance of Player:
Player* p = new Player;
Which shouldn’t be anything new to you, however, the first offset is there due to the structure of our class.
class Player { public: int health = 100; int power = 50; };
We know that the int data type takes 4 bytes. (that’s also why we searched for a 4-byte value in cheat engine)
So by adding 0x4 to the address of Player, we head over to the power member variable.
Oh and this is what the disassembly would look like if we used the health variable instead of power:
As expected, since health is the first member variable, we don’t need to add anything to the address of Player.
Level 3 Pointer Example
Now, we saw level 1 and 2, let’s take a look at a 4th:
class Weapon { public: int weaponID = 1337; int damage = 25; }; class Player { public: int health = 100; Weapon weapon; }; Player* p = new Player; ... p->weapon.damage++; printf("Player's weapon damage is: %i\n", p->weapon.damage); ...
I’ll be skipping the steps to get here since you already know.
If we now check the assembly:
Strange isn’t it? Well, let’s take a look at the structure since we know the address.
By pressing CTRL+D or going to:
Now we can either grab the dereferenced address or the actual “instruction”:
Then paste that into the Group address and define a new structure:
Press ok to everything(seriously, it doesn’t matter) and as you can see:
The class Weapon merged into the Player one, that’s why it is 0x8, it’s 0x4 to get to the weaponID and another 0x4 for the damage.
So we still get the 2-level pointer yet if we change the weapon variable inside the Player class to a pointer, we get a 3-level pointer.
Changes made:
class Player { public: int health = 100; Weapon* weapon = new Weapon; }; ... p->weapon->damage++; printf("Player's weapon damage is: %i\n", p->weapon->damage); ...
Checking in cheat engine’s disassembly view(again, same steps):
Now we can see that our multi-level pointer is:
Base: BaseModule + 0x1C2DC 1st offset: 0x4 2nd offset: 0x4
Meaning it is a level 3 pointer.
The first offset is to get the address of the Weapon instance and the second offset is to get the damage member variable.
This is one example where multi-level pointers are used, another example is of course when you actually make a multi-level pointer in the code:
int abc = 1337; int* onePtr = &abc; int** twoPtr = &onePtr; while(true) { (**twoPtr)++; printf("abc is: %i\n", **twoPtr); getchar(); }
There’s nothing special here, just a pointer that points to a pointer, no offset here either.
We could make another class inside Weapon and have another pointer and we would have a 4 level pointer, there’s no difference except there would be another dereference.
That’s it!
I hope that this has helped you understand exactly what multi-level pointers are and how they’re used.