1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-26 04:02:11 +00:00

Baby steps.

This commit is contained in:
Pablo Curiel 2020-04-15 16:50:07 -04:00
parent 65e40e7600
commit eccd3f0c1c
53 changed files with 5785 additions and 5145 deletions

View file

@ -32,8 +32,8 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
VERSION_MAJOR := 1
VERSION_MINOR := 1
VERSION_MICRO := 9
VERSION_MINOR := 2
VERSION_MICRO := 0
APP_TITLE := nxdumptool
APP_AUTHOR := DarkMatterCore

177
old/main.c Normal file
View file

@ -0,0 +1,177 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <switch.h>
#include "ui.h"
#include "util.h"
int main(int argc, char *argv[])
{
int ret = 0;
bool exitMainLoop = false;
/* Initialize application resources */
if (!initApplicationResources(argc, argv))
{
ret = -1;
goto out;
}
/* Main application loop */
while(appletMainLoop())
{
UIResult result = uiProcess();
switch(result)
{
case resultShowMainMenu:
uiSetState(stateMainMenu);
break;
case resultShowGameCardMenu:
uiSetState(stateGameCardMenu);
break;
case resultShowXciDumpMenu:
uiSetState(stateXciDumpMenu);
break;
case resultDumpXci:
uiSetState(stateDumpXci);
break;
case resultShowNspDumpMenu:
uiSetState(stateNspDumpMenu);
break;
case resultShowNspAppDumpMenu:
uiSetState(stateNspAppDumpMenu);
break;
case resultShowNspPatchDumpMenu:
uiSetState(stateNspPatchDumpMenu);
break;
case resultShowNspAddOnDumpMenu:
uiSetState(stateNspAddOnDumpMenu);
break;
case resultDumpNsp:
uiSetState(stateDumpNsp);
break;
case resultShowHfs0Menu:
uiSetState(stateHfs0Menu);
break;
case resultShowRawHfs0PartitionDumpMenu:
uiSetState(stateRawHfs0PartitionDumpMenu);
break;
case resultDumpRawHfs0Partition:
uiSetState(stateDumpRawHfs0Partition);
break;
case resultShowHfs0PartitionDataDumpMenu:
uiSetState(stateHfs0PartitionDataDumpMenu);
break;
case resultDumpHfs0PartitionData:
uiSetState(stateDumpHfs0PartitionData);
break;
case resultShowHfs0BrowserMenu:
uiSetState(stateHfs0BrowserMenu);
break;
case resultHfs0BrowserGetList:
uiSetState(stateHfs0BrowserGetList);
break;
case resultShowHfs0Browser:
uiSetState(stateHfs0Browser);
break;
case resultHfs0BrowserCopyFile:
uiSetState(stateHfs0BrowserCopyFile);
break;
case resultShowExeFsMenu:
uiSetState(stateExeFsMenu);
break;
case resultShowExeFsSectionDataDumpMenu:
uiSetState(stateExeFsSectionDataDumpMenu);
break;
case resultDumpExeFsSectionData:
uiSetState(stateDumpExeFsSectionData);
break;
case resultShowExeFsSectionBrowserMenu:
uiSetState(stateExeFsSectionBrowserMenu);
break;
case resultExeFsSectionBrowserGetList:
uiSetState(stateExeFsSectionBrowserGetList);
break;
case resultShowExeFsSectionBrowser:
uiSetState(stateExeFsSectionBrowser);
break;
case resultExeFsSectionBrowserCopyFile:
uiSetState(stateExeFsSectionBrowserCopyFile);
break;
case resultShowRomFsMenu:
uiSetState(stateRomFsMenu);
break;
case resultShowRomFsSectionDataDumpMenu:
uiSetState(stateRomFsSectionDataDumpMenu);
break;
case resultDumpRomFsSectionData:
uiSetState(stateDumpRomFsSectionData);
break;
case resultShowRomFsSectionBrowserMenu:
uiSetState(stateRomFsSectionBrowserMenu);
break;
case resultRomFsSectionBrowserGetEntries:
uiSetState(stateRomFsSectionBrowserGetEntries);
break;
case resultShowRomFsSectionBrowser:
uiSetState(stateRomFsSectionBrowser);
break;
case resultRomFsSectionBrowserChangeDir:
uiSetState(stateRomFsSectionBrowserChangeDir);
break;
case resultRomFsSectionBrowserCopyFile:
uiSetState(stateRomFsSectionBrowserCopyFile);
break;
case resultRomFsSectionBrowserCopyDir:
uiSetState(stateRomFsSectionBrowserCopyDir);
break;
case resultDumpGameCardCertificate:
uiSetState(stateDumpGameCardCertificate);
break;
case resultShowSdCardEmmcMenu:
uiSetState(stateSdCardEmmcMenu);
break;
case resultShowSdCardEmmcTitleMenu:
uiSetState(stateSdCardEmmcTitleMenu);
break;
case resultShowSdCardEmmcOrphanPatchAddOnMenu:
uiSetState(stateSdCardEmmcOrphanPatchAddOnMenu);
break;
case resultShowSdCardEmmcBatchModeMenu:
uiSetState(stateSdCardEmmcBatchModeMenu);
break;
case resultSdCardEmmcBatchDump:
uiSetState(stateSdCardEmmcBatchDump);
break;
case resultShowTicketMenu:
uiSetState(stateTicketMenu);
break;
case resultDumpTicket:
uiSetState(stateDumpTicket);
break;
case resultShowUpdateMenu:
uiSetState(stateUpdateMenu);
break;
case resultUpdateNSWDBXml:
uiSetState(stateUpdateNSWDBXml);
break;
case resultUpdateApplication:
uiSetState(stateUpdateApplication);
break;
case resultExit:
exitMainLoop = true;
break;
default:
break;
}
if (exitMainLoop) break;
}
out:
/* Deinitialize application resources */
deinitApplicationResources();
return ret;
}

3465
old/nca.c Normal file

File diff suppressed because it is too large Load diff

761
old/nca.h Normal file
View file

@ -0,0 +1,761 @@
#pragma once
#ifndef __NCA_H__
#define __NCA_H__
#include <switch.h>
#define NCA3_MAGIC (u32)0x4E434133 // "NCA3"
#define NCA2_MAGIC (u32)0x4E434132 // "NCA2"
#define NCA_HEADER_LENGTH 0x400
#define NCA_SECTION_HEADER_LENGTH 0x200
#define NCA_SECTION_HEADER_CNT 4
#define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_SECTION_HEADER_LENGTH * NCA_SECTION_HEADER_CNT))
#define NCA_AES_XTS_SECTOR_SIZE 0x200
#define NCA_KEY_AREA_KEY_CNT 4
#define NCA_KEY_AREA_KEY_SIZE 0x10
#define NCA_KEY_AREA_SIZE (NCA_KEY_AREA_KEY_CNT * NCA_KEY_AREA_KEY_SIZE)
#define NCA_FS_HEADER_PARTITION_PFS0 0x01
#define NCA_FS_HEADER_FSTYPE_PFS0 0x02
#define NCA_FS_HEADER_PARTITION_ROMFS 0x00
#define NCA_FS_HEADER_FSTYPE_ROMFS 0x03
#define NCA_FS_HEADER_CRYPT_NONE 0x01
#define NCA_FS_HEADER_CRYPT_XTS 0x02
#define NCA_FS_HEADER_CRYPT_CTR 0x03
#define NCA_FS_HEADER_CRYPT_BKTR 0x04
#define PFS0_MAGIC (u32)0x50465330 // "PFS0"
#define IVFC_MAGIC (u32)0x49564643 // "IVFC"
#define IVFC_MAX_LEVEL 6
#define BKTR_MAGIC (u32)0x424B5452 // "BKTR"
#define ROMFS_HEADER_SIZE 0x50
#define ROMFS_ENTRY_EMPTY (u32)0xFFFFFFFF
#define ROMFS_NONAME_DIRENTRY_SIZE 0x18
#define ROMFS_NONAME_FILEENTRY_SIZE 0x20
#define ROMFS_ENTRY_DIR 1
#define ROMFS_ENTRY_FILE 2
#define META_MAGIC (u32)0x4D455441 // "META"
#define NPDM_SIGNATURE_SIZE 0x100
#define NPDM_SIGNATURE_AREA_SIZE 0x200
#define NSP_NCA_FILENAME_LENGTH 0x25 // NCA ID + ".nca" + NULL terminator
#define NSP_CNMT_FILENAME_LENGTH 0x2A // NCA ID + ".cnmt.nca" / ".cnmt.xml" + NULL terminator
#define NSP_PROGRAM_XML_FILENAME_LENGTH 0x31 // NCA ID + ".programinfo.xml" + NULL terminator
#define NSP_NACP_XML_FILENAME_LENGTH 0x2A // NCA ID + ".nacp.xml" + NULL terminator
#define NSP_LEGAL_XML_FILENAME_LENGTH 0x2F // NCA ID + ".legalinfo.xml" + NULL terminator
#define NSP_TIK_FILENAME_LENGTH 0x25 // Rights ID + ".tik" + NULL terminator
#define NSP_CERT_FILENAME_LENGTH 0x26 // Rights ID + ".cert" + NULL terminator
#define ETICKET_ENTRY_SIZE 0x400
#define ETICKET_TITLEKEY_OFFSET 0x180
#define ETICKET_RIGHTSID_OFFSET 0x2A0
#define ETICKET_UNKNOWN_FIELD_SIZE 0x140
#define ETICKET_DATA_OFFSET 0x140
#define ETICKET_CA_CERT_SIZE 0x400
#define ETICKET_XS_CERT_SIZE 0x300
#define ETICKET_TIK_FILE_SIZE (ETICKET_ENTRY_SIZE - 0x140)
#define ETICKET_CERT_FILE_SIZE (ETICKET_CA_CERT_SIZE + ETICKET_XS_CERT_SIZE)
#define ETICKET_TITLEKEY_COMMON 0
#define ETICKET_TITLEKEY_PERSONALIZED 1
typedef enum {
DUMP_APP_NSP = 0,
DUMP_PATCH_NSP,
DUMP_ADDON_NSP
} nspDumpType;
typedef struct {
u32 magic;
u32 file_cnt;
u32 str_table_size;
u32 reserved;
} PACKED pfs0_header;
typedef struct {
u64 file_offset;
u64 file_size;
u32 filename_offset;
u32 reserved;
} PACKED pfs0_file_entry;
typedef struct {
u32 media_start_offset;
u32 media_end_offset;
u8 _0x8[0x8]; /* Padding. */
} PACKED nca_section_entry_t;
typedef struct {
u8 master_hash[0x20]; /* SHA-256 hash of the hash table. */
u32 block_size; /* In bytes. */
u32 always_2;
u64 hash_table_offset; /* Normally zero. */
u64 hash_table_size;
u64 pfs0_offset;
u64 pfs0_size;
u8 _0x48[0xF0];
} PACKED pfs0_superblock_t;
typedef struct {
u64 logical_offset;
u64 hash_data_size;
u32 block_size;
u32 reserved;
} PACKED ivfc_level_hdr_t;
typedef struct {
u32 magic;
u32 id;
u32 master_hash_size;
u32 num_levels;
ivfc_level_hdr_t level_headers[IVFC_MAX_LEVEL];
u8 _0xA0[0x20];
u8 master_hash[0x20];
} PACKED ivfc_hdr_t;
typedef struct {
ivfc_hdr_t ivfc_header;
u8 _0xE0[0x58];
} PACKED romfs_superblock_t;
typedef struct {
u64 offset;
u64 size;
u32 magic; /* "BKTR" */
u32 _0x14; /* Version? */
u32 num_entries;
u32 _0x1C; /* Reserved? */
} PACKED bktr_header_t;
typedef struct {
ivfc_hdr_t ivfc_header;
u8 _0xE0[0x18];
bktr_header_t relocation_header;
bktr_header_t subsection_header;
} PACKED bktr_superblock_t;
/* NCA FS header. */
typedef struct {
u8 _0x0;
u8 _0x1;
u8 partition_type;
u8 fs_type;
u8 crypt_type;
u8 _0x5[0x3];
union { /* FS-specific superblock. Size = 0x138. */
pfs0_superblock_t pfs0_superblock;
romfs_superblock_t romfs_superblock;
bktr_superblock_t bktr_superblock;
};
union {
u8 section_ctr[0x8];
struct {
u32 section_ctr_low;
u32 section_ctr_high;
};
};
u8 _0x148[0xB8]; /* Padding. */
} PACKED nca_fs_header_t;
/* Nintendo content archive header. */
typedef struct {
u8 fixed_key_sig[0x100]; /* RSA-PSS signature over header with fixed key. */
u8 npdm_key_sig[0x100]; /* RSA-PSS signature over header with key in NPDM. */
u32 magic;
u8 distribution; /* System vs gamecard. */
u8 content_type;
u8 crypto_type; /* Which keyblob (field 1) */
u8 kaek_ind; /* Which kaek index? */
u64 nca_size; /* Entire archive size. */
u64 title_id;
u8 _0x218[0x4]; /* Padding. */
union {
u32 sdk_version; /* What SDK was this built with? */
struct {
u8 sdk_revision;
u8 sdk_micro;
u8 sdk_minor;
u8 sdk_major;
};
};
u8 crypto_type2; /* Which keyblob (field 2) */
u8 fixed_key_generation;
u8 _0x222[0xE]; /* Padding. */
u8 rights_id[0x10]; /* Rights ID (for titlekey crypto). */
nca_section_entry_t section_entries[4]; /* Section entry metadata. */
u8 section_hashes[4][0x20]; /* SHA-256 hashes for each section header. */
u8 nca_keys[4][0x10]; /* Key area (encrypted) */
u8 _0x340[0xC0]; /* Padding. */
nca_fs_header_t fs_headers[4]; /* FS section headers. */
} PACKED nca_header_t;
typedef struct {
u32 magic;
u32 _0x4;
u32 _0x8;
u8 mmu_flags;
u8 _0xD;
u8 main_thread_prio;
u8 default_cpuid;
u64 _0x10;
u32 process_category;
u32 main_stack_size;
char title_name[0x50];
u32 aci0_offset;
u32 aci0_size;
u32 acid_offset;
u32 acid_size;
} PACKED npdm_t;
typedef struct {
u64 title_id;
u32 version;
u8 type;
u8 unk1;
u16 extended_header_size;
u16 content_cnt;
u16 content_meta_cnt;
u8 attr;
u8 unk2[0x03];
u32 required_dl_sysver;
u8 unk3[0x04];
} PACKED cnmt_header;
typedef struct {
u64 patch_tid; /* Patch TID / Application TID */
u32 min_sysver; /* Minimum system/application version */
u32 min_appver; /* Minimum application version (only for base applications) */
} PACKED cnmt_extended_header;
typedef struct {
u8 hash[0x20];
u8 nca_id[0x10];
u8 size[6];
u8 type;
u8 id_offset;
} PACKED cnmt_content_record;
typedef struct {
u8 type;
u64 title_id;
u32 version;
u32 required_dl_sysver;
u32 nca_cnt;
u8 digest[SHA256_HASH_SIZE];
char digest_str[(SHA256_HASH_SIZE * 2) + 1];
u8 min_keyblob;
u32 min_sysver;
u64 patch_tid;
u32 min_appver;
} cnmt_xml_program_info;
typedef struct {
u8 type;
u8 nca_id[SHA256_HASH_SIZE / 2];
char nca_id_str[SHA256_HASH_SIZE + 1];
u64 size;
u8 hash[SHA256_HASH_SIZE];
char hash_str[(SHA256_HASH_SIZE * 2) + 1];
u8 keyblob;
u8 id_offset;
u64 cnt_record_offset; // Relative to the start of the content records section in the CNMT
u8 decrypted_nca_keys[NCA_KEY_AREA_SIZE];
u8 encrypted_header_mod[NCA_FULL_HEADER_LENGTH];
} cnmt_xml_content_info;
typedef struct {
u32 nca_index;
u8 *hash_table;
u64 hash_table_offset; // Relative to NCA start
u64 hash_table_size;
u8 block_mod_cnt;
u8 *block_data[2];
u64 block_offset[2]; // Relative to NCA start
u64 block_size[2];
} nca_program_mod_data;
typedef struct {
char filename[100];
u64 icon_size;
u8 icon_data[0x20000];
} nacp_icons_ctx;
typedef struct {
u32 nca_index;
u64 xml_size;
char *xml_data;
u8 nacp_icon_cnt; // Only used with Control NCAs
nacp_icons_ctx *nacp_icons; // Only used with Control NCAs
} xml_record_info;
typedef struct {
u64 section_offset; // Relative to NCA start
u64 section_size;
u64 hash_table_offset; // Relative to NCA start
u64 hash_block_size;
u32 hash_block_cnt;
u64 pfs0_offset; // Relative to NCA start
u64 pfs0_size;
u64 title_cnmt_offset; // Relative to NCA start
u64 title_cnmt_size;
} nca_cnmt_mod_data;
typedef struct {
u32 sig_type;
u8 signature[0x100];
u8 padding[0x3C];
char sig_issuer[0x40];
u8 titlekey_block[0x100];
u8 unk1;
u8 titlekey_type;
u8 unk2[0x03];
u8 master_key_rev;
u8 unk3[0x0A];
u64 ticket_id;
u64 device_id;
u8 rights_id[0x10];
u32 account_id;
u8 unk4[0x0C];
} PACKED rsa2048_sha256_ticket;
typedef struct {
bool has_rights_id;
u8 rights_id[0x10];
char rights_id_str[33];
char tik_filename[37];
char cert_filename[38];
u8 enc_titlekey[0x10];
u8 dec_titlekey[0x10];
u8 cert_data[ETICKET_CERT_FILE_SIZE];
rsa2048_sha256_ticket tik_data;
bool retrieved_tik;
bool missing_tik;
} title_rights_ctx;
typedef struct {
NcmStorageId storageId;
NcmContentStorage ncmStorage;
NcmContentId ncaId;
u8 idOffset;
Aes128CtrContext aes_ctx;
u64 exefs_offset; // Relative to NCA start
u64 exefs_size;
pfs0_header exefs_header;
u64 exefs_entries_offset; // Relative to NCA start
pfs0_file_entry *exefs_entries;
u64 exefs_str_table_offset; // Relative to NCA start
char *exefs_str_table;
u64 exefs_data_offset; // Relative to NCA start
} exefs_ctx_t;
typedef struct {
NcmStorageId storageId;
NcmContentStorage ncmStorage;
NcmContentId ncaId;
u8 idOffset;
Aes128CtrContext aes_ctx;
u64 section_offset; // Relative to NCA start
u64 section_size;
u64 romfs_offset; // Relative to NCA start
u64 romfs_size;
u64 romfs_dirtable_offset; // Relative to NCA start
u64 romfs_dirtable_size;
romfs_dir *romfs_dir_entries;
u64 romfs_filetable_offset; // Relative to NCA start
u64 romfs_filetable_size;
romfs_file *romfs_file_entries;
u64 romfs_filedata_offset; // Relative to NCA start
} romfs_ctx_t;
typedef struct {
u64 virt_offset;
u64 phys_offset;
u32 is_patch;
} PACKED bktr_relocation_entry_t;
typedef struct {
u32 _0x0;
u32 num_entries;
u64 virtual_offset_end;
bktr_relocation_entry_t entries[0x3FF0 / sizeof(bktr_relocation_entry_t)];
u8 padding[0x3FF0 % sizeof(bktr_relocation_entry_t)];
} PACKED bktr_relocation_bucket_t;
typedef struct {
u32 _0x0;
u32 num_buckets;
u64 total_size;
u64 bucket_virtual_offsets[0x3FF0 / sizeof(u64)];
bktr_relocation_bucket_t buckets[];
} PACKED bktr_relocation_block_t;
typedef struct {
u64 offset;
u32 _0x8;
u32 ctr_val;
} PACKED bktr_subsection_entry_t;
typedef struct {
u32 _0x0;
u32 num_entries;
u64 physical_offset_end;
bktr_subsection_entry_t entries[0x3FF];
} PACKED bktr_subsection_bucket_t;
typedef struct {
u32 _0x0;
u32 num_buckets;
u64 total_size;
u64 bucket_physical_offsets[0x3FF0 / sizeof(u64)];
bktr_subsection_bucket_t buckets[];
} PACKED bktr_subsection_block_t;
typedef struct {
NcmStorageId storageId;
NcmContentStorage ncmStorage;
NcmContentId ncaId;
u8 idOffset;
Aes128CtrContext aes_ctx;
u64 section_offset; // Relative to NCA start
u64 section_size;
bktr_superblock_t superblock;
bktr_relocation_block_t *relocation_block;
bktr_subsection_block_t *subsection_block;
u64 virtual_seek; // Relative to section start
u64 bktr_seek; // Relative to section start (patch BKTR section)
u64 base_seek; // Relative to section start (base application RomFS section)
u64 romfs_offset; // Relative to section start
u64 romfs_size;
u64 romfs_dirtable_offset; // Relative to section start
u64 romfs_dirtable_size;
romfs_dir *romfs_dir_entries;
u64 romfs_filetable_offset; // Relative to section start
u64 romfs_filetable_size;
romfs_file *romfs_file_entries;
u64 romfs_filedata_offset; // Relative to section start
} bktr_ctx_t;
// Used in HFS0 / ExeFS / RomFS browsers
typedef struct {
u64 size;
char sizeStr[32];
} browser_entry_size_info;
typedef struct {
u8 type; // 1 = Dir, 2 = File
u64 offset; // Relative to directory/file table, depending on type
browser_entry_size_info sizeInfo; // Only used if type == 2
} romfs_browser_entry;
typedef struct {
char name[0x200];
char publisher[0x100];
} Title;
typedef enum {
Language_AmericanEnglish = 0,
Language_BritishEnglish = 1,
Language_Japanese = 2,
Language_French = 3,
Language_German = 4,
Language_LatinAmericanSpanish = 5,
Language_Spanish = 6,
Language_Italian = 7,
Language_Dutch = 8,
Language_CanadianFrench = 9,
Language_Portuguese = 10,
Language_Russian = 11,
Language_Korean = 12,
Language_TraditionalChinese = 13,
Language_SimplifiedChinese = 14
} Language;
typedef enum {
StartupUserAccount_None = 0,
StartupUserAccount_Required = 1,
StartupUserAccount_RequiredWithNetworkServiceAccountAvailable = 2
} StartupUserAccount;
/* Introduced as of SDK 6.4.0 */
typedef enum {
UserAccountSwitchLock_Disable = 0,
UserAccountSwitchLock_Enable = 1
} UserAccountSwitchLock;
/* Introduced as of SDK 3.4.0 */
typedef enum {
AddOnContentRegistrationType_AllOnLaunch = 0,
AddOnContentRegistrationType_OnDemand = 1
} AddOnContentRegistrationType;
typedef struct {
u32 AttributeFlag_Demo : 1;
u32 AttributeFlag_RetailInteractiveDisplay : 1; /* Introduced as of SDK 3.4.0 */
} AttributeFlag;
typedef struct {
u32 SupportedLanguageFlag_AmericanEnglish : 1;
u32 SupportedLanguageFlag_BritishEnglish : 1;
u32 SupportedLanguageFlag_Japanese : 1;
u32 SupportedLanguageFlag_French : 1;
u32 SupportedLanguageFlag_German : 1;
u32 SupportedLanguageFlag_LatinAmericanSpanish : 1;
u32 SupportedLanguageFlag_Spanish : 1;
u32 SupportedLanguageFlag_Italian : 1;
u32 SupportedLanguageFlag_Dutch : 1;
u32 SupportedLanguageFlag_CanadianFrench : 1;
u32 SupportedLanguageFlag_Portuguese : 1;
u32 SupportedLanguageFlag_Russian : 1;
u32 SupportedLanguageFlag_Korean : 1;
u32 SupportedLanguageFlag_TraditionalChinese : 1;
u32 SupportedLanguageFlag_SimplifiedChinese : 1;
} SupportedLanguageFlag;
typedef struct {
u32 ParentalControlFlag_FreeCommunication : 1;
} ParentalControlFlag;
typedef enum {
Screenshot_Allow = 0,
Screenshot_Deny = 1
} Screenshot;
typedef enum {
VideoCapture_Disable = 0,
VideoCapture_Manual = 1,
VideoCapture_Enable = 2
} VideoCapture;
typedef enum {
DataLossConfirmation_None = 0,
DataLossConfirmation_Required = 1
} DataLossConfirmation;
typedef enum {
PlayLogPolicy_All = 0,
PlayLogPolicy_LogOnly = 1,
PlayLogPolicy_None = 2
} PlayLogPolicy;
typedef enum {
RatingAgeOrganization_CERO = 0,
RatingAgeOrganization_GRACGCRB = 1,
RatingAgeOrganization_GSRMR = 2,
RatingAgeOrganization_ESRB = 3,
RatingAgeOrganization_ClassInd = 4,
RatingAgeOrganization_USK = 5,
RatingAgeOrganization_PEGI = 6,
RatingAgeOrganization_PEGIPortugal = 7,
RatingAgeOrganization_PEGIBBFC = 8,
RatingAgeOrganization_Russian = 9,
RatingAgeOrganization_ACB = 10,
RatingAgeOrganization_OFLC = 11,
RatingAgeOrganization_IARCGeneric = 12 /* Introduced as of SDK 9.3.1 */
} RatingAgeOrganization;
typedef struct {
u8 cero;
u8 gracgcrb;
u8 gsrmr;
u8 esrb;
u8 class_ind;
u8 usk;
u8 pegi;
u8 pegi_portugal;
u8 pegibbfc;
u8 russian;
u8 acb;
u8 oflc;
u8 iarc_generic;
u8 reserved_1[0x13];
} RatingAge;
typedef enum {
LogoType_LicensedByNintendo = 0,
LogoType_DistributedByNintendo = 1, /* Removed in SDK 3.5.2 */
LogoType_Nintendo = 2
} LogoType;
typedef enum {
LogoHandling_Auto = 0,
LogoHandling_Manual = 1
} LogoHandling;
/* Introduced as of SDK 4.5.0 */
typedef enum {
RuntimeAddOnContentInstall_Deny = 0,
RuntimeAddOnContentInstall_AllowAppend = 1
} RuntimeAddOnContentInstall;
/* Introduced as of SDK 9.3.1 */
typedef enum {
RuntimeParameterDelivery_Always = 0,
RuntimeParameterDelivery_AlwaysIfUserStateMatched = 1,
RuntimeParameterDelivery_OnRestart = 2
} RuntimeParameterDelivery;
/* Introduced as of SDK 3.5.2 */
typedef enum {
CrashReport_Deny = 0,
CrashReport_Allow = 1
} CrashReport;
typedef enum {
Hdcp_None = 0,
Hdcp_Required = 1
} Hdcp;
/* Introduced as of SDK 7.6.0 */
typedef struct {
u8 StartupUserAccountOptionFlag_IsOptional : 1;
} StartupUserAccountOptionFlag;
/* Introduced as of SDK 5.3.0 */
typedef enum {
PlayLogQueryCapability_None = 0,
PlayLogQueryCapability_WhiteList = 1,
PlayLogQueryCapability_All = 2
} PlayLogQueryCapability;
/* Introduced as of SDK 5.3.0 */
typedef struct {
u8 RepairFlag_SuppressGameCardAccess : 1;
} RepairFlag;
/* Introduced as of SDK 6.4.0 */
typedef struct {
u8 RequiredNetworkServiceLicenseOnLaunchFlag_Common : 1;
} RequiredNetworkServiceLicenseOnLaunchFlag;
typedef struct {
u64 group_id;
u8 key[0x10];
} NeighborDetectionGroupConfiguration;
typedef struct {
NeighborDetectionGroupConfiguration send_group_configuration;
NeighborDetectionGroupConfiguration receivable_group_configurations[0x10];
} NeighborDetectionClientConfiguration;
/* Introduced as of SDK 7.6.0 */
typedef enum {
JitConfigurationFlag_None = 0,
JitConfigurationFlag_Enabled = 1
} JitConfigurationFlag;
typedef struct {
u64 jit_configuration_flag;
u64 memory_size;
} JitConfiguration;
typedef struct {
Title titles[0x10];
char isbn[0x25];
u8 startup_user_account;
u8 user_account_switch_lock; /* Old: touch_screen_usage (None, Supported, Required) */
u8 add_on_content_registration_type;
AttributeFlag attribute_flag;
SupportedLanguageFlag supported_language_flag;
ParentalControlFlag parental_control_flag;
u8 screenshot;
u8 video_capture;
u8 data_loss_confirmation;
u8 play_log_policy;
u64 presence_group_id;
RatingAge rating_ages;
char display_version[0x10];
u64 add_on_content_base_id;
u64 save_data_owner_id;
u64 user_account_save_data_size;
u64 user_account_save_data_journal_size;
u64 device_save_data_size;
u64 device_save_data_journal_size;
u64 bcat_delivery_cache_storage_size;
char application_error_code_category[0x8];
u64 local_communication_ids[0x8];
u8 logo_type;
u8 logo_handling;
u8 runtime_add_on_content_install;
u8 runtime_parameter_delivery;
u8 reserved_1[0x2];
u8 crash_report;
u8 hdcp;
u64 seed_for_pseudo_device_id;
char bcat_passphrase[0x41];
StartupUserAccountOptionFlag startup_user_account_option;
u8 reserved_2[0x6];
u64 user_account_save_data_size_max;
u64 user_account_save_data_journal_size_max;
u64 device_save_data_size_max;
u64 device_save_data_journal_size_max;
u64 temporary_storage_size;
u64 cache_storage_size;
u64 cache_storage_journal_size;
u64 cache_storage_data_and_journal_size_max;
u16 cache_storage_index_max;
u8 reserved_3[0x6];
u64 play_log_queryable_application_ids[0x10];
u8 play_log_query_capability;
RepairFlag repair_flag;
u8 program_index;
RequiredNetworkServiceLicenseOnLaunchFlag required_network_service_license_on_launch_flag;
u8 reserved_4[0x4];
NeighborDetectionClientConfiguration neighbor_detection_client_configuration;
JitConfiguration jit_configuration;
u8 reserved_5[0xC40];
} nacp_t;
char *getContentType(u8 type);
void generateCnmtXml(cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, char *out);
void convertNcaSizeToU64(const u8 size[0x6], u64 *out);
void convertU64ToNcaSize(const u64 size, u8 out[0x6]);
bool readNcaDataByContentId(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, u64 offset, void *outBuf, size_t bufSize);
bool processNcaCtrSectionBlock(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, Aes128CtrContext *ctx, u64 offset, void *outBuf, size_t bufSize, bool encrypt);
bool readBktrSectionBlock(u64 offset, void *outBuf, size_t bufSize);
bool encryptNcaHeader(nca_header_t *input, u8 *outBuf, u64 outBufSize);
bool decryptNcaHeader(const u8 *ncaBuf, u64 ncaBufSize, nca_header_t *out, title_rights_ctx *rights_info, u8 *decrypted_nca_keys, bool retrieveTitleKeyData);
bool retrieveTitleKeyFromGameCardTicket(title_rights_ctx *rights_info, u8 *decrypted_nca_keys);
bool processProgramNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, cnmt_xml_content_info *xml_content_info, nca_program_mod_data **output, u32 *cur_mod_cnt, u32 idx);
bool retrieveCnmtNcaData(NcmStorageId curStorageId, u8 *ncaBuf, cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, u32 cnmtNcaIndex, nca_cnmt_mod_data *output, title_rights_ctx *rights_info);
bool patchCnmtNca(u8 *ncaBuf, u64 ncaBufSize, cnmt_xml_program_info *xml_program_info, cnmt_xml_content_info *xml_content_info, nca_cnmt_mod_data *cnmt_mod);
bool parseExeFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool parseRomFsEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool parseBktrEntryFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys);
bool generateProgramInfoXml(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, bool useCustomAcidRsaPubKey, char **outBuf, u64 *outBufSize);
bool retrieveNacpDataFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **out_nacp_xml, u64 *out_nacp_xml_size, nacp_icons_ctx **out_nacp_icons_ctx, u8 *out_nacp_icons_ctx_cnt);
bool retrieveLegalInfoXmlFromNca(NcmContentStorage *ncmStorage, const NcmContentId *ncaId, nca_header_t *dec_nca_header, u8 *decrypted_nca_keys, char **outBuf, u64 *outBufSize);
#endif

View file

@ -19,63 +19,6 @@
#define LOGFILE(fmt, ...) utilsWriteLogMessage(__func__, fmt, ##__VA_ARGS__)
#define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member)
#define SLEEP(x) svcSleepThread((x) * (u64)1000000000)
static Mutex g_logfileMutex = 0;
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...)
{
mutexLock(&g_logfileMutex);
va_list args;
FILE *logfile = NULL;
logfile = fopen(APP_BASE_PATH "log.txt", "a+");
if (!logfile) return;
time_t now = time(NULL);
struct tm *ts = localtime(&now);
fprintf(logfile, "%d-%d-%d %d:%d:%d -> %s: ", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, func_name);
va_start(args, fmt);
vfprintf(logfile, fmt, args);
va_end(args);
fprintf(logfile, "\r\n");
fclose(logfile);
mutexUnlock(&g_logfileMutex);
}
typedef enum {
UtilsCustomFirmwareType_Atmosphere = 0,
UtilsCustomFirmwareType_SXOS = 1,
UtilsCustomFirmwareType_ReiNX = 2
} UtilsCustomFirmwareType;
typedef struct {
u16 major : 6;
u16 minor : 6;
u16 micro : 4;
u16 bugfix;
} TitleVersion;
#define CONFIG_PATH APP_BASE_PATH "config.bin"
#define NRO_NAME APP_TITLE ".nro"
#define NRO_PATH APP_BASE_PATH NRO_NAME

View file

@ -9,7 +9,9 @@
#define CERT_SAVEFILE_PATH "sys:/save/80000000000000e0"
#define CERT_SAVEFILE_STORAGE_BASE_PATH "/certificate/"
#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig_PubKeyRsa4096 : (pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig_PubKeyRsa2048 : CertType_Sig##sig_PubKeyEcsda240))
#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : (pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcsda240))
/* Function prototypes. */
static u8 certGetCertificateType(const void *data, u64 data_size);
static u32 certGetCertificateCountInSignatureIssuer(const char *issuer);

View file

@ -3,6 +3,7 @@
#ifndef __CERT_H__
#define __CERT_H__
#include <switch/types.h>
#include "signature.h"
#define CERT_MAX_SIZE 0x500 /* Equivalent to sizeof(CertSigRsa4096PubKeyRsa4096) */

View file

@ -1,4 +1,3 @@
#include <switch/arm/atomics.h>
#include <switch/services/sm.h>
#include <stdlib.h>
#include <string.h>
@ -34,7 +33,7 @@ Result esCountPersonalizedTicket(s32 *out_count)
return rc;
}
Result esListCommonTicket(s32 *out_entries_written, FsRightsId *out_ids, s32 count);
Result esListCommonTicket(s32 *out_entries_written, FsRightsId *out_ids, s32 count)
{
struct {
s32 num_rights_ids_written;

View file

@ -12,7 +12,7 @@
#include <switch.h>
#include "utils.h"
#include "../utils.h"
/*-----------------------------------------------------------------------*/
/* Get Drive Status */

55
source/fspusb.c Normal file
View file

@ -0,0 +1,55 @@
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "fspusb.h"
#include "service_guard.h"
#include <string.h>
static Service g_fspusbSrv;
NX_GENERATE_SERVICE_GUARD(fspusb);
Result _fspusbInitialize(void) {
return smGetService(&g_fspusbSrv, "fsp-usb");
}
void _fspusbCleanup(void) {
serviceClose(&g_fspusbSrv);
}
Service* fspusbGetServiceSession(void) {
return &g_fspusbSrv;
}
Result fspusbListMountedDrives(s32 *drives_buf, size_t drive_count, s32 *out_total) {
return serviceDispatchOut(&g_fspusbSrv, 0, *out_total,
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
.buffers = { { drives_buf, drive_count * sizeof(s32) } },
);
}
Result fspusbGetDriveFileSystemType(s32 interface_id, FspUsbFileSystemType *out_type) {
return serviceDispatchInOut(&g_fspusbSrv, 1, interface_id, *out_type);
}
Result fspusbGetDriveLabel(s32 interface_id, char *out_label, size_t out_label_size) {
return serviceDispatchIn(&g_fspusbSrv, 2, interface_id,
.buffer_attrs = { SfBufferAttr_Out | SfBufferAttr_HipcMapAlias },
.buffers = { { out_label, out_label_size } },
);
}
Result fspusbSetDriveLabel(s32 interface_id, const char *label) {
char inputlbl[11 + 1] = {0}; // Actual limit is 11 characters
strncpy(inputlbl, label, 11);
return serviceDispatchIn(&g_fspusbSrv, 3, interface_id,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcMapAlias },
.buffers = { { inputlbl, 11 + 1 } },
);
}
Result fspusbOpenDriveFileSystem(s32 interface_id, FsFileSystem *out_fs) {
return serviceDispatchIn(&g_fspusbSrv, 4, interface_id,
.out_num_objects = 1,
.out_objects = &out_fs->s,
);
}

37
source/fspusb.h Normal file
View file

@ -0,0 +1,37 @@
/**
* @file fspusb.h
* @brief USB filesystem extension (fsp-usb) service IPC wrapper.
* @author XorTroll
* @copyright libnx Authors
*/
#pragma once
#ifndef __FSPUSB_H__
#define __FSPUSB_H__
#include <switch.h>
/// This is basically FATFS' file system types.
typedef enum {
FspUsbFileSystemType_FAT12 = 1,
FspUsbFileSystemType_FAT16 = 2,
FspUsbFileSystemType_FAT32 = 3,
FspUsbFileSystemType_exFAT = 4,
} FspUsbFileSystemType;
/// Initialize fsp-usb.
Result fspusbInitialize(void);
/// Exit fsp-usb.
void fspusbExit(void);
/// Gets the Service object for the actual fsp-usb service session.
Service* fspusbGetServiceSession(void);
Result fspusbListMountedDrives(s32 *drives_buf, size_t drive_count, s32 *out_total);
Result fspusbGetDriveFileSystemType(s32 interface_id, FspUsbFileSystemType *out_type);
Result fspusbGetDriveLabel(s32 interface_id, char *out_label, size_t out_label_size);
Result fspusbSetDriveLabel(s32 interface_id, const char *label);
Result fspusbOpenDriveFileSystem(s32 interface_id, FsFileSystem *out_fs);
#endif /* __FSPUSB_H__ */

View file

@ -16,12 +16,24 @@
#define GAMECARD_ECC_BLOCK_SIZE 0x200
#define GAMECARD_ECC_DATA_SIZE 0x24
#define GAMECARD_STORAGE_AREA_NAME(x) ((x) == GameCardStorageArea_Normal ? "normal" : ((x) == GameCardStorageArea_Secure ? "secure" : "none"))
/* Type definitions. */
typedef enum {
GameCardStorageArea_None = 0,
GameCardStorageArea_Normal = 1,
GameCardStorageArea_Secure = 2
} GameCardStorageArea;
typedef struct {
u64 offset; ///< Relative to the start of the gamecard header.
u64 size; ///< Whole partition size.
u8 *header; ///< GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table.
} GameCardHashFileSystemPartitionInfo;
/* Global variables. */
static FsDeviceOperator g_deviceOperator = {0};
static FsEventNotifier g_gameCardEventNotifier = {0};
static Event g_gameCardKernelEvent = {0};
@ -33,20 +45,23 @@ static mtx_t g_gameCardSharedDataMutex;
static bool g_gameCardDetectionThreadCreated = false, g_gameCardInserted = false, g_gameCardInfoLoaded = false;
static FsGameCardHandle g_gameCardHandle = {0};
static FsStorage g_gameCardStorageNormal = {0}, g_gameCardStorageSecure = {0};
static FsStorage g_gameCardStorage = {0};
static u8 g_gameCardStorageCurrentArea = GameCardStorageArea_None;
static u8 *g_gameCardReadBuf = NULL;
static GameCardHeader g_gameCardHeader = {0};
static u64 g_gameCardStorageNormalAreaSize = 0, g_gameCardStorageSecureAreaSize = 0;
static u8 *g_gameCardHfsRootHeader = NULL; /* GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table */
static u8 *g_gameCardHfsRootHeader = NULL; /// GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table.
static GameCardHashFileSystemPartitionInfo *g_gameCardHfsPartitions = NULL;
/* Function prototypes. */
static bool gamecardCreateDetectionThread(void);
static void gamecardDestroyDetectionThread(void);
static int gamecardDetectionThreadFunc(void *arg);
static inline bool gamecardCheckIfInserted(void);
static inline bool gamecardIsInserted(void);
static void gamecardLoadInfo(void);
static void gamecardFreeInfo(void);
@ -54,16 +69,16 @@ static void gamecardFreeInfo(void);
static bool gamecardGetHandle(void);
static inline void gamecardCloseHandle(void);
static bool gamecardOpenStorageAreas(void);
static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock);
static void gamecardCloseStorageAreas(void);
static bool gamecardOpenStorageArea(u8 area);
static bool gamecardReadStorageArea(void *out, u64 out_size, u64 offset, bool lock);
static void gamecardCloseStorageArea(void);
static bool gamecardGetSizesFromStorageAreas(void);
/* Service guard used to generate thread-safe initialize + exit functions */
NX_GENERATE_SERVICE_GUARD(gamecard);
bool gamecardCheckReadyStatus(void)
bool gamecardIsReady(void)
{
mtx_lock(&g_gameCardSharedDataMutex);
bool status = (g_gameCardInserted && g_gameCardInfoLoaded);
@ -71,9 +86,9 @@ bool gamecardCheckReadyStatus(void)
return status;
}
bool gamecardStorageRead(void *out, u64 out_size, u64 offset)
bool gamecardRead(void *out, u64 out_size, u64 offset)
{
return _gamecardStorageRead(out, out_size, offset, true);
return gamecardReadStorageArea(out, out_size, offset, true);
}
bool gamecardGetHeader(GameCardHeader *out)
@ -321,10 +336,10 @@ static int gamecardDetectionThreadFunc(void *arg)
mtx_lock(&g_gameCardSharedDataMutex);
/* Retrieve initial gamecard insertion status */
g_gameCardInserted = prev_status = gamecardCheckIfInserted();
g_gameCardInserted = prev_status = gamecardIsInserted();
/* Load gamecard info right away if a gamecard is inserted and if a handle can be retrieved */
if (g_gameCardInserted && gamecardGetHandle()) gamecardLoadInfo();
/* Load gamecard info right away if a gamecard is inserted */
if (g_gameCardInserted) gamecardLoadInfo();
mtx_unlock(&g_gameCardSharedDataMutex);
@ -341,19 +356,18 @@ static int gamecardDetectionThreadFunc(void *arg)
/* Only proceed if we're dealing with a status change */
mtx_lock(&g_gameCardSharedDataMutex);
g_gameCardInserted = gamecardCheckIfInserted();
g_gameCardInserted = gamecardIsInserted();
if (!prev_status && g_gameCardInserted)
{
/* Don't access the gamecard immediately to avoid conflicts with HOS / sysmodules */
SLEEP(GAMECARD_ACCESS_WAIT_TIME);
/* Load gamecard info if a gamecard is inserted and if a handle can be retrieved */
if (gamecardGetHandle()) gamecardLoadInfo();
/* Load gamecard info */
gamecardLoadInfo();
} else {
/* Free gamecard info and close gamecard handle */
/* Free gamecard info */
gamecardFreeInfo();
gamecardCloseHandle();
}
prev_status = g_gameCardInserted;
@ -364,14 +378,13 @@ static int gamecardDetectionThreadFunc(void *arg)
/* Free gamecard info and close gamecard handle */
mtx_lock(&g_gameCardSharedDataMutex);
gamecardFreeInfo();
gamecardCloseHandle();
g_gameCardInserted = false;
mtx_unlock(&g_gameCardSharedDataMutex);
return 0;
}
static inline bool gamecardCheckIfInserted(void)
static inline bool gamecardIsInserted(void)
{
bool inserted = false;
Result rc = fsDeviceOperatorIsGameCardInserted(&g_deviceOperator, &inserted);
@ -386,15 +399,16 @@ static void gamecardLoadInfo(void)
GameCardHashFileSystemHeader *fs_header = NULL;
GameCardHashFileSystemEntry *fs_entry = NULL;
/* Open gamecard storage areas */
if (!gamecardOpenStorageAreas())
/* Retrieve gamecard storage area sizes */
/* gamecardReadStorageArea() actually checks if the storage area sizes are greater than zero, so we must first perform this step */
if (!gamecardGetSizesFromStorageAreas())
{
LOGFILE("Failed to open gamecard storage areas!");
LOGFILE("Failed to retrieve gamecard storage area sizes!");
goto out;
}
/* Read gamecard header */
if (!_gamecardStorageRead(&g_gameCardHeader, sizeof(GameCardHeader), 0, false))
if (!gamecardReadStorageArea(&g_gameCardHeader, sizeof(GameCardHeader), 0, false))
{
LOGFILE("Failed to read gamecard header!");
goto out;
@ -409,9 +423,9 @@ static void gamecardLoadInfo(void)
if (utilsGetCustomFirmwareType() == UtilsCustomFirmwareType_SXOS)
{
/* Total size for the secure storage area is maxed out under SX OS */
/* The total size for the secure storage area is maxed out under SX OS */
/* Let's try to calculate it manually */
u64 capacity = gamecardGetCapacity(&g_gameCardHeader);
u64 capacity = gamecardGetCapacityFromHeader(&g_gameCardHeader);
if (!capacity)
{
LOGFILE("Invalid gamecard capacity value! (0x%02X)", g_gameCardHeader.rom_size);
@ -430,7 +444,7 @@ static void gamecardLoadInfo(void)
}
/* Read root hash FS header */
if (!_gamecardStorageRead(g_gameCardHfsRootHeader, g_gameCardHeader.partition_fs_header_size, g_gameCardHeader.partition_fs_header_address, false))
if (!gamecardReadStorageArea(g_gameCardHfsRootHeader, g_gameCardHeader.partition_fs_header_size, g_gameCardHeader.partition_fs_header_address, false))
{
LOGFILE("Failed to read root hash FS header from offset 0x%lX!", g_gameCardHeader.partition_fs_header_address);
goto out;
@ -475,7 +489,7 @@ static void gamecardLoadInfo(void)
/* Partially read the current hash FS partition header */
GameCardHashFileSystemHeader partition_header = {0};
if (!_gamecardStorageRead(&partition_header, sizeof(GameCardHashFileSystemHeader), g_gameCardHfsPartitions[i].offset, false))
if (!gamecardReadStorageArea(&partition_header, sizeof(GameCardHashFileSystemHeader), g_gameCardHfsPartitions[i].offset, false))
{
LOGFILE("Failed to partially read hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset);
goto out;
@ -505,7 +519,7 @@ static void gamecardLoadInfo(void)
}
/* Finally, read the full hash FS partition header */
if (!_gamecardStorageRead(g_gameCardHfsPartitions[i].header, partition_header_size, g_gameCardHfsPartitions[i].offset, false))
if (!gamecardReadStorageArea(g_gameCardHfsPartitions[i].header, partition_header_size, g_gameCardHfsPartitions[i].offset, false))
{
LOGFILE("Failed to read full hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset);
goto out;
@ -521,6 +535,8 @@ out:
static void gamecardFreeInfo(void)
{
memset(&g_gameCardHeader, 0, sizeof(GameCardHeader));
g_gameCardStorageNormalAreaSize = 0;
g_gameCardStorageSecureAreaSize = 0;
if (g_gameCardHfsRootHeader)
{
@ -544,7 +560,7 @@ static void gamecardFreeInfo(void)
g_gameCardHfsPartitions = NULL;
}
gamecardCloseStorageAreas();
gamecardCloseStorageArea();
g_gameCardInfoLoaded = false;
}
@ -557,8 +573,6 @@ static bool gamecardGetHandle(void)
return false;
}
if (g_gameCardInfoLoaded && g_gameCardHandle.value) return true;
Result rc1 = 0, rc2 = 0;
FsStorage tmp_storage = {0};
@ -598,58 +612,50 @@ static inline void gamecardCloseHandle(void)
g_gameCardHandle.value = 0;
}
static bool gamecardOpenStorageAreas(void)
static bool gamecardOpenStorageArea(u8 area)
{
if (!g_gameCardInserted || !g_gameCardHandle.value)
if (!g_gameCardInserted || (area != GameCardStorageArea_Normal && area != GameCardStorageArea_Secure))
{
LOGFILE("Invalid parameters!");
return false;
}
if (g_gamecardInfoLoaded && serviceIsActive(&(g_gameCardStorageNormal.s)) && serviceIsActive(&(g_gameCardStorageSecure.s))) return true;
if (g_gameCardHandle.value && serviceIsActive(&(g_gameCardStorage.s)) && g_gameCardStorageCurrentArea == area) return true;
gamecardCloseStorageAreas();
gamecardCloseStorageArea();
Result rc = 0;
bool success = false;
u32 partition = (area - 1); /* Zero-based index */
rc = fsOpenGameCardStorage(&g_gameCardStorageNormal, &g_gameCardHandle, 0);
/* Retrieve a new gamecard handle */
if (!gamecardGetHandle())
{
LOGFILE("Failed to retrieve gamecard handle!");
return false;
}
/* Open storage area */
rc = fsOpenGameCardStorage(&g_gameCardStorage, &g_gameCardHandle, partition);
if (R_FAILED(rc))
{
LOGFILE("fsOpenGameCardStorage failed! (0x%08X) (normal)", rc);
goto out;
LOGFILE("fsOpenGameCardStorage failed to open %s storage area! (0x%08X)", GAMECARD_STORAGE_AREA_NAME(area), rc);
gamecardCloseHandle();
return false;
}
rc = fsOpenGameCardStorage(&g_gameCardStorageSecure, &g_gameCardHandle, 1);
if (R_FAILED(rc))
{
LOGFILE("fsOpenGameCardStorage failed! (0x%08X) (secure)", rc);
goto out;
}
g_gameCardStorageCurrentArea = area;
if (!gamecardGetSizesFromStorageAreas())
{
LOGFILE("Failed to retrieve sizes from storage areas!");
goto out;
}
success = true;
out:
if (!success) gamecardCloseStorageAreas();
return success;
return true;
}
static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock)
static bool gamecardReadStorageArea(void *out, u64 out_size, u64 offset, bool lock)
{
if (lock) mtx_lock(&g_gameCardSharedDataMutex);
bool success = false;
if (!g_gameCardInserted || !serviceIsActive(&(g_gameCardStorageNormal.s)) || !g_gameCardStorageNormalAreaSize || !serviceIsActive(&(g_gameCardStorageSecure.s)) || \
!g_gameCardStorageSecureAreaSize || !out || !out_size || offset >= (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize) || \
(offset + out_size) > (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize))
if (!g_gameCardInserted || !g_gameCardStorageNormalAreaSize || !g_gameCardStorageSecureAreaSize || !out || !out_size || \
offset >= (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize) || (offset + out_size) > (g_gameCardStorageNormalAreaSize + g_gameCardStorageSecureAreaSize))
{
LOGFILE("Invalid parameters!");
goto out;
@ -657,33 +663,41 @@ static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock)
Result rc = 0;
u8 *out_u8 = (u8*)out;
u8 area = (offset < g_gameCardStorageNormalAreaSize ? GameCardStorageArea_Normal : GameCardStorageArea_Secure);
/* Handle reads between the end of the normal storage area and the start of the secure storage area */
if (offset < g_gameCardStorageNormalAreaSize && (offset + out_size) > g_gameCardStorageNormalAreaSize)
/* Handle reads that span both the normal and secure gamecard storage areas */
if (area == GameCardStorageArea_Normal && (offset + out_size) > g_gameCardStorageNormalAreaSize)
{
/* Calculate normal storage area size difference */
u64 diff_size = (g_gameCardStorageNormalAreaSize - offset);
if (!_gamecardStorageRead(out_u8, diff_size, offset, false)) goto out;
if (!gamecardReadStorageArea(out_u8, diff_size, offset, false)) goto out;
/* Adjust variables to start reading right from the start of the secure storage area */
out_u8 += diff_size;
offset = g_gameCardStorageNormalAreaSize;
/* Adjust variables to read right from the start of the secure storage area */
out_size -= diff_size;
offset = g_gameCardStorageNormalAreaSize;
out_u8 += diff_size;
area = GameCardStorageArea_Secure;
}
/* Open a storage area if needed */
/* If the right storage area has already been opened, this will return true */
if (!gamecardOpenStorageArea(area))
{
LOGFILE("Failed to open %s storage area!", GAMECARD_STORAGE_AREA_NAME(area));
goto out;
}
/* Calculate appropiate storage area offset and retrieve the right storage area pointer */
const char *area = (offset < g_gameCardStorageNormalAreaSize ? "normal" : "secure");
u64 base_offset = (offset < g_gameCardStorageNormalAreaSize ? offset : (offset - g_gameCardStorageNormalAreaSize));
FsStorage *storage = (offset < g_gameCardStorageNormalAreaSize ? &g_gameCardStorageNormal : &g_gameCardStorageSecure);
u64 base_offset = (area == GameCardStorageArea_Normal ? offset : (offset - g_gameCardStorageNormalAreaSize));
if (!(base_offset % GAMECARD_MEDIA_UNIT_SIZE) && !(out_size % GAMECARD_MEDIA_UNIT_SIZE))
{
/* Optimization for reads that are already aligned to GAMECARD_MEDIA_UNIT_SIZE bytes */
rc = fsStorageRead(storage, base_offset, out_u8, out_size);
rc = fsStorageRead(&g_gameCardStorage, base_offset, out_u8, out_size);
if (R_FAILED(rc))
{
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (aligned)", out_size, base_offset, area, rc);
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (aligned)", out_size, base_offset, GAMECARD_STORAGE_AREA_NAME(area), rc);
goto out;
}
@ -691,22 +705,22 @@ static bool _gamecardStorageRead(void *out, u64 out_size, u64 offset, bool lock)
} else {
/* Fix offset and/or size to avoid unaligned reads */
u64 block_start_offset = (base_offset - (base_offset % GAMECARD_MEDIA_UNIT_SIZE));
u64 block_end_offset = round_up(base_offset + out_size, GAMECARD_MEDIA_UNIT_SIZE);
u64 block_end_offset = ROUND_UP(base_offset + out_size, GAMECARD_MEDIA_UNIT_SIZE);
u64 block_size = (block_end_offset - block_start_offset);
u64 chunk_size = (block_size > GAMECARD_READ_BUFFER_SIZE ? GAMECARD_READ_BUFFER_SIZE : block_size);
u64 out_chunk_size = (block_size > GAMECARD_READ_BUFFER_SIZE ? (GAMECARD_READ_BUFFER_SIZE - (base_offset - block_start_offset)) : out_size);
rc = fsStorageRead(storage, block_start_offset, g_gameCardReadBuf, chunk_size);
if (!R_FAILED(rc))
rc = fsStorageRead(&g_gameCardStorage, block_start_offset, g_gameCardReadBuf, chunk_size);
if (R_FAILED(rc))
{
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (unaligned)", chunk_size, block_start_offset, area, rc);
LOGFILE("fsStorageRead failed to read 0x%lX bytes at offset 0x%lX from %s storage area! (0x%08X) (unaligned)", chunk_size, block_start_offset, GAMECARD_STORAGE_AREA_NAME(area), rc);
goto out;
}
memcpy(out_u8, g_gameCardReadBuf + (base_offset - block_start_offset), out_chunk_size);
success = (block_size > GAMECARD_READ_BUFFER_SIZE ? _gamecardStorageRead(out_u8 + out_chunk_size, out_size - out_chunk_size, base_offset + out_chunk_size, false) : true);
success = (block_size > GAMECARD_READ_BUFFER_SIZE ? gamecardReadStorageArea(out_u8 + out_chunk_size, out_size - out_chunk_size, base_offset + out_chunk_size, false) : true);
}
out:
@ -715,48 +729,56 @@ out:
return success;
}
static void gamecardCloseStorageAreas(void)
static void gamecardCloseStorageArea(void)
{
if (serviceIsActive(&(g_gameCardStorageNormal.s)))
if (serviceIsActive(&(g_gameCardStorage.s)))
{
fsStorageClose(&g_gameCardStorageNormal);
memset(&g_gameCardStorageNormal, 0, sizeof(FsStorage));
fsStorageClose(&g_gameCardStorage);
memset(&g_gameCardStorage, 0, sizeof(FsStorage));
}
g_gameCardStorageNormalAreaSize = 0;
gamecardCloseHandle();
if (serviceIsActive(&(g_gameCardStorageSecure.s)))
{
fsStorageClose(&g_gameCardStorageSecure);
memset(&g_gameCardStorageSecure, 0, sizeof(FsStorage));
}
g_gameCardStorageSecureAreaSize = 0;
g_gameCardStorageCurrentArea = GameCardStorageArea_None;
}
static bool gamecardGetSizesFromStorageAreas(void)
{
if (!g_gameCardInserted || !serviceIsActive(&(g_gameCardStorageNormal.s)) || !serviceIsActive(&(g_gameCardStorageSecure.s)))
if (!g_gameCardInserted)
{
LOGFILE("Invalid parameters!");
LOGFILE("Gamecard not inserted!");
return false;
}
Result rc = 0;
rc = fsStorageGetSize(&g_gameCardStorageNormal, (s64*)&g_gameCardStorageNormalAreaSize);
if (R_FAILED(rc))
for(u8 i = 0; i < 2; i++)
{
LOGFILE("fsStorageGetSize failed! (0x%08X) (normal)", rc);
return false;
}
Result rc = 0;
u64 area_size = 0;
u8 area = (i == 0 ? GameCardStorageArea_Normal : GameCardStorageArea_Secure);
rc = fsStorageGetSize(&g_gameCardStorageSecure, (s64*)&g_gameCardStorageSecureAreaSize);
if (R_FAILED(rc))
{
LOGFILE("fsStorageGetSize failed! (0x%08X) (secure)", rc);
g_gameCardStorageNormalAreaSize = 0;
return false;
if (!gamecardOpenStorageArea(area))
{
LOGFILE("Failed to open %s storage area!", GAMECARD_STORAGE_AREA_NAME(area));
return false;
}
rc = fsStorageGetSize(&g_gameCardStorage, (s64*)&area_size);
gamecardCloseStorageArea();
if (R_FAILED(rc))
{
LOGFILE("fsStorageGetSize failed to retrieve %s storage area size! (0x%08X)", GAMECARD_STORAGE_AREA_NAME(area), rc);
g_gameCardStorageNormalAreaSize = g_gameCardStorageSecureAreaSize = 0;
return false;
}
if (area == GameCardStorageArea_Normal)
{
g_gameCardStorageNormalAreaSize = area_size;
} else {
g_gameCardStorageSecureAreaSize = area_size;
}
}
return true;

View file

@ -124,12 +124,12 @@ Result gamecardInitialize(void);
void gamecardExit(void);
/// Used to check if a gamecard has been inserted and if info could be loaded from it.
bool gamecardCheckReadyStatus(void);
bool gamecardIsReady(void);
/// Used to read data from the inserted gamecard.
/// All required handles are managed internally.
/// All required handles, changes between normal <-> secure storage areas and proper offset calculations are managed internally.
/// offset + out_size should never exceed the value returned by gamecardGetTotalRomSize().
bool gamecardStorageRead(void *out, u64 out_size, u64 offset);
bool gamecardRead(void *out, u64 out_size, u64 offset);
/// Miscellaneous functions.
bool gamecardGetHeader(GameCardHeader *out);
@ -138,13 +138,11 @@ bool gamecardGetTrimmedRomSize(u64 *out);
bool gamecardGetCertificate(FsGameCardCertificate *out);
bool gamecardGetBundledFirmwareUpdateVersion(u32 *out);
static inline u64 gamecardGetCapacity(GameCardHeader *header)
static inline u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size)
{
if (!header) return 0;
u64 capacity = 0;
switch(header->rom_size)
switch(rom_size)
{
case GameCardRomSize_1GB:
capacity = (u64)0x40000000;
@ -168,7 +166,13 @@ static inline u64 gamecardGetCapacity(GameCardHeader *header)
break;
}
return out;
return capacity;
}
static inline u64 gamecardGetCapacityFromHeader(GameCardHeader *header)
{
if (!header) return 0;
return gamecardGetCapacityFromRomSizeValue(header->rom_size);
}
#endif /* __GAMECARD_H__ */

View file

@ -277,7 +277,7 @@ static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 progr
return false;
}
rc = svcGetProcessList(&num_processes, pids, 300);
rc = svcGetProcessList((s32*)&num_processes, pids, 300);
if (R_FAILED(rc))
{
LOGFILE("svcGetProcessList failed! (0x%08X)", rc);

View file

@ -3,6 +3,8 @@
#ifndef __KEYS_H__
#define __KEYS_H__
#include <switch/types.h>
#define KEYS_FILE_PATH "sdmc:/switch/prod.keys" /* Location used by Lockpick_RCM */
bool keysLoadNcaKeyset(void);

View file

@ -3,175 +3,72 @@
#include <string.h>
#include <switch.h>
#include "ui.h"
#include "util.h"
//#include "lvgl_helper.h"
#include "utils.h"
#include "gamecard.h"
int main(int argc, char *argv[])
{
int ret = 0;
bool exitMainLoop = false;
(void)argc;
(void)argv;
/* Initialize application resources */
if (!initApplicationResources(argc, argv))
int ret = 0;
LOGFILE("nxdumptool starting.");
if (!utilsInitializeResources())
{
ret = -1;
goto out;
}
/* Main application loop */
/*lv_test();
while(appletMainLoop())
{
UIResult result = uiProcess();
switch(result)
{
case resultShowMainMenu:
uiSetState(stateMainMenu);
break;
case resultShowGameCardMenu:
uiSetState(stateGameCardMenu);
break;
case resultShowXciDumpMenu:
uiSetState(stateXciDumpMenu);
break;
case resultDumpXci:
uiSetState(stateDumpXci);
break;
case resultShowNspDumpMenu:
uiSetState(stateNspDumpMenu);
break;
case resultShowNspAppDumpMenu:
uiSetState(stateNspAppDumpMenu);
break;
case resultShowNspPatchDumpMenu:
uiSetState(stateNspPatchDumpMenu);
break;
case resultShowNspAddOnDumpMenu:
uiSetState(stateNspAddOnDumpMenu);
break;
case resultDumpNsp:
uiSetState(stateDumpNsp);
break;
case resultShowHfs0Menu:
uiSetState(stateHfs0Menu);
break;
case resultShowRawHfs0PartitionDumpMenu:
uiSetState(stateRawHfs0PartitionDumpMenu);
break;
case resultDumpRawHfs0Partition:
uiSetState(stateDumpRawHfs0Partition);
break;
case resultShowHfs0PartitionDataDumpMenu:
uiSetState(stateHfs0PartitionDataDumpMenu);
break;
case resultDumpHfs0PartitionData:
uiSetState(stateDumpHfs0PartitionData);
break;
case resultShowHfs0BrowserMenu:
uiSetState(stateHfs0BrowserMenu);
break;
case resultHfs0BrowserGetList:
uiSetState(stateHfs0BrowserGetList);
break;
case resultShowHfs0Browser:
uiSetState(stateHfs0Browser);
break;
case resultHfs0BrowserCopyFile:
uiSetState(stateHfs0BrowserCopyFile);
break;
case resultShowExeFsMenu:
uiSetState(stateExeFsMenu);
break;
case resultShowExeFsSectionDataDumpMenu:
uiSetState(stateExeFsSectionDataDumpMenu);
break;
case resultDumpExeFsSectionData:
uiSetState(stateDumpExeFsSectionData);
break;
case resultShowExeFsSectionBrowserMenu:
uiSetState(stateExeFsSectionBrowserMenu);
break;
case resultExeFsSectionBrowserGetList:
uiSetState(stateExeFsSectionBrowserGetList);
break;
case resultShowExeFsSectionBrowser:
uiSetState(stateExeFsSectionBrowser);
break;
case resultExeFsSectionBrowserCopyFile:
uiSetState(stateExeFsSectionBrowserCopyFile);
break;
case resultShowRomFsMenu:
uiSetState(stateRomFsMenu);
break;
case resultShowRomFsSectionDataDumpMenu:
uiSetState(stateRomFsSectionDataDumpMenu);
break;
case resultDumpRomFsSectionData:
uiSetState(stateDumpRomFsSectionData);
break;
case resultShowRomFsSectionBrowserMenu:
uiSetState(stateRomFsSectionBrowserMenu);
break;
case resultRomFsSectionBrowserGetEntries:
uiSetState(stateRomFsSectionBrowserGetEntries);
break;
case resultShowRomFsSectionBrowser:
uiSetState(stateRomFsSectionBrowser);
break;
case resultRomFsSectionBrowserChangeDir:
uiSetState(stateRomFsSectionBrowserChangeDir);
break;
case resultRomFsSectionBrowserCopyFile:
uiSetState(stateRomFsSectionBrowserCopyFile);
break;
case resultRomFsSectionBrowserCopyDir:
uiSetState(stateRomFsSectionBrowserCopyDir);
break;
case resultDumpGameCardCertificate:
uiSetState(stateDumpGameCardCertificate);
break;
case resultShowSdCardEmmcMenu:
uiSetState(stateSdCardEmmcMenu);
break;
case resultShowSdCardEmmcTitleMenu:
uiSetState(stateSdCardEmmcTitleMenu);
break;
case resultShowSdCardEmmcOrphanPatchAddOnMenu:
uiSetState(stateSdCardEmmcOrphanPatchAddOnMenu);
break;
case resultShowSdCardEmmcBatchModeMenu:
uiSetState(stateSdCardEmmcBatchModeMenu);
break;
case resultSdCardEmmcBatchDump:
uiSetState(stateSdCardEmmcBatchDump);
break;
case resultShowTicketMenu:
uiSetState(stateTicketMenu);
break;
case resultDumpTicket:
uiSetState(stateDumpTicket);
break;
case resultShowUpdateMenu:
uiSetState(stateUpdateMenu);
break;
case resultUpdateNSWDBXml:
uiSetState(stateUpdateNSWDBXml);
break;
case resultUpdateApplication:
uiSetState(stateUpdateApplication);
break;
case resultExit:
exitMainLoop = true;
break;
default:
break;
}
lv_task_handler();
if (lvglHelperGetExitFlag()) break;
}*/
if (exitMainLoop) break;
consoleInit(NULL);
printf("waiting...\n");
consoleUpdate(NULL);
while(appletMainLoop())
{
if (gamecardIsReady()) break;
}
u64 size = 0;
if (!gamecardGetTotalRomSize(&size))
{
printf("totalromsize failed");
goto out2;
}
printf("totalromsize: 0x%lX\n", size);
consoleUpdate(NULL);
if (!gamecardGetTrimmedRomSize(&size))
{
printf("trimmedromsize failed");
goto out2;
}
printf("trimmedromsize: 0x%lX\n", size);
out2:
consoleUpdate(NULL);
SLEEP(3);
consoleExit(NULL);
out:
/* Deinitialize application resources */
deinitApplicationResources();
utilsCloseResources();
return ret;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,357 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "nca.h"
#include "keys.h"
#include "rsa.h"
#include "utils.h"
static const u8 g_nca0KeyAreaHash[SHA256_HASH_SIZE] = {
0x9A, 0xBB, 0xD2, 0x11, 0x86, 0x00, 0x21, 0x9D, 0x7A, 0xDC, 0x5B, 0x43, 0x95, 0xF8, 0x4E, 0xFD,
0xFF, 0x6B, 0x25, 0xEF, 0x9F, 0x96, 0x85, 0x28, 0x18, 0x9E, 0x76, 0xB0, 0x92, 0xF0, 0x6A, 0xCB
};
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx);
static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset);
static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset);
size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt)
{
if (!ctx || !dst || !src || !size || !sector_size || (size % sector_size) != 0)
{
LOGFILE("Invalid parameters!");
return 0;
}
size_t i, crypt_res = 0;
u64 cur_sector = sector;
u8 *dst_u8 = (u8*)dst;
const u8 *src_u8 = (const u8*)src;
for(i = 0; i < size; i += sector_size, cur_sector++)
{
/* We have to force a sector reset on each new sector to actually enable Nintendo AES-XTS cipher tweak */
aes128XtsContextResetSector(ctx, cur_sector, true);
if (encrypt)
{
crypt_res = aes128XtsEncrypt(ctx, dst_u8 + i, src_u8 + i, sector_size);
} else {
crypt_res = aes128XtsDecrypt(ctx, dst_u8 + i, src_u8 + i, sector_size);
}
if (crypt_res != sector_size) break;
}
return i;
}
bool ncaDecryptKeyArea(NcaContext *ctx)
{
if (!ctx)
{
LOGFILE("Invalid NCA context!");
return false;
}
Result rc = 0;
const u8 *kek_src = NULL;
u8 key_count, tmp_kek[0x10] = {0};
/* Check if we're dealing with a NCA0 with a plain text key area */
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
{
memcpy(ctx->decrypted_keys, ctx->header.encrypted_keys, 0x40);
return true;
}
kek_src = keysGetKeyAreaEncryptionKeySource(ctx->header.kaek_index);
if (!kek_src)
{
LOGFILE("Unable to retrieve KAEK source for index 0x%02X!", ctx->header.kaek_index);
return false;
}
rc = splCryptoGenerateAesKek(kek_src, ctx->key_generation, 0, tmp_kek);
if (R_FAILED(rc))
{
LOGFILE("splCryptoGenerateAesKek failed! (0x%08X)", rc);
return false;
}
key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
for(u8 i = 0; i < key_count; i++)
{
rc = splCryptoGenerateAesKey(tmp_kek, ctx->header.encrypted_keys[i], ctx->decrypted_keys[i]);
if (R_FAILED(rc))
{
LOGFILE("splCryptoGenerateAesKey failed! (0x%08X)", rc);
return false;
}
}
return true;
}
bool ncaEncryptKeyArea(NcaContext *ctx)
{
if (!ctx)
{
LOGFILE("Invalid NCA context!");
return false;
}
u8 key_count;
const u8 *kaek = NULL;
Aes128Context key_area_ctx = {0};
/* Check if we're dealing with a NCA0 with a plain text key area */
if (ctx->format_version == NcaVersion_Nca0 && !ncaCheckIfVersion0KeyAreaIsEncrypted(ctx))
{
memcpy(ctx->header.encrypted_keys, ctx->decrypted_keys, 0x40);
return true;
}
kaek = keysGetKeyAreaEncryptionKey(ctx->key_generation, ctx->header.kaek_index);
if (!kaek)
{
LOGFILE("Unable to retrieve KAEK for key generation 0x%02X and KAEK index 0x%02X!", ctx->key_generation, ctx->header.kaek_index);
return false;
}
key_count = (ctx->format_version == NcaVersion_Nca0 ? 2 : 4);
aes128ContextCreate(&key_area_ctx, kaek, true);
for(u8 i = 0; i < key_count; i++) aes128EncryptBlock(&key_area_ctx, ctx->header.encrypted_keys[i], ctx->decrypted_keys[i]);
return true;
}
bool ncaDecryptHeader(NcaContext *ctx)
{
if (!ctx)
{
LOGFILE("Invalid NCA context!");
return false;
}
u32 i, magic = 0;
size_t crypt_res = 0;
const u8 *header_key = NULL;
Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0};
header_key = keysGetNcaHeaderKey();
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, false);
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false);
if (crypt_res != NCA_HEADER_LENGTH)
{
LOGFILE("Invalid output length for decrypted NCA header! (0x%X != 0x%lX)", NCA_HEADER_LENGTH, crypt_res);
return false;
}
magic = __builtin_bswap32(ctx->header.magic);
switch(magic)
{
case NCA_NCA3_MAGIC:
ctx->format_version = NcaVersion_Nca3;
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_FULL_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false);
if (crypt_res != NCA_FULL_HEADER_LENGTH)
{
LOGFILE("Error decrypting full NCA3 header!");
return false;
}
break;
case NCA_NCA2_MAGIC:
ctx->format_version = NcaVersion_Nca2;
for(i = 0; i < NCA_SECTION_HEADER_CNT; i++)
{
if (!ctx->header.fs_entries[i].enable_entry) continue;
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, false);
if (crypt_res != NCA_FS_HEADER_LENGTH)
{
LOGFILE("Error decrypting NCA2 FS section header #%u!", i);
return false;
}
}
break;
case NCA_NCA0_MAGIC:
ctx->format_version = NcaVersion_Nca0;
/* We first need to decrypt the key area from the NCA0 header in order to access its FS section headers */
if (!ncaDecryptKeyArea(ctx))
{
LOGFILE("Error decrypting key area from NCA0 header!");
return false;
}
aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_keys[0], ctx->decrypted_keys[1], false);
for(i = 0; i < NCA_SECTION_HEADER_CNT; i++)
{
if (!ctx->header.fs_entries[i].enable_entry) continue;
}
break;
default:
LOGFILE("Invalid NCA magic word! Wrong header key? (0x%08X)", magic);
return false;
}
/* Fill additional context info */
ctx->key_generation = ncaGetKeyGenerationValue(ctx);
ctx->rights_id_available = ncaCheckRightsIdAvailability(ctx);
return true;
}
bool ncaEncryptHeader(NcaContext *ctx)
{
if (!ctx)
{
LOGFILE("Invalid NCA context!");
return false;
}
u32 i;
size_t crypt_res = 0;
const u8 *header_key = NULL;
Aes128XtsContext hdr_aes_ctx = {0};
header_key = keysGetNcaHeaderKey();
aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + 0x10, true);
switch(ctx->format_version)
{
case NcaVersion_Nca3:
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_FULL_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true);
if (crypt_res != NCA_FULL_HEADER_LENGTH)
{
LOGFILE("Error encrypting full NCA3 header!");
return false;
}
break;
case NcaVersion_Nca2:
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), NCA_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true);
if (crypt_res != NCA_HEADER_LENGTH)
{
LOGFILE("Error encrypting partial NCA2 header!");
return false;
}
for(i = 0; i < NCA_SECTION_HEADER_CNT; i++)
{
if (!ctx->header.fs_entries[i].enable_entry) continue;
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header.fs_headers[i]), &(ctx->header.fs_headers[i]), NCA_FS_HEADER_LENGTH, 0, NCA_AES_XTS_SECTOR_SIZE, true);
if (crypt_res != NCA_FS_HEADER_LENGTH)
{
LOGFILE("Error encrypting NCA2 FS section header #%u!", i);
return false;
}
}
break;
case NcaVersion_Nca0:
/* There's nothing else to do */
break;
default:
LOGFILE("Invalid NCA format version! (0x%02X)", ctx->format_version);
return false;
}
return true;
}
static bool ncaCheckIfVersion0KeyAreaIsEncrypted(NcaContext *ctx)
{
if (!ctx || ctx->format_version != NcaVersion_Nca0) return false;
u8 nca0_key_area_hash[SHA256_HASH_SIZE] = {0};
sha256CalculateHash(nca0_key_area_hash, ctx->header.encrypted_keys, 0x40);
if (!memcmp(nca0_key_area_hash, g_nca0KeyAreaHash, SHA256_HASH_SIZE)) return false;
return true;
}
static void ncaUpdateAesCtrIv(u8 *ctr, u64 offset)
{
if (!ctr) return;
offset >>= 4;
for(u8 i = 0; i < 8; i++)
{
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
offset >>= 8;
}
}
static void ncaUpdateAesCtrExIv(u8 *ctr, u32 ctr_val, u64 offset)
{
if (!ctr) return;
offset >>= 4;
for(u8 i = 0; i < 8; i++)
{
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
offset >>= 8;
}
for(u8 i = 0; i < 4; i++)
{
ctr[0x8 - i - 1] = (u8)(ctr_val & 0xFF);
ctr_val >>= 8;
}
}

View file

@ -1,335 +0,0 @@
#pragma once
#ifndef __NCA_H__
#define __NCA_H__
#define NCA_HEADER_LENGTH 0x400
#define NCA_FS_HEADER_LENGTH 0x200
#define NCA_FS_HEADER_COUNT 4
#define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_SECTION_HEADER_LENGTH * NCA_SECTION_HEADER_CNT))
#define NCA_NCA0_MAGIC 0x4E434130 /* "NCA0" */
#define NCA_NCA2_MAGIC 0x4E434132 /* "NCA2" */
#define NCA_NCA3_MAGIC 0x4E434133 /* "NCA3" */
#define NCA_IVFC_MAGIC 0x49564643 /* "IVFC" */
#define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR" */
#define NCA_FS_ENTRY_BLOCK_SIZE 0x200
#define NCA_AES_XTS_SECTOR_SIZE 0x200
#define NCA_IVFC_BLOCK_SIZE(x) (1 << (x))
typedef enum {
NcaVersion_Nca0 = 0,
NcaVersion_Nca2 = 1,
NcaVersion_Nca3 = 2
} NcaVersion;
typedef enum {
NcaDistributionType_Download = 0,
NcaDistributionType_GameCard = 1
} NcaDistributionType;
typedef enum {
NcaContentType_Program = 0,
NcaContentType_Meta = 1,
NcaContentType_Control = 2,
NcaContentType_Manual = 3,
NcaContentType_Data = 4,
NcaContentType_PublicData = 5
} NcaContentType;
typedef enum {
NcaKeyGenerationOld_100_230 = 0,
NcaKeyGenerationOld_300 = 2
} NcaKeyGenerationOld;
typedef enum {
NcaKeyAreaEncryptionKeyIndex_Application = 0,
NcaKeyAreaEncryptionKeyIndex_Ocean = 1,
NcaKeyAreaEncryptionKeyIndex_System = 2
} NcaKeyAreaEncryptionKeyIndex;
/// 'NcaKeyGeneration_Latest' will always point to the last known key generation value.
typedef enum {
NcaKeyGeneration_301_302 = 3,
NcaKeyGeneration_400_410 = 4,
NcaKeyGeneration_500_510 = 5,
NcaKeyGeneration_600_610 = 6,
NcaKeyGeneration_620 = 7,
NcaKeyGeneration_700_801 = 8,
NcaKeyGeneration_810_811 = 9,
NcaKeyGeneration_900_901 = 10,
NcaKeyGeneration_910_920 = 11,
NcaKeyGeneration_Latest = NcaKeyGeneration_910_920
} NcaKeyGeneration;
typedef struct {
u32 start_block_offset; ///< Expressed in NCA_FS_ENTRY_BLOCK_SIZE blocks.
u32 end_block_offset; ///< Expressed in NCA_FS_ENTRY_BLOCK_SIZE blocks.
u8 enable_entry;
u8 reserved[0x7];
} NcaFsEntry;
typedef struct {
u8 hash[SHA256_HASH_SIZE];
} NcaFsHash;
typedef struct {
u8 key[0x10];
} NcaEncryptedKey;
typedef enum {
NcaFsType_RomFs = 0,
NcaFsType_PartitionFs = 1
} NcaFsType;
typedef enum {
NcaHashType_Auto = 0,
NcaHashType_None = 1,
NcaHashType_HierarchicalSha256 = 2, ///< Used by NcaFsType_PartitionFs.
NcaHashType_HierarchicalIntegrity = 3 ///< Used by NcaFsType_RomFs.
} NcaHashType;
typedef enum {
NcaEncryptionType_Auto = 0,
NcaEncryptionType_None = 1,
NcaEncryptionType_AesXts = 2,
NcaEncryptionType_AesCtr = 3,
NcaEncryptionType_AesCtrEx = 4
} NcaEncryptionType;
typedef struct {
u64 offset;
u64 size;
} NcaHierarchicalSha256LayerInfo;
/// Used for NcaFsType_PartitionFs and NCA0 RomFS.
typedef struct {
u8 master_hash[SHA256_HASH_SIZE];
u32 hash_block_size;
u32 layer_count;
NcaHierarchicalSha256LayerInfo hash_data_layer_info;
NcaHierarchicalSha256LayerInfo hash_target_layer_info;
} NcaHierarchicalSha256;
typedef struct {
u64 offset;
u64 size;
u32 block_size; ///< Use NCA_IVFC_CALC_BLOCK_SIZE to calculate the actual block size using this value.
u8 reserved[0x4];
} NcaHierarchicalIntegrityLayerInfo;
/// Used for NcaFsType_RomFs.
typedef struct {
u32 magic; ///< "IVFC".
u32 version;
u32 master_hash_size;
u32 layer_count;
NcaHierarchicalIntegrityLayerInfo hash_data_layer_info[5];
NcaHierarchicalIntegrityLayerInfo hash_target_layer_info;
u8 signature_salt[0x20];
u8 master_hash[0x20];
} NcaHierarchicalIntegrity;
typedef struct {
union {
struct {
///< Used if hash_type == NcaHashType_HierarchicalSha256 (NcaFsType_PartitionFs).
NcaHierarchicalSha256 hierarchical_sha256;
u8 reserved_1[0xB0];
};
struct {
///< Used if hash_type == NcaHashType_HierarchicalIntegrity (NcaFsType_RomFs).
NcaHierarchicalIntegrity hierarchical_integrity;
u8 reserved_2[0x18];
};
};
} NcaHashInfo;
typedef struct {
u32 magic; ///< "BKTR".
u32 bucket_count;
u32 entry_count;
u8 reserved[0x4];
} NcaBucketTreeHeader;
/// Only used for NcaEncryptionType_AesCtrEx (PatchRomFs).
typedef struct {
u64 indirect_offset;
u64 indirect_size;
NcaBucketTreeHeader indirect_header;
u64 aes_ctr_ex_offset;
u64 aes_ctr_ex_size;
NcaBucketTreeHeader aes_ctr_ex_header;
} NcaPatchInfo;
/// Format unknown.
typedef struct {
u8 unknown[0x30];
} NcaSparseInfo;
typedef struct {
u16 version;
u8 fs_type; ///< NcaFsType.
u8 hash_type; ///< NcaHashType.
u8 encryption_type; ///< NcaEncryptionType.
u8 reserved_1[0x3];
NcaHashInfo hash_info;
NcaPatchInfo patch_info;
union {
u8 section_ctr[0x8];
struct {
u32 generation;
u32 secure_value;
};
};
NcaSparseInfo sparse_info;
u8 reserved_2[0x88];
} NcaFsHeader;
typedef struct {
u8 main_signature[0x100]; ///< RSA-PSS signature over header with fixed key.
u8 acid_signature[0x100]; ///< RSA-PSS signature over header with key in NPDM.
u32 magic; ///< "NCA0" / "NCA2" / "NCA3".
u8 distribution_type; ///< NcaDistributionType.
u8 content_type; ///< NcaContentType.
u8 key_generation_old; ///< NcaKeyGenerationOld.
u8 kaek_index; ///< NcaKeyAreaEncryptionKeyIndex.
u64 content_size;
u64 program_id;
u32 content_index;
union {
u32 sdk_addon_version;
struct {
u8 sdk_addon_revision;
u8 sdk_addon_micro;
u8 sdk_addon_minor;
u8 sdk_addon_major;
};
};
u8 key_generation; ///< NcaKeyGeneration.
u8 main_signature_key_generation;
u8 reserved_1[0xE];
FsRightsId rights_id; ///< Used for titlekey crypto.
NcaFsEntry fs_entries[4]; ///< Start and end offsets for each NCA FS section.
NcaFsHash fs_hashes[4]; ///< SHA-256 hashes calculated over each NCA FS section header.
NcaEncryptedKey encrypted_keys[4]; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted.
u8 reserved_2[0xC0];
NcaFsHeader fs_headers[4]; /// NCA FS section headers.
} NcaHeader;
typedef struct {
u64 offset;
u64 size;
u32 section_num;
NcaFsHeader *fs_header;
u8 ctr
} NcaFsContext;
typedef struct {
u8 storage_id; ///< NcmStorageId.
NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data.
u64 gc_secure_area_base_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard.
NcmContentId id; ///< Also used to read NCA data.
char id_str[0x21];
u8 hash[0x20];
char hash_str[0x41];
u8 format_version; ///< NcaVersion.
u8 type; ///< NcmContentType. Retrieved from NcmContentInfo.
u64 size; ///< Retrieved from NcmContentInfo.
u8 key_generation; ///< NcaKeyGenerationOld / NcaKeyGeneration. Retrieved from the decrypted header.
u8 id_offset; ///< Retrieved from NcmContentInfo.
bool rights_id_available;
NcaHeader header;
bool dirty_header;
NcaEncryptedKey decrypted_keys[4];
NcaFsContext fs_contexts[4];
} NcaContext;
static inline void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out)
{
if (!size || !out) return;
*out = 0;
memcpy(out, size, 6);
}
static inline void ncaConvertU64ToNcmContentSize(const u64 *size, u8 *out)
{
if (!size || !out) return;
memcpy(out, size, 6);
}
static inline u8 ncaGetKeyGenerationValue(NcaContext *ctx)
{
if (!ctx) return 0;
return (ctx->header.key_generation > ctx->header.key_generation_old ? ctx->header.key_generation : ctx->header.key_generation_old);
}
static inline void ncaSetDownloadDistributionType(NcaContext *ctx)
{
if (!ctx || ctx->header.distribution_type == NcaDistributionType_Download) return;
ctx->header.distribution_type = NcaDistributionType_Download;
ctx->dirty_header = true;
}
static inline bool ncaCheckRightsIdAvailability(NcaContext *ctx)
{
if (!ctx) return;
bool rights_id_available = false;
for(u8 i = 0; i < 0x10; i++)
{
if (ctx->header.rights_id.c[i] != 0)
{
rights_id_available = true;
break;
}
}
return rights_id_available;
}
static inline void ncaWipeRightsId(NcaContext *ctx)
{
if (!ctx) return;
memset(ctx->header.rights_id, 0, sizeof(FsRightsId));
ctx->dirty_header = true;
}
bool ncaDecryptKeyArea(NcaContext *nca_ctx);
bool ncaEncryptKeyArea(NcaContext *nca_ctx);
bool ncaDecryptHeader(NcaContext *ctx);
bool ncaEncryptHeader(NcaContext *ctx);
#endif /* __NCA_H__ */

View file

@ -9,7 +9,9 @@
#include "rsa.h"
#include "utils.h"
/* Self-generated private key */
/* Global variables. */
/// Self-generated private key.
static const char g_rsa2048CustomAcidPrivateKey[] = "-----BEGIN RSA PRIVATE KEY-----\r\n"
"MIIEowIBAAKCAQEAvVRzt+8mE7oE4RkmSh3ws4CGlBj7uhHkfwCpPFsn4TNVdLRo\r\n"
"YYY17jQYWTtcOYPMcHxwUpgJyspGN8QGXEkJqY8jILv2eO0jBGtg7Br2afUBp6/x\r\n"
@ -38,7 +40,7 @@ static const char g_rsa2048CustomAcidPrivateKey[] = "-----BEGIN RSA PRIVATE KEY-
"n5NEG+mY4WZaOFRNiZu8+4aJI1yycXMyA22iKcU8+nN/sMAJs3Nx\r\n"
"-----END RSA PRIVATE KEY-----\r\n";
/* Self-generated public key */
/// Self-generated public key.
static const u8 g_rsa2048CustomAcidPublicKey[] = {
0xBD, 0x54, 0x73, 0xB7, 0xEF, 0x26, 0x13, 0xBA, 0x04, 0xE1, 0x19, 0x26, 0x4A, 0x1D, 0xF0, 0xB3,
0x80, 0x86, 0x94, 0x18, 0xFB, 0xBA, 0x11, 0xE4, 0x7F, 0x00, 0xA9, 0x3C, 0x5B, 0x27, 0xE1, 0x33,
@ -58,6 +60,8 @@ static const u8 g_rsa2048CustomAcidPublicKey[] = {
0x6D, 0x5D, 0x5B, 0xD2, 0x55, 0x20, 0xB9, 0x1E, 0x59, 0x13, 0x3C, 0x17, 0xC2, 0x25, 0x56, 0xC7
};
/* Function prototypes. */
static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_src, size_t h_src_size);
bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, size_t size)
@ -127,7 +131,7 @@ out:
return success;
}
const u8 rsa2048GetCustomAcidPublicKey(void)
const u8 *rsa2048GetCustomAcidPublicKey(void)
{
return g_rsa2048CustomAcidPublicKey;
}
@ -205,6 +209,8 @@ static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_s
u32 seed = 0;
size_t i, offset = 0;
u8 *data_u8 = (u8*)data;
u8 mgf1_buf[SHA256_HASH_SIZE] = {0};
u8 h_buf[RSA2048_SIGNATURE_SIZE] = {0};
@ -216,7 +222,7 @@ static void rsaCalculateMgf1AndXor(void *data, size_t data_size, const void *h_s
sha256CalculateHash(mgf1_buf, h_buf, h_src_size + 4);
for(i = offset; i < data_size && i < (offset + 0x20); i++) data[i] ^= mgf1_buf[i - offset];
for(i = offset; i < data_size && i < (offset + 0x20); i++) data_u8[i] ^= mgf1_buf[i - offset];
seed++;
offset += 0x20;

View file

@ -3,12 +3,12 @@
#ifndef __RSA_H__
#define __RSA_H__
#include <switch.h>
#include <switch/types.h>
#define RSA2048_SIGNATURE_SIZE 0x100
bool rsa2048GenerateSha256BasedCustomAcidSignature(void *dst, const void *src, size_t size);
const u8 rsa2048GetCustomAcidPublicKey(void);
const u8 *rsa2048GetCustomAcidPublicKey(void);
bool rsa2048OaepDecryptAndVerify(void *dst, size_t dst_size, const void *signature, const void *modulus, const void *exponent, size_t exponent_size, const void *label_hash, size_t *out_size);

233
source/services.c Normal file
View file

@ -0,0 +1,233 @@
#include <stdio.h>
#include <string.h>
#include <switch.h>
#include "services.h"
#include "es.h"
#include "fspusb.h"
#include "utils.h"
/* Type definitions. */
typedef bool (*ServiceCondFunction)(void *arg); /* Used to perform a runtime condition check (e.g. system version) before initializing the service */
typedef Result (*ServiceInitFunction)(void); /* Used to initialize the service */
typedef void (*ServiceCloseFunction)(void); /* Used to close the service */
typedef struct ServicesInfoEntry {
bool initialized;
char name[8];
ServiceCondFunction cond_func;
ServiceInitFunction init_func;
ServiceCloseFunction close_func;
} ServicesInfoEntry;
/* Function prototypes. */
static Result servicesNifmUserInitialize(void);
static bool servicesClkGetServiceType(void *arg);
static bool servicesSplCryptoCheckAvailability(void *arg);
static bool servicesFspUsbCheckAvailability(void *arg);
/* Global variables. */
static ServicesInfoEntry g_serviceInfo[] = {
{ false, "ncm", NULL, &ncmInitialize, &ncmExit },
{ false, "ns", NULL, &nsInitialize, &nsExit },
{ false, "csrng", NULL, &csrngInitialize, &csrngExit },
{ false, "spl", NULL, &splInitialize, &splExit },
{ false, "spl:mig", &servicesSplCryptoCheckAvailability, &splCryptoInitialize, &splCryptoExit }, /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice) */
{ false, "pm:dmnt", NULL, &pmdmntInitialize, &pmdmntExit },
{ false, "pl", NULL, &plInitialize, &plExit },
{ false, "psm", NULL, &psmInitialize, &psmExit },
{ false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit },
{ false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst */
{ false, "fsp-usb", &servicesFspUsbCheckAvailability, &fspusbInitialize, &fspusbExit }, /* Checks if fsp-usb is really available */
{ false, "es", NULL, &esInitialize, &esExit },
{ false, "set:cal", NULL, &setcalInitialize, &setcalExit }
};
static const u32 g_serviceInfoCount = MAX_ELEMENTS(g_serviceInfo);
static bool g_clkSvcUsePcv = false;
static ClkrstSession g_clkrstCpuSession = {0}, g_clkrstMemSession = {0};
bool servicesInitialize(void)
{
Result rc = 0;
bool ret = true;
for(u32 i = 0; i < g_serviceInfoCount; i++)
{
/* Check if this service has been already initialized */
if (g_serviceInfo[i].initialized) continue;
/* Check if this service depends on a condition function */
if (g_serviceInfo[i].cond_func != NULL)
{
/* Run the condition function - it will update the current service member */
/* Skip this service if the required conditions aren't met */
if (!g_serviceInfo[i].cond_func(&(g_serviceInfo[i]))) continue;
}
/* Check if this service has a valid initialize function */
if (g_serviceInfo[i].init_func == NULL) continue;
/* Initialize service */
rc = g_serviceInfo[i].init_func();
if (R_FAILED(rc))
{
utilsConsoleErrorScreen("%s: failed to initialize %s service! (0x%08X)", __func__, g_serviceInfo[i].name, rc);
ret = false;
break;
}
/* Update initialized flag */
g_serviceInfo[i].initialized = true;
}
return ret;
}
void servicesClose(void)
{
for(u32 i = 0; i < g_serviceInfoCount; i++)
{
/* Check if this service has not been initialized, or if it doesn't have a valid close function */
if (!g_serviceInfo[i].initialized || g_serviceInfo[i].close_func == NULL) continue;
/* Close service */
g_serviceInfo[i].close_func();
}
}
bool servicesCheckRunningServiceByName(const char *name)
{
if (!name || !strlen(name)) return false;
Handle handle;
SmServiceName service_name = smEncodeName(name);
Result rc = smRegisterService(&handle, service_name, false, 1);
bool running = R_FAILED(rc);
svcCloseHandle(handle);
if (!running) smUnregisterService(service_name);
return running;
}
bool servicesCheckInitializedServiceByName(const char *name)
{
if (!name || !strlen(name)) return false;
bool ret = false;
size_t name_len = strlen(name);
for(u32 i = 0; i < g_serviceInfoCount; i++)
{
if (!strncmp(g_serviceInfo[i].name, name, name_len))
{
ret = g_serviceInfo[i].initialized;
break;
}
}
return ret;
}
void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate)
{
if (g_clkSvcUsePcv)
{
pcvSetClockRate(PcvModule_CpuBus, cpu_rate);
pcvSetClockRate(PcvModule_EMC, mem_rate);
} else {
clkrstSetClockRate(&g_clkrstCpuSession, cpu_rate);
clkrstSetClockRate(&g_clkrstMemSession, mem_rate);
}
}
static Result servicesNifmUserInitialize(void)
{
return nifmInitialize(NifmServiceType_User);
}
static Result servicesClkrstInitialize(void)
{
Result rc = 0;
/* Open clkrst service handle */
rc = clkrstInitialize();
if (R_FAILED(rc)) return rc;
/* Initialize CPU and MEM clkrst sessions */
memset(&g_clkrstCpuSession, 0, sizeof(ClkrstSession));
memset(&g_clkrstMemSession, 0, sizeof(ClkrstSession));
rc = clkrstOpenSession(&g_clkrstCpuSession, PcvModuleId_CpuBus, 3);
if (R_FAILED(rc))
{
clkrstExit();
return rc;
}
rc = clkrstOpenSession(&g_clkrstMemSession, PcvModuleId_EMC, 3);
if (R_FAILED(rc))
{
clkrstCloseSession(&g_clkrstCpuSession);
clkrstExit();
}
return rc;
}
static void servicesClkrstExit(void)
{
/* Close CPU and MEM clkrst sessions */
clkrstCloseSession(&g_clkrstMemSession);
clkrstCloseSession(&g_clkrstCpuSession);
/* Close clkrst service handle */
clkrstExit();
}
static bool servicesClkGetServiceType(void *arg)
{
if (!arg) return false;
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
if (!strlen(info->name) || strncmp(info->name, "clk", 3) != 0 || info->init_func != NULL || info->close_func != NULL) return false;
/* Determine which service needs to be used to control hardware clock rates, depending on the system version */
/* This may either be pcv (sysver lower than 8.0.0) or clkrst (sysver equal or greater than 8.0.0) */
g_clkSvcUsePcv = hosversionBefore(8, 0, 0);
/* Fill service info */
sprintf(info->name, "%s", (g_clkSvcUsePcv ? "pcv" : "clkrst"));
info->init_func = (g_clkSvcUsePcv ? &pcvInitialize : &servicesClkrstInitialize);
info->close_func = (g_clkSvcUsePcv ? &pcvExit : &servicesClkrstExit);
return true;
}
static bool servicesSplCryptoCheckAvailability(void *arg)
{
if (!arg) return false;
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
if (!strlen(info->name) || strncmp(info->name, "spl:mig", 7) != 0 || info->init_func == NULL || info->close_func == NULL) return false;
/* Check if spl:mig is available (sysver equal or greater than 4.0.0) */
return !hosversionBefore(4, 0, 0);
}
static bool servicesFspUsbCheckAvailability(void *arg)
{
if (!arg) return false;
ServicesInfoEntry *info = (ServicesInfoEntry*)arg;
if (!strlen(info->name) || strncmp(info->name, "fsp-usb", 7) != 0 || info->init_func == NULL || info->close_func == NULL) return false;
/* Check if fsp-usb is actually running in the background */
return servicesCheckRunningServiceByName("fsp-usb");
}

20
source/services.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#ifndef __SERVICES_H__
#define __SERVICES_H__
/* Hardware clocks expressed in MHz */
#define CPU_CLKRT_NORMAL 1020
#define CPU_CLKRT_OVERCLOCKED 1785
#define MEM_CLKRT_NORMAL 1331
#define MEM_CLKRT_OVERCLOCKED 1600
bool servicesInitialize();
void servicesClose();
bool servicesCheckRunningServiceByName(const char *name);
bool servicesCheckInitializedServiceByName(const char *name);
void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate);
#endif /* __SERVICES_H__ */

View file

@ -15,6 +15,8 @@
#define ETICKET_DEVKEY_PUBLIC_EXPONENT 0x10001
/* Type definitions. */
/// Everything after the AES CTR is encrypted.
typedef struct {
u8 ctr[0x10];
@ -26,15 +28,19 @@ typedef struct {
u8 ghash[0x10];
} tikEticketDeviceKeyData;
/* Global variables. */
static SetCalRsa2048DeviceKey g_eTicketDeviceKey = {0};
static bool g_eTicketDeviceKeyRetrieved = false;
/* Used during the RSA-OAEP titlekey decryption stage */
/// Used during the RSA-OAEP titlekey decryption stage.
static const u8 g_nullHash[0x20] = {
0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55
};
/* Function prototypes. */
static bool tikRetrieveRightsIdsByTitleKeyType(FsRightsId **out, u32 *out_count, bool personalized);
static u8 tikGetTitleKeyTypeFromRightsId(const FsRightsId *id);
static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_type, u64 *out_size);
@ -176,7 +182,7 @@ out:
return success;
}
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TicketCommonBlock *tik_common_blk)
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TikCommonBlock *tik_common_blk)
{
if (!dst || !tik_common_blk)
{
@ -184,7 +190,6 @@ bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TicketCommonBlock *tik_commo
return false;
}
Result rc = 0;
size_t out_keydata_size = 0;
u8 out_keydata[0x100] = {0};
tikEticketDeviceKeyData *eticket_devkey = NULL;
@ -288,7 +293,7 @@ void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik, const void *titleke
if (!tik || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || !titlekey) return;
bool dev_cert = false;
TicketCommonBlock *tik_common_blk = NULL;
TikCommonBlock *tik_common_blk = NULL;
tik_common_blk = tikGetTicketCommonBlockFromTicket(tik);
if (!tik_common_blk || tik_common_blk->title_key_type != TikTitleKeyType_Personalized) return;

View file

@ -3,6 +3,7 @@
#ifndef __TIK_H__
#define __TIK_H__
#include <switch.h>
#include "signature.h"
#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES system savefiles */
@ -70,17 +71,17 @@ typedef struct {
} TikCommonBlock;
typedef struct {
SignatureRsa4096Block sig_block;
SignatureBlockRsa4096 sig_block;
TikCommonBlock tik_common_blk;
} TikSigRsa4096;
typedef struct {
SignatureRsa2048Block sig_block;
SignatureBlockRsa2048 sig_block;
TikCommonBlock tik_common_blk;
} TikSigRsa2048;
typedef struct {
SignatureEcsda240Block sig_block;
SignatureBlockEcsda240 sig_block;
TikCommonBlock tik_common_blk;
} TikSigEcsda240;
@ -112,7 +113,7 @@ static inline TikCommonBlock *tikGetTicketCommonBlockFromTicket(Ticket *tik)
bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id);
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TicketCommonBlock *tik_common_blk);
bool tikGetTitleKeyFromTicketCommonBlock(void *dst, TikCommonBlock *tik_common_blk);
static inline bool tikGetTitleKeyFromTicket(void *dst, Ticket *tik)
{

164
source/utils.c Normal file
View file

@ -0,0 +1,164 @@
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <switch.h>
//#include "freetype_helper.h"
//#include "lvgl_helper.h"
#include "services.h"
#include "gamecard.h"
#include "utils.h"
/* Global variables. */
static AppletHookCookie g_systemOverclockCookie = {0};
static Mutex g_logfileMutex = 0;
/* Function prototypes. */
static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param);
u64 utilsHidKeysAllDown(void)
{
u8 controller;
u64 keys_down = 0;
for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_down |= hidKeysDown((HidControllerID)controller);
return keys_down;
}
u64 utilsHidKeysAllHeld(void)
{
u8 controller;
u64 keys_held = 0;
for(controller = 0; controller < (u8)CONTROLLER_P1_AUTO; controller++) keys_held |= hidKeysHeld((HidControllerID)controller);
return keys_held;
}
void utilsWaitForButtonPress(void)
{
u64 flag, keys_down;
/* Don't consider touch screen presses nor stick movement as button inputs */
flag = ~(KEY_TOUCH | KEY_LSTICK_LEFT | KEY_LSTICK_RIGHT | KEY_LSTICK_UP | KEY_LSTICK_DOWN | KEY_RSTICK_LEFT | KEY_RSTICK_RIGHT | KEY_RSTICK_UP | KEY_RSTICK_DOWN);
while(appletMainLoop())
{
hidScanInput();
keys_down = utilsHidKeysAllDown();
if (keys_down & flag) break;
}
}
void utilsConsoleErrorScreen(const char *fmt, ...)
{
consoleInit(NULL);
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
printf("\nPress any button to exit.\n");
consoleUpdate(NULL);
utilsWaitForButtonPress();
consoleExit(NULL);
}
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...)
{
mutexLock(&g_logfileMutex);
va_list args;
FILE *logfile = NULL;
logfile = fopen(APP_BASE_PATH "nxdumptool.log", "a+");
if (!logfile) goto out;
time_t now = time(NULL);
struct tm *ts = localtime(&now);
fprintf(logfile, "%d/%d/%d %d:%d:%d -> %s: ", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, func_name);
va_start(args, fmt);
vfprintf(logfile, fmt, args);
va_end(args);
fprintf(logfile, "\r\n");
fclose(logfile);
out:
mutexUnlock(&g_logfileMutex);
}
void utilsOverclockSystem(bool restore)
{
u32 cpuClkRate = ((restore ? CPU_CLKRT_NORMAL : CPU_CLKRT_OVERCLOCKED) * 1000000);
u32 memClkRate = ((restore ? MEM_CLKRT_NORMAL : MEM_CLKRT_OVERCLOCKED) * 1000000);
servicesChangeHardwareClockRates(cpuClkRate, memClkRate);
}
bool utilsInitializeResources(void)
{
/* Initialize all needed services */
if (!servicesInitialize()) return false;
/* Initialize FreeType */
//if (!freeTypeHelperInitialize()) return false;
/* Initialize LVGL */
//if (!lvglHelperInitialize()) return false;
/* Overclock system */
utilsOverclockSystem(false);
/* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked) */
appletHook(&g_systemOverclockCookie, utilsOverclockSystemAppletHook, NULL);
/* Initialize gamecard */
Result rc = gamecardInitialize();
if (R_FAILED(rc))
{
utilsConsoleErrorScreen("gamecard fail\n");
return false;
}
return true;
}
void utilsCloseResources(void)
{
/* Deinitialize gamecard */
gamecardExit();
/* Unset our overclock applet hook */
appletUnhook(&g_systemOverclockCookie);
/* Restore hardware clocks */
utilsOverclockSystem(true);
/* Free LVGL resources */
//lvglHelperExit();
/* Free FreeType resouces */
//freeTypeHelperExit();
/* Close initialized services */
servicesClose();
}
static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param)
{
(void)param;
if (hook != AppletHookType_OnOperationMode && hook != AppletHookType_OnPerformanceMode) return;
/* To do: read config here to actually know the value to use with utilsOverclockSystem */
utilsOverclockSystem(true);
}

66
source/utils.h Normal file
View file

@ -0,0 +1,66 @@
#pragma once
#ifndef __UTILS_H__
#define __UTILS_H__
#include <switch.h>
#define APP_BASE_PATH "sdmc:/switch/nxdumptool/"
#define LOGFILE(fmt, ...) utilsWriteLogMessage(__func__, fmt, ##__VA_ARGS__)
#define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member)
#define SLEEP(x) svcSleepThread((x) * (u64)1000000000)
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0])))
#define ROUND_UP(x, y) ((x) + (((y) - ((x) % (y))) % (y))) /* Aligns 'x' bytes to a 'y' bytes boundary. */
typedef enum {
UtilsCustomFirmwareType_Atmosphere = 0,
UtilsCustomFirmwareType_SXOS = 1,
UtilsCustomFirmwareType_ReiNX = 2
} UtilsCustomFirmwareType;
typedef struct {
u16 major : 6;
u16 minor : 6;
u16 micro : 4;
u16 bugfix;
} TitleVersion;
u64 utilsHidKeysAllDown(void);
u64 utilsHidKeysAllHeld(void);
void utilsWaitForButtonPress(void);
void utilsConsoleErrorScreen(const char *fmt, ...);
void utilsWriteLogMessage(const char *func_name, const char *fmt, ...);
void utilsOverclockSystem(bool restore);
bool utilsInitializeResources(void);
void utilsCloseResources(void);
static inline FsStorage *utilsGetEmmcBisSystemStorage(void)
{
return NULL;
}
static inline u8 utilsGetCustomFirmwareType(void)
{
return UtilsCustomFirmwareType_Atmosphere;
}
#endif /* __UTILS_H__ */