Wii Hacking Example
Infinite Health in Mario Galaxy
Last Updated: 6/28/2008
DO NOT PM me with questions regarding the material in this text or Wii hacking in general. If you want to know why this is bad read this. I recommend that those seeking help visit one of the various forums dedicated to Nintendo systems hacking (I am a member of a few of them).
Please make sure you have read my WiiRd Hacking Tutorial and all previous examples before starting this example. You need to be familiar with the WiiRd code types along with general hacking topics such as Hex.
6/28/08 - Initial release
We are now actually going to hack a game with WiiRd to get a hands on example of all that boring stuff you just learned and to understand concepts which knowledge of hex and code types alone will not teach you. I highly recommend that you follow along with your copy of WiiRd.
Example 2 (Infinite
Hopefully you learned a lot from the previous example because now we are going to get into the really good stuff - pointers. OK, well pointers are more annoying than they are good but finding them is pretty easy.....most of the time and WiiRd makes it even easier. So in today's example we are going to hack Super Mario Galaxy once again. I am not going to baby step you through the whole process like I did in my last example so hopefully you know how to do things like dump MEM1, and poke a value in RAM. Now before we get started we need to learn some background information about pointers.
All About Pointers
As you may or may not know, almost all video games that run on consoles are programmed in a language called C++. C++ as a language falls under the category of Object Oriented languages. Now I could spend a long time talking about Object Oriented programming and how it relates to game design but I will try to just tell you what you need to know. Most of this is just background information but I will point out things you absolutely need to know.
In an Object Oriented language everything is what we call an object. Mario is an Object, the Goomba enemies are each their own objects, even the whole level is its own object. Objects can contain variables such as health, position, speed, etc. Since objects contain variables they take up memory. You can think of an object as a collection of related variables. Now an object does not exist until it is created and all objects are created by the game's code. This is a little tricky to understand if you have never programmed but stay with me.
Imagine an object is a house. In order to build a house the construction team needs a blueprint, right? In the object oriented world we call this blueprint a class. All objects are created from classes. A class lays out all the variables that objects created from it will need. The mario class for example will say that when a Mario object is created from it, it needs a health variable, a position variable, etc. A goomba class might say that a goomba object created from it, it needs a position variable, a "have I spotted Mario and am running towards him" variable, etc. What's important to remember is that an unlimited number of objects can be created from one class. Every goomba on every level was created from the goomba class yet every goomba is its own object.
What's important to realize is that not only does a class specify what variables all objects made from it will have, it also specifies the order those variables will be in memory. (REMEMBER THAT) So the Mario class might say that health should come right after position. Therefore if you found the position variable you would just need to move to the next variable to find health. Of course you have to remember that variables can be 8-bit, 16-bit or 32-bit in size so how far you have to move in memory to get to the next variable may vary between 2 and 4 bytes. What I also need you to remember is that the address of an object is the same as the address of it's first variable (REMEMBER THAT).
Now, a Nintendo Wii game disc has a big storage capacity, almost 4 gigabytes, and most games take full advantage of it. They fill this storage space with the game's executable code, graphics, textures, sounds, etc. Now the Wii only has 24 MB of memory and 64 MB of graphics memory which even combined is very small compared to 4 gigabytes. So when you start up a game, the game does not load the entire contents of the disc into memory, it can't. So instead the game only loads what it needs from the disc. So when you enter level 1, the game loads level 1, all of the enemies on level 1, and Mario. This is why you must wait a few seconds each time you move from level to level.
But it is a little more complex than that and we need to understand this process in order to master pointers. Like I said before, everything is an object, even the level itself. So when the game loads level 1 what it does is create a level 1 object from the level 1 class then it proceeds to load all the graphics data for level 1 from the disc. Next it creates objects for all the enemies on level 1 from their respective classes and loads their graphics data from the disc. Finally it creates a Mario object from the mario class and loads his graphics data. The game can do this in any order it wants, it doesn't matter. What's important to us is how this process affects the games memory. The game fills up it's memory in a linear fashion. What this means is that once the level object is created, the game shoves it into the first available space memory. Next it creates the enemy objects and loads them into memory right after the level data. Finally it creates a Mario object and shoves it into memory right after all the enemy objects. And it performs this stacking of stuff in memory until it has loaded everything it needs. And remember, that game can stack the stuff in whatever order it wants. It may want to load Mario first, then the enemies, then the level. This process is called Dynamic Memory Allocation or DMA and it is the reason why we have/need pointers.
Now DMA explains why you can search and find Mario's health at some address on one level but find it at another address on another level. Because different levels are bigger or smaller than others and have different amounts of enemy objects that need to be created and stored the memory stack, the address of your health variable will change. Check the diagram below. Note that the Mario data is the same size on both levels. Also note that Enemy Data refers to the collection of enemy objects for that level.
Ok, so we now hopefully realize that the Mario object is being shifted around in memory because of the amount of memory used by the level and enemies changes. Another thing to note is that when the game is done with a level (say you move onto the next one) it will do what's called deallocate the memory that level used before loading the next level. This simply means that it takes all the memory used by the level, the enemy, and the Mario objects and clears it to prepare it to be filled up with the next level's data.
So now that you know all about DMA, it's time to talk about pointers. We are not going to concern ourselves with the level data or enemy data, only with Mario's data. Since Mario' data does move around in memory the game needs a way to keep track of where the Mario object is in memory. So the game uses what is called a Pointer. A pointer is an address in memory who's value is another address in memory.
Now, not all memory is dynamically allocated in a game. There is plenty of static memory which stores things that need to be in the same spot each time the game loads and no matter what level you are on. This memory always stores two things: the executable, and what's called a pointer table. It is important that the pointer table be stored in static memory because we can't have our pointers moving around in memory (although there are very complicated pointers that do move around). Static memory will always start at 80000000 and will extend for a different length depending on the game. Usually pointers will be found between 806XXXXX and 809XXXXX. It is safe to assume that any address that is 81XXXXXX is deep in the dynamically allocated memory range and will require a pointer if it is to be used in a code.
Finding The Pointer
So as I stated at the top of this huge block of text, we are going to be hacking Super Mario Galaxy. Specifically we are going to hack an unlimited health code. Now of course health is a variable in the Mario object which will move around from level to level so we get to use all that pointer knowledge you just learned. Go ahead and start up Super Mario Galaxy along with WiiRd and jump into a profile that preferably is not new.
Despite the fact that the health meter is present in the observatory I don't know of any way to loose health here so we need to go somewhere where we can change the amount of health in our meter. I am opting for the Good Egg Galaxy. Pick a star and start the game.
I have to apologize because I did say I was going to show you how to do relative (unknown value) searching in this example but hacking health just is not a good example for that since the game is nice enough to print out exactly how much health we have on the screen. Besides, showing you how to do relative (unknown value) searching would surely double the length of this already long example.
So let's take our first RAM dump. Once again we are going to use a specific value search with a compare type of equal to. Now for the size, I am going to try 32-bits so make sure you select "32-bits" Data Size. Last but not least we need a value. Providing you have not done anything stupid you should still have 3 health left. So our value will be 3. Be sure to click the "Convert Dec to Hex" button to have WiiRd convert and pad the value. Got all that? Click start.
The search settings for our first search.
Hmmm. 00000003 seems to be a common value in the games memory (no surprise). So it looks like we need another memory dump to compare to. Go ahead and loose a slice of your health. Change the 3 to a 2 in your specific value text box and "Search" again.
Well, I am left with 11 results after that last search. True, it is under 20 and I could just poke them all but I think we can cut our results down with another search. Go ahead and loose another slice of health so you now have 1 health left. Be careful not to die. If you die, its all over and you will need to start your searching all over again! So now search again for values equal to 00000001.
I still am left with 7 results. Let's try gaining health now so our health goes back to 2. Since there seems to be a lack of coins on this starting planetoid, I am going to fly to the next one to grab a coin. Now that our health is back 2 lets search again for all values equal to 00000002.
Mario with 2 health.
Well, I still have 4 results left and I'm sick of searching so let's start poking. If you still have a lot of results I recommend you keep loosing/gaining health then searching to narrow down your results. Once you get it under 10, start poking.
My results. Your's will differ.
Well, you can see my results in the picture above. YOUR ADDRESSES WILL BE DIFFERENT! Don't worry just follow along. The idea now is to start poking each address that we think may contain our health variable. I can instantly eliminate 2 of those results right away, can you guess which ones? The first two actually. Why? Because they are in the 806XXXXX range, that's way to low to be in the DMA area. I want to at least see 80EXXXXX before I consider it a valid address. So, I'm going to start with the 8106DC94 address. Right click what you think is your first valid address and click "Poke". Now, the last search I did was when my Mario had 1 health so poke fills in the value box with 00000001. I am going to change it to 00000003. Click "Poke".
The poke box.
If your health is now 3, congratulations you found the health variable for this level. But what if the health box just shook at you and did not change? Well that means you found the address of how much health is displayed on the screen. Sorry, try again. If nothing happened then the address you just poked has nothing to do with health. When I poked my 8106DC94 address my health box shook at me and did not change so I am going to try the 810B5CB4 address.
That one seemed to work. Hooray! I found the health address....for this level. For me, my address was 810B5CB4. Make sure you write yours down somewhere. We will need it later. Now we need to start working out the pointer, but first here is a little more pointer theory.
More About Pointers
As we now know, a pointer is a static address in memory who's value represents another address in memory, usually the address of the object it is pointing to (first variable in the object remember?). Now pointers are always in the same spot in memory. Lets say we found out the address for health on level 1 was 81200000 and then we found out the address for health was 81300000 on level 2. What is the distance in memory between those two addresses? 0x100000 actually. So we know that the address for Mario's health moved 0x100000 between level 1 and level 2? Think about this: How far did the Mario object move between level 1 and level 2? Remember that a class also specifies the order the variables of all its objects will be in. So since health is still the same distance from the start of the Mario object on level 1 as it is on level 2, the whole Mario object also moved 0x100000 in memory. Now, let's tie it all together: By how much did the value of the pointer change between level 1 and level 2? Are you getting the hang of this? In case your not, the answer was +0x100000. Yes, I put the positive sign there for a reason. The Mario object could shift in the negative direction too.
So here is how we find a pointer. We find the address of health on level 1 and 2. Subtract the 'address from 2' - 'the address from 1' and then we look for addresses in memory who's values are different by (address 2 - address1) between levels 2 and 1. Ever noticed the "Different by" search box in WiiRd, this is the kind of stuff it is used for ;)
However, you probably also noticed that "Pointer Search" tab in WiiRd too. Lucky for us, WiiRd does all that work described in the last paragraph for you and all it needs from you is 1 memory dump from each of 2 levels along with the address where we found the health variable on each of those levels.
Back To Our Example
We have found the address where the health variable lives. Let's click over to that "Pointer Search" tab. As you can see there are two places to provide a dump of the memory along with two places to provide the address of the health variable. WiiRd is even nice enough to have a button that takes a dump of the memory for that level for you. So let;s use it. Go ahead and click the "Dump" button directly across from the "File 1" button. WiiRd will dump the game's memory and automatically load it into the pointer search. While we are here, go ahead and enter the address we found for our health in the box that says "Address 1". You don't need to prefix it with a 0x.
The pointer search tab.
So now we are about a third of the way done with this hack. We have now found the address for health on level 1 and we have taken a memory dump while on level 1. Since that's all done, go ahead and either die or fly back to the observatory. We are done here.
Now we need to jump into another level an repeat everything we just did over again. I am going to fly to the Honeyhive Galaxy. Once you are in your second level click your way back to the "Code Search" tab. Make sure that the specific value box now has 00000003 typed in since we now have 3 health again. Click the "Restart" button and confirm that you want to reset all the search values. Be sure to set the "Data Size" back to 32-bits before you click search as Restart seems to have reset it. Got all that? Click search.
Like before you will have a ton of addresses. I am going to leave it up to you to narrow your results down and figure out the address of your health variable on this level. Remember to make sure the address you find is for the real health variable and not the 'number displayed on the screen' variable.
Mario waits for you to find the health variable.
Well, that went a lot quicker for me this time. Only 3 searches and I have cut my results down to 2. After a bit of poking around, I came up 810B3250 for my address. Make sure you write it down. We will need it in a minute. Once again, I must remind you to check and make sure you found the real health variable. You will not be happy if you didn't.
OK. Let's click our way over to the "Pointer Search" tab. We found our address for health on level 2 so enter it in the box that says "Address 2". We also need to grab a memory dump from level 2 so click the "Dump" button directly across from "File 2". Go ahead and ignore all the other settings. Click "Search!".
Side Note: If your search button is disabled than you need to click the File 1/2 buttons next to where it says DUMP180.bin and DUMP280.bin and browse into the WiiRd folder and select those files. Your search button will then become enabled.
My pointer search results
The search takes a few seconds and when it is done you should be left with a few results. If you did not get any results than you really messed up and will want to start the example all over again. Now unfortunately, WiiRd will only allow us to plug in data from two levels. If you use the old fashion method (the one I talked about above) you can keep going level through level finding the address and taking a memory dump to narrow your possible pointer list down. But we can't do that with WiiRd so we will just have to guess and check.
OK, we need to leave this level before we can start testing our possible pointers since they all will work right now and that would not be a good test. While we could test in the observatory, I prefer to jump into another level. You also could go back into one of the two levels you originally used but I am going to a new level, the Space Junk Galaxy.
So now we get to start testing. Instead of trying to create a code using each pointer and seeing if it works, we are going to test all our possible pointers using a different method. If you look in the results list you can see that WiiRd gives us an address inside some [ ] and then it shows another number after the [ ]. That number is what we call the Offset. Remember that a pointer points to the first variable in an object (most of the time). And also remember that all the variables in an object appear in the exact same order every level. So the offset is the distance from the first variable of the object to the one we want to control, in this case health. That means that if you were to take the value the pointer gives you and add the offset to it you should be at the address in memory for Mario's health. And that's just what we are going to do to test these pointers.
Write down or remember the first result that WiiRd gave you. Remember to write down both the address and the offset. Now click on over to the "Memory Viewer" tab. The "Memory Viewer" tab lets you view the value at any address in memory even in real time if you want. Now take your first possible pointer address (the one we wrote down) and enter it into the box that currently says "80000000" then click enter. My first possible pointer was 806B7B40 with an offset of +380 so I will enter 806B7B40 in the box.
The Memory Viewer
The memory viewer will now jump to the address you entered and highlight it. The number highlighted is the address your pointer is pointing to. So now, we are going to add our offset that came with the possible pointer to the value in the memory viewer. Break out the windows calculator and switch it to hex mode. My 806B7B40 possible pointer is currently pointing to 810B4608 so I am going to add 810B4608+380 which equals 810B4988. Now for the moment of truth. Enter whatever windows calculator spit out back into the address box in the memory viewer and press enter. The memory viewer will jump to that address. If the value in the highlighted box is 00000003 (providing you have not lost any health) than you have just found a valid pointer. Actually, 806B7B40 is the pointer I use in my official code so it should have appeared somewhere in your possible pointer list. There very well may be other valid pointers but the one I found seems to work OK. Now, if 806B7B40 did not appear in your list at all and none of your pointers work than you messed up big time. You probably will want to start all over again with this example.
Creating The Code
So now we need to turn all this into a working code and to do that we need to talk about how WiiRd handles pointers. As I talked about in my original tutorial, WiiRd has a dedicated temporary storage space for pointers called the Pointer Offset or P.O. for short. The P.O. holds a 32-bit value which you can change in your codes. By default it is 80000000. And like I said before, the value in the P.O. gets added to the RAM Write codes's address. This is why you can get away with only specifying 6-digits for the address portion of a RAM Write code as long as the address your trying to write to is less than 81000000.
But there's more to the P.O. than that. Not only can you load a value directly into the P.O. but you can also tell the P.O. to go to an address in RAM, and set itself to the value at that address. By doing that, all of the RAM Write codes will write to whatever value you specify in their address + the address stored in the P.O. Making sense? So why don't we tell the P.O. to go to the address 806B7B40 and make itself whatever value is stored at that address. Then, call a RAM Write code to write to 00000380 which would really be [806B7B40]+380. Remember that [XXXXXXXX] means that value at address XXXXXXXX.
That's exactly what we are going to do! So let's introduce a new code type. Meet the 48000000 XXXXXXXX code where the XXXXXXXX is the address of our pointer (in this case 806B7B40). This 48000 code type (don't ask about the last 3 0s) takes care of setting the P.O. to whatever value is at XXXXXXXX just like I described above. Notice how this code type dedicates the whole value section to the address you want it to load from. That means that, unlike the RAM Write codes, you need to specify the 80 part of your address.
So let's create our code. Click over to the "GCT Codes" tab in WiiRd and make a new code. Call it whatever you want. Now, we are going to use that 48000 code type first because before we do anything else we need to get our P.O. all setup. So the first line of your new code will be 48000000 806B7B40. Go ahead an enter all that in. Now the next thing we need to do is use a RAM Write code to constantly write a value of 00000003 to our health address. And since the address of a RAM Write code gets added to the P.O. and since we know that the health variable is 00000380 away from wherever the pointer points (our offset), we are going to use 00000380 as our address for the RAM Write code. So our next line of code is going to be 14000380 00000003. I am using the 14 code type because we were performing 32-bit searches to find the health variable.
Our code so far.
If you enable the code and Apply It it should now work and you will not loose any health if you get hit. But, our code is definitely not up to release standards. You see, the P.O. is shared among all the currently executing codes. Remember how I said that by default the P.O. is 80000000? Well currently your code changes that value and does not set it back to 80000000 when it is done executing. THIS IS VERY RUDE! If the next code expects the P.O. to be 80000000 guess what, you just broke that code. Because, if the next code say is say 14F63CF0 00630000 (look familiar) that code will actually end up writing to [806B7B40]+F63CF0 because the P.O. is still [806B7B40] when your code ends and the next code begins since you did not reset it.
So how do you reset it? Kindly enough, WiiRd includes a nice code type to do this operation for you. It is the E0000000 XXXXYYYY code type also known as the Full Terminator. Not only does it reset the P.O. but it also resets the B.A. if you changed it. I am not going to go into detail about how you use this code type but always add E0000000 80008000 to the end of your code if you use a pointer or change the B.A. Your code should now look like this.
O.K. now you have a releasable code but there is one more problem with it. Remember that the value at 806B7B40 is being loaded into the P.O. and then the code is writing to 0x380 + the P.O. value. Well what if the value at 806B7B40 is 00000000? Then where is your code writing to? Yeah, 00000380 which would not really be good since you can't write there. The game will crash. But when would the value at 806B7B40 ever equal 00000000? Umm, lots of times. When you are changing levels, a lot of games like to zero out the pointers before they set them to their next address.
So how do we make sure not to try and write anywhere if our P.O. equals 00000000? Well, WiiRd includes another convenient code type just for that purpose. It's the DE code type. I am not going to go into detail on it but it should mostly always look like this when you use it: DE000000 80008180. This tells WiiRd to make sure that the P.O. value is between 80000000 and 81800000. As long as you stick this code type in after any change you make to the P.O. your codes will be protected against crashes caused by writing to a bad spot in the address space. Be sure to stick that DE code in right after the set the pointer value so your code looks like this.
The finished code.
Now the code is complete, and stable. Good job! Hopefully you gained a solid understanding of pointers and how to use them. Almost every code you will make involves the use of a pointer. Go ahead and try out your new code.
"I'm just pretending it hurts" - Mario
And with that, this example is complete.
I am thinking about doing some video tutorials next. I don't know what I would hack (I don't have a huge selection of games) but I'll find (rent) something. I'm also thinking about doing a multi-part series on assembly hacking.