diff --git a/INJECTING.md b/INJECTING.md new file mode 100644 index 0000000..c8abcb1 --- /dev/null +++ b/INJECTING.md @@ -0,0 +1,14 @@ +# Injecting a Mod into a Data.bin Save +* Install [Python 3](https://www.python.org/) if you don't already have it +* Download the latest `SaveBinPatcher.zip` from [here](https://github.com/SeekyCt/spm-save-exploit/releases) +* Extract the zip somewhere on your computer +* Move your data.bin file into your extracted SaveBinPatcher +* Move the rel for the mod into your extracted SaveBinPatcher +* Run inject.py in SaveBinPatcher + * Enter `data.bin` when asked for the path to the input data.bin + * Enter your game version when prompted + * Enter the id of the save file you want to replace with the rel loader when prompted + * Enter the filename of the rel you downloaded when asked for the path to the input rel + * Enter `output.bin` when asked for the path to the output data.bin + * Wait a little while for the save to be repacked (usually takes about 20-30 seconds) +* Your save is now patched, return to the tutorial that linked you here for instructions on what to do next diff --git a/README.md b/README.md index da7b66f..8ac27e3 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ -# spm-save-exploit \ No newline at end of file +# Super Paper Mario Save File ACE Exploit + +This is an exploit for all versions of Super Paper Mario to allow arbitrary code to be ran from a save file. See [this video](https://youtu.be/aqeQ21WHMVE) for a demonstration. + +## How does it work? + +The game calls the function `pausewinSetMessage` with the item id of every item in the inventory to display their descriptions in the pause menu. The id is not checked to be valid here, and is used as an index into a table to read the message's name string from. The game then copies that name message onto the stack without checking the length so that it can append "_ex" to it: + +![](https://cdn.discordapp.com/attachments/610974864706371585/868163069606527016/unknown.png) + +By editing a save file's inventory, the id of an item can be set high enough that the pointer to its description message name string can be read from the save file, and that pointer can be set to point to somewhere in the save file too to use a custom string. That custom string can then used to overflow the buffer for the string on the stack and overwrite the link register save after it, meaning that the game can be made to branch to any address desired when this function finishes. By setting this address to somewhere within the save file, arbitrary code can then be ran. + +## How does this load mods? + +Thanks to a lot of help from Zephiles, as well as some code by PistonMiner that was re-used from the TTYD save exploit rel loader, the payload for this exploit can be made to reboot the game and hook in a few places to load and execute a rel from NAND. See `source` for more details. + +## Credits +* Seeky - finding the exploit +* Zephiles - writing the majority of the rel loader payload +* PistonMiner - code re-used from the TTYD save file rel loader +* Segher and Dolphin Emulator developers - Wii save unpacking & packing code referenced diff --git a/SaveFileHack.py b/SaveFileHack.py new file mode 100644 index 0000000..e2600f7 --- /dev/null +++ b/SaveFileHack.py @@ -0,0 +1,197 @@ +#!/usr/bin/python3 +import binascii +import sys + +# stringToInt taken from text_to_bits from here: +# https://stackoverflow.com/questions/7396849/convert-binary-to-ascii-and-vice-versa +def stringToInt(string, encoding="utf-8", errors="surrogatepass"): + bits = bin(int(binascii.hexlify(string.encode(encoding, errors)), 16))[2:] + return int(bits.zfill(8 * ((len(bits) + 7) // 8)), 2) + +def verifyVersionString(string): + if string == "eu0": + return True + elif string == "eu1": + return True + elif string == "jp0": + return True + elif string == "jp1": + return True + elif string == "kr0": + return True + elif string == "us0": + return True + elif string == "us1": + return True + elif string == "us2": + return True + else: + return False + +# Make sure something was passed in +if len(sys.argv) < 2: + input("You must pass in a proper SPM save file. Press Enter to close this window.") + sys.exit("") + +# Check if the version number was passed in +VersionString = "" +if len(sys.argv) < 3: + + # Prompt for the version number to use + while (VersionString == ""): + VersionString = input("Enter the version of the game to hack\n(eu0, eu1, jp0, jp1, kr0, us0, us1, us2): ") + + # Make sure the input is valid + if (not verifyVersionString(VersionString)): + VersionString = "" +else: + VersionString = sys.argv[2] + + # Make sure the input is valid + if (not verifyVersionString(VersionString)): + VersionString = "" + + # Prompt for the version number to use + while (VersionString == ""): + VersionString = input("Enter the version of the game to hack\n(eu0, eu1, jp0, jp1, kr0, us0, us1, us2): ") + + # Make sure the input is valid + if (not verifyVersionString(VersionString)): + VersionString = "" + +# Set version-specific values +if (VersionString == "eu0") or (VersionString == "eu1"): + InitAsmFunctionPointer = 0x80526294 + TextBufferPointerOffset = 0x1B0 + TextBufferPointer = 0x805256FC + BinVersion = "EU" + ItemId = 0x6E59 +elif VersionString == "jp0": + InitAsmFunctionPointer = 0x804B8594 + TextBufferPointerOffset = 0x17C + TextBufferPointer = 0x804B79C8 + BinVersion = "JP_0" + ItemId = 0x6D0A +elif VersionString == "jp1": + InitAsmFunctionPointer = 0x804B9B94 + TextBufferPointerOffset = 0x174 + TextBufferPointer = 0x804B8FC0 + BinVersion = "JP_1" + ItemId = 0x6D24 +elif VersionString == "kr0": + InitAsmFunctionPointer = 0x8055DBF4 + TextBufferPointerOffset = 0x170 + TextBufferPointer = 0x8055D01C + BinVersion = "KR" + ItemId = 0x70D9 +elif VersionString == "us0": + InitAsmFunctionPointer = 0x804E3294 + TextBufferPointerOffset = 0x1A4 + TextBufferPointer = 0x804E26F0 + BinVersion = "US_0" + ItemId = 0x6D08 +elif VersionString == "us1": + InitAsmFunctionPointer = 0x804E4B14 + TextBufferPointerOffset = 0x1AC + TextBufferPointer = 0x804E3F78 + BinVersion = "US_1" + ItemId = 0x6D26 +elif VersionString == "us2": + InitAsmFunctionPointer = 0x804E4C94 + TextBufferPointerOffset = 0x174 + TextBufferPointer = 0x804E40C0 + BinVersion = "US_2" + ItemId = 0x6D24 + +f = open(sys.argv[1], "r+b") + +# Clear all of the bytes in the file +f.seek(0, 0) +f.write((0).to_bytes(0x25B0, byteorder="big", signed=False)) + +# Set the default efb width and height, as they can apparently effect being able to open the item menu +f.seek(0x20, 0) +f.write((0x26001E0).to_bytes(4, byteorder="big", signed=False)) + +# Write the new file name +FileNameString = "REL Loader\0" +f.seek(0x28, 0) +f.write(stringToInt(FileNameString).to_bytes(len(FileNameString), byteorder="big", signed=False)) + +# Write the map name +MapNameString = "dos_01\0" +f.seek(0x4C, 0) +f.write(stringToInt(MapNameString).to_bytes(len(MapNameString), byteorder="big", signed=False)) + +# Set Mario's level to 1, to prevent leveling up immediately +f.seek(0x1B14, 0) +f.write((1).to_bytes(4, byteorder="big", signed=False)) + +# Set the flip timer to 10 to prevent counting up immediately +f.seek(0x1B24, 0) +f.write((10).to_bytes(4, byteorder="big", signed=False)) + +# Write the item id +f.seek(0x1B70, 0) +f.write(ItemId.to_bytes(2, byteorder="big", signed=False)) + +# Write the pointer to text buffer +f.seek(TextBufferPointerOffset, 0) +f.write(TextBufferPointer.to_bytes(4, byteorder="big", signed=False)) + +# Write the text buffer +f.seek(TextBufferPointerOffset + 0x4, 0) +for i in range(0x94): + f.write((0x33).to_bytes(1, byteorder="big", signed=False)) + +# Write the pointer to the init asm function +f.seek(TextBufferPointerOffset + 0x98, 0) +f.write(InitAsmFunctionPointer.to_bytes(4, byteorder="big", signed=False)) + +# Write the init asm function +# The init function is the same for all versions except for Korean +if VersionString == "kr0": + InitAsmFuncBinName = "Init_KR" +else: + InitAsmFuncBinName = "Init_Main" + +InitAsmFuncOffset = 0xD4C +g = open("bin/" + InitAsmFuncBinName + ".bin", "rb") + +# Perform the write +Func = g.read() + +f.seek(InitAsmFuncOffset, 0) +for b in Func: + f.write(b.to_bytes(1, byteorder="big", signed=False)) + +g.close() + +# Write the main asm function +g = open("bin/Main_" + BinVersion + ".bin", "rb") + +# Perform the write +Func = g.read() + +f.seek(InitAsmFuncOffset + 0x24, 0) +for b in Func: + f.write(b.to_bytes(1, byteorder="big", signed=False)) + +g.close() + +# Get the sum of the bytes for the data field +f.seek(0x8, 0) +DataFieldSum = 0x3FC +DataField = f.read(0x25A8) +for b in DataField: + DataFieldSum += b + +# Set the checksum of the bytes for the data field +f.seek(0x25B0, 0) +f.write(DataFieldSum.to_bytes(4, byteorder="big", signed=False)) + +# Set the inverted checksum of the bytes for the data field +f.seek(0x25B4, 0) +f.write((~DataFieldSum).to_bytes(4, byteorder="big", signed=True)) + +f.close() diff --git a/Source/Init_KR.txt b/Source/Init_KR.txt new file mode 100644 index 0000000..bc21fb1 --- /dev/null +++ b/Source/Init_KR.txt @@ -0,0 +1,27 @@ +# Code created by Zephiles + +# Set global function/variable offsets +.set GP_OFFSET,-0x7DA8 +.set MAIN_FUNC_OFFSET,0xD70 +.set NANDMGR_WORK_PTR_OFFSET,-0x7D70 + +# Get the index for the file loaded +lwz r4,GP_OFFSET(r13) +lwz r4,0xDC(r4) + +# Adjust the index to be in multiples of 0x25B8 bytes +mulli r4,r4,0x25B8 + +# Add the offset to main function +addi r4,r4,MAIN_FUNC_OFFSET + +# Get the NAND File pointer +lwz r5,NANDMGR_WORK_PTR_OFFSET(r13) +lwz r5,0x10(r5) + +# Get the start of the main function in the current file +add r12,r5,r4 + +# Jump to the main function +mtctr r12 +bctr \ No newline at end of file diff --git a/Source/Init_Main.txt b/Source/Init_Main.txt new file mode 100644 index 0000000..4e8e77e --- /dev/null +++ b/Source/Init_Main.txt @@ -0,0 +1,27 @@ +# Code created by Zephiles + +# Set global function/variable offsets +.set GP_OFFSET,-0x7D88 +.set MAIN_FUNC_OFFSET,0xD70 +.set NANDMGR_WORK_PTR_OFFSET,-0x7D50 + +# Get the index for the file loaded +lwz r4,GP_OFFSET(r13) +lwz r4,0xDC(r4) + +# Adjust the index to be in multiples of 0x25B8 bytes +mulli r4,r4,0x25B8 + +# Add the offset to main function +addi r4,r4,MAIN_FUNC_OFFSET + +# Get the NAND File pointer +lwz r5,NANDMGR_WORK_PTR_OFFSET(r13) +lwz r5,0x10(r5) + +# Get the start of the main function in the current file +add r12,r5,r4 + +# Jump to the main function +mtctr r12 +bctr \ No newline at end of file diff --git a/Source/Main_EU.txt b/Source/Main_EU.txt new file mode 100644 index 0000000..4a3e52d --- /dev/null +++ b/Source/Main_EU.txt @@ -0,0 +1,370 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) + +# Modifications made by Zephiles + +# Set global function/variable/offset locations/values +.set LOADER_SIZE,(__end - __start) +.set LOADER_ARENA_LOCATION,0x81000000 +.set LOADER_LOWMEM_LOCATION,0x80000F98 + +.set SAVE_REGION_START_OFFSET,-0x7390 +.set SAVE_REGION_END_OFFSET,-0x738C + +.set PAYLOAD_HOOK_LOCATION,0x8023E5FC + +.set wiiResetCode,0x801A85F0 +.set __OSReboot,0x80275C98 +.set Run_main,0x802713E8 +.set Run_restart,0x81333B28 + +.set relIsLoadedBoolCheck,0x8023E604 + +.set memmove,0x8025A874 +.set DCFlushRange,0x80270050 +.set ICInvalidateRange,0x802700D8 + +.set relWpOffset,-0x7D60 +.set NANDOpen,0x8029A3C8 +.set NANDRead,0x80299B74 +.set OSLink,0x80274C0C +.set __memAlloc,0x801A626C +.set __memFree,0x801A62F0 +.set OSUnlink,0x80274E50 +.set NANDClose,0x8029A648 + +__start: +# Hook __OSReboot +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +bl main_pic + +main_pic: +mflr r4 +addi r4,r4,(__OSReboot_hook - main_pic) +bl writeBranch + +# Branch to wiiResetCode without returning +lis r3,wiiResetCode@h +ori r3,r3,wiiResetCode@l +mtctr r3 +bctr + +# Runs out of initial location. Relocates to arena location, sets saved region +# and patches Run in game binary. +__OSReboot_hook: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +# Save arguments for original function +mr r30,r3 +mr r31,r4 + +# Initialize static values used for addresses +lis r29,LOADER_ARENA_LOCATION@h + +# Relocate into arena memory +mr r3,r29 # LOADER_ARENA_LOCATION +bl __OSReboot_hook_pic + +__OSReboot_hook_pic: +mflr r4 +addi r4,r4,(__start - __OSReboot_hook_pic) +li r5,LOADER_SIZE +lis r12,memmove@h +ori r12,r12,memmove@l +mtlr r12 +blrl + +mr r3,r29 # LOADER_ARENA_LOCATION +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Set the saved region start and end +stw r29,SAVE_REGION_START_OFFSET(r13) +addi r3,r29,LOADER_SIZE +stw r3,SAVE_REGION_END_OFFSET(r13) + +# Set up Run hook in main binary +lis r3,Run_main@h +ori r3,r3,Run_main@l +ori r4,r29,(LOADER_ARENA_LOCATION + (Run_main_hook - __start))@l +bl writeBranch + +# Back into __OSReboot +lis r3,(__OSReboot + 4)@h +ori r12,r3,(__OSReboot + 4)@l +mtctr r12 + +# Restore arguments for original function +mr r3,r30 +mr r4,r31 + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +bctr + +# Runs out of arena location. Called when restart binary is about to be +# executed. Patch restart binary. +Run_main_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Set up Run hook in restart DOL +lis r3,Run_restart@h +ori r3,r3,Run_restart@l +lis r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@h +ori r4,r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@l +bl writeBranch + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_main + 4) - LOADER_ARENA_LOCATION + __start) + +# Runs out of arena location. Called when game binary is about to be executed. +# Relocates to low memory and patches reloaded game DOL. +Run_restart_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Relocate to low memory +lis r30,LOADER_LOWMEM_LOCATION@h +ori r3,r30,LOADER_LOWMEM_LOCATION@l +lis r4,LOADER_ARENA_LOCATION@h +li r5,LOADER_SIZE +bl (memmove - LOADER_ARENA_LOCATION + __start) + +ori r3,r30,LOADER_LOWMEM_LOCATION@l +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Place hook for payload +lis r3,PAYLOAD_HOOK_LOCATION@h +ori r3,r3,PAYLOAD_HOOK_LOCATION@l +ori r4,r30,(LOADER_LOWMEM_LOCATION + (gPayload - __start))@l +bl writeBranch + +# Place restart hook +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +lis r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@h +ori r4,r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@l +bl writeBranch + +# Change relIsLoaded to check the bool at 0x9 instead of 0x8 +lis r3,relIsLoadedBoolCheck@h +ori r3,r3,relIsLoadedBoolCheck@l +li r4,9 +stb r4,0x3(r3) +bl makeWordChangeVisible + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_restart + 4) - LOADER_ARENA_LOCATION + __start) + +writeBranch: +# Write instruction +subf r4,r3,r4 +rlwinm r4,r4,0,6,29 +lis r5,0x4800 +or r5,r5,r4 +stw r5,0(r3) + +# Make visible, tailcall +makeWordChangeVisible: +li r4,4 + +makeCodeChangesVisible: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +mr r31,r3 # address +mr r30,r4 # size + +lis r29,DCFlushRange@h +ori r5,r29,DCFlushRange@l +mtlr r5 +blrl + +ori r3,r29,ICInvalidateRange@l +mtlr r3 +mr r3,r31 # address +mr r4,r30 # size +blrl + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +gPayload: +# Original REL Loader code by SeekyCt, with various modifications by Zephiles +# If the custom rel is loaded, then exit +lwz r3,relWpOffset(r13) +lbz r0,0x9(r3) +cmpwi r0,0 +bnelr+ + +# If the game's rel is not loaded, then exit +lbz r0,0x8(r3) +cmpwi r0,0 +beqlr- + +# Push stack +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r27,0x8(sp) + +# Initialize static values used for addresses +lis r31,0x8000 +li r30,0 # Used to check if a REL file is loaded or not + +# Allocate memory for NANDFileInfo +li r4,0x8C +bl allocateMemory + +# Backup the returned address to be used for later +mr r29,r3 + +# Open the file +ori r3,r31,(LOADER_LOWMEM_LOCATION + filenameString)@l +mr r4,r29 # Pointer to NANDFileInfo +li r5,1 # NAND_ACCESS_READ +bl (NANDOpen - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +bne- freeNandFileInfo + +# Allocate 0x20 bytes of memory, as the read size must be in multiples of 0x20 bytes +li r4,0x20 # Bytes to allocate +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read from the NAND +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +li r5,0x20 # Amount of bytes to read +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the file size and adjust it to be in multiples of 0x20 bytes +lwz r28,0(r27) # File Size +addi r28,r28,31 +rlwinm r28,r28,0,0,26 + +# Free the 0x20 bytes from earlier +mr r4,r27 +bl freeMemory + +# Allocate more bytes based on the adjusted file size +mr r4,r28 +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read the REL Area of the file +# NANDRead automatically seeks to the next section +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +mr r5,r28 # Adjusted File Size +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the BSS Area size and allocate memory for it +lwz r4,0x20(r27) +bl allocateMemory + +# Backup the returned address to be used for later +mr r28,r3 + +# Link the functions in the REL +mr r3,r27 # Pointer to the Module +mr r4,r28 # Pointer to the BSS Area +bl (OSLink - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,1 # Check if something went wrong +bne- freeRemainingMemory + +# Store the BSS Area and the Module +stw r28,0x13F8(r31) # Pointer to the BSS Area +stw r27,0x13FC(r31) # Pointer to the Module + +# Get the REL Prolog Pointer +lwz r30,0x34(r27) + +# Done, so close the file +b closeNand + +# Function definitions, error handling, etc. +freeMemory: +li r3,0 # Heap to use +b (__memFree - LOADER_LOWMEM_LOCATION + __start) + +allocateMemory: +li r3,0 # Heap to use +b (__memAlloc - LOADER_LOWMEM_LOCATION + __start) + +freeRemainingMemory: +mr r3,r27 # Pointer to the Module +bl (OSUnlink - LOADER_LOWMEM_LOCATION + __start) + +# Free the BSS Area and the File Buffer +mr r4,r28 # Pointer to the BSS Area +bl freeMemory + +freeCurrentMemoryArea: +mr r4,r27 # Pointer to the File Buffer or the 0x20 Bytes Buffer +bl freeMemory + +closeNand: +mr r3,r29 # Pointer to NANDFileInfo +bl (NANDClose - LOADER_LOWMEM_LOCATION + __start) + +freeNandFileInfo: +mr r4,r29 +bl freeMemory + +# Run the REL Prolog if the load was successful +cmpwi r30,0 +beq- exit +mtlr r30 +blrl + +exit: +# Stop this code from running again and let the game continue +lwz r3,relWpOffset(r13) +li r0,1 +stb r0,0x9(r3) + +# Pop stack +lmw r27,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +filenameString: +.asciz "pcrel.bin" + +__end: \ No newline at end of file diff --git a/Source/Main_JP_0.txt b/Source/Main_JP_0.txt new file mode 100644 index 0000000..66ed114 --- /dev/null +++ b/Source/Main_JP_0.txt @@ -0,0 +1,370 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) + +# Modifications made by Zephiles + +# Set global function/variable/offset locations/values +.set LOADER_SIZE,(__end - __start) +.set LOADER_ARENA_LOCATION,0x81000000 +.set LOADER_LOWMEM_LOCATION,0x80000F98 + +.set SAVE_REGION_START_OFFSET,-0x7390 +.set SAVE_REGION_END_OFFSET,-0x738C + +.set PAYLOAD_HOOK_LOCATION,0x8023BFB0 + +.set wiiResetCode,0x801A77F8 +.set __OSReboot,0x802733EC +.set Run_main,0x8026EB78 +.set Run_restart,0x81333B28 + +.set relIsLoadedBoolCheck,0x8023BFB8 + +.set memmove,0x80257FCC +.set DCFlushRange,0x8026D7B0 +.set ICInvalidateRange,0x8026D868 + +.set relWpOffset,-0x7D60 +.set NANDOpen,0x80297A5C +.set NANDRead,0x80297208 +.set OSLink,0x8027237C +.set __memAlloc,0x801A5624 +.set __memFree,0x801A56A8 +.set OSUnlink,0x802725A4 +.set NANDClose,0x80297CDC + +__start: +# Hook __OSReboot +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +bl main_pic + +main_pic: +mflr r4 +addi r4,r4,(__OSReboot_hook - main_pic) +bl writeBranch + +# Branch to wiiResetCode without returning +lis r3,wiiResetCode@h +ori r3,r3,wiiResetCode@l +mtctr r3 +bctr + +# Runs out of initial location. Relocates to arena location, sets saved region +# and patches Run in game binary. +__OSReboot_hook: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +# Save arguments for original function +mr r30,r3 +mr r31,r4 + +# Initialize static values used for addresses +lis r29,LOADER_ARENA_LOCATION@h + +# Relocate into arena memory +mr r3,r29 # LOADER_ARENA_LOCATION +bl __OSReboot_hook_pic + +__OSReboot_hook_pic: +mflr r4 +addi r4,r4,(__start - __OSReboot_hook_pic) +li r5,LOADER_SIZE +lis r12,memmove@h +ori r12,r12,memmove@l +mtlr r12 +blrl + +mr r3,r29 # LOADER_ARENA_LOCATION +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Set the saved region start and end +stw r29,SAVE_REGION_START_OFFSET(r13) +addi r3,r29,LOADER_SIZE +stw r3,SAVE_REGION_END_OFFSET(r13) + +# Set up Run hook in main binary +lis r3,Run_main@h +ori r3,r3,Run_main@l +ori r4,r29,(LOADER_ARENA_LOCATION + (Run_main_hook - __start))@l +bl writeBranch + +# Back into __OSReboot +lis r3,(__OSReboot + 4)@h +ori r12,r3,(__OSReboot + 4)@l +mtctr r12 + +# Restore arguments for original function +mr r3,r30 +mr r4,r31 + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +bctr + +# Runs out of arena location. Called when restart binary is about to be +# executed. Patch restart binary. +Run_main_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Set up Run hook in restart DOL +lis r3,Run_restart@h +ori r3,r3,Run_restart@l +lis r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@h +ori r4,r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@l +bl writeBranch + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_main + 4) - LOADER_ARENA_LOCATION + __start) + +# Runs out of arena location. Called when game binary is about to be executed. +# Relocates to low memory and patches reloaded game DOL. +Run_restart_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Relocate to low memory +lis r30,LOADER_LOWMEM_LOCATION@h +ori r3,r30,LOADER_LOWMEM_LOCATION@l +lis r4,LOADER_ARENA_LOCATION@h +li r5,LOADER_SIZE +bl (memmove - LOADER_ARENA_LOCATION + __start) + +ori r3,r30,LOADER_LOWMEM_LOCATION@l +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Place hook for payload +lis r3,PAYLOAD_HOOK_LOCATION@h +ori r3,r3,PAYLOAD_HOOK_LOCATION@l +ori r4,r30,(LOADER_LOWMEM_LOCATION + (gPayload - __start))@l +bl writeBranch + +# Place restart hook +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +lis r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@h +ori r4,r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@l +bl writeBranch + +# Change relIsLoaded to check the bool at 0x9 instead of 0x8 +lis r3,relIsLoadedBoolCheck@h +ori r3,r3,relIsLoadedBoolCheck@l +li r4,9 +stb r4,0x3(r3) +bl makeWordChangeVisible + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_restart + 4) - LOADER_ARENA_LOCATION + __start) + +writeBranch: +# Write instruction +subf r4,r3,r4 +rlwinm r4,r4,0,6,29 +lis r5,0x4800 +or r5,r5,r4 +stw r5,0(r3) + +# Make visible, tailcall +makeWordChangeVisible: +li r4,4 + +makeCodeChangesVisible: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +mr r31,r3 # address +mr r30,r4 # size + +lis r29,DCFlushRange@h +ori r5,r29,DCFlushRange@l +mtlr r5 +blrl + +ori r3,r29,ICInvalidateRange@l +mtlr r3 +mr r3,r31 # address +mr r4,r30 # size +blrl + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +gPayload: +# Original REL Loader code by SeekyCt, with various modifications by Zephiles +# If the custom rel is loaded, then exit +lwz r3,relWpOffset(r13) +lbz r0,0x9(r3) +cmpwi r0,0 +bnelr+ + +# If the game's rel is not loaded, then exit +lbz r0,0x8(r3) +cmpwi r0,0 +beqlr- + +# Push stack +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r27,0x8(sp) + +# Initialize static values used for addresses +lis r31,0x8000 +li r30,0 # Used to check if a REL file is loaded or not + +# Allocate memory for NANDFileInfo +li r4,0x8C +bl allocateMemory + +# Backup the returned address to be used for later +mr r29,r3 + +# Open the file +ori r3,r31,(LOADER_LOWMEM_LOCATION + filenameString)@l +mr r4,r29 # Pointer to NANDFileInfo +li r5,1 # NAND_ACCESS_READ +bl (NANDOpen - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +bne- freeNandFileInfo + +# Allocate 0x20 bytes of memory, as the read size must be in multiples of 0x20 bytes +li r4,0x20 # Bytes to allocate +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read from the NAND +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +li r5,0x20 # Amount of bytes to read +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the file size and adjust it to be in multiples of 0x20 bytes +lwz r28,0(r27) # File Size +addi r28,r28,31 +rlwinm r28,r28,0,0,26 + +# Free the 0x20 bytes from earlier +mr r4,r27 +bl freeMemory + +# Allocate more bytes based on the adjusted file size +mr r4,r28 +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read the REL Area of the file +# NANDRead automatically seeks to the next section +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +mr r5,r28 # Adjusted File Size +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the BSS Area size and allocate memory for it +lwz r4,0x20(r27) +bl allocateMemory + +# Backup the returned address to be used for later +mr r28,r3 + +# Link the functions in the REL +mr r3,r27 # Pointer to the Module +mr r4,r28 # Pointer to the BSS Area +bl (OSLink - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,1 # Check if something went wrong +bne- freeRemainingMemory + +# Store the BSS Area and the Module +stw r28,0x13F8(r31) # Pointer to the BSS Area +stw r27,0x13FC(r31) # Pointer to the Module + +# Get the REL Prolog Pointer +lwz r30,0x34(r27) + +# Done, so close the file +b closeNand + +# Function definitions, error handling, etc. +freeMemory: +li r3,0 # Heap to use +b (__memFree - LOADER_LOWMEM_LOCATION + __start) + +allocateMemory: +li r3,0 # Heap to use +b (__memAlloc - LOADER_LOWMEM_LOCATION + __start) + +freeRemainingMemory: +mr r3,r27 # Pointer to the Module +bl (OSUnlink - LOADER_LOWMEM_LOCATION + __start) + +# Free the BSS Area and the File Buffer +mr r4,r28 # Pointer to the BSS Area +bl freeMemory + +freeCurrentMemoryArea: +mr r4,r27 # Pointer to the File Buffer or the 0x20 Bytes Buffer +bl freeMemory + +closeNand: +mr r3,r29 # Pointer to NANDFileInfo +bl (NANDClose - LOADER_LOWMEM_LOCATION + __start) + +freeNandFileInfo: +mr r4,r29 +bl freeMemory + +# Run the REL Prolog if the load was successful +cmpwi r30,0 +beq- exit +mtlr r30 +blrl + +exit: +# Stop this code from running again and let the game continue +lwz r3,relWpOffset(r13) +li r0,1 +stb r0,0x9(r3) + +# Pop stack +lmw r27,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +filenameString: +.asciz "pcrel.bin" + +__end: \ No newline at end of file diff --git a/Source/Main_JP_1.txt b/Source/Main_JP_1.txt new file mode 100644 index 0000000..51b789b --- /dev/null +++ b/Source/Main_JP_1.txt @@ -0,0 +1,370 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) + +# Modifications made by Zephiles + +# Set global function/variable/offset locations/values +.set LOADER_SIZE,(__end - __start) +.set LOADER_ARENA_LOCATION,0x81000000 +.set LOADER_LOWMEM_LOCATION,0x80000F98 + +.set SAVE_REGION_START_OFFSET,-0x7390 +.set SAVE_REGION_END_OFFSET,-0x738C + +.set PAYLOAD_HOOK_LOCATION,0x8023C65C + +.set wiiResetCode,0x801A7840 +.set __OSReboot,0x80273AA8 +.set Run_main,0x8026F1F8 +.set Run_restart,0x81333B28 + +.set relIsLoadedBoolCheck,0x8023C664 + +.set memmove,0x80258688 +.set DCFlushRange,0x8026DE60 +.set ICInvalidateRange,0x8026DEE8 + +.set relWpOffset,-0x7D60 +.set NANDOpen,0x80297FF8 +.set NANDRead,0x802977A4 +.set OSLink,0x80272A1C +.set __memAlloc,0x801A566C +.set __memFree,0x801A56F0 +.set OSUnlink,0x80272C60 +.set NANDClose,0x80298278 + +__start: +# Hook __OSReboot +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +bl main_pic + +main_pic: +mflr r4 +addi r4,r4,(__OSReboot_hook - main_pic) +bl writeBranch + +# Branch to wiiResetCode without returning +lis r3,wiiResetCode@h +ori r3,r3,wiiResetCode@l +mtctr r3 +bctr + +# Runs out of initial location. Relocates to arena location, sets saved region +# and patches Run in game binary. +__OSReboot_hook: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +# Save arguments for original function +mr r30,r3 +mr r31,r4 + +# Initialize static values used for addresses +lis r29,LOADER_ARENA_LOCATION@h + +# Relocate into arena memory +mr r3,r29 # LOADER_ARENA_LOCATION +bl __OSReboot_hook_pic + +__OSReboot_hook_pic: +mflr r4 +addi r4,r4,(__start - __OSReboot_hook_pic) +li r5,LOADER_SIZE +lis r12,memmove@h +ori r12,r12,memmove@l +mtlr r12 +blrl + +mr r3,r29 # LOADER_ARENA_LOCATION +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Set the saved region start and end +stw r29,SAVE_REGION_START_OFFSET(r13) +addi r3,r29,LOADER_SIZE +stw r3,SAVE_REGION_END_OFFSET(r13) + +# Set up Run hook in main binary +lis r3,Run_main@h +ori r3,r3,Run_main@l +ori r4,r29,(LOADER_ARENA_LOCATION + (Run_main_hook - __start))@l +bl writeBranch + +# Back into __OSReboot +lis r3,(__OSReboot + 4)@h +ori r12,r3,(__OSReboot + 4)@l +mtctr r12 + +# Restore arguments for original function +mr r3,r30 +mr r4,r31 + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +bctr + +# Runs out of arena location. Called when restart binary is about to be +# executed. Patch restart binary. +Run_main_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Set up Run hook in restart DOL +lis r3,Run_restart@h +ori r3,r3,Run_restart@l +lis r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@h +ori r4,r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@l +bl writeBranch + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_main + 4) - LOADER_ARENA_LOCATION + __start) + +# Runs out of arena location. Called when game binary is about to be executed. +# Relocates to low memory and patches reloaded game DOL. +Run_restart_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Relocate to low memory +lis r30,LOADER_LOWMEM_LOCATION@h +ori r3,r30,LOADER_LOWMEM_LOCATION@l +lis r4,LOADER_ARENA_LOCATION@h +li r5,LOADER_SIZE +bl (memmove - LOADER_ARENA_LOCATION + __start) + +ori r3,r30,LOADER_LOWMEM_LOCATION@l +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Place hook for payload +lis r3,PAYLOAD_HOOK_LOCATION@h +ori r3,r3,PAYLOAD_HOOK_LOCATION@l +ori r4,r30,(LOADER_LOWMEM_LOCATION + (gPayload - __start))@l +bl writeBranch + +# Place restart hook +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +lis r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@h +ori r4,r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@l +bl writeBranch + +# Change relIsLoaded to check the bool at 0x9 instead of 0x8 +lis r3,relIsLoadedBoolCheck@h +ori r3,r3,relIsLoadedBoolCheck@l +li r4,9 +stb r4,0x3(r3) +bl makeWordChangeVisible + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_restart + 4) - LOADER_ARENA_LOCATION + __start) + +writeBranch: +# Write instruction +subf r4,r3,r4 +rlwinm r4,r4,0,6,29 +lis r5,0x4800 +or r5,r5,r4 +stw r5,0(r3) + +# Make visible, tailcall +makeWordChangeVisible: +li r4,4 + +makeCodeChangesVisible: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +mr r31,r3 # address +mr r30,r4 # size + +lis r29,DCFlushRange@h +ori r5,r29,DCFlushRange@l +mtlr r5 +blrl + +ori r3,r29,ICInvalidateRange@l +mtlr r3 +mr r3,r31 # address +mr r4,r30 # size +blrl + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +gPayload: +# Original REL Loader code by SeekyCt, with various modifications by Zephiles +# If the custom rel is loaded, then exit +lwz r3,relWpOffset(r13) +lbz r0,0x9(r3) +cmpwi r0,0 +bnelr+ + +# If the game's rel is not loaded, then exit +lbz r0,0x8(r3) +cmpwi r0,0 +beqlr- + +# Push stack +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r27,0x8(sp) + +# Initialize static values used for addresses +lis r31,0x8000 +li r30,0 # Used to check if a REL file is loaded or not + +# Allocate memory for NANDFileInfo +li r4,0x8C +bl allocateMemory + +# Backup the returned address to be used for later +mr r29,r3 + +# Open the file +ori r3,r31,(LOADER_LOWMEM_LOCATION + filenameString)@l +mr r4,r29 # Pointer to NANDFileInfo +li r5,1 # NAND_ACCESS_READ +bl (NANDOpen - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +bne- freeNandFileInfo + +# Allocate 0x20 bytes of memory, as the read size must be in multiples of 0x20 bytes +li r4,0x20 # Bytes to allocate +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read from the NAND +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +li r5,0x20 # Amount of bytes to read +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the file size and adjust it to be in multiples of 0x20 bytes +lwz r28,0(r27) # File Size +addi r28,r28,31 +rlwinm r28,r28,0,0,26 + +# Free the 0x20 bytes from earlier +mr r4,r27 +bl freeMemory + +# Allocate more bytes based on the adjusted file size +mr r4,r28 +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read the REL Area of the file +# NANDRead automatically seeks to the next section +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +mr r5,r28 # Adjusted File Size +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the BSS Area size and allocate memory for it +lwz r4,0x20(r27) +bl allocateMemory + +# Backup the returned address to be used for later +mr r28,r3 + +# Link the functions in the REL +mr r3,r27 # Pointer to the Module +mr r4,r28 # Pointer to the BSS Area +bl (OSLink - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,1 # Check if something went wrong +bne- freeRemainingMemory + +# Store the BSS Area and the Module +stw r28,0x13F8(r31) # Pointer to the BSS Area +stw r27,0x13FC(r31) # Pointer to the Module + +# Get the REL Prolog Pointer +lwz r30,0x34(r27) + +# Done, so close the file +b closeNand + +# Function definitions, error handling, etc. +freeMemory: +li r3,0 # Heap to use +b (__memFree - LOADER_LOWMEM_LOCATION + __start) + +allocateMemory: +li r3,0 # Heap to use +b (__memAlloc - LOADER_LOWMEM_LOCATION + __start) + +freeRemainingMemory: +mr r3,r27 # Pointer to the Module +bl (OSUnlink - LOADER_LOWMEM_LOCATION + __start) + +# Free the BSS Area and the File Buffer +mr r4,r28 # Pointer to the BSS Area +bl freeMemory + +freeCurrentMemoryArea: +mr r4,r27 # Pointer to the File Buffer or the 0x20 Bytes Buffer +bl freeMemory + +closeNand: +mr r3,r29 # Pointer to NANDFileInfo +bl (NANDClose - LOADER_LOWMEM_LOCATION + __start) + +freeNandFileInfo: +mr r4,r29 +bl freeMemory + +# Run the REL Prolog if the load was successful +cmpwi r30,0 +beq- exit +mtlr r30 +blrl + +exit: +# Stop this code from running again and let the game continue +lwz r3,relWpOffset(r13) +li r0,1 +stb r0,0x9(r3) + +# Pop stack +lmw r27,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +filenameString: +.asciz "pcrel.bin" + +__end: \ No newline at end of file diff --git a/Source/Main_KR.txt b/Source/Main_KR.txt new file mode 100644 index 0000000..574c69c --- /dev/null +++ b/Source/Main_KR.txt @@ -0,0 +1,370 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) + +# Modifications made by Zephiles + +# Set global function/variable/offset locations/values +.set LOADER_SIZE,(__end - __start) +.set LOADER_ARENA_LOCATION,0x81000000 +.set LOADER_LOWMEM_LOCATION,0x80000F98 + +.set SAVE_REGION_START_OFFSET,-0x7418 +.set SAVE_REGION_END_OFFSET,-0x7414 + +.set PAYLOAD_HOOK_LOCATION,0x80236CC8 + +.set wiiResetCode,0x801A104C +.set __OSReboot,0x80278470 +.set Run_main,0x80273B28 +.set Run_restart,0x8133405C + +.set relIsLoadedBoolCheck,0x80236CD0 + +.set memmove,0x8025C57C +.set DCFlushRange,0x8027243C +.set ICInvalidateRange,0x802724F4 + +.set relWpOffset,-0x7D80 +.set NANDOpen,0x802A0298 +.set NANDRead,0x8029F97C +.set OSLink,0x80277328 +.set __memAlloc,0x8019EB44 +.set __memFree,0x8019EBC8 +.set OSUnlink,0x8027756C +.set NANDClose,0x802A0518 + +__start: +# Hook __OSReboot +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +bl main_pic + +main_pic: +mflr r4 +addi r4,r4,(__OSReboot_hook - main_pic) +bl writeBranch + +# Branch to wiiResetCode without returning +lis r3,wiiResetCode@h +ori r3,r3,wiiResetCode@l +mtctr r3 +bctr + +# Runs out of initial location. Relocates to arena location, sets saved region +# and patches Run in game binary. +__OSReboot_hook: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +# Save arguments for original function +mr r30,r3 +mr r31,r4 + +# Initialize static values used for addresses +lis r29,LOADER_ARENA_LOCATION@h + +# Relocate into arena memory +mr r3,r29 # LOADER_ARENA_LOCATION +bl __OSReboot_hook_pic + +__OSReboot_hook_pic: +mflr r4 +addi r4,r4,(__start - __OSReboot_hook_pic) +li r5,LOADER_SIZE +lis r12,memmove@h +ori r12,r12,memmove@l +mtlr r12 +blrl + +mr r3,r29 # LOADER_ARENA_LOCATION +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Set the saved region start and end +stw r29,SAVE_REGION_START_OFFSET(r13) +addi r3,r29,LOADER_SIZE +stw r3,SAVE_REGION_END_OFFSET(r13) + +# Set up Run hook in main binary +lis r3,Run_main@h +ori r3,r3,Run_main@l +ori r4,r29,(LOADER_ARENA_LOCATION + (Run_main_hook - __start))@l +bl writeBranch + +# Back into __OSReboot +lis r3,(__OSReboot + 4)@h +ori r12,r3,(__OSReboot + 4)@l +mtctr r12 + +# Restore arguments for original function +mr r3,r30 +mr r4,r31 + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +bctr + +# Runs out of arena location. Called when restart binary is about to be +# executed. Patch restart binary. +Run_main_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Set up Run hook in restart DOL +lis r3,Run_restart@h +ori r3,r3,Run_restart@l +lis r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@h +ori r4,r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@l +bl writeBranch + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_main + 4) - LOADER_ARENA_LOCATION + __start) + +# Runs out of arena location. Called when game binary is about to be executed. +# Relocates to low memory and patches reloaded game DOL. +Run_restart_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Relocate to low memory +lis r30,LOADER_LOWMEM_LOCATION@h +ori r3,r30,LOADER_LOWMEM_LOCATION@l +lis r4,LOADER_ARENA_LOCATION@h +li r5,LOADER_SIZE +bl (memmove - LOADER_ARENA_LOCATION + __start) + +ori r3,r30,LOADER_LOWMEM_LOCATION@l +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Place hook for payload +lis r3,PAYLOAD_HOOK_LOCATION@h +ori r3,r3,PAYLOAD_HOOK_LOCATION@l +ori r4,r30,(LOADER_LOWMEM_LOCATION + (gPayload - __start))@l +bl writeBranch + +# Place restart hook +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +lis r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@h +ori r4,r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@l +bl writeBranch + +# Change relIsLoaded to check the bool at 0x9 instead of 0x8 +lis r3,relIsLoadedBoolCheck@h +ori r3,r3,relIsLoadedBoolCheck@l +li r4,9 +stb r4,0x3(r3) +bl makeWordChangeVisible + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_restart + 4) - LOADER_ARENA_LOCATION + __start) + +writeBranch: +# Write instruction +subf r4,r3,r4 +rlwinm r4,r4,0,6,29 +lis r5,0x4800 +or r5,r5,r4 +stw r5,0(r3) + +# Make visible, tailcall +makeWordChangeVisible: +li r4,4 + +makeCodeChangesVisible: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +mr r31,r3 # address +mr r30,r4 # size + +lis r29,DCFlushRange@h +ori r5,r29,DCFlushRange@l +mtlr r5 +blrl + +ori r3,r29,ICInvalidateRange@l +mtlr r3 +mr r3,r31 # address +mr r4,r30 # size +blrl + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +gPayload: +# Original REL Loader code by SeekyCt, with various modifications by Zephiles +# If the custom rel is loaded, then exit +lwz r3,relWpOffset(r13) +lbz r0,0x9(r3) +cmpwi r0,0 +bnelr+ + +# If the game's rel is not loaded, then exit +lbz r0,0x8(r3) +cmpwi r0,0 +beqlr- + +# Push stack +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r27,0x8(sp) + +# Initialize static values used for addresses +lis r31,0x8000 +li r30,0 # Used to check if a REL file is loaded or not + +# Allocate memory for NANDFileInfo +li r4,0x8C +bl allocateMemory + +# Backup the returned address to be used for later +mr r29,r3 + +# Open the file +ori r3,r31,(LOADER_LOWMEM_LOCATION + filenameString)@l +mr r4,r29 # Pointer to NANDFileInfo +li r5,1 # NAND_ACCESS_READ +bl (NANDOpen - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +bne- freeNandFileInfo + +# Allocate 0x20 bytes of memory, as the read size must be in multiples of 0x20 bytes +li r4,0x20 # Bytes to allocate +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read from the NAND +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +li r5,0x20 # Amount of bytes to read +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the file size and adjust it to be in multiples of 0x20 bytes +lwz r28,0(r27) # File Size +addi r28,r28,31 +rlwinm r28,r28,0,0,26 + +# Free the 0x20 bytes from earlier +mr r4,r27 +bl freeMemory + +# Allocate more bytes based on the adjusted file size +mr r4,r28 +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read the REL Area of the file +# NANDRead automatically seeks to the next section +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +mr r5,r28 # Adjusted File Size +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the BSS Area size and allocate memory for it +lwz r4,0x20(r27) +bl allocateMemory + +# Backup the returned address to be used for later +mr r28,r3 + +# Link the functions in the REL +mr r3,r27 # Pointer to the Module +mr r4,r28 # Pointer to the BSS Area +bl (OSLink - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,1 # Check if something went wrong +bne- freeRemainingMemory + +# Store the BSS Area and the Module +stw r28,0x13F8(r31) # Pointer to the BSS Area +stw r27,0x13FC(r31) # Pointer to the Module + +# Get the REL Prolog Pointer +lwz r30,0x34(r27) + +# Done, so close the file +b closeNand + +# Function definitions, error handling, etc. +freeMemory: +li r3,0 # Heap to use +b (__memFree - LOADER_LOWMEM_LOCATION + __start) + +allocateMemory: +li r3,0 # Heap to use +b (__memAlloc - LOADER_LOWMEM_LOCATION + __start) + +freeRemainingMemory: +mr r3,r27 # Pointer to the Module +bl (OSUnlink - LOADER_LOWMEM_LOCATION + __start) + +# Free the BSS Area and the File Buffer +mr r4,r28 # Pointer to the BSS Area +bl freeMemory + +freeCurrentMemoryArea: +mr r4,r27 # Pointer to the File Buffer or the 0x20 Bytes Buffer +bl freeMemory + +closeNand: +mr r3,r29 # Pointer to NANDFileInfo +bl (NANDClose - LOADER_LOWMEM_LOCATION + __start) + +freeNandFileInfo: +mr r4,r29 +bl freeMemory + +# Run the REL Prolog if the load was successful +cmpwi r30,0 +beq- exit +mtlr r30 +blrl + +exit: +# Stop this code from running again and let the game continue +lwz r3,relWpOffset(r13) +li r0,1 +stb r0,0x9(r3) + +# Pop stack +lmw r27,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +filenameString: +.asciz "pcrel.bin" + +__end: \ No newline at end of file diff --git a/Source/Main_US_0.txt b/Source/Main_US_0.txt new file mode 100644 index 0000000..6d49b48 --- /dev/null +++ b/Source/Main_US_0.txt @@ -0,0 +1,370 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) + +# Modifications made by Zephiles + +# Set global function/variable/offset locations/values +.set LOADER_SIZE,(__end - __start) +.set LOADER_ARENA_LOCATION,0x81000000 +.set LOADER_LOWMEM_LOCATION,0x80000F98 + +.set SAVE_REGION_START_OFFSET,-0x7390 +.set SAVE_REGION_END_OFFSET,-0x738C + +.set PAYLOAD_HOOK_LOCATION,0x8023C008 + +.set wiiResetCode,0x801A7850 +.set __OSReboot,0x8027343C +.set Run_main,0x8026EBC8 +.set Run_restart,0x81333B28 + +.set relIsLoadedBoolCheck,0x8023C010 + +.set memmove,0x80258024 +.set DCFlushRange,0x8026D800 +.set ICInvalidateRange,0x8026D8B8 + +.set relWpOffset,-0x7D60 +.set NANDOpen,0x80297AAC +.set NANDRead,0x80297258 +.set OSLink,0x802723CC +.set __memAlloc,0x801A5634 +.set __memFree,0x801A56B8 +.set OSUnlink,0x802725F4 +.set NANDClose,0x80297D2C + +__start: +# Hook __OSReboot +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +bl main_pic + +main_pic: +mflr r4 +addi r4,r4,(__OSReboot_hook - main_pic) +bl writeBranch + +# Branch to wiiResetCode without returning +lis r3,wiiResetCode@h +ori r3,r3,wiiResetCode@l +mtctr r3 +bctr + +# Runs out of initial location. Relocates to arena location, sets saved region +# and patches Run in game binary. +__OSReboot_hook: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +# Save arguments for original function +mr r30,r3 +mr r31,r4 + +# Initialize static values used for addresses +lis r29,LOADER_ARENA_LOCATION@h + +# Relocate into arena memory +mr r3,r29 # LOADER_ARENA_LOCATION +bl __OSReboot_hook_pic + +__OSReboot_hook_pic: +mflr r4 +addi r4,r4,(__start - __OSReboot_hook_pic) +li r5,LOADER_SIZE +lis r12,memmove@h +ori r12,r12,memmove@l +mtlr r12 +blrl + +mr r3,r29 # LOADER_ARENA_LOCATION +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Set the saved region start and end +stw r29,SAVE_REGION_START_OFFSET(r13) +addi r3,r29,LOADER_SIZE +stw r3,SAVE_REGION_END_OFFSET(r13) + +# Set up Run hook in main binary +lis r3,Run_main@h +ori r3,r3,Run_main@l +ori r4,r29,(LOADER_ARENA_LOCATION + (Run_main_hook - __start))@l +bl writeBranch + +# Back into __OSReboot +lis r3,(__OSReboot + 4)@h +ori r12,r3,(__OSReboot + 4)@l +mtctr r12 + +# Restore arguments for original function +mr r3,r30 +mr r4,r31 + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +bctr + +# Runs out of arena location. Called when restart binary is about to be +# executed. Patch restart binary. +Run_main_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Set up Run hook in restart DOL +lis r3,Run_restart@h +ori r3,r3,Run_restart@l +lis r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@h +ori r4,r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@l +bl writeBranch + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_main + 4) - LOADER_ARENA_LOCATION + __start) + +# Runs out of arena location. Called when game binary is about to be executed. +# Relocates to low memory and patches reloaded game DOL. +Run_restart_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Relocate to low memory +lis r30,LOADER_LOWMEM_LOCATION@h +ori r3,r30,LOADER_LOWMEM_LOCATION@l +lis r4,LOADER_ARENA_LOCATION@h +li r5,LOADER_SIZE +bl (memmove - LOADER_ARENA_LOCATION + __start) + +ori r3,r30,LOADER_LOWMEM_LOCATION@l +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Place hook for payload +lis r3,PAYLOAD_HOOK_LOCATION@h +ori r3,r3,PAYLOAD_HOOK_LOCATION@l +ori r4,r30,(LOADER_LOWMEM_LOCATION + (gPayload - __start))@l +bl writeBranch + +# Place restart hook +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +lis r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@h +ori r4,r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@l +bl writeBranch + +# Change relIsLoaded to check the bool at 0x9 instead of 0x8 +lis r3,relIsLoadedBoolCheck@h +ori r3,r3,relIsLoadedBoolCheck@l +li r4,9 +stb r4,0x3(r3) +bl makeWordChangeVisible + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_restart + 4) - LOADER_ARENA_LOCATION + __start) + +writeBranch: +# Write instruction +subf r4,r3,r4 +rlwinm r4,r4,0,6,29 +lis r5,0x4800 +or r5,r5,r4 +stw r5,0(r3) + +# Make visible, tailcall +makeWordChangeVisible: +li r4,4 + +makeCodeChangesVisible: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +mr r31,r3 # address +mr r30,r4 # size + +lis r29,DCFlushRange@h +ori r5,r29,DCFlushRange@l +mtlr r5 +blrl + +ori r3,r29,ICInvalidateRange@l +mtlr r3 +mr r3,r31 # address +mr r4,r30 # size +blrl + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +gPayload: +# Original REL Loader code by SeekyCt, with various modifications by Zephiles +# If the custom rel is loaded, then exit +lwz r3,relWpOffset(r13) +lbz r0,0x9(r3) +cmpwi r0,0 +bnelr+ + +# If the game's rel is not loaded, then exit +lbz r0,0x8(r3) +cmpwi r0,0 +beqlr- + +# Push stack +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r27,0x8(sp) + +# Initialize static values used for addresses +lis r31,0x8000 +li r30,0 # Used to check if a REL file is loaded or not + +# Allocate memory for NANDFileInfo +li r4,0x8C +bl allocateMemory + +# Backup the returned address to be used for later +mr r29,r3 + +# Open the file +ori r3,r31,(LOADER_LOWMEM_LOCATION + filenameString)@l +mr r4,r29 # Pointer to NANDFileInfo +li r5,1 # NAND_ACCESS_READ +bl (NANDOpen - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +bne- freeNandFileInfo + +# Allocate 0x20 bytes of memory, as the read size must be in multiples of 0x20 bytes +li r4,0x20 # Bytes to allocate +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read from the NAND +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +li r5,0x20 # Amount of bytes to read +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the file size and adjust it to be in multiples of 0x20 bytes +lwz r28,0(r27) # File Size +addi r28,r28,31 +rlwinm r28,r28,0,0,26 + +# Free the 0x20 bytes from earlier +mr r4,r27 +bl freeMemory + +# Allocate more bytes based on the adjusted file size +mr r4,r28 +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read the REL Area of the file +# NANDRead automatically seeks to the next section +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +mr r5,r28 # Adjusted File Size +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the BSS Area size and allocate memory for it +lwz r4,0x20(r27) +bl allocateMemory + +# Backup the returned address to be used for later +mr r28,r3 + +# Link the functions in the REL +mr r3,r27 # Pointer to the Module +mr r4,r28 # Pointer to the BSS Area +bl (OSLink - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,1 # Check if something went wrong +bne- freeRemainingMemory + +# Store the BSS Area and the Module +stw r28,0x13F8(r31) # Pointer to the BSS Area +stw r27,0x13FC(r31) # Pointer to the Module + +# Get the REL Prolog Pointer +lwz r30,0x34(r27) + +# Done, so close the file +b closeNand + +# Function definitions, error handling, etc. +freeMemory: +li r3,0 # Heap to use +b (__memFree - LOADER_LOWMEM_LOCATION + __start) + +allocateMemory: +li r3,0 # Heap to use +b (__memAlloc - LOADER_LOWMEM_LOCATION + __start) + +freeRemainingMemory: +mr r3,r27 # Pointer to the Module +bl (OSUnlink - LOADER_LOWMEM_LOCATION + __start) + +# Free the BSS Area and the File Buffer +mr r4,r28 # Pointer to the BSS Area +bl freeMemory + +freeCurrentMemoryArea: +mr r4,r27 # Pointer to the File Buffer or the 0x20 Bytes Buffer +bl freeMemory + +closeNand: +mr r3,r29 # Pointer to NANDFileInfo +bl (NANDClose - LOADER_LOWMEM_LOCATION + __start) + +freeNandFileInfo: +mr r4,r29 +bl freeMemory + +# Run the REL Prolog if the load was successful +cmpwi r30,0 +beq- exit +mtlr r30 +blrl + +exit: +# Stop this code from running again and let the game continue +lwz r3,relWpOffset(r13) +li r0,1 +stb r0,0x9(r3) + +# Pop stack +lmw r27,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +filenameString: +.asciz "pcrel.bin" + +__end: \ No newline at end of file diff --git a/Source/Main_US_1.txt b/Source/Main_US_1.txt new file mode 100644 index 0000000..4863cc6 --- /dev/null +++ b/Source/Main_US_1.txt @@ -0,0 +1,370 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) + +# Modifications made by Zephiles + +# Set global function/variable/offset locations/values +.set LOADER_SIZE,(__end - __start) +.set LOADER_ARENA_LOCATION,0x81000000 +.set LOADER_LOWMEM_LOCATION,0x80000F98 + +.set SAVE_REGION_START_OFFSET,-0x7390 +.set SAVE_REGION_END_OFFSET,-0x738C + +.set PAYLOAD_HOOK_LOCATION,0x8023C6F4 + +.set wiiResetCode,0x801A78AC +.set __OSReboot,0x80273B48 +.set Run_main,0x8026F298 +.set Run_restart,0x81333B28 + +.set relIsLoadedBoolCheck,0x8023C6FC + +.set memmove,0x80258720 +.set DCFlushRange,0x8026DF00 +.set ICInvalidateRange,0x8026DF88 + +.set relWpOffset,-0x7D60 +.set NANDOpen,0x80298278 +.set NANDRead,0x80297A24 +.set OSLink,0x80272ABC +.set __memAlloc,0x801A5690 +.set __memFree,0x801A5714 +.set OSUnlink,0x80272D00 +.set NANDClose,0x802984F8 + +__start: +# Hook __OSReboot +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +bl main_pic + +main_pic: +mflr r4 +addi r4,r4,(__OSReboot_hook - main_pic) +bl writeBranch + +# Branch to wiiResetCode without returning +lis r3,wiiResetCode@h +ori r3,r3,wiiResetCode@l +mtctr r3 +bctr + +# Runs out of initial location. Relocates to arena location, sets saved region +# and patches Run in game binary. +__OSReboot_hook: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +# Save arguments for original function +mr r30,r3 +mr r31,r4 + +# Initialize static values used for addresses +lis r29,LOADER_ARENA_LOCATION@h + +# Relocate into arena memory +mr r3,r29 # LOADER_ARENA_LOCATION +bl __OSReboot_hook_pic + +__OSReboot_hook_pic: +mflr r4 +addi r4,r4,(__start - __OSReboot_hook_pic) +li r5,LOADER_SIZE +lis r12,memmove@h +ori r12,r12,memmove@l +mtlr r12 +blrl + +mr r3,r29 # LOADER_ARENA_LOCATION +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Set the saved region start and end +stw r29,SAVE_REGION_START_OFFSET(r13) +addi r3,r29,LOADER_SIZE +stw r3,SAVE_REGION_END_OFFSET(r13) + +# Set up Run hook in main binary +lis r3,Run_main@h +ori r3,r3,Run_main@l +ori r4,r29,(LOADER_ARENA_LOCATION + (Run_main_hook - __start))@l +bl writeBranch + +# Back into __OSReboot +lis r3,(__OSReboot + 4)@h +ori r12,r3,(__OSReboot + 4)@l +mtctr r12 + +# Restore arguments for original function +mr r3,r30 +mr r4,r31 + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +bctr + +# Runs out of arena location. Called when restart binary is about to be +# executed. Patch restart binary. +Run_main_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Set up Run hook in restart DOL +lis r3,Run_restart@h +ori r3,r3,Run_restart@l +lis r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@h +ori r4,r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@l +bl writeBranch + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_main + 4) - LOADER_ARENA_LOCATION + __start) + +# Runs out of arena location. Called when game binary is about to be executed. +# Relocates to low memory and patches reloaded game DOL. +Run_restart_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Relocate to low memory +lis r30,LOADER_LOWMEM_LOCATION@h +ori r3,r30,LOADER_LOWMEM_LOCATION@l +lis r4,LOADER_ARENA_LOCATION@h +li r5,LOADER_SIZE +bl (memmove - LOADER_ARENA_LOCATION + __start) + +ori r3,r30,LOADER_LOWMEM_LOCATION@l +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Place hook for payload +lis r3,PAYLOAD_HOOK_LOCATION@h +ori r3,r3,PAYLOAD_HOOK_LOCATION@l +ori r4,r30,(LOADER_LOWMEM_LOCATION + (gPayload - __start))@l +bl writeBranch + +# Place restart hook +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +lis r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@h +ori r4,r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@l +bl writeBranch + +# Change relIsLoaded to check the bool at 0x9 instead of 0x8 +lis r3,relIsLoadedBoolCheck@h +ori r3,r3,relIsLoadedBoolCheck@l +li r4,9 +stb r4,0x3(r3) +bl makeWordChangeVisible + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_restart + 4) - LOADER_ARENA_LOCATION + __start) + +writeBranch: +# Write instruction +subf r4,r3,r4 +rlwinm r4,r4,0,6,29 +lis r5,0x4800 +or r5,r5,r4 +stw r5,0(r3) + +# Make visible, tailcall +makeWordChangeVisible: +li r4,4 + +makeCodeChangesVisible: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +mr r31,r3 # address +mr r30,r4 # size + +lis r29,DCFlushRange@h +ori r5,r29,DCFlushRange@l +mtlr r5 +blrl + +ori r3,r29,ICInvalidateRange@l +mtlr r3 +mr r3,r31 # address +mr r4,r30 # size +blrl + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +gPayload: +# Original REL Loader code by SeekyCt, with various modifications by Zephiles +# If the custom rel is loaded, then exit +lwz r3,relWpOffset(r13) +lbz r0,0x9(r3) +cmpwi r0,0 +bnelr+ + +# If the game's rel is not loaded, then exit +lbz r0,0x8(r3) +cmpwi r0,0 +beqlr- + +# Push stack +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r27,0x8(sp) + +# Initialize static values used for addresses +lis r31,0x8000 +li r30,0 # Used to check if a REL file is loaded or not + +# Allocate memory for NANDFileInfo +li r4,0x8C +bl allocateMemory + +# Backup the returned address to be used for later +mr r29,r3 + +# Open the file +ori r3,r31,(LOADER_LOWMEM_LOCATION + filenameString)@l +mr r4,r29 # Pointer to NANDFileInfo +li r5,1 # NAND_ACCESS_READ +bl (NANDOpen - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +bne- freeNandFileInfo + +# Allocate 0x20 bytes of memory, as the read size must be in multiples of 0x20 bytes +li r4,0x20 # Bytes to allocate +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read from the NAND +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +li r5,0x20 # Amount of bytes to read +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the file size and adjust it to be in multiples of 0x20 bytes +lwz r28,0(r27) # File Size +addi r28,r28,31 +rlwinm r28,r28,0,0,26 + +# Free the 0x20 bytes from earlier +mr r4,r27 +bl freeMemory + +# Allocate more bytes based on the adjusted file size +mr r4,r28 +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read the REL Area of the file +# NANDRead automatically seeks to the next section +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +mr r5,r28 # Adjusted File Size +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the BSS Area size and allocate memory for it +lwz r4,0x20(r27) +bl allocateMemory + +# Backup the returned address to be used for later +mr r28,r3 + +# Link the functions in the REL +mr r3,r27 # Pointer to the Module +mr r4,r28 # Pointer to the BSS Area +bl (OSLink - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,1 # Check if something went wrong +bne- freeRemainingMemory + +# Store the BSS Area and the Module +stw r28,0x13F8(r31) # Pointer to the BSS Area +stw r27,0x13FC(r31) # Pointer to the Module + +# Get the REL Prolog Pointer +lwz r30,0x34(r27) + +# Done, so close the file +b closeNand + +# Function definitions, error handling, etc. +freeMemory: +li r3,0 # Heap to use +b (__memFree - LOADER_LOWMEM_LOCATION + __start) + +allocateMemory: +li r3,0 # Heap to use +b (__memAlloc - LOADER_LOWMEM_LOCATION + __start) + +freeRemainingMemory: +mr r3,r27 # Pointer to the Module +bl (OSUnlink - LOADER_LOWMEM_LOCATION + __start) + +# Free the BSS Area and the File Buffer +mr r4,r28 # Pointer to the BSS Area +bl freeMemory + +freeCurrentMemoryArea: +mr r4,r27 # Pointer to the File Buffer or the 0x20 Bytes Buffer +bl freeMemory + +closeNand: +mr r3,r29 # Pointer to NANDFileInfo +bl (NANDClose - LOADER_LOWMEM_LOCATION + __start) + +freeNandFileInfo: +mr r4,r29 +bl freeMemory + +# Run the REL Prolog if the load was successful +cmpwi r30,0 +beq- exit +mtlr r30 +blrl + +exit: +# Stop this code from running again and let the game continue +lwz r3,relWpOffset(r13) +li r0,1 +stb r0,0x9(r3) + +# Pop stack +lmw r27,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +filenameString: +.asciz "pcrel.bin" + +__end: \ No newline at end of file diff --git a/Source/Main_US_2.txt b/Source/Main_US_2.txt new file mode 100644 index 0000000..4bd4b60 --- /dev/null +++ b/Source/Main_US_2.txt @@ -0,0 +1,370 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright 2020 Linus S. (aka PistonMiner) + +# Modifications made by Zephiles + +# Set global function/variable/offset locations/values +.set LOADER_SIZE,(__end - __start) +.set LOADER_ARENA_LOCATION,0x81000000 +.set LOADER_LOWMEM_LOCATION,0x80000F98 + +.set SAVE_REGION_START_OFFSET,-0x7390 +.set SAVE_REGION_END_OFFSET,-0x738C + +.set PAYLOAD_HOOK_LOCATION,0x8023CA18 + +.set wiiResetCode,0x801A7BC4 +.set __OSReboot,0x80273BE8 +.set Run_main,0x8026F338 +.set Run_restart,0x81333B28 + +.set relIsLoadedBoolCheck,0x8023CA20 + +.set memmove,0x802587C4 +.set DCFlushRange,0x8026DFA0 +.set ICInvalidateRange,0x8026E028 + +.set relWpOffset,-0x7D60 +.set NANDOpen,0x80298318 +.set NANDRead,0x80297AC4 +.set OSLink,0x80272B5C +.set __memAlloc,0x801A59A8 +.set __memFree,0x801A5A2C +.set OSUnlink,0x80272DA0 +.set NANDClose,0x80298598 + +__start: +# Hook __OSReboot +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +bl main_pic + +main_pic: +mflr r4 +addi r4,r4,(__OSReboot_hook - main_pic) +bl writeBranch + +# Branch to wiiResetCode without returning +lis r3,wiiResetCode@h +ori r3,r3,wiiResetCode@l +mtctr r3 +bctr + +# Runs out of initial location. Relocates to arena location, sets saved region +# and patches Run in game binary. +__OSReboot_hook: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +# Save arguments for original function +mr r30,r3 +mr r31,r4 + +# Initialize static values used for addresses +lis r29,LOADER_ARENA_LOCATION@h + +# Relocate into arena memory +mr r3,r29 # LOADER_ARENA_LOCATION +bl __OSReboot_hook_pic + +__OSReboot_hook_pic: +mflr r4 +addi r4,r4,(__start - __OSReboot_hook_pic) +li r5,LOADER_SIZE +lis r12,memmove@h +ori r12,r12,memmove@l +mtlr r12 +blrl + +mr r3,r29 # LOADER_ARENA_LOCATION +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Set the saved region start and end +stw r29,SAVE_REGION_START_OFFSET(r13) +addi r3,r29,LOADER_SIZE +stw r3,SAVE_REGION_END_OFFSET(r13) + +# Set up Run hook in main binary +lis r3,Run_main@h +ori r3,r3,Run_main@l +ori r4,r29,(LOADER_ARENA_LOCATION + (Run_main_hook - __start))@l +bl writeBranch + +# Back into __OSReboot +lis r3,(__OSReboot + 4)@h +ori r12,r3,(__OSReboot + 4)@l +mtctr r12 + +# Restore arguments for original function +mr r3,r30 +mr r4,r31 + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +bctr + +# Runs out of arena location. Called when restart binary is about to be +# executed. Patch restart binary. +Run_main_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Set up Run hook in restart DOL +lis r3,Run_restart@h +ori r3,r3,Run_restart@l +lis r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@h +ori r4,r4,(LOADER_ARENA_LOCATION + (Run_restart_hook - __start))@l +bl writeBranch + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_main + 4) - LOADER_ARENA_LOCATION + __start) + +# Runs out of arena location. Called when game binary is about to be executed. +# Relocates to low memory and patches reloaded game DOL. +Run_restart_hook: +# This is just before loading a new DOL which will reset everything, so we +# don't need to worry about preserving register contents + +# Save entrypoint +mr r31,r3 + +# Relocate to low memory +lis r30,LOADER_LOWMEM_LOCATION@h +ori r3,r30,LOADER_LOWMEM_LOCATION@l +lis r4,LOADER_ARENA_LOCATION@h +li r5,LOADER_SIZE +bl (memmove - LOADER_ARENA_LOCATION + __start) + +ori r3,r30,LOADER_LOWMEM_LOCATION@l +li r4,LOADER_SIZE +bl makeCodeChangesVisible + +# Place hook for payload +lis r3,PAYLOAD_HOOK_LOCATION@h +ori r3,r3,PAYLOAD_HOOK_LOCATION@l +ori r4,r30,(LOADER_LOWMEM_LOCATION + (gPayload - __start))@l +bl writeBranch + +# Place restart hook +lis r3,__OSReboot@h +ori r3,r3,__OSReboot@l +lis r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@h +ori r4,r4,(LOADER_LOWMEM_LOCATION + (__OSReboot_hook - __start))@l +bl writeBranch + +# Change relIsLoaded to check the bool at 0x9 instead of 0x8 +lis r3,relIsLoadedBoolCheck@h +ori r3,r3,relIsLoadedBoolCheck@l +li r4,9 +stb r4,0x3(r3) +bl makeWordChangeVisible + +# Restore entrypoint +mr r3,r31 + +# Back into Run +b ((Run_restart + 4) - LOADER_ARENA_LOCATION + __start) + +writeBranch: +# Write instruction +subf r4,r3,r4 +rlwinm r4,r4,0,6,29 +lis r5,0x4800 +or r5,r5,r4 +stw r5,0(r3) + +# Make visible, tailcall +makeWordChangeVisible: +li r4,4 + +makeCodeChangesVisible: +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r29,0x8(sp) + +mr r31,r3 # address +mr r30,r4 # size + +lis r29,DCFlushRange@h +ori r5,r29,DCFlushRange@l +mtlr r5 +blrl + +ori r3,r29,ICInvalidateRange@l +mtlr r3 +mr r3,r31 # address +mr r4,r30 # size +blrl + +lmw r29,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +gPayload: +# Original REL Loader code by SeekyCt, with various modifications by Zephiles +# If the custom rel is loaded, then exit +lwz r3,relWpOffset(r13) +lbz r0,0x9(r3) +cmpwi r0,0 +bnelr+ + +# If the game's rel is not loaded, then exit +lbz r0,0x8(r3) +cmpwi r0,0 +beqlr- + +# Push stack +stwu sp,-0x20(sp) +mflr r0 +stw r0,0x24(sp) +stmw r27,0x8(sp) + +# Initialize static values used for addresses +lis r31,0x8000 +li r30,0 # Used to check if a REL file is loaded or not + +# Allocate memory for NANDFileInfo +li r4,0x8C +bl allocateMemory + +# Backup the returned address to be used for later +mr r29,r3 + +# Open the file +ori r3,r31,(LOADER_LOWMEM_LOCATION + filenameString)@l +mr r4,r29 # Pointer to NANDFileInfo +li r5,1 # NAND_ACCESS_READ +bl (NANDOpen - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +bne- freeNandFileInfo + +# Allocate 0x20 bytes of memory, as the read size must be in multiples of 0x20 bytes +li r4,0x20 # Bytes to allocate +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read from the NAND +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +li r5,0x20 # Amount of bytes to read +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the file size and adjust it to be in multiples of 0x20 bytes +lwz r28,0(r27) # File Size +addi r28,r28,31 +rlwinm r28,r28,0,0,26 + +# Free the 0x20 bytes from earlier +mr r4,r27 +bl freeMemory + +# Allocate more bytes based on the adjusted file size +mr r4,r28 +bl allocateMemory + +# Backup the returned address to be used for later +mr r27,r3 + +# Read the REL Area of the file +# NANDRead automatically seeks to the next section +mr r3,r29 # Pointer to NANDFileInfo +mr r4,r27 # Pointer to the File Buffer +mr r5,r28 # Adjusted File Size +bl (NANDRead - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,0 # Check if something went wrong +blt- freeCurrentMemoryArea + +# Get the BSS Area size and allocate memory for it +lwz r4,0x20(r27) +bl allocateMemory + +# Backup the returned address to be used for later +mr r28,r3 + +# Link the functions in the REL +mr r3,r27 # Pointer to the Module +mr r4,r28 # Pointer to the BSS Area +bl (OSLink - LOADER_LOWMEM_LOCATION + __start) +cmpwi r3,1 # Check if something went wrong +bne- freeRemainingMemory + +# Store the BSS Area and the Module +stw r28,0x13F8(r31) # Pointer to the BSS Area +stw r27,0x13FC(r31) # Pointer to the Module + +# Get the REL Prolog Pointer +lwz r30,0x34(r27) + +# Done, so close the file +b closeNand + +# Function definitions, error handling, etc. +freeMemory: +li r3,0 # Heap to use +b (__memFree - LOADER_LOWMEM_LOCATION + __start) + +allocateMemory: +li r3,0 # Heap to use +b (__memAlloc - LOADER_LOWMEM_LOCATION + __start) + +freeRemainingMemory: +mr r3,r27 # Pointer to the Module +bl (OSUnlink - LOADER_LOWMEM_LOCATION + __start) + +# Free the BSS Area and the File Buffer +mr r4,r28 # Pointer to the BSS Area +bl freeMemory + +freeCurrentMemoryArea: +mr r4,r27 # Pointer to the File Buffer or the 0x20 Bytes Buffer +bl freeMemory + +closeNand: +mr r3,r29 # Pointer to NANDFileInfo +bl (NANDClose - LOADER_LOWMEM_LOCATION + __start) + +freeNandFileInfo: +mr r4,r29 +bl freeMemory + +# Run the REL Prolog if the load was successful +cmpwi r30,0 +beq- exit +mtlr r30 +blrl + +exit: +# Stop this code from running again and let the game continue +lwz r3,relWpOffset(r13) +li r0,1 +stb r0,0x9(r3) + +# Pop stack +lmw r27,0x8(sp) +lwz r0,0x24(sp) +mtlr r0 +addi sp,sp,0x20 +blr + +filenameString: +.asciz "pcrel.bin" + +__end: \ No newline at end of file diff --git a/bin/Init_KR.bin b/bin/Init_KR.bin new file mode 100644 index 0000000..61f2538 Binary files /dev/null and b/bin/Init_KR.bin differ diff --git a/bin/Init_Main.bin b/bin/Init_Main.bin new file mode 100644 index 0000000..9e4ce70 Binary files /dev/null and b/bin/Init_Main.bin differ diff --git a/bin/Main_EU.bin b/bin/Main_EU.bin new file mode 100644 index 0000000..9c121e4 Binary files /dev/null and b/bin/Main_EU.bin differ diff --git a/bin/Main_JP_0.bin b/bin/Main_JP_0.bin new file mode 100644 index 0000000..409abad Binary files /dev/null and b/bin/Main_JP_0.bin differ diff --git a/bin/Main_JP_1.bin b/bin/Main_JP_1.bin new file mode 100644 index 0000000..fb35fe9 Binary files /dev/null and b/bin/Main_JP_1.bin differ diff --git a/bin/Main_KR.bin b/bin/Main_KR.bin new file mode 100644 index 0000000..b7e184a Binary files /dev/null and b/bin/Main_KR.bin differ diff --git a/bin/Main_US_0.bin b/bin/Main_US_0.bin new file mode 100644 index 0000000..47f8129 Binary files /dev/null and b/bin/Main_US_0.bin differ diff --git a/bin/Main_US_1.bin b/bin/Main_US_1.bin new file mode 100644 index 0000000..6c7f0df Binary files /dev/null and b/bin/Main_US_1.bin differ diff --git a/bin/Main_US_2.bin b/bin/Main_US_2.bin new file mode 100644 index 0000000..d8ae256 Binary files /dev/null and b/bin/Main_US_2.bin differ diff --git a/binarybuffer.py b/binarybuffer.py new file mode 100644 index 0000000..f6a91a9 --- /dev/null +++ b/binarybuffer.py @@ -0,0 +1,56 @@ +# Copyright 2021 Seeky +# Licensed under GPLv2+ + +class BinaryBuffer: + def __init__(self, data): + self.data = bytearray(data[:]) + + def writeat(self, offset, data): + for i, val in enumerate(data): + self.data[offset+i] = val + + def readat(self, offset, length): + out = bytearray() + for i in range(0, length): + out.append(self.data[offset+i]) + return out + + def writeWord(self, offset, val): + b = int.to_bytes(val, 4, 'big') + self.writeat(offset, b) + + def writeHalfword(self, offset, val): + b = int.to_bytes(val, 2, 'big') + self.writeat(offset, b) + + def writeByte(self, offset, val): + b = int.to_bytes(val, 1, 'big') + self.writeat(offset, b) + + def writeStr(self, offset, str): + i = 0 + for c in str: + self.data[offset+i] = ord(c) + i += 1 + self.data[offset+i] = 0 + + def readWord(self, offset): + return int.from_bytes(self.readat(offset, 4), 'big') + + def readHalfword(self, offset): + return int.from_bytes(self.readat(offset, 2), 'big') + + def readByte(self, offset): + return int.from_bytes(self.readat(offset, 1), 'big') + + def readStr(self, offset, encoding='ascii'): + out = bytearray() + i = 0 + while True: + c = self.data[offset+i] + if c == 0: + break + i += 1 + out.append(c) + return out.decode(encoding=encoding) + diff --git a/bn.py b/bn.py new file mode 100644 index 0000000..bb8bd1a --- /dev/null +++ b/bn.py @@ -0,0 +1,77 @@ +# Copyright 2010 Dolphin Emulator Project +# Licensed under GPLv2+ + +# Copyright 2007,2008 Segher Boessenkool +# Licensed under the terms of the GNU GPL, version 2 +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +# Ported to Python by Seeky + +def zero(d, n): + for i in range(0, n): + d[i] = 0 + +def copy(d, a, n): + d[:n] = a[:n] + +def compare(a, b, n): + for i in range(0, n): + if a[i] > b[i]: + return 1 + if a[i] < b[i]: + return -1 + return 0 + +def sub_modulus(a, N, n): + c = 0 + for i in range(n - 1, -1, -1): + dig = N[i] + c + c = (a[i] < dig) + a[i] = (a[i] - dig) & 0xff + +def add(d, a, b, N, n): + c = 0 + for i in range(n - 1, -1, -1): + dig = a[i] + b[i] + c + c = (dig >= 0x100) + d[i] = dig & 0xff + + if c: + sub_modulus(d, N, n) + if compare(d, N, n) >= 0: + sub_modulus(d, N, n) + +def mul(d, a, b, N, n): + zero(d, n) + + for i in range(0, n): + mask = 0x80 + while mask != 0: + add(d, d, d, N, n) + if a[i] & mask: + add(d, d, b, N, n) + mask >>= 1 + +def exp(d, a, N, n, e, en): + t = bytearray(512) + zero(d, n) + d[n - 1] = 1 + for i in range(0, en): + mask = 0x80 + while mask != 0: + mul(t, d, d, N, n) + if e[i] & mask: + mul(d, t, a, N, n) + else: + copy(d, t, n) + mask >>= 1 + +def inv(d, a, N, n): + t = bytearray(512) + s = bytearray(512) + + copy(t, N, n) + zero(s, n) + s[n - 1] = 2 + sub_modulus(t, s, n) + exp(d, a, N, n, t, n) diff --git a/commondefs.py b/commondefs.py new file mode 100644 index 0000000..5d525ed --- /dev/null +++ b/commondefs.py @@ -0,0 +1,88 @@ +########## +# Crypto # +########## + +SD_KEY = bytearray([ + 0xab, 0x01, 0xb9, 0xd8, 0xe1, 0x62, 0x2b, 0x08, + 0xaf, 0xba, 0xd8, 0x4d, 0xbf, 0xc2, 0xa5, 0x5d +]) +SD_IV = bytearray([ + 0x21, 0x67, 0x12, 0xe6, 0xaa, 0x1f, 0x68, 0x9f, + 0x95, 0xc5, 0xa2, 0x23, 0x24, 0xdc, 0x6a, 0x98 +]) +IV_SIZE = len(SD_IV) + +DEFAULT_DEVICE_ID = 0x0403AC68 +DEFAULT_KEY_ID = 0x6AAB8C59 +DEFAULT_PRIVATE_KEY = bytearray([ + 0x00, 0xAB, 0xEE, 0xC1, 0xDD, 0xB4, 0xA6, 0x16, 0x6B, 0x70, 0xFD, 0x7E, 0x56, 0x67, 0x70, 0x57, + 0x55, 0x27, 0x38, 0xA3, 0x26, 0xC5, 0x46, 0x16, 0xF7, 0x62, 0xC9, 0xED, 0x73, 0xF2 +]) +# DEFAULT_PUBLIC_KEY = ec.privToPub(commondefs.DEFAULT_PRIVATE_KEY) +DEFAULT_PUBLIC_KEY = bytearray([ # generating the public key can take a while, so it's pre-calculated + 0x01, 0x04, 0x0b, 0xe0, 0x46, 0xea, 0x95, 0x19, 0xf2, 0x85, 0x9b, 0x0d, 0x94, 0x29, 0xa2, 0xc6, + 0x91, 0x80, 0x15, 0x89, 0x8f, 0x2e, 0xba, 0x20, 0xcf, 0xfd, 0xb3, 0x16, 0x4f, 0x0c, 0x01, 0x38, + 0xc5, 0xd2, 0x2f, 0xc1, 0xe9, 0xee, 0x17, 0x6c, 0x2d, 0x8f, 0xa4, 0x74, 0xb0, 0xe9, 0x38, 0x66, + 0x6e, 0x60, 0xcf, 0x06, 0xd5, 0x08, 0x7a, 0xc2, 0x4f, 0x01, 0x39, 0x79 +]) +DEFAULT_SIGNATURE = bytearray([ + 0x00, 0xD8, 0x81, 0x63, 0xB2, 0x00, 0x6B, 0x0B, 0x54, 0x82, 0x88, 0x63, 0x81, 0x1C, 0x00, 0x71, + 0x12, 0xED, 0xB7, 0xFD, 0x21, 0xAB, 0x0E, 0x50, 0x0E, 0x1F, 0xBF, 0x78, 0xAD, 0x37, 0x00, 0x71, + 0x8D, 0x82, 0x41, 0xEE, 0x45, 0x11, 0xC7, 0x3B, 0xAC, 0x08, 0xB6, 0x83, 0xDC, 0x05, 0xB8, 0xA8, + 0x90, 0x1F, 0xA8, 0x2A, 0x0E, 0x4E, 0x76, 0xEF, 0x44, 0x72, 0x99, 0xF8 +]) + +SIGNATURE_END_MAGIC = 0x2f536969 + +################## +# Cert Structure # +################## + +CERT_SIG_TYPE_ECC = 0x10002 +CERT_KEY_TYPE_ECC = 2 + +CERT_SIG_TYPE_OFFSET = 0x0 +CERT_SIG_OFFSET = 0x4 +CERT_ISSUER_OFFSET = 0x80 +CERT_KEY_TYPE_OFFSET = 0xc0 +CERT_NAME_OFFSET = 0xc4 +CERT_KEY_ID_OFFSET = 0x104 +CERT_PUBLIC_KEY_OFFSET = 0x108 + +################## +# File Structure # +################## + +HEADER_SIZE = 0xf0c0 + +BK_SIZE = 0x80 +BK_TRUE_SIZE = 0x70 +BK_MAGIC = 0x426b0001 + +BK_SIZE_OFFSET = 0x0 +BK_MAGIC_OFFSET = 0x4 +BK_NG_ID_OFFSET = 0x8 +BK_FILE_COUNT_OFFSET = 0xc +BK_FILE_SIZE_OFFSET = 0x10 +BK_TOTAL_SIZE_OFFSET = 0x1c +BK_TITLE_ID_OFFSET = 0x60 + +FILE_HEADER_SIZE = 0x80 +FILE_MAGIC = 0x3adf17e +FILE_NAME_SIZE = 0x44 + +FILE_TYPE_FILE = 1 +FILE_TYPE_DIR = 2 + +FILE_MAGIC_OFFSET = 0x0 +FILE_SIZE_OFFSET = 0x4 +FILE_PERM_OFFSET = 0x8 +FILE_ATTR_OFFSET = 0x9 +FILE_TYPE_OFFSET = 0xa +FILE_NAME_OFFSET = 0xb +FILE_IV_OFFSET = 0x50 + +SIG_SIZE = 0x40 +NG_CERT_SIZE = 0x180 +AP_CERT_SIZE = 0x180 +TOTAL_CERTS_SIZE = SIG_SIZE + NG_CERT_SIZE + AP_CERT_SIZE diff --git a/ec.py b/ec.py new file mode 100644 index 0000000..8e00363 --- /dev/null +++ b/ec.py @@ -0,0 +1,253 @@ +# Copyright 2010 Dolphin Emulator Project +# Licensed under GPLv2+ + +# Copyright 2007,2008 Segher Boessenkool +# Licensed under the terms of the GNU GPL, version 2 +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +# Ported to Python by Seeky + +from copy import deepcopy +from random import randint +import bn + +def dumpBytes(b): + for i in range(0, len(b)): + if i % 8 == 0 and i > 0: + print('') + print(f"{b[i]:02x} ", end='') + print('') + +square = [0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15, + 0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55] + +class Elt(): + def __init__(self): + self.data = bytearray(30) + + def isZero(self): + for d in self.data: + if d != 0: + return False + return True + + def mulX(self): + carry = self.data[0] & 1 + x = 0 + for i in range(0, len(self.data) - 1): + y = self.data[i + 1] + self.data[i] = (x ^ (y >> 7)) & 0xff + x = (y << 1) & 0xff + self.data[29] = x ^ carry + self.data[20] ^= (carry << 2) & 0xff + + def square(self): + wide = bytearray(60) + for i in range(0, len(self.data)): + wide[2 * i] = square[self.data[i] >> 4] + wide[2 * i + 1] = square[self.data[i] & 15] + for i in range(0, len(self.data)): + x = wide[i] + wide[i + 19] ^= x >> 7 + wide[i + 20] ^= (x << 1) & 0xff + wide[i + 29] ^= x >> 1 + wide[i + 30] ^= (x << 7) & 0xff + + x = wide[30] & ~1 + wide[49] ^= x >> 7 + wide[50] ^= (x << 1) & 0xff + wide[59] ^= x >> 1 + wide[30] &= 1 + + ret = Elt() + ret.data = wide[30:] + return ret + + def itohTsujii(self, b, j): + t = Elt() + t.data = self.data[:] + while j != 0: + t = t.square() + j -= 1 + return t.mult(b) + + def inv(self): + t = self.itohTsujii(self, 1) + s = t.itohTsujii(self, 1) + t = s.itohTsujii(s, 3) + s = t.itohTsujii(self, 1) + t = s.itohTsujii(s, 7) + s = t.itohTsujii(t, 14) + t = s.itohTsujii(self, 1) + s = t.itohTsujii(t, 29) + t = s.itohTsujii(s, 58) + s = t.itohTsujii(t, 116) + return s.square() + + def add(self, b): + d = Elt() + for i in range(0, 30): + d.data[i] = self.data[i] ^ b.data[i] + return d + + def mult(self, b): + d = Elt() + i = 0 + mask = 1 + for n in range(0, 233): + d.mulX() + if (self.data[i] & mask) != 0: + d = d.add(b) + mask >>= 1 + if mask == 0: + mask = 0x80 + i += 1 + return d + + def div(self, b): + return self.mult(b.inv()) + +class Point: + def __init__(self): + self.x = Elt() + self.y = Elt() + + def init_copy(self, data): + self.x.data = data[:30] + self.y.data = data[30:60] + + def init_copy2(self, eltX, eltY): + self.x.data = eltX.data[:] + self.y.data = eltY.data[:] + + def isZero(self): + return self.x.isZero() and self.y.isZero() + + def double(self): + r = Point() + if self.x.isZero(): + return r + s = self.y.div(self.x).add(self.x) + r.x = s.square().add(s) + r.x.data[29] ^= 1 + r.y = s.mult(r.x).add(r.x).add(self.x.square()) + return r + + def add(self, b): + if self.isZero(): + return deepcopy(b) + if b.isZero(): + return deepcopy(self) + + u = self.x.add(b.x) + if u.isZero(): + u = self.y.add(b.y) + if u.isZero(): + return self.double() + return Point() + + s = self.y.add(b.y).div(u) + t = s.square().add(s).add(b.x) + t.data[29] ^= 1 + rx = t.add(self.x) + ry = s.mult(t).add(self.y).add(rx) + ret = Point() + ret.init_copy2(rx, ry) + return ret + +def pointMult(a, b): + d = Point() + for i in range(0, 30): + mask = 0x80 + while mask != 0: + d = d.double() + if (a[i] & mask): + d = d.add(b) + mask >>= 1 + return d + + +# order of the addition group of points +ec_N = bytearray([ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xe9, 0x74, 0xe7, 0x2f, + 0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7 +]) + +# base point +ec_G = Point() +ec_G.init_copy([ + # X + 0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1, 0xbb, 0x75, 0x5f, + 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, + + # Y + 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19, 0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf, + 0x8a, 0x0b, 0xef, 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52 +]) + +def sign(key, hash): + e = bytearray(30) + e[10:] = hash[:20] + + m = bytearray(30) + while True: + for i in range(0, 30): + m[i] = randint(0, 0xff) + m[0] &= 1 + if bn.compare(m, ec_N, 30) < 0: + break + + r = pointMult(m, ec_G).x + if bn.compare(r.data, ec_N, 30) >= 0: + bn.sub_modulus(r.data, ec_N, 30) + + # S = m**-1*(e + Rk) (mod N) + + kk = bytearray(30) + kk = key[:30] + if bn.compare(kk, ec_N, 30) >= 0: + bn.sub_modulus(kk, ec_N, 30) + + s = Elt() + bn.mul(s.data, r.data, kk, ec_N, 30) + bn.add(kk, s.data, e, ec_N, 30) + + minv = bytearray(30) + bn.inv(minv, m, ec_N, 30) + bn.mul(s.data, minv, kk, ec_N, 30) + + signature = bytearray(60) + signature[:30] = r.data[:] + signature[30:] = s.data[:] + return signature + +def verifySignature(publicKey, signature, hash): + R = signature[:30] + S = signature[30:] + + Sinv = bytearray(30) + bn.inv(Sinv, S, ec_N, 30) + e = bytearray(30) + e[10:] = hash[:20] + + w1 = bytearray(30) + w2 = bytearray(30) + bn.mul(w1, e, Sinv, ec_N, 30) + bn.mul(w2, R, Sinv, ec_N, 30) + + publicKeyPoint = Point() + publicKeyPoint.init_copy(publicKey) + r1 = pointMult(w1, ec_G).add(pointMult(w2, publicKeyPoint)) + rx = r1.x.data + if bn.compare(rx, ec_N, 30) >= 0: + bn.sub_modulus(rx, ec_N, 30) + + return bn.compare(rx, R, 30) == 0 + +def privToPub(key): + data = pointMult(key, ec_G) + result = bytearray(60) + result[:30] = data.x.data[:] + result[30:] = data.y.data[:] + return result diff --git a/inject.py b/inject.py new file mode 100644 index 0000000..60f8c59 --- /dev/null +++ b/inject.py @@ -0,0 +1,446 @@ +# Copyright 2010 Dolphin Emulator Project +# Licensed under GPLv2+ + +# Copyright 2007,2008 Segher Boessenkool +# Licensed under the terms of the GNU GPL, version 2 +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +# Copyright 2021 Zephiles, Seeky + +from hashlib import sha1 +from sys import argv + +from Crypto.Cipher import AES + +from binarybuffer import BinaryBuffer +import commondefs +import ec + +# Based off of SaveFileHack.py by Zephiles +def generateRelLoader(VersionString): + # Set version-specific values + if (VersionString == "eu0") or (VersionString == "eu1"): + InitAsmFunctionPointer = 0x80526294 + TextBufferPointerOffset = 0x1B0 + TextBufferPointer = 0x805256FC + BinVersion = "EU" + ItemId = 0x6E59 + elif VersionString == "jp0": + InitAsmFunctionPointer = 0x804B8594 + TextBufferPointerOffset = 0x17C + TextBufferPointer = 0x804B79C8 + BinVersion = "JP_0" + ItemId = 0x6D0A + elif VersionString == "jp1": + InitAsmFunctionPointer = 0x804B9B94 + TextBufferPointerOffset = 0x174 + TextBufferPointer = 0x804B8FC0 + BinVersion = "JP_1" + ItemId = 0x6D24 + elif VersionString == "kr0": + InitAsmFunctionPointer = 0x8055DBF4 + TextBufferPointerOffset = 0x170 + TextBufferPointer = 0x8055D01C + BinVersion = "KR" + ItemId = 0x70D9 + elif VersionString == "us0": + InitAsmFunctionPointer = 0x804E3294 + TextBufferPointerOffset = 0x1A4 + TextBufferPointer = 0x804E26F0 + BinVersion = "US_0" + ItemId = 0x6D08 + elif VersionString == "us1": + InitAsmFunctionPointer = 0x804E4B14 + TextBufferPointerOffset = 0x1AC + TextBufferPointer = 0x804E3F78 + BinVersion = "US_1" + ItemId = 0x6D26 + elif VersionString == "us2": + InitAsmFunctionPointer = 0x804E4C94 + TextBufferPointerOffset = 0x174 + TextBufferPointer = 0x804E40C0 + BinVersion = "US_2" + ItemId = 0x6D24 + + # Create a cleared file + ret = BinaryBuffer(bytearray(0x25b8)) + + # Set the default efb width and height, as they can apparently effect being able to open the item menu + ret.writeHalfword(0x20, 608) + ret.writeHalfword(0x22, 480) + + # Write the new file name + ret.writeStr(0x28, "REL Loader") + + # Write the map name + ret.writeStr(0x4c, "dos_01") + + # Set Mario's level to 1, to prevent leveling up immediately + ret.writeWord(0x1b14, 1) + + # Set the flip timer to 10 to prevent counting up immediately + ret.writeWord(0x1b24, 10) + + # Write the item id + ret.writeHalfword(0x1b70, ItemId) + + # Write the pointer to text buffer + ret.writeWord(TextBufferPointerOffset, TextBufferPointer) + + # Write the text buffer + ret.writeat(TextBufferPointerOffset + 4, [0x33] * 0x94) + + # Write the pointer to the init asm function + ret.writeWord(TextBufferPointerOffset + 0x98, InitAsmFunctionPointer) + + # Write the init asm function + # The init function is the same for all versions except for Korean + if VersionString == "kr0": + InitAsmFuncBinName = "Init_KR" + else: + InitAsmFuncBinName = "Init_Main" + + InitAsmFuncOffset = 0xD4C + with open("bin/" + InitAsmFuncBinName + ".bin", "rb") as g: + ret.writeat(InitAsmFuncOffset, g.read()) + + # Write the main asm function + with open("bin/Main_" + BinVersion + ".bin", "rb") as g: + ret.writeat(InitAsmFuncOffset + 0x24, g.read()) + + # Get the sum of the bytes for the data field + DataFieldSum = sum(ret.data[:0x25b0]) + (0xff * 4) + + # Set the checksum of the bytes for the data field + ret.writeWord(0x25b0, DataFieldSum) + + # Set the inverted checksum of the bytes for the data field + ret.writeWord(0x25b4, ~DataFieldSum & 0xffffffff) + + return ret.data + +def makeBlankEccCert(issuer, name, publicKey, keyId): + cert = BinaryBuffer(bytearray(0x180)) + cert.writeWord(commondefs.CERT_SIG_TYPE_OFFSET, commondefs.CERT_SIG_TYPE_ECC) # sig type ECC + cert.writeStr(commondefs.CERT_ISSUER_OFFSET, issuer) + cert.writeWord(commondefs.CERT_KEY_TYPE_OFFSET, commondefs.CERT_KEY_TYPE_ECC) # key type ECC + cert.writeStr(commondefs.CERT_NAME_OFFSET, name) + cert.writeWord(commondefs.CERT_KEY_ID_OFFSET, keyId) # key id + cert.writeat(commondefs.CERT_PUBLIC_KEY_OFFSET, publicKey) # public key + return cert + +class SaveFile(BinaryBuffer): + def __init__(self, name, data, perm, attr): + self.name = name + self.data = bytearray(data) + self.perm = perm + self.attr = attr + +def unpackDataBin(dataBinPath): + ######### + # Setup # + ######### + + dataBin = open(dataBinPath, 'rb') + files = [] + + ############### + # Main Header # + ############### + + header = dataBin.read(commondefs.HEADER_SIZE) + + ############# + # Bk Header # + ############# + + _bk = dataBin.read(commondefs.BK_SIZE) + bk = BinaryBuffer(_bk) + + fileCount = bk.readWord(commondefs.BK_FILE_COUNT_OFFSET) + _titleId = bk.readat(commondefs.BK_TITLE_ID_OFFSET, 8) + titleId = "" + for c in _titleId: + titleId += f"{c:02x}" + + ######### + # Files # + ######### + + for i in range(0, fileCount): + curHeader = BinaryBuffer(dataBin.read(commondefs.FILE_HEADER_SIZE)) + + # directories currently aren't supported, since SPM doesn't use them + assert curHeader.readByte(commondefs.FILE_TYPE_OFFSET) == commondefs.FILE_TYPE_FILE, "Directories are not supported" + + curSize = curHeader.readWord(commondefs.FILE_SIZE_OFFSET) + trueSize = (curSize + 63) & ~63 + + curName = curHeader.readStr(commondefs.FILE_NAME_OFFSET) + curIV = curHeader.readat(commondefs.FILE_IV_OFFSET, commondefs.IV_SIZE) + + cipher = AES.new(commondefs.SD_KEY, AES.MODE_CBC, iv=curIV) + curData = cipher.decrypt(dataBin.read(trueSize)) + + files.append(SaveFile( + curName, + curData[:curSize], + curHeader.readByte(commondefs.FILE_PERM_OFFSET), + curHeader.readByte(commondefs.FILE_ATTR_OFFSET) + )) + + dataBin.close() + + return header, titleId, files + +def repackDataBin(outPath, header, titleId, files): + ######### + # Setup # + ######### + + outBin = open(outPath, 'wb') + filesHashWork = sha1() + + ############### + # Main Header # + ############### + + outBin.write(header) + + ################# + # Prepare Files # + ################# + + fileSize = 0 + for file in files: + fileSize += ((len(file.data) + 63) & ~63) + commondefs.FILE_HEADER_SIZE + + ############# + # Bk Header # + ############# + + # Unknown fields and mac address left as 0 + bk = BinaryBuffer(bytearray(commondefs.BK_SIZE)) + bk.writeWord(commondefs.BK_SIZE_OFFSET, commondefs.BK_TRUE_SIZE) + bk.writeWord(commondefs.BK_MAGIC_OFFSET, commondefs.BK_MAGIC) + bk.writeWord(commondefs.BK_NG_ID_OFFSET, commondefs.DEFAULT_DEVICE_ID) + bk.writeWord(commondefs.BK_FILE_COUNT_OFFSET, len(files)) + bk.writeWord(commondefs.BK_FILE_SIZE_OFFSET, fileSize) + bk.writeWord(commondefs.BK_TOTAL_SIZE_OFFSET, commondefs.BK_SIZE + fileSize + commondefs.TOTAL_CERTS_SIZE) + for i in range(0, len(titleId), 2): # convert from hex string to bytes + h = titleId[i] + titleId[i+1] + bk.writeByte(commondefs.BK_TITLE_ID_OFFSET+(i//2), int(h, 16)) + outBin.write(bk.data) + filesHashWork.update(bk.data) + + ############# + # Add Files # + ############# + + for i, file in enumerate(files): + curHeader = BinaryBuffer(bytearray(commondefs.FILE_HEADER_SIZE)) + curHeader.writeWord(commondefs.FILE_MAGIC_OFFSET, commondefs.FILE_MAGIC) + curHeader.writeWord(commondefs.FILE_SIZE_OFFSET, len(file.data)) + curHeader.writeByte(commondefs.FILE_PERM_OFFSET, files[i].perm) + curHeader.writeByte(commondefs.FILE_ATTR_OFFSET, files[i].attr) + # directories currently aren't supported, since SPM doesn't use them + curHeader.writeByte(commondefs.FILE_TYPE_OFFSET, commondefs.FILE_TYPE_FILE) + curHeader.writeStr(commondefs.FILE_NAME_OFFSET, files[i].name) + curHeader.writeat(commondefs.FILE_IV_OFFSET, commondefs.SD_IV) + + outBin.write(curHeader.data) + filesHashWork.update(curHeader.data) + + trueSize = (len(file.data) + 63) & ~63 + while len(file.data) < trueSize: + file.data.append(0) + + cipher = AES.new(commondefs.SD_KEY, AES.MODE_CBC, iv=commondefs.SD_IV) + enc = cipher.encrypt(file.data) + outBin.write(enc) + filesHashWork.update(enc) + + ########### + # Signing # + ########### + + apPriv = bytearray(30) + apPriv[0x1d] = 1 + + # # apPub = ec.privToPub(apPriv) + # apPub = bytearray([ # generating the public key can take a while, so it's pre-calculated + # 0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, + # 0xbb, 0x21, 0x39, 0xf1, 0xbb, 0x75, 0x5f, 0xef, + # 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, + # 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, 0x01, 0x00, + # 0x6a, 0x08, 0xa4, 0x19, 0x03, 0x35, 0x06, 0x78, + # 0xe5, 0x85, 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef, + # 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, + # 0x01, 0xf8, 0x10, 0x52 + # ]) + # apCert = makeBlankEccCert("Root-CA00000001-MS00000002-NG0403ac68", "AP0000000100000002", apPub, 0) + # apHashWork = sha1() + # apHashWork.update(apCert.data[0x80:]) + # apHash = apHashWork.digest() + # apCert.writeat(commondefs.CERT_SIG_OFFSET, ec.sign(commondefs.DEFAULT_PRIVATE_KEY, apHash)) + + apCert = BinaryBuffer([ # That code can take a while, so the cert is pre-calculated + 0x00, 0x01, 0x00, 0x02, 0x00, 0x31, 0x6d, 0xe7, 0xe7, 0xca, 0x8d, 0xdf, 0xfd, 0xf1, 0x3f, 0xe0, + 0x35, 0x54, 0xf3, 0x9e, 0x35, 0x92, 0x5d, 0x3e, 0xb3, 0x40, 0x85, 0x06, 0x11, 0x13, 0xdd, 0x37, + 0x11, 0xe5, 0x00, 0xf9, 0x60, 0x22, 0xe0, 0xde, 0x92, 0x09, 0x92, 0xf4, 0xa3, 0x9b, 0xe3, 0xc5, + 0x6f, 0xf7, 0x9c, 0xdd, 0x0b, 0x1a, 0x3e, 0x3b, 0x2a, 0x8f, 0x4f, 0xea, 0xc7, 0xe4, 0x0d, 0xa7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x6f, 0x6f, 0x74, 0x2d, 0x43, 0x41, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x2d, + 0x4d, 0x53, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x2d, 0x4e, 0x47, 0x30, 0x34, 0x30, + 0x33, 0x61, 0x63, 0x36, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x41, 0x50, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, + 0xbb, 0x21, 0x39, 0xf1, 0xbb, 0x75, 0x5f, 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, + 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19, 0x03, 0x35, 0x06, 0x78, + 0xe5, 0x85, 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef, 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, + 0x01, 0xf8, 0x10, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]) + + filesHash = filesHashWork.digest() + sigHashWork = sha1() + sigHashWork.update(filesHash) + sigHash = sigHashWork.digest() + apSig = ec.sign(apPriv, sigHash) + + deviceCert = makeBlankEccCert("Root-CA00000001-MS00000002", "NG0403ac68", commondefs.DEFAULT_PUBLIC_KEY, commondefs.DEFAULT_KEY_ID) + deviceCert.writeat(commondefs.CERT_SIG_OFFSET, commondefs.DEFAULT_SIGNATURE) + + outBin.write(apSig) + outBin.write(int.to_bytes(commondefs.SIGNATURE_END_MAGIC, 4, 'big')) + outBin.write(deviceCert.data) + outBin.write(apCert.data) + + outBin.close() + +def getNameEncoding(version): + if version[:2] in ["eu", "us", "kr"]: + return 'ansi' + else: # jp + return 'shift-jis' + +if __name__ == "__main__": + ########## + # Unpack # + ########## + + # Get input data.bin path + if len(argv) > 1: + inBinName = argv[1] + else: + inBinName = input("Enter path to input data.bin: ") + + # Unpack input data.bin + print("Unpacking data.bin...") + header, titleId, files = unpackDataBin(inBinName) + + ################# + # Inject loader # + ################# + + # Get game version + if len(argv) > 2: + gameVer = argv[2].lower() + else: + gameVer = "" + while not gameVer in ["eu0", "eu1", "jp0", "jp1", "us0", "us1", "us2", "kr0"]: + gameVer = input("Enter game version (eu0/eu1/jp0/jp1/us0/us1/us2/kr0): ").lower() + + # Scan for wiimario save files + encoding = getNameEncoding(gameVer) + fileNames = [None] * 4 + filePositions = [None] * 4 + for i, file in enumerate(files): + if not file.name.startswith("wiimario"): + continue + idx = int(file.name[len("wiimario"):]) + if not 0 <= idx <= 3: + continue + name = file.readStr(0x8 + 0x20, encoding) + try: + name = file.readStr(0x8 + 0x20, encoding) + if len(name) == 0: + name = "" + except: + name = "" + fileNames[idx] = name + filePositions[idx] = i + + + # Get save file id to replace + if len(argv) > 3: + try: + saveNumber = int(argv[3]) + except: + saveNumber = 0 + else: + saveNumber = 0 + + if not 1 <= saveNumber <= 4: + print("Saves found: ") + for i, info in enumerate(fileNames): + print(f" {i+1}: {info}") + saveNumber = 0 + while not 1 <= saveNumber <= 4: + inp = input("Enter file id to replace with rel loader (1-4): ") + try: + saveNumber = int(inp) + except: + pass + + # Inject rel loader + print("Injecting loader...") + files[filePositions[saveNumber - 1]].data = generateRelLoader(gameVer) + + ############## + # Inject rel # + ############## + + # Remove existing rel if present + for i, file in enumerate(files): + if file.name == "pcrel.bin": + del files[i] + break + + # Get input rel path + if len(argv) > 4: + inRelName = argv[4] + else: + inRelName = input("Enter path to input rel: ") + + print("Injecting rel...") + with open(inRelName, 'rb') as inRel: + relData = inRel.read() + files.append(SaveFile( + "pcrel.bin", int.to_bytes(len(relData), 4, 'big') + bytes(0x1c) + relData, 0x3f, 0 + )) + + ########## + # Repack # + ########## + + # Get output data.bin path + if len(argv) > 5: + outBinName = argv[5] + else: + outBinName = input("Enter path to output data.bin: ") + + # Pack output data.bin + print("Repacking data.bin... (this can take a while)") + repackDataBin(outBinName, header, titleId, files) + + print("Done!")