mirror of
https://github.com/d0k3/SafeB9SInstaller.git
synced 2025-06-26 13:42:45 +00:00
First look version - some stuff not working yet
.. won't write to NAND yet
This commit is contained in:
parent
5aa7fadf86
commit
64b1275c39
4
Makefile
4
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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
236
source/installer.c
Normal file
236
source/installer.c
Normal file
@ -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;
|
||||
}
|
6
source/installer.h
Normal file
6
source/installer.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
u32 ShowInstallerStatus(void);
|
||||
u32 SafeSigHaxInstaller(void);
|
20
source/main.c
Normal file
20
source/main.c
Normal file
@ -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;
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
32
source/safety/safewrite.c
Normal file
32
source/safety/safewrite.c
Normal file
@ -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;
|
||||
}
|
7
source/safety/safewrite.h
Normal file
7
source/safety/safewrite.h
Normal file
@ -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);
|
94
source/safety/validator.c
Normal file
94
source/safety/validator.c
Normal file
@ -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;
|
||||
}
|
7
source/safety/validator.h
Normal file
7
source/safety/validator.h
Normal file
@ -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);
|
Loading…
x
Reference in New Issue
Block a user