diff --git a/Makefile b/Makefile index 883e3baf1..edf5fbf6c 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ $(call rwildcard, $(dir_source), *.s *.c))) bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/svcGetCFWInfopatch.h $(dir_build)/twl_k11modulespatch.h \ - $(dir_build)/injector.h $(dir_build)/loader.h + $(dir_build)/injector.h $(dir_build)/loader.h .PHONY: all all: a9lh @@ -92,7 +92,7 @@ $(dir_build)/loader.h: $(dir_loader)/Makefile @$(MAKE) -C $(dir_loader) @bin2c -o $@ -n loader $(@D)/loader.bin -$(dir_build)/memory.o: CFLAGS += -O3 +$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3 $(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\"" $(dir_build)/patches.o: CFLAGS += -DREVISION=\"$(revision)\" -DCOMMIT_HASH="0x$(commit)" diff --git a/injector/Makefile b/injector/Makefile index d568a733a..6fa584441 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) 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 000000000..30b4781a1 --- /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 000000000..8337620c0 --- /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 7d00f6777..8ef687b80 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 485b19ee3..9ebd350d6 100644 --- a/injector/source/patcher.h +++ b/injector/source/patcher.h @@ -15,19 +15,4 @@ #define OPTION_ERRDISP 12 #define OPTION_TESTMENU 13 - -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 4605d2555..f6d9caea0 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 a7e6cfa11..0836c1fb7 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 799d156eb..9174a2334 100644 --- a/patches/reboot.s +++ b/patches/reboot.s @@ -5,10 +5,14 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB .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. - ; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle - ; - *(sp + 0x3A8 - 0x198) + 0x28: fread function. + ; Interesting registers and locations to keep in mind, set just before this code is ran: + ; - r1: FIRM path in exefs. + ; - r7: pointer to file object + ; - *r7: vtable + ; - *(vtable + 0x28): fread function + ; - *(r7 + 8): file handle + + mov r8, r1 pxi_wait_recv: ldr r2, =0x44846 @@ -47,7 +51,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB cmp r4, #0 movne r3, #0x12000 ; Skip the first 0x12000 bytes. moveq r3, payload_maxsize - ldr r6, [sp, #0x3A8-0x198] + ldr r6, [r7] ldr r6, [r6, #0x28] blx r6 cmp r4, #0 @@ -55,8 +59,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB bne read_payload ; Go read the real payload. ; Copy the low TID (in UTF-16) of the wanted firm to the 5th byte of the payload - add r0, sp, #0x3A8 - 0x70 - add r0, 0x1A + add r0, r8, 0x1A add r1, r0, #0x10 ldr r2, =payload_addr + 4 copy_TID_low: @@ -75,7 +78,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB goto_reboot: ; Jump to reboot code ldr r0, =(kernelcode_start - goto_reboot - 12) - add r0, pc + add r0, pc ; pc is two instructions ahead of the instruction being executed (12 = 2*4 + 4) swi 0x7B die: @@ -122,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 117888d7c..d808b27ec 100644 --- a/patches/twl_k11modules.s +++ b/patches/twl_k11modules.s @@ -44,7 +44,7 @@ patch: cmp r7, r6 bne end - ldr r7, =#0xabcdabcd ; offset of the dev launcher (will be replaced later) + ldr r7, [launcher] ; offset of the dev launcher (will be replaced later) add r7, r9 adr r5, patchesStart @@ -81,7 +81,9 @@ patch: end: - pop {r0-r11, pc} + pop {r0-r11, pc} + +launcher: .ascii "LAUN" .align 2 .thumb diff --git a/source/config.c b/source/config.c index 0a042fab9..5da3e1e1c 100644 --- a/source/config.c +++ b/source/config.c @@ -21,12 +21,50 @@ */ #include "config.h" +#include "memory.h" +#include "fs.h" #include "utils.h" #include "screen.h" #include "draw.h" #include "buttons.h" +#include "pin.h" -void configureCFW(void) +bool readConfig(const char *configPath) +{ + if(fileRead(&configData, configPath) != sizeof(cfgData) || + memcmp(configData.magic, "CONF", 4) != 0 || + configData.formatVersionMajor != CONFIG_VERSIONMAJOR || + configData.formatVersionMinor != CONFIG_VERSIONMINOR) + { + configData.config = 0; + return false; + } + + return true; +} + +void writeConfig(const char *configPath, u32 configTemp, ConfigurationStatus needConfig) +{ + /* If the configuration is different from previously, overwrite it. + Just the no-forcing flag being set is not enough */ + if(needConfig == CREATE_CONFIGURATION || (configTemp & 0xFFFFFFEF) != configData.config) + { + if(needConfig == CREATE_CONFIGURATION) + { + memcpy(configData.magic, "CONF", 4); + configData.formatVersionMajor = CONFIG_VERSIONMAJOR; + configData.formatVersionMinor = CONFIG_VERSIONMINOR; + } + + //Merge the new options and new boot configuration + configData.config = (configData.config & 0xFFFFFFC0) | (configTemp & 0x3F); + + if(!fileWrite(&configData, configPath, sizeof(cfgData))) + error("Error writing the configuration file"); + } +} + +void configMenu(bool oldPinStatus) { initScreens(); @@ -41,16 +79,15 @@ void configureCFW(void) "( ) Use second EmuNAND as default", //2 "( ) Enable region/language emu. and ext. .code", //3 "( ) Show current NAND/kernel in System Settings", //4 - "( ) Show GBA boot screen in patched AGB_FIRM", //5 - "( ) Display splash screen before payloads", //6 - "( ) Use a PIN", //7 - "( ) Enable experimental TwlBg patches", //8 + "( ) Enable experimental TwlBg patches",//5 + "( ) Show GBA boot screen in patched AGB_FIRM", //6 + "( ) Display splash screen before payloads", //7 + "( ) Use a PIN", //8 "( ) Region free", //9 "( ) Try to block mandatory updates", //10 "( ) SecureInfo: sigpatch + use _C if available", //11 "( ) Verbose errors (ErrDisp)", //12 "( ) Force TestMenu as home screen" }; //13 - struct multiOption { int posXs[4]; int posY; @@ -190,14 +227,19 @@ void configureCFW(void) } //Preserve the last-used boot options (last 12 bits) - config &= 0x3F; + configData.config &= 0x3F; //Parse and write the new configuration for(u32 i = 0; i < multiOptionsAmount; i++) - config |= multiOptions[i].enabled << (i * 2 + 6); + configData.config |= multiOptions[i].enabled << (i * 2 + 6); for(u32 i = 0; i < singleOptionsAmount; i++) - config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16); + configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16); + + if(CONFIG(8)) newPin(oldPinStatus); + else if(oldPinStatus) fileDelete(PIN_LOCATION); //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 95fa0c681..f3e2bb231 100644 --- a/source/config.h +++ b/source/config.h @@ -24,10 +24,30 @@ #include "types.h" -#define CONFIG(a) (((config >> (a + 16)) & 1) != 0) -#define MULTICONFIG(a) ((config >> (a * 2 + 6)) & 3) -#define BOOTCONFIG(a, b) ((config >> a) & b) +#define CONFIG(a) (((configData.config >> (a + 16)) & 1) != 0) +#define MULTICONFIG(a) ((configData.config >> (a * 2 + 6)) & 3) +#define BOOTCONFIG(a, b) ((configData.config >> a) & b) -extern u32 config; +#define CONFIG_VERSIONMAJOR 1 +#define CONFIG_VERSIONMINOR 0 -void configureCFW(void); \ No newline at end of file +typedef struct __attribute__((packed)) +{ + char magic[4]; + u16 formatVersionMajor, formatVersionMinor; + + u32 config; +} cfgData; + +typedef enum ConfigurationStatus +{ + DONT_CONFIGURE = 0, + MODIFY_CONFIGURATION = 1, + CREATE_CONFIGURATION = 2 +} ConfigurationStatus; + +extern cfgData configData; + +bool readConfig(const char *configPath); +void writeConfig(const char *configPath, u32 configTemp, ConfigurationStatus needConfig); +void configMenu(bool oldPinStatus); \ No newline at end of file diff --git a/source/crypto.c b/source/crypto.c index 70140273b..e6a4751a5 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 0b30bf9ba..c7221c8d6 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/draw.c b/source/draw.c index 024063320..518904952 100644 --- a/source/draw.c +++ b/source/draw.c @@ -26,20 +26,12 @@ */ #include "draw.h" +#include "strings.h" #include "screen.h" #include "utils.h" #include "fs.h" #include "font.h" -static inline int strlen(const char *string) -{ - char *stringEnd = (char *)string; - - while(*stringEnd) stringEnd++; - - return stringEnd - string; -} - bool loadSplash(void) { //Don't delay boot nor init the screens if no splash image is on the SD diff --git a/source/emunand.c b/source/emunand.c index 2b6f02c01..0115a1ca1 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 1930c342a..543fbe060 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 90a327e1a..3b77ae677 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 62d5b81fc..f4a2f7905 100755 --- a/source/firm.c +++ b/source/firm.c @@ -35,18 +35,18 @@ #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; -u32 config, - emuOffset; +u32 emuOffset; bool isN3DS, isDevUnit, isFirmlaunch; +cfgData configData; FirmwareSource firmSource; void main(void) @@ -72,17 +72,17 @@ void main(void) const char configPath[] = "/puma/config.bin"; //Attempt to read the configuration file - needConfig = fileRead(&config, configPath) ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION; + needConfig = readConfig(configPath) ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION; //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); @@ -99,11 +99,8 @@ void main(void) //Determine if booting with A9LH isA9lh = !PDN_SPI_CNT; - //Determine if the user chose to use the SysNAND FIRM as default for a R boot - bool useSysAsDefault = isA9lh ? CONFIG(1) : false; - //Save old options and begin saving the new boot configuration - configTemp = (config & 0xFFFFFFC0) | ((u32)isA9lh << 3); + configTemp = (configData.config & 0xFFFFFFC0) | ((u32)isA9lh << 3); //If it's a MCU reboot, try to force boot options if(isA9lh && CFG_BOOTENV) @@ -112,7 +109,7 @@ void main(void) if(CFG_BOOTENV == 7) { nandType = FIRMWARE_SYSNAND; - firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCONFIG(2, 1); + firmSource = CONFIG(1) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCONFIG(2, 1); needConfig = DONT_CONFIGURE; //Flag to prevent multiple boot options-forcing @@ -121,7 +118,7 @@ void main(void) /* Else, force the last used boot options unless a button is pressed or the no-forcing flag is set */ - else if(!pressed && !BOOTCONFIG(4, 1)) + else if(needConfig != CREATE_CONFIGURATION && !pressed && !BOOTCONFIG(4, 1)) { nandType = (FirmwareSource)BOOTCONFIG(0, 3); firmSource = (FirmwareSource)BOOTCONFIG(2, 1); @@ -132,23 +129,14 @@ void main(void) //Boot options aren't being forced if(needConfig != DONT_CONFIGURE) { - PINData pin; - - bool pinExists = CONFIG(7) && 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) { - configureCFW(); - - if(!pinExists && CONFIG(7)) newPin(); - - chrono(2); + configMenu(pinExists); //Update pressed buttons pressed = HID_PAD; @@ -161,24 +149,34 @@ 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(6) && loadSplash()) pressed = HID_PAD; + 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); - if(!CONFIG(6)) loadSplash(); + if(!CONFIG(7)) loadSplash(); + + //Determine if the user chose to use the SysNAND FIRM as default for a R boot + bool useSysAsDefault = isA9lh ? CONFIG(1) : false; //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, @@ -199,35 +197,21 @@ 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) { configTemp |= (u32)nandType | ((u32)firmSource << 2); - - /* If the configuration is different from previously, overwrite it. - Just the no-forcing flag being set is not enough */ - if((configTemp & 0xFFFFFFEF) != config) - { - //Merge the new options and new boot configuration - config = (config & 0xFFFFFFC0) | (configTemp & 0x3F); - - if(!fileWrite(&config, configPath, 4)) - { - createDirectory("puma"); - if(!fileWrite(&config, configPath, 4)) - error("Error writing the configuration file"); - } - } + writeConfig(configPath, configTemp, needConfig); } - u32 firmVersion = loadFirm(firmType); + u32 firmVersion = loadFirm(&firmType, firmSource); switch(firmType) { @@ -235,7 +219,8 @@ void main(void) patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh); break; case SAFE_FIRM: - patchSafeFirm(); + case NATIVE_FIRM1X2X: + if(isA9lh) patch1x2xNativeAndSafeFirm(); break; default: //Skip patching on unsupported O3DS AGB/TWL FIRMs @@ -246,27 +231,38 @@ void main(void) launchFirm(firmType); } -static inline u32 loadFirm(FirmwareType firmType) +static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource) { section = firm->section; - //Load FIRM from CTRNAND, unless it's an O3DS and we're loading a pre-5.0 NATIVE FIRM - u32 firmVersion = firmRead(firm, (u32)firmType); + //Load FIRM from CTRNAND + u32 firmVersion = firmRead(firm, (u32)*firmType); - if(!isN3DS && firmType == NATIVE_FIRM && firmVersion < 0x25) + if(!isN3DS && *firmType == NATIVE_FIRM) { - //We can't boot < 3.x NANDs + //We can't boot < 3.x EmuNANDs if(firmVersion < 0x18) - error("An old unsupported NAND has been detected.\nPuma33DS is unable to boot it.\nConsider flashing a more updated backup\n or transferring a newer CTRNAND."); + { + if(firmSource != FIRMWARE_SYSNAND) + error("A pre 4.0 EmuNAND has been detected.\nPuma33DS is unable to boot it"); - //We can't boot a 4.x NATIVE_FIRM, load one from SD - if(!fileRead(firm, "/puma/firmware.bin") || (((u32)section[2].address >> 8) & 0xFF) != 0x68) - error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /puma to boot.\nConsider updating to 9.6+."); + if(BOOTCONFIG(5, 1)) error("SAFE_MODE is not supported on 1.x/2.x FIRM"); - //No assumption regarding FIRM version - firmVersion = 0xffffffff; + *firmType = NATIVE_FIRM1X2X; + } + + //We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD + else if(firmVersion < 0x25) + { + if(!fileRead(firm, "/puma/firmware.bin") || (((u32)section[2].address >> 8) & 0xFF) != 0x68) + error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /puma to boot"); + + //No assumption regarding FIRM version + firmVersion = 0xFFFFFFFF; + } } - else decryptExeFs((u8 *)firm); + + if(firmVersion != 0xFFFFFFFF) decryptExeFs((u8 *)firm); return firmVersion; } @@ -291,6 +287,10 @@ 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 *arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &freeK11Space); + //Apply signature patches patchSignatureChecks(process9Offset, process9Size); @@ -298,7 +298,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 @@ -314,10 +314,10 @@ 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, section[1].size); + implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, &freeK11Space); } static inline void patchLegacyFirm(FirmwareType firmType) @@ -331,11 +331,10 @@ static inline void patchLegacyFirm(FirmwareType firmType) applyLegacyFirmPatches((u8 *)firm, firmType); - if(firmType == TWL_FIRM && CONFIG(8)) - 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 patch1x2xNativeAndSafeFirm(void) { u8 *arm9Section = (u8 *)firm + section[2].offset; @@ -347,38 +346,35 @@ static inline void patchSafeFirm(void) patchFirmWrites(arm9Section, section[2].size); } - else patchFirmWriteSafe(arm9Section, section[2].size); + else patchOldFirmWrites(arm9Section, section[2].size); } static inline void copySection0AndInjectSystemModules(void) { - u8 *arm11Section0 = (u8 *)firm + section[0].offset; + u32 srcModuleSize, + dstModuleSize; - struct + for(u8 *src = (u8 *)firm + section[0].offset, *srcEnd = src + section[0].size, *dst = section[0].address; + src < srcEnd; src += srcModuleSize, dst += dstModuleSize) { - u32 size; - const u8 *addr; - } modules[5]; + srcModuleSize = *(u32 *)(src + 0x104) * 0x200; + char *moduleName = (char *)(src + 0x200); - u32 n = 0, - loaderIndex; - u8 *pos = arm11Section0; + void *module; - for(u8 *end = pos + section[0].size; pos < end; pos += modules[n++].size) - { - modules[n].addr = pos; - modules[n].size = *(u32 *)(pos + 0x104) * 0x200; + if(memcmp(moduleName, "loader", 6) == 0) + { + module = (void *)injector; + dstModuleSize = injector_size; + } + else + { + module = src; + dstModuleSize = srcModuleSize; + } - if(memcmp(modules[n].addr + 0x200, "loader", 7) == 0) loaderIndex = n; + memcpy(dst, module, dstModuleSize); } - - modules[loaderIndex].addr = injector; - modules[loaderIndex].size = injector_size; - - pos = section[0].address; - - for(u32 i = 0; i < n; pos += modules[i++].size) - memcpy(pos, modules[i].addr, modules[i].size); } static inline void launchFirm(FirmwareType firmType) diff --git a/source/firm.h b/source/firm.h index 2898a3531..4fdce8b7d 100644 --- a/source/firm.h +++ b/source/firm.h @@ -24,6 +24,11 @@ #include "types.h" +#define CFG_BOOTENV (*(vu32 *)0x10010000) +#define CFG_UNITINFO (*(vu8 *)0x10010010) +#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC) +#define PDN_SPI_CNT (*(vu32 *)0x101401C0) + //FIRM Header layout typedef struct firmSectionHeader { u32 offset; @@ -41,17 +46,10 @@ typedef struct firmHeader { u8 reserved2[0x30]; firmSectionHeader section[4]; } firmHeader; - -typedef enum ConfigurationStatus -{ - DONT_CONFIGURE = 0, - MODIFY_CONFIGURATION = 1, - 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 patch1x2xNativeAndSafeFirm(void); static inline void copySection0AndInjectSystemModules(void); static inline void launchFirm(FirmwareType firmType); \ No newline at end of file diff --git a/source/fs.c b/source/fs.c index b7383c671..9dcb95c96 100644 --- a/source/fs.c +++ b/source/fs.c @@ -22,6 +22,7 @@ #include "fs.h" #include "memory.h" +#include "strings.h" #include "cache.h" #include "screen.h" #include "fatfs/ff.h" @@ -64,7 +65,9 @@ bool fileWrite(const void *buffer, const char *path, u32 size) { FIL file; - if(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS) == FR_OK) + FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS); + + if(result == FR_OK) { unsigned int written; f_write(&file, buffer, size, &written); @@ -72,13 +75,26 @@ bool fileWrite(const void *buffer, const char *path, u32 size) return true; } + else if(result == FR_NO_PATH) + { + char folder[256]; - return false; + for(u32 i = 1; path[i] != 0; i++) + if(path[i] == '/') + { + memcpy(folder, path, i); + folder[i] = 0; + f_mkdir(folder); + } + + return fileWrite(buffer, path, size); + } + else return false; } -void createDirectory(const char *path) +void fileDelete(const char *path) { - f_mkdir(path); + f_unlink(path); } void loadPayload(u32 pressed) @@ -112,8 +128,8 @@ void loadPayload(u32 pressed) memcpy(loaderAddress, loader, loader_size); - path[14] = '/'; - memcpy(&path[15], info.altname, 13); + concatenateStrings(path, "/"); + concatenateStrings(path, info.altname); loaderAddress[1] = fileRead((void *)0x24F00000, path); @@ -131,8 +147,9 @@ u32 firmRead(void *dest, u32 firmType) { "00000202", "20000202" }, { "00000003", "20000003" }}; - char path[48] = "1:/title/00040138/00000000/content"; - memcpy(&path[18], firmFolders[firmType][isN3DS ? 1 : 0], 8); + char path[48] = "1:/title/00040138/"; + concatenateStrings(path, firmFolders[firmType][isN3DS ? 1 : 0]); + concatenateStrings(path, "/content"); DIR dir; FILINFO info; @@ -162,19 +179,10 @@ u32 firmRead(void *dest, u32 firmType) f_closedir(&dir); //Complete the string with the .app name - memcpy(&path[34], "/00000000.app", 14); - - //Last digit of the .app - u32 i = 42; + concatenateStrings(path, "/00000000.app"); //Convert back the .app name from integer to array - u32 tempVersion = firmVersion; - while(tempVersion) - { - static const char hexDigits[] = "0123456789ABCDEF"; - path[i--] = hexDigits[tempVersion & 0xF]; - tempVersion >>= 4; - } + hexItoa(firmVersion, &path[35]); fileRead(dest, path); diff --git a/source/fs.h b/source/fs.h index 805c099ab..d74f24f0b 100644 --- a/source/fs.h +++ b/source/fs.h @@ -32,6 +32,6 @@ 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 createDirectory(const char *path); +void fileDelete(const char *path); void loadPayload(u32 pressed); u32 firmRead(void *dest, u32 firmType); \ No newline at end of file diff --git a/source/memory.c b/source/memory.c index e7a05c92f..03a36ae8c 100644 --- a/source/memory.c +++ b/source/memory.c @@ -41,7 +41,7 @@ void memset32(void *dest, u32 filler, u32 size) { u32 *dest32 = (u32 *)dest; - for (u32 i = 0; i < size / 4; i++) + for(u32 i = 0; i < size / 4; i++) dest32[i] = filler; } diff --git a/source/patches.c b/source/patches.c index cb8bad5e6..b6f3e5b07 100644 --- a/source/patches.c +++ b/source/patches.c @@ -27,47 +27,6 @@ #include "../build/svcGetCFWInfopatch.h" #include "../build/twl_k11modulespatch.h" -static u32 *arm11ExceptionsPage = NULL; -static u32 *arm11SvcTable = NULL; -static u32 *arm11SvcHandler = NULL; - -static u8 *freeK11Space = NULL; //other than the one used for svcBackdoor - -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 bogus_pattern[] = { 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, - 0x2F, 0xE1, 0x00, 0x10, 0xA0, 0xE3, 0x00, 0x10, 0xC0, 0xE5, - 0x1E, 0xFF, 0x2F, 0xE1 }; - - u32 *someSpace = (u32 *)memsearch(pos, bogus_pattern, size, 24); - - // We couldn't find the place where to begin our search of an empty block - if (someSpace == NULL) - return; - - // Advance until we reach the padding area (filled with 0xFF) - u32 *freeSpace; - for(freeSpace = someSpace; *freeSpace != 0xFFFFFFFF; freeSpace++); - - freeK11Space = (u8 *)freeSpace; - } -} - u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) { u8 *off = memsearch(pos, "ess9", size, 4); @@ -79,6 +38,22 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200; } +u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space) +{ + const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; + + u32 *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 + 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) { const u16 sigPatch[2] = {0x2000, 0x4770}; @@ -98,9 +73,9 @@ void patchSignatureChecks(u8 *pos, u32 size) void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr) { //Look for firmlaunch code - const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2}; + const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90}; - u8 *off = memsearch(pos, pattern, size, 4) - 0x10; + u8 *off = memsearch(pos, pattern, size, 4) - 0x13; //Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1 u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr); @@ -127,60 +102,53 @@ 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 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]) { - u32 *freeSpace; - for(freeSpace = arm11ExceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++); - - memcpy(freeSpace, svcBackdoor, 40); + memcpy(*freeK11Space, svcBackdoor, 40); - arm11SvcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)arm11ExceptionsPage); + arm11SvcTable[0x7B] = 0xFFF00000 + *freeK11Space - pos; + (*freeK11Space) += 40; } } -void implementSvcGetCFWInfo(u8 *pos, u32 size) +void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space) { - const char *rev = REVISION; - bool isRelease; + memcpy(*freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size); - findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); - findFreeK11Space(pos, size); - - memcpy(freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size); + CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_size, 4); - CFWInfo *info = (CFWInfo *)memsearch(freeK11Space, "LUMA", svcGetCFWInfo_size, 4); + const char *rev = REVISION; + bool isRelease; info->commitHash = COMMIT_HASH; - info->config = config; + info->config = configData.config; info->versionMajor = (u8)(rev[1] - '0'); info->versionMinor = (u8)(rev[3] - '0'); if(rev[4] == '.') @@ -188,13 +156,12 @@ void implementSvcGetCFWInfo(u8 *pos, u32 size) info->versionBuild = (u8)(rev[5] - '0'); isRelease = rev[6] == 0; } - else - isRelease = rev[4] == 0; + else isRelease = rev[4] == 0; - info->flags = 0 /* master branch */ | (((isRelease) ? 1 : 0) << 1) /* is release */; + info->flags = 0 /* master branch */ | ((isRelease ? 1 : 0) << 1) /* is release */; - 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) @@ -227,7 +194,7 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType) /* 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 == TWL_FIRM ? (sizeof(twlPatches) / sizeof(patchData)) : - (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(5)); + (sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6)); const patchData *patches = firmType == TWL_FIRM ? twlPatches : agbPatches; //Patch @@ -249,13 +216,15 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType) void patchTwlBg(u8 *pos) { - u8 *dst = pos + ((isN3DS) ? 0xFEA4 : 0xFCA0); - u16 *src1 = (u16 *)(pos + ((isN3DS) ? 0xE38 : 0xE3C)), *src2 = (u16 *)(pos + ((isN3DS) ? 0xE54 : 0xE58)); - memcpy(dst, twl_k11modules, twl_k11modules_size); //install k11 hook + u8 *dst = pos + (isN3DS ? 0xFEA4 : 0xFCA0); + + memcpy(dst, twl_k11modules, twl_k11modules_size); //Install K11 hook - u32 *off; - for(off = (u32 *)dst; *off != 0xABCDABCD; off++); - *off = (isN3DS) ? 0xCDE88 : 0xCD5F8; //dev SRL launcher offset + u32 *off = (u32 *)memsearch(dst, "LAUN", twl_k11modules_size, 4); + *off = isN3DS ? 0xCDE88 : 0xCD5F8; //Dev SRL launcher offset + + 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); diff --git a/source/patches.h b/source/patches.h index 5b162c4b2..65a0e7b1f 100644 --- a/source/patches.h +++ b/source/patches.h @@ -48,15 +48,15 @@ typedef struct __attribute__((packed)) } CFWInfo; extern bool isN3DS; -extern u32 config; u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr); +u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space); 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 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); \ No newline at end of file diff --git a/source/pin.c b/source/pin.c index a073a8625..b0c69c527 100644 --- a/source/pin.c +++ b/source/pin.c @@ -34,40 +34,29 @@ #include "pin.h" #include "crypto.h" -bool readPin(PINData *out) -{ - u8 __attribute__((aligned(4))) zeroes[16] = {0}; - u8 __attribute__((aligned(4))) tmp[32] = {0}; - - if(fileRead(out, PIN_LOCATION) != sizeof(PINData)) return false; - if(memcmp(out->magic, "PINF", 4) != 0) return false; - - 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) { @@ -76,46 +65,61 @@ 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; } - PINData pin = {0}; - u8 __attribute__((aligned(4))) tmp[32] = {0}; + PINData pin; + u8 __attribute__((aligned(4))) tmp[32]; u8 __attribute__((aligned(4))) zeroes[16] = {0}; memcpy(pin.magic, "PINF", 4); - pin.formatVersionMajor = 1; - pin.formatVersionMinor = 0; + 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); - fileWrite(&pin, PIN_LOCATION, sizeof(PINData)); - - while(HID_PAD & PIN_BUTTONS); + if(!fileWrite(&pin, PIN_LOCATION, sizeof(PINData))) + error("Error writing the PIN file"); } -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; + + if(fileRead(&pin, PIN_LOCATION) != 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); - // Set the default characters as 0x00 so we can check if there are any unentered characters. + //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; @@ -124,6 +128,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 { @@ -133,22 +140,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] = {0}; - - 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) { @@ -157,10 +163,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 b094548b6..23cb5b207 100644 --- a/source/pin.h +++ b/source/pin.h @@ -30,9 +30,12 @@ #include "types.h" -#define PIN_LENGTH 4 #define PIN_LOCATION "/puma/pin.bin" +#define PIN_LENGTH 4 +#define PIN_VERSIONMAJOR 1 +#define PIN_VERSIONMINOR 0 + typedef struct __attribute__((packed)) { char magic[4]; @@ -42,6 +45,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/screen.c b/source/screen.c index 6b121a600..ffcf8636c 100644 --- a/source/screen.c +++ b/source/screen.c @@ -242,7 +242,7 @@ void initScreens(void) if(PDN_GPU_CNT == 1) { - flushDCacheRange(&config, 4); + flushDCacheRange(&configData, sizeof(cfgData)); flushDCacheRange((void *)fb, sizeof(struct fb)); invokeArm11Function(ARM11); diff --git a/source/screen.h b/source/screen.h index 1e86289bc..720d01e13 100644 --- a/source/screen.h +++ b/source/screen.h @@ -29,6 +29,8 @@ #include "types.h" +#define PDN_GPU_CNT (*(vu8 *)0x10141200) + #define ARM11_STUB_ADDRESS (0x25000000 - 0x30) //It's currently only 0x28 bytes large. We're putting 0x30 just to be sure here #define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)(); diff --git a/source/start.s b/source/start.s index f0f7f3c94..3a5b4df8a 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/strings.c b/source/strings.c new file mode 100644 index 000000000..9d0bb2da3 --- /dev/null +++ b/source/strings.c @@ -0,0 +1,55 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +#include "strings.h" +#include "memory.h" + +int strlen(const char *string) +{ + char *stringEnd = (char *)string; + + while(*stringEnd) stringEnd++; + + return stringEnd - string; +} + +void concatenateStrings(char *destination, const char *source) +{ + int i = strlen(source), + j = strlen(destination); + + memcpy(&destination[j], source, i + 1); +} + +void hexItoa(u32 number, char *out) +{ + const char hexDigits[] = "0123456789ABCDEF"; + u32 i = 0; + + while(number > 0) + { + out[7 - i++] = hexDigits[number & 0xF]; + number >>= 4; + } + + for(; i < 8; i++) out[7 - i] = '0'; +} \ No newline at end of file diff --git a/source/strings.h b/source/strings.h new file mode 100644 index 000000000..f6ac599cd --- /dev/null +++ b/source/strings.h @@ -0,0 +1,29 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2016 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified +* reasonable legal notices or author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +*/ + +#pragma once + +#include "types.h" + +int strlen(const char *string); +void concatenateStrings(char *destination, const char *source); +void hexItoa(u32 number, char *out); \ No newline at end of file diff --git a/source/types.h b/source/types.h index 0aa6fc5b3..d5fd26953 100644 --- a/source/types.h +++ b/source/types.h @@ -26,13 +26,6 @@ #include #include -#define CFG_BOOTENV (*(vu32 *)0x10010000) -#define CFG_UNITINFO (*(vu8 *)0x10010010) - -#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC) -#define PDN_SPI_CNT (*(vu32 *)0x101401C0) -#define PDN_GPU_CNT (*(vu8 *)0x10141200) - //Common data types typedef uint8_t u8; typedef uint16_t u16; @@ -43,7 +36,7 @@ typedef volatile u16 vu16; typedef volatile u32 vu32; typedef volatile u64 vu64; -//Used by multiple files: +//Used by multiple files typedef enum FirmwareSource { FIRMWARE_SYSNAND = 0, @@ -56,5 +49,6 @@ typedef enum FirmwareType NATIVE_FIRM = 0, TWL_FIRM = 1, AGB_FIRM = 2, - SAFE_FIRM = 3 + SAFE_FIRM = 3, + NATIVE_FIRM1X2X = 4 } FirmwareType; \ No newline at end of file diff --git a/source/utils.c b/source/utils.c index 3a931f999..c5706a7da 100644 --- a/source/utils.c +++ b/source/utils.c @@ -27,8 +27,6 @@ #include "draw.h" #include "cache.h" -extern bool isFirmlaunch; - u32 waitInput(void) { u32 pressedKey = 0, diff --git a/source/utils.h b/source/utils.h index bf74e1c2f..34b51eb91 100644 --- a/source/utils.h +++ b/source/utils.h @@ -28,6 +28,8 @@ #define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i) #define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i) +extern bool isFirmlaunch; + u32 waitInput(void); void mcuReboot(void); void mcuPowerOff(void);