forked from Mirror/GodMode9
Full / proper verification for romFS in NCCH
This commit is contained in:
parent
5c138e1219
commit
8f24ccec0c
@ -1,7 +1,44 @@
|
||||
#include "romfs.h"
|
||||
#include "utf.h"
|
||||
|
||||
// validate header by checking offsets and sizes
|
||||
|
||||
// get lvl datablock offset from IVC (zero for total size)
|
||||
// see: https://github.com/profi200/Project_CTR/blob/046bb359ee95423938886dbf477d00690aaecd3e/ctrtool/ivfc.c#L88-L111
|
||||
u64 GetRomFsLvOffset(RomFsIvfcHeader* ivfc, u32 lvl) {
|
||||
// regardless of lvl given, we calculate them all
|
||||
u64 offset[4];
|
||||
|
||||
// lvl1/2/3 offset and size
|
||||
offset[3] = align(sizeof(RomFsIvfcHeader) + ivfc->size_masterhash, 1 << ivfc->log_lvl3);
|
||||
offset[1] = offset[3] + align(ivfc->size_lvl3, 1 << ivfc->log_lvl3);
|
||||
offset[2] = offset[1] + align(ivfc->size_lvl1, 1 << ivfc->log_lvl1);
|
||||
offset[0] = offset[2] + align(ivfc->size_lvl2, 1 << ivfc->log_lvl2); // (min) size
|
||||
|
||||
return (lvl <= 3) ? offset[lvl] : 0;
|
||||
}
|
||||
|
||||
// validate IVFC header by checking offsets and hash sizes
|
||||
u32 ValidateRomFsHeader(RomFsIvfcHeader* ivfc, u32 max_size) {
|
||||
u8 magic[] = { ROMFS_MAGIC };
|
||||
|
||||
// check magic number
|
||||
if (memcmp(magic, ivfc->magic, sizeof(magic)) != 0)
|
||||
return 1;
|
||||
|
||||
// check hash block sizes vs data block sizes
|
||||
if ((((ivfc->size_masterhash / 0x20) << ivfc->log_lvl1) < ivfc->size_lvl1) || // lvl1
|
||||
(((ivfc->size_lvl1 / 0x20) << ivfc->log_lvl2) < ivfc->size_lvl2) || // lvl2
|
||||
(((ivfc->size_lvl2 / 0x20) << ivfc->log_lvl3) < ivfc->size_lvl3)) // lvl3
|
||||
return 1;
|
||||
|
||||
// check size if given
|
||||
if (max_size && (max_size < GetRomFsLvOffset(ivfc, 0)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// validate lvl3 header by checking offsets and sizes
|
||||
u32 ValidateLv3Header(RomFsLv3Header* lv3, u32 max_size) {
|
||||
return ((lv3->size_header == 0x28) &&
|
||||
(lv3->offset_dirhash >= lv3->size_header) &&
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define ROMFS_MAGIC 0x49, 0x56, 0x46, 0x43, 0x00, 0x00, 0x01, 0x00
|
||||
#define ROMFS_MAGIC 0x49, 0x56, 0x46, 0x43, 0x00, 0x00, 0x01, 0x00 // "IVFC" 0x0001000
|
||||
#define OFFSET_LV3 0x1000
|
||||
|
||||
#define LV3_GET_DIR(offset, idx) \
|
||||
@ -26,6 +26,26 @@
|
||||
((idx)->dirmeta + (dm)->offset_parent : NULL)))
|
||||
|
||||
|
||||
typedef struct {
|
||||
u8 magic[8];
|
||||
u32 size_masterhash;
|
||||
u64 offset_lvl1; // seems to be useless?
|
||||
u64 size_lvl1;
|
||||
u32 log_lvl1;
|
||||
u8 reserved0[4];
|
||||
u64 offset_lvl2; // seems to be useless?
|
||||
u64 size_lvl2;
|
||||
u32 log_lvl2;
|
||||
u8 reserved1[4];
|
||||
u64 offset_lvl3; // seems to be useless?
|
||||
u64 size_lvl3;
|
||||
u32 log_lvl3;
|
||||
u8 reserved2[4];
|
||||
u32 unknown0;
|
||||
u32 unknown1;
|
||||
u8 padding[4]; // masterhash follows
|
||||
} __attribute__((packed)) RomFsIvfcHeader;
|
||||
|
||||
typedef struct {
|
||||
u32 size_header;
|
||||
u32 offset_dirhash;
|
||||
@ -72,6 +92,9 @@ typedef struct {
|
||||
u32 size_filemeta;
|
||||
} __attribute__((packed)) RomFsLv3Index;
|
||||
|
||||
|
||||
u64 GetRomFsLvOffset(RomFsIvfcHeader* ivfc, u32 lvl);
|
||||
u32 ValidateRomFsHeader(RomFsIvfcHeader* ivfc, u32 max_size);
|
||||
u32 ValidateLv3Header(RomFsLv3Header* lv3, u32 max_size);
|
||||
u32 BuildLv3Index(RomFsLv3Index* index, u8* lv3);
|
||||
u32 HashLv3Path(u16* wname, u32 name_len, u32 offset_parent);
|
||||
|
@ -1479,7 +1479,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
||||
const char* path = current_dir->entry[i].path;
|
||||
if (!current_dir->entry[i].marked)
|
||||
continue;
|
||||
if (!(filetype & (GAME_CIA|GAME_TMD)) &&
|
||||
if (!(filetype & (GAME_CIA|GAME_TMD|GAME_NCSD|GAME_NCCH)) &&
|
||||
!ShowProgress(n_processed++, n_marked, path)) break;
|
||||
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
|
||||
n_other++;
|
||||
@ -1492,7 +1492,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
||||
char lpathstr[32+1];
|
||||
TruncateString(lpathstr, path, 32, 8);
|
||||
if (ShowPrompt(true, "%s\nVerification failed\n \nContinue?", lpathstr)) {
|
||||
if (!(filetype & (GAME_CIA|GAME_TMD))) ShowProgress(0, n_marked, path); // restart progress bar
|
||||
if (!(filetype & (GAME_CIA|GAME_TMD|GAME_NCSD|GAME_NCCH)))
|
||||
ShowProgress(0, n_marked, path); // restart progress bar
|
||||
continue;
|
||||
} else break;
|
||||
}
|
||||
|
@ -376,6 +376,7 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) {
|
||||
}
|
||||
|
||||
// thorough exefs verification (workaround for Process9)
|
||||
if (!ShowProgress(0, 0, path)) return 1;
|
||||
if ((ncch.size_exefs > 0) && (memcmp(exthdr.name, "Process9", 8) != 0)) {
|
||||
for (u32 i = 0; !ver_exefs && (i < 10); i++) {
|
||||
ExeFsFileHeader* exefile = exefs.files + i;
|
||||
@ -385,6 +386,93 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) {
|
||||
ver_exefs = CheckNcchHash(hash, &file, exefile->size, offset, &ncch, &exefs);
|
||||
}
|
||||
}
|
||||
|
||||
// thorough romfs verification
|
||||
if (!ver_romfs && (ncch.size_romfs > 0)) {
|
||||
UINT btr;
|
||||
|
||||
// load ivfc header
|
||||
RomFsIvfcHeader ivfc;
|
||||
fvx_lseek(&file, offset + (ncch.offset_romfs * NCCH_MEDIA_UNIT));
|
||||
if ((fvx_read(&file, &ivfc, sizeof(RomFsIvfcHeader), &btr) != FR_OK) ||
|
||||
(DecryptNcch((u8*) &ivfc, ncch.offset_romfs * NCCH_MEDIA_UNIT, sizeof(RomFsIvfcHeader), &ncch, NULL) != 0) )
|
||||
ver_romfs = 1;
|
||||
|
||||
// load data
|
||||
u64 lvl1_size = 0;
|
||||
u64 lvl2_size = 0;
|
||||
u8* masterhash = NULL;
|
||||
u8* lvl1_data = NULL;
|
||||
u8* lvl2_data = NULL;
|
||||
if (!ver_romfs && (ValidateRomFsHeader(&ivfc, ncch.size_romfs * NCCH_MEDIA_UNIT) == 0)) {
|
||||
// load masterhash(es)
|
||||
masterhash = malloc(ivfc.size_masterhash);
|
||||
if (masterhash) {
|
||||
u64 offset_add = (ncch.offset_romfs * NCCH_MEDIA_UNIT) + sizeof(RomFsIvfcHeader);
|
||||
fvx_lseek(&file, offset + offset_add);
|
||||
if ((fvx_read(&file, masterhash, ivfc.size_masterhash, &btr) != FR_OK) ||
|
||||
(DecryptNcch(masterhash, offset_add, ivfc.size_masterhash, &ncch, NULL) != 0))
|
||||
ver_romfs = 1;
|
||||
}
|
||||
|
||||
// load lvl1
|
||||
lvl1_size = align(ivfc.size_lvl1, 1 << ivfc.log_lvl1);
|
||||
lvl1_data = malloc(lvl1_size);
|
||||
if (lvl1_data) {
|
||||
u64 offset_add = (ncch.offset_romfs * NCCH_MEDIA_UNIT) + GetRomFsLvOffset(&ivfc, 1);
|
||||
fvx_lseek(&file, offset + offset_add);
|
||||
if ((fvx_read(&file, lvl1_data, lvl1_size, &btr) != FR_OK) ||
|
||||
(DecryptNcch(lvl1_data, offset_add, lvl1_size, &ncch, NULL) != 0))
|
||||
ver_romfs = 1;
|
||||
}
|
||||
|
||||
// load lvl2
|
||||
lvl2_size = align(ivfc.size_lvl2, 1 << ivfc.log_lvl2);
|
||||
lvl2_data = malloc(lvl2_size);
|
||||
if (lvl2_data) {
|
||||
u64 offset_add = (ncch.offset_romfs * NCCH_MEDIA_UNIT) + GetRomFsLvOffset(&ivfc, 2);
|
||||
fvx_lseek(&file, offset + offset_add);
|
||||
if ((fvx_read(&file, lvl2_data, lvl2_size, &btr) != FR_OK) ||
|
||||
(DecryptNcch(lvl2_data, offset_add, lvl2_size, &ncch, NULL) != 0))
|
||||
ver_romfs = 1;
|
||||
}
|
||||
|
||||
// check mallocs
|
||||
if (!masterhash || !lvl1_data || !lvl2_data)
|
||||
ver_romfs = 1; // should never happen
|
||||
}
|
||||
|
||||
// actual verification
|
||||
if (!ver_romfs) {
|
||||
// verify lvl1
|
||||
u32 n_blocks = lvl1_size >> ivfc.log_lvl1;
|
||||
u32 block_log = ivfc.log_lvl1;
|
||||
for (u32 i = 0; !ver_romfs && (i < n_blocks); i++)
|
||||
ver_romfs = (u32) sha_cmp(masterhash + (i*0x20), lvl1_data + (i<<block_log), 1<<block_log, SHA256_MODE);
|
||||
|
||||
// verify lvl2
|
||||
n_blocks = lvl2_size >> ivfc.log_lvl2;
|
||||
block_log = ivfc.log_lvl2;
|
||||
for (u32 i = 0; !ver_romfs && (i < n_blocks); i++) {
|
||||
ver_romfs = sha_cmp(lvl1_data + (i*0x20), lvl2_data + (i<<block_log), 1<<block_log, SHA256_MODE);
|
||||
}
|
||||
|
||||
// lvl3 verification (this will take long)
|
||||
u64 offset_add = (ncch.offset_romfs * NCCH_MEDIA_UNIT) + GetRomFsLvOffset(&ivfc, 3);
|
||||
n_blocks = align(ivfc.size_lvl3, 1 << ivfc.log_lvl3) >> ivfc.log_lvl3;
|
||||
block_log = ivfc.log_lvl3;
|
||||
fvx_lseek(&file, offset + offset_add);
|
||||
for (u32 i = 0; !ver_romfs && (i < n_blocks); i++) {
|
||||
ver_romfs = CheckNcchHash(lvl2_data + (i*0x20), &file, 1 << block_log, offset, &ncch, NULL);
|
||||
offset_add += 1 << block_log;
|
||||
if (!(i % 16) && !ShowProgress(i+1, n_blocks, path)) ver_romfs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (masterhash) free(masterhash);
|
||||
if (lvl1_data) free(lvl1_data);
|
||||
if (lvl2_data) free(lvl2_data);
|
||||
}
|
||||
|
||||
if (!offset && (ver_exthdr|ver_exefs|ver_romfs)) { // verification summary
|
||||
ShowPrompt(false, "%s\nNCCH verification failed:\nExtHdr/ExeFS/RomFS: %s/%s/%s", pathstr,
|
||||
|
@ -875,22 +875,20 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||
if (!BuildVGameExeFsDir()) return false;
|
||||
} else if ((vdir->flags & VFLAG_ROMFS) && (offset_romfs != vdir->offset)) {
|
||||
offset_nitro = (u64) -1; // mutually exclusive
|
||||
// validate romFS magic
|
||||
u8 magic[] = { ROMFS_MAGIC };
|
||||
u8 header[sizeof(magic)];
|
||||
if ((ReadNcchImageBytes(header, vdir->offset, sizeof(magic)) != 0) ||
|
||||
(memcmp(magic, header, sizeof(magic)) != 0))
|
||||
// validate ivfc header
|
||||
RomFsIvfcHeader ivfc;
|
||||
if ((ReadNcchImageBytes(&ivfc, vdir->offset, sizeof(RomFsIvfcHeader)) != 0) ||
|
||||
(ValidateRomFsHeader(&ivfc, 0) != 0))
|
||||
return false;
|
||||
// validate lv3 header
|
||||
RomFsLv3Header lv3;
|
||||
for (u32 i = 1; i < 8; i++) {
|
||||
offset_lv3 = vdir->offset + (i*OFFSET_LV3);
|
||||
if (ReadNcchImageBytes(&lv3, offset_lv3, sizeof(RomFsLv3Header)) != 0)
|
||||
return false;
|
||||
if (ValidateLv3Header(&lv3, 0) == 0)
|
||||
break;
|
||||
offset_lv3 = vdir->offset + GetRomFsLvOffset(&ivfc, 3);
|
||||
if ((ReadNcchImageBytes(&lv3, offset_lv3, sizeof(RomFsLv3Header)) != 0) ||
|
||||
(ValidateLv3Header(&lv3, 0) != 0)) {
|
||||
offset_lv3 = (u64) -1;
|
||||
return false;
|
||||
}
|
||||
// set up filesystem buffer
|
||||
if (vgame_fs_buffer) free(vgame_fs_buffer);
|
||||
vgame_fs_buffer = malloc(lv3.offset_filedata);
|
||||
if (!vgame_fs_buffer || (offset_lv3 == (u64) -1) ||
|
||||
|
Loading…
x
Reference in New Issue
Block a user