top of page

Pointer chaining

In the last article we saw how convenient it is to extract global data. This is hardly the case in a real program. The data you're looking for may be part of a struct which is pointed to by a member in another struct and so on. Take the following program for example.

It is a modification of our original test program. Instead of modifying a global value directly, we now modify a member of a "LevelThreeStruct" struct object. This struct object is pointed at by a member of a "LevelTwoStruct" object, which is in turn pointed at by a member of a "LevelOneStruct" object, which is global (That was a mouthful!). In other words, the value we are looking for is two levels deep. If we follow the steps described in the previous articles to isolate this address via cheat engine, we end up with something like this

The address for your variable is not static (indicated by the black color). This makes sense, since we're now dealing with a heap value. This value on its own is not much use to use. We could change it, but will need to find it again each time the program is run.

The way around this is to see if we can find a "chain" of pointers originating from a static memory address that lead to the value we are interested in. We begin this process by right clicking on our address in the tracked addresses window and select "Find out what accesses this address". This will attach cheat engine to the program using the "DebugActiveProcess" system call, allowing it to receive debug events from the target process. Cheat engine then sets a hardware data breakpoint at the memory address, which causes the emission of debug events whenever the address is read from/written to (this is a good article on software and hardware breakpoints). The next time we increment our target variable, cheat engine will intercept these debug events and tell us which instructions accessed our memory location. Be aware that attaching cheat engine to a multi-player game or any game which wants to prevent you from cheating could lead to a ban. In most cases though, this is safe for single player games.

The resulting window will look like this.

In some cases, we will start seeing instructions which access the address from the get go (happens when the value is being written to/read from each frame). In our case though, the program does not touch the value unless the console input tells it to. We need to go back to our console window and increment the target value. This changes the above window on my machine in the following way.

We need to then double click on any of those values to get more info.

The assembly instruction "mov [rcx+04], eax" tells the processor to store the value in the eax register to the address pointed to by "rcx+4". What we're looking at here is a write into the LevelThreeStruct object, four bytes from the beginning of the object. This 4 byte offset is caused by the first member of the struct, the integer "int_value" (integers being 4 bytes long on my system). The object in memory looks like this

When we look closely at the "Extra info" window, we see that the RCX register holds 135001821f0. This is the address at which the object pointed to by "l3_pointer" starts. We can now search the program for this value and find everything which holds a reference to this struct object. Cheat engine makes this easy for you. You don't need to look at the registers to find the value you need (sometimes they will not have the correct values, since we are shown the state of the registers AFTER the instruction is executed) , you can note down the value at the end of "The value of the pointer needed to find this address is probably xyz". Go back to the main cheat engine window, enter the value you just noted, check the "Hex" box, and hit "New scan"->"First Scan". Here are the results I got on my machine

We now repeat the process we followed for our first scan: Find out what accesses each of the addresses we just found (2 in this case). You could end up with 0 results while investigating some other program, indicating a dead end, or you could end up with a bunch of results, in which case you have to investigate all of them, or try a different approach.

In my case, the first address did not show me any instructions that accessed it, but the second one yielded the following results.

This time, the offset is 16 (in decimal, we're seeing 10 in hex above) bytes. This is because we're accessing the "struct_pointer" member inside the LevelTwoPointer object, which is 16 bytes from the beginning of the object (8 bytes for the double and 4 each for the float and int). We double click it for more information, to get the new address we need to search for.

We search for "1350017D750" and I got a single result on my machine

The instructions accessing the address were

As you might have already guessed, the offset this time is 8 bytes. We're dealing with the "LevelOneStruct" object this time, and the offset is caused by the int and float members, each being 4 bytes long. We get more info on the instruction to get the final address we need to search for (of course, for an unknown program you would not know how deep you need to search).

The final scan gives us what we've been looking for, a static (green) address! This is the address of the global variable "l1_pointer" in our case.

Double click on the address to move it to the tracked values section, and then double click the address column again to get the relative offset for the address (in my case it was "PointerChaining.exe+10370")

Cheat engine lets you store pointer chains conveniently. Click on "Add Address Manually", check "Pointer" and add offsets till you're at 3 offsets. Fill in the offsets you've noted so far and hit OK. Cheat engine now knows how to locate the value you are looking for, and will be able to do so even if you restart your program.

You could create a standalone program which uses "ReadProcessMemory" to traverse this pointer chain, find the desired memory location and modify it!


Comments


bottom of page