First cleanup & adaptions

This commit is contained in:
d0k3 2016-02-16 16:15:08 +01:00
parent 0df734a76a
commit 256327a8c9
15 changed files with 333 additions and 2477 deletions

View File

@ -18,9 +18,9 @@ include $(DEVKITARM)/ds_rules
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
export TARGET := GodMode9 export TARGET := GodMode9
BUILD := build BUILD := build
SOURCES := source source/fatfs source/fatfs/crypto source/abstraction SOURCES := source source/fatfs source/decryptor source/abstraction
DATA := data DATA := data
INCLUDES := include source source/fatfs source/fatfs/crypto INCLUDES := include source source/fatfs source/decryptor
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# options for code generation # options for code generation

220
source/fatfs/3dsnand.c Normal file
View File

@ -0,0 +1,220 @@
#include "platform.h"
#include "fatfs/aes.h"
#include "fatfs/3dsnand.h"
#include "fatfs/sdmmc.h"
// see: http://3dbrew.org/wiki/Flash_Filesystem
static PartitionInfo partitions[] = {
{ "TWLN", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x00012E00, 0x08FB5200, 0x3, AES_CNT_TWLNAND_MODE },
{ "TWLP", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x09011A00, 0x020B6600, 0x3, AES_CNT_TWLNAND_MODE },
{ "AGBSAVE", {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 0x0B100000, 0x00030000, 0x7, AES_CNT_CTRNAND_MODE },
{ "FIRM0", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B130000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
{ "FIRM1", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B530000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95CA00, 0x2F3E3600, 0x4, AES_CNT_CTRNAND_MODE }, // O3DS
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95AE00, 0x41D2D200, 0x5, AES_CNT_CTRNAND_MODE } // N3DS
};
static u32 emunand_header = 0;
static u32 emunand_offset = 0;
u32 CheckEmuNand(void)
{
u8* buffer = BUFFER_ADDRESS;
u32 nand_size_sectors = getMMCDevice(0)->total_size;
u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS;
u32 ret = EMUNAND_NOT_READY;
// check the MBR for presence of a hidden partition
sdmmc_sdcard_readsectors(0, 1, buffer);
u32 hidden_sectors = getle32(buffer + 0x1BE + 0x8);
for (u32 offset_sector = 0; offset_sector + nand_size_sectors < hidden_sectors; offset_sector += multi_sectors) {
// check for Gateway type EmuNAND
sdmmc_sdcard_readsectors(offset_sector + nand_size_sectors, 1, buffer);
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
ret |= EMUNAND_GATEWAY << (2 * (offset_sector / multi_sectors));
continue;
}
// check for RedNAND type EmuNAND
sdmmc_sdcard_readsectors(offset_sector + 1, 1, buffer);
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
ret |= EMUNAND_REDNAND << (2 * (offset_sector / multi_sectors));
continue;
}
// EmuNAND ready but not set up
ret |= EMUNAND_READY << (2 * (offset_sector / multi_sectors));
}
return ret;
}
void SetNand(bool set_emunand, u32 base_sector)
{
// no checks here AT ALL - be careful!!!
if (set_emunand) {
emunand_header = base_sector + getMMCDevice(0)->total_size;
emunand_offset = base_sector;
} else {
emunand_header = 0;
emunand_offset = 0;
}
}
int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out)
{
if (emunand_header) {
if (sector_no == 0) {
int errorcode = sdmmc_sdcard_readsectors(emunand_header, 1, out);
if (errorcode) return errorcode;
sector_no = 1;
numsectors--;
out += 0x200;
}
return sdmmc_sdcard_readsectors(sector_no + emunand_offset, numsectors, out);
} else return sdmmc_nand_readsectors(sector_no, numsectors, out);
}
int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in)
{
if (emunand_header) {
if (sector_no == 0) {
int errorcode = sdmmc_sdcard_writesectors(emunand_header, 1, in);
if (errorcode) return errorcode;
sector_no = 1;
numsectors--;
in += 0x200;
}
return sdmmc_sdcard_writesectors(sector_no + emunand_offset, numsectors, in);
} else return sdmmc_nand_writesectors(sector_no, numsectors, in);
}
u32 GetNandSize(void)
{
return getMMCDevice(0)->total_size * NAND_SECTOR_SIZE;
}
PartitionInfo* GetPartitionInfo(u32 partition_id)
{
u32 partition_num = 0;
if (partition_id == P_CTRNAND) {
partition_num = (GetUnitPlatform() == PLATFORM_3DS) ? 5 : 6;
} else {
for(; !(partition_id & (1<<partition_num)) && (partition_num < 32); partition_num++);
}
return (partition_num >= 32) ? NULL : &(partitions[partition_num]);
}
u32 GetNandCtr(u8* ctr, u32 offset)
{
static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"};
static const u8* version_ctrs[] = {
(u8*)0x080D7CAC,
(u8*)0x080D858C,
(u8*)0x080D748C,
(u8*)0x080D740C,
(u8*)0x080D74CC,
(u8*)0x080D794C
};
static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32);
static u8* ctr_start = NULL;
if (ctr_start == NULL) {
for (u32 i = 0; i < version_ctrs_len; i++) {
if (*(u32*)version_ctrs[i] == 0x5C980) {
ctr_start = (u8*) version_ctrs[i] + 0x30;
}
}
// If value not in previous list start memory scanning (test range)
if (ctr_start == NULL) {
for (u8* c = (u8*) 0x080D8FFF; c > (u8*) 0x08000000; c--) {
if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) {
ctr_start = c + 0x30;
break;
}
}
}
if (ctr_start == NULL) {
return 1;
}
}
// the ctr is stored backwards in memory
if (offset >= 0x0B100000) { // CTRNAND/AGBSAVE region
for (u32 i = 0; i < 16; i++)
ctr[i] = *(ctr_start + (0xF - i));
} else { // TWL region
for (u32 i = 0; i < 16; i++)
ctr[i] = *(ctr_start + 0x88 + (0xF - i));
}
// increment counter
add_ctr(ctr, offset / 0x10);
return 0;
}
u32 CryptBuffer(CryptBufferInfo *info)
{
u8 ctr[16] __attribute__((aligned(32)));
memcpy(ctr, info->ctr, 16);
u8* buffer = info->buffer;
u32 size = info->size;
u32 mode = info->mode;
if (info->setKeyY) {
u8 keyY[16] __attribute__((aligned(32)));
memcpy(keyY, info->keyY, 16);
setup_aeskeyY(info->keyslot, keyY);
info->setKeyY = 0;
}
use_aeskey(info->keyslot);
for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) {
set_ctr(ctr);
if ((mode & (0x7 << 27)) == AES_CBC_DECRYPT_MODE)
memcpy(ctr, buffer, 0x10);
aes_decrypt((void*) buffer, (void*) buffer, 1, mode);
if ((mode & (0x7 << 27)) == AES_CBC_ENCRYPT_MODE)
memcpy(ctr, buffer, 0x10);
else if ((mode & (0x7 << 27)) == AES_CTR_MODE)
add_ctr(ctr, 0x1);
}
memcpy(info->ctr, ctr, 16);
return 0;
}
u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
{
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
if(GetNandCtr(info.ctr, offset) != 0)
return 1;
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
u32 start_sector = offset / NAND_SECTOR_SIZE;
ReadNandSectors(start_sector, n_sectors, buffer);
CryptBuffer(&info);
return 0;
}
u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
{
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
if(GetNandCtr(info.ctr, offset) != 0)
return 1;
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
u32 start_sector = offset / NAND_SECTOR_SIZE;
CryptBuffer(&info);
WriteNandSectors(start_sector, n_sectors, buffer);
return 0;
}

View File

@ -2,6 +2,9 @@
#include "common.h" #include "common.h"
#define BUFFER_ADDRESS ((u8*) 0x21000000) // buffer must not be used for anything else!
#define BUFFER_MAX_SIZE (1 * 1024 * 1024) // must be a multiple of 0x40 (64)
#define NAND_SECTOR_SIZE 0x200 #define NAND_SECTOR_SIZE 0x200
#define SECTORS_PER_READ (BUFFER_MAX_SIZE / NAND_SECTOR_SIZE) #define SECTORS_PER_READ (BUFFER_MAX_SIZE / NAND_SECTOR_SIZE)
@ -12,12 +15,6 @@
#define P_FIRM1 (1<<4) #define P_FIRM1 (1<<4)
#define P_CTRNAND (1<<5) #define P_CTRNAND (1<<5)
// these three are not handled by the feature functions
// they have to be handled by the menu system
#define N_EMUNAND (1<<29)
#define N_FORCENAND (1<<30)
#define N_NANDWRITE (1<<31)
// return values for the CheckEmuNAND() function // return values for the CheckEmuNAND() function
#define EMUNAND_NOT_READY 0 // must be zero #define EMUNAND_NOT_READY 0 // must be zero
#define EMUNAND_READY 1 #define EMUNAND_READY 1
@ -28,6 +25,16 @@
#define EMUNAND_MULTI_OFFSET_O3DS 0x00200000 #define EMUNAND_MULTI_OFFSET_O3DS 0x00200000
#define EMUNAND_MULTI_OFFSET_N3DS 0x00400000 #define EMUNAND_MULTI_OFFSET_N3DS 0x00400000
typedef struct {
u32 keyslot;
u32 setKeyY;
u8 ctr[16];
u8 keyY[16];
u32 size;
u32 mode;
u8* buffer;
} __attribute__((packed)) CryptBufferInfo;
typedef struct { typedef struct {
char name[16]; char name[16];
u8 magic[8]; u8 magic[8];
@ -38,25 +45,16 @@ typedef struct {
} __attribute__((packed)) PartitionInfo; } __attribute__((packed)) PartitionInfo;
PartitionInfo* GetPartitionInfo(u32 partition_id); PartitionInfo* GetPartitionInfo(u32 partition_id);
u32 GetNandCtr(u8* ctr, u32 offset);
u32 OutputFileNameSelector(char* filename, const char* basename, char* extension); u32 CheckEmuNand(void);
u32 InputFileNameSelector(char* filename, const char* basename, char* extension, u8* magic, u32 msize, u32 fsize); void SetNand(bool set_emunand, u32 base_sector);
u32 GetNandCtr(u8* ctr, u32 offset);
u32 CryptBuffer(CryptBufferInfo *info);
int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out);
int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in);
u32 GetNandSize(void);
u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition); u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition);
u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition);
u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition); u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition);
u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition);
// --> FEATURE FUNCTIONS <--
u32 CheckEmuNand(void);
u32 SetNand(bool set_emunand, bool force_emunand);
u32 CtrNandPadgen(u32 param);
u32 TwlNandPadgen(u32 param);
u32 Firm0Firm1Padgen(u32 param);
u32 DumpNand(u32 param);
u32 RestoreNand(u32 param);
u32 DecryptNandPartition(u32 param);
u32 InjectNandPartition(u32 param);

View File

@ -1,68 +0,0 @@
#include "fs.h"
#include "draw.h"
#include "decryptor/decryptor.h"
#include "decryptor/aes.h"
u32 CryptBuffer(CryptBufferInfo *info)
{
u8 ctr[16] __attribute__((aligned(32)));
memcpy(ctr, info->ctr, 16);
u8* buffer = info->buffer;
u32 size = info->size;
u32 mode = info->mode;
if (info->setKeyY) {
u8 keyY[16] __attribute__((aligned(32)));
memcpy(keyY, info->keyY, 16);
setup_aeskeyY(info->keyslot, keyY);
info->setKeyY = 0;
}
use_aeskey(info->keyslot);
for (u32 i = 0; i < size; i += 0x10, buffer += 0x10) {
set_ctr(ctr);
if ((mode & (0x7 << 27)) == AES_CBC_DECRYPT_MODE)
memcpy(ctr, buffer, 0x10);
aes_decrypt((void*) buffer, (void*) buffer, 1, mode);
if ((mode & (0x7 << 27)) == AES_CBC_ENCRYPT_MODE)
memcpy(ctr, buffer, 0x10);
else if ((mode & (0x7 << 27)) == AES_CTR_MODE)
add_ctr(ctr, 0x1);
}
memcpy(info->ctr, ctr, 16);
return 0;
}
u32 CreatePad(PadInfo *info)
{
u8* buffer = BUFFER_ADDRESS;
u32 result = 0;
if (!FileCreate(info->filename, true)) // No DebugFileCreate() here - messages are already given
return 1;
CryptBufferInfo decryptInfo = {.keyslot = info->keyslot, .setKeyY = info->setKeyY, .mode = info->mode, .buffer = buffer};
memcpy(decryptInfo.ctr, info->ctr, 16);
memcpy(decryptInfo.keyY, info->keyY, 16);
u32 size_bytes = info->size_mb * 1024*1024;
for (u32 i = 0; i < size_bytes; i += BUFFER_MAX_SIZE) {
u32 curr_block_size = min(BUFFER_MAX_SIZE, size_bytes - i);
decryptInfo.size = curr_block_size;
memset(buffer, 0x00, curr_block_size);
ShowProgress(i, size_bytes);
CryptBuffer(&decryptInfo);
if (!DebugFileWrite((void*)buffer, curr_block_size, i)) {
result = 1;
break;
}
}
ShowProgress(0, 0);
FileClose();
return result;
}

View File

@ -1,30 +0,0 @@
#pragma once
#include "common.h"
#define BUFFER_ADDRESS ((u8*) 0x21000000)
#define BUFFER_MAX_SIZE (1 * 1024 * 1024) // must be a multiple of 0x40 (64)
typedef struct {
u32 keyslot;
u32 setKeyY;
u8 ctr[16];
u8 keyY[16];
u32 size;
u32 mode;
u8* buffer;
} __attribute__((packed)) CryptBufferInfo;
typedef struct {
u32 keyslot;
u32 setKeyY;
u8 ctr[16];
u8 keyY[16];
u32 size_mb;
u32 mode;
char filename[180];
} __attribute__((packed, aligned(16))) PadInfo;
u32 CryptBuffer(CryptBufferInfo *info);
u32 CreatePad(PadInfo *info);

File diff suppressed because it is too large Load Diff

View File

@ -1,107 +0,0 @@
#pragma once
#include "common.h"
#include "decryptor/decryptor.h"
#define GC_NCCH_PROCESS (1<<0)
#define GC_CIA_PROCESS (1<<1)
#define GC_CIA_DEEP (1<<2)
#define GC_NCCH_ENCRYPT (1<<3)
#define GC_CIA_ENCRYPT (1<<4)
#define GC_CXI_ONLY (1<<5)
#define MAX_ENTRIES 1024
typedef struct {
u64 titleId;
u8 external_seed[16];
u8 reserved[8];
} __attribute__((packed)) SeedInfoEntry;
typedef struct {
u32 n_entries;
u8 padding[12];
SeedInfoEntry entries[MAX_ENTRIES];
} __attribute__((packed)) SeedInfo;
typedef struct {
u8 ctr[16];
u32 size_mb;
char filename[180];
} __attribute__((packed)) SdInfoEntry;
typedef struct {
u32 n_entries;
SdInfoEntry entries[MAX_ENTRIES];
} __attribute__((packed, aligned(16))) SdInfo;
typedef struct {
u8 ctr[16];
u8 keyY[16];
u32 size_mb;
u8 reserved[4];
u32 usesSeedCrypto;
u32 uses7xCrypto;
u64 titleId;
char filename[112];
} __attribute__((packed)) NcchInfoEntry;
typedef struct {
u32 padding;
u32 ncch_info_version;
u32 n_entries;
u8 reserved[4];
NcchInfoEntry entries[MAX_ENTRIES];
} __attribute__((packed, aligned(16))) NcchInfo;
typedef struct {
u8 signature[0x100];
u8 magic[0x4];
u32 size;
u64 partitionId;
u16 makercode;
u16 version;
u8 reserved0[0x4];
u64 programId;
u8 reserved1[0x10];
u8 hash_logo[0x20];
char productCode[0x10];
u8 hash_exthdr[0x20];
u32 size_exthdr;
u8 reserved2[0x4];
u8 flags[0x8];
u32 offset_plain;
u32 size_plain;
u32 offset_logo;
u32 size_logo;
u32 offset_exefs;
u32 size_exefs;
u32 size_exefs_hash;
u8 reserved3[0x4];
u32 offset_romfs;
u32 size_romfs;
u32 size_romfs_hash;
u8 reserved4[0x4];
u8 hash_exefs[0x20];
u8 hash_romfs[0x20];
} __attribute__((packed, aligned(16))) NcchHeader;
u32 GetSdCtr(u8* ctr, const char* path);
u32 GetSd0x34KeyY(u8* movable_keyY, bool from_nand);
u32 SdFolderSelector(char* path, u8* keyY);
u32 SdInfoGen(SdInfo* info, const char* base_path);
u32 CryptSdToSd(const char* filename, u32 offset, u32 size, CryptBufferInfo* info);
u32 GetHashFromFile(const char* filename, u32 offset, u32 size, u8* hash);
u32 CheckHashFromFile(const char* filename, u32 offset, u32 size, u8* hash);
u32 CryptNcch(const char* filename, u32 offset, u32 size, u64 seedId, u8* encrypt_flags);
u32 CryptCia(const char* filename, u8* ncch_crypt, bool cia_encrypt, bool cxi_only);
// --> FEATURE FUNCTIONS <--
u32 NcchPadgen(u32 param);
u32 SdPadgen(u32 param);
u32 SdPadgenDirect(u32 param);
u32 UpdateSeedDb(u32 param);
u32 CryptGameFiles(u32 param);
u32 CryptSdFiles(u32 param);
u32 DecryptSdFilesDirect(u32 param);

View File

@ -1,682 +0,0 @@
#include "fs.h"
#include "draw.h"
#include "hid.h"
#include "platform.h"
#include "decryptor/aes.h"
#include "decryptor/decryptor.h"
#include "decryptor/nand.h"
#include "fatfs/sdmmc.h"
// see: http://3dbrew.org/wiki/Flash_Filesystem
static PartitionInfo partitions[] = {
{ "TWLN", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x00012E00, 0x08FB5200, 0x3, AES_CNT_TWLNAND_MODE },
{ "TWLP", {0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20}, 0x09011A00, 0x020B6600, 0x3, AES_CNT_TWLNAND_MODE },
{ "AGBSAVE", {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 0x0B100000, 0x00030000, 0x7, AES_CNT_CTRNAND_MODE },
{ "FIRM0", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B130000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
{ "FIRM1", {0x46, 0x49, 0x52, 0x4D, 0x00, 0x00, 0x00, 0x00}, 0x0B530000, 0x00400000, 0x6, AES_CNT_CTRNAND_MODE },
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95CA00, 0x2F3E3600, 0x4, AES_CNT_CTRNAND_MODE }, // O3DS
{ "CTRNAND", {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20}, 0x0B95AE00, 0x41D2D200, 0x5, AES_CNT_CTRNAND_MODE } // N3DS
};
static u32 emunand_header = 0;
static u32 emunand_offset = 0;
u32 CheckEmuNand(void)
{
u8* buffer = BUFFER_ADDRESS;
u32 nand_size_sectors = getMMCDevice(0)->total_size;
u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS;
u32 ret = EMUNAND_NOT_READY;
// check the MBR for presence of a hidden partition
sdmmc_sdcard_readsectors(0, 1, buffer);
u32 hidden_sectors = getle32(buffer + 0x1BE + 0x8);
for (u32 offset_sector = 0; offset_sector + nand_size_sectors < hidden_sectors; offset_sector += multi_sectors) {
// check for Gateway type EmuNAND
sdmmc_sdcard_readsectors(offset_sector + nand_size_sectors, 1, buffer);
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
ret |= EMUNAND_GATEWAY << (2 * (offset_sector / multi_sectors));
continue;
}
// check for RedNAND type EmuNAND
sdmmc_sdcard_readsectors(offset_sector + 1, 1, buffer);
if (memcmp(buffer + 0x100, "NCSD", 4) == 0) {
ret |= EMUNAND_REDNAND << (2 * (offset_sector / multi_sectors));
continue;
}
// EmuNAND ready but not set up
ret |= EMUNAND_READY << (2 * (offset_sector / multi_sectors));
}
return ret;
}
u32 SetNand(bool set_emunand, bool force_emunand)
{
if (set_emunand) {
u32 emunand_state = CheckEmuNand();
u32 emunand_count = 0;
u32 offset_sector = 0;
for (emunand_count = 0; (emunand_state >> (2 * emunand_count)) & 0x3; emunand_count++);
if (emunand_count > 1) { // multiple EmuNANDs -> use selector
u32 multi_sectors = (GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS;
u32 emunand_no = 0;
Debug("Use arrow keys and <A> to choose EmuNAND");
while (true) {
u32 emunandn_state = (emunand_state >> (2 * emunand_no)) & 0x3;
offset_sector = emunand_no * multi_sectors;
Debug("\rEmuNAND #%u: %s", emunand_no, (emunandn_state == EMUNAND_READY) ? "EmuNAND ready" : (emunandn_state == EMUNAND_GATEWAY) ? "GW EmuNAND" : "RedNAND");
// user input routine
u32 pad_state = InputWait();
if (pad_state & BUTTON_DOWN) {
emunand_no = (emunand_no + 1) % emunand_count;
} else if (pad_state & BUTTON_UP) {
emunand_no = (emunand_no) ? emunand_no - 1 : emunand_count - 1;
} else if (pad_state & BUTTON_A) {
Debug("EmuNAND #%u", emunand_no);
emunand_state = emunandn_state;
break;
} else if (pad_state & BUTTON_B) {
Debug("(cancelled by user)");
return 2;
}
}
}
if ((emunand_state == EMUNAND_READY) && force_emunand)
emunand_state = EMUNAND_GATEWAY;
switch (emunand_state) {
case EMUNAND_NOT_READY:
Debug("SD is not formatted for EmuNAND");
return 1;
case EMUNAND_GATEWAY:
emunand_header = offset_sector + getMMCDevice(0)->total_size;
emunand_offset = offset_sector;
Debug("Using EmuNAND @ %06X/%06X", emunand_header, emunand_offset);
return 0;
case EMUNAND_REDNAND:
emunand_header = offset_sector + 1;
emunand_offset = offset_sector + 1;
Debug("Using RedNAND @ %06X/%06X", emunand_header, emunand_offset);
return 0;
default:
Debug("EmuNAND is not available");
return 1;
}
} else {
emunand_header = 0;
emunand_offset = 0;
return 0;
}
}
static inline int ReadNandSectors(u32 sector_no, u32 numsectors, u8 *out)
{
if (emunand_header) {
if (sector_no == 0) {
int errorcode = sdmmc_sdcard_readsectors(emunand_header, 1, out);
if (errorcode) return errorcode;
sector_no = 1;
numsectors--;
out += 0x200;
}
return sdmmc_sdcard_readsectors(sector_no + emunand_offset, numsectors, out);
} else return sdmmc_nand_readsectors(sector_no, numsectors, out);
}
static inline int WriteNandSectors(u32 sector_no, u32 numsectors, u8 *in)
{
if (emunand_header) {
if (sector_no == 0) {
int errorcode = sdmmc_sdcard_writesectors(emunand_header, 1, in);
if (errorcode) return errorcode;
sector_no = 1;
numsectors--;
in += 0x200;
}
return sdmmc_sdcard_writesectors(sector_no + emunand_offset, numsectors, in);
} else return sdmmc_nand_writesectors(sector_no, numsectors, in);
}
u32 OutputFileNameSelector(char* filename, const char* basename, char* extension) {
char bases[3][64] = { 0 };
char* dotpos = NULL;
// build first base name and extension
strncpy(bases[0], basename, 63);
dotpos = strrchr(bases[0], '.');
if (dotpos) {
*dotpos = '\0';
if (!extension)
extension = dotpos + 1;
}
// build other two base names
snprintf(bases[1], 63, "%s_%s", bases[0], (emunand_header) ? "emu" : "sys");
snprintf(bases[2], 63, "%s%s" , (emunand_header) ? "emu" : "sys", bases[0]);
u32 fn_id = (emunand_header) ? 1 : 0;
u32 fn_num = (emunand_header) ? (emunand_offset / ((GetUnitPlatform() == PLATFORM_3DS) ? EMUNAND_MULTI_OFFSET_O3DS : EMUNAND_MULTI_OFFSET_N3DS)) : 0;
bool exists = false;
char extstr[16] = { 0 };
if (extension)
snprintf(extstr, 15, ".%s", extension);
Debug("Use arrow keys and <A> to choose a name");
while (true) {
char numstr[2] = { 0 };
// build and output file name (plus "(!)" if existing)
numstr[0] = (fn_num > 0) ? '0' + fn_num : '\0';
snprintf(filename, 63, "%s%s%s", bases[fn_id], numstr, extstr);
if ((exists = FileOpen(filename)))
FileClose();
Debug("\r%s%s", filename, (exists) ? " (!)" : "");
// user input routine
u32 pad_state = InputWait();
if (pad_state & BUTTON_DOWN) { // increment filename id
fn_id = (fn_id + 1) % 3;
} else if (pad_state & BUTTON_UP) { // decrement filename id
fn_id = (fn_id > 0) ? fn_id - 1 : 2;
} else if ((pad_state & BUTTON_RIGHT) && (fn_num < 9)) { // increment number
fn_num++;
} else if ((pad_state & BUTTON_LEFT) && (fn_num > 0)) { // decrement number
fn_num--;
} else if (pad_state & BUTTON_A) {
Debug("%s%s", filename, (exists) ? " (!)" : "");
break;
} else if (pad_state & BUTTON_B) {
Debug("(cancelled by user)");
return 2;
}
}
// overwrite confirmation
if (exists) {
Debug("Press <A> to overwrite existing file");
while (true) {
u32 pad_state = InputWait();
if (pad_state & BUTTON_A) {
break;
} else if (pad_state & BUTTON_B) {
Debug("(cancelled by user)");
return 2;
}
}
}
return 0;
}
u32 InputFileNameSelector(char* filename, const char* basename, char* extension, u8* magic, u32 msize, u32 fsize) {
char** fnptr = (char**) 0x20400000; // allow using 0x8000 byte
char* fnlist = (char*) 0x20408000; // allow using 0x80000 byte
u32 n_names = 0;
// get the file list - try work directory first
if (!GetFileList(WORK_DIR, fnlist, 0x80000, false, true, false) && !GetFileList("/", fnlist, 0x800000, false, true, false)) {
Debug("Failed retrieving the file names list");
return 1;
}
// get base name, extension
char base[64] = { 0 };
if (basename != NULL) {
// build base name and extension
strncpy(base, basename, 63);
char* dotpos = strrchr(base, '.');
if (dotpos) {
*dotpos = '\0';
if (!extension)
extension = dotpos + 1;
}
}
// limit magic number size
if (msize > 0x200)
msize = 0x200;
// parse the file names list for usable entries
for (char* fn = strtok(fnlist, "\n"); fn != NULL; fn = strtok(NULL, "\n")) {
u8 data[0x200];
char* dotpos = strrchr(fn, '.');
if (strrchr(fn, '/'))
fn = strrchr(fn, '/') + 1;
if (strnlen(fn, 128) > 63)
continue; // file name too long
if ((basename != NULL) && !strstr(fn, base))
continue; // basename check failed
if ((extension != NULL) && (dotpos != NULL) && (strncmp(dotpos + 1, extension, strnlen(extension, 16))))
continue; // extension check failed
else if ((extension == NULL) != (dotpos == NULL))
continue; // extension check failed
if (!FileOpen(fn))
continue; // file can't be opened
if (fsize && (FileGetSize() != fsize)) {
FileClose();
continue; // file size check failed
}
if (msize) {
if (FileRead(data, msize, 0) != msize) {
FileClose();
continue; // can't be read
}
if (memcmp(data, magic, msize) != 0) {
FileClose();
continue; // magic number does not match
}
}
FileClose();
// this is a match - keep it
fnptr[n_names++] = fn;
if (n_names * sizeof(char**) >= 0x8000)
return 1;
}
if (n_names == 0) {
Debug("No usable file found");
return 1;
}
u32 index = 0;
Debug("Use arrow keys and <A> to choose a file");
while (true) {
snprintf(filename, 63, "%s", fnptr[index]);
Debug("\r%s", filename);
u32 pad_state = InputWait();
if (pad_state & BUTTON_DOWN) { // next filename
index = (index + 1) % n_names;
} else if (pad_state & BUTTON_UP) { // previous filename
index = (index > 0) ? index - 1 : n_names - 1;
} else if (pad_state & BUTTON_A) {
Debug("%s", filename);
break;
} else if (pad_state & BUTTON_B) {
Debug("(cancelled by user)");
return 2;
}
}
return 0;
}
PartitionInfo* GetPartitionInfo(u32 partition_id)
{
u32 partition_num = 0;
if (partition_id == P_CTRNAND) {
partition_num = (GetUnitPlatform() == PLATFORM_3DS) ? 5 : 6;
} else {
for(; !(partition_id & (1<<partition_num)) && (partition_num < 32); partition_num++);
}
return (partition_num >= 32) ? NULL : &(partitions[partition_num]);
}
u32 CtrNandPadgen(u32 param)
{
u32 keyslot;
u32 nand_size;
// legacy sizes & offset, to work with 3DSFAT16Tool
if (GetUnitPlatform() == PLATFORM_3DS) {
keyslot = 0x4;
nand_size = 758;
} else {
keyslot = 0x5;
nand_size = 1055;
}
Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size);
Debug("Filename: nand.fat16.xorpad");
PadInfo padInfo = {
.keyslot = keyslot,
.setKeyY = 0,
.size_mb = nand_size,
.filename = "nand.fat16.xorpad",
.mode = AES_CNT_CTRNAND_MODE
};
if(GetNandCtr(padInfo.ctr, 0xB930000) != 0)
return 1;
return CreatePad(&padInfo);
}
u32 TwlNandPadgen(u32 param)
{
u32 size_mb = (partitions[0].size + (1024 * 1024) - 1) / (1024 * 1024);
Debug("Creating TWLN FAT16 xorpad. Size (MB): %u", size_mb);
Debug("Filename: twlnand.fat16.xorpad");
PadInfo padInfo = {
.keyslot = partitions[0].keyslot,
.setKeyY = 0,
.size_mb = size_mb,
.filename = "twlnand.fat16.xorpad",
.mode = AES_CNT_TWLNAND_MODE
};
if(GetNandCtr(padInfo.ctr, partitions[0].offset) != 0)
return 1;
return CreatePad(&padInfo);
}
u32 Firm0Firm1Padgen(u32 param)
{
u32 size_mb = (partitions[3].size + partitions[4].size + (1024 * 1024) - 1) / (1024 * 1024);
Debug("Creating FIRM0FIRM1 xorpad. Size (MB): %u", size_mb);
Debug("Filename: firm0firm1.xorpad");
PadInfo padInfo = {
.keyslot = partitions[3].keyslot,
.setKeyY = 0,
.size_mb = size_mb,
.filename = "firm0firm1.xorpad",
.mode = AES_CNT_CTRNAND_MODE
};
if(GetNandCtr(padInfo.ctr, partitions[3].offset) != 0)
return 1;
return CreatePad(&padInfo);
}
u32 GetNandCtr(u8* ctr, u32 offset)
{
static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"};
static const u8* version_ctrs[] = {
(u8*)0x080D7CAC,
(u8*)0x080D858C,
(u8*)0x080D748C,
(u8*)0x080D740C,
(u8*)0x080D74CC,
(u8*)0x080D794C
};
static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32);
static u8* ctr_start = NULL;
if (ctr_start == NULL) {
for (u32 i = 0; i < version_ctrs_len; i++) {
if (*(u32*)version_ctrs[i] == 0x5C980) {
Debug("System version %s", versions[i]);
ctr_start = (u8*) version_ctrs[i] + 0x30;
}
}
// If value not in previous list start memory scanning (test range)
if (ctr_start == NULL) {
for (u8* c = (u8*) 0x080D8FFF; c > (u8*) 0x08000000; c--) {
if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) {
ctr_start = c + 0x30;
Debug("CTR start 0x%08X", ctr_start);
break;
}
}
}
if (ctr_start == NULL) {
Debug("CTR start not found!");
return 1;
}
}
// the ctr is stored backwards in memory
if (offset >= 0x0B100000) { // CTRNAND/AGBSAVE region
for (u32 i = 0; i < 16; i++)
ctr[i] = *(ctr_start + (0xF - i));
} else { // TWL region
for (u32 i = 0; i < 16; i++)
ctr[i] = *(ctr_start + 0x88 + (0xF - i));
}
// increment counter
add_ctr(ctr, offset / 0x10);
return 0;
}
u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
{
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
if(GetNandCtr(info.ctr, offset) != 0)
return 1;
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
u32 start_sector = offset / NAND_SECTOR_SIZE;
ReadNandSectors(start_sector, n_sectors, buffer);
CryptBuffer(&info);
return 0;
}
u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition)
{
u8* buffer = BUFFER_ADDRESS;
u32 result = 0;
if (!DebugFileCreate(filename, true))
return 1;
for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) {
u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i));
ShowProgress(i, size);
DecryptNandToMem(buffer, offset + i, read_bytes, partition);
if(!DebugFileWrite(buffer, read_bytes, i)) {
result = 1;
break;
}
}
ShowProgress(0, 0);
FileClose();
return result;
}
u32 DumpNand(u32 param)
{
char filename[64];
u8* buffer = BUFFER_ADDRESS;
u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE;
u32 result = 0;
Debug("Dumping %sNAND. Size (MB): %u", (param & N_EMUNAND) ? "Emu" : "Sys", nand_size / (1024 * 1024));
if (OutputFileNameSelector(filename, "NAND.bin", NULL) != 0)
return 1;
if (!DebugFileCreate(filename, true))
return 1;
u32 n_sectors = nand_size / NAND_SECTOR_SIZE;
for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) {
u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i));
ShowProgress(i, n_sectors);
ReadNandSectors(i, read_sectors, buffer);
if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) {
result = 1;
break;
}
}
ShowProgress(0, 0);
FileClose();
return result;
}
u32 DecryptNandPartition(u32 param)
{
PartitionInfo* p_info = NULL;
char filename[64];
u8 magic[NAND_SECTOR_SIZE];
for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) {
if (param & partition_id) {
p_info = GetPartitionInfo(partition_id);
break;
}
}
if (p_info == NULL) {
Debug("No partition to dump");
return 1;
}
Debug("Dumping & Decrypting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024));
if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0)
return 1;
if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) {
Debug("Decryption error, please contact us");
return 1;
}
if (OutputFileNameSelector(filename, p_info->name, "bin") != 0)
return 1;
return DecryptNandToFile(filename, p_info->offset, p_info->size, p_info);
}
u32 EncryptMemToNand(u8* buffer, u32 offset, u32 size, PartitionInfo* partition)
{
CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode};
if(GetNandCtr(info.ctr, offset) != 0)
return 1;
u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE;
u32 start_sector = offset / NAND_SECTOR_SIZE;
CryptBuffer(&info);
WriteNandSectors(start_sector, n_sectors, buffer);
return 0;
}
u32 EncryptFileToNand(const char* filename, u32 offset, u32 size, PartitionInfo* partition)
{
u8* buffer = BUFFER_ADDRESS;
u32 result = 0;
if (!DebugFileOpen(filename))
return 1;
if (FileGetSize() != size) {
Debug("%s has wrong size", filename);
FileClose();
return 1;
}
for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) {
u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i));
ShowProgress(i, size);
if(!DebugFileRead(buffer, read_bytes, i)) {
result = 1;
break;
}
EncryptMemToNand(buffer, offset + i, read_bytes, partition);
}
ShowProgress(0, 0);
FileClose();
return result;
}
u32 RestoreNand(u32 param)
{
char filename[64];
u8* buffer = BUFFER_ADDRESS;
u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE;
u32 result = 0;
u8 magic[4];
if (!(param & N_NANDWRITE)) // developer screwup protection
return 1;
// User file select
if (InputFileNameSelector(filename, "NAND.bin", NULL, NULL, 0, nand_size) != 0)
return 1;
if (!DebugFileOpen(filename))
return 1;
if (nand_size != FileGetSize()) {
FileClose();
Debug("NAND backup has the wrong size!");
return 1;
};
if(!DebugFileRead(magic, 4, 0x100)) {
FileClose();
return 1;
}
if (memcmp(magic, "NCSD", 4) != 0) {
FileClose();
Debug("Not a proper NAND backup!");
return 1;
}
Debug("Restoring %sNAND. Size (MB): %u", (param & N_EMUNAND) ? "Emu" : "Sys", nand_size / (1024 * 1024));
u32 n_sectors = nand_size / NAND_SECTOR_SIZE;
for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) {
u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i));
ShowProgress(i, n_sectors);
if(!DebugFileRead(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) {
result = 1;
break;
}
WriteNandSectors(i, read_sectors, buffer);
}
ShowProgress(0, 0);
FileClose();
return result;
}
u32 InjectNandPartition(u32 param)
{
PartitionInfo* p_info = NULL;
char filename[64];
u8 magic[NAND_SECTOR_SIZE];
if (!(param & N_NANDWRITE)) // developer screwup protection
return 1;
for (u32 partition_id = P_TWLN; partition_id <= P_CTRNAND; partition_id = partition_id << 1) {
if (param & partition_id) {
p_info = GetPartitionInfo(partition_id);
break;
}
}
if (p_info == NULL) {
Debug("No partition to inject to");
return 1;
}
Debug("Encrypting & Injecting %s, size (MB): %u", p_info->name, p_info->size / (1024 * 1024));
// User file select
if (InputFileNameSelector(filename, p_info->name, "bin",
p_info->magic, (p_info->magic[0] != 0xFF) ? 8 : 0, p_info->size) != 0)
return 1;
// Encryption check
if (DecryptNandToMem(magic, p_info->offset, 16, p_info) != 0)
return 1;
if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) {
Debug("Decryption error, please contact us");
return 1;
}
// File check
if (FileOpen(filename)) {
if(!DebugFileRead(magic, 8, 0)) {
FileClose();
return 1;
}
if ((p_info->magic[0] != 0xFF) && (memcmp(p_info->magic, magic, 8) != 0)) {
Debug("Bad file content, won't inject");
FileClose();
return 1;
}
FileClose();
}
return EncryptFileToNand(filename, p_info->offset, p_info->size, p_info);
}

View File

@ -1,32 +0,0 @@
#include "sha.h"
void sha_init(u32 mode)
{
while(*REG_SHACNT & 1);
*REG_SHACNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
}
void sha_update(const void* src, u32 size)
{
const u32* src32 = (const u32*)src;
while(size >= 0x40) {
while(*REG_SHACNT & 1);
for(u32 i = 0; i < 4; i++) {
*REG_SHAINFIFO = *src32++;
*REG_SHAINFIFO = *src32++;
*REG_SHAINFIFO = *src32++;
*REG_SHAINFIFO = *src32++;
}
size -= 0x40;
}
while(*REG_SHACNT & 1);
memcpy((void*)REG_SHAINFIFO, src32, size);
}
void sha_get(void* res) {
*REG_SHACNT = (*REG_SHACNT & ~SHA_NORMAL_ROUND) | SHA_FINAL_ROUND;
while(*REG_SHACNT & SHA_FINAL_ROUND);
while(*REG_SHACNT & 1);
memcpy(res, (void*)REG_SHAHASH, (256 / 8));
}

View File

@ -1,27 +0,0 @@
#pragma once
#include "common.h"
#define REG_SHACNT ((volatile uint32_t*)0x1000A000)
#define REG_SHABLKCNT ((volatile uint32_t*)0x1000A004)
#define REG_SHAHASH ((volatile uint32_t*)0x1000A040)
#define REG_SHAINFIFO ((volatile uint32_t*)0x1000A080)
#define SHA_CNT_STATE 0x00000003
#define SHA_CNT_OUTPUT_ENDIAN 0x00000008
#define SHA_CNT_MODE 0x00000030
#define SHA_CNT_ENABLE 0x00010000
#define SHA_CNT_ACTIVE 0x00020000
#define SHA_HASH_READY 0x00000000
#define SHA_NORMAL_ROUND 0x00000001
#define SHA_FINAL_ROUND 0x00000002
#define SHA256_MODE 0
#define SHA224_MODE 0x00000010
#define SHA1_MODE 0x00000020
void sha_init(u32 mode);
void sha_update(const void* src, u32 size);
void sha_get(void* res);

View File

@ -8,8 +8,52 @@
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
#include "diskio.h" /* FatFs lower layer API */ #include "diskio.h" /* FatFs lower layer API */
#include "platform.h"
#include "sdmmc.h" #include "sdmmc.h"
#include "decryptor/nand.h"
#define TYPE_SDCARD 0
#define TYPE_SYSNAND 1
#define TYPE_EMUNAND 2
typedef struct {
DWORD offset;
DWORD subtype;
BYTE type;
} FATpartition;
FATpartition DriveInfo[28] = {
{ 0x000000, TYPE_SCARD, 0 }, // 0 - SDCARD
{ 0x000000, TYPE_SYSNAND, P_CTRNAND }, // 1 - SYSNAND CTRNAND
{ 0x000000, TYPE_SYSNAND, P_TWLN }, // 2 - SYSNAND TWLN
{ 0x000000, TYPE_SYSNAND, P_TWLP }, // 3 - SYSNAND TWLP
{ 0x000000, TYPE_EMUNAND, P_CTRNAND }, // 4 - EMUNAND0 O3DS CTRNAND
{ 0x000000, TYPE_EMUNAND, P_TWLN }, // 5 - EMUNAND0 O3DS TWLN
{ 0x000000, TYPE_EMUNAND, P_TWLP }, // 6 - EMUNAND0 O3DS TWLP
{ 0x200000, TYPE_EMUNAND, P_CTRNAND }, // 7 - EMUNAND1 O3DS CTRNAND
{ 0x200000, TYPE_EMUNAND, P_TWLN }, // 8 - EMUNAND1 O3DS TWLN
{ 0x200000, TYPE_EMUNAND, P_TWLP }, // 9 - EMUNAND1 O3DS TWLP
{ 0x400000, TYPE_EMUNAND, P_CTRNAND }, // 10 - EMUNAND2 O3DS CTRNAND
{ 0x400000, TYPE_EMUNAND, P_TWLN }, // 11 - EMUNAND2 O3DS TWLN
{ 0x400000, TYPE_EMUNAND, P_TWLP }, // 12 - EMUNAND2 O3DS TWLP
{ 0x600000, TYPE_EMUNAND, P_CTRNAND }, // 13 - EMUNAND3 O3DS CTRNAND
{ 0x600000, TYPE_EMUNAND, P_TWLN }, // 14 - EMUNAND3 O3DS TWLN
{ 0x600000, TYPE_EMUNAND, P_TWLP }, // 15 - EMUNAND3 O3DS TWLP
{ 0x000000, TYPE_EMUNAND, P_CTRNAND }, // 16 - EMUNAND0 N3DS CTRNAND
{ 0x000000, TYPE_EMUNAND, P_TWLN }, // 17 - EMUNAND0 N3DS TWLN
{ 0x000000, TYPE_EMUNAND, P_TWLP }, // 18 - EMUNAND0 N3DS TWLP
{ 0x400000, TYPE_EMUNAND, P_CTRNAND }, // 19 - EMUNAND1 N3DS CTRNAND
{ 0x400000, TYPE_EMUNAND, P_TWLN }, // 20 - EMUNAND1 N3DS TWLN
{ 0x400000, TYPE_EMUNAND, P_TWLP }, // 21 - EMUNAND1 N3DS TWLP
{ 0x800000, TYPE_EMUNAND, P_CTRNAND }, // 22 - EMUNAND2 N3DS CTRNAND
{ 0x800000, TYPE_EMUNAND, P_TWLN }, // 23 - EMUNAND2 N3DS TWLN
{ 0x800000, TYPE_EMUNAND, P_TWLP }, // 24 - EMUNAND2 N3DS TWLP
{ 0xC00000, TYPE_EMUNAND, P_CTRNAND }, // 25 - EMUNAND3 N3DS CTRNAND
{ 0xC00000, TYPE_EMUNAND, P_TWLN }, // 26 - EMUNAND3 N3DS TWLN
{ 0xC00000, TYPE_EMUNAND, P_TWLP } // 27 - EMUNAND3 N3DS TWLP
};
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* Get Drive Status */ /* Get Drive Status */
@ -52,9 +96,18 @@ DRESULT disk_read (
UINT count /* Number of sectors to read */ UINT count /* Number of sectors to read */
) )
{ {
if (sdmmc_sdcard_readsectors(sector, count, buff)) { if (DriveInfo[pdrv].type == TYPE_SCARD) {
return RES_PARERR; if (sdmmc_sdcard_readsectors(sector, count, buff)) {
} return RES_PARERR;
}
} else {
PartitionInfo* partition = GetPartitionInfo(DriveInfo[pdrv].subtype);
if (partition == NULL) return RES_PARERR;
u32 offset = (sector * 0x200) + partition->offset;
SetNand(DriveInfo[pdrv].type == TYPE_EMUNAND, DriveInfo[pdrv].offset);
if (DecryptNandToMem(buff, offset, count * 0x200, partition) != 0)
return RES_PARERR;
}
return RES_OK; return RES_OK;
} }
@ -74,9 +127,19 @@ DRESULT disk_write (
UINT count /* Number of sectors to write */ UINT count /* Number of sectors to write */
) )
{ {
if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) { if (DriveInfo[pdrv].type == TYPE_SCARD) {
return RES_PARERR; if (sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) {
} return RES_PARERR;
}
} else {
PartitionInfo* partition = GetPartitionInfo(DriveInfo[pdrv].subtype);
if (partition == NULL) return RES_PARERR;
u32 offset = (sector * 0x200) + partition->offset;
SetNand(DriveInfo[pdrv].type == TYPE_EMUNAND, DriveInfo[pdrv].offset);
// if (EncryptMemToNand(buff, offset, count * 0x200, partition) != 0)
return RES_PARERR;
// NO, not yet!
}
return RES_OK; return RES_OK;
} }
@ -103,11 +166,11 @@ DRESULT disk_ioctl (
*((DWORD*) buff) = 0x200; *((DWORD*) buff) = 0x200;
return RES_OK; return RES_OK;
case GET_SECTOR_COUNT: case GET_SECTOR_COUNT:
*((DWORD*) buff) = getMMCDevice(1)->total_size; *((DWORD*) buff) = getMMCDevice((DriveInfo[pdrv].type == TYPE_SCARD) ? 1 : 0)->total_size;
return RES_OK; return RES_OK;
case GET_BLOCK_SIZE: case GET_BLOCK_SIZE:
*((DWORD*) buff) = 0x2000; *((DWORD*) buff) = 0x2000;
return RES_OK; return (DriveInfo[pdrv].type == TYPE_SCARD) ? RES_OK : RES_PARERR;
case CTRL_SYNC: case CTRL_SYNC:
// nothing to do here - the disk_write function handles that // nothing to do here - the disk_write function handles that
return RES_OK; return RES_OK;

View File

@ -2,6 +2,22 @@
#include "common.h" #include "common.h"
typedef enum {
T_NAND_BASE,
T_NONFAT_ROOT,
T_FAT_ROOT,
T_FAT_FILE,
T_FAT_DIR
} EntryType;
typedef struct {
char* name; // should point to the correct portion of the path
char path[256];
u32 size;
u32 offset;
EntryType entryType;
} DirEntry;
bool InitFS(); bool InitFS();
void DeinitFS(); void DeinitFS();

View File

@ -5,188 +5,6 @@
#include "i2c.h" #include "i2c.h"
#include "decryptor/game.h" #include "decryptor/game.h"
#include "decryptor/nand.h" #include "decryptor/nand.h"
#include "decryptor/nandfat.h"
#include "decryptor/titlekey.h"
#define SUBMENU_START 5
MenuInfo menu[] =
{
{
"XORpad Generator Options", 7,
{
{ "NCCH Padgen", &NcchPadgen, 0 },
{ "SD Padgen (SDinfo.bin)", &SdPadgen, 0 },
{ "SD Padgen (SysNAND dir)", &SdPadgenDirect, 0 },
{ "SD Padgen (EmuNAND dir)", &SdPadgenDirect, N_EMUNAND },
{ "CTRNAND Padgen", &CtrNandPadgen, 0 },
{ "TWLNAND Padgen", &TwlNandPadgen, 0 },
{ "FIRM0FIRM1 Padgen", &Firm0Firm1Padgen, 0 }
}
},
{
"Titlekey Decrypt Options", 3,
{
{ "Titlekey Decrypt (file)", &DecryptTitlekeysFile, 0 },
{ "Titlekey Decrypt (SysNAND)", &DecryptTitlekeysNand, 0 },
{ "Titlekey Decrypt (EmuNAND)", &DecryptTitlekeysNand, N_EMUNAND }
}
},
{
"SysNAND Options", 8,
{
{ "NAND Backup", &DumpNand, 0 },
{ "NAND Restore", &RestoreNand, N_NANDWRITE },
{ "Partition Dump...", NULL, SUBMENU_START + 0 },
{ "Partition Inject...", NULL, SUBMENU_START + 2 },
{ "File Dump...", NULL, SUBMENU_START + 4 },
{ "File Inject...", NULL, SUBMENU_START + 6 },
{ "Health&Safety Dump", &DumpHealthAndSafety, 0 },
{ "Health&Safety Inject", &InjectHealthAndSafety, N_NANDWRITE }
}
},
{
"EmuNAND Options", 9,
{
{ "EmuNAND Backup", &DumpNand, N_EMUNAND },
{ "EmuNAND Restore", &RestoreNand, N_NANDWRITE | N_EMUNAND | N_FORCENAND },
{ "Partition Dump...", NULL, SUBMENU_START + 1 },
{ "Partition Inject...", NULL, SUBMENU_START + 3 },
{ "File Dump...", NULL, SUBMENU_START + 5 },
{ "File Inject...", NULL, SUBMENU_START + 7 },
{ "Health&Safety Dump", &DumpHealthAndSafety, N_EMUNAND },
{ "Health&Safety Inject", &InjectHealthAndSafety, N_NANDWRITE | N_EMUNAND },
{ "Update SeedDB", &UpdateSeedDb, N_EMUNAND }
}
},
{
"Game Decryptor Options", 10,
{
{ "NCCH/NCSD Decryptor", &CryptGameFiles, GC_NCCH_PROCESS },
{ "NCCH/NCSD Encryptor", &CryptGameFiles, GC_NCCH_PROCESS | GC_NCCH_ENCRYPT },
{ "CIA Decryptor (shallow)", &CryptGameFiles, GC_CIA_PROCESS },
{ "CIA Decryptor (deep)", &CryptGameFiles, GC_CIA_PROCESS | GC_CIA_DEEP },
{ "CIA Decryptor (CXI only)", &CryptGameFiles, GC_CIA_PROCESS | GC_CIA_DEEP | GC_CXI_ONLY },
{ "CIA Encryptor (NCCH)", &CryptGameFiles, GC_CIA_PROCESS | GC_NCCH_ENCRYPT },
{ "CIA Encryptor (CXI only)", &CryptGameFiles, GC_CIA_PROCESS | GC_NCCH_ENCRYPT | GC_CXI_ONLY },
{ "SD Decryptor/Encryptor", &CryptSdFiles, 0 },
{ "SD Decryptor (SysNAND dir)", &DecryptSdFilesDirect, 0 },
{ "SD Decryptor (EmuNAND dir)", &DecryptSdFilesDirect, N_EMUNAND }
}
},
// everything below is not contained in the main menu
{
"Partition Dump... (SysNAND)", 6, // ID 0
{
{ "Dump TWLN Partition", &DecryptNandPartition, P_TWLN },
{ "Dump TWLP Partition", &DecryptNandPartition, P_TWLP },
{ "Dump AGBSAVE Partition", &DecryptNandPartition, P_AGBSAVE },
{ "Dump FIRM0 Partition", &DecryptNandPartition, P_FIRM0 },
{ "Dump FIRM1 Partition", &DecryptNandPartition, P_FIRM1 },
{ "Dump CTRNAND Partition", &DecryptNandPartition, P_CTRNAND }
}
},
{
"Partition Dump...(EmuNAND)", 6, // ID 1
{
{ "Dump TWLN Partition", &DecryptNandPartition, N_EMUNAND | P_TWLN },
{ "Dump TWLP Partition", &DecryptNandPartition, N_EMUNAND | P_TWLP },
{ "Dump AGBSAVE Partition", &DecryptNandPartition, N_EMUNAND | P_AGBSAVE },
{ "Dump FIRM0 Partition", &DecryptNandPartition, N_EMUNAND | P_FIRM0 },
{ "Dump FIRM1 Partition", &DecryptNandPartition, N_EMUNAND | P_FIRM1 },
{ "Dump CTRNAND Partition", &DecryptNandPartition, N_EMUNAND | P_CTRNAND }
}
},
{
"Partition Inject... (SysNAND)", 6, // ID 2
{
{ "Inject TWLN Partition", &InjectNandPartition, N_NANDWRITE | P_TWLN },
{ "Inject TWLP Partition", &InjectNandPartition, N_NANDWRITE | P_TWLP },
{ "Inject AGBSAVE Partition", &InjectNandPartition, N_NANDWRITE | P_AGBSAVE },
{ "Inject FIRM0 Partition", &InjectNandPartition, N_NANDWRITE | P_FIRM0 },
{ "Inject FIRM1 Partition", &InjectNandPartition, N_NANDWRITE | P_FIRM1 },
{ "Inject CTRNAND Partition", &InjectNandPartition, N_NANDWRITE | P_CTRNAND }
}
},
{
"Partition Inject... (EmuNAND)", 6, // ID 3
{
{ "Inject TWLN Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_TWLN },
{ "Inject TWLP Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_TWLP },
{ "Inject AGBSAVE Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_AGBSAVE },
{ "Inject FIRM0 Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_FIRM0 },
{ "Inject FIRM1 Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_FIRM1 },
{ "Inject CTRNAND Partition", &InjectNandPartition, N_NANDWRITE | N_EMUNAND | P_CTRNAND }
}
},
{
"File Dump... (SysNAND)", 10, // ID 4
{
{ "Dump ticket.db", &DumpFile, F_TICKET },
{ "Dump title.db", &DumpFile, F_TITLE },
{ "Dump import.db", &DumpFile, F_IMPORT },
{ "Dump certs.db", &DumpFile, F_CERTS },
{ "Dump SecureInfo_A", &DumpFile, F_SECUREINFO },
{ "Dump LocalFriendCodeSeed_B", &DumpFile, F_LOCALFRIEND },
{ "Dump rand_seed", &DumpFile, F_RANDSEED },
{ "Dump movable.sed", &DumpFile, F_MOVABLE },
{ "Dump nagsave.bin", &DumpFile, F_NAGSAVE },
{ "Dump nnidsave.bin", &DumpFile, F_NNIDSAVE }
}
},
{
"File Dump... (EmuNAND)", 11, // ID 5
{
{ "Dump ticket.db", &DumpFile, N_EMUNAND | F_TICKET },
{ "Dump title.db", &DumpFile, N_EMUNAND | F_TITLE },
{ "Dump import.db", &DumpFile, N_EMUNAND | F_IMPORT },
{ "Dump certs.db", &DumpFile, N_EMUNAND | F_CERTS },
{ "Dump SecureInfo_A", &DumpFile, N_EMUNAND | F_SECUREINFO },
{ "Dump LocalFriendCodeSeed_B", &DumpFile, N_EMUNAND | F_LOCALFRIEND },
{ "Dump rand_seed", &DumpFile, N_EMUNAND | F_RANDSEED },
{ "Dump movable.sed", &DumpFile, N_EMUNAND | F_MOVABLE },
{ "Dump seedsave.bin", &DumpFile, N_EMUNAND | F_SEEDSAVE },
{ "Dump nagsave.bin", &DumpFile, N_EMUNAND | F_NAGSAVE },
{ "Dump nnidsave.bin", &DumpFile, N_EMUNAND | F_NNIDSAVE }
}
},
{
"File Inject... (SysNAND)", 10, // ID 6
{
{ "Inject ticket.db", &InjectFile, N_NANDWRITE | F_TICKET },
{ "Inject title.db", &InjectFile, N_NANDWRITE | F_TITLE },
{ "Inject import.db", &InjectFile, N_NANDWRITE | F_IMPORT },
{ "Inject certs.db", &InjectFile, N_NANDWRITE | F_CERTS },
{ "Inject SecureInfo_A", &InjectFile, N_NANDWRITE | F_SECUREINFO },
{ "Inject LocalFriendCodeSeed_B", &InjectFile, N_NANDWRITE | F_LOCALFRIEND },
{ "Inject rand_seed", &InjectFile, N_NANDWRITE | F_RANDSEED },
{ "Inject movable.sed", &InjectFile, N_NANDWRITE | F_MOVABLE },
{ "Inject nagsave.bin", &InjectFile, N_NANDWRITE | F_NAGSAVE },
{ "Inject nnidsave.bin", &InjectFile, N_NANDWRITE | F_NNIDSAVE }
}
},
{
"File Inject... (EmuNAND)", 11, // ID 7
{
{ "Inject ticket.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_TICKET },
{ "Inject title.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_TITLE },
{ "Inject import.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_IMPORT },
{ "Inject certs.db", &InjectFile, N_NANDWRITE | N_EMUNAND | F_CERTS },
{ "Inject SecureInfo_A", &InjectFile, N_NANDWRITE | N_EMUNAND | F_SECUREINFO },
{ "Inject LocalFriendCodeSeed_B", &InjectFile, N_NANDWRITE | N_EMUNAND | F_LOCALFRIEND },
{ "Inject rand_seed", &InjectFile, N_NANDWRITE | N_EMUNAND | F_RANDSEED },
{ "Inject movable.sed", &InjectFile, N_NANDWRITE | N_EMUNAND | F_MOVABLE },
{ "Inject seedsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_SEEDSAVE },
{ "Inject nagsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_NAGSAVE },
{ "Inject nnidsave.bin", &InjectFile, N_NANDWRITE | N_EMUNAND | F_NNIDSAVE }
}
},
{
NULL, 0, {}, // empty menu to signal end
}
};
void Reboot() void Reboot()
{ {
@ -207,9 +25,9 @@ int main()
ClearScreenFull(true, true); ClearScreenFull(true, true);
InitFS(); InitFS();
u32 menu_exit = ProcessMenu(menu, SUBMENU_START); u32 godmode_exit = 0;
DeinitFS(); DeinitFS();
(menu_exit == MENU_EXIT_REBOOT) ? Reboot() : PowerOff(); (godmode_exit == 0) ? Reboot() : PowerOff();
return 0; return 0;
} }