diff --git a/Makefile b/Makefile index 9467eb1b..e780988d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ LD := arm-none-eabi-ld OC := arm-none-eabi-objcopy name := Luma3DS -version := $(shell git describe --abbrev=0 --tags) +revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i') dir_source := source dir_patches := patches @@ -34,8 +34,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ $(call rwildcard, $(dir_source), *.s *.c))) -bundled = $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/arm9_exceptions.h $(dir_build)/screeninit.h - +bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/arm9_exceptions.h $(dir_build)/injector.h $(dir_build)/loader.h $(dir_build)/screeninit.h .PHONY: all all: launcher a9lh ninjhax @@ -86,17 +85,23 @@ $(dir_build)/main.bin: $(dir_build)/main.elf $(dir_build)/main.elf: $(objects) $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ -$(dir_build)/patches.h: $(dir_patches)/emunand.s $(dir_patches)/reboot.s $(dir_injector)/Makefile +$(dir_build)/emunandpatch.h: $(dir_patches)/emunand.s $(dir_injector)/Makefile @mkdir -p "$(@D)" @armips $< - @armips $(word 2,$^) + @bin2c -o $@ -n emunand $(@D)/emunand.bin + +$(dir_build)/rebootpatch.h: $(dir_patches)/reboot.s + @mkdir -p "$(@D)" + @armips $< + @bin2c -o $@ -n reboot $(@D)/reboot.bin + +$(dir_build)/injector.h: $(dir_injector)/Makefile + @mkdir -p "$(@D)" @$(MAKE) -C $(dir_injector) - @mv emunand.bin reboot.bin $(dir_injector)/injector.cxi $(@D) - @bin2c -o $@ -n emunand $(@D)/emunand.bin -n reboot $(@D)/reboot.bin -n injector $(@D)/injector.cxi + @bin2c -o $@ -n injector $(@D)/injector.cxi $(dir_build)/loader.h: $(dir_loader)/Makefile @$(MAKE) -C $(dir_loader) - @mv $(dir_loader)/loader.bin $(@D) @bin2c -o $@ -n loader $(@D)/loader.bin $(dir_build)/arm9_exceptions.h: $(dir_arm9_exceptions)/Makefile @@ -106,11 +111,10 @@ $(dir_build)/arm9_exceptions.h: $(dir_arm9_exceptions)/Makefile $(dir_build)/screeninit.h: $(dir_screeninit)/Makefile @$(MAKE) -C $(dir_screeninit) - @mv $(dir_screeninit)/screeninit.bin $(@D) @bin2c -o $@ -n screeninit $(@D)/screeninit.bin $(dir_build)/memory.o: CFLAGS += -O3 -$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(version) configuration\"" +$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\"" $(dir_build)/%.o: $(dir_source)/%.c $(bundled) @mkdir -p "$(@D)" diff --git a/injector/Makefile b/injector/Makefile index c33e3d85..d568a733 100755 --- a/injector/Makefile +++ b/injector/Makefile @@ -31,13 +31,13 @@ objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ $(call rwildcard, $(dir_source), *.c)) .PHONY: all -all: $(name).cxi +all: ../$(dir_build)/$(name).cxi .PHONY: clean clean: @rm -rf $(dir_build) -$(name).cxi: $(dir_build)/$(name).elf +../$(dir_build)/$(name).cxi: $(dir_build)/$(name).elf @makerom -f ncch -rsf loader.rsf -nocodepadding -o $@ -elf $< $(dir_build)/$(name).elf: $(objects) diff --git a/injector/source/patcher.c b/injector/source/patcher.c index e5f53632..b7075cf6 100644 --- a/injector/source/patcher.c +++ b/injector/source/patcher.c @@ -472,6 +472,48 @@ void patchCode(u64 progId, u8 *code, u32 size) break; } + + case 0x0004013000003702LL: // RO + { + static const u8 sigCheckPattern[] = { + 0x30, 0x40, 0x2D, 0xE9, 0x02, 0x50, 0xA0, 0xE1 + }; + static const u8 sha256ChecksPattern1[] = { + 0x30, 0x40, 0x2D, 0xE9, 0x24, 0xD0, 0x4D, 0xE2 + }; + static const u8 sha256ChecksPattern2[] = { + 0xF8, 0x4F, 0x2D, 0xE9, 0x01, 0x70, 0xA0, 0xE1 + }; + + static const u8 stub[] = { + 0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 // mov r0, #0; bx lr + }; + + //Disable CRR0 signature (RSA2048 with SHA256) check + patchMemory(code, size, + sigCheckPattern, + sizeof(sigCheckPattern), 0, + stub, + sizeof(stub), 1 + ); + + //Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table) + patchMemory(code, size, + sha256ChecksPattern1, + sizeof(sha256ChecksPattern1), 0, + stub, + sizeof(stub), 1 + ); + + patchMemory(code, size, + sha256ChecksPattern2, + sizeof(sha256ChecksPattern2), 0, + stub, + sizeof(stub), 1 + ); + + break; + } default: if(CONFIG(4)) diff --git a/loader/Makefile b/loader/Makefile index 56f606c0..421d9c9e 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -25,13 +25,13 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ $(call rwildcard, $(dir_source), *.s *.c))) .PHONY: all -all: $(name).bin +all: ../$(dir_build)/$(name).bin .PHONY: clean clean: @rm -rf $(dir_build) -$(name).bin: $(dir_build)/$(name).elf +../$(dir_build)/$(name).bin: $(dir_build)/$(name).elf $(OC) -S -O binary $< $@ $(dir_build)/$(name).elf: $(objects) diff --git a/patches/emunand.s b/patches/emunand.s index 2c111268..a7e6cfa1 100644 --- a/patches/emunand.s +++ b/patches/emunand.s @@ -1,6 +1,6 @@ .arm.little -.create "emunand.bin", 0 +.create "build/emunand.bin", 0 .arm nand_sd: ; Original code that still needs to be executed. diff --git a/patches/reboot.s b/patches/reboot.s index cedb4af4..e662db5f 100644 --- a/patches/reboot.s +++ b/patches/reboot.s @@ -3,7 +3,7 @@ payload_addr equ 0x23F00000 ; Brahma payload address. payload_maxsize equ 0x20000 ; Maximum size for the payload (200 KB will do). -.create "reboot.bin", 0 +.create "build/reboot.bin", 0 .arm ; Interesting registers and locations to keep in mind, set before this code is ran: ; - sp + 0x3A8 - 0x70: FIRM path in exefs. diff --git a/screeninit/Makefile b/screeninit/Makefile index 28ed64b2..0096fd86 100755 --- a/screeninit/Makefile +++ b/screeninit/Makefile @@ -25,13 +25,13 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ $(call rwildcard, $(dir_source), *.s *.c))) .PHONY: all -all: $(name).bin +all: ../$(dir_build)/$(name).bin .PHONY: clean clean: @rm -rf $(dir_build) -$(name).bin: $(dir_build)/$(name).elf +../$(dir_build)/$(name).bin: $(dir_build)/$(name).elf $(OC) -S -O binary $< $@ $(dir_build)/$(name).elf: $(objects) diff --git a/source/emunand.c b/source/emunand.c index 25834d83..c58f9c5a 100644 --- a/source/emunand.c +++ b/source/emunand.c @@ -5,8 +5,9 @@ #include "emunand.h" #include "memory.h" #include "fatfs/sdmmc/sdmmc.h" +#include "../build/emunandpatch.h" -void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND) +void locateEmuNAND(u32 *off, u32 *head, u32 *emuNAND) { static u8 *const temp = (u8 *)0x24300000; @@ -35,11 +36,19 @@ void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND) else { (*emuNAND)--; - if(*emuNAND) getEmunandSect(off, head, emuNAND); + if(*emuNAND) locateEmuNAND(off, head, emuNAND); } } -u32 getSDMMC(u8 *pos, u32 size) +static inline void *getEmuCode(u8 *pos, u32 size) +{ + const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; + + //Looking for the last free space before Process9 + return memsearch(pos + 0x13500, pattern, size - 0x13500, 6) + 0x455; +} + +static inline u32 getSDMMC(u8 *pos, u32 size) { //Look for struct code const u8 pattern[] = {0x21, 0x20, 0x18, 0x20}; @@ -48,27 +57,58 @@ u32 getSDMMC(u8 *pos, u32 size) return *(u32 *)(off + 9) + *(u32 *)(off + 0xD); } -void getEmuRW(u8 *pos, u32 size, u16 **readOffset, u16 **writeOffset) +static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset) { + const u16 nandRedir[2] = {0x4C00, 0x47A0}; + //Look for read/write code const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05}; - *readOffset = (u16 *)memsearch(pos, pattern, size, 4) - 3; - *writeOffset = (u16 *)memsearch((u8 *)(*readOffset + 5), pattern, 0x100, 4) - 3; + u16 *readOffset = (u16 *)memsearch(pos, pattern, size, 4) - 3; + u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, 4) - 3; + + *readOffset = nandRedir[0]; + readOffset[1] = nandRedir[1]; + ((u32 *)readOffset)[1] = branchOffset; + *writeOffset = nandRedir[0]; + writeOffset[1] = nandRedir[1]; + ((u32 *)writeOffset)[1] = branchOffset; } -u32 *getMPU(u8 *pos, u32 size) +static inline void patchMPU(u8 *pos, u32 size) { + const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603}; + //Look for MPU pattern const u8 pattern[] = {0x03, 0x00, 0x24, 0x00}; - return (u32 *)memsearch(pos, pattern, size, 4); + u32 *off = (u32 *)memsearch(pos, pattern, size, 4); + + off[0] = mpuPatch[0]; + off[6] = mpuPatch[1]; + off[9] = mpuPatch[2]; } -void *getEmuCode(u8 *pos) +void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive) { - const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; + //Copy emuNAND code + void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize); + memcpy(emuCodeOffset, emunand, emunand_size); - //Looking for the last free space before Process9 - return memsearch(pos + 0x13500, pattern, 0x1000, 6) + 0x455; + //Add the data of the found emuNAND + u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4); + u32 *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4); + *pos_offset = emuOffset; + *pos_header = emuHeader; + + //Find and add the SDMMC struct + u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4); + *pos_sdmmc = getSDMMC(process9Offset, process9Size); + + //Add emuNAND hooks + u32 branchOffset = (u32)emuCodeOffset - branchAdditive; + patchNANDRW(process9Offset, process9Size, branchOffset); + + //Set MPU for emu code region + patchMPU(arm9Section, arm9SectionSize); } \ No newline at end of file diff --git a/source/emunand.h b/source/emunand.h index abe8e4a7..1d2c44a8 100644 --- a/source/emunand.h +++ b/source/emunand.h @@ -8,8 +8,5 @@ #define NCSD_MAGIC 0x4453434E -void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND); -u32 getSDMMC(u8 *pos, u32 size); -void getEmuRW(u8 *pos, u32 size, u16 **readOffset, u16 **writeOffset); -u32 *getMPU(u8 *pos, u32 size); -void *getEmuCode(u8 *pos); \ No newline at end of file +void locateEmuNAND(u32 *off, u32 *head, u32 *emuNAND); +void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive); \ No newline at end of file diff --git a/source/fatfs/diskio.c b/source/fatfs/diskio.c index 426d1cfa..68d0de6c 100644 --- a/source/fatfs/diskio.c +++ b/source/fatfs/diskio.c @@ -9,6 +9,7 @@ #include "diskio.h" /* FatFs lower layer API */ #include "sdmmc/sdmmc.h" +#include "../crypto.h" /* Definitions of physical drive number for each media */ #define SDCARD 0 diff --git a/source/fatfs/sdmmc/common.h b/source/fatfs/sdmmc/common.h index 2d13a640..90a327e1 100644 --- a/source/fatfs/sdmmc/common.h +++ b/source/fatfs/sdmmc/common.h @@ -1,5 +1,4 @@ #pragma once #include -#include "../../types.h" -#include "../../crypto.h" \ No newline at end of file +#include "../../types.h" \ No newline at end of file diff --git a/source/firm.c b/source/firm.c index 75938fb9..ae351835 100755 --- a/source/firm.c +++ b/source/firm.c @@ -14,7 +14,7 @@ #include "draw.h" #include "screeninit.h" #include "buttons.h" -#include "../build/patches.h" +#include "../build/injector.h" static firmHeader *const firm = (firmHeader *)0x24000000; static const firmSectionHeader *section; @@ -222,13 +222,13 @@ void main(void) //If we need to boot emuNAND, make sure it exists if(nandType) { - getEmunandSect(&emuOffset, &emuHeader, &nandType); + locateEmuNAND(&emuOffset, &emuHeader, &nandType); if(!nandType) firmSource = 0; } //Same if we're using emuNAND as the FIRM source else if(firmSource) - getEmunandSect(&emuOffset, &emuHeader, &firmSource); + locateEmuNAND(&emuOffset, &emuHeader, &firmSource); if(!bootType) { @@ -268,7 +268,7 @@ void main(void) stopChrono(); } - launchFirm(!firmType, bootType); + launchFirm(firmType, bootType); } static inline void loadFirm(u32 firmType, u32 externalFirm) @@ -346,24 +346,27 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode) process9MemAddr; u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr); + //Apply signature patches + patchSignatureChecks(process9Offset, process9Size); + + //Apply anti-anti-DG patches for >= 11.0 firmwares + if(nativeFirmType == 1) patchTitleInstallMinVersionCheck(process9Offset, process9Size); + //Apply emuNAND patches - if(nandType) patchEmuNAND(arm9Section, process9Offset, process9Size, emuHeader); + if(nandType) + { + u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address; + patchEmuNAND(arm9Section, section[2].size, process9Offset, process9Size, emuOffset, emuHeader, branchAdditive); + } //Apply FIRM0/1 writes patches on sysNAND to protect A9LH - else if(a9lhMode) patchFirmWrites(process9Offset, process9Size, 1); + else if(a9lhMode) patchFirmWrites(process9Offset, process9Size); - //Apply FIRM reboot patches, not on 9.0 FIRM as it breaks firmlaunchhax - if(nativeFirmType || a9lhMode == 2) patchReboots(process9Offset, process9Size, process9MemAddr); - - //Apply signature checks patches - u16 *sigOffset, - *sigOffset2; - getSigChecks(process9Offset, process9Size, &sigOffset, &sigOffset2); - *sigOffset = sigPatch[0]; - sigOffset2[0] = sigPatch[0]; - sigOffset2[1] = sigPatch[1]; + //Apply firmlaunch patches, not on 9.0 FIRM as it breaks firmlaunchhax + if(nativeFirmType || a9lhMode == 2) patchFirmlaunches(process9Offset, process9Size, process9MemAddr); //Does nothing if svcBackdoor is still there + if(nativeFirmType == 1) reimplementSvcBackdoor((u8 *)firm + section[1].offset, section[1].size); if(nativeFirmType == 1) reimplementSvcBackdoor(); if(DEVMODE) @@ -375,79 +378,32 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode) //Make FCRAM (and VRAM as a side effect) globally executable from arm11 kernel patchKernelFCRAMAndVRAMMappingPermissions(); } - //Replace the FIRM loader with the injector while copying section0 - copySection0AndInjectLoader(); } -static inline void patchEmuNAND(u8 *arm9Section, u8 *process9Offset, u32 process9Size, u32 emuHeader) +static inline void patchLegacyFirm(u32 firmType) { - //Copy emuNAND code - void *emuCodeOffset = getEmuCode(arm9Section); - memcpy(emuCodeOffset, emunand, emunand_size); - - //Add the data of the found emuNAND - u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4); - u32 *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4); - *pos_offset = emuOffset; - *pos_header = emuHeader; - - //Find and add the SDMMC struct - u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4); - *pos_sdmmc = getSDMMC(process9Offset, process9Size); - - //Calculate offset for the hooks - u32 branchOffset = (u32)emuCodeOffset - (u32)firm - - section[2].offset + (u32)section[2].address; - - //Add emuNAND hooks - u16 *emuRead, - *emuWrite; - - getEmuRW(process9Offset, process9Size, &emuRead, &emuWrite); - *emuRead = nandRedir[0]; - emuRead[1] = nandRedir[1]; - ((u32 *)emuRead)[1] = branchOffset; - *emuWrite = nandRedir[0]; - emuWrite[1] = nandRedir[1]; - ((u32 *)emuWrite)[1] = branchOffset; - - //Set MPU for emu code region - u32 *mpuOffset = getMPU(arm9Section, section[2].size); - *mpuOffset = mpuPatch[0]; - mpuOffset[6] = mpuPatch[1]; - mpuOffset[9] = mpuPatch[2]; -} - -static inline void patchReboots(u8 *process9Offset, u32 process9Size, u32 process9MemAddr) -{ - //Calculate offset for the firmlaunch code and fOpen - u32 fOpenOffset; - void *rebootOffset = getReboot(process9Offset, process9Size, process9MemAddr, &fOpenOffset); - - //Copy firmlaunch code - memcpy(rebootOffset, reboot, reboot_size); - - //Put the fOpen offset in the right location - u32 *pos_fopen = (u32 *)memsearch(rebootOffset, "OPEN", reboot_size, 4); - *pos_fopen = fOpenOffset; -} - -static inline void reimplementSvcBackdoor(void) -{ - u8 *arm11Section1 = (u8 *)firm + section[1].offset; - - u32 *exceptionsPage; - u32 *svcTable = getSvcAndExceptions(arm11Section1, section[1].size, &exceptionsPage); - - if(!svcTable[0x7B]) + //On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader + if(console) { - u32 *freeSpace; - for(freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++); - - memcpy(freeSpace, svcBackdoor, 40); - - svcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)exceptionsPage); + arm9Loader((u8 *)firm + section[3].offset, 0); + firm->arm9Entry = (u8 *)0x801301C; } + + applyLegacyFirmPatches((u8 *)firm, firmType, console);} + +static inline void patchSafeFirm(void) +{ + u8 *arm9Section = (u8 *)firm + section[2].offset; + + if(console) + { + //Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader + arm9Loader(arm9Section, 0); + firm->arm9Entry = (u8 *)0x801B01C; + + patchFirmWrites(arm9Section, section[2].size); + } + else patchFirmWriteSafe(arm9Section, section[2].size); } static inline void copySection0AndInjectLoader(void) @@ -462,87 +418,17 @@ static inline void copySection0AndInjectLoader(void) memcpy(section[0].address + loaderOffset + injector_size, arm11Section0 + loaderOffset + loaderSize, section[0].size - (loaderOffset + loaderSize)); } -static inline void patchSafeFirm(void) +static inline void launchFirm(u32 firmType, u32 bootType) { - u8 *arm9Section = (u8 *)firm + section[2].offset; - - if(console) + //If we're booting NATIVE_FIRM, section0 needs to be copied separately to inject 3ds_injector + u32 sectionNum; + if(!firmType) { - //Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader - arm9Loader(arm9Section, 0); - firm->arm9Entry = (u8 *)0x801B01C; + copySection0AndInjectLoader(); + sectionNum = 1; } + else sectionNum = 0; - //Apply FIRM0/1 writes patches to protect A9LH - patchFirmWrites(arm9Section, section[2].size, console); -} - -static void patchFirmWrites(u8 *offset, u32 size, u32 mode) -{ - if(mode) - { - u16 *writeOffset = getFirmWrite(offset, size); - *writeOffset = writeBlock[0]; - *(writeOffset + 1) = writeBlock[1]; - } - else - { - u16 *writeOffset = getFirmWriteSafe(offset, size); - *writeOffset = writeBlockSafe[0]; - *(writeOffset + 1) = writeBlockSafe[1]; - } -} - -static inline void patchLegacyFirm(u32 firmType) -{ - //On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader - if(console) - { - arm9Loader((u8 *)firm + section[3].offset, 0); - firm->arm9Entry = (u8 *)0x801301C; - } - - const patchData twlPatches[] = { - {{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0}, - {{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1}, - {{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2}, - {{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2}, - {{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2}, - {{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2}, - {{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2}, - {{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1}, - {{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1} - }, - agbPatches[] = { - {{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0}, - {{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1} - }; - - /* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM - if the matching option was enabled (keep it as last) */ - u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) : - (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6)); - const patchData *patches = firmType == 1 ? twlPatches : agbPatches; - - //Patch - for(u32 i = 0; i < numPatches; i++) - { - switch(patches[i].type) - { - case 0: - memcpy((u8 *)firm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]); - break; - case 2: - *(u16 *)((u8 *)firm + patches[i].offset[console] + 2) = 0; - case 1: - *(u16 *)((u8 *)firm + patches[i].offset[console]) = patches[i].patch.type1; - break; - } - } -} - -static inline void launchFirm(u32 sectionNum, u32 bootType) -{ //Copy FIRM sections to respective memory locations for(; sectionNum < 4 && section[sectionNum].size; sectionNum++) memcpy(section[sectionNum].address, (u8 *)firm + section[sectionNum].offset, section[sectionNum].size); diff --git a/source/firm.h b/source/firm.h index 2e4cee2c..8e3098b2 100644 --- a/source/firm.h +++ b/source/firm.h @@ -28,23 +28,10 @@ typedef struct firmHeader { firmSectionHeader section[4]; } firmHeader; -typedef struct patchData { - u32 offset[2]; - union { - u8 type0[8]; - u16 type1; - } patch; - u32 type; -} patchData; - static inline void loadFirm(u32 firmType, u32 externalFirm); static inline void patchKernelFCRAMAndVRAMMappingPermissions(void); static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode); -static inline void patchEmuNAND(u8 *arm9Section, u8 *process9Offset, u32 process9Size, u32 emuHeader); -static inline void patchReboots(u8 *process9Offset, u32 process9Size, u32 process9MemAddr); -static inline void reimplementSvcBackdoor(void); -static inline void copySection0AndInjectLoader(void); -static inline void patchSafeFirm(void); -static void patchFirmWrites(u8 *offset, u32 size, u32 mode); static inline void patchLegacyFirm(u32 firmType); +static inline void patchSafeFirm(void); +static inline void copySection0AndInjectLoader(void); static inline void launchFirm(u32 sectionNum, u32 bootType); \ No newline at end of file diff --git a/source/patches.c b/source/patches.c index b5884907..b412f481 100644 --- a/source/patches.c +++ b/source/patches.c @@ -4,36 +4,11 @@ #include "patches.h" #include "memory.h" - -/************************************************** -* Patches -**************************************************/ - -const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603}; - -const u16 nandRedir[2] = {0x4C00, 0x47A0}, - sigPatch[2] = {0x2000, 0x4770}, - writeBlock[2] = {0x2000, 0x46C0}, - writeBlockSafe[2] = {0x2400, 0xE01D}; +#include "config.h" +#include "../build/rebootpatch.h" const u8 unitInfoPatch = 0xE3; -//Official implementation of svcBackdoor -const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff - 0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00 - 0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28 - 0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1] - 0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr} - 0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2 - 0x30, 0xFF, 0x2F, 0xE1, //blx r0 - 0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1} - 0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0 - 0x11, 0xFF, 0x2F, 0xE1}; //bx r1 - -/************************************************** -* Functions -**************************************************/ - u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) { u8 *off = memsearch(pos, "ess9", size, 4); @@ -45,17 +20,32 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200; } -void getSigChecks(u8 *pos, u32 size, u16 **off, u16 **off2) +void patchSignatureChecks(u8 *pos, u32 size) { + const u16 sigPatch[2] = {0x2000, 0x4770}; + //Look for signature checks const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7}, pattern2[] = {0xB5, 0x22, 0x4D, 0x0C}; - *off = (u16 *)memsearch(pos, pattern, size, 4); - *off2 = (u16 *)(memsearch(pos, pattern2, size, 4) - 1); + u16 *off = (u16 *)memsearch(pos, pattern, size, 4), + *off2 = (u16 *)(memsearch(pos, pattern2, size, 4) - 1); + + *off = sigPatch[0]; + off2[0] = sigPatch[0]; + off2[1] = sigPatch[1]; } -void *getReboot(u8 *pos, u32 size, u32 process9MemAddr, u32 *fOpenOffset) +void patchTitleInstallMinVersionCheck(u8 *pos, u32 size) +{ + const u8 pattern[] = {0x89, 0x0A, 0x81, 0x42, 0x02, 0xD2}; + + u8 *off = memsearch(pos, pattern, size, 6); + + if(off != NULL) off[5] = 0xE0; +} + +void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr) { //Look for FIRM reboot code const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2}; @@ -63,26 +53,41 @@ void *getReboot(u8 *pos, u32 size, u32 process9MemAddr, u32 *fOpenOffset) u8 *off = memsearch(pos, pattern, size, 4) - 0x10; //Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1 - *fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr); + u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr); - return off; + //Copy firmlaunch code + memcpy(off, reboot, reboot_size); + + //Put the fOpen offset in the right location + u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_size, 4); + *pos_fopen = fOpenOffset; } -u16 *getFirmWrite(u8 *pos, u32 size) +void patchFirmWrites(u8 *pos, u32 size) { + const u16 writeBlock[2] = {0x2000, 0x46C0}; + //Look for FIRM writing code - u8 *const off = memsearch(pos, "exe:", size, 4); + u8 *const off1 = memsearch(pos, "exe:", size, 4); const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA}; - return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4); + u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, 4); + + off2[0] = writeBlock[0]; + off2[1] = writeBlock[1]; } -u16 *getFirmWriteSafe(u8 *pos, u32 size) +void patchFirmWriteSafe(u8 *pos, u32 size) { + const u16 writeBlockSafe[2] = {0x2400, 0xE01D}; + //Look for FIRM writing code const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB}; - return (u16 *)memsearch(pos, pattern, size, 4); + u16 *off = (u16 *)memsearch(pos, pattern, size, 4); + + off[0] = writeBlockSafe[0]; + off[1] = writeBlockSafe[1]; } u8 *getUnitInfoValueSet(u8 *pos, u32 size) @@ -93,6 +98,80 @@ u8 *getUnitInfoValueSet(u8 *pos, u32 size) return memsearch(pos, pattern, size, 4) + 3; } +void reimplementSvcBackdoor(u8 *pos, u32 size) +{ + //Official implementation of svcBackdoor + const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff + 0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00 + 0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28 + 0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1] + 0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr} + 0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2 + 0x30, 0xFF, 0x2F, 0xE1, //blx r0 + 0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1} + 0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0 + 0x11, 0xFF, 0x2F, 0xE1}; //bx r1 + + const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; //cpsid aif + + u32 *exceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB; + + u32 svcOffset = (-((exceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch + u32 *svcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address + while(*svcTable) svcTable++; //Look for SVC0 (NULL) + + if(!svcTable[0x7B]) + { + u32 *freeSpace; + for(freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++); + + memcpy(freeSpace, svcBackdoor, 40); + + svcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)exceptionsPage); + } +} + +void applyLegacyFirmPatches(u8 *pos, u32 firmType, u32 console) +{ + const patchData twlPatches[] = { + {{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0}, + {{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1}, + {{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2}, + {{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2}, + {{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2}, + {{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2}, + {{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2}, + {{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1}, + {{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1} + }, + agbPatches[] = { + {{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0}, + {{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1} + }; + + /* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM + if the matching option was enabled (keep it as last) */ + u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) : + (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6)); + const patchData *patches = firmType == 1 ? twlPatches : agbPatches; + + //Patch + for(u32 i = 0; i < numPatches; i++) + { + switch(patches[i].type) + { + case 0: + memcpy(pos + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]); + break; + case 2: + *(u16 *)(pos + patches[i].offset[console] + 2) = 0; + case 1: + *(u16 *)(pos + patches[i].offset[console]) = patches[i].patch.type1; + break; + } + } +} + u32 getLoader(u8 *pos, u32 *loaderSize) { u8 *off = pos; @@ -108,17 +187,4 @@ u32 getLoader(u8 *pos, u32 *loaderSize) *loaderSize = size; return (u32)(off - pos); -} - -u32 *getSvcAndExceptions(u8 *pos, u32 size, u32 **exceptionsPage) -{ - const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; //cpsid aif - - *exceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB; - - u32 svcOffset = (-(((*exceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch - u32 *svcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address - while(*svcTable) svcTable++; //Look for SVC0 (NULL) - - return svcTable; } \ No newline at end of file diff --git a/source/patches.h b/source/patches.h index aa47464b..07041abe 100644 --- a/source/patches.h +++ b/source/patches.h @@ -6,25 +6,21 @@ #include "types.h" -/************************************************** -* Patches -**************************************************/ -const u32 mpuPatch[3]; -const u16 nandRedir[2], - sigPatch[2], - writeBlock[2], - writeBlockSafe[2]; +typedef struct patchData { + u32 offset[2]; + union { + u8 type0[8]; + u16 type1; + } patch; + u32 type; +} patchData; const u8 unitInfoPatch; -const u8 svcBackdoor[40]; - -/************************************************** -* Functions -**************************************************/ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr); -void getSigChecks(u8 *pos, u32 size, u16 **off, u16 **off2); -void *getReboot(u8 *pos, u32 size, u32 process9MemAddr, u32 *fOpenOffset); -u16 *getFirmWrite(u8 *pos, u32 size); -u16 *getFirmWriteSafe(u8 *pos, u32 size); -u8 *getUnitInfoValueSet(u8 *pos, u32 size); -u32 getLoader(u8 *pos, u32 *loaderSize); -u32 *getSvcAndExceptions(u8 *pos, u32 size, u32 **exceptionsPage); \ No newline at end of file +void patchSignatureChecks(u8 *pos, u32 size); +void patchTitleInstallMinVersionCheck(u8 *pos, u32 size); +void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr); +void patchFirmWrites(u8 *pos, u32 size); +void patchFirmWriteSafe(u8 *pos, u32 size); +void reimplementSvcBackdoor(u8 *pos, u32 size); +void applyLegacyFirmPatches(u8 *pos, u32 firmType, u32 console); +u8 *getUnitInfoValueSet(u8 *pos, u32 size); \ No newline at end of file