WiiRd forum

Wii & Gamecube Hacking => Wii Game hacking help => Topic started by: live2play on April 09, 2010, 04:11:46 AM

Title: 16-Bit Write Help
Post by: live2play on April 09, 2010, 04:11:46 AM
Using WiiRD, I found address 811B2976 to be the address for the amount of money in a game.  The original value at 811B2974 was, for instance, 000010C4.  When I used WiiRD to POKE FFFF to that address, I had, as expected, $65, 535 in the game.  Now I tried to create a code to automatically do this during game play.  I created the following two code variations and tried them one at a time.

42000000 81000000 -> Set BA=81000000
021B2976 0000FFFF -> 16-bit write of FFFF to 811B2976
E0000000 80008000 -> Set BA back to 80000000

AND

031B2976 0000FFFF -> 16-bit write of FFFF to 811B2976

The first code resulted in no change in the amount of money in the game.  The second code resulted in the game freezing before it even started.  What am I missing here?  I know that 811B2976 is, most likely, deep in the dynamic memory address range and probably referenced by a pointer, but for testing, I always went into the same level in the game to make sure that the money amount would always be at the same address.
Title: Re: 16-Bit Write Help
Post by: wiiztec on April 09, 2010, 04:19:11 AM
That's odd the second one should work
Title: Re: 16-Bit Write Help
Post by: live2play on April 09, 2010, 04:40:57 AM
The first one should have been ok too, right?  I ended up back tracking in the assembly and just changed the code so that my original money never decreases.  However, it bothers me that the 16-bit write is having issues.
Title: Re: 16-Bit Write Help
Post by: wiiztec on April 09, 2010, 05:09:46 AM
No You can't use the 42 codetype to set the base address to 81000000 only 90000000 & 92000000
Title: Re: 16-Bit Write Help
Post by: dcx2 on April 09, 2010, 10:05:37 PM
From the code-type document:

Quoteba : Base Address. Set at 0x80000000 each time the code handler is executed. Each time the ba is added to a code address, the code handler do : address = address + (ba & 0xFE000000).

What this means is that only the first seven bits of the BA are *EVER* used.  So when you do

42000000 81000000

What actually happens is 81000000 & FE000000 = 80000000, which is why your first code wouldn't work.  However, the PO is not masked, so all 32 bits of the PO are always used.  A good exercise might be to re-write your BA code using the PO instead.

---

Why did your second code crash?  I would double-check to make sure your money was still at 811B2976 before loading the second code.  Going to the same level does not always mean that dynamically-allocated objects will be in the same location in memory.  Reloading the game, or leaving the level and coming back might be enough to cause it to move somewhere else.

A much better option would be to hook some assembly that is reading the money value from memory.  Your hook would then load the immediate 0xFFFF into the money register and write it back to memory.  The ASM will always have the correct pointer loaded, without requiring any guessing games or any pointer searches.
Title: Re: 16-Bit Write Help
Post by: live2play on April 10, 2010, 09:07:22 PM
Thanks for all of the great answers.  So, it seems that the best solution is to find the ASM instruction that is reading the money floating point into a float register and then modify the data in the register, right?  If this is the case, what is the ASM instruction to change the value of a float register.  For instance, if the instruction is lfs f1,72(r3), how would I change the value in f1 to my desired value after it's been read in?
Title: Re: 16-Bit Write Help
Post by: dcx2 on April 10, 2010, 09:42:14 PM
Set a read breakpoint on your money address.  This should hit instantly, all the time, because the game is always reading the value of money from memory and writing it to the screen.  When you find your read instruction, use a C2 code to replace it with other instructions that load FFFF.

It's hard to explain exactly how to patch assembly code.  You need to mirror the lha with a sth using the same register/offset.  You also need to load the immediate FFFF into a temporary register, and temp registers don't always stand out.  Look for other registers after your lha that are being loaded into, because the load will overwrite the immediate without any negative side effects.

A lot of this depends on what the disassembly looks like.  Set a read breakpoint on the money address, switch to disassembly after it hits, and scroll up a few lines.  Copy and paste that and it'll be a lot easier to help you.
Title: Re: 16-Bit Write Help
Post by: live2play on April 10, 2010, 10:03:25 PM
The money value gets read into r3 in green below.  The blr after the read takes the program flow to the assembly starting with the blue text below.  So, ideally, it would be best to use ASM to write 0xffff to the memory address referenced by 116(r3), right?

8013B884:  4E800020   blr   
8013B888:  80630074   lwz   r3,116(r3)
8013B88C:  4E800020   blr   
8013B890:  9421FFE0   stwu   r1,-32(r1)
8013B894:  7C0802A6   mflr   r0
8013B898:  90010024   stw   r0,36(r1)
8013B89C:  93E1001C   stw   r31,28(r1)
8013B8A0:  7C7F1B78   mr   r31,r3
8013B8A4:  80C30074   lwz   r6,116(r3)
8013B8A8:  7C043000   cmpw   r4,r6
8013B8AC:  418200AC   beq-   0x8013b958
8013B8B0:  3CA0000F   lis   r5,15
8013B8B4:  90830074   stw   r4,116(r3)
8013B8B8:  3805423F   addi   r0,r5,16959
8013B8BC:  7C040000   cmpw   r4,r0


801C3930:  801B0114   lwz   r0,276(r27)
801C3934:  7C7E1B78   mr   r30,r3
801C3938:  7C001800   cmpw   r0,r3
801C393C:  41820070   beq-   0x801c39ac
801C3940:  38610090   addi   r3,r1,144
801C3944:  3B800001   li   r28,1
801C3948:  4BF26501   bl   0x800e9e48
801C394C:  7FC3F378   mr   r3,r30
801C3950:  38810090   addi   r4,r1,144
801C3954:  38A00000   li   r5,0
801C3958:  4809A1CD   bl   0x8025db24
801C395C:  38610090   addi   r3,r1,144
801C3960:  4BF9ADE9   bl   0x8015e748
801C3964:  7C641B78   mr   r4,r3
801C3968:  387F0918   addi   r3,r31,2328
801C396C:  4BFB2B89   bl   0x801764f4

Title: Re: 16-Bit Write Help
Post by: live2play on April 10, 2010, 10:13:13 PM
The code at the blue colored memory address below may be even better for changing the assembly as this is the one place in the game where the money value gets updated regardless of how the money was spent in the game.  So, it would be great to know how to patch the assembly at this point to write 0xffff to the memory address referenced by 116(r3).

8013B888:  80630074   lwz   r3,116(r3)
8013B88C:  4E800020   blr   
8013B890:  9421FFE0   stwu   r1,-32(r1)
8013B894:  7C0802A6   mflr   r0
8013B898:  90010024   stw   r0,36(r1)
8013B89C:  93E1001C   stw   r31,28(r1)
8013B8A0:  7C7F1B78   mr   r31,r3
8013B8A4:  80C30074   lwz   r6,116(r3)
8013B8A8:  7C043000   cmpw   r4,r6
8013B8AC:  418200AC   beq-   0x8013b958
8013B8B0:  3CA0000F   lis   r5,15
8013B8B4:  90830074   stw   r4,116(r3)
8013B8B8:  3805423F   addi   r0,r5,16959
8013B8BC:  7C040000   cmpw   r4,r0
8013B8C0:  7CA62050   sub   r5,r4,r6
8013B8C4:  40810008   ble-   0x8013b8cc

Title: Re: 16-Bit Write Help
Post by: dcx2 on April 10, 2010, 10:23:48 PM
Yes, you're on to something.  ^_^  But you should give me a few (like 5-10) instructions before the blue line; 801C3910 - 801C3930.  Those instructions will probably load r3 with the magic pointer that you're looking for.

That said, with what you've given me, we can still make something.  This is tricky, because the pointer gets over-written by the load.  However, we can usually do whatever we want with r0 before a blr, as I think it's considered volatile (i.e. it can change without screwing anything up).  So we could replace the green line with the following lines, using a C2 code.

li r0,-1
stw r0,116(r3)
lwz r3,116(r3)

-1 = ffff, btw.  two's complement notation.

However, it's not so safe to edit a short function like that, because you don't know who else is calling it, even if r0 is generally safe.  If you show some more lines before the (first) blue line, we might be able to make a safer code.
Title: Re: 16-Bit Write Help
Post by: live2play on April 10, 2010, 10:34:27 PM
Thanks!  The assembly you're giving me is to be used with my first post of assembly.  Do you have an assembly example for the second post that references 8013B8B4:  90830074   stw   r4,116(r3), right?  Also, when I use a C2 code, I'm assuming I would give it a memory address for inserting the assembly, right?  if so, what address would I give?  Also, I'm assuming I should use the ASM Helper Tool, right?
Title: Re: 16-Bit Write Help
Post by: dcx2 on April 10, 2010, 10:40:11 PM
I'm gunna make a second post to reply to your second post.

The reason I like read breakpoints better is because a read breakpoint is executed all the time, so it makes a good hook for constantly doing something.  A write breakpoint finds instructions that are only executed under certain circumstances.  What if you don't have enough money to buy stuff yet?  You won't be able to trigger the write.  etc.

However, since you did all that work...if you C2 the load instruction 8013B8A4:  80C30074   lwz   r6,116(r3) with the following assembly, you will get your desired effect.  

li r4,-1
lwz r6,116(r3)

In this case, the value in r4 was passed in by the caller, and the code is checking to see if your current money (loaded into r6) is equal to the value passed in (cmp with r4, beq), and if so it's probably skipping to the end of the function (the beq is to an address 0xAC bytes away).  You are replacing the caller's argument r4 with your own argument.

You might be able to use a simple RAM write to replace the beq with a li r4,-1.  This wouldn't even require the C2 code.  It basically makes the branch "never taken", while also giving you max money.

EDIT:

For a C2 code, you provide the address of the instruction you want to hook.  This will replace that instruction with a branch to somewhere in the code handler, where it lays out your C2 assembly code.  Yes, you would use Link's asmwiird or hawkeye's PyiiASMH.
Title: Re: 16-Bit Write Help
Post by: live2play on April 10, 2010, 10:43:27 PM
Given that I need to convert my hex to two's complement, do you have a good calculator for that?  Also, can you give me an ASM example for doing the following but using float values and registers.  I know that the stw would be stfs and the lwz would probably be an lfs, right?  How would I load a float into a float register much like you did the non-float register with the li r0,-1 command?

li r0,-1
stw r0,116(r3)
lwz r3,116(r3)
Title: Re: 16-Bit Write Help
Post by: dcx2 on April 10, 2010, 10:50:14 PM
Uh...the whole twos comp thing just becomes second nature after a while.  Flip the bits and add 1.  i.e. FFFE -> flipped bits is 0001 -> add 1 = 2.  So FFFE = -2.

You can force windows calculator to do it in scientific mode.  Change to Hex mode, change the word size as appropriate (16 bits is Word, 32 is Dword).  Then do "0 - FFFE" and it will give you 2, so that you know FFFE = -2.  EDIT: instead of "0 - FFFE", you can type "FFFE", and make sure you hit enter, and then you can press F9 (which would change the sign in decimal mode, but actually takes the two's comp in hex mode)

Float is much harder to deal with.  You can't load directly into float registers, you need to load them from memory...make room on the stack, write your float using lis and ori and a regular register, then lfs from the stack, and finally clean up the stack.  It's a delicate procedure.
Title: Re: 16-Bit Write Help
Post by: live2play on April 10, 2010, 10:51:53 PM
Thanks for all of your help.  Do you, by any chance, have a sample of ASM code that does what you mentioned for a floating point value?
Title: Re: 16-Bit Write Help
Post by: dcx2 on April 10, 2010, 10:56:21 PM
I think I saw brkirch write a sample template somewhere showing how to write to a float register.  But I can't remember where...if I find it again I'll link it here.

BTW, dunno if you saw my edit, but you can use F9 to take the two's comp in the Windows Calculator, just make sure you hit Enter after putting in the hex value.
Title: Re: 16-Bit Write Help
Post by: Romaap on April 11, 2010, 12:03:14 AM
You dont have to convert it, you could just do:
li r0,0xFFFF
Title: Re: 16-Bit Write Help
Post by: live2play on April 11, 2010, 01:14:46 AM
Thanks for all of the great information.  If you do find that floating point register write template, I would definitely be interested.
Title: Re: 16-Bit Write Help
Post by: dcx2 on April 11, 2010, 01:40:01 AM
This is the brkirch post (http://wiird.l0nk.org/forum/index.php/topic,2289.msg23710.html#msg23710) I was talking about.  Read everything before the "To Create F6 Codes" part.  You'll see the templates I was talking about.  That is how you turn RAM-write-codes into ASM.  You only need the stack if there aren't any dependency-free registers.  Take for instance the first code, which was a lwz rD,d(rD).  Here is the template.


stwu r1,-16(r1) # make room on the stack
stw r11,8(r1) # push r11 onto the stack, so we don't lose it
lis r11,0xHHHH # load the upper 16 bits of r11 with our immediate, HHHH = 0000
ori r11,r11,0xLLLL # load the lower 16 bits of r11 with our immediate, LLLL = FFFF
stw r11,d(rD) # Write our immediate to the destination address
lwz r11,8(r1) # pop r11 off the stack
addi r1,r1,16 # clear the room we made on the stack
lwz rD,d(rD) # do the original load that we're replacing


Now, this is made inelegant by the stack stuff that deals with r1, all because we just aren't sure if r11 is important or not.  If you can tell for sure that a register isn't important (that no further instruction has a dependency on it), then you don't have to do this stack business.

In our case, we were pretty sure r0 was safe, because of the blr.  So we could remove the stack crap that preserves r11 and just blast whatever was in r0 since no further instructions will depend on it.

Quote
stwu r1,-16(r1)      # make room on the stack
stw r11,8(r1)      # push r11 onto the stack, so we don't lose it
lis r0,0xHHHH      # load the upper 16 bits of r11 with our immediate, HHHH = 0000
ori r0,r0,0xLLLL      # load the lower 16 bits of r11 with our immediate, LLLL = FFFF
stw r0,d(rD)      # Write our immediate to the destination address
lwz r11,8(r1)      # pop r11 off the stack
addi r1,r1,16      # clear the room we made on the stack
lwz rD,d(rD)      # do the original load that we're replacing

This can be further reduced, because your value has 0000 for HHHH, and li zeroes the upper 16 bits for free.

Quote
li r0,0xHHHH      # load the upper 16 bits of r11 with our immediate, HHHH = 0000
li r0,0xLLLL      # load r0 with LLLL = FFFF
stw r0,d(rD)      # Write our immediate to the destination address
lwz rD,d(rD)      # do the original load that we're replacing

For the example here, we don't need the stack, because we found a dependency-free register.  

Follow the pattern near the middle of brkirch's guide for the lfs.  For HHHHLLLL, use the hex representation of your float.
Title: Re: 16-Bit Write Help
Post by: live2play on April 11, 2010, 02:44:55 AM
dcx2, thanks for the link and the explanation of the steps.  Probably one of the hardest things to do is determine if a register I want to use is being used by any other part of the code.  I guess always safer to do the stack stuff, right?  I'll look at the floating part of the turorial and hopefully I can get something to work.  My floating question is directly related to my post at http://wiird.l0nk.org/forum/index.php/topic,5425.0.html.  Do you have any ideas on that?  Seems like my only option is to hook the ASM that reads the "moving" MEM2 address so that I don't have to worry about trying to determine the address.  Do you know if there is a way to check the value of an address and only perform a write to the address if the value is present?  I was thinking that I could do that for the MEM2 value that is always in a different address by just making a code that checks all of the MEM2 addresses where the value may exist and only write the new value if the target value is found.  It seems like a lot of overhead, but seems that it would work.
Title: Re: 16-Bit Write Help
Post by: live2play on April 11, 2010, 02:49:32 AM
I'm not able to access the link you provided (http://wiird.l0nk.org/forum/index.php/topic,2289.msg23710.html#msg23710) as it says that it is either incorrect or off limits to me.  Maybe noobs aren't welcome.  ;)
Title: Re: 16-Bit Write Help
Post by: dcx2 on April 11, 2010, 01:05:07 PM
As far as the link, I don't know why you can't see it.  It's posted in the Gecko 1.8 forum (http://wiird.l0nk.org/forum/index.php/board,37.0.html).  There might be a restriction on the ability to post, but it shouldn't restrict you from viewing...

In general, I prefer ASM hooks to pointer codes (actually, I probably prefer ASM everything, heh).  I don't think I've ever even used the pointer search function, it's just so much easier to breakpoint on the value and use the ASM that's already loaded with the right pointer.  The only caution is that some ASM is run on more than one thing (i.e. things that take away from your health might also take away from the enemies' health or environmental object "health", like in Resident Evil 4 when my damage divider made it harder to open boxes).

Scanning for dependencies isn't actually hard, it's just hard to explain.  It's like looking at the matrix.  I'll use one of your disassemblies as an example.

Quote
8013B88C:  4E800020   blr   
8013B890:  9421FFE0   stwu   r1,-32(r1)
8013B894:  7C0802A6   mflr   r0
8013B898:  90010024   stw   r0,36(r1)
8013B89C:  93E1001C   stw   r31,28(r1)
8013B8A0:  7C7F1B78   mr   r31,r3
8013B8A4:  80C30074   lwz   r6,116(r3)
8013B8A8:  7C043000   cmpw   r4,r6
8013B8AC:  418200AC   beq-   0x8013b958
8013B8B0:  3CA0000F   lis   r5,15
8013B8B4:  90830074   stw   r4,116(r3)
8013B8B8:  3805423F   addi   r0,r5,16959
8013B8BC:  7C040000   cmpw   r4,r0
8013B8C0:  7CA62050   sub   r5,r4,r6


Start with r5.  Notice that r5 is not used by any instruction until 8013B8B0, at which point it is used as a destination operand.  Since nothing used r5 up until the load, it is dependency-free.  You can do anything to r5 before 8013B8B0 and you're safe because the lis will always replace your "anything".

Once we've reached 8013B8B0, we can't touch r5 because the add at 8013B8B8 uses r5 as a source operand.  If we were to put something else there, the add would get messed up, because it depends on r5.

However, once we've reached 8013B8B8, r5 is free again.  This is because the next instruction that uses r5 is over-writing it (the sub at 8013B8C0) as a destination operand, so the value in r5 is dependency-free between 8013B8B8 and the sub.

In general, a register is dependency-free in the region between the last instruction that used the register as a source operand (8013B8B8) and the first instruction that uses it as a destination operand (8013B8C0).  If you don't see any instructions that use a register as a source operand, then the register is dependency free up until it is used as a destination operand for the first time (8013B8B0).