diff --git a/source/common/unittype.h b/source/common/unittype.h index 98dfa7f..cff5f7e 100644 --- a/source/common/unittype.h +++ b/source/common/unittype.h @@ -11,3 +11,10 @@ // see: https://3dbrew.org/wiki/CONFIG11_Registers #define IS_A9LH ((*(vu32*) 0x101401C0) == 0) + +// https://www.3dbrew.org/wiki/CONFIG9_Registers +// (actually checks for an unlocked OTP) +#define IS_UNLOCKED (!((*(vu8*)0x10000000) & 0x2)) + +// A9LH + unlocked = SigHax +#define IS_SIGHAX (IS_A9LH && IS_UNLOCKED) diff --git a/source/godmode.c b/source/godmode.c index 181e52f..76d7ebc 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -244,7 +244,7 @@ u32 SdFormatMenu(void) { return 1; } - if (IS_A9LH) { + if (IS_A9LH && !IS_SIGHAX) { InitSDCardFS(); // on A9LH: copy the payload from mem to SD root FileSetData("0:/arm9loaderhax_si.bin", (u8*) 0x23F00000, SELF_MAX_SIZE, 0, true); DeinitSDCardFS(); @@ -679,7 +679,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur bool restorable = (FTYPE_RESTORABLE(filetype) && IS_A9LH && !(drvtype & DRV_SYSNAND)); bool ebackupable = (FTYPE_EBACKUP(filetype)); bool xorpadable = (FTYPE_XORPAD(filetype)); - bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT)); + bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT) && !IS_SIGHAX); bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || transferable || hsinjectable || restorable || xorpadable || launchable || ebackupable; diff --git a/source/nand/nand.c b/source/nand/nand.c index d297e46..b2e93e5 100644 --- a/source/nand/nand.c +++ b/source/nand/nand.c @@ -120,7 +120,10 @@ bool InitNandCrypto(void) { // part #0: KeyX / KeyY for secret sector 0x96 // on a9lh this MUST be run before accessing the SHA register in any other way - if (IS_A9LH) { // for a9lh + if (IS_UNLOCKED) { // if OTP is unlocked + // see: https://www.3dbrew.org/wiki/OTP_Registers + sha_quick(OtpSha256, (u8*) 0x10012000, 0x90, SHA256_MODE); + } else if (IS_A9LH) { // for a9lh // store the current SHA256 from register memcpy(OtpSha256, (void*) REG_SHAHASH, 32); } else { diff --git a/source/virtual/vmem.c b/source/virtual/vmem.c index 1f6d396..1c4d41f 100644 --- a/source/virtual/vmem.c +++ b/source/virtual/vmem.c @@ -1,23 +1,52 @@ #include "vmem.h" #include "unittype.h" +#include "sha.h" +#include "aes.h" +#define VFLAG_PAYLOAD (1UL<<27) +#define VFLAG_BOOT9 (1UL<<28) +#define VFLAG_BOOT11 (1UL<<29) +#define VFLAG_OTP (1UL<<30) #define VFLAG_N3DS_ONLY (1UL<<31) +// offsets provided by SciresM +#define BOOT9_POS 0x08080000 +#define BOOT11_POS 0x08090000 +#define BOOT9_LEN 0x00010000 +#define BOOT11_LEN 0x00010000 + +// see: https://www.youtube.com/watch?v=wogNzUypLuI +u8 boot9_sha256[0x20] = { + 0x2F, 0x88, 0x74, 0x4F, 0xEE, 0xD7, 0x17, 0x85, 0x63, 0x86, 0x40, 0x0A, 0x44, 0xBB, 0xA4, 0xB9, + 0xCA, 0x62, 0xE7, 0x6A, 0x32, 0xC7, 0x15, 0xD4, 0xF3, 0x09, 0xC3, 0x99, 0xBF, 0x28, 0x16, 0x6F +}; +u8 boot11_sha256[0x20] = { + 0x74, 0xDA, 0xAC, 0xE1, 0xF8, 0x06, 0x7B, 0x66, 0xCC, 0x81, 0xFC, 0x30, 0x7A, 0x3F, 0xDB, 0x50, + 0x9C, 0xBE, 0xDC, 0x32, 0xF9, 0x03, 0xAE, 0xBE, 0x90, 0x61, 0x44, 0xDE, 0xA7, 0xA0, 0x75, 0x12 +}; + +// see: https://github.com/SciresM/CTRAesEngine/blob/8312adc74b911a6b9cb9e03982ba3768b8e2e69c/CTRAesEngine/AesEngine.cs#L672-L688 +#define OTP_KEY ((u8*) BOOT9_POS + ((IS_DEVKIT) ? + 0x5710 : 0x56E0)) +#define OTP_IV (OTP_KEY + 0x10) + // see: http://3dbrew.org/wiki/Memory_layout#ARM9 static const VirtualFile vMemFileTemplates[] = { { "itcm.mem" , 0x01FF8000, 0x00008000, 0xFF, 0 }, { "arm9.mem" , 0x08000000, 0x00100000, 0xFF, 0 }, { "arm9ext.mem" , 0x08100000, 0x00080000, 0xFF, VFLAG_N3DS_ONLY }, + { "boot9.bin" , BOOT9_POS , BOOT9_LEN , 0xFF, VFLAG_BOOT9 }, + { "boot11.bin" , BOOT11_POS, BOOT11_LEN, 0xFF, VFLAG_BOOT11 }, { "vram.mem" , 0x18000000, 0x00600000, 0xFF, 0 }, { "dsp.mem" , 0x1FF00000, 0x00080000, 0xFF, 0 }, { "axiwram.mem" , 0x1FF80000, 0x00080000, 0xFF, 0 }, { "fcram.mem" , 0x20000000, 0x08000000, 0xFF, 0 }, { "fcramext.mem" , 0x28000000, 0x08000000, 0xFF, VFLAG_N3DS_ONLY }, { "dtcm.mem" , 0x30008000, 0x00004000, 0xFF, 0 }, - // { "otp.mem" , 0x10012000, 0x00000108, 0xFF, 0 }, + { "otp.mem" , 0x10012000, 0x00000100, 0xFF, VFLAG_OTP }, + // { "otp_dec.mem" , 0x10012000, 0x00000100, 0x11, VFLAG_OTP | VFLAG_BOOT9 }, { "bootrom.mem" , 0xFFFF0000, 0x00010000, 0xFF, 0 }, { "bootrom_unp.mem" , 0xFFFF0000, 0x00008000, 0xFF, 0 }, - { "godmode9.bin" , 0x23F00000, SELF_MAX_SIZE, 0xFF, 0 } + { "godmode9.bin" , 0x23F00000, SELF_MAX_SIZE, 0xFF, VFLAG_PAYLOAD } }; bool ReadVMemDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir object generated in virtual.c @@ -28,9 +57,13 @@ bool ReadVMemDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir // copy current template to vfile memcpy(vfile, templates + vdir->index, sizeof(VirtualFile)); - // process special flag - if ((vfile->flags & VFLAG_N3DS_ONLY) && (IS_O3DS)) - continue; // this is not on O3DS consoles + // process special flags + if (((vfile->flags & VFLAG_N3DS_ONLY) && (IS_O3DS)) || // this is not on O3DS consoles + ((vfile->flags & VFLAG_OTP) && !(IS_UNLOCKED)) || // OTP still locked + ((vfile->flags & VFLAG_BOOT9) && (sha_cmp(boot9_sha256, (u8*) BOOT9_POS, BOOT9_LEN, SHA256_MODE) != 0)) || // boot9 not found + ((vfile->flags & VFLAG_BOOT11) && (sha_cmp(boot11_sha256, (u8*) BOOT11_POS, BOOT11_LEN, SHA256_MODE) != 0)) || // boot11 not found + ((vfile->flags & VFLAG_PAYLOAD) && IS_SIGHAX)) // not available on SigHax (yet) + continue; // found if arriving here return true; @@ -40,13 +73,26 @@ bool ReadVMemDir(VirtualFile* vfile, VirtualDir* vdir) { // uses a generic vdir } int ReadVMemFile(const VirtualFile* vfile, u8* buffer, u64 offset, u64 count) { - u32 foffset = vfile->offset + offset; - memcpy(buffer, (u8*) foffset, count); + if ((vfile->flags & VFLAG_OTP) && (vfile->keyslot == 0x11)) { + u8 __attribute__((aligned(32))) otp_local[vfile->size]; + u8* otp_mem = (u8*) (u32) vfile->offset; + setup_aeskey(0x11, OTP_KEY); + use_aeskey(0x11); + cbc_decrypt(otp_mem, otp_local, vfile->size, AES_CNT_TITLEKEY_DECRYPT_MODE, OTP_IV); + memcpy(buffer, otp_local + offset, count); + } else { + u32 foffset = vfile->offset + offset; + memcpy(buffer, (u8*) foffset, count); + } return 0; } int WriteVMemFile(const VirtualFile* vfile, const u8* buffer, u64 offset, u64 count) { - u32 foffset = vfile->offset + offset; - memcpy((u8*) foffset, buffer, count); + if (vfile->flags & (VFLAG_OTP|VFLAG_BOOT9|VFLAG_BOOT11)) { + return 1; // not writable / writes blocked + } else { + u32 foffset = vfile->offset + offset; + memcpy((u8*) foffset, buffer, count); + } return 0; }