From 8ebb74b0bc1dde0c1f3d9dce2d7237df58f16e24 Mon Sep 17 00:00:00 2001 From: d0k3 Date: Wed, 30 Dec 2020 11:34:44 +0100 Subject: [PATCH] Allow dumping NDS carts with secure area encrypted Fixes #417 --- arm9/source/gamecart/command_ntr.h | 2 +- arm9/source/gamecart/gamecart.c | 43 ++++++++++++++++++++++++------ arm9/source/gamecart/gamecart.h | 3 ++- arm9/source/gamecart/secure_ntr.c | 3 ++- arm9/source/virtual/vcart.c | 23 +++++++++++----- 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/arm9/source/gamecart/command_ntr.h b/arm9/source/gamecart/command_ntr.h index 559dc33..9f3e2be 100644 --- a/arm9/source/gamecart/command_ntr.h +++ b/arm9/source/gamecart/command_ntr.h @@ -15,5 +15,5 @@ void NTR_CmdEnter16ByteMode(void); void NTR_CmdReadHeader (u8* buffer); void NTR_CmdReadData (u32 offset, void* buffer); -bool NTR_Secure_Init (u8* buffer, u32 CartID, int iCardDevice); +bool NTR_Secure_Init (u8* buffer, u8* sa_copy, u32 CartID, int iCardDevice); diff --git a/arm9/source/gamecart/gamecart.c b/arm9/source/gamecart/gamecart.c index ee56662..0886cc2 100644 --- a/arm9/source/gamecart/gamecart.c +++ b/arm9/source/gamecart/gamecart.c @@ -33,8 +33,7 @@ typedef struct { TwlHeader ntr_header; u8 ntr_padding[0x3000]; // 0x00 u8 secure_area[0x4000]; - TwlHeader twl_header; - u8 twl_padding[0x3000]; // 0x00 + u8 secure_area_enc[0x4000]; u8 modcrypt_area[0x4000]; u32 cart_type; u32 cart_id; @@ -45,7 +44,8 @@ typedef struct { u32 arm9i_rom_offset; } PACKED_ALIGN(16) CartDataNtrTwl; -DsTime init_time; +static DsTime init_time; +static bool encrypted_sa = false; u32 GetCartName(char* name, CartData* cdata) { if (cdata->cart_type & CART_CTR) { @@ -86,8 +86,14 @@ u32 GetCartInfoString(char* info, CartData* cdata) { return 0; } +u32 SetSecureAreaEncryption(bool encrypted) { + encrypted_sa = encrypted; + return 0; +} + u32 InitCartRead(CartData* cdata) { get_dstime(&init_time); + encrypted_sa = false; memset(cdata, 0x00, sizeof(CartData)); cdata->cart_type = CART_NONE; if (!CART_INSERTED) return 1; @@ -153,9 +159,11 @@ u32 InitCartRead(CartData* cdata) { } else { // NTR/TWL cartridges // NTR header TwlHeader* nds_header = (void*)cdata->header; + u8 secure_area_enc[0x4000]; NTR_CmdReadHeader(cdata->header); if (!(*(cdata->header))) return 1; // error reading the header - if (!NTR_Secure_Init(cdata->header, Cart_GetID(), 0)) return 1; + if (!NTR_Secure_Init(cdata->header, secure_area_enc, Cart_GetID(), 0)) return 1; + // cartridge size, trimmed size, twl presets if (nds_header->device_capacity >= 15) return 1; // too big, not valid @@ -177,11 +185,14 @@ u32 InitCartRead(CartData* cdata) { // We'll only want to use TWL secure init if this is a TWL cartridge. if (cdata->cart_id & 0x40000000U) { // TWL cartridge Cart_Init(); - NTR_CmdReadHeader(cdata->twl_header); - if (!NTR_Secure_Init(cdata->twl_header, Cart_GetID(), 1)) return 1; + NTR_CmdReadHeader(cdata->storage); + if (!NTR_Secure_Init(cdata->storage, NULL, Cart_GetID(), 1)) return 1; } } + // store encrypted secure area + memcpy(cdata->storage, secure_area_enc, 0x4000); + // last safety check if (cdata->data_size > cdata->cart_size) return 1; @@ -199,7 +210,7 @@ u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata) { u8* buffer8 = (u8*) buffer; if (!CART_INSERTED) return 1; // header - u32 header_sectors = (cdata->cart_type & CART_CTR) ? 0x4000/0x200 : 0x8000/0x200; + const u32 header_sectors = 0x4000/0x200; if (sector < header_sectors) { u32 header_count = (sector + count > header_sectors) ? header_sectors - sector : count; memcpy(buffer8, cdata->header + (sector * 0x200), header_count * 0x200); @@ -219,6 +230,7 @@ u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata) { CTR_CmdReadData(sector + i, 0x200, min(max_read, count - i), buff); buff += max_read * 0x200; } + // overwrite the card2 savegame with 0xFF u32 card2_offset = getle32(cdata->header + 0x200); if ((card2_offset != 0xFFFFFFFF) && @@ -231,9 +243,24 @@ u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata) { } } else if (cdata->cart_type & CART_NTR) { u8* buff = buffer8; + + // secure area handling + const u32 sa_sector_end = 0x8000/0x200; + if (sector < sa_sector_end) { + CartDataNtrTwl* cdata_twl = (CartDataNtrTwl*) cdata; + u8* sa = encrypted_sa ? cdata_twl->secure_area_enc : cdata_twl->secure_area; + u32 count_sa = ((sector + count) > sa_sector_end) ? sa_sector_end - sector : count; + memcpy(buff, sa + ((sector - header_sectors) * 0x200), count_sa * 0x200); + buff += count_sa * 0x200; + sector += count_sa; + count -= count_sa; + } + + // regular cart data u32 off = sector * 0x200; for (u32 i = 0; i < count; i++, off += 0x200, buff += 0x200) NTR_CmdReadData(off, buff); + // modcrypt area handling if ((cdata->cart_type & CART_TWL) && ((sector+count) * 0x200 > cdata->arm9i_rom_offset) && @@ -248,7 +275,7 @@ u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata) { size_i = MODC_AREA_SIZE - offset_i; if (size_i > (count * 0x200) - (buffer_arm9i - buffer8)) size_i = (count * 0x200) - (buffer_arm9i - buffer8); - if (size_i) memcpy(buffer_arm9i, cdata->twl_header + 0x4000 + offset_i, size_i); + if (size_i) memcpy(buffer_arm9i, cdata->storage + 0x4000 + offset_i, size_i); } } else return 1; return 0; diff --git a/arm9/source/gamecart/gamecart.h b/arm9/source/gamecart/gamecart.h index 9da0a53..cafc407 100644 --- a/arm9/source/gamecart/gamecart.h +++ b/arm9/source/gamecart/gamecart.h @@ -14,7 +14,7 @@ typedef struct { u8 header[0x8000]; // NTR header + secure area / CTR header + private header - u8 twl_header[0x8000]; // TWL header + modcrypt area / unused + u8 storage[0x8000]; // encrypted secure area + modcrypt area / unused u32 cart_type; u32 cart_id; u64 cart_size; @@ -26,6 +26,7 @@ typedef struct { u32 GetCartName(char* name, CartData* cdata); u32 GetCartInfoString(char* info, CartData* cdata); +u32 SetSecureAreaEncryption(bool encrypted); u32 InitCartRead(CartData* cdata); u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata); u32 ReadCartBytes(void* buffer, u64 offset, u64 count, CartData* cdata); diff --git a/arm9/source/gamecart/secure_ntr.c b/arm9/source/gamecart/secure_ntr.c index 55e91ec..9a4e2ce 100644 --- a/arm9/source/gamecart/secure_ntr.c +++ b/arm9/source/gamecart/secure_ntr.c @@ -231,7 +231,7 @@ void NTR_CmdSecure (u32 flags, void* buffer, u32 length, u8* pcmd) cardPolledTransfer (flags, buffer, length, pcmd); } -bool NTR_Secure_Init (u8* header, u32 CartID, int iCardDevice) +bool NTR_Secure_Init (u8* header, u8* sa_copy, u32 CartID, int iCardDevice) { u32 iGameCode; u32 iCardHash[0x412] = {0}; @@ -329,6 +329,7 @@ bool NTR_Secure_Init (u8* header, u32 CartID, int iCardDevice) NTR_CmdSecure (flagsKey1, NULL, 0, cmdData); //CycloDS doesn't like the dsi secure area being decrypted + if (sa_copy) memcpy(sa_copy, secureArea, 0x4000); if(!iCardDevice && ((nds9Offset != 0x4000) || secureArea[0] || secureArea[1])) { NTR_DecryptSecureArea (iGameCode, iCardHash, nCardHash, iKeyCode, secureArea, iCardDevice); diff --git a/arm9/source/virtual/vcart.c b/arm9/source/virtual/vcart.c index 48098ae..709c5c3 100644 --- a/arm9/source/virtual/vcart.c +++ b/arm9/source/virtual/vcart.c @@ -2,6 +2,7 @@ #include "gamecart.h" #define FAT_LIMIT 0x100000000 +#define VFLAG_SECURE_AREA_ENC (1UL<<27) #define VFLAG_GAMECART_NFO (1UL<<28) #define VFLAG_JEDECID_AND_SRFG (1UL<<29) #define VFLAG_SAVEGAME (1UL<<30) @@ -34,7 +35,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) { vfile->keyslot = 0xFF; // unused vfile->flags = VFLAG_READONLY; - while (++vdir->index <= 8) { + while (++vdir->index <= 9) { if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom snprintf(vfile->name, 32, "%s.%s", name, ext); vfile->size = cdata->cart_size; @@ -53,24 +54,31 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) { vfile->size = (FAT_LIMIT / 2); vfile->offset = (FAT_LIMIT / 2); return true; - } else if ((vdir->index == 5) && (cdata->cart_type & CART_CTR)) { // private header + } else if ((vdir->index == 5) && (cdata->data_size < FAT_LIMIT) && + (cdata->cart_type & CART_NTR)) { // encrypted secure area + snprintf(vfile->name, 32, "%s.nds.enc", name); + vfile->size = cdata->cart_size; + if (vfile->size == FAT_LIMIT) vfile->size--; + vfile->flags = VFLAG_SECURE_AREA_ENC; + return true; + } else if ((vdir->index == 6) && (cdata->cart_type & CART_CTR)) { // private header snprintf(vfile->name, 32, "%s-priv.bin", name); vfile->size = PRIV_HDR_SIZE; vfile->flags |= VFLAG_PRIV_HDR; return true; - } else if ((vdir->index == 6) && (cdata->save_size > 0)) { // savegame + } else if ((vdir->index == 7) && (cdata->save_size > 0)) { // savegame snprintf(vfile->name, 32, "%s.sav", name); vfile->size = cdata->save_size; vfile->flags = VFLAG_SAVEGAME; return true; - } else if (vdir->index == 7) { // gamecart info + } else if (vdir->index == 8) { // gamecart info char info[256]; GetCartInfoString(info, cdata); snprintf(vfile->name, 32, "%s.txt", name); vfile->size = strnlen(info, 255); vfile->flags |= VFLAG_GAMECART_NFO; return true; - } else if ((vdir->index == 8) && cdata->save_type.chip) { // JEDEC id and status register + } else if ((vdir->index == 9) && cdata->save_type.chip) { // JEDEC id and status register strcpy(vfile->name, "jedecid_and_sreg.bin"); vfile->size = JEDECID_AND_SREG_SIZE; vfile->flags |= VFLAG_JEDECID_AND_SRFG; @@ -84,6 +92,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) { int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) { u32 foffset = vfile->offset + offset; if (!cdata) return -1; + if (vfile->flags & VFLAG_PRIV_HDR) return ReadCartPrivateHeader(buffer, foffset, count, cdata); else if (vfile->flags & VFLAG_SAVEGAME) @@ -92,7 +101,9 @@ int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) return ReadCartInfo(buffer, foffset, count, cdata); else if (vfile->flags & VFLAG_JEDECID_AND_SRFG) return ReadCartSaveJedecId(buffer, foffset, count, cdata); - else return ReadCartBytes(buffer, foffset, count, cdata); + + SetSecureAreaEncryption(vfile->flags & VFLAG_SECURE_AREA_ENC); + return ReadCartBytes(buffer, foffset, count, cdata); } int WriteVCartFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) {