Wii Hacking Example
2
Infinite Health in Mario Galaxy
Last
Updated: 6/28/2008
Quick Note
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).
Notice
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.
Changelog
6/28/08 - Initial release
Before
You Begin
----------------
We
Need To Prepare A Few Things
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
Health)
----------------
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.
My results
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.
Almost Done!
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.
Coming
Up Next...
----------------
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.