Fix NAND offset hardcoding

... SafeB9SInstaller now works with custom NCSDs
This commit is contained in:
d0k3 2017-06-02 17:02:11 +02:00
parent 73bcc07349
commit 88bc7955d8
10 changed files with 528 additions and 292 deletions

View File

@ -55,11 +55,11 @@
// buffer area defines (big buffer for firm)
#define WORK_BUFFER ((u8*) 0x21000000)
#define WORK_BUFFER_SIZE (0x100000)
#define NAND_BUFFER ((u8*) 0x22000000)
#define NAND_BUFFER_SIZE (0x100000)
#define FIRM_BUFFER ((u8*) 0x23000000)
#define WORK_BUFFER_SIZE (0x400000)
#define FIRM_BUFFER ((u8*) 0x21400000)
#define FIRM_BUFFER_SIZE (0x400000)
#define NAND_BUFFER ((u8*) 0x21800000)
#define NAND_BUFFER_SIZE (0x100000)
inline u32 strchrcount(const char* str, char symbol) {
u32 count = 0;

View File

@ -10,11 +10,12 @@
#define IS_DEVKIT ((*(vu8*) (0x01FFB800+0x19)) != 0x0)
// see: https://3dbrew.org/wiki/CONFIG11_Registers
// (also returns true for sighaxed systems, maybe change the name later?)
#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
// A9LH + unlocked == SigHax
#define IS_SIGHAX (IS_A9LH && IS_UNLOCKED)

View File

@ -11,71 +11,34 @@
#include "nand.h"
#include "sdmmc.h"
#define PART_INFO(pdrv) (DriveInfo + pdrv)
#define PART_TYPE(pdrv) (DriveInfo[pdrv].type)
#define PART_SUBTYPE(pdrv) (DriveInfo[pdrv].subtype)
#define TYPE_NONE 0
#define TYPE_SYSNAND NAND_SYSNAND
#define TYPE_SYSNAND (1UL<<0)
#define TYPE_SDCARD (1UL<<4)
#define SUBTYPE_CTRN 0
#define SUBTYPE_CTRN_N 1
#define SUBTYPE_CTRN_NO 2
#define SUBTYPE_TWLN 3
#define SUBTYPE_TWLP 4
#define SUBTYPE_NONE 5
#define SUBTYPE_CTRN 1
#define SUBTYPE_TWLN 2
#define SUBTYPE_TWLP 3
#define SUBTYPE_NONE 0
typedef struct {
BYTE type;
BYTE subtype;
} FATpartition;
typedef struct {
DWORD offset;
DWORD size;
BYTE keyslot;
} SubtypeDesc;
} FATpartition;
FATpartition DriveInfo[4] = {
{ TYPE_SDCARD, SUBTYPE_NONE }, // 0 - SDCARD
{ TYPE_SYSNAND, SUBTYPE_CTRN }, // 1 - SYSNAND CTRNAND
{ TYPE_SYSNAND, SUBTYPE_TWLN }, // 2 - SYSNAND TWLN
{ TYPE_SYSNAND, SUBTYPE_TWLP }, // 3 - SYSNAND TWLP
FATpartition DriveInfo[13] = {
{ TYPE_SDCARD, SUBTYPE_NONE, 0, 0, 0xFF }, // 0 - SDCARD
{ TYPE_SYSNAND, SUBTYPE_CTRN, 0, 0, 0xFF }, // 1 - SYSNAND CTRNAND
{ TYPE_SYSNAND, SUBTYPE_TWLN, 0, 0, 0xFF }, // 2 - SYSNAND TWLN
{ TYPE_SYSNAND, SUBTYPE_TWLP, 0, 0, 0xFF }, // 3 - SYSNAND TWLP
};
SubtypeDesc SubTypes[5] = {
{ 0x05C980, 0x17AE80, 0x04 }, // O3DS CTRNAND
{ 0x05C980, 0x20F680, 0x05 }, // N3DS CTRNAND
{ 0x05C980, 0x20F680, 0x04 }, // N3DS CTRNAND (downgraded)
{ 0x000097, 0x047DA9, 0x03 }, // TWLN
{ 0x04808D, 0x0105B3, 0x03 } // TWLP
};
static BYTE nand_type_sys = 0;
/*-----------------------------------------------------------------------*/
/* Get Drive Subtype helper */
/*-----------------------------------------------------------------------*/
static inline SubtypeDesc* get_subtype_desc(
__attribute__((unused))
BYTE pdrv /* Physical drive number to identify the drive */
)
{
BYTE subtype = PART_SUBTYPE(pdrv);
if (subtype == SUBTYPE_NONE) {
return NULL;
} else if (subtype == SUBTYPE_CTRN) {
if (nand_type_sys != NAND_TYPE_O3DS)
subtype = (nand_type_sys == NAND_TYPE_N3DS) ? SUBTYPE_CTRN_N : SUBTYPE_CTRN_NO;
}
return &(SubTypes[subtype]);
}
/*-----------------------------------------------------------------------*/
@ -101,12 +64,33 @@ DSTATUS disk_initialize (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
if (pdrv == 0) { // a mounted SD card is the preriquisite for everything else
FATpartition* fat_info = PART_INFO(pdrv);
BYTE type = PART_TYPE(pdrv);
fat_info->offset = fat_info->size = 0;
fat_info->keyslot = 0xFF;
if (type == TYPE_SDCARD) {
if (sdmmc_sdcard_init() != 0) return STA_NOINIT|STA_NODISK;
} else if (pdrv < 4) {
nand_type_sys = CheckNandType();
if (!nand_type_sys) return STA_NOINIT|STA_NODISK;
fat_info->size = getMMCDevice(1)->total_size;
} else if (type == TYPE_SYSNAND) {
NandPartitionInfo nprt_info;
if ((fat_info->subtype == SUBTYPE_CTRN) &&
(GetNandPartitionInfo(&nprt_info, NP_TYPE_STD, NP_SUBTYPE_CTR, 0) != 0) &&
(GetNandPartitionInfo(&nprt_info, NP_TYPE_STD, NP_SUBTYPE_CTR_N, 0) != 0)) {
return STA_NOINIT|STA_NODISK;
} else if ((fat_info->subtype == SUBTYPE_TWLN) &&
(GetNandPartitionInfo(&nprt_info, NP_TYPE_FAT, NP_SUBTYPE_TWL, 0) != 0)) {
return STA_NOINIT|STA_NODISK;
} else if ((fat_info->subtype == SUBTYPE_TWLP) &&
(GetNandPartitionInfo(&nprt_info, NP_TYPE_FAT, NP_SUBTYPE_TWL, 1) != 0)) {
return STA_NOINIT|STA_NODISK;
}
fat_info->offset = nprt_info.sector;
fat_info->size = nprt_info.count;
fat_info->keyslot = nprt_info.keyslot;
}
return RES_OK;
}
@ -132,11 +116,8 @@ DRESULT disk_read (
if (sdmmc_sdcard_readsectors(sector, count, buff))
return RES_PARERR;
} else {
SubtypeDesc* subtype = get_subtype_desc(pdrv);
BYTE keyslot = subtype->keyslot;
DWORD isector = subtype->offset + sector;
if (ReadNandSectors(buff, isector, count, keyslot))
FATpartition* fat_info = PART_INFO(pdrv);
if (ReadNandSectors(buff, fat_info->offset + sector, count, fat_info->keyslot))
return RES_PARERR;
}
@ -166,11 +147,8 @@ DRESULT disk_write (
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff))
return RES_PARERR;
} else {
SubtypeDesc* subtype = get_subtype_desc(pdrv);
BYTE keyslot = subtype->keyslot;
DWORD isector = subtype->offset + sector;
if (WriteNandSectors(buff, isector, count, keyslot))
FATpartition* fat_info = PART_INFO(pdrv);
if (WriteNandSectors(buff, fat_info->offset + sector, count, fat_info->keyslot))
return RES_PARERR; // unstubbed!
}
@ -194,18 +172,12 @@ DRESULT disk_ioctl (
void *buff /* Buffer to send/receive control data */
)
{
BYTE type = PART_TYPE(pdrv);
switch (cmd) {
case GET_SECTOR_SIZE:
*((DWORD*) buff) = 0x200;
return RES_OK;
case GET_SECTOR_COUNT:
if (type == TYPE_SDCARD) { // SD card
*((DWORD*) buff) = getMMCDevice(1)->total_size;
} else if (type != TYPE_NONE) { // NAND
*((DWORD*) buff) = get_subtype_desc(pdrv)->size;
}
*((DWORD*) buff) = PART_INFO(pdrv)->size;
return RES_OK;
case GET_BLOCK_SIZE:
*((DWORD*) buff) = 0x2000;

32
source/fatfs/fatmbr.c Normal file
View File

@ -0,0 +1,32 @@
#include "fatmbr.h"
u32 ValidateMbrHeader(MbrHeader* mbr) {
if (mbr->magic != FATMBR_MAGIC) return 1; // check magic
u32 sector = 1; // check partitions
for (u32 i = 0; i < 4; i++) {
MbrPartitionInfo* partition = mbr->partitions + i;
if (!partition->count && i) continue;
else if (!partition->count) return 1; // first partition can't be empty
if ((partition->type != 0x1) && (partition->type != 0x4) && (partition->type != 0x6) &&
(partition->type != 0xB) && (partition->type != 0xC) && (partition->type != 0xE))
return 1; // bad / unknown filesystem type
if (partition->sector < sector) return 1; // overlapping partitions
sector = partition->sector + partition->count;
}
return 0;
}
u32 ValidateFatHeader(void* fat) {
if (getle16((u8*) fat + 0x1FE) != FATMBR_MAGIC) return 1; // check magic
Fat32Header* fat32 = (Fat32Header*) fat;
if (strncmp(fat32->fs_type, "FAT32 ", 8) == 0)
return 0; // is FAT32 header
Fat16Header* fat16 = (Fat16Header*) fat;
if ((strncmp(fat16->fs_type, "FAT16 ", 8) == 0) ||
(strncmp(fat16->fs_type, "FAT12 ", 8) == 0) ||
(strncmp(fat16->fs_type, "FAT ", 8) == 0))
return 0; // is FAT16 / FAT12 header
if ((getle64(fat16->fs_type) == 0) && (fat16->sct_size == 0x200))
return 0; // special case for public.sav
return 1; // failed, not a FAT header
}

90
source/fatfs/fatmbr.h Normal file
View File

@ -0,0 +1,90 @@
#pragma once
#include "common.h"
#define FATMBR_MAGIC 0xAA55 // little endian!
typedef struct {
u8 status; // 0x80
u8 chs_start[3]; // 0x01 0x01 0x00
u8 type; // 0x0C
u8 chs_end[3]; // 0xFE 0xFF 0xFF
u32 sector; // 0x2000 (4MB offset, 512 byte sectors)
u32 count;
} __attribute__((packed)) MbrPartitionInfo;
typedef struct {
char text[446];
MbrPartitionInfo partitions[4];
u16 magic; // 0xAA55
} __attribute__((packed)) MbrHeader;
typedef struct { // unused
u32 signature0; // 0x41615252
u8 reserved0[480];
u32 signature1; // 0x61417272
u32 clr_free; // 0xFFFFFFFF
u32 clr_next; // 0xFFFFFFFF
u8 reserved1[14];
u16 magic; // 0xAA55
} __attribute__((packed)) FileSystemInfo;
typedef struct {
u8 jmp[3]; // 0x90 0x00 0xEB
char oemname[8]; // "anything"
u16 sct_size; // 0x0200
u8 clr_size; // 0x40 -> 32kB clusters with 512byte sectors
u16 sct_reserved; // 0x20
u8 fat_n; // 0x02
u16 reserved0; // root entry count in FAT16
u16 reserved1; // partition size when <= 32MB
u8 mediatype; // 0xF8
u16 reserved2; // FAT size in sectors in FAT16
u16 sct_track; // 0x3F
u16 sct_heads; // 0xFF
u32 sct_hidden; // same as partition offset in MBR
u32 sct_total; // same as partition size in MBR
u32 fat_size; // roundup((((sct_total - sct_reserved) / clr_size) * 4) / sct_size)
u16 flags; // 0x00
u16 version; // 0x00
u32 clr_root; // 0x02
u16 sct_fsinfo; // 0x01
u16 sct_backup; // 0x06
u8 reserved3[12];
u8 ndrive; // 0x80
u8 head_cur; // 0x00
u8 boot_sig; // 0x29
u32 vol_id; // volume id / 0x00
char vol_label[11]; // "anything "
char fs_type[8]; // "FAT32 "
u8 reserved4[420];
u16 magic; // 0xAA55
} __attribute__((packed)) Fat32Header;
typedef struct { // this struct is not tested enough!
u8 jmp[3]; // 0x90 0x00 0xEB
char oemname[8]; // "anything"
u16 sct_size; // 0x0200
u8 clr_size; // 0x20 (???) -> 16kB clusters with 512byte sectors
u16 sct_reserved; // 0x01
u8 fat_n; // 0x02
u16 root_n; // 0x0200
u16 reserved0; // partition size when <= 32MB
u8 mediatype; // 0xF8
u16 fat_size; // roundup((((sct_total - sct_reserved) / clr_size) * 2) / sct_size)
u16 sct_track; // 0x3F
u16 sct_heads; // 0xFF
u32 sct_hidden; // same as partition offset in MBR
u32 sct_total; // same as partition size in MBR
u8 ndrive; // 0x80
u8 head_cur; // 0x00
u8 boot_sig; // 0x29
u32 vol_id; // volume id / 0x00
char vol_label[11]; // "anything "
char fs_type[8]; // "FAT16 "
u8 reserved4[448];
u16 magic; // 0xAA55
} __attribute__((packed)) Fat16Header;
u32 ValidateMbrHeader(MbrHeader* mbr);
u32 ValidateFatHeader(void* fat);

View File

@ -1,6 +1,7 @@
#include "installer.h"
#include "safewrite.h"
#include "validator.h"
#include "unittype.h"
#include "nand.h"
#include "ui.h"
#include "qff.h"
@ -9,16 +10,12 @@
#define COLOR_STATUS(s) ((s == STATUS_GREEN) ? COLOR_BRIGHTGREEN : (s == STATUS_YELLOW) ? COLOR_BRIGHTYELLOW : (s == STATUS_RED) ? COLOR_RED : COLOR_DARKGREY)
#define MIN_SD_FREE (16 * 1024 * 1024) // 16MB
#define FIRM_NAND_OFFSET 0x0B130000
#define FIRM_NAND_SIZE 0x800000
#define FIRM0_NAND_OFFSET FIRM_NAND_OFFSET
#define FIRM1_NAND_OFFSET (FIRM_NAND_OFFSET + (FIRM_NAND_SIZE/2))
#define NAME_SIGHAXFIRM (IS_DEVKIT ? INPUT_PATH "/" NAME_FIRM "_dev.firm" : INPUT_PATH "/" NAME_FIRM ".firm")
#define NAME_SIGHAXFIRMSHA (IS_DEVKIT ? INPUT_PATH "/" NAME_FIRM "_dev.firm.sha" : INPUT_PATH "/" NAME_FIRM ".firm.sha")
#define NAME_SECTOR0x96 (IS_DEVKIT ? INPUT_PATH "/secret_sector_dev.bin" : INPUT_PATH "/secret_sector.bin")
#define NAME_FIRMBACKUP INPUT_PATH "/firm0firm1.bak"
#define NAME_SECTORBACKUP INPUT_PATH "/sector0x96.bak"
#define NAME_FIRMBACKUP INPUT_PATH "/firm%lu_enc.bak"
#define NAME_SECTORBACKUP INPUT_PATH "/sector0x96_enc.bak"
#define STATUS_GREY -1
#define STATUS_GREEN 0
@ -73,6 +70,7 @@ u32 ShowInstallerStatus(void) {
u32 SafeB9SInstaller(void) {
UINT bt;
u32 ret = 0;
// initialization
ShowString("Initializing, please wait...");
@ -169,6 +167,20 @@ u32 SafeB9SInstaller(void) {
snprintf(msgCrypto, 64, "checking...");
statusCrypto = STATUS_YELLOW;
ShowInstallerStatus();
u32 n_firms = 0;
for (; n_firms < 8; n_firms++) {
NandPartitionInfo np_info;
if (GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, n_firms) != 0) break;
if ((firm_size > np_info.count * 0x200) || (np_info.count * 0x200 > WORK_BUFFER_SIZE)) {
n_firms = 0;
break;
}
}
if (!n_firms) {
snprintf(msgCrypto, 64, "FIRM partition fail");
statusCrypto = STATUS_RED;
return 1;
}
if (!CheckFirmCrypto()) {
snprintf(msgCrypto, 64, "FIRM crypto fail");
statusCrypto = STATUS_RED;
@ -198,25 +210,24 @@ u32 SafeB9SInstaller(void) {
snprintf(msgBackup, 64, "FIRM backup...");
statusBackup = STATUS_YELLOW;
ShowInstallerStatus();
FIL fp;
u32 ret = 0;
if (f_open(&fp, NAME_FIRMBACKUP, FA_READ|FA_WRITE|FA_CREATE_ALWAYS) != FR_OK) {
snprintf(msgBackup, 64, "FIRM backup fail");
statusBackup = STATUS_RED;
return 1;
}
ShowProgress(0, 0, "FIRM backup");
for (u32 pos = 0; (pos < FIRM_NAND_SIZE) && (ret == 0); pos += WORK_BUFFER_SIZE) {
UINT bytes = min(WORK_BUFFER_SIZE, FIRM_NAND_SIZE - pos);
snprintf(msgBackup, 64, "FIRM backup (%luMB/%luMB)",
pos / (1024*1024), (u32) FIRM_NAND_SIZE / (1024 * 1024));
for (u32 i = 0; i < n_firms; i++) {
NandPartitionInfo np_info;
ret = GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i);
if (ret != 0) break;
u32 fsize = np_info.count * 0x200;
u32 foffset = np_info.sector * 0x200;
char bakname[64];
snprintf(bakname, 64, NAME_FIRMBACKUP, i);
snprintf(msgBackup, 64, "FIRM backup (%lu/%lu)", i, n_firms);
ShowInstallerStatus();
if ((ReadNandBytes(WORK_BUFFER, FIRM_NAND_OFFSET + pos, bytes, 0xFF) != 0) ||
(SafeWriteFile(&fp, WORK_BUFFER, pos, bytes) != 0))
if ((ReadNandBytes(WORK_BUFFER, foffset, fsize, 0xFF) != 0) ||
(SafeQWriteFile(bakname, WORK_BUFFER, fsize) != 0)) {
ret = 1;
ShowProgress(pos + bytes, FIRM_NAND_SIZE, "FIRM backup");
break;
}
ShowProgress(i + 1, n_firms, "FIRM backup");
}
f_close(&fp);
if (ret != 0) {
snprintf(msgBackup, 64, "FIRM backup fail");
statusBackup = STATUS_RED;
@ -250,20 +261,21 @@ u32 SafeB9SInstaller(void) {
#ifndef NO_WRITE
ShowProgress(0, 0, "FIRM install");
do {
ret = SafeWriteNand(FIRM_BUFFER, FIRM0_NAND_OFFSET, firm_size, 0x06);
for (u32 i = 0; i < n_firms; i++) {
NandPartitionInfo np_info;
ret = GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i);
if (ret != 0) break;
ShowProgress(1, 2, "FIRM install (1/2)");
snprintf(msgInstall, 64, "FIRM install (1/2)");
ShowInstallerStatus();
ret = SafeWriteNand(FIRM_BUFFER, FIRM1_NAND_OFFSET, firm_size, 0x06);
ret = SafeWriteNand(FIRM_BUFFER, np_info.sector * 0x200, firm_size, np_info.keyslot);
if (ret != 0) break;
ShowProgress(1, 2, "FIRM install (2/2)");
snprintf(msgInstall, 64, "FIRM install (2/2)");
ShowProgress(i+1, n_firms, "FIRM install");
snprintf(msgInstall, 64, "FIRM install (%li/8)", i+1);
ShowInstallerStatus();
}
if (ret != 0) break;
if ((IS_A9LH && !IS_SIGHAX)) {
snprintf(msgInstall, 64, "0x96 revert...");
ShowInstallerStatus();
ret = SafeWriteNand(secret_sector, 0x96 * 0x200, 0x200, IS_O3DS ? 0xFF : 0x11);
ret = SafeWriteNand(secret_sector, SECTOR_SECRET * 0x200, 0x200, IS_O3DS ? 0xFF : 0x11);
if (ret == 0) snprintf(msgA9lh, 64, "uninstalled");
}
} while (false);
@ -281,11 +293,11 @@ u32 SafeB9SInstaller(void) {
ShowInstallerStatus();
}
#elif !defined FAIL_TEST
snprintf(msgInstall, 64, "test mode, not done");
snprintf(msgInstall, 64, "no write mode");
statusInstall = STATUS_YELLOW;
return 0;
#else
snprintf(msgInstall, 64, "fail test mode...");
snprintf(msgInstall, 64, "emergency mode");
statusInstall = STATUS_YELLOW;
ShowInstallerStatus();
#endif
@ -299,23 +311,24 @@ u32 SafeB9SInstaller(void) {
snprintf(msgBackup, 64, "FIRM restore...");
statusBackup = STATUS_YELLOW;
ShowInstallerStatus();
if (f_open(&fp, NAME_FIRMBACKUP, FA_READ|FA_OPEN_EXISTING) != FR_OK) {
snprintf(msgBackup, 64, "FIRM restore fail");
statusBackup = STATUS_RED;
return 1;
}
ShowProgress(0, 0, "FIRM restore");
for (u32 pos = 0; (pos < FIRM_NAND_SIZE) && (ret == 0); pos += WORK_BUFFER_SIZE) {
UINT bytes = min(WORK_BUFFER_SIZE, FIRM_NAND_SIZE - pos);
snprintf(msgBackup, 64, "FIRM restore (%luMB/%luMB)",
pos / (1024*1024), (u32) FIRM_NAND_SIZE / (1024 * 1024));
for (u32 i = 0; i < n_firms; i++) {
NandPartitionInfo np_info;
ret = GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i);
if (ret != 0) break;
u32 fsize = np_info.count * 0x200;
u32 foffset = np_info.sector * 0x200;
char bakname[64];
snprintf(bakname, 64, NAME_FIRMBACKUP, i);
snprintf(msgBackup, 64, "FIRM restore (%lu/%lu)", i, n_firms);
ShowInstallerStatus();
if ((f_read(&fp, WORK_BUFFER, bytes, &bt) != FR_OK) || (bt != bytes) ||
(WriteNandBytes(WORK_BUFFER, FIRM_NAND_OFFSET + pos, bytes, 0xFF) != 0))
if ((f_qread(bakname, WORK_BUFFER, 0, fsize, &bt) != FR_OK) || (bt != fsize) ||
(WriteNandBytes(WORK_BUFFER, foffset, fsize, 0xFF) != 0)) {
ret = 1;
ShowProgress(pos + bytes, FIRM_NAND_SIZE, "FIRM restore");
break;
}
ShowProgress(i + 1, n_firms, "FIRM restore");
}
f_close(&fp);
if (ret != 0) {
snprintf(msgBackup, 64, "FIRM restore fail");
statusBackup = STATUS_RED;
@ -326,7 +339,7 @@ u32 SafeB9SInstaller(void) {
ShowInstallerStatus();
u8 sector_backup[0x200];
if ((f_qread(NAME_SECTORBACKUP, sector_backup, 0, 0x200, &bt) != FR_OK) || (bt != 0x200) ||
(WriteNandSectors(sector_backup, 0x96, 1, 0xFF) != 0)) {
(WriteNandSectors(sector_backup, SECTOR_SECRET, 1, 0xFF) != 0)) {
snprintf(msgBackup, 64, "0x96 restore fail");
statusBackup = STATUS_RED;
return 1;

View File

@ -2,14 +2,35 @@
#include "keydb.h"
#include "aes.h"
#include "sha.h"
#include "fatmbr.h"
#include "unittype.h"
#include "sdmmc.h"
#include "qff.h"
#define KEY95_SHA256 ((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256)
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
static const u32 np_keyslots[9][4] = { // [NP_TYPE][NP_SUBTYPE]
{ 0xFF, 0xFF, 0xFF, 0xFF }, // none
{ 0xFF, 0x03, 0x04, 0x05 }, // standard
{ 0xFF, 0x03, 0x04, 0x05 }, // FAT (custom, not in NCSD)
{ 0xFF, 0xFF, 0x06, 0xFF }, // FIRM
{ 0xFF, 0xFF, 0x07, 0xFF }, // AGBSAVE
{ 0xFF, 0xFF, 0xFF, 0xFF }, // NCSD (custom)
{ 0xFF, 0xFF, 0xFF, 0xFF }, // D0K3 (custom)
{ 0xFF, 0xFF, 0xFF, 0x11 }, // SECRET (custom)
{ 0xFF, 0xFF, 0xFF, 0xFF } // BONUS (custom)
};
static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file
static const u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte)
0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86,
0x62, 0x22, 0x5C, 0xFD, 0x6F, 0xAE, 0x9B, 0x0A, 0x85, 0xA5, 0xCE, 0x21, 0xAA, 0xB6, 0xC8, 0x4D
};
static const u8 slot0x24KeyY_sha256[0x20] = { // hash for slot0x24KeyY (16 byte)
0x5F, 0x04, 0x01, 0x22, 0x95, 0xB2, 0x23, 0x70, 0x12, 0x40, 0x53, 0x30, 0xC0, 0xA7, 0xBF, 0x7C,
0xD4, 0x40, 0x92, 0x25, 0xD1, 0x9D, 0xA2, 0xDE, 0xCD, 0xC7, 0x12, 0x97, 0x08, 0x46, 0x54, 0xB7
};
static const u8 slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 byte of sector0x96)
0xBA, 0xC1, 0x40, 0x9C, 0x6E, 0xE4, 0x1F, 0x04, 0xAA, 0xC4, 0xE2, 0x09, 0x5C, 0xE9, 0x4F, 0x78,
@ -21,32 +42,6 @@ static const u8 slot0x11Key95dev_sha256[0x20] = { // slot0x11Key95 hash (first 1
0xD7, 0x6C, 0xE9, 0xAD, 0xE7, 0xFE, 0x9A, 0x25, 0x4E, 0x4A, 0x0C, 0x82, 0x67, 0xB5, 0x4A, 0x7B
};
static const u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
0x80, 0x89, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0xA9, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00,
0x80, 0xC9, 0x05, 0x00, 0x80, 0xF6, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const u8 nand_magic_o3ds[0x60] = { // NCSD NAND header O3DS magic
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
0x80, 0x89, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0xA9, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00,
0x80, 0xC9, 0x05, 0x00, 0x80, 0xAE, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/*static const u8 twl_mbr[0x42] = { // encrypted version inside the NCSD NAND header (@0x1BE)
0x00, 0x04, 0x18, 0x00, 0x06, 0x01, 0xA0, 0x3F, 0x97, 0x00, 0x00, 0x00, 0xA9, 0x7D, 0x04, 0x00,
0x00, 0x04, 0x8E, 0x40, 0x06, 0x01, 0xA0, 0xC3, 0x8D, 0x80, 0x04, 0x00, 0xB3, 0x05, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};*/
static u8 CtrNandCtr[16];
static u8 TwlNandCtr[16];
static u8 OtpSha256[32] = { 0 };
@ -54,23 +49,24 @@ static u8 OtpSha256[32] = { 0 };
u32 LoadKeyYFromP9(u8* key, const u8* keyhash, u32 offset, u32 keyslot)
{
static u32 offsetA9l = 0x066A00; // fixed offset, this only has to work for FIRM90 / FIRM81
static const u32 offsetA9l = 0x066A00; // fixed offset, this only has to work for FIRM90 / FIRM81
static const u32 sector_firm0 = 0x058980; // standard firm0 sector (this only has to work in A9LH anyways)
u8 ctr0x15[16] __attribute__((aligned(32)));
u8 keyY0x15[16] __attribute__((aligned(32)));
u8 keyY[16] __attribute__((aligned(32)));
u8 header[0x200];
// check arm9loaderhax
if (!IS_A9LH || (offset < (offsetA9l + 0x0800))) return 1;
if (!IS_A9LH || IS_SIGHAX || (offset < (offsetA9l + 0x0800))) return 1;
// section 2 (arm9loader) header of FIRM
// this is @0x066A00 in FIRM90 & FIRM81
ReadNandBytes(header, (SECTOR_FIRM0 * 0x200) + offsetA9l, 0x200, 0x06);
ReadNandBytes(header, (sector_firm0 * 0x200) + offsetA9l, 0x200, 0x06);
memcpy(keyY0x15, header + 0x10, 0x10); // 0x15 keyY
memcpy(ctr0x15, header + 0x20, 0x10); // 0x15 counter
// read and decrypt the encrypted keyY
ReadNandBytes(keyY, (SECTOR_FIRM0 * 0x200) + offset, 0x10, 0x06);
ReadNandBytes(keyY, (sector_firm0 * 0x200) + offset, 0x10, 0x06);
setup_aeskeyY(0x15, keyY0x15);
use_aeskey(0x15);
ctr_decrypt_byte(keyY, keyY, 0x10, offset - (offsetA9l + 0x800), AES_CNT_CTRNAND_MODE, ctr0x15);
@ -92,23 +88,27 @@ 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);
if (!CheckSector0x96Crypto()) { // 3dssafe users be damned (no offense meant to mashers)
}
if (!CheckSector0x96Crypto()) { // if all else fails...
u8 __attribute__((aligned(32))) otp0x90[0x90];
u8 __attribute__((aligned(32))) otp_key[0x10];
u8 __attribute__((aligned(32))) otp_iv[0x10];
memcpy(otp0x90, (u8*) 0x01FFB800, 0x90);
if ((LoadKeyFromFile(otp_key, 0x11, 'N', "OTP") == 0) &&
(LoadKeyFromFile(otp_iv, 0x11, 'I', "IVOTP") == 0)) {
((LoadKeyFromFile(otp_iv, 0x11, 'I', "IVOTP") == 0) ||
(LoadKeyFromFile(otp_iv, 0x11, 'I', "OTP") == 0))) {
setup_aeskey(0x11, otp_key);
use_aeskey(0x11);
cbc_encrypt(otp0x90, otp0x90, 0x90 / 0x10, AES_CNT_TITLEKEY_ENCRYPT_MODE, otp_iv);
sha_quick(OtpSha256, otp0x90, 0x90, SHA256_MODE);
}
}
}
// part #1: Get NAND CID, set up TWL/CTR counter
u32 NandCid[4];
@ -124,7 +124,7 @@ bool InitNandCrypto(void)
// part #2: TWL KEY
// see: https://www.3dbrew.org/wiki/Memory_layout#ARM9_ITCM
if (IS_A9LH) { // only for a9lh
if (IS_A9LH) { // only for a9lh (and sighax, for now)
u32* TwlCustId = (u32*) (0x01FFB808);
u8 TwlKeyX[16] __attribute__((aligned(32)));
u8 TwlKeyY[16] __attribute__((aligned(32)));
@ -148,14 +148,21 @@ bool InitNandCrypto(void)
use_aeskey(0x03);
}
// part #3: CTRNAND N3DS KEY
// part #3: CTRNAND N3DS KEY / AGBSAVE CMAC KEY
// thanks AuroraWright and Gelex for advice on this
// see: https://github.com/AuroraWright/Luma3DS/blob/master/source/crypto.c#L347
if (IS_A9LH) { // only for a9lh
if (IS_A9LH && !IS_SIGHAX) { // only on A9LH, not required on sighax
// keyY 0x05 is encrypted @0x0EB014 in the FIRM90
// keyY 0x05 is encrypted @0x0EB24C in the FIRM81
if ((LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB014, 0x05) != 0) &&
(LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB24C, 0x05) != 0)) {};
(LoadKeyYFromP9(slot0x05KeyY, slot0x05KeyY_sha256, 0x0EB24C, 0x05) != 0))
LoadKeyFromFile(slot0x05KeyY, 0x05, 'Y', NULL);
// keyY 0x24 is encrypted @0x0E62DC in the FIRM90
// keyY 0x24 is encrypted @0x0E6514 in the FIRM81
if ((LoadKeyYFromP9(NULL, slot0x24KeyY_sha256, 0x0E62DC, 0x24) != 0) &&
(LoadKeyYFromP9(NULL, slot0x24KeyY_sha256, 0x0E6514, 0x24) != 0))
LoadKeyFromFile(NULL, 0x24, 'Y', NULL);
}
return true;
@ -164,17 +171,11 @@ bool InitNandCrypto(void)
bool CheckSlot0x05Crypto(void)
{
// step #1 - check the slot0x05KeyY SHA-256
u8 shasum[32];
sha_quick(shasum, slot0x05KeyY, 16, SHA256_MODE);
if (memcmp(shasum, slot0x05KeyY_sha256, 32) == 0)
if (sha_cmp(slot0x05KeyY_sha256, slot0x05KeyY, 16, SHA256_MODE) == 0)
return true;
// step #2 - check actual CTRNAND magic
const u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20};
const u32 sector = 0x05CAD7;
u8 buffer[0x200];
ReadNandSectors(buffer, sector, 1, 0x05);
if (memcmp(buffer, magic, 8) == 0)
// step #2 - check actual presence of CTRNAND FAT
if (GetNandPartitionInfo(NULL, NP_TYPE_STD, NP_SUBTYPE_CTR_N, 0) == 0)
return true;
// failed if we arrive here
@ -184,19 +185,20 @@ bool CheckSlot0x05Crypto(void)
bool CheckSector0x96Crypto(void)
{
u8 buffer[0x200];
ReadNandSectors(buffer, 0x96, 1, 0x11);
return (sha_cmp((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256, buffer, 16, SHA256_MODE) == 0);
ReadNandSectors(buffer, SECTOR_SECRET, 1, 0x11);
return (sha_cmp(KEY95_SHA256, buffer, 16, SHA256_MODE) == 0);
}
bool CheckFirmCrypto(void)
{
// check the FIRM magic
const u8 magic[8] = {'F', 'I', 'R', 'M'};
const u32 sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 };
for (u32 i = 0; i < 8; i++) {
NandPartitionInfo np_info;
u8 buffer[0x200];
for (u32 i = 0; i < sizeof(sectors) / sizeof(u32); i++) {
ReadNandSectors(buffer, sectors[i], 1, 0x06);
if (memcmp(buffer, magic, sizeof(magic)) == 0) return true;
if ((GetNandPartitionInfo(&np_info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, i) != 0) ||
(ReadNandSectors(buffer, np_info.sector, 1, np_info.keyslot) != 0)) break;
if (memcmp(buffer, magic, sizeof(magic)) == 0) return true; // success
}
// failed if we arrive here
@ -205,17 +207,17 @@ bool CheckFirmCrypto(void)
void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot)
{
u32 mode = (sector >= SECTOR_TWL + SIZE_TWL) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE;
u32 mode = (keyslot != 0x03) ? AES_CNT_CTRNAND_MODE : AES_CNT_TWLNAND_MODE; // somewhat hacky
u8 ctr[16] __attribute__((aligned(32)));
u32 blocks = count * (0x200 / 0x10);
// copy NAND CTR and increment it
memcpy(ctr, (sector >= SECTOR_TWL + SIZE_TWL) ? CtrNandCtr : TwlNandCtr, 16);
memcpy(ctr, (keyslot != 0x03) ? CtrNandCtr : TwlNandCtr, 16); // hacky again
add_ctr(ctr, sector * (0x200 / 0x10));
// decrypt the data
use_aeskey(keyslot);
ctr_decrypt(buffer, buffer, blocks, mode, ctr);
ctr_decrypt((void*) buffer, (void*) buffer, blocks, mode, ctr);
}
void CryptSector0x96(void* buffer, bool encrypt)
@ -228,16 +230,16 @@ void CryptSector0x96(void* buffer, bool encrypt)
// decrypt the sector
use_aeskey(0x11);
ecb_decrypt(buffer, buffer, 0x200 / AES_BLOCK_SIZE, mode);
ecb_decrypt((void*) buffer, (void*) buffer, 0x200 / AES_BLOCK_SIZE, mode);
}
int ReadNandBytes(void* buffer, u32 offset, u32 count, u32 keyslot)
int ReadNandBytes(void* buffer, u64 offset, u64 count, u32 keyslot)
{
u8* buffer8 = (u8*) buffer;
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
// simple wrapper function for ReadNandSectors(...)
return ReadNandSectors(buffer8, offset / 0x200, count / 0x200, keyslot);
return ReadNandSectors(buffer, offset / 0x200, count / 0x200, keyslot);
} else { // misaligned data -> -___-
u8* buffer8 = (u8*) buffer;
u8 l_buffer[0x200];
int errorcode = 0;
if (offset % 0x200) { // handle misaligned offset
@ -264,13 +266,13 @@ int ReadNandBytes(void* buffer, u32 offset, u32 count, u32 keyslot)
}
}
int WriteNandBytes(const void* buffer, u32 offset, u32 count, u32 keyslot)
int WriteNandBytes(const void* buffer, u64 offset, u64 count, u32 keyslot)
{
u8* buffer8 = (u8*) buffer;
if (!(offset % 0x200) && !(count % 0x200)) { // aligned data -> simple case
// simple wrapper function for WriteNandSectors(...)
return WriteNandSectors(buffer8, offset / 0x200, count / 0x200, keyslot);
return WriteNandSectors(buffer, offset / 0x200, count / 0x200, keyslot);
} else { // misaligned data -> -___-
u8* buffer8 = (u8*) buffer8;
u8 l_buffer[0x200];
int errorcode = 0;
if (offset % 0x200) { // handle misaligned offset
@ -303,23 +305,24 @@ int WriteNandBytes(const void* buffer, u32 offset, u32 count, u32 keyslot)
int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot)
{
u8* buffer8 = (u8*) buffer;
if (!count) return 0; // <--- just to be safe
int errorcode = sdmmc_nand_readsectors(sector, count, buffer);
int errorcode = sdmmc_nand_readsectors(sector, count, buffer8);
if (errorcode) return errorcode;
if ((keyslot == 0x11) && (sector == 0x96)) CryptSector0x96(buffer, false);
else if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot);
if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(buffer8, false);
else if (keyslot < 0x40) CryptNand(buffer8, sector, count, keyslot);
return 0;
}
int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot)
{
u8* buffer8 = (u8*) buffer;
// buffer must not be changed, so this is a little complicated
const u8* buffer8 = (u8*) buffer;
for (u32 s = 0; s < count; s += (NAND_BUFFER_SIZE / 0x200)) {
u32 pcount = min((NAND_BUFFER_SIZE/0x200), (count - s));
memcpy(NAND_BUFFER, buffer8 + (s*0x200), pcount * 0x200);
if ((keyslot == 0x11) && (sector == 0x96)) CryptSector0x96(NAND_BUFFER, true);
if ((keyslot == 0x11) && (sector == SECTOR_SECRET)) CryptSector0x96(NAND_BUFFER, true);
else if (keyslot < 0x40) CryptNand(NAND_BUFFER, sector + s, pcount, keyslot);
int errorcode = sdmmc_nand_writesectors(sector + s, pcount, NAND_BUFFER);
if (errorcode) return errorcode;
@ -328,43 +331,132 @@ int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot)
return 0;
}
u32 CheckNandHeader(void* header)
// shamelessly stolen from myself
// see: https://github.com/d0k3/GodMode9/blob/master/source/game/ncsd.c#L4
u32 ValidateNandNcsdHeader(NandNcsdHeader* header)
{
// TWL MBR check - ignored
/*u8 header_dec[0x200];
memcpy(header_dec, header, 0x200);
CryptNand(header_dec, 0, 1, 0x03);
if (memcmp(header_dec + 0x1BE, twl_mbr, sizeof(twl_mbr)) != 0)
return 0; // header does not belong to console */
u8 zeroes[16] = { 0 };
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
(memcmp(header->partitions_fs_type, zeroes, 8) == 0) || header->mediaId) // prevent detection of cart NCSD images
return 1;
// header type check
u8* header_enc = header;
if (memcmp(header_enc + 0x100, nand_magic_n3ds, sizeof(nand_magic_n3ds) == 0) == 0)
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
else if (memcmp(header_enc + 0x100, nand_magic_o3ds, sizeof(nand_magic_o3ds) == 0) == 0)
return NAND_TYPE_O3DS;
u32 data_units = 0;
u32 firm_count = 0;
for (u32 i = 0; i < 8; i++) {
NandNcsdPartition* partition = header->partitions + i;
u8 np_type = header->partitions_fs_type[i];
if ((i == 0) && !partition->size) return 1; // first content must be there
else if (!partition->size) continue;
if (!np_type) return 1; // partition must have a type
if (partition->offset < data_units)
return 1; // overlapping partitions, failed
data_units = partition->offset + partition->size;
if (np_type == NP_TYPE_FIRM) firm_count++; // count firms
}
if (data_units > header->size) return 1;
if (!firm_count) return 1; // at least one firm is required
return 0;
}
u32 CheckNandType(void)
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd)
{
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF) != 0)
u32 nand_minsize = 0;
for (u32 prt_idx = 0; prt_idx < 8; prt_idx++) {
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
if (prt_end > nand_minsize) nand_minsize = prt_end;
}
return nand_minsize;
}
u32 GetNandMinSizeSectors(void)
{
NandNcsdHeader ncsd;
if ((ReadNandSectors((u8*) &ncsd, 0, 1, 0xFF) != 0) ||
(ValidateNandNcsdHeader(&ncsd) != 0)) return 0;
return GetNandNcsdMinSizeSectors(&ncsd);
}
u32 GetNandSizeSectors(void)
{
return getMMCDevice(0)->total_size;
}
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd)
{
// safety / set keyslot
if ((type == NP_TYPE_FAT) || (type > NP_TYPE_BONUS) || (subtype > NP_SUBTYPE_CTR_N)) return 1;
info->keyslot = np_keyslots[type][subtype];
// full (minimum) NAND "partition"
if (type == NP_TYPE_NONE) {
info->sector = 0x00;
info->count = GetNandNcsdMinSizeSectors(ncsd);
return 0;
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, 0x60) == 0) {
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, 0x60) == 0) {
u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20};
if (ReadNandSectors(NAND_BUFFER, 0x5CAE5, 1, 0x04) != 0)
}
// special, custom partition types, not in NCSD
if (type >= NP_TYPE_NCSD) {
if (type == NP_TYPE_NCSD) {
info->sector = 0x00; // hardcoded
info->count = 0x01;
} else if (type == NP_TYPE_D0K3) {
info->sector = SECTOR_D0K3; // hardcoded
info->count = SECTOR_SECRET - info->sector;
} else if (type == NP_TYPE_SECRET) {
info->sector = SECTOR_SECRET;
info->count = 0x01;
} else if (type == NP_TYPE_BONUS) {
info->sector = GetNandNcsdMinSizeSectors(ncsd);
info->count = 0x00; // placeholder, actual size needs info from NAND chip
} else return 1;
return 0;
return ((IS_O3DS) || (memcmp(magic, NAND_BUFFER, 8) == 0)) ?
NAND_TYPE_O3DS : NAND_TYPE_NO3DS;
}
u32 prt_idx = 8;
for (prt_idx = 0; prt_idx < 8; prt_idx++) {
if ((ncsd->partitions_fs_type[prt_idx] != type) ||
(ncsd->partitions_crypto_type[prt_idx] != subtype)) continue;
if (index == 0) break;
index--;
}
if (prt_idx >= 8) return 1; // not found
info->sector = ncsd->partitions[prt_idx].offset;
info->count = ncsd->partitions[prt_idx].size;
return 0;
}
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index)
{
// workaround for info == NULL
NandPartitionInfo dummy;
if (!info) info = &dummy;
// find type & subtype in NCSD header
u8 header[0x200];
ReadNandSectors(header, 0x00, 1, 0xFF);
NandNcsdHeader* ncsd = (NandNcsdHeader*) header;
if ((ValidateNandNcsdHeader(ncsd) != 0) ||
((type == NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, NP_TYPE_STD, subtype, 0, ncsd) != 0)) ||
((type != NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, type, subtype, index, ncsd) != 0)))
return 1; // not found
if (type == NP_TYPE_BONUS) { // size of bonus partition
info->count = GetNandSizeSectors() - info->sector;
} else if (type == NP_TYPE_FAT) { // FAT type specific stuff
ReadNandSectors(header, info->sector, 1, info->keyslot);
MbrHeader* mbr = (MbrHeader*) header;
if ((ValidateMbrHeader(mbr) != 0) || (index >= 4) ||
(mbr->partitions[index].sector == 0) || (mbr->partitions[index].count == 0) ||
(mbr->partitions[index].sector + mbr->partitions[index].count > info->count))
return 1;
info->sector += mbr->partitions[index].sector;
info->count = mbr->partitions[index].count;
}
return 0;
}
u64 GetNandSizeSectors(void)
{
return getMMCDevice(0)->total_size;
}

View File

@ -1,39 +1,59 @@
#pragma once
#include "common.h"
#include "unittype.h"
#define NAND_MIN_SECTORS ((!IS_O3DS) ? NAND_MIN_SECTORS_N3DS : NAND_MIN_SECTORS_O3DS)
#define NAND_SYSNAND (1<<0)
#define NAND_ZERONAND (1<<3)
#define NAND_TYPE_O3DS (1<<4)
#define NAND_TYPE_N3DS (1<<5)
#define NAND_TYPE_NO3DS (1<<6)
// minimum size of NAND memory
#define NAND_MIN_SECTORS_O3DS 0x1D7800
#define NAND_MIN_SECTORS_N3DS 0x26C000
// start sectors of partitions
#define SECTOR_TWL 0x000000
// hardcoded start sectors
#define SECTOR_D0K3 0x000001
#define SECTOR_SECRET 0x000096
#define SECTOR_TWLN 0x000097
#define SECTOR_TWLP 0x04808D
#define SECTOR_AGBSAVE 0x058800
#define SECTOR_FIRM0 0x058980
#define SECTOR_FIRM1 0x05A980
#define SECTOR_CTR 0x05C980
// sizes of partitions (in sectors)
#define SIZE_TWL 0x058800
#define SIZE_TWLN 0x047DA9
#define SIZE_TWLP 0x0105B3
#define SIZE_AGBSAVE 0x000180
#define SIZE_FIRM0 0x002000
#define SIZE_FIRM1 0x002000
#define SIZE_CTR_O3DS 0x17AE80
#define SIZE_CTR_N3DS 0x20F680
// filenames for sector 0x96
#define SECTOR_NAME "sector0x96.bin"
#define SECRET_NAME "secret_sector.bin"
// 0x110...0x118 in the NAND NCSD header
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
#define NP_TYPE_NONE 0
#define NP_TYPE_STD 1
#define NP_TYPE_FAT 2 // this is of our own making
#define NP_TYPE_FIRM 3
#define NP_TYPE_AGB 4
#define NP_TYPE_NCSD 5 // this is of our own making
#define NP_TYPE_D0K3 6 // my own partition ^_^
#define NP_TYPE_SECRET 7 // this is of our own making
#define NP_TYPE_BONUS 8 // this is of our own making
// 0x118...0x120 in the NAND NCSD header
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
#define NP_SUBTYPE_NONE 0
#define NP_SUBTYPE_TWL 1
#define NP_SUBTYPE_CTR 2
#define NP_SUBTYPE_CTR_N 3
typedef struct {
u32 sector;
u32 count;
u32 keyslot;
} __attribute__((packed)) NandPartitionInfo;
typedef struct {
u32 offset;
u32 size;
} __attribute__((packed)) NandNcsdPartition;
// see: https://www.3dbrew.org/wiki/NCSD#NCSD_header
typedef struct {
u8 signature[0x100];
u8 magic[4];
u32 size;
u64 mediaId; // this is zero
u8 partitions_fs_type[8];
u8 partitions_crypto_type[8];
NandNcsdPartition partitions[8];
u8 unknown[0x5E];
u8 twl_mbr[0x42];
} __attribute__((packed)) NandNcsdHeader;
bool InitNandCrypto(void);
bool CheckSlot0x05Crypto(void);
@ -42,11 +62,14 @@ bool CheckFirmCrypto(void);
void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot);
void CryptSector0x96(void* buffer, bool encrypt);
int ReadNandBytes(void* buffer, u32 offset, u32 count, u32 keyslot);
int WriteNandBytes(const void* buffer, u32 offset, u32 count, u32 keyslot);
int ReadNandBytes(void* buffer, u64 offset, u64 count, u32 keyslot);
int WriteNandBytes(const void* buffer, u64 offset, u64 count, u32 keyslot);
int ReadNandSectors(void* buffer, u32 sector, u32 count, u32 keyslot);
int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot);
u64 GetNandSizeSectors(void);
u32 CheckNandHeader(void* header);
u32 CheckNandType(void);
u32 ValidateNandNcsdHeader(NandNcsdHeader* header);
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd);
u32 GetNandMinSizeSectors(void);
u32 GetNandSizeSectors(void);
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd);
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index);

View File

@ -18,6 +18,18 @@ u32 SafeWriteFile(FIL* file, void* buff, FSIZE_t ofs, UINT btw) {
return (sha_cmp(sha_in, buff, btw, SHA256_MODE) == 0) ? 0 : 1;
}
// safe file quick writer function
// same as SafeWriteFile(), but also handles filec reate, too, same warnings apply
u32 SafeQWriteFile(const TCHAR* path, void* buff, UINT btw) {
FIL fp;
u32 ret = 0;
if (f_open(&fp, path, FA_READ|FA_WRITE|FA_CREATE_ALWAYS) != FR_OK)
return 1;
ret = SafeWriteFile(&fp, buff, 0, btw);
f_close(&fp);
return ret;
}
// safe NAND writer function, warnings:
// (1) contents of buffer may change on corruption
// (2) uses SHA register

View File

@ -4,4 +4,5 @@
#include "ff.h"
u32 SafeWriteFile(FIL* file, void* buff, FSIZE_t ofs, UINT btw);
u32 SafeQWriteFile(const TCHAR* path, void* buff, UINT btw);
u32 SafeWriteNand(void* buff, u32 ofs, u32 btw, u32 keyslot);