您的位置:首页 > 其它

cracker's DS Hooking Tutorial

2013-03-28 11:00 477 查看

cracker's DS Hooking Tutorial

This document is best viewed in Firefox

Preface

I am creating this tutorial for those who are wanting to learn how to hack into DS games but have no idea where to begin. I released tutorials a while ago but they relied on some very poor hacks that - rather than hooked code to jump to a function properly - destroyed opcodes (which, obviously, could many times cause bad effects). I have learned a vast amount of information since then so I want to share my improved knowledge with everyone interested. -- cracker

What's needed

text editor (notepad is fine) a calculator capable of hex mode ARM ASM kit Hex editor ARM7Fixa ARM7extractor DSATM (not required but recommended for the DEADBEEF function)

Terminology

asm Short for assembly language. It is the lowest level language. Thus it is very fast to execute but hard to do even simple functions in comparison to high level languages (C/++, BASIC, VB, Pascal, etc). This tutorial relies soley upon it so you need to know it already or be a quick learner. arbitrary code Custom code that is injected into a program/game. Or simply a 'hack'. ARM7 The lesser of the two DS CPUs. It's usual duties include reading the input (buttons and touchscreen), sound processing, and save functions. It is the better of the two CPUs to hook into since there is a much smaller chance of slowdown or graphics problems since the ARM7's load is usually pretty light in comparison to the ARM9's. ARM9 The greater of the two DS CPUs. It's usual duties include graphics computation/rendering, math computation, controlling the video RAM, etc (everything the ARM7 doesn't do of course). CPU modes ARM mode 32-bit instruction mode. Instructions are always stored at addresses divisible by 4. This is the method I like to use for trainers since it has a bigger set of opcodes and aligning addresses isn't a concern. It takes more bytes but it's worth it in my opinion. Thumb mode 16-bit instruction mode. Instructions are always stored at addresses divisible by 2. It makes smaller code than ARM instructions but can only handle a subset of the functions that the ARM mode can handle and aligning can cause problems.

To change to between modes you need to do a bx rx where rx holds the address of the function. If the function is made with Thumb code then you need to OR the address in rx with 0x1, otherwise the CPU will be in ARM mode.

dynamic can be moved in memory flags bits that are set in the CPU (CPSR - current program status register) when a comparison or test is run. They determine the flow of conditional branches or other instructions.
Carry Negative oVerflow Zero

Note: There are more flags but they aren't needed for what you will be doing. hooking Redirecting the flow of a program so that your code gets executed as if it were part of the program. More often than not this will be during an IRQ call. IRQ Interrupt Request. little endian 16- and 32-bit values are stored backwords in memory. This is the format that ARM CPUs use. ex: 0xAABBCCDD would be stored as DD CC BB AA. opcode CPU instruction registers CPU registers (r0 ~ r15) - each can hold a 32-bit integer. Some registers are reserved by the CPU for special uses
r13 SP (stack pointer) this is the address of the stack r14 LR (link return) - when using the bl opcode the address of the next opcode after the subroutine branch is placed in r14 before the branch occurs r15 PC (program counter) - holds the address of the current opcode to be executed

stack This is an area in RAM that is used to temporarily store data from registers when it will be destroyed. It is usually written to before a call to a subfunction and restored from after the subfunction returns (or at the beginning/end of a subroutine) but it can be used at any time (ex: when registers are running low). static always stored at the same place in memory

Notable opcodes

Note: rx,ry,rz refer to any of the registers - r0~r15

Note: yyy is any number 0~255 - It can also handle *some* values over 255/below 0 if they only have 2 or less non-zero digits that are in a row (usually). Ex: 0x380, 0xff00, 0xf000 are fine but 0xf01, 0xfff are not

add [rz],rx,ry adds ry to rx, if rz is omitted then the result is stored in rx add [rz],rx,#yyy adds yyy to rx, if rz is omitted then the result is stored in rx b label|relative address branch to the address of the label b rx branch to the address stored in the register cmp rx,ry subtracts ry from rx and sets flags depending on the result - the result isn't stored cmp rx,#yyy subtracts yyy from rx and sets flags depending on the result - the result isn't stored ldr rx,label loads the value into rx from the address of the label ldr rx,[ry[,#+/-yyy]] loads the value stored in the address that ry (plus/minus the option offset) points to into rx, the optional offset can be positive or negative mov rx,ry copies the value of ry to rx mov rx,#yyy copies yyy to rx str rx,label writes the value in rx at the address of the label str rx,[ry[,#+/-yyy]] writes the value in rx at the address that ry (plus/minus the option offset) points to, the optional offset can be positive or negative tst rx,ry ANDs ry with rx, sets the Z flag if they are equal tst rx,#yyy ANDs yyy with rx, sets the Z flag if they are equal teq rx,ry XORs (EORs) ry with rx, sets the Z flag if the result is 0, sets the N flag if it is negative teq rx,#yyy XORs (EORs) yyy with rx, sets the Z flag if the result is 0, sets the N flag if it is negative

Conditionals can be added to almost any opcode

cc - the result of an ADD or OR fits in a 32-bit number cs - the result of an ADD or OR doesn't fit in a 32-bit number lt - the first parameter is less than the second le - the first parameter is less than or equal to the second gt - the first parameter is greater than the second ge - the first parameter is greater than or equal to the second eq - the result of a CMP, SUB, MUL, OR, etc. where the result is 0 or FALSE ne - the result of a CMP, SUB, MUL, OR, etc. where the result is non-0 or TRUE

Note: There are more but these are most commonly occurring.

ldr and str are 32-bit commands but can optionally use 8- or 16-bit b - byte (8-bit) h - halfword (16-bit) ex: ldrb r0,[r1,#+0x20] ex: streqh r3,[r2,#-0x10]
*Note: It can also handle *some* values over 255 if they only have 2 or less non-zero digits that are in a row. ex: 0x380, 0xff00, 0xf000 are fine but 0xf01, 0xfff are not For a complete list of opcodes see this

Hooking by replacing opcodes

Make sure that whichever method you use that you don't destroy registers that contain useful data!

Places to hook are where any of the following won't get overwritten: add rx,r15,(ry|value) ldr rx,=xxxxxxxx b[yy] rx|address

Static Hooking

Method 1

This is a simple method of redirecting the flow of a program with only replacing one opcode and destroying no registers.

@0x2380000 b 0x23B0000

@0x23B0000 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your load function ... ;@ End of your load function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack mov r12,#0x4000000 ;@ Patch in overwritten opcode b 0x2380004 ;@ Return jump

Method 2

Copy the two opcode at 0x2380000-4 for later

Let's say that your code is located at 0x23B0000

@0x2380000 - Replace the two original opcodes ldr r15,[r15,#-0x4] .long 0x23B0000

Of course this means you need to copy the two opcodes that get overwritten by your jump hack

@0x23B0000 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your load function ... ;@ End of your load function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack mov r12,#0x4000000 ;@ Patch in overwritten opcodes str r12,[r12,#0x208] ldr r15,[r15,#-0x4] ;@ Return jump .long 0x2380008

Dynamic hooking

Method 1

This is a good method to use in cases where the code may/will be moved around in memory. The downside is that it requires more space.

Note: Although it is highly unlikely, be sure that r14 isn't needed because it will be destroyed obviously.

@0x2387280 add r14,r15,#0x4 ;@ r14=0x238000C ldr r15,[r15,#-0x4] .long 0x23B0000

@0x23B0000 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your load function ... ;@ End of your load function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack mov r1,#0x4000000 ;@ Patch in overwritten opcodes ldr r1,[r1,#0x130] and r1,r1,#0x80 b r14 ;@ Return jump

Method 2

This is also a good method to use in cases where the code may/will be moved around in memory.

Note: Be sure that r0,r14 aren't needed because it they will be destroyed obviously

@0x2387280 ldr r0,[r15] ;@ r0=0x23B0000 ldr bl r0 ;@ r14=next execution address (0x2387288), branch to address stored in r0 .long 0x23B0000

@0x23B0000 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your load function ... ;@ End of your load function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack mov r1,#0x4000000 ;@ Patch in overwritten opcodes ldr r1,[r1,#0x130] and r1,r1,#0x80 add r14,#0x4 ;@ needs to be done or a branch back to 0x2380008 will occur (which is the address of your code) b r14 ;@ Return jump

Executing arbitrary code on boot

Header Hacking

This is a very simple hack to perform. All that needs to be done is to change the addresses to those where your arbitrary code resides. Then just load the original execution address into the PC (r15) to execute the game as normal.

*Note that you may need to fix the CRC16 @ 0x15E (calculated from 0x0-0x15D with start value of 0xFFFF) for some cards to run it

ARM9 Execution Address Located at 0x24 Default - 0x2000800

ARM7 Execution Address Located at 0x34 Default - 0x2380000

Note: Not all flash carts will use the execution address from the header so the alternative method to running your code on boot is more proper. They may blindly start code execution -- ARM9 code at 0x2000800 and ARM7 code at 0x2380000 which will result in your code never being run.

ex: Place your ASM code at the end of the ARM7 binary

Change the ARM7 execution address to that address at 0x24 (stored backwards)

At the end of your code jump to the real start:

ldr r15,[r15,-0x4] .long 0x2380000

ARM7/9 Bin Hacks

Here is the standard startup code for the ARM7:

mov r12,#0x4000000 str r12,[r12,#0x208] ldr r1,=0x2380xxx

Notice that only the first two opcodes can be overwritten because the third is a bad code to patch over. Actually you could work around it by loading 0x23801E8 into r1 before returning but it would just waste space since we know the code is always at the same spot. So just use one of the static hooking methods

IRQ hooking

ARM7

0x380FFFC acts as a register. It is reserved for the the address of IRQs.

To find the IRQ handlers' address search for 0x380FFFC (backwards in a hex editor of course) and the next 4 bytes are the address (also backwards) will be the IRQ address. Usually the first IRQ address you find will be the one that is always used in-game. Rarely the second address you find will be the one you need.

To do this hack it is just a simple matter of redirection. Replace the address with that of your function's and then make a jump to the real IRQ handler at the end.

Ex: @2380108 0x380FFFC 0x37FB488 ;@ replace with 0x23B0000

The address where the IRQ handler code is copied from is stored at 36 bytes before the occurance of 0x380FFFC (0x23800E4 in this case).

@23B0000 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your function ... ;@ End of your function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack ldr r15,[r15,#-0x4] ;@ jump to IRQ handler .long 0x37FB488

ARM9

To find the IRQ handlers' address search for 0xFFFF0000 (backwards of course) and then go 8 bytes. If the value there is in the 0x1FF0000-0x1FFFFFF (also backwards) range then that will be the IRQ address. Usually the first IRQ address you find will be the one that is always used in-game. Rarely the second address you find will be the one you need.

To do this hack it is just a simple matter of redirection. Replace the address with that of your function's and then make a jump to the real IRQ handler at the end.

Ex: @2000848 0x1FF8000 ;@ replace with 0x23B0000 0x2000A64 ;@ address where the IRQ handler code is copied from 0xFFFF0000

@23B0000 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your function ... ;@ End of your function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack ldr r15,[r15,#-0x4] ;@ jump to IRQ handler .long 0x1FF8000

Other Hooking

Occassionally there will be times where the memory you want to alter is buried under pointers. For these times you may want to hook directly in the code that has the pointers already set to access the address you need. You can use one of the execution at boot methods or dynamic methods to hook into depending upon if the code moves around or not and/or how many opcodes are available to be safely overwritten.

Ex:

Say we want to alter the coordinates that the playable character is located at. Using a debugger we find the code that changes the X coordinate when you press left:

@20087FC ldrh r0,[r3,+r4] cmp r2,#0x1 ;@move left? subs r0,#0x5 movlt r0,#0x0 strh r0,[r3,+r4] mov r5,#0x0

Using a static hook

@20087FC b 0x237FC00 cmp r2,#0x1 ;@move left? subeqs r0,#0x5 movlt r0,#0x0 strh r0,[r3,+r4] mov r5,#0x0

@237FC00 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your function ... mov r0,#0x0 strh r0,[r3,+r4] ... ;@ End of your function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack ldrh r0,[r3,+r4] ;@ Patch in overwritten opcode b 0x2008800 ;@ jump back

Using a dynamic hook

@20087FC add r5,r15,#0x4 ;@ r5 = 2008008 (return address) ldr r15,[r15,#-0x4] .long 0x237FC00 movlt r0,#0x0 strh r0,[r3,+r4] mov r5,#0x0 ;@ note that r5 is safe to destroy before this opcode

@237FC00 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your function ... mov r0,#0x0 strh r0,[r3,+r4] ... ;@ End of your function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack ldrh r0,[r3,+r4] ;@ Patch in overwritten opcodes cmp r2,#0x1 subeqs r0,#0x5 b r5 ;@ jump back

Thumb mode hooking

In some games you may find that you need to hook into the Thumb code. It is very similar to hooking into an ARM function but with some additional rules. To set up the jump 4 Thumb commands will be destroyed (2 for jumping and 2 for the address to jump to). The address must be aligned to an address divisible by 4. Additionally, you must use a bx command to jump to your function if it is in ARM code since loading a value into r15 directly doesn't switch between CPU modes.

Ex: @37FFB20 ldr r0,[r1] cmp r0,#0x0 blt 0x37FFB3C mov r2,#0x5 sub r0,r2 str r0,[r1] add r1,#0xFC ldr r0,[r1] mov r4,#0x8

Notice that there is a branch statement at 0x37FFB24 that prevents us from placing the code at 0x37FFB20. So then you see 4 opcodes starting at 0x37FFB22 that look like they will work. But then the jump address would be mis-aligned and would be placed at 0x37FFB26. So we move it down one opcode and it looks like 0x37FFB28 will work fine.

The resulting jump patch is:

@37FFB20 ldr r0,[r1] cmp r0,#0x0 blt 0x37FFB3C mov r2,#0x5 ldr r4,[r15,#-0x2] bx r4 .long 0x23FE000 mov r4,#0x8

Notice that r4 is being loaded with a new value right after our hook so we know it is safe to use it for our jump.

@23FE000 stmdb r13!,{r0-r12,r14} ;@ Save registers to stack ;@ Start of your function ... ;@ End of your function ldmia r13!,{r0-r12,r14} ;@ Restore registers from stack sub r0,r2 ;@ Patch in overwritten opcodes str r0,[r1] add r1,#0xFC ldr r0,[r1] ldr r4,[r15] bx r4 ;@ jump to IRQ handler .long 0x37FFB31 ;@ return address ORed with 1 to switch back to Thumb mode

Patching the arm7.bin

Now that you have an understanding of how the hooking methods work we can move on to the final step.

Before you start make sure you have everything needed

Getting set up

Extract armasmkit.rar, arm7fixa.rar, and arm7extractor.rar to the same place

Extract the arm7.bin from the .nds Do this by dragging and dropping the .nds onto arm7extractor.exe

Open the .nds in the hex editor Go to offset 0x34 and make sure it is 00 00 38 02 If it isn't then there may be something wrong with your .nds (I have never seen a different ARM7 execution address used) Reverse the bytes and write them down (should always be 0x2380000) Close the hex editor

Open arm7.bin in the hex editor Reverse the first 4 bytes and write them down Do the same with the second set of 4 bytes These values will be needed to patch up what you will destroy with your code

Search for FCFF8003 Go 4 bytes forward This is the IRQ address you want to hook into Write down the address you are at Reverse the bytes and write down the value there (it will be 0x37Fxxxx) Search for the next occurance and write down that IRQ address as well

Go to the end of the arm7.bin Add 1 to the end address and write this down

It should go without saying that you should have the address you want to write to, etc. already found by using an emulator or from a cheat code. Without this the 'hack' would be useless. Cheat finding is beyond the scope of this article but many tutorials can be found by searching online.

Determining what type of hack is needed

There are 2 types of hacks: An IRQ hack by itself A combination of an execution jump hack and an IRQ hack (recommended)

The reason why the combo with a jump hack is recommended is that you can copy all of your code to a safe area in RAM before the game can overwrite it and cause a crash. With the IRQ hack by itself you are only able to place your code between the end of the original ARM7 code and 0x23BFDFF since 0x23BFE00-0x23FFFFF can't be written to at boot.

Standalone IRQ hack

For this example we will be working on Break Em All (U)

When using a standalone IRQ hack you must pad the arm7.bin so that your code (once appended to the arm7.bin) is at the right address. Using any address <0x23B0000 isn't advised since a crash is more likely to occur. It is usually best to store your code as close to the end of memory as possible - 0x23BFA00 is a good place to try. If you need help figuring out where to store your function safely then you can use DSATM to DEADBEEF pad the game so you can run the game in an emulator like iDeaS to find an area in RAM that is never touched.

To resize the the arm7.bin to the right size in the hex editor subtract the address you want to use from the ARM7 execution address 0x23BFA00-0x2380000=0x3FA00

Now subtract the size of the arm7.bin from this 0x3FA00-0x277E0=0x18220 (98848 in decimal)

Now go to the end of the arm7.bin in your hex editor and append that many bytes (use 00 for a value if your hex editor gives you the option) In frhed the option is under Edit - Append and a decimal value must be used The end of the arm7.bin should now be 0x3F9FF (if not then something went wrong and you should reload the arm7.bin)

Save a copy of the padded arm7.bin in case you need it later

Now go to the address where you found the first IRQ address 0xF4

Overwrite this address with the address of your code (stored backwards) 08B77F03 > 00FA3B02

Save your new arm7.bin

The only thing left now is our code

We are going to be doing a simple cheat for BEA to give unlimited balls

To do this we must write 0x63 to 0x2145EDE

In code: mov r0,#0x63 ldr r1,balladdress strb r0,[r1] balladdress: .long 0x2145EDE

But we don't want to leave it like that or else it would try to execute the ball value address as a command so we must jump over it: mov r0,#0x63 ldr r1,balladdress strb r0,[r1] b endoffunc balladdress: .long 0x2145EDE endoffunc:

Now we need to add the code to save and restore the contents of the registers and jump to the real IRQ address .long 0xE92D5FFF ;@ push r0-r12,r14 ;@your code start mov r0,#0x63 ldr r1,balladdress strb r0,[r1] b endoffunc balladdress: .long 0x2145EDE endoffunc: ;@your code end .long 0xE8BD5FFF ;@ pop r0-r12,r14 .long 0xE51FF004 ;@ ldr r15,=realirq .long 0x37FB708 ;@ change to the real IRQ address

Save this code as bea.asm in the same directory as everything you extracted earlier

Drag and drop bea.asm onto assemble.bat

As long as there are no errors with the assembly code bea.bin will be produced. This is the pure assembled binary that has been stripped of any other object code.

Now we append this binary onto the arm7.bin Use the hex editor to append the bea.bin to the end of arm7.bin and save it as patched-arm7.bin In frhed it is under File - Insert File Note: Make sure you have the cursor at the end of the arm7.bin when you append! Alternatively, you could create a text file with the following text and save it as append.bat. copy /B arm7.bin+bea.bin patched-arm7.bin Then run append.bat and patched-arm7.bin should be produced

You should now have patched-arm7.bin in the directory and it will be 260,644 bytes in length

Rename the arm7.bin to old.arm7.bin

Rename patched-arm7.bin to arm7.bin

Drag and drop the original .nds onto arm7fixa.exe and the old arm7.bin will be replaced with our patched version

The final result will be a newly created .nds that is 4,950,376 bytes in length Don't worry about the 'strange' size. The original has a lot of padding added to it and it isn't needed.

And now the moment of truth... Let's run the game in an emulator and see how we did...

It didn't crash so that's a good sign... Getting into the game... OK there are 99 balls left! Lets lose one to make sure it stays at 99... It did! Success!

If the game runs fine but doesn't seem to be executing your code then the problem is probably that the IRQ address you patched over isn't the one that is being used in-game. So reload the copy of the padded arm7.bin (you did make a copy didn't you) and use the second IRQ address you found.

Combination of execution hack and IRQ hack

This method is a little bit trickier to use but it is better for a few reasons: Smaller arm7.bin size (neglible) No need to resize the arm7.bin with padding and append your code onto it each time you want to try a different RAM address Ability to dynamically search for an IRQ in the ARM7 code and hook it at runtime

Once again Break Em All (U) will be the game we are going to use

The first thing we will do is write down the first opcode and then overwrite them with a jump to our code.

We could take the easy way out and assemble a single instruction that would give us the hex code for the jump but that's no fun. To get the hex value all we need to do is take 0xEA000000 ('EA' is hex value for the unconditional branch opcode) and OR it with a value we get from a calculation: (address our code is at-0x2380000-0x8)/4= (0x23A77E0-0x2380000-0x8)/4= 0x9DF6 AND this value with 0xFFFFFF to make sure only the least significant 3 bytes get ORed with our opcode This is only needed for a branch backwards since all branches forward (within a reasonable distance) will be a positive value. It won't hurt either way and I just mention it for completeness. 0xEA000000 OR 0x9DF6 = 0xEA009DF6

Now that we have our branch opcode figured out we overwrite the opcode at 0x2380000 (0x0 of the arm7.bin) 01C3A0E3 > F69D00EA

Now go to the address where you found the first IRQ address 0xF4

Overwrite this address with the address of your code (stored backwards) 08B77F03 > 00FA3B02

Save your new arm7.bin

We can use the same code we used with the standalone IRQ hack and just add some code to copy our function.

ldr r3,copylength ;@ r3 = counter of 32-bit ints to copy ldr r2,destaddress ;@ r2 = destination address add r1,r15,#(codestart-copyloop-4) ;@ r1 = source address (points to codestart) copyloop: ldr r0,[r1],#+0x4 ;@ read a 32-bit int from the source and increase the source pointer str r0,[r2],#+0x4 ;@ write the int to the destination and increase the destination pointer subs r3,r3,#0x1 ;@ decrease the counter bne copyloop ;@ loop until the counter is 0 .long 0xE3A0C301 ;@ Patch in overwritten opcode ldr r15,returnaddress ;@ jump to the original ARM7 bootup code destaddress: .long 0x23BFA00 returnaddress: .long 0x2380004 copylength: .long (codeend-codestart)/4 ;@the assembler processes this calculation and replaces it with the result prior to assembling codestart: .long 0xE92D5FFF ;@ push r0-r12,r14 ;@your code start mov r0,#0x63 ldr r1,balladdress strb r0,[r1] b endoffunc balladdress: .long 0x2145EDE endoffunc: ;@your code end .long 0xE8BD5FFF ;@ pop r0-r12,r14 .long 0xE51FF004 ;@ ldr r15,realirq realirq: .long 0x37FB708 ;@ change to the real IRQ address codeend:

Put this into a text file and name it bea.asm

Drag and drop bea.asm onto assemble.bat Assuming everything went well then there should now be bea.bin If there isn't then there was an error in the source code and you failed at copy/paste

Now we append this binary onto the arm7.bin Use the hex editor to append the bea.bin to the end of arm7.bin and save it as patched-arm7.bin In frhed it is under File - Insert File Note: Make sure you have the cursor at the end of the arm7.bin when you append! Alternatively, you could create a text file with the following text and save it as append.bat. copy /B arm7.bin+bea.bin patched-arm7.bin Then run append.bat and patched-arm7.bin should be produced

You should now have patched-arm7.bin in the directory and it will be 161,844 bytes in length

Rename the arm7.bin to old.arm7.bin

Rename patched-arm7.bin to arm7.bin

Drag and drop the original .nds onto arm7fixa.exe and the old arm7.bin will be replaced with our patched version

The final result will be a newly created .nds that is 4,851,576 bytes in length

Once again we at are the moment of suspense and must run the game in an emulator to see how we did

So far everything seems normal...At the title screen...Entering the game...99 balls...We lose one and...99 balls! Success!

Now that we know how to dynamically copy the cheat function we should learn how to dynamically create the hook

mov r3,#0x0 ;@ (only needed if the first IRQ handler doesn't work) ldr r2,irqreg ldr r1,searchaddress searchloop: ldr r0,[r1],#0x4 ;@ read 32-bit value from the address r1 points to and increments r1 to the next 32-bit value cmp r0,r2 ;@ did the address hold the IRQ 'register' value? bne searchloop ;@ if not keep looking add r3,#0x1 ;@ increase the number of IRQ 'register' instances found (only needed if the first IRQ handler doesn't work) cmp r3,#0x1 ;@ is it the right IRQ handler? (only needed if the first IRQ handler doesn't work) bne searchloop ;@ if it isn't the right IRQ handler then keep looking ldr r0,[r1] ;@ load the real IRQ handler's address into r0 str r0,realirq ;@ and store it for our handler to use ldr r2,destaddress ;@ load the address of our function into r2 str r2,[r1] ;@ and overwrite the original handler's address b donehooking searchaddress: .long 0x2380000 irqreg: .long 0x380FFFC donehooking: ldr r3,copylength ;@ r3 = counter of 32-bit ints to copy ldr r2,destaddress ;@ r2 = destination address add r1,r15,#(codestart-copyloop-4) ;@ r1 = source address (points to codestart) copyloop: ldr r0,[r1],#+0x4 ;@ read a 32-bit int from the source and increase the source pointer str r0,[r2],#+0x4 ;@ write the int to the destination and increase the destination pointer subs r3,r3,#0x1 ;@ decrease the counter bne copyloop ;@ loop until the counter is 0 .long 0xE3A0C301 ;@ Patch in overwritten opcode ldr r15,returnaddress ;@ jump to the original ARM7 bootup code destaddress: .long 0x23BFA00 returnaddress: .long 0x2380004 copylength: .long (codeend-codestart)/4 ;@the assembler processes this calculation and replaces it with the result prior to assembling codestart: .long 0xE92D5FFF ;@ push r0-r12,r14 ;@your code start mov r0,#0x63 ldr r1,balladdress strb r0,[r1] b endoffunc balladdress: .long 0x2145EDE endoffunc: ;@your code end .long 0xE8BD5FFF ;@ pop r0-r12,r14 .long 0xE51FF004 ;@ ldr r15,realirq realirq: .long 0x0 codeend:

Assembling the code results in a binary that is 148 bytes in length

Append the binary to the end of the original arm7.bin The resulting binary will be 161,908 bytes in length

Now the only other thing we need to do with the patched binary is to put the branch command at the start 01C3A0E3 > F69D00EA

Rename the patched binary to arm7.bin and swap it into the game using ARM7Fixa The patched game will be 4,851,640 bytes in length

And now for the finale...We start up the patched game and wait for the splash screens...Past the title screen...Into the game...And it works! Success!

Closing

I hope that you enjoyed this tutorial. Feedback and comments are welcome!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hook ARM