mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Full / proper verification for romFS in NCCH
This commit is contained in:
parent
5c138e1219
commit
8f24ccec0c
@ -1,7 +1,44 @@
|
|||||||
#include "romfs.h"
|
#include "romfs.h"
|
||||||
#include "utf.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) {
|
u32 ValidateLv3Header(RomFsLv3Header* lv3, u32 max_size) {
|
||||||
return ((lv3->size_header == 0x28) &&
|
return ((lv3->size_header == 0x28) &&
|
||||||
(lv3->offset_dirhash >= lv3->size_header) &&
|
(lv3->offset_dirhash >= lv3->size_header) &&
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#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 OFFSET_LV3 0x1000
|
||||||
|
|
||||||
#define LV3_GET_DIR(offset, idx) \
|
#define LV3_GET_DIR(offset, idx) \
|
||||||
@ -26,6 +26,26 @@
|
|||||||
((idx)->dirmeta + (dm)->offset_parent : NULL)))
|
((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 {
|
typedef struct {
|
||||||
u32 size_header;
|
u32 size_header;
|
||||||
u32 offset_dirhash;
|
u32 offset_dirhash;
|
||||||
@ -72,6 +92,9 @@ typedef struct {
|
|||||||
u32 size_filemeta;
|
u32 size_filemeta;
|
||||||
} __attribute__((packed)) RomFsLv3Index;
|
} __attribute__((packed)) RomFsLv3Index;
|
||||||
|
|
||||||
|
|
||||||
|
u64 GetRomFsLvOffset(RomFsIvfcHeader* ivfc, u32 lvl);
|
||||||
|
u32 ValidateRomFsHeader(RomFsIvfcHeader* ivfc, u32 max_size);
|
||||||
u32 ValidateLv3Header(RomFsLv3Header* lv3, u32 max_size);
|
u32 ValidateLv3Header(RomFsLv3Header* lv3, u32 max_size);
|
||||||
u32 BuildLv3Index(RomFsLv3Index* index, u8* lv3);
|
u32 BuildLv3Index(RomFsLv3Index* index, u8* lv3);
|
||||||
u32 HashLv3Path(u16* wname, u32 name_len, u32 offset_parent);
|
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;
|
const char* path = current_dir->entry[i].path;
|
||||||
if (!current_dir->entry[i].marked)
|
if (!current_dir->entry[i].marked)
|
||||||
continue;
|
continue;
|
||||||
if (!(filetype & (GAME_CIA|GAME_TMD)) &&
|
if (!(filetype & (GAME_CIA|GAME_TMD|GAME_NCSD|GAME_NCCH)) &&
|
||||||
!ShowProgress(n_processed++, n_marked, path)) break;
|
!ShowProgress(n_processed++, n_marked, path)) break;
|
||||||
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
|
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
|
||||||
n_other++;
|
n_other++;
|
||||||
@ -1492,7 +1492,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
|
|||||||
char lpathstr[32+1];
|
char lpathstr[32+1];
|
||||||
TruncateString(lpathstr, path, 32, 8);
|
TruncateString(lpathstr, path, 32, 8);
|
||||||
if (ShowPrompt(true, "%s\nVerification failed\n \nContinue?", lpathstr)) {
|
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;
|
continue;
|
||||||
} else break;
|
} else break;
|
||||||
}
|
}
|
||||||
|
@ -376,6 +376,7 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// thorough exefs verification (workaround for Process9)
|
// thorough exefs verification (workaround for Process9)
|
||||||
|
if (!ShowProgress(0, 0, path)) return 1;
|
||||||
if ((ncch.size_exefs > 0) && (memcmp(exthdr.name, "Process9", 8) != 0)) {
|
if ((ncch.size_exefs > 0) && (memcmp(exthdr.name, "Process9", 8) != 0)) {
|
||||||
for (u32 i = 0; !ver_exefs && (i < 10); i++) {
|
for (u32 i = 0; !ver_exefs && (i < 10); i++) {
|
||||||
ExeFsFileHeader* exefile = exefs.files + 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);
|
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
|
if (!offset && (ver_exthdr|ver_exefs|ver_romfs)) { // verification summary
|
||||||
ShowPrompt(false, "%s\nNCCH verification failed:\nExtHdr/ExeFS/RomFS: %s/%s/%s", pathstr,
|
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;
|
if (!BuildVGameExeFsDir()) return false;
|
||||||
} else if ((vdir->flags & VFLAG_ROMFS) && (offset_romfs != vdir->offset)) {
|
} else if ((vdir->flags & VFLAG_ROMFS) && (offset_romfs != vdir->offset)) {
|
||||||
offset_nitro = (u64) -1; // mutually exclusive
|
offset_nitro = (u64) -1; // mutually exclusive
|
||||||
// validate romFS magic
|
// validate ivfc header
|
||||||
u8 magic[] = { ROMFS_MAGIC };
|
RomFsIvfcHeader ivfc;
|
||||||
u8 header[sizeof(magic)];
|
if ((ReadNcchImageBytes(&ivfc, vdir->offset, sizeof(RomFsIvfcHeader)) != 0) ||
|
||||||
if ((ReadNcchImageBytes(header, vdir->offset, sizeof(magic)) != 0) ||
|
(ValidateRomFsHeader(&ivfc, 0) != 0))
|
||||||
(memcmp(magic, header, sizeof(magic)) != 0))
|
|
||||||
return false;
|
return false;
|
||||||
// validate lv3 header
|
// validate lv3 header
|
||||||
RomFsLv3Header lv3;
|
RomFsLv3Header lv3;
|
||||||
for (u32 i = 1; i < 8; i++) {
|
offset_lv3 = vdir->offset + GetRomFsLvOffset(&ivfc, 3);
|
||||||
offset_lv3 = vdir->offset + (i*OFFSET_LV3);
|
if ((ReadNcchImageBytes(&lv3, offset_lv3, sizeof(RomFsLv3Header)) != 0) ||
|
||||||
if (ReadNcchImageBytes(&lv3, offset_lv3, sizeof(RomFsLv3Header)) != 0)
|
(ValidateLv3Header(&lv3, 0) != 0)) {
|
||||||
return false;
|
|
||||||
if (ValidateLv3Header(&lv3, 0) == 0)
|
|
||||||
break;
|
|
||||||
offset_lv3 = (u64) -1;
|
offset_lv3 = (u64) -1;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
// set up filesystem buffer
|
||||||
if (vgame_fs_buffer) free(vgame_fs_buffer);
|
if (vgame_fs_buffer) free(vgame_fs_buffer);
|
||||||
vgame_fs_buffer = malloc(lv3.offset_filedata);
|
vgame_fs_buffer = malloc(lv3.offset_filedata);
|
||||||
if (!vgame_fs_buffer || (offset_lv3 == (u64) -1) ||
|
if (!vgame_fs_buffer || (offset_lv3 == (u64) -1) ||
|
||||||
|
Loading…
x
Reference in New Issue
Block a user