About Pointers

Pointers are 4-byte (this is machine-dependant) integral values like any other. Literally, they are nothing but unsigned longs (or dwords). Pointers are not magic, and have no special properties that make them confusing. Why are so many people confused about pointers? Because they have been led to believe they are more than they are—they have been given a misunderstanding.

Pointers themselves are not magic; you can not normally look at a chunk of RAM and determine what are pointers and what are not. The only thing that separates pointers from any other unsigned long is how they are used. For the sake of clarity, pointers are always displayed in hexadecimal format. See Introduction to Number Systems.

On the Data Types page, you can see that each data type has specific properties and specific uses. Since pointers are unsigned longs, they have the same range and sign as unsigned longs. Unsigned longs are used to perform mathematical operations on numbers with a large range. Chars are usually used to hold text strings. Shorts are also used for math, but with a smaller range, and floats are used for math, but on a much more precise scale. Doubles are used for even more precise math. Now it becomes clear that data types are actually defined by how they are used rather than their properties—the usage of a data type is defined first, and then it is given properties to facilitate that use.


So What About Pointers?

Now that it is clear that the only true separation between data types is the way they are intended to be used, we can discuss pointers, which are nothing more than another data type with an intended usage. Pointers, however, are not intended to be used for mathematical operations. Pointers are used to indicate the locations of objects (structures, classes, functions, data types, anything) in the RAM space of your game.

That means to understand pointers, you need to understand the RAM in your game.

We view RAM from low to high, with the low addresses on top and the higher ones on bottom. Humans are silly creatures.

This diagram illustrates how RAM generally appears in a game, with the red lines indicating the modules loaded by the game (these are different in every game). A module is any .DLL or .EXE file in this RAM space—the most familiar module is the one at 0x00400000, where your game is almost always loaded. Kernel32.dll, USER32.dll, and SHELL32.dll are other common modules.


The accessible range (as shown) is from 0x00000000 to 0x7FFFE000, which covers two gigabytes. But wait. What if you don’t have two gigabytes of RAM? And, wait a minute. How can both of your games be loaded at 0x004000000 at the same time? Most of the processes on your computer are running at either 0x00400000 or 0x01000000, so that must mean they each have their own 2 gigabytes of RAM, right? But then, that means every time you load a process, you suddenly gain 2 gigabytes of RAM, right?

In my left hand is a red pill. If you take it I will show you the truth. I lost my right hand in the war, so I’m afraid you’re stuck with the red pill.

Every address you see here is a lie. You always see 0x00400000 as the base address of your game, but that is not its real address. Windows® hides its real address, and uses a virtual mapping system to give each process its own “set of RAM”. These addresses are virtual. But luckily for us, we don’t need to know this at all! All that is important to know is that when working in any given process, we are working within a virtual space with addresses that have meaning only to that process. For the sake of simplicity we always treat them as literal real addresses.

It feels as if we are looking down on an ocean, from so high that all the tiny modules are just thin lines. The cluster of lines at the end are the system modules which generally have no use during hacking. The modules of interest are usually at the very base of this image, from 0x00400000 onward. In this diagram, address 0x00400000 is actually the second line! Address 0x00400000 seems as if it is deep inside the RAM of the game, but in fact it is hardly even a spec at the beginning of the whole. Every pixel in this diagram covers 0x00400000 addresses. Now you can see just how small 0x00400000 really is.

To actually see the modules, we are going to need to zoom in a bit. In this diagram, we have zoomed into the range 0x00300000 to 0x00440000. This entire range is a single line in the above diagram, but it covers the area just before and just after the address where games are typically loaded (0x00400000), and contains several modules.

This diagram illustrates where the modules have been loaded and graphically depicts their sizes. Any value you find inside the space of a module is static. In the diagram, logger.dll is 0x0004C000 bytes in size, ending at 0x0037C000. That means all addresses from 0x00330000 to 0x0037C000 are static, and they belong to logger.dll.

It is important to be very clear on what a static address is. In our above example, address 0x00340000 would be static. Some people define static addresses as addresses that never move. But quite clearly that would mean every address is static. Address 0x00340000 is always address 0x00340000, address 0x00300000 is always 0x00300000, 3 is always 3, etc. So it isn’t the address that never moves, since addresses never move anyway. It’s the data that never moves. The data for my player may be at address 0x00340000. Address 0x00340000 will always be there, but the data for my player may later move off to address 0x0234C000.

So a static address really means (almost), “An address containing data that never moves.” Thus, all the integers, floats, chars, bytes, structures, classes, etc., from 0x00330000 to 0x0037C000 in logger.dll, will always be at the same addresses. Well, almost, anyway. If you read the EXTRA section above you will remember that there is no such thing as static data.

Windows® reserves the right to load modules to different locations as it desires. That means logger.dll may not be at 0x00330000 next time we start the game. But the addresses from 0x00330000 to 0x0037C000 are static, right? That’s what we just established, right? Well, they are static, but the definition of “static” needs to be modified just a bit.


The Real Definition of Static Addresses

The final, real, true, undisputable definition of static addresses is, “Addresses that move predictably in relation to the module that owns them.” 99.999% of the time your game will always be loaded to 0x00400000, and thus 99.999% of the time, it is accurate to think of static addresses as being data that never moves. But it’s still a dirty lie that has millions of people fooled.

Using the diagram above, we defined addresses from 0x00330000 to 0x0037C000 as being static. Static addresses are relative to the module that owns them, and in this case that means logger.dll. Thus if logger.dll decides to relocate to 0x05000000, it is no problem, because all the values from 0x00330000 to 0x0037C000 have now moved to addresses from 0x05000000 to 0x0504C000.

Static addresses are noted in the format [module+offset]. For example, In logger.dll, we may have discovered a valuable integer at address 0x003304C0. Since logger.dll may move, we don’t just use this address directly. Instead, we use [logger.dll+0x4C0]. Therefore, when logger.dll decides to packs its things and move on to 0x05000000, [logger.dll+0x4C0] gives us 0x050004C0, and we still have our valuable integer.


The Other Kind of Address

Between modules there are many pockets of empty space, as shown in both diagrams above. Not all, but many of these addresses are available to the game to use as it pleases. This is the free store, where the game can use undefined amounts of RAM for undefined purposes. Games never know exactly how much RAM they are going to need at any given moment. Literally, this is up to the user to decide. For example, the user decides which map to play in a first-person shooter. Each map contains different data, and thus will consume different amounts of RAM.

When the game needs more memory, it asks Windows® to give it some. Windows® will create an area of usable RAM and give the game a pointer to it. Ah! Now we are starting to see the so-called magic behind pointers. Windows® will create an area of usable RAM at any location where there is enough unused RAM available. The only way the game is ever going to know where that new RAM is located is by the pointer that Windows® gives back to the game. Because the location where Windows® will find enough free RAM for the game changes, it is called dynamic. When the game requests new RAM, it is called allocation. Thus the process is called dynamic memory allocation, or DMA.


How a Pointer is Used

Remember, a pointer is just another data type, and data types are defined by how they are used. During dynamic memory allocation, Windows® will find an arbitrary address that can not be predicted, thus pointers are used to tell the game where the requested memory has been allocated. A pointer is an unsigned long that, instead of being used for math, is used to indicate the locations of various data the game needs to run. The game knows where the pointer itself is, and then by using that pointer it can determine where other things are—and so can we.

All data in all games have addresses and values. An integer can have an address of 0x00443C0C and have a value of 100 (0x64), and this value is used for mathematical purposes. Likewise, a pointer can have an address of 0x0042104C and a value of 0x004AE804, but this value isn’t used for math. This value is yet another address.

In these diagrams, we have zoomed into the range from 0x00400000 to 0x00500000. Our pointer is at address 0x0042104C, which we note as [lspiro.exe+0x2104C].

In the diagram to the left, the value of this pointer is 0x004AE804. 0x004AE804 is itself another address. That means if we go to address 0x004AE804, we will find more data that the game is using. It can be any kind of data, but let’s assume it is the data for our player.

The largest problem new hackers face is that after they die, they notice that the data for their player is no longer at the same address. This is a result of dynamic memory allocation, mentioned above. The game has decided it no longer needs the data at address 0x004AE804, and has told Windows® to get rid of it, and then to give it a new address where it can store the new data for your player as you respawn.

In the diagram to the right, the pointer at [lspiro.exe+0x2104C] is now pointing to 0x004BD808. The data for our player is now at this location, and we know that because the value of the pointer at [lspiro.exe+0x2104C] is 0x004BD808.

Notice that the pointer itself did not move—only its value changed to indicate a new address for our player data. The game relies on this pointer staying in the same location, or else it has no way to know where our player data is in RAM. When the game was compiled, address [lspiro.exe+0x2104C] was designated by the compiler as being the location the game can always use to find its player data.

This is why it is important that all the data inside a module moves exactly with the module itself. During compilation, the compiler writes instructions that simply “understand” the way each address (from the module base) is to be used, and if the data in the module does not stay perfectly aligned, the game simply can not run.



So How is This Useful for Hacking?

Surely you jest. If the game knows how to locate the data for our player, we know how to locate the data for our player. And that means always. Every time we die, every time the game is restarted, etc.

The only thing important to note that is that if the value of a pointer is 0x00000000, it does not mean there is actually an object at address 0x00000000. Value 0x00000000 is reserved to tell the game that the pointer itself is invalid, and points to nothing.

It is also worthy of noting that the values of pointers will be divisible by 4 in 99% of all cases. Windows® always gives the game pointers that are aligned on 4-byte boundaries because the architecture of x86 processors allows them to work faster on data that is aligned this way. If data is not aligned by every 4 bytes, the game may suffer from slowdown. Thus the only way pointers can point to addresses that are not divisible by 4 is if the programmers specifically go out of their ways to change the pointers. This is sometimes done as an optimization technique when performing some string-based parsing, but it is never done on objects that are frequently and randomly accessed throughout the execution of the game. Incidentally, these objects are the very objects we are interested in hacking, and thus it is always safe to assume that the data you are trying to find will have its base along a 4-byte boundary.


Structures, and the Pointers that Love Them

In our above examples, the pointer is pointing to our player data. Our player has a weapon, health, magic, experience, a level, gold, and a position in the 3-D game space.

Games organize sets of related data into structures and classes. Because your player has a lot of related data, it will always be organized a structure or class. This is actually done simply so that the programmers themselves (the ones making the games you hack) can work in an organized fashion, making complicated sets of data easier to manage.

These images depict how our player structure might look in RAM. The left image shows our player data at address 0x004EA804 and the right at address 0x004BD808.

Notice that the positions of each data element (officially called members) inside the structures are the same distance from each other, no matter where the structure itself is located in RAM. That is, our GOLD is always 0x10 bytes after the start of the structure. Structures and classes share something in common with modules: when they move in RAM, the data that is related to them also moves exactly with them. For this reason, we note their members the same way as we note addresses in modules: [structbase+offset]. In this case, HEALTH is offset 0x4, MP is offset 0x8, LEVEL is offset 0xA, etc.

We know quite a bit now. Let’s assume we wanted to know our player’s health at any given moment.

  1. Get the base address of lspiro.exe. That is 0x00400000 in this case.
  2. Go to [lspiro.exe+0x2104C]. This means 0x00400000 + 0x2104C. The result is 0x0042104C.
  3. Get the value at 0x0042104C. Let’s assume it is 0x004AE804.
  4. Go to the address depicted by the above value. That means we are now at address 0x004AE804. This is the structbase.
  5. Go to [structbase+offset]. Our structbase is 0x004AE804, and the offset of our health is 0x4. 0x004AE804 + 0x4 = 0x004AE808.
  6. 0x004AE808 is the address where our health is, as shown in the above diagram. And getting the value here gives us 9,999.


Pointers to Pointers

We have covered a static pointer ([lspiro.exe+0x2104C]) that points to dynamic data. But pointers are not limited to being static themselves. In the above player structure, the very first member (WPN PTR *) is a pointer to the player’s weapon data. Luckily, this has no special meaning what-so-ever. We already discussed everything there is to know about pointers, and, just because this particular pointer is moving around with our player structure, it does not mean it follows any special set of rules apart from other pointers. The value of this pointer is 0x028F3B0C, and like every other pointer we turn this value into an address. Now we know that at address 0x028F3B0C there is another set of data that is used to give details regarding the weapon our player is using.

Pointers can point to pointers that point to pointers that point to pointers that point to pointers. It doesn’t matter how many layers of pointers there are, because you now know everything there is to know about pointers, and they all work the same.


And So the Magic Behind Pointers Is…

Copyright © 2006 Shawn (L. Spiro) Wilcoxen