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.
That's odd the second one should work
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.
No You can't use the 42 codetype to set the base address to 81000000 only 90000000 & 92000000
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.
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?
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.
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
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
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.
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?
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.
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)
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.
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?
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.
You dont have to convert it, you could just do:
li r0,0xFFFF
Thanks for all of the great information. If you do find that floating point register write template, I would definitely be interested.
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.
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.
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. ;)
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).