Examining memory addresses is useful in the making of TASes for looking at important values that are hidden from the player or difficult to determine just by looking at the screen. They are also important for making bots to assist gameplay, which is covered on the Lua scripting
Memory search is also known as "cheat search", which is a misleading name.
Table of contents [expand all
] [collapse all
Searching for an Address
Picking the right settings
One of the most important first steps is to use the right settings to find addresses (for instance, 2-byte is not a good idea on 8 bit systems). See Default Settings
for per platform recommended settings
There are more memory addresses than one can imagine, so to find what we are looking for, we must filter the memory addresses using the search box.
First, go to some relevant point in the game, one where you are about to change the value of what you want to find. Open the search box. Don't search anything yet, but click "Start" or "Reset". This initializes the memory addresses to those currently in the game. If you want to find addresses with a specific value now, enter the value, select "equal to", and click search.
Now play the game until the value has presumably changed. Now go back to the search box. Select one of "less than", "equal to", "not equal to" and so on. If comparing to the previous value, select "previous value" and click search. If trying to find addresses with a specific value, enter the value, select "equal to", and click search. Click "Update values" if the emulator doesn't do this automatically.
Keep doing this until you have narrowed down the possibilities and you can easily guess which one it is. Sometimes there may be two or more addresses which are similar to the one you're trying to find.
When you have found the address or there are a few addresses left and you can't figure out which one it is, watch the addresses in memory. Play the game, and the correct address or addresses should stand out.
A Useful Example - Finding X Position.
The simplest yet most useful value one can often find is the x position of a character. By monitoring x position one can be sure to maximize the distance covered in a given period of time. The following would be a procedure applicable to most games where x position would need to be found.
Super Mario Bros. (Japan, USA) will be used as an example.
- Start 1-1.
- Tools -> RAM Search (Sometimes also labeled Cheat Search)
The emulator sorts bytes based on how the value is changing. When standing still the x position should stay ‘equal’. Once moved to a different location, the x position is ‘not equal’ to the previous value. It should be noted that the search by default always compares to the previous value, not the original value. Another option is to compare the value to a known value.
- Click Reset to start a new search of all bytes. This initializes the value to be compared with.
- While standing at the original location, hit ‘equal’ numerous times while the game is playing. This gets rid all bytes that change when standing still (timers, random numbers).
- Move to the right, stop walking, and press ‘not equal’ exactly one time. This will eliminate values that did not change when Mario moved, and it should leave you with about 20 values. While standing at this location, one could press ‘equal’ a few more times to again get rid of bytes that are changing while you are standing still.
- Repeat as necessary
Following this method further, it should narrow the possibilities to about 20 bytes. Observing these bytes while playing in real time one could conclude that the x position is located at 0400. Further investigation may find the x subpixel position at 0086. The subpixel is Mario's x position decimal value out of 256. These memory addresses can be placed in Tools -> Memory Watch for convenient observation while playing.
Generally moving right introduces a value 'greater than' the previous value, and moving left introduces a value 'less than' the previous value. This is dependent on the programming convention of the game, and may not always be true. For example, some games don't have the constant addresses of objects' coordinates, but calculate them from screen and sprite positions.
You can easily save work by checking around the address you found. For example, if you found the player's X position, it is often reasonable to assume the player's Y position is two or four bytes ahead.
When looking for a speed value, moving right is generally positive and moving left is generally negative. In this case you usually need to use 'signed' comparisons.
Negative numbers are interpreted differently depending on whether the value is being read as signed or unsigned. When reading a byte ranging from 0 to 255 without interpreting the sign (unsigned), a positive number counts up from 0 and a negative number counts down from 255. When the emulator is searching for a 'signed' value, it performs this interpretation for you to make the values range from -128 to +127. Comparisons must account for this.
In newer emulators one may have to choose between a data size of 1,2, or 4 bytes. One must predict whether the byte holds less than or equal to 255, 65535, or 4294967295 intervals of precision. One step by a character could be anywhere from 1 unit to 10,000 units, so trial and error of data size may be necessary. Checking misaligned values is usually not necessary.
A good starting approach may be to select "2-bytes" for search. This avoids confusion near byte boundaries (such as when 127 goes to -128, or 255 goes to 0). If it doesn't work, try "1-byte" or "4-bytes."
For relatively simple platforms, like NES, you can use the inbuilt Hex Editor to observe RAM, watching what addresses are changing while you're acting. For example, you never know, how the subpixel value exactly changes, so it may be easier to look for values, that change every frame when the acceleration works, and stay still when it doesn't. Also, it's more useful to turn the whole pages ("Page Up", "Page Down" keys) while observing RAM, not to scroll them.
Dynamically allocated memory
Games sometimes allocate data during execution, this means that the exact address of things may change between levels, starts of the game or even every frame. This can greatly complicate your work. But you can always find a pointer to the currently used address.
The simplest case would be that a fixed address always holds the exact address of the value you do care about.
But more often will the pointer hold a base address and the value you are looking for be stored at a known offset from the base address.
Of course, there is no rule that the pointer will be stored at a fixed address either. But there will be a pointer to the pointer in those cases.
The callstack is an especially popular location for direct pointers.
Arrays, linked lists and other fun data structures
Games sometimes store collections of similar items, like the list of active enemies. These collections are often stored as either arrays or linked lists.
Arrays are just regions of memory where several instances of the same type of data is stored in sequence. They are very popular with simple games, since they can be statically allocated in a fixed spot all the time.
Linked lists are more common on more modern systems, but can still be found on older platforms. They are built as a chain of similarly formated regions of memory that are linked together using pointers. Linked lists are often double linked, each item having a pointer both to the next and the previous item in the list.
You may also find hashmaps. Hashmaps act like an array, but allow for fast retrieval for data with a specific key. In order to work with these you will need to reverse engineer the exact hashing function the game uses.
In all cases for these data structures you will need not only knowledge of where in memory the data structure can be found, but also knowledge of how to find the specific item in the collection that you want to know about. You can often find pointers or indexes to the specific value elsewhere in memory.
Practically every computer system actually used in practice uses a callstack approach.
The callstack is a dedicated region of memory used to implement function calls. The callstack commonly grows downwards, the address of new data decreasing.
As functions in the program are called values are added to the callstack. Some of these values can be very interesting, since they can be the value you are searching for.
The problem with the callstack is that values are often overwritten. Functions returning and new functions being called makes memory reuse exceptionally high and can greatly complicate memory watching since very exact timing may be required in order to see the memory as it is being used for the desired purpose.
The advantage to watching the stack is that very interesting values tend to be stored on it, such as intermediary values used in game logic.
Mirrors, duplicates, leftover values and other copies
Data is very frequently fetched from one location and stored in another. The address you find might not be the actual version of the value, only a copy made for further processing.
Examples of these duplicates include values used for display purposes, previous allocated memory and arguments on the callstack but there are endless reasons for games to duplicate data.
Be vary that these copies might lag behind the real value, might be more or less stable in their address and can have subtly different rules about the actual stored value.
- Ram Search contains further documentation on the RAM Search dialog that is common in many rerecording emulators. It may be a helpful resource to further detail the information on this page.
- Memory Hacking Software by L. Spiro is an external memory search tool that works with any program. It is extremely useful for finding bytes in emulators without built in cheat search functions.
- Addresses Sets contain many useful addresses, allow you to download them as .wch files for the supported emulators, let you contribute your own, and provide modules to embed existing address sets on Wiki pages.
MemorySearch last edited by adelikat
on 2013-10-07 15:41:57Page info and history | Latest diff | List referrers | View Source