From 64b1275c39a9a59e6504ce3601162f6af185a9da Mon Sep 17 00:00:00 2001 From: d0k3 Date: Wed, 1 Feb 2017 23:41:05 +0100 Subject: [PATCH] First look version - some stuff not working yet .. won't write to NAND yet --- Makefile | 4 +- source/common/common.h | 10 +- source/crypto/sha.c | 6 + source/crypto/sha.h | 1 + source/fatfs/ffconf.h | 4 +- source/fatfs/qff.c | 4 - source/installer.c | 236 ++++++++++++++++++++++++++++++++++++++ source/installer.h | 6 + source/main.c | 20 ++++ source/nand/nand.c | 37 ++++-- source/nand/nand.h | 4 + source/safety/safewrite.c | 32 ++++++ source/safety/safewrite.h | 7 ++ source/safety/validator.c | 94 +++++++++++++++ source/safety/validator.h | 7 ++ 15 files changed, 451 insertions(+), 21 deletions(-) create mode 100644 source/installer.c create mode 100644 source/installer.h create mode 100644 source/main.c create mode 100644 source/safety/safewrite.c create mode 100644 source/safety/safewrite.h create mode 100644 source/safety/validator.c create mode 100644 source/safety/validator.h diff --git a/Makefile b/Makefile index ebef87c..e689a44 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,9 @@ include $(DEVKITARM)/ds_rules #--------------------------------------------------------------------------------- export TARGET := SafeSigHaxInstaller BUILD := build -SOURCES := source source/common source/fs source/crypto source/fatfs source/nand source/abstraction +SOURCES := source source/common source/fs source/crypto source/fatfs source/nand source/safety source/abstraction DATA := data -INCLUDES := source source/common source/font source/fs source/crypto source/fatfs source/nand +INCLUDES := source source/common source/font source/fs source/crypto source/fatfs source/nand source/safety #--------------------------------------------------------------------------------- # options for code generation diff --git a/source/common/common.h b/source/common/common.h index 9968c7c..e43785f 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -43,11 +43,13 @@ // input / output paths #define INPUT_PATH "0:/sighax" -// buffer area defines (main & special for NAND) -#define MAIN_BUFFER ((u8*) 0x21000000) -#define MAIN_BUFFER_SIZE (0x400000) -#define NAND_BUFFER ((u8*) 0x25000000) +// buffer area defines (big buffer for firm) +#define WORK_BUFFER ((u8*) 0x21000000) +#define WORK_BUFFER_SIZE (0x100000) +#define NAND_BUFFER ((u8*) 0x22000000) #define NAND_BUFFER_SIZE (0x100000) +#define FIRM_BUFFER ((u8*) 0x23000000) +#define FIRM_BUFFER_SIZE (0x400000) inline u32 strchrcount(const char* str, char symbol) { u32 count = 0; diff --git a/source/crypto/sha.c b/source/crypto/sha.c index a3b9bbd..7db1308 100644 --- a/source/crypto/sha.c +++ b/source/crypto/sha.c @@ -36,3 +36,9 @@ void sha_quick(void* res, const void* src, u32 size, u32 mode) { sha_update(src, size); sha_get(res); } + +int sha_cmp(const void* sha, const void* src, u32 size, u32 mode) { + u8 res[0x20]; + sha_quick(res, src, size, mode); + return memcmp(sha, res, 0x20); +} diff --git a/source/crypto/sha.h b/source/crypto/sha.h index d4347d4..10782da 100644 --- a/source/crypto/sha.h +++ b/source/crypto/sha.h @@ -26,3 +26,4 @@ void sha_init(u32 mode); void sha_update(const void* src, u32 size); void sha_get(void* res); void sha_quick(void* res, const void* src, u32 size, u32 mode); +int sha_cmp(const void* sha, const void* src, u32 size, u32 mode); diff --git a/source/fatfs/ffconf.h b/source/fatfs/ffconf.h index e1f75e6..9ab7e88 100644 --- a/source/fatfs/ffconf.h +++ b/source/fatfs/ffconf.h @@ -39,7 +39,7 @@ / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ -#define _USE_MKFS 1 +#define _USE_MKFS 0 /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ @@ -160,7 +160,7 @@ / the drive ID strings are: A-Z and 0-9. */ -#define _MULTI_PARTITION 1 +#define _MULTI_PARTITION 0 /* This option switches support of multi-partition on a physical drive. / By default (0), each logical drive number is bound to the same physical drive / number and only an FAT volume found on the physical drive will be mounted. diff --git a/source/fatfs/qff.c b/source/fatfs/qff.c index a495ee6..3cf2ba5 100644 --- a/source/fatfs/qff.c +++ b/source/fatfs/qff.c @@ -26,8 +26,6 @@ FRESULT f_qread (const TCHAR* path, void* buff, FSIZE_t ofs, UINT btr, UINT* br) res = f_read(&fp, buff, btr, br); f_close(&fp); - if (*br != btr) res = FR_DENIED; // hacky - return res; } @@ -49,8 +47,6 @@ FRESULT f_qwrite (const TCHAR* path, const void* buff, FSIZE_t ofs, UINT btw, UI res = f_write(&fp, buff, btw, bw); f_close(&fp); - if (*bw != btw) res = FR_DENIED; // hacky - return res; } diff --git a/source/installer.c b/source/installer.c new file mode 100644 index 0000000..93657d5 --- /dev/null +++ b/source/installer.c @@ -0,0 +1,236 @@ +#include "installer.h" +#include "validator.h" +#include "safewrite.h" +#include "nand.h" +#include "ui.h" +#include "qff.h" + +#define COLOR_STATUS(s) ((s == STATUS_GREEN) ? COLOR_BRIGHTGREEN : (s == STATUS_YELLOW) ? COLOR_BRIGHTYELLOW : (s == STATUS_RED) ? COLOR_RED : COLOR_DARKGREY) + +#define MIN_SD_FREE (16 * 1024 * 1024) // 16MB +#define FIRM_NAND_OFFSET 0x0B130000 +#define FIRM_NAND_SIZE 0x800000 + +#define NAME_SIGHAXFIRM (INPUT_PATH "/sighaxfirm.bin") +#define NAME_SIGHAXFIRMSHA (INPUT_PATH "/sighaxfirm.bin.sha") +#define NAME_SECTOR0x96 (INPUT_PATH "/secret_sector.bin") +#define NAME_FIRMBACKUP (INPUT_PATH "/firm0firm1.bak") +#define NAME_SECTORBACKUP (INPUT_PATH "/sector0x96.bak") + +#define STATUS_GREY -1 +#define STATUS_GREEN 0 +#define STATUS_YELLOW 1 +#define STATUS_RED 2 + +static int statusA9lh = STATUS_GREY; +static int statusSdCard = STATUS_GREY; +static int statusFirm = STATUS_GREY; +static int statusSector = STATUS_GREY; +static int statusCrypto = STATUS_GREY; +static int statusBackup = STATUS_GREY; +static int statusInstall = STATUS_GREY; +static char msgA9lh[64] = "not started"; +static char msgSdCard[64] = "not started"; +static char msgFirm[64] = "not started"; +static char msgSector[64] = "not started"; +static char msgCrypto[64] = "not started"; +static char msgBackup[64] = "not started"; +static char msgInstall[64] = "not started"; + +u32 ShowInstallerStatus(void) { + const u32 pos_xb = 10; + const u32 pos_x0 = pos_xb; + const u32 pos_x1 = pos_x0 + (16*FONT_WIDTH_EXT); + const u32 pos_yb = 10; + const u32 pos_y0 = pos_yb + 20; + + DrawStringF(BOT_SCREEN, pos_xb, pos_yb, COLOR_STD_FONT, COLOR_STD_BG, "SafeSigHaxInstaller v" VERSION); + + DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + 0, COLOR_STD_FONT, COLOR_STD_BG, "ARM9LoaderHax :"); + DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + 10, COLOR_STD_FONT, COLOR_STD_BG, "MicroSD Card :"); + DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + 20, COLOR_STD_FONT, COLOR_STD_BG, "Sighaxed FIRM :"); + DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + 30, COLOR_STD_FONT, COLOR_STD_BG, "Secret Sector :"); + DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + 40, COLOR_STD_FONT, COLOR_STD_BG, "Crypto Status :"); + DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + 50, COLOR_STD_FONT, COLOR_STD_BG, "Backup Status :"); + DrawStringF(BOT_SCREEN, pos_x0, pos_y0 + 60, COLOR_STD_FONT, COLOR_STD_BG, "Install Status:"); + + DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + 0, COLOR_STATUS(statusA9lh) , COLOR_STD_BG, msgA9lh ); + DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + 10, COLOR_STATUS(statusSdCard) , COLOR_STD_BG, msgSdCard ); + DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + 20, COLOR_STATUS(statusFirm) , COLOR_STD_BG, msgFirm ); + DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + 30, COLOR_STATUS(statusSector) , COLOR_STD_BG, msgSector ); + DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + 40, COLOR_STATUS(statusCrypto) , COLOR_STD_BG, msgCrypto ); + DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + 50, COLOR_STATUS(statusBackup) , COLOR_STD_BG, msgBackup ); + DrawStringF(BOT_SCREEN, pos_x1, pos_y0 + 60, COLOR_STATUS(statusInstall), COLOR_STD_BG, msgInstall); + + return 0; +} + +u32 SafeSigHaxInstaller(void) { + UINT bt; + + // initialization + ShowString("Initializing, please wait..."); + + + // step #0 - a9lh check + InitNandCrypto(); // for sector0x96 crypto and NAND drives + snprintf(msgA9lh, 64, CheckA9lh() ? "installed" : "not installed"); + statusA9lh = STATUS_GREEN; + ShowInstallerStatus(); + + // step #1 - init/check SD card + snprintf(msgSdCard, 64, "checking..."); + statusSdCard = STATUS_YELLOW; + ShowInstallerStatus(); + u64 sdFree = 0; + u64 sdTotal = 0; + if ((fs_init() != FR_OK) || + (f_getfreebyte("0:", &sdFree) != FR_OK) || + (f_gettotalbyte("0:", &sdTotal) != FR_OK)) { + snprintf(msgSdCard, 64, "init failed"); + statusSdCard = STATUS_RED; + return 1; + } + snprintf(msgSdCard, 64, "%lluMB / %lluMB free", sdFree / (1024 * 1024), sdTotal / (1024 * 1024)); + statusSdCard = (sdFree < MIN_SD_FREE) ? STATUS_RED : STATUS_GREEN; + ShowInstallerStatus(); + if (sdFree < MIN_SD_FREE) return 1; + // SD card okay! + + + // step #2 - check sighaxed firm + snprintf(msgFirm, 64, "checking..."); + statusFirm = STATUS_YELLOW; + ShowInstallerStatus(); + u8 firm_sha[0x20]; + UINT firm_size; + if ((f_qread(NAME_SIGHAXFIRM, FIRM_BUFFER, 0, FIRM_BUFFER_SIZE, &firm_size) != FR_OK) || + (firm_size < 0x200)) { + snprintf(msgFirm, 64, "file not found"); + statusFirm = STATUS_RED; + return 1; + } + if ((f_qread(NAME_SIGHAXFIRMSHA, firm_sha, 0, 0x20, &bt) != FR_OK) || (bt != 0x20)) { + snprintf(msgFirm, 64, ".sha file not found"); + statusFirm = STATUS_RED; + return 1; + } + if (ValidateFirm(FIRM_BUFFER, firm_sha, firm_size, NULL) != 0) { + snprintf(msgFirm, 64, "invalid FIRM"); + statusFirm = STATUS_RED; + return 1; + } + if (CheckFirmSigHax(FIRM_BUFFER) != 0) { + snprintf(msgFirm, 64, "not sighaxed"); + statusFirm = STATUS_RED; + return 1; + } + snprintf(msgFirm, 64, "loaded & verified"); + statusFirm = STATUS_GREEN; + ShowInstallerStatus(); + // provided FIRM is okay! + + + // step #3 - check secret_sector.bin file + u8 secret_sector[0x20]; + if (CheckA9lh()) { + snprintf(msgSector, 64, "checking..."); + statusSector = STATUS_YELLOW; + ShowInstallerStatus(); + if ((f_qread(NAME_SECTOR0x96, secret_sector, 0, 0x200, &bt) != FR_OK) || (bt != 0x200)) { + snprintf(msgSector, 64, "file not found"); + statusSector = STATUS_RED; + return 1; + } + if (ValidateSector(secret_sector) != 0) { + snprintf(msgSector, 64, "invalid file"); + statusSector = STATUS_RED; + return 1; + } + snprintf(msgSector, 64, "loaded & verified"); + } else snprintf(msgSector, 64, "not required"); + statusSector = STATUS_GREEN; + ShowInstallerStatus(); + // secret_sector.bin okay or not required! + + + // step #3 - check NAND crypto + snprintf(msgCrypto, 64, "checking..."); + statusCrypto = STATUS_YELLOW; + ShowInstallerStatus(); + if (!CheckFirmCrypto()) { + snprintf(msgCrypto, 64, "FIRM crypto fail"); + statusCrypto = STATUS_RED; + return 1; + } + if (CheckA9lh() && !CheckSector0x96Crypto()) { + snprintf(msgCrypto, 64, "OTP crypto fail"); + statusCrypto = STATUS_RED; + return 1; + } + snprintf(msgCrypto, 64, "all checks passed"); + statusCrypto = STATUS_GREEN; + ShowInstallerStatus(); + + + // step #X - point of no return + if (!ShowUnlockSequence(1, "All input files verified.\nTo install FIRM, enter the sequence\nbelow or press B to cancel.")) { + snprintf(msgBackup, 64, "cancelled by user"); + snprintf(msgInstall, 64, "cancelled by user"); + return 1; + } + + + // step #5 - backup of current FIRMs and sector 0x96 + snprintf(msgBackup, 64, "FIRM backup..."); + statusBackup = STATUS_YELLOW; + ShowInstallerStatus(); + FIL fp; + u32 ret = 0; + if (f_open(&fp, NAME_FIRMBACKUP, FA_READ|FA_WRITE|FA_CREATE_ALWAYS) != FR_OK) { + snprintf(msgBackup, 64, "FIRM backup fail"); + statusBackup = STATUS_RED; + return 1; + } + ShowProgress(0, 0, "FIRM backup"); + for (u32 pos = 0; (pos < FIRM_NAND_SIZE) && (ret == 0); pos += WORK_BUFFER_SIZE) { + UINT bytes = min(WORK_BUFFER_SIZE, FIRM_NAND_SIZE - pos); + snprintf(msgBackup, 64, "FIRM backup (%luMB/%luMB)", + pos / (1024*1024), (u32) FIRM_NAND_SIZE / (1024 * 1024)); + ShowInstallerStatus(); + if ((ReadNandBytes(WORK_BUFFER, FIRM_NAND_OFFSET + pos, bytes, 0xFF) != 0) || + (SafeWriteFile(&fp, WORK_BUFFER, pos, bytes) != 0)) + ret = 1; + ShowProgress(pos + bytes, FIRM_NAND_SIZE, "FIRM backup"); + } + f_close(&fp); + if (ret != 0) { + snprintf(msgBackup, 64, "FIRM backup fail"); + statusBackup = STATUS_RED; + return 1; + } + if (CheckA9lh()) { + snprintf(msgBackup, 64, "0x96 backup..."); + ShowInstallerStatus(); + u8 sector_backup0[0x200]; + u8 sector_backup1[0x200]; + f_unlink(NAME_SECTORBACKUP); + if ((ReadNandSectors(sector_backup0, 96, 1, 0xFF) != 0) || + (f_qwrite(NAME_SECTORBACKUP, sector_backup0, 0, 0x200, &bt) != FR_OK) || (bt != 0x200) || + (f_qread(NAME_SECTORBACKUP, sector_backup1, 0, 0x200, &bt) != FR_OK) || (bt != 0x200) || + (memcmp(sector_backup0, sector_backup1, 0x200) != 0)) { + snprintf(msgBackup, 64, "0x96 backup fail"); + statusBackup = STATUS_RED; + return 1; + } + } + snprintf(msgBackup, 64, "backed up & verified"); + statusBackup = STATUS_GREEN; + ShowInstallerStatus(); + // backups done + + // step #6 - install sighaxed FIRM + + + return 0; +} diff --git a/source/installer.h b/source/installer.h new file mode 100644 index 0000000..4cbcaa3 --- /dev/null +++ b/source/installer.h @@ -0,0 +1,6 @@ +#pragma once + +#include "common.h" + +u32 ShowInstallerStatus(void); +u32 SafeSigHaxInstaller(void); diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..b2e73be --- /dev/null +++ b/source/main.c @@ -0,0 +1,20 @@ +#include "installer.h" +#include "ui.h" +#include "i2c.h" + +void Reboot() +{ + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); + while(true); +} + + +int main() +{ + u32 ret = SafeSigHaxInstaller(); + ShowInstallerStatus(); // update installer status one last time + if (ret) ShowPrompt(false, "SigHaxed FIRM was not installed!\nCheck lower screen for info."); + ClearScreenF(true, true, COLOR_STD_BG); + Reboot(); + return 0; +} diff --git a/source/nand/nand.c b/source/nand/nand.c index 0288f53..3f44750 100644 --- a/source/nand/nand.c +++ b/source/nand/nand.c @@ -1,17 +1,19 @@ #include "nand.h" -#include "platform.h" -#include "qff.h" #include "aes.h" #include "sha.h" #include "sdmmc.h" - -#define NAND_MIN_SECTORS ((GetUnitPlatform() == PLATFORM_N3DS) ? NAND_MIN_SECTORS_N3DS : NAND_MIN_SECTORS_O3DS) +#include "qff.h" static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file static const u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte) 0x98, 0x24, 0x27, 0x14, 0x22, 0xB0, 0x6B, 0xF2, 0x10, 0x96, 0x9C, 0x36, 0x42, 0x53, 0x7C, 0x86, 0x62, 0x22, 0x5C, 0xFD, 0x6F, 0xAE, 0x9B, 0x0A, 0x85, 0xA5, 0xCE, 0x21, 0xAA, 0xB6, 0xC8, 0x4D }; + +static const u8 slot0x11Key95_sha256[0x20] = { // slot0x11Key95 hash (first 16 byte of sector0x96) + 0xBF, 0x01, 0x4C, 0x85, 0x9B, 0xA1, 0x07, 0xFA, 0x3B, 0xAC, 0x25, 0x20, 0x1A, 0x3F, 0x3A, 0xF4, + 0x4E, 0x97, 0xE9, 0x5C, 0x06, 0x46, 0xF8, 0xE7, 0xC1, 0xC2, 0xC3, 0x29, 0x88, 0xBF, 0xF6, 0x50 +}; static const u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic 0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -160,8 +162,25 @@ bool CheckSlot0x05Crypto(void) bool CheckSector0x96Crypto(void) { - const u8 zeroes[32] = { 0 }; - return !(memcmp(OtpSha256, zeroes, 32) == 0); + u8 buffer[0x200]; + ReadNandSectors(buffer, 0x96, 1, 0x11); + return (sha_cmp(slot0x11Key95_sha256, buffer, 16, SHA256_MODE) == 0); +} + +bool CheckFirmCrypto(void) +{ + // check the FIRM magic + const u8 magic[8] = {'F', 'I', 'R', 'M', '\0', '\0', '\0', '\0'}; + const u32 sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 }; + u8 buffer[0x200]; + for (u32 i = 0; i < sizeof(sectors) / sizeof(u32); i++) { + ReadNandSectors(buffer, sectors[i], 1, 0x06); + ShowPrompt(false, "%016llX\n%016llX", getbe64(magic), getbe64(buffer)); + if (memcmp(buffer, magic, 8) != 0) return false; + } + + // success if we arrive here + return false; } bool CheckA9lh(void) @@ -296,12 +315,12 @@ int WriteNandSectors(const void* buffer, u32 sector, u32 count, u32 keyslot) u32 CheckNandHeader(void* header) { - // TWL MBR check - u8 header_dec[0x200]; + // TWL MBR check - ignored + /*u8 header_dec[0x200]; memcpy(header_dec, header, 0x200); CryptNand(header_dec, 0, 1, 0x03); if (memcmp(header_dec + 0x1BE, twl_mbr, sizeof(twl_mbr)) != 0) - return 0; // header does not belong to console + return 0; // header does not belong to console */ // header type check u8* header_enc = header; diff --git a/source/nand/nand.h b/source/nand/nand.h index 6ea1515..c204294 100644 --- a/source/nand/nand.h +++ b/source/nand/nand.h @@ -1,6 +1,9 @@ #pragma once #include "common.h" +#include "platform.h" + +#define NAND_MIN_SECTORS ((GetUnitPlatform() == PLATFORM_N3DS) ? NAND_MIN_SECTORS_N3DS : NAND_MIN_SECTORS_O3DS) #define NAND_SYSNAND (1<<0) #define NAND_ZERONAND (1<<3) @@ -35,6 +38,7 @@ bool InitNandCrypto(void); bool CheckSlot0x05Crypto(void); bool CheckSector0x96Crypto(void); +bool CheckFirmCrypto(void); bool CheckA9lh(void); void CryptNand(void* buffer, u32 sector, u32 count, u32 keyslot); diff --git a/source/safety/safewrite.c b/source/safety/safewrite.c new file mode 100644 index 0000000..0b35373 --- /dev/null +++ b/source/safety/safewrite.c @@ -0,0 +1,32 @@ +#include "safewrite.h" +#include "nand.h" +#include "sha.h" + +// safe file writer function, warnings: +// (1) file must be opened in RW mode for this to work +// (2) contents of buffer may change on corruption +// (3) uses SHA register +u32 SafeWriteFile(FIL* file, void* buff, FSIZE_t ofs, UINT btw) { + u8 sha_in[0x20]; + UINT bw; + + sha_quick(sha_in, buff, btw, SHA256_MODE); + if ((f_lseek(file, ofs) != FR_OK) || (f_write(file, buff, btw, &bw) != FR_OK) || (btw != bw) || + (f_lseek(file, ofs) != FR_OK) || (f_read(file, buff, btw, &bw) != FR_OK) || (btw != bw)) + return 1; + + return (sha_cmp(sha_in, buff, btw, SHA256_MODE) == 0) ? 0 : 1; +} + +// safe NAND writer function, warnings: +// (1) contents of buffer may change on corruption +// (2) uses SHA register +u32 SafeWriteNand(void* buff, u32 ofs, u32 btw, u32 keyslot) { + u8 sha_in[0x20]; + + sha_quick(sha_in, buff, btw, SHA256_MODE); + if ((WriteNandBytes(buff, ofs, btw, keyslot) != 0) || (ReadNandBytes(buff, ofs, btw, keyslot) != 0)) + return 1; + + return (sha_cmp(sha_in, buff, btw, SHA256_MODE) == 0) ? 0 : 1; +} \ No newline at end of file diff --git a/source/safety/safewrite.h b/source/safety/safewrite.h new file mode 100644 index 0000000..6369cd9 --- /dev/null +++ b/source/safety/safewrite.h @@ -0,0 +1,7 @@ +#pragma once + +#include "common.h" +#include "ff.h" + +u32 SafeWriteFile(FIL* file, void* buff, FSIZE_t ofs, UINT btw); +u32 SafeWriteNand(void* buff, u32 ofs, u32 btw, u32 keyslot); diff --git a/source/safety/validator.c b/source/safety/validator.c new file mode 100644 index 0000000..fa9bf5a --- /dev/null +++ b/source/safety/validator.c @@ -0,0 +1,94 @@ +#include "validator.h" +#include "sha.h" + +#define FIRM_MAGIC 'F', 'I', 'R', 'M', '\0', '\0', '\0', '\0' +#define FIRM_MAX_SIZE 0x400000 // 4MB, due to FIRM partition size + +// see: https://www.3dbrew.org/wiki/FIRM#Firmware_Section_Headers +typedef struct { + u32 offset; + u32 address; + u32 size; + u32 type; + u8 hash[0x20]; +} __attribute__((packed)) FirmSectionHeader; + +// see: https://www.3dbrew.org/wiki/FIRM#FIRM_Header +typedef struct { + u8 magic[8]; + u32 entry_arm11; + u32 entry_arm9; + u8 reserved1[0x30]; + FirmSectionHeader sections[4]; + u8 signature[0x100]; +} __attribute__((packed, aligned(16))) FirmHeader; + +// from: https://github.com/AuroraWright/SafeA9LHInstaller/blob/master/source/installer.c#L9-L17 +const u8 sectorHash[0x20] = { + 0x82, 0xF2, 0x73, 0x0D, 0x2C, 0x2D, 0xA3, 0xF3, 0x01, 0x65, 0xF9, 0x87, 0xFD, 0xCC, 0xAC, 0x5C, + 0xBA, 0xB2, 0x4B, 0x4E, 0x5F, 0x65, 0xC9, 0x81, 0xCD, 0x7B, 0xE6, 0xF4, 0x38, 0xE6, 0xD9, 0xD3 +}; + +// standard sighax signature hash - still unknown +const u8 sighaxHash[0x20] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) { + u8 magic[] = { FIRM_MAGIC }; + if (memcmp(header->magic, magic, sizeof(magic)) != 0) + return 1; + + u32 firm_size = sizeof(FirmHeader); + for (u32 i = 0; i < 4; i++) { + FirmSectionHeader* section = header->sections + i; + if (!section->size) continue; + if (section->offset < firm_size) return 1; + firm_size = section->offset + section->size; + } + + if ((firm_size > FIRM_MAX_SIZE) || (data_size && (firm_size > data_size))) + return 1; + + return 0; +} + +u32 ValidateFirm(void* firm, u8* firm_sha, u32 firm_size, char* output) { + FirmHeader* header = (FirmHeader*) firm; + + // validate firm header + if (ValidateFirmHeader(header, firm_size) != 0) + return 1; + + // hash verify all available sections + for (u32 i = 0; i < 4; i++) { + FirmSectionHeader* section = header->sections + i; + if (!section->size) continue; + if (sha_cmp(section->hash, (u8*) firm + section->offset, section->size, SHA256_MODE) != 0) { + if (output) snprintf(output, 64, "Section %lu hash mismatch", i); + return 1; + } + } + + // check provided .SHA + if (sha_cmp(firm_sha, firm, firm_size, SHA256_MODE) != 0) { + if (output) snprintf(output, 64, "SHA hash mismatch"); + return 1; + } + + // ARM9 / ARM11 area check (?) + // more (?) + + return 0; +} + +u32 ValidateSector(void* sector) { + return (sha_cmp(sectorHash, sector, 0x200, SHA256_MODE) == 0) ? 0 : 1; +} + +u32 CheckFirmSigHax(void* firm) { + return 0; // not like we can check that already + FirmHeader* header = (FirmHeader*) firm; + return (sha_cmp(sighaxHash, header->signature, 0x100, SHA256_MODE) == 0) ? 0 : 1; +} diff --git a/source/safety/validator.h b/source/safety/validator.h new file mode 100644 index 0000000..e03adca --- /dev/null +++ b/source/safety/validator.h @@ -0,0 +1,7 @@ +#pragma once + +#include "common.h" + +u32 ValidateFirm(void* firm, u8* firm_sha, u32 firm_size, char* output); +u32 ValidateSector(void* sector); +u32 CheckFirmSigHax(void* firm);