diff --git a/loader/linker.ld b/loader/linker.ld index a3a699a..c5903f0 100644 --- a/loader/linker.ld +++ b/loader/linker.ld @@ -4,11 +4,18 @@ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { - . = 0x27FFE000; + . = 0x01FF9000; + + __start__ = ABSOLUTE(.); .text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); } .rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); } .data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); } . = ALIGN(4); + + __end__ = ABSOLUTE(.); + + __stack_top__ = 0x01FFB800; + __stack_bottom__ = 0x01FFA800; } diff --git a/loader/source/main.c b/loader/source/main.c index 0799851..bb1e5e1 100644 --- a/loader/source/main.c +++ b/loader/source/main.c @@ -27,25 +27,29 @@ void main(int argc, char **argv) { Firm *firm = (Firm *)0x20001000; + char *argvPassed[2], + absPath[24 + 255]; struct fb fbs[2]; - char absPath[24 + 255]; - if(argc == 2) - { - struct fb *fbsrc = (struct fb *)argv[1]; - fbs[0] = fbsrc[0]; - fbs[1] = fbsrc[1]; - } - - if(argc >= 1) + if(argc > 0) { u32 i; for(i = 0; i < sizeof(absPath) - 1 && argv[0][i] != 0; i++) absPath[i] = argv[0][i]; absPath[i] = 0; + + argvPassed[0] = (char *)absPath; } - char *argvPassed[2] = {absPath, (char *)&fbs}; + if(argc == 2) + { + struct fb *fbsrc = (struct fb *)argv[1]; + + fbs[0] = fbsrc[0]; + fbs[1] = fbsrc[1]; + + argvPassed[1] = (char *)&fbs; + } launchFirm(firm, argc, argvPassed); } diff --git a/loader/source/start.s b/loader/source/start.s index 4bd3096..4391eec 100644 --- a/loader/source/start.s +++ b/loader/source/start.s @@ -24,7 +24,7 @@ .align 4 .global _start _start: - ldr sp, =0x27ffe000 + ldr sp, =__stack_top__ b main .text diff --git a/source/crypto.c b/source/crypto.c index 40e7eb4..9989e26 100755 --- a/source/crypto.c +++ b/source/crypto.c @@ -324,6 +324,15 @@ static u32 fatStart; FirmwareSource firmSource; +__attribute__((aligned(4))) static const u8 key1s[2][AES_BLOCK_SIZE] = { + {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8}, + {0xA2, 0xF4, 0x00, 0x3C, 0x7A, 0x95, 0x10, 0x25, 0xDF, 0x4E, 0x9E, 0x74, 0xE3, 0x0C, 0x92, 0x99} +}, + key2s[2][AES_BLOCK_SIZE] = { + {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}, + {0xFF, 0x77, 0xA0, 0x9A, 0x99, 0x81, 0xE9, 0x48, 0xEC, 0x51, 0xC9, 0x32, 0x5D, 0x14, 0xEC, 0x25} +}; + void ctrNandInit(void) { __attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE], @@ -335,9 +344,6 @@ void ctrNandInit(void) if(ISN3DS) { - __attribute__((aligned(4))) u8 keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE}; - aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); - nandSlot = 0x05; fatStart = 0x5CAD7; } @@ -441,6 +447,73 @@ bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize) return decryptExeFs(cxi); } +static inline void twlConsoleInfoInit(void) +{ + u64 twlConsoleId = CFG_UNITINFO != 0 ? OTP_DEVCONSOLEID : (0x80000000ULL | (*(vu64 *)0x01FFB808 ^ 0x8C267B7B358A6AFULL)); + CFG_TWLUNITINFO = CFG_UNITINFO; + OTP_TWLCONSOLEID = twlConsoleId; + + *REG_AESCNT = 0; + + vu32 *k3X = REGs_AESTWLKEYS[3][1], *k1X = REGs_AESTWLKEYS[1][1]; + + k3X[0] = (u32)twlConsoleId; + k3X[3] = (u32)(twlConsoleId >> 32); + + k1X[2] = (u32)(twlConsoleId >> 32); + k1X[3] = (u32)twlConsoleId; +} + +void setupKeyslots(void) +{ + //Setup 0x24 KeyY + __attribute__((aligned(4))) u8 keyY0x24[AES_BLOCK_SIZE] = {0x74, 0xCA, 0x07, 0x48, 0x84, 0xF4, 0x22, 0x8D, 0xEB, 0x2A, 0x1C, 0xA7, 0x2D, 0x28, 0x77, 0x62}; + aes_setkey(0x24, keyY0x24, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Setup 0x25 KeyX and 0x2F KeyY + __attribute__((aligned(4))) const u8 keyX0x25s[2][AES_BLOCK_SIZE] = { + {0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3}, + {0x81, 0x90, 0x7A, 0x4B, 0x6F, 0x1B, 0x47, 0x32, 0x3A, 0x67, 0x79, 0x74, 0xCE, 0x4A, 0xD7, 0x1B} + }, + keyY0x2Fs[2][AES_BLOCK_SIZE] = { + {0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16}, + {0x73, 0x25, 0xC4, 0xEB, 0x14, 0x3A, 0x0D, 0x5F, 0x5D, 0xB6, 0xE5, 0xC5, 0x7A, 0x21, 0x95, 0xAC} + }; + + aes_setkey(0x25, keyX0x25s[ISDEVUNIT ? 1 : 0], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_setkey(0x2F, keyY0x2Fs[ISDEVUNIT ? 1 : 0], AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + + if(ISN3DS) + { + //Setup 0x05 KeyY + __attribute__((aligned(4))) u8 keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE}; + aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Setup TWL keys + twlConsoleInfoInit(); + } + + __attribute__((aligned(4))) u8 keyBlocks[2][AES_BLOCK_SIZE] = { + {0xA4, 0x8D, 0xE4, 0xF1, 0x0B, 0x36, 0x44, 0xAA, 0x90, 0x31, 0x28, 0xFF, 0x4D, 0xCA, 0x76, 0xDF}, + {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98} + }, decKey[AES_BLOCK_SIZE]; + + //Initialize Key 0x18 + aes_setkey(0x11, key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x11); + aes(decKey, keyBlocks[0], 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes_setkey(0x18, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + + //Initialize Key 0x19-0x1F + aes_setkey(0x11, key2s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); + aes_use_keyslot(0x11); + for(u8 slot = 0x19; slot < 0x20; slot++, keyBlocks[1][0xF]++) + { + aes(decKey, keyBlocks[1], 1, NULL, AES_ECB_DECRYPT_MODE, 0); + aes_setkey(slot, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL); + } +} + void kernel9Loader(Arm9Bin *arm9Section) { //Determine the kernel9loader version @@ -460,16 +533,6 @@ void kernel9Loader(Arm9Bin *arm9Section) u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800); if(*startOfArm9Bin == 0x47704770 || *startOfArm9Bin == 0xB0862000) return; //Already decrypted - //Set 0x11 keyslot - __attribute__((aligned(4))) const u8 key1s[2][AES_BLOCK_SIZE] = { - {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8}, - {0xA2, 0xF4, 0x00, 0x3C, 0x7A, 0x95, 0x10, 0x25, 0xDF, 0x4E, 0x9E, 0x74, 0xE3, 0x0C, 0x92, 0x99} - }, - key2s[2][AES_BLOCK_SIZE] = { - {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}, - {0xFF, 0x77, 0xA0, 0x9A, 0x99, 0x81, 0xE9, 0x48, 0xEC, 0x51, 0xC9, 0x32, 0x5D, 0x14, 0xEC, 0x25} - }; - aes_setkey(0x11, k9lVersion == 2 ? key2s[ISDEVUNIT ? 1 : 0] : key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL); u8 arm9BinSlot = k9lVersion == 0 ? 0x15 : 0x16; diff --git a/source/crypto.h b/source/crypto.h index a41bd2f..aa613fb 100755 --- a/source/crypto.h +++ b/source/crypto.h @@ -115,5 +115,6 @@ int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf); int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf); bool decryptExeFs(Cxi *cxi); bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize); +void setupKeyslots(void); void kernel9Loader(Arm9Bin *arm9Section); void computePinHash(u8 *outbuf, const u8 *inbuf); diff --git a/source/firm.c b/source/firm.c index 4cff6be..2dbff17 100755 --- a/source/firm.c +++ b/source/firm.c @@ -76,48 +76,51 @@ static inline bool loadFirmFromStorage(FirmwareType firmType) static inline void mergeSection0(FirmwareType firmType, bool loadFromStorage) { - u32 maxModuleSize = firmType == NATIVE_FIRM ? 0x60000 : 0x600000, - srcModuleSize; + u32 srcModuleSize; const char *extModuleSizeError = "The external FIRM modules are too large."; - u32 nbModules = 0; - u32 nbCustomModules = 0; + u32 nbModules = 0, + isCustomModule = false; struct { char name[8]; u8 *src; u32 size; - } moduleList[8]; + } moduleList[6]; //1) Parse info concerning Nintendo's modules - for(u8 *src = (u8 *)firm + firm->section[0].offset, *srcEnd = src + firm->section[0].size; src < srcEnd; src += srcModuleSize) + for(u8 *src = (u8 *)firm + firm->section[0].offset, *srcEnd = src + firm->section[0].size; src < srcEnd; src += srcModuleSize, nbModules++) { memcpy(moduleList[nbModules].name, ((Cxi *)src)->exHeader.systemControlInfo.appTitle, 8); moduleList[nbModules].src = src; - srcModuleSize = moduleList[nbModules++].size = ((Cxi *)src)->ncch.contentSize * 0x200; + srcModuleSize = moduleList[nbModules].size = ((Cxi *)src)->ncch.contentSize * 0x200; } - - //2) Merge that info with our own modules' - for(u8 *src = (u8 *)0x1FF60000; src < (u8 *)(0x1FF60000 + LUMA_SECTION0_SIZE); src += srcModuleSize) + + if(firmType == NATIVE_FIRM) { - const char *name = ((Cxi *)src)->exHeader.systemControlInfo.appTitle; + //2) Merge that info with our own modules' + for(u8 *src = (u8 *)0x1FF60000; src < (u8 *)(0x1FF60000 + LUMA_SECTION0_SIZE); src += srcModuleSize) + { + const char *name = ((Cxi *)src)->exHeader.systemControlInfo.appTitle; - u32 i; - for(i = 0; i < nbModules && memcmp(name, moduleList[i].name, 8) != 0; i++); - u32 index = i < nbModules || firmType != NATIVE_FIRM ? i : nbModules + nbCustomModules++; + u32 i; + for(i = 0; i < nbModules && memcmp(name, moduleList[i].name, 8) != 0; i++); - memcpy(moduleList[index].name, ((Cxi *)src)->exHeader.systemControlInfo.appTitle, 8); - moduleList[index].src = src; - srcModuleSize = moduleList[index].size = ((Cxi *)src)->ncch.contentSize * 0x200; + if(i == nbModules) isCustomModule = true; + + memcpy(moduleList[i].name, ((Cxi *)src)->exHeader.systemControlInfo.appTitle, 8); + moduleList[i].src = src; + srcModuleSize = moduleList[i].size = ((Cxi *)src)->ncch.contentSize * 0x200; + } + + if(isCustomModule) nbModules++; } - nbModules += nbCustomModules; - - //4) Read or copy the modules + //3) Read or copy the modules u8 *dst = firm->section[0].address; - for(u32 i = 0; i < nbModules; i++) + for(u32 i = 0, dstModuleSize; i < nbModules; i++) { - const char *moduleName = moduleList[i].name; + dstModuleSize = 0; if(loadFromStorage) { @@ -126,37 +129,33 @@ static inline void mergeSection0(FirmwareType firmType, bool loadFromStorage) //Read modules from files if they exist sprintf(fileName, "sysmodules/%.8s.cxi", moduleList[i].name); - u32 dstModuleSize = getFileSize(fileName); + dstModuleSize = getFileSize(fileName); if(dstModuleSize != 0) { - if(dstModuleSize > maxModuleSize) error(extModuleSizeError); + if(dstModuleSize > 0x60000) error(extModuleSizeError); if(dstModuleSize <= sizeof(Cxi) + 0x200 || fileRead(dst, fileName, dstModuleSize) != dstModuleSize || memcmp(((Cxi *)dst)->ncch.magic, "NCCH", 4) != 0 || - memcmp(moduleName, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0) + memcmp(moduleList[i].name, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0) error("An external FIRM module is invalid or corrupted."); - + dst += dstModuleSize; } - else - { - memcpy(dst, moduleList[i].src, moduleList[i].size); - dst += moduleList[i].size; - } } - else + + if(!dstModuleSize) { memcpy(dst, moduleList[i].src, moduleList[i].size); dst += moduleList[i].size; } } - //5) Patch NATIVE_FIRM if necessary - if(nbCustomModules != 0) + //4) Patch NATIVE_FIRM if necessary + if(isCustomModule) { - if(patchK11ModuleLoading(firm->section[0].size, dst - firm->section[0].address, nbCustomModules, firm->section[1].address, firm->section[1].size) != 0) + if(patchK11ModuleLoading(firm->section[0].size, dst - firm->section[0].address, firm->section[1].address, firm->section[1].size) != 0) error("Failed to inject custom sysmodule"); } } @@ -442,7 +441,7 @@ bool checkFirmPayload(void) if((section->offset < 0x200) || (section->address + section->size < section->address) || //Overflow check ((u32)section->address & 3) || (section->offset & 0x1FF) || (section->size & 0x1FF) || //Alignment check - (overlaps((u32)section->address, (u32)section->address + section->size, 0x27FFE000 - 0x1000, 0x28000000)) || + (overlaps((u32)section->address, (u32)section->address + section->size, 0x01FF8000, 0x01FF8000 + 0x8000)) || (overlaps((u32)section->address, (u32)section->address + section->size, 0x1FFFFC00, 0x20000000)) || (overlaps((u32)section->address, (u32)section->address + section->size, (u32)firm + section->offset, (u32)firm + size))) return false; @@ -464,7 +463,7 @@ bool checkFirmPayload(void) void launchFirm(int argc, char **argv) { - u32 *loaderAddress = (u32 *)0x27FFE000; + u32 *loaderAddress = (u32 *)0x01FF9000; prepareArm11ForFirmlaunch(); diff --git a/source/main.c b/source/main.c index f04fd6f..48671bb 100644 --- a/source/main.c +++ b/source/main.c @@ -68,7 +68,11 @@ void main(int argc, char **argv, u32 magicWord) isFirmlaunch = true; } else - error("Unsupported launcher or entrypoint (magic = 0x%08x, argc = %d).", magicWord, argc); + { + const char argv[] = "firm0:"; + for(u32 i = 0; i < sizeof(argv); i++) //Copy and convert the path to UTF-16 + launchedPath[i] = argv[i]; + } if(memcmp(launchedPath, u"sdmc", 8) == 0) { @@ -81,6 +85,14 @@ void main(int argc, char **argv, u32 magicWord) if(!mountFs(false, true)) error("Failed to mount CTRNAND."); isSdMode = false; } + else if(memcmp(launchedPath, u"firm", 8) == 0) + { + setupKeyslots(); + + if(mountFs(true, false)) isSdMode = true; + else if(mountFs(false, true)) isSdMode = false; + else error("Failed to mount SD and CTRNAND."); + } else { char mountPoint[5]; diff --git a/source/patches.c b/source/patches.c index a6e91d2..bfe2e0f 100644 --- a/source/patches.c +++ b/source/patches.c @@ -237,33 +237,29 @@ u32 patchCheckForDevCommonKey(u8 *pos, u32 size) return 0; } -u32 patchK11ModuleLoading(u32 section0size, u32 modulesSize, u32 nbCustomModules, u8 *startPos, u32 size) +u32 patchK11ModuleLoading(u32 section0size, u32 modulesSize, u8 *pos, u32 size) { - const u8 moduleLoadingPattern[] = { - 0x01, 0x70, 0x87, 0xE2, // add r7, #1 - 0x05, 0x00, 0x57, 0xE3, // cmp r7, #5 - }; + const u8 moduleLoadingPattern[] = {0xE2, 0x05, 0x00, 0x57}, + modulePidPattern[] = {0x06, 0xA0, 0xE1, 0xF2}; //GetSystemInfo - //GetSystemInfo - const u8 modulePidPattern[] = { - 0x00, 0xF0, 0x20, 0xE3, // nop - 0x05, 0x00, 0xA0, 0xE3, // mov r0, #5 - }; + u8 *off = memsearch(pos, moduleLoadingPattern, size, 4); - u8 *off = memsearch(startPos, moduleLoadingPattern, size, 8); if(off == NULL) return 1; - off[4] += nbCustomModules; + + off[1]++; u32 *off32; - for(off32 = (u32 *)off; *off32 != 0xE59F0000; off32++); + for(off32 = (u32 *)(off - 3); *off32 != 0xE59F0000; off32++); off32 += 2; off32[1] = off32[0] + modulesSize; for(; *off32 != section0size; off32++); *off32 += ((modulesSize + 0x1FF) >> 9) << 9; - off = memsearch(startPos, modulePidPattern, size, 8); + off = memsearch(pos, modulePidPattern, size, 4); + if(off == NULL) return 1; - off[4] = 6; + + off[0xB] = 6; return 0; } diff --git a/source/patches.h b/source/patches.h index 91f4433..c93656f 100644 --- a/source/patches.h +++ b/source/patches.h @@ -46,7 +46,7 @@ u32 patchOldFirmWrites(u8 *pos, u32 size); u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion); u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size); u32 patchNandNcchEncryptionCheck(u8 *pos, u32 size); -u32 patchK11ModuleLoading(u32 section0size, u32 modulesSize, u32 nbCustomModules, u8 *startPos, u32 size); +u32 patchK11ModuleLoading(u32 section0size, u32 modulesSize, u8 *startPos, u32 size); u32 patchCheckForDevCommonKey(u8 *pos, u32 size); u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space); u32 stubSvcRestrictGpuDma(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA); diff --git a/source/screen.c b/source/screen.c index c7e051c..94758d2 100644 --- a/source/screen.c +++ b/source/screen.c @@ -38,16 +38,15 @@ #include "screen.h" #include "config.h" #include "memory.h" -#include "cache.h" #include "i2c.h" #include "utils.h" +struct fb fbs[2]; + static const u32 brightness[4] = {0x5F, 0x4C, 0x39, 0x26}; static volatile Arm11Operation *operation = (volatile Arm11Operation *)0x1FF80004; -struct fb fbs[2]; - static void invokeArm11Function(Arm11Operation op) { while(*operation != ARM11_READY);