16-Bit Write Help

Started by live2play, April 09, 2010, 04:11:46 AM

Previous topic - Next topic

live2play

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.

wiiztec

That's odd the second one should work
If there's any code at all that you want to be button activated, or even able to toggle on & off, and I have the game, just PM me and I'll make it happen

live2play

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.

wiiztec

No You can't use the 42 codetype to set the base address to 81000000 only 90000000 & 92000000
If there's any code at all that you want to be button activated, or even able to toggle on & off, and I have the game, just PM me and I'll make it happen

dcx2

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.

live2play

#5
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?

dcx2

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.

live2play

#7
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


live2play

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


dcx2

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.

live2play

#10
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?

dcx2

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.

live2play

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)

dcx2

#13
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.

live2play

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?