diff --git a/injector/Makefile b/injector/Makefile index d568a73..f7194b2 100755 --- a/injector/Makefile +++ b/injector/Makefile @@ -22,13 +22,13 @@ LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include) -ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft -CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \ +ASFLAGS := -mcpu=mpcore -mfloat-abi=hard -mtp=soft +CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \ -ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS -LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ARCH) +LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ASFLAGS) -L$(DEVKITPRO)/libctru/lib objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ - $(call rwildcard, $(dir_source), *.c)) + $(call rwildcard, $(dir_source), *.s *.c)) .PHONY: all all: ../$(dir_build)/$(name).cxi @@ -48,4 +48,8 @@ $(dir_build)/memory.o : CFLAGS += -O3 $(dir_build)/%.o: $(dir_source)/%.c @mkdir -p "$(@D)" $(COMPILE.c) $(OUTPUT_OPTION) $< + +$(dir_build)/%.o: $(dir_source)/%.s + @mkdir -p "$(@D)" + $(COMPILE.s) $(OUTPUT_OPTION) $< include $(call rwildcard, $(dir_build), *.d) diff --git a/injector/source/CFWInfo.h b/injector/source/CFWInfo.h new file mode 100644 index 0000000..30b4781 --- /dev/null +++ b/injector/source/CFWInfo.h @@ -0,0 +1,19 @@ +#pragma once + +#include <3ds/types.h> + +typedef struct __attribute__((packed)) +{ + char magic[4]; + + u8 versionMajor; + u8 versionMinor; + u8 versionBuild; + u8 flags; /* bit 0: dev branch; bit 1: is release */ + + u32 commitHash; + + u32 config; +} CFWInfo; + +int svcGetCFWInfo(CFWInfo *info); \ No newline at end of file diff --git a/injector/source/CFWInfo.s b/injector/source/CFWInfo.s new file mode 100644 index 0000000..8337620 --- /dev/null +++ b/injector/source/CFWInfo.s @@ -0,0 +1,9 @@ +.text +.arm +.align 4 + +.global svcGetCFWInfo +.type svcGetCFWInfo, %function +svcGetCFWInfo: + svc 0x2e + bx lr diff --git a/injector/source/patcher.c b/injector/source/patcher.c index cf389f0..1d65585 100644 --- a/injector/source/patcher.c +++ b/injector/source/patcher.c @@ -2,8 +2,9 @@ #include "memory.h" #include "patcher.h" #include "ifile.h" +#include "CFWInfo.h" -static CFWInfo info = {0}; +static CFWInfo info; static int memcmp(const void *buf1, const void *buf2, u32 size) { @@ -85,11 +86,6 @@ static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int f return IFile_Open(file, archiveId, archivePath, filePath, flags); } -int __attribute__((naked)) svcGetCFWInfo(CFWInfo __attribute__((unused)) *out) -{ - __asm__ volatile("svc 0x2E; bx lr"); -} - static void loadCFWInfo(void) { static bool infoLoaded = false; @@ -97,11 +93,10 @@ static void loadCFWInfo(void) if(!infoLoaded) { svcGetCFWInfo(&info); + IFile file; if(BOOTCONFIG(5, 1) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted - { IFile_Close(&file); - } infoLoaded = true; } diff --git a/injector/source/patcher.h b/injector/source/patcher.h index ea3ea5e..e67e9e6 100644 --- a/injector/source/patcher.h +++ b/injector/source/patcher.h @@ -8,18 +8,4 @@ #define MULTICONFIG(a) ((info.config >> (a * 2 + 6)) & 3) #define BOOTCONFIG(a, b) ((info.config >> a) & b) -typedef struct __attribute__((packed)) -{ - char magic[4]; - - u8 versionMajor; - u8 versionMinor; - u8 versionBuild; - u8 flags; /* bit 0: dev branch; bit 1: is release */ - - u32 commitHash; - - u32 config; -} CFWInfo; - void patchCode(u64 progId, u8 *code, u32 size); \ No newline at end of file diff --git a/loader/source/main.c b/loader/source/main.c index 4605d25..f6d9cae 100644 --- a/loader/source/main.c +++ b/loader/source/main.c @@ -23,7 +23,7 @@ #include "memory.h" #include "cache.h" -extern u32 payloadSize; //defined in start.s +extern u32 payloadSize; //Defined in start.s void main(void) { diff --git a/patches/emunand.s b/patches/emunand.s index a7e6cfa..0836c1f 100644 --- a/patches/emunand.s +++ b/patches/emunand.s @@ -43,4 +43,4 @@ nand_sd: sdmmc: .ascii "SDMC" nand_offset: .ascii "NAND" ; for rednand this should be 1 ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED) -.close +.close \ No newline at end of file diff --git a/patches/reboot.s b/patches/reboot.s index 9cb779f..9174a23 100644 --- a/patches/reboot.s +++ b/patches/reboot.s @@ -125,4 +125,4 @@ dat_fname: .dcw "sdmc:/Luma3DS.dat" bx r0 .pool -.close +.close \ No newline at end of file diff --git a/patches/twl_k11modules.s b/patches/twl_k11modules.s index 88fcc30..d808b27 100644 --- a/patches/twl_k11modules.s +++ b/patches/twl_k11modules.s @@ -143,4 +143,4 @@ patchesEnd: .pool -.close +.close \ No newline at end of file diff --git a/source/config.c b/source/config.c index 8474f6b..ef10f18 100644 --- a/source/config.c +++ b/source/config.c @@ -27,6 +27,7 @@ #include "screen.h" #include "draw.h" #include "buttons.h" +#include "pin.h" bool readConfig(const char *configPath) { @@ -64,7 +65,7 @@ void writeConfig(const char *configPath, u32 configTemp) } } -void configure(void) +void configMenu(bool oldPinStatus) { initScreens(); @@ -234,6 +235,11 @@ void configure(void) for(u32 i = 0; i < singleOptionsAmount; i++) configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16); + if(CONFIG(8)) newPin(oldPinStatus); + else if(oldPinStatus) fileDelete("/luma/pin.bin"); + //Wait for the pressed buttons to change - while(HID_PAD == BUTTON_START); + while(HID_PAD & PIN_BUTTONS); + + chrono(2); } \ No newline at end of file diff --git a/source/config.h b/source/config.h index 5989beb..08f4d7d 100644 --- a/source/config.h +++ b/source/config.h @@ -44,4 +44,4 @@ extern cfgData configData; bool readConfig(const char *configPath); void writeConfig(const char *configPath, u32 configTemp); -void configure(void); \ No newline at end of file +void configMenu(bool oldPinStatus); diff --git a/source/crypto.c b/source/crypto.c index 7014027..e6a4751 100755 --- a/source/crypto.c +++ b/source/crypto.c @@ -457,13 +457,13 @@ void arm9Loader(u8 *arm9Section) } } -void computePINHash(u8 out[32], u8 *in, u32 blockCount) +void computePinHash(u8 *out, u8 *in, u32 blockCount) { u8 __attribute__((aligned(4))) cid[0x10]; u8 __attribute__((aligned(4))) cipherText[0x10]; sdmmc_get_cid(1, (u32 *)cid); - aes_use_keyslot(4); // console-unique keyslot which keys are set by the Arm9 bootROM + aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM aes(cipherText, in, blockCount, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); sha(out, cipherText, 0x10, SHA_256_MODE); diff --git a/source/crypto.h b/source/crypto.h index 0b30bf9..c7221c8 100755 --- a/source/crypto.h +++ b/source/crypto.h @@ -100,8 +100,7 @@ #define SHA_1_HASH_SIZE (160 / 8) extern u32 emuOffset; -extern bool isN3DS; -extern bool isDevUnit; +extern bool isN3DS, isDevUnit; extern FirmwareSource firmSource; void ctrNandInit(void); @@ -109,5 +108,4 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf); void setRSAMod0DerivedKeys(void); void decryptExeFs(u8 *inbuf); void arm9Loader(u8 *arm9Section); - -void computePINHash(u8 out[32], u8 *in, u32 blockCount); \ No newline at end of file +void computePinHash(u8 *out, u8 *in, u32 blockCount); \ No newline at end of file diff --git a/source/emunand.c b/source/emunand.c index 2b6f02c..0115a1c 100644 --- a/source/emunand.c +++ b/source/emunand.c @@ -25,12 +25,12 @@ #include "fatfs/sdmmc/sdmmc.h" #include "../build/emunandpatch.h" -void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND) +void locateEmuNand(u32 *off, u32 *head, FirmwareSource *emuNand) { static u8 temp[0x200]; const u32 nandSize = getMMCDevice(0)->total_size; - u32 nandOffset = *emuNAND == FIRMWARE_EMUNAND ? 0 : + u32 nandOffset = *emuNand == FIRMWARE_EMUNAND ? 0 : (nandSize > 0x200000 ? 0x400000 : 0x200000); //Check for RedNAND @@ -53,12 +53,12 @@ void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND) or to SysNAND if there isn't any */ else { - *emuNAND = (*emuNAND == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; - if(*emuNAND) locateEmuNAND(off, head, emuNAND); + *emuNand = (*emuNand == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; + if(*emuNand) locateEmuNand(off, head, emuNand); } } -static inline void *getEmuCode(u8 *pos, u32 size) +static inline void *getFreeK9Space(u8 *pos, u32 size) { const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; @@ -66,7 +66,7 @@ static inline void *getEmuCode(u8 *pos, u32 size) return memsearch(pos + 0x13500, pattern, size - 0x13500, 6) + 0x455; } -static inline u32 getSDMMC(u8 *pos, u32 size) +static inline u32 getSdmmc(u8 *pos, u32 size) { //Look for struct code const u8 pattern[] = {0x21, 0x20, 0x18, 0x20}; @@ -75,7 +75,7 @@ static inline u32 getSDMMC(u8 *pos, u32 size) return *(u32 *)(off + 9) + *(u32 *)(off + 0xD); } -static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset) +static inline void patchNandRw(u8 *pos, u32 size, u32 branchOffset) { const u16 nandRedir[2] = {0x4C00, 0x47A0}; @@ -93,7 +93,7 @@ static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset) ((u32 *)writeOffset)[1] = branchOffset; } -static inline void patchMPU(u8 *pos, u32 size) +static inline void patchMpu(u8 *pos, u32 size) { const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603}; @@ -107,26 +107,26 @@ static inline void patchMPU(u8 *pos, u32 size) off[9] = mpuPatch[2]; } -void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive) +void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive) { //Copy emuNAND code - void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize); - memcpy(emuCodeOffset, emunand, emunand_size); + void *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize); + memcpy(freeK9Space, emunand, emunand_size); //Add the data of the found emuNAND - u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4), - *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4); - *pos_offset = emuOffset; - *pos_header = emuHeader; + u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_size, 4), + *posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_size, 4); + *posOffset = emuOffset; + *posHeader = emuHeader; //Find and add the SDMMC struct - u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4); - *pos_sdmmc = getSDMMC(process9Offset, process9Size); + u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_size, 4); + *posSdmmc = getSdmmc(process9Offset, process9Size); //Add emuNAND hooks - u32 branchOffset = (u32)emuCodeOffset - branchAdditive; - patchNANDRW(process9Offset, process9Size, branchOffset); + u32 branchOffset = (u32)freeK9Space - branchAdditive; + patchNandRw(process9Offset, process9Size, branchOffset); //Set MPU for emu code region - patchMPU(arm9Section, arm9SectionSize); + patchMpu(arm9Section, arm9SectionSize); } \ No newline at end of file diff --git a/source/emunand.h b/source/emunand.h index 1930c34..543fbe0 100644 --- a/source/emunand.h +++ b/source/emunand.h @@ -26,5 +26,7 @@ #define NCSD_MAGIC 0x4453434E -void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND); -void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive); \ No newline at end of file +extern u32 emuOffset; + +void locateEmuNand(u32 *off, u32 *head, FirmwareSource *emuNand); +void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive); \ No newline at end of file diff --git a/source/fatfs/sdmmc/common.h b/source/fatfs/sdmmc/common.h index 90a327e..3b77ae6 100644 --- a/source/fatfs/sdmmc/common.h +++ b/source/fatfs/sdmmc/common.h @@ -1,4 +1,3 @@ #pragma once -#include #include "../../types.h" \ No newline at end of file diff --git a/source/firm.c b/source/firm.c index 41a314f..1d28b39 100755 --- a/source/firm.c +++ b/source/firm.c @@ -36,7 +36,7 @@ #include "pin.h" #include "../build/injector.h" -extern u16 launchedFirmTIDLow[8]; //Defined in start.s +extern u16 launchedFirmTidLow[8]; //Defined in start.s static firmHeader *const firm = (firmHeader *)0x24000000; static const firmSectionHeader *section; @@ -80,14 +80,14 @@ void main(void) } //Determine if this is a firmlaunch boot - if(launchedFirmTIDLow[5] != 0) + if(launchedFirmTidLow[5] != 0) { if(needConfig == CREATE_CONFIGURATION) mcuReboot(); isFirmlaunch = true; //'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM - firmType = launchedFirmTIDLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTIDLow[5] - u'0'); + firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0'); nandType = (FirmwareSource)BOOTCONFIG(0, 3); firmSource = (FirmwareSource)BOOTCONFIG(2, 1); @@ -134,23 +134,14 @@ void main(void) //Boot options aren't being forced if(needConfig != DONT_CONFIGURE) { - PINData pin; - - bool pinExists = CONFIG(8) && readPin(&pin); - - //If we get here we should check the PIN (if it exists) in all cases - if(pinExists) verifyPin(&pin); + bool pinExists = CONFIG(8) && verifyPin(); //If no configuration file exists or SELECT is held, load configuration menu - bool shouldLoadConfigurationMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1)); + bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1)); - if(shouldLoadConfigurationMenu) + if(shouldLoadConfigMenu) { - configure(); - - if(!pinExists && CONFIG(8)) newPin(); - - chrono(2); + configMenu(pinExists); //Update pressed buttons pressed = HID_PAD; @@ -163,13 +154,20 @@ void main(void) //Flag to tell loader to init SD configTemp |= 1 << 5; + + //If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo + if(pinExists && !shouldLoadConfigMenu) + { + while(HID_PAD & PIN_BUTTONS); + chrono(2); + } } else { if(CONFIG(7) && loadSplash()) pressed = HID_PAD; /* If L and R/A/Select or one of the single payload buttons are pressed, - chainload an external payload (the PIN, if any, has been verified)*/ + chainload an external payload */ bool shouldLoadPayload = (pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS)); if(shouldLoadPayload) loadPayload(pressed); @@ -182,8 +180,8 @@ void main(void) //If R is pressed, boot the non-updated NAND with the FIRM of the opposite one if(pressed & BUTTON_R1) { - nandType = (useSysAsDefault) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; - firmSource = (useSysAsDefault) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND; + nandType = useSysAsDefault ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; + firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND; } /* Else, boot the NAND the user set to autoboot or the opposite one, depending on L, @@ -204,13 +202,13 @@ void main(void) //If we need to boot emuNAND, make sure it exists if(nandType != FIRMWARE_SYSNAND) { - locateEmuNAND(&emuOffset, &emuHeader, &nandType); + locateEmuNand(&emuOffset, &emuHeader, &nandType); if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND; } //Same if we're using emuNAND as the FIRM source else if(firmSource != FIRMWARE_SYSNAND) - locateEmuNAND(&emuOffset, &emuHeader, &firmSource); + locateEmuNand(&emuOffset, &emuHeader, &firmSource); if(!isFirmlaunch) { @@ -218,7 +216,7 @@ void main(void) writeConfig(configPath, configTemp); } - u32 firmVersion = loadFirm(firmType); + u32 firmVersion = loadFirm(&firmType, firmSource); switch(firmType) { @@ -226,7 +224,8 @@ void main(void) patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh); break; case SAFE_FIRM: - patchSafeFirm(); + case NATIVE_FIRM2X: + if(isA9lh) patch2xNativeAndSafeFirm(); break; default: //Skip patching on unsupported O3DS AGB/TWL FIRMs @@ -237,7 +236,7 @@ void main(void) launchFirm(firmType); } -static inline u32 loadFirm(FirmwareType firmType) +static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource) { section = firm->section; const char *firmwareFiles[4] = { @@ -251,25 +250,39 @@ static inline u32 loadFirm(FirmwareType firmType) if(fileRead(firm, firmwareFiles[(u32)firmType])) { - firmVersion = 0xffffffff; + firmVersion = 0xFFFFFFFF; } else { firmVersion = firmRead(firm, (u32)firmType); - if(firmType == NATIVE_FIRM && !isN3DS) + if(!isN3DS && *firmType == NATIVE_FIRM) { - //We can't boot < 3.x NANDs (if firmware.bin is in the /luma folder, booting will fail) + //We can't boot < 2.x SysNANDs and < 3.x EmuNANDs if(firmVersion < 0x18) - error("An old unsupported NAND has been detected.\nLuma3DS is unable to boot it."); - - //We can't boot a 4.x NATIVE_FIRM - if(firmVersion < 0x25) - error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /luma to boot"); + { + if(firmSource != FIRMWARE_SYSNAND || firmVersion < 9) + error("An old unsupported NAND has been detected.\nLuma3DS is unable to boot it"); + + if(BOOTCONFIG(5, 1)) error("SAFE_MODE is not supported on 2.x FIRM"); + + *firmType = NATIVE_FIRM2X; + } + + //We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD + else if(firmVersion < 0x25) + { + if(!fileRead(firm, "/luma/firmware.bin") || (((u32)section[2].address >> 8) & 0xFF) != 0x68) + error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /luma to boot"); + + //No assumption regarding FIRM version + firmVersion = 0xFFFFFFFF; + } } - decryptExeFs((u8 *)firm); } + if(firmVersion != 0xFFFFFFFF) decryptExeFs((u8 *)firm); + return firmVersion; } @@ -293,6 +306,13 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 process9MemAddr; u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr); + //Find Kernel11 SVC table and free space locations + u8 *freeK11Space; + u32 *arm11SvcHandler, + *arm11ExceptionsPage; + + u32 *arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage); + //Apply signature patches patchSignatureChecks(process9Offset, process9Size); @@ -300,7 +320,7 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 if(nandType != FIRMWARE_SYSNAND) { u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address; - patchEmuNAND(arm9Section, section[2].size, process9Offset, process9Size, emuOffset, emuHeader, branchAdditive); + patchEmuNand(arm9Section, section[2].size, process9Offset, process9Size, emuHeader, branchAdditive); } //Apply FIRM0/1 writes patches on sysNAND to protect A9LH @@ -316,9 +336,11 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 patchTitleInstallMinVersionCheck(process9Offset, process9Size); //Restore svcBackdoor - reimplementSvcBackdoor(arm11Section1, section[1].size); + reimplementSvcBackdoor(arm11Section1, arm11SvcTable, &freeK11Space); } + implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, &freeK11Space); + //Apply UNITINFO patch if(DEV_OPTIONS == 1) patchUnitInfoValueSet(arm9Section, section[2].size); @@ -326,28 +348,27 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 { //Install arm11 exception handlers u32 stackAddress, codeSetOffset; - u32 *exceptionsPage = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &stackAddress, &codeSetOffset); - installArm11Handlers(exceptionsPage, stackAddress, codeSetOffset); + getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &stackAddress, &codeSetOffset); + installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset); //Kernel9/Process9 debugging - patchExceptionHandlersInstall(arm9Section, section[2].size); + patchArm9ExceptionHandlersInstall(arm9Section, section[2].size); patchSvcBreak9(arm9Section, section[2].size, (u32)(section[2].address)); patchKernel9Panic(arm9Section, section[2].size, NATIVE_FIRM); //Stub svcBreak11 with "bkpt 65535" - patchSvcBreak11(arm11Section1, section[1].size); + patchSvcBreak11(arm11Section1, arm11SvcTable); + //Stub kernel11panic with "bkpt 65534" patchKernel11Panic(arm11Section1, section[1].size); } if(CONFIG(9)) { - patchArm11SvcAccessChecks(arm11Section1, section[1].size); - patchK11ModuleChecks(arm11Section1, section[1].size); + patchArm11SvcAccessChecks(arm11SvcHandler); + patchK11ModuleChecks(arm11Section1, section[1].size, &freeK11Space); patchP9AccessChecks(process9Offset, process9Size); } - - implementSvcGetCFWInfo(arm11Section1, section[1].size); } static inline void patchLegacyFirm(FirmwareType firmType) @@ -367,18 +388,17 @@ static inline void patchLegacyFirm(FirmwareType firmType) if(DEV_OPTIONS != 2) { //Kernel9/Process9 debugging - patchExceptionHandlersInstall(arm9Section, section[3].size); + patchArm9ExceptionHandlersInstall(arm9Section, section[3].size); patchSvcBreak9(arm9Section, section[3].size, (u32)(section[3].address)); patchKernel9Panic(arm9Section, section[3].size, firmType); } applyLegacyFirmPatches((u8 *)firm, firmType); - if(firmType == TWL_FIRM && CONFIG(5)) - patchTwlBg((u8 *)firm + section[1].offset); + if(firmType == TWL_FIRM && CONFIG(5)) patchTwlBg((u8 *)firm + section[1].offset); } -static inline void patchSafeFirm(void) +static inline void patch2xNativeAndSafeFirm(void) { u8 *arm9Section = (u8 *)firm + section[2].offset; @@ -390,12 +410,12 @@ static inline void patchSafeFirm(void) patchFirmWrites(arm9Section, section[2].size); } - else patchFirmWriteSafe(arm9Section, section[2].size); + else patchOldFirmWrites(arm9Section, section[2].size); if(DEV_OPTIONS != 2) { //Kernel9/Process9 debugging - patchExceptionHandlersInstall(arm9Section, section[2].size); + patchArm9ExceptionHandlersInstall(arm9Section, section[2].size); patchSvcBreak9(arm9Section, section[2].size, (u32)(section[2].address)); } } diff --git a/source/firm.h b/source/firm.h index c53e539..711238b 100644 --- a/source/firm.h +++ b/source/firm.h @@ -54,9 +54,9 @@ typedef enum ConfigurationStatus CREATE_CONFIGURATION = 2 } ConfigurationStatus; -static inline u32 loadFirm(FirmwareType firmType); +static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource); static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh); static inline void patchLegacyFirm(FirmwareType firmType); -static inline void patchSafeFirm(void); +static inline void patch2xNativeAndSafeFirm(void); static inline void copySection0AndInjectSystemModules(FirmwareType firmType); static inline void launchFirm(FirmwareType firmType); \ No newline at end of file diff --git a/source/fs.c b/source/fs.c index 5212f5c..ea9d406 100644 --- a/source/fs.c +++ b/source/fs.c @@ -76,6 +76,11 @@ bool fileWrite(const void *buffer, const char *path, u32 size) return false; } +void fileDelete(const char *path) +{ + f_unlink(path); +} + void createDirectory(const char *path) { f_mkdir(path); diff --git a/source/fs.h b/source/fs.h index 6f80cde..23244fe 100644 --- a/source/fs.h +++ b/source/fs.h @@ -32,6 +32,7 @@ void mountFs(void); u32 fileRead(void *dest, const char *path); u32 getFileSize(const char *path); bool fileWrite(const void *buffer, const char *path, u32 size); +void fileDelete(const char *path); void createDirectory(const char *path); void findDumpFile(const char *path, char *fileName); -void loadPayload(u32 pressed);u32 firmRead(void *dest, u32 firmType); \ No newline at end of file +void loadPayload(u32 pressed);u32 firmRead(void *dest, u32 firmType); diff --git a/source/patches.c b/source/patches.c index dedd583..bb3a11d 100644 --- a/source/patches.c +++ b/source/patches.c @@ -28,35 +28,6 @@ #include "../build/k11modulespatch.h" #include "../build/twl_k11modulespatch.h" -static u32 *arm11ExceptionsPage = NULL; -static u32 *arm11SvcTable = NULL; -static u32 *arm11SvcHandler = NULL; - -static u8 *freeK11Space = NULL; - -static void findArm11ExceptionsPageAndSvcHandlerAndTable(u8 *pos, u32 size) -{ - const u8 arm11ExceptionsPagePattern[] = {0x00, 0xB0, 0x9C, 0xE5}; - - if(arm11ExceptionsPage == NULL) arm11ExceptionsPage = (u32 *)memsearch(pos, arm11ExceptionsPagePattern, size, 4) - 0xB; - if((arm11SvcTable == NULL || arm11SvcHandler == NULL) && arm11ExceptionsPage != NULL) - { - u32 svcOffset = (-((arm11ExceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch - arm11SvcHandler = arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address - while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL) - } -} - -static void findFreeK11Space(u8 *pos, u32 size) -{ - if(freeK11Space == NULL) - { - const u8 pattern[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - - freeK11Space = memsearch(pos, pattern, size, 5) + 1; - } -} - u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) { u8 *off = memsearch(pos, "ess9", size, 4); @@ -68,22 +39,21 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200; } -u32* getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset) -{ - //This function has to succeed. Crash if it doesn't (we'll get an exception dump of it anyways) - - const u8 callExceptionDispatcherPattern[] = {0x0F, 0x00, 0xBD, 0xE8, 0x13, 0x00, 0x02, 0xF1}; - const u8 getTitleIDFromCodeSetPattern[] = {0xDC, 0x05, 0xC0, 0xE1, 0x20, 0x04, 0xA0, 0xE1}; - - *stackAddr = *((u32 *)memsearch(pos, callExceptionDispatcherPattern, size, 8) + 3); - - u32 *loadCodeSet = (u32 *)memsearch(pos, getTitleIDFromCodeSetPattern, size, 8); - while((*loadCodeSet >> 20) != 0xE59 || ((*loadCodeSet >> 12) & 0xF) != 0) //ldr r0, [rX, #offset] - loadCodeSet--; - *codeSetOffset = *loadCodeSet & 0xFFF; - - findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); - return arm11ExceptionsPage; +u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage) +{ + const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; + + *arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB; + u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch + u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address + *arm11SvcHandler = arm11SvcTable; + while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL) + + const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + *freeK11Space = memsearch(pos, pattern2, size, 5) + 1; + + return arm11SvcTable; } void patchSignatureChecks(u8 *pos, u32 size) @@ -134,201 +104,48 @@ void patchFirmWrites(u8 *pos, u32 size) off2[1] = writeBlock[1]; } -void patchFirmWriteSafe(u8 *pos, u32 size) +void patchOldFirmWrites(u8 *pos, u32 size) { - const u16 writeBlockSafe[2] = {0x2400, 0xE01D}; + const u16 writeBlockOld[2] = {0x2400, 0xE01D}; //Look for FIRM writing code const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB}; u16 *off = (u16 *)memsearch(pos, pattern, size, 4); - off[0] = writeBlockSafe[0]; - off[1] = writeBlockSafe[1]; + off[0] = writeBlockOld[0]; + off[1] = writeBlockOld[1]; } -void patchExceptionHandlersInstall(u8 *pos, u32 size) -{ - const u8 pattern[] = { - 0x18, 0x10, 0x80, 0xE5, - 0x10, 0x10, 0x80, 0xE5, - 0x20, 0x10, 0x80, 0xE5, - 0x28, 0x10, 0x80, 0xE5, - }; //i.e when it stores ldr pc, [pc, #-4] - - u32* off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern))); - if(off == NULL) return; - off += sizeof(pattern)/4; - - u32 r0 = 0x08000000; - - for(; *off != 0xE3A01040; off++) //Until mov r1, #0x40 - { - if((*off >> 26) != 0x39 || ((*off >> 16) & 0xF) != 0 || ((*off >> 25) & 1) != 0 || ((*off >> 20) & 5) != 0) - continue; //Discard everything that's not str rX, [r0, #imm](!) - - int rD = (*off >> 12) & 0xF, - offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1), - writeback = (*off >> 21) & 1, - pre = (*off >> 24) & 1; - - u32 addr = r0 + ((pre || !writeback) ? offset : 0); - if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) - *off = 0xE1A00000; //nop - else - *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers - - if(!pre) addr += offset; - if(writeback) r0 = addr; - } -} - -void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr) -{ - //Stub svcBreak with "bkpt 65535" so we can debug the panic. - //Thanks @yellows8 and others for mentioning this idea on #3dsdev. - const u8 svcHandlerPattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr - - u32 *arm9SvcTable = (u32 *)memsearch(pos, svcHandlerPattern, size, 4); - while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL) - u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - k9addr); - *addr = 0xE12FFF7F; -} - -void patchSvcBreak11(u8 *pos, u32 size) -{ - //Same as above, for NFIRM arm11 - - findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); - u32 *addr = (u32 *)(pos + arm11SvcTable[0x3C] - 0xFFF00000); - *addr = 0xE12FFF7F; -} - -void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType) -{ - if(firmType == TWL_FIRM || firmType == AGB_FIRM) - { - u8 *off = pos + ((isN3DS) ? 0x723C : 0x69A8); - *(u16 *)off = 0x4778; //bx pc - *(u16 *)(off + 2) = 0x46C0; //nop - *(u32 *)(off + 4) = 0xE12FFF7E; //bkpt 65534 - } - - else - { - const u8 pattern[] = {0x00, 0x20, 0xA0, 0xE3, 0x02, 0x30, 0xA0, 0xE1, 0x02, 0x10, 0xA0, 0xE1, 0x05, 0x00, 0xA0, 0xE3}; - - u32 *off = (u32 *)memsearch(pos, pattern, size, 16); - *off = 0xE12FFF7E; - } -} - -void patchKernel11Panic(u8 *pos, u32 size) -{ - const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2, 0x00, 0x10, 0x90, 0xE5}; - - u32 *off = (u32 *)memsearch(pos, pattern, size, 8); - *off = 0xE12FFF7E; -} - -void patchArm11SvcAccessChecks(u8 *pos, u32 size) -{ - findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); - - u32 *off = arm11SvcHandler; - while(*off != 0xE11A0E1B) off++; //TST R10, R11,LSL LR - - *off = 0xE3B0A001; //MOVS R10, #1 -} - -//It's mainly Subv's code here: -void patchK11ModuleChecks(u8 *pos, u32 size) -{ - // We have to detour a function in the ARM11 kernel because builtin modules - // are compressed in memory and are only decompressed at runtime. - - findFreeK11Space(pos, size); - u8 *freeSpace = freeK11Space; - freeK11Space += k11modules_size; - - // Inject our code into the free space - memcpy(freeSpace, k11modules, k11modules_size); - - // Find the code that decompresses the .code section of the builtin modules and detour it with a jump to our code - const u8 pattern[] = { 0x00, 0x00, 0x94, 0xE5, 0x18, 0x10, 0x90, 0xE5, 0x28, 0x20, - 0x90, 0xE5, 0x48, 0x00, 0x9D, 0xE5 }; - - u8 *off = memsearch(pos, pattern, size, 16); - - // We couldn't find the code that decompresses the module - if (off == NULL) - return; - - // Inject a jump instruction to our code at the offset we found - // Construct a jump (BL) instruction to our code - u32 offset = ((((u32)freeSpace) - ((u32)off + 8)) >> 2) & 0xFFFFFF; - u32 instruction = offset | (1 << 24) | (0x5 << 25) | (0xE << 28); - - // Write our jump - memcpy(off, &instruction, 4); -} - -void patchP9AccessChecks(u8 *pos, u32 size) -{ - const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39, 0x08, 0x58}; - - u16 *off = (u16 *)memsearch(pos, pattern, size, 6) - 7; - - off[0] = 0x2001; //mov r0, #1 - off[1] = 0x4770; //bx lr -} - - -void patchUnitInfoValueSet(u8 *pos, u32 size) -{ - //Look for UNITINFO value being set during kernel sync - const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13}; - - u8 *off = memsearch(pos, pattern, size, 4); - - off[0] = (isDevUnit) ? 0 : 1; - off[3] = 0xE3; -} - -void reimplementSvcBackdoor(u8 *pos, u32 size) +void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space) { //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 - - findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); + 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 if(!arm11SvcTable[0x7B]) { - findFreeK11Space(pos, size); + memcpy(*freeK11Space, svcBackdoor, 40); - memcpy(freeK11Space, svcBackdoor, 40); - - arm11SvcTable[0x7B] = 0xFFF00000 + freeK11Space - pos; - freeK11Space += 40; + arm11SvcTable[0x7B] = 0xFFF00000 + *freeK11Space - pos; + (*freeK11Space) += 40; } } -void implementSvcGetCFWInfo(u8 *pos, u32 size) -{ - findFreeK11Space(pos, size); - - memcpy(freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size); - CFWInfo *info = (CFWInfo *)memsearch(freeK11Space, "LUMA", svcGetCFWInfo_size, 4); +void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space) +{ + memcpy(*freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size); + + CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_size, 4); const char *rev = REVISION; bool isRelease; @@ -344,12 +161,10 @@ void implementSvcGetCFWInfo(u8 *pos, u32 size) } else isRelease = rev[4] == 0; - info->flags = 1 /* dev branch */ | (((isRelease) ? 1 : 0) << 1) /* is release */; + info->flags = 1 /* dev branch */ | ((isRelease ? 1 : 0) << 1) /* is release */; - findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); - - arm11SvcTable[0x2E] = 0xFFF00000 + freeK11Space - pos; //Stubbed svc - freeK11Space += svcGetCFWInfo_size; + arm11SvcTable[0x2E] = 0xFFF00000 + *freeK11Space - pos; //Stubbed svc + (*freeK11Space) += svcGetCFWInfo_size; } void patchTitleInstallMinVersionCheck(u8 *pos, u32 size) @@ -404,15 +219,15 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType) void patchTwlBg(u8 *pos) { - u8 *dst = pos + ((isN3DS) ? 0xFEA4 : 0xFCA0); + u8 *dst = pos + (isN3DS ? 0xFEA4 : 0xFCA0); memcpy(dst, twl_k11modules, twl_k11modules_size); //Install K11 hook u32 *off = (u32 *)memsearch(dst, "LAUN", twl_k11modules_size, 4); - *off = (isN3DS) ? 0xCDE88 : 0xCD5F8; //Dev SRL launcher offset + *off = isN3DS ? 0xCDE88 : 0xCD5F8; //Dev SRL launcher offset - u16 *src1 = (u16 *)(pos + ((isN3DS) ? 0xE38 : 0xE3C)), - *src2 = (u16 *)(pos + ((isN3DS) ? 0xE54 : 0xE58)); + u16 *src1 = (u16 *)(pos + (isN3DS ? 0xE38 : 0xE3C)), + *src2 = (u16 *)(pos + (isN3DS ? 0xE54 : 0xE58)); //Construct BLX instructions: src1[0] = 0xF000 | ((((u32)dst - (u32)src1 - 4) & (0xFFF << 11)) >> 12); @@ -420,4 +235,163 @@ void patchTwlBg(u8 *pos) src2[0] = 0xF000 | ((((u32)dst - (u32)src2 - 4) & (0xFFF << 11)) >> 12); src2[1] = 0xE800 | ((((u32)dst - (u32)src2 - 4) & 0xFFF) >> 1); +} + +void getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset) +{ + //This function has to succeed. Crash if it doesn't (we'll get an exception dump of it anyways) + + const u8 callExceptionDispatcherPattern[] = {0x0F, 0x00, 0xBD, 0xE8, 0x13, 0x00, 0x02, 0xF1}; + const u8 getTitleIDFromCodeSetPattern[] = {0xDC, 0x05, 0xC0, 0xE1, 0x20, 0x04, 0xA0, 0xE1}; + + *stackAddr = *((u32 *)memsearch(pos, callExceptionDispatcherPattern, size, 8) + 3); + + u32 *loadCodeSet = (u32 *)memsearch(pos, getTitleIDFromCodeSetPattern, size, 8); + while((*loadCodeSet >> 20) != 0xE59 || ((*loadCodeSet >> 12) & 0xF) != 0) //ldr r0, [rX, #offset] + loadCodeSet--; + *codeSetOffset = *loadCodeSet & 0xFFF; +} + +void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size) +{ + const u8 pattern[] = { + 0x18, 0x10, 0x80, 0xE5, + 0x10, 0x10, 0x80, 0xE5, + 0x20, 0x10, 0x80, 0xE5, + 0x28, 0x10, 0x80, 0xE5, + }; //i.e when it stores ldr pc, [pc, #-4] + + u32* off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern))); + if(off == NULL) return; + off += sizeof(pattern)/4; + + u32 r0 = 0x08000000; + + for(; *off != 0xE3A01040; off++) //Until mov r1, #0x40 + { + if((*off >> 26) != 0x39 || ((*off >> 16) & 0xF) != 0 || ((*off >> 25) & 1) != 0 || ((*off >> 20) & 5) != 0) + continue; //Discard everything that's not str rX, [r0, #imm](!) + + int rD = (*off >> 12) & 0xF, + offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1), + writeback = (*off >> 21) & 1, + pre = (*off >> 24) & 1; + + u32 addr = r0 + ((pre || !writeback) ? offset : 0); + if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) + *off = 0xE1A00000; //nop + else + *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers + + if(!pre) addr += offset; + if(writeback) r0 = addr; + } +} + +void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr) +{ + //Stub svcBreak with "bkpt 65535" so we can debug the panic. + //Thanks @yellows8 and others for mentioning this idea on #3dsdev. + const u8 svcHandlerPattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr + + u32 *arm9SvcTable = (u32 *)memsearch(pos, svcHandlerPattern, size, 4); + while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL) + u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - k9addr); + *addr = 0xE12FFF7F; +} + +void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable) +{ + //Same as above, for NFIRM arm11 + + u32 *addr = (u32 *)(pos + arm11SvcTable[0x3C] - 0xFFF00000); + *addr = 0xE12FFF7F; +} + +void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType) +{ + if(firmType == TWL_FIRM || firmType == AGB_FIRM) + { + u8 *off = pos + ((isN3DS) ? 0x723C : 0x69A8); + *(u16 *)off = 0x4778; //bx pc + *(u16 *)(off + 2) = 0x46C0; //nop + *(u32 *)(off + 4) = 0xE12FFF7E; //bkpt 65534 + } + + else + { + const u8 pattern[] = {0x00, 0x20, 0xA0, 0xE3, 0x02, 0x30, 0xA0, 0xE1, 0x02, 0x10, 0xA0, 0xE1, 0x05, 0x00, 0xA0, 0xE3}; + + u32 *off = (u32 *)memsearch(pos, pattern, size, 16); + *off = 0xE12FFF7E; + } +} + +void patchKernel11Panic(u8 *pos, u32 size) +{ + const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2, 0x00, 0x10, 0x90, 0xE5}; + + u32 *off = (u32 *)memsearch(pos, pattern, size, 8); + *off = 0xE12FFF7E; +} + +void patchArm11SvcAccessChecks(u32 *arm11SvcHandler) +{ + u32 *off = arm11SvcHandler; + while(*off != 0xE11A0E1B) off++; //TST R10, R11,LSL LR + + *off = 0xE3B0A001; //MOVS R10, #1 +} + +//It's mainly Subv's code here: +void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space) +{ + // We have to detour a function in the ARM11 kernel because builtin modules + // are compressed in memory and are only decompressed at runtime. + + u8 *freeSpace = *freeK11Space; + *freeK11Space += k11modules_size; + + // Inject our code into the free space + memcpy(freeSpace, k11modules, k11modules_size); + + // Find the code that decompresses the .code section of the builtin modules and detour it with a jump to our code + const u8 pattern[] = { 0x00, 0x00, 0x94, 0xE5, 0x18, 0x10, 0x90, 0xE5, 0x28, 0x20, + 0x90, 0xE5, 0x48, 0x00, 0x9D, 0xE5 }; + + u8 *off = memsearch(pos, pattern, size, 16); + + // We couldn't find the code that decompresses the module + if (off == NULL) + return; + + // Inject a jump instruction to our code at the offset we found + // Construct a jump (BL) instruction to our code + u32 offset = ((((u32)freeSpace) - ((u32)off + 8)) >> 2) & 0xFFFFFF; + u32 instruction = offset | (1 << 24) | (0x5 << 25) | (0xE << 28); + + // Write our jump + memcpy(off, &instruction, 4); +} + +void patchP9AccessChecks(u8 *pos, u32 size) +{ + const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39, 0x08, 0x58}; + + u16 *off = (u16 *)memsearch(pos, pattern, size, 6) - 7; + + off[0] = 0x2001; //mov r0, #1 + off[1] = 0x4770; //bx lr +} + + +void patchUnitInfoValueSet(u8 *pos, u32 size) +{ + //Look for UNITINFO value being set during kernel sync + const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13}; + + u8 *off = memsearch(pos, pattern, size, 4); + + off[0] = (isDevUnit) ? 0 : 1; + off[3] = 0xE3; } \ No newline at end of file diff --git a/source/patches.h b/source/patches.h index f4089e7..ae0cd62 100644 --- a/source/patches.h +++ b/source/patches.h @@ -50,22 +50,24 @@ typedef struct __attribute__((packed)) extern bool isN3DS, isDevUnit; u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr); -u32* getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset); +u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage); 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 patchExceptionHandlersInstall(u8 *pos, u32 size); -void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr); -void patchSvcBreak11(u8 *pos, u32 size); -void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType); -void patchKernel11Panic(u8 *pos, u32 size); -void patchArm11SvcAccessChecks(u8 *pos, u32 size); -void patchK11ModuleChecks(u8 *pos, u32 size); -void patchP9AccessChecks(u8 *pos, u32 size); -void patchUnitInfoValueSet(u8 *pos, u32 size); -void reimplementSvcBackdoor(u8 *pos, u32 size); -void implementSvcGetCFWInfo(u8 *pos, u32 size); +void patchOldFirmWrites(u8 *pos, u32 size); +void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space); +void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space); void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType); void patchTwlBg(u8 *pos); + +void getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset); +void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size); +void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr); +void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable); +void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType); +void patchKernel11Panic(u8 *pos, u32 size); +void patchArm11SvcAccessChecks(u32 *arm11SvcHandler); +void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space); +void patchP9AccessChecks(u8 *pos, u32 size); +void patchUnitInfoValueSet(u8 *pos, u32 size); \ No newline at end of file diff --git a/source/pin.c b/source/pin.c index aac0d41..0f52200 100644 --- a/source/pin.c +++ b/source/pin.c @@ -34,43 +34,29 @@ #include "pin.h" #include "crypto.h" -bool readPin(PINData *out) -{ - if(fileRead(out, "/luma/pin.bin") != sizeof(PINData) || - memcmp(out->magic, "PINF", 4) != 0 || - out->formatVersionMajor != PIN_VERSIONMAJOR || - out->formatVersionMinor != PIN_VERSIONMINOR) - return false; - - u8 __attribute__((aligned(4))) zeroes[16] = {0}; - u8 __attribute__((aligned(4))) tmp[32]; - - computePINHash(tmp, zeroes, 1); - - return memcmp(out->testHash, tmp, 32) == 0; //test vector verification (SD card has (or hasn't) been used on another console) -} - -static inline char PINKeyToLetter(u32 pressed) +static char pinKeyToLetter(u32 pressed) { const char keys[] = "AB--------XY"; u32 i; - __asm__ volatile("clz %[i], %[pressed]" : [i] "=r" (i) : [pressed] "r" (pressed)); + for(i = 31; pressed > 1; i--) pressed /= 2; return keys[31 - i]; } -void newPin(void) +void newPin(bool allowSkipping) { clearScreens(); - drawString("Enter your NEW PIN: ", 10, 10, COLOR_WHITE); + char *title = allowSkipping ? "Press START to skip or enter a new PIN" : "Enter a new PIN to proceed"; + drawString(title, 10, 10, COLOR_TITLE); + drawString("PIN: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE); - // Set the default value as 0x00 so we can check if there are any unentered characters. - u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; // pad to AES block length + //Pad to AES block length with zeroes + u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; u32 cnt = 0; - int charDrawPos = 20 * SPACING_X; + int charDrawPos = 5 * SPACING_X; while(cnt < PIN_LENGTH) { @@ -79,16 +65,19 @@ void newPin(void) { pressed = waitInput(); } - while(!(pressed & PIN_BUTTONS & ~BUTTON_START)); + while(!(pressed & PIN_BUTTONS)); - pressed &= PIN_BUTTONS & ~BUTTON_START; + pressed &= PIN_BUTTONS; + if(!allowSkipping) pressed &= ~BUTTON_START; + if(pressed & BUTTON_START) return; if(!pressed) continue; - char key = PINKeyToLetter(pressed); - enteredPassword[cnt++] = (u8)key; // add character to password. - // visualize character on screen. - drawCharacter(key, 10 + charDrawPos, 10, COLOR_WHITE); + char key = pinKeyToLetter(pressed); + enteredPassword[cnt++] = (u8)key; //Add character to password + + //Visualize character on screen + drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE); charDrawPos += 2 * SPACING_X; } @@ -100,10 +89,10 @@ void newPin(void) pin.formatVersionMajor = PIN_VERSIONMAJOR; pin.formatVersionMinor = PIN_VERSIONMINOR; - computePINHash(tmp, zeroes, 1); + computePinHash(tmp, zeroes, 1); memcpy(pin.testHash, tmp, 32); - computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); + computePinHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); memcpy(pin.hash, tmp, 32); if(!fileWrite(&pin, "/luma/pin.bin", sizeof(PINData))) @@ -112,18 +101,29 @@ void newPin(void) if(!fileWrite(&pin, "/luma/pin.bin", sizeof(PINData))) error("Error writing the PIN file"); } - - while(HID_PAD & PIN_BUTTONS); } -void verifyPin(PINData *in) +bool verifyPin(void) { initScreens(); - drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); - drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE); + PINData pin; - // Set the default characters as 0x00 so we can check if there are any unentered characters. + if(fileRead(&pin, "/luma/pin.bin") != sizeof(PINData) || + memcmp(pin.magic, "PINF", 4) != 0 || + pin.formatVersionMajor != PIN_VERSIONMAJOR || + pin.formatVersionMinor != PIN_VERSIONMINOR) + return false; + + u8 __attribute__((aligned(4))) zeroes[16] = {0}; + u8 __attribute__((aligned(4))) tmp[32]; + + computePinHash(tmp, zeroes, 1); + + //Test vector verification (SD card has, or hasn't been used on another console) + if(memcmp(pin.testHash, tmp, 32) != 0) return false; + + //Pad to AES block length with zeroes u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; u32 cnt = 0; @@ -132,6 +132,9 @@ void verifyPin(PINData *in) while(!unlock) { + drawString("Press START to shutdown or enter PIN to proceed", 10, 10, COLOR_TITLE); + drawString("PIN: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE); + u32 pressed; do { @@ -141,22 +144,21 @@ void verifyPin(PINData *in) if(pressed & BUTTON_START) mcuPowerOff(); - pressed &= PIN_BUTTONS & ~BUTTON_START; + pressed &= PIN_BUTTONS; + if(!pressed) continue; - char key = PINKeyToLetter(pressed); - enteredPassword[cnt++] = (u8)key; // add character to password. + char key = pinKeyToLetter(pressed); + enteredPassword[cnt++] = (u8)key; //Add character to password - // visualize character on screen. + //Visualize character on screen drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE); charDrawPos += 2 * SPACING_X; if(cnt >= PIN_LENGTH) { - u8 __attribute__((aligned(4))) tmp[32]; - - computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); - unlock = memcmp(in->hash, tmp, 32) == 0; + computePinHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); + unlock = memcmp(pin.hash, tmp, 32) == 0; if(!unlock) { @@ -165,10 +167,10 @@ void verifyPin(PINData *in) clearScreens(); - drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); - drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE); - drawString("Wrong pin! Try again!", 10, 10 + 3 * SPACING_Y, COLOR_RED); + drawString("Wrong PIN, try again", 10, 10 + 4 * SPACING_Y, COLOR_RED); } } } + + return true; } \ No newline at end of file diff --git a/source/pin.h b/source/pin.h index ce6f370..b8aa6de 100644 --- a/source/pin.h +++ b/source/pin.h @@ -43,6 +43,5 @@ typedef struct __attribute__((packed)) u8 hash[32]; } PINData; -bool readPin(PINData* out); -void newPin(void); -void verifyPin(PINData *in); \ No newline at end of file +void newPin(bool allowSkipping); +bool verifyPin(void); \ No newline at end of file diff --git a/source/start.s b/source/start.s index 5591421..b18a7bc 100644 --- a/source/start.s +++ b/source/start.s @@ -26,8 +26,8 @@ _start: b start -.global launchedFirmTIDLow -launchedFirmTIDLow: +.global launchedFirmTidLow +launchedFirmTidLow: .hword 0, 0, 0, 0, 0, 0, 0, 0 start: diff --git a/source/types.h b/source/types.h index a6946d5..223d38c 100644 --- a/source/types.h +++ b/source/types.h @@ -49,5 +49,6 @@ typedef enum FirmwareType NATIVE_FIRM = 0, TWL_FIRM = 1, AGB_FIRM = 2, - SAFE_FIRM = 3 + SAFE_FIRM = 3, + NATIVE_FIRM2X = 4 } FirmwareType; \ No newline at end of file