Allow dumping NDS carts with secure area encrypted

Fixes #417
This commit is contained in:
d0k3 2020-12-30 11:34:44 +01:00
parent af14376c84
commit 8ebb74b0bc
5 changed files with 57 additions and 17 deletions

View File

@ -15,5 +15,5 @@ void NTR_CmdEnter16ByteMode(void);
void NTR_CmdReadHeader (u8* buffer); void NTR_CmdReadHeader (u8* buffer);
void NTR_CmdReadData (u32 offset, void* 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);

View File

@ -33,8 +33,7 @@ typedef struct {
TwlHeader ntr_header; TwlHeader ntr_header;
u8 ntr_padding[0x3000]; // 0x00 u8 ntr_padding[0x3000]; // 0x00
u8 secure_area[0x4000]; u8 secure_area[0x4000];
TwlHeader twl_header; u8 secure_area_enc[0x4000];
u8 twl_padding[0x3000]; // 0x00
u8 modcrypt_area[0x4000]; u8 modcrypt_area[0x4000];
u32 cart_type; u32 cart_type;
u32 cart_id; u32 cart_id;
@ -45,7 +44,8 @@ typedef struct {
u32 arm9i_rom_offset; u32 arm9i_rom_offset;
} PACKED_ALIGN(16) CartDataNtrTwl; } PACKED_ALIGN(16) CartDataNtrTwl;
DsTime init_time; static DsTime init_time;
static bool encrypted_sa = false;
u32 GetCartName(char* name, CartData* cdata) { u32 GetCartName(char* name, CartData* cdata) {
if (cdata->cart_type & CART_CTR) { if (cdata->cart_type & CART_CTR) {
@ -86,8 +86,14 @@ u32 GetCartInfoString(char* info, CartData* cdata) {
return 0; return 0;
} }
u32 SetSecureAreaEncryption(bool encrypted) {
encrypted_sa = encrypted;
return 0;
}
u32 InitCartRead(CartData* cdata) { u32 InitCartRead(CartData* cdata) {
get_dstime(&init_time); get_dstime(&init_time);
encrypted_sa = false;
memset(cdata, 0x00, sizeof(CartData)); memset(cdata, 0x00, sizeof(CartData));
cdata->cart_type = CART_NONE; cdata->cart_type = CART_NONE;
if (!CART_INSERTED) return 1; if (!CART_INSERTED) return 1;
@ -153,9 +159,11 @@ u32 InitCartRead(CartData* cdata) {
} else { // NTR/TWL cartridges } else { // NTR/TWL cartridges
// NTR header // NTR header
TwlHeader* nds_header = (void*)cdata->header; TwlHeader* nds_header = (void*)cdata->header;
u8 secure_area_enc[0x4000];
NTR_CmdReadHeader(cdata->header); NTR_CmdReadHeader(cdata->header);
if (!(*(cdata->header))) return 1; // error reading the 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 // cartridge size, trimmed size, twl presets
if (nds_header->device_capacity >= 15) return 1; // too big, not valid 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. // We'll only want to use TWL secure init if this is a TWL cartridge.
if (cdata->cart_id & 0x40000000U) { // TWL cartridge if (cdata->cart_id & 0x40000000U) { // TWL cartridge
Cart_Init(); Cart_Init();
NTR_CmdReadHeader(cdata->twl_header); NTR_CmdReadHeader(cdata->storage);
if (!NTR_Secure_Init(cdata->twl_header, Cart_GetID(), 1)) return 1; 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 // last safety check
if (cdata->data_size > cdata->cart_size) return 1; 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; u8* buffer8 = (u8*) buffer;
if (!CART_INSERTED) return 1; if (!CART_INSERTED) return 1;
// header // header
u32 header_sectors = (cdata->cart_type & CART_CTR) ? 0x4000/0x200 : 0x8000/0x200; const u32 header_sectors = 0x4000/0x200;
if (sector < header_sectors) { if (sector < header_sectors) {
u32 header_count = (sector + count > header_sectors) ? header_sectors - sector : count; u32 header_count = (sector + count > header_sectors) ? header_sectors - sector : count;
memcpy(buffer8, cdata->header + (sector * 0x200), header_count * 0x200); 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); CTR_CmdReadData(sector + i, 0x200, min(max_read, count - i), buff);
buff += max_read * 0x200; buff += max_read * 0x200;
} }
// overwrite the card2 savegame with 0xFF // overwrite the card2 savegame with 0xFF
u32 card2_offset = getle32(cdata->header + 0x200); u32 card2_offset = getle32(cdata->header + 0x200);
if ((card2_offset != 0xFFFFFFFF) && 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) { } else if (cdata->cart_type & CART_NTR) {
u8* buff = buffer8; 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; u32 off = sector * 0x200;
for (u32 i = 0; i < count; i++, off += 0x200, buff += 0x200) for (u32 i = 0; i < count; i++, off += 0x200, buff += 0x200)
NTR_CmdReadData(off, buff); NTR_CmdReadData(off, buff);
// modcrypt area handling // modcrypt area handling
if ((cdata->cart_type & CART_TWL) && if ((cdata->cart_type & CART_TWL) &&
((sector+count) * 0x200 > cdata->arm9i_rom_offset) && ((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; size_i = MODC_AREA_SIZE - offset_i;
if (size_i > (count * 0x200) - (buffer_arm9i - buffer8)) if (size_i > (count * 0x200) - (buffer_arm9i - buffer8))
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; } else return 1;
return 0; return 0;

View File

@ -14,7 +14,7 @@
typedef struct { typedef struct {
u8 header[0x8000]; // NTR header + secure area / CTR header + private header 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_type;
u32 cart_id; u32 cart_id;
u64 cart_size; u64 cart_size;
@ -26,6 +26,7 @@ typedef struct {
u32 GetCartName(char* name, CartData* cdata); u32 GetCartName(char* name, CartData* cdata);
u32 GetCartInfoString(char* info, CartData* cdata); u32 GetCartInfoString(char* info, CartData* cdata);
u32 SetSecureAreaEncryption(bool encrypted);
u32 InitCartRead(CartData* cdata); u32 InitCartRead(CartData* cdata);
u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata); u32 ReadCartSectors(void* buffer, u32 sector, u32 count, CartData* cdata);
u32 ReadCartBytes(void* buffer, u64 offset, u64 count, CartData* cdata); u32 ReadCartBytes(void* buffer, u64 offset, u64 count, CartData* cdata);

View File

@ -231,7 +231,7 @@ void NTR_CmdSecure (u32 flags, void* buffer, u32 length, u8* pcmd)
cardPolledTransfer (flags, buffer, length, 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 iGameCode;
u32 iCardHash[0x412] = {0}; u32 iCardHash[0x412] = {0};
@ -329,6 +329,7 @@ bool NTR_Secure_Init (u8* header, u32 CartID, int iCardDevice)
NTR_CmdSecure (flagsKey1, NULL, 0, cmdData); NTR_CmdSecure (flagsKey1, NULL, 0, cmdData);
//CycloDS doesn't like the dsi secure area being decrypted //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])) if(!iCardDevice && ((nds9Offset != 0x4000) || secureArea[0] || secureArea[1]))
{ {
NTR_DecryptSecureArea (iGameCode, iCardHash, nCardHash, iKeyCode, secureArea, iCardDevice); NTR_DecryptSecureArea (iGameCode, iCardHash, nCardHash, iKeyCode, secureArea, iCardDevice);

View File

@ -2,6 +2,7 @@
#include "gamecart.h" #include "gamecart.h"
#define FAT_LIMIT 0x100000000 #define FAT_LIMIT 0x100000000
#define VFLAG_SECURE_AREA_ENC (1UL<<27)
#define VFLAG_GAMECART_NFO (1UL<<28) #define VFLAG_GAMECART_NFO (1UL<<28)
#define VFLAG_JEDECID_AND_SRFG (1UL<<29) #define VFLAG_JEDECID_AND_SRFG (1UL<<29)
#define VFLAG_SAVEGAME (1UL<<30) #define VFLAG_SAVEGAME (1UL<<30)
@ -34,7 +35,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
vfile->keyslot = 0xFF; // unused vfile->keyslot = 0xFF; // unused
vfile->flags = VFLAG_READONLY; vfile->flags = VFLAG_READONLY;
while (++vdir->index <= 8) { while (++vdir->index <= 9) {
if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom
snprintf(vfile->name, 32, "%s.%s", name, ext); snprintf(vfile->name, 32, "%s.%s", name, ext);
vfile->size = cdata->cart_size; vfile->size = cdata->cart_size;
@ -53,24 +54,31 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
vfile->size = (FAT_LIMIT / 2); vfile->size = (FAT_LIMIT / 2);
vfile->offset = (FAT_LIMIT / 2); vfile->offset = (FAT_LIMIT / 2);
return true; 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); snprintf(vfile->name, 32, "%s-priv.bin", name);
vfile->size = PRIV_HDR_SIZE; vfile->size = PRIV_HDR_SIZE;
vfile->flags |= VFLAG_PRIV_HDR; vfile->flags |= VFLAG_PRIV_HDR;
return true; 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); snprintf(vfile->name, 32, "%s.sav", name);
vfile->size = cdata->save_size; vfile->size = cdata->save_size;
vfile->flags = VFLAG_SAVEGAME; vfile->flags = VFLAG_SAVEGAME;
return true; return true;
} else if (vdir->index == 7) { // gamecart info } else if (vdir->index == 8) { // gamecart info
char info[256]; char info[256];
GetCartInfoString(info, cdata); GetCartInfoString(info, cdata);
snprintf(vfile->name, 32, "%s.txt", name); snprintf(vfile->name, 32, "%s.txt", name);
vfile->size = strnlen(info, 255); vfile->size = strnlen(info, 255);
vfile->flags |= VFLAG_GAMECART_NFO; vfile->flags |= VFLAG_GAMECART_NFO;
return true; 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"); strcpy(vfile->name, "jedecid_and_sreg.bin");
vfile->size = JEDECID_AND_SREG_SIZE; vfile->size = JEDECID_AND_SREG_SIZE;
vfile->flags |= VFLAG_JEDECID_AND_SRFG; 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) { int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
u32 foffset = vfile->offset + offset; u32 foffset = vfile->offset + offset;
if (!cdata) return -1; if (!cdata) return -1;
if (vfile->flags & VFLAG_PRIV_HDR) if (vfile->flags & VFLAG_PRIV_HDR)
return ReadCartPrivateHeader(buffer, foffset, count, cdata); return ReadCartPrivateHeader(buffer, foffset, count, cdata);
else if (vfile->flags & VFLAG_SAVEGAME) 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); return ReadCartInfo(buffer, foffset, count, cdata);
else if (vfile->flags & VFLAG_JEDECID_AND_SRFG) else if (vfile->flags & VFLAG_JEDECID_AND_SRFG)
return ReadCartSaveJedecId(buffer, foffset, count, cdata); 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) { int WriteVCartFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) {