mirror of
https://github.com/AuroraWright/SafeA9LHInstaller.git
synced 2025-06-26 13:42:45 +00:00
Removed stage2-only install, added A9LH update which only needs the stage1 and 2 payloads to update A9LH. Also, secret_sector.bin is never needed on New 3DS anymore
This commit is contained in:
parent
9c7e70bb81
commit
b6aa3e1fd1
@ -349,16 +349,17 @@ void generateSector(u8 *keySector){
|
||||
}
|
||||
|
||||
//Test the OTP to be correct by verifying key2
|
||||
u32 testOtp(u32 a9lhBoot, u8 *tempOffset){
|
||||
u32 testOtp(u32 a9lhBoot, u8 *keySector){
|
||||
//Read keysector from NAND
|
||||
sdmmc_nand_readsectors(0x96, 1, tempOffset);
|
||||
sdmmc_nand_readsectors(0x96, 1, keySector);
|
||||
|
||||
//Decrypt key2
|
||||
//Decrypt keys (we need just key2 on A9LH)
|
||||
aes_use_keyslot(0x11);
|
||||
aes(tempOffset + 0x10, tempOffset + 0x10, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
for(u32 i = 0, keysAmount = a9lhBoot ? 2 : 32; i < keysAmount; i++)
|
||||
aes(keySector + (0x10 * i), keySector + (0x10 * i), 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
|
||||
//Test key2
|
||||
if(memcmp(tempOffset + 0x10, a9lhBoot ? a9lhKey2 : key2, 0x10) != 0) return 0;
|
||||
if(memcmp(keySector + 0x10, a9lhBoot ? a9lhKey2 : key2, 0x10) != 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,6 @@ u32 mountSD(void){
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 unmountSD(void){
|
||||
if(f_mount(NULL, "0:", 1) != FR_OK) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 fileRead(void *dest, const char *path, u32 size){
|
||||
FRESULT fr;
|
||||
FIL fp;
|
||||
|
@ -7,6 +7,5 @@
|
||||
#include "types.h"
|
||||
|
||||
u32 mountSD(void);
|
||||
u32 unmountSD(void);
|
||||
u32 fileRead(void *dest, const char *path, u32 size);
|
||||
u32 fileSize(const char *path);
|
@ -16,6 +16,11 @@ static const u8 firm0Hash[0x20] = {
|
||||
0x95, 0x47, 0x61, 0xA1, 0xD5, 0xEA, 0x03, 0xB5, 0xEB, 0x50, 0x47, 0xAC, 0x63, 0xAC, 0x5D, 0x6B
|
||||
};
|
||||
|
||||
static const u8 firm0A9lhHash[0x20] = {
|
||||
0xFC, 0x7C, 0xC7, 0x2D, 0xA3, 0x0A, 0xDE, 0x3A, 0xE2, 0xFE, 0x7D, 0x79, 0xA3, 0xC2, 0x6C, 0x82,
|
||||
0xB8, 0xA3, 0xDB, 0xC4, 0x46, 0x22, 0xB4, 0x61, 0x8D, 0xDC, 0x73, 0x90, 0x50, 0x5D, 0x40, 0x11
|
||||
};
|
||||
|
||||
static const u8 firm1Hash[0x20] = {
|
||||
0xD2, 0x53, 0xC1, 0xCC, 0x0A, 0x5F, 0xFA, 0xC6, 0xB3, 0x83, 0xDA, 0xC1, 0x82, 0x7C, 0xFB, 0x3B,
|
||||
0x2D, 0x3D, 0x56, 0x6C, 0x6A, 0x1A, 0x8E, 0x52, 0x54, 0xE3, 0x89, 0xC2, 0x95, 0x06, 0x23, 0xE5
|
||||
@ -23,65 +28,18 @@ static const u8 firm1Hash[0x20] = {
|
||||
|
||||
int pos_y;
|
||||
|
||||
static void installStage2(u32 mode){
|
||||
if(!mode){
|
||||
pos_y = drawString("You are about to update stage2 only", 10, pos_y + 10, COLOR_RED);
|
||||
pos_y = drawString("Doing this could brick your console!", 10, pos_y, COLOR_RED);
|
||||
pos_y = drawString("If you would like to continue, press:", 10, pos_y, COLOR_WHITE);
|
||||
pos_y = drawString("Up, Down, Left, Right, B, A, START, SELECT", 10, pos_y, COLOR_WHITE);
|
||||
|
||||
u16 unlockSequence[] = { BUTTON_UP, BUTTON_DOWN, BUTTON_LEFT, BUTTON_RIGHT, BUTTON_B, BUTTON_A, BUTTON_START, BUTTON_SELECT };
|
||||
u32 sequenceSize = sizeof(unlockSequence) / sizeof(u16);
|
||||
|
||||
for(u32 correctPresses = 0; correctPresses < sequenceSize; correctPresses++){
|
||||
if(waitInput() != unlockSequence[correctPresses])
|
||||
shutdown(1, "Button sequence not entered correctly");
|
||||
}
|
||||
|
||||
//Mount the SD card
|
||||
mountSD();
|
||||
}
|
||||
|
||||
//Read stage2
|
||||
const char path[] = "a9lh/payload_stage2.bin";
|
||||
u32 size = fileSize(path);
|
||||
if(!size || size > MAX_STAGE2_SIZE)
|
||||
shutdown(1, "Error: stage2.bin doesn't exist or exceeds\nmax size");
|
||||
memset((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE);
|
||||
fileRead((void *)STAGE2_OFFSET, path, size);
|
||||
|
||||
if(mode) return;
|
||||
|
||||
sdmmc_nand_writesectors(0x5C000, 0x20, (vu8 *)STAGE2_OFFSET);
|
||||
|
||||
//Unmount the SD card
|
||||
unmountSD();
|
||||
shutdown(2, "Stage2 update: success!");
|
||||
}
|
||||
|
||||
void installer(void){
|
||||
//Determine if booting with A9LH
|
||||
u32 a9lhBoot = (PDN_SPI_CNT == 0x0) ? 1 : 0;
|
||||
//Detect the console being used
|
||||
u32 console = (PDN_MPCORE_CFG == 1) ? 0 : 1;
|
||||
|
||||
while(1){
|
||||
clearScreens();
|
||||
drawString("Safe A9LH Installer v1.4", 10, 10, COLOR_TITLE);
|
||||
pos_y = drawString("Thanks to delebile, #cakey and StandardBus", 10, 40, COLOR_WHITE);
|
||||
pos_y = drawString("Press SELECT for a full install", 10, pos_y + SPACING_VERT, COLOR_WHITE);
|
||||
if(a9lhBoot){ pos_y = drawString("Press START to only update stage2", 10, pos_y, COLOR_WHITE);
|
||||
pos_y = drawString("(Only do stage2 updates from the fork you use!)", 10, pos_y, COLOR_RED); }
|
||||
pos_y = drawString("Press any other button to shutdown", 10, pos_y, COLOR_WHITE);
|
||||
drawString("Safe A9LH Installer v1.5", 10, 10, COLOR_TITLE);
|
||||
pos_y = drawString("Thanks to delebile, #cakey and StandardBus", 10, 40, COLOR_WHITE);
|
||||
pos_y = drawString(a9lhBoot ? "Press SELECT to update A9LH" : "Press SELECT for a full install", 10, pos_y, COLOR_WHITE);
|
||||
pos_y = drawString("Press any other button to shutdown", 10, pos_y, COLOR_WHITE);
|
||||
|
||||
u16 pressed = waitInput();
|
||||
if(a9lhBoot && pressed == BUTTON_START) installStage2(0);
|
||||
else if(pressed == BUTTON_SELECT) break;
|
||||
else shutdown(0, NULL);
|
||||
}
|
||||
|
||||
//Mount the SD card
|
||||
mountSD();
|
||||
if(waitInput() != BUTTON_SELECT) shutdown(0, NULL);
|
||||
|
||||
const char *path;
|
||||
|
||||
@ -97,65 +55,78 @@ void installer(void){
|
||||
//Setup the key sector de/encryption with the SHA register or otp.bin
|
||||
setupKeyslot0x11(a9lhBoot, (void *)OTP_OFFSET);
|
||||
|
||||
if(a9lhBoot && !testOtp(a9lhBoot, (u8 *)TEMP_OFFSET))
|
||||
if(a9lhBoot && !testOtp(a9lhBoot, (u8 *)SECTOR_OFFSET))
|
||||
shutdown(1, "Error: the OTP hash is invalid");
|
||||
|
||||
if(!a9lhBoot && console && !testOtp(a9lhBoot, (u8 *)TEMP_OFFSET))
|
||||
if(!a9lhBoot && console && !testOtp(a9lhBoot, (u8 *)SECTOR_OFFSET))
|
||||
shutdown(1, "Error: otp.bin is invalid or corrupted");
|
||||
|
||||
//Calculate the CTR for the 3DS partitions
|
||||
getNandCTR();
|
||||
|
||||
//Test that the CTR is correct
|
||||
readFirm0((u8 *)TEMP_OFFSET, 0x200);
|
||||
if(memcmp((void *)TEMP_OFFSET, "FIRM", 4) != 0)
|
||||
//Get NAND FIRM0 and test that the CTR is correct
|
||||
readFirm0((u8 *)FIRM0_OFFSET, FIRM_SIZE);
|
||||
if(memcmp((void *)FIRM0_OFFSET, "FIRM", 4) != 0)
|
||||
shutdown(1, "Error: failed to setup FIRM encryption");
|
||||
|
||||
//Read decrypted key sector
|
||||
path = "a9lh/secret_sector.bin";
|
||||
if(fileSize(path) != 0x200)
|
||||
shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size");
|
||||
fileRead((void *)SECTOR_OFFSET, path, 0x200);
|
||||
if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash))
|
||||
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
||||
if(!a9lhBoot){
|
||||
if(!console){
|
||||
//Read decrypted key sector
|
||||
path = "a9lh/secret_sector.bin";
|
||||
if(fileSize(path) != 0x200)
|
||||
shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size");
|
||||
fileRead((void *)SECTOR_OFFSET, path, 0x200);
|
||||
if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash))
|
||||
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
||||
}
|
||||
else if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash))
|
||||
shutdown(1, "Error: the NAND key sector is invalid");
|
||||
|
||||
//Generate and encrypt a per-console A9LH key sector
|
||||
generateSector((u8 *)SECTOR_OFFSET);
|
||||
//Generate and encrypt a per-console A9LH key sector
|
||||
generateSector((u8 *)SECTOR_OFFSET);
|
||||
|
||||
//Read FIRM0
|
||||
path = "a9lh/firm0.bin";
|
||||
u32 firm0Size = fileSize(path);
|
||||
if(!firm0Size)
|
||||
shutdown(1, "Error: firm0.bin doesn't exist");
|
||||
fileRead((void *)FIRM0_OFFSET, path, firm0Size);
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, firm0Size, firm0Hash))
|
||||
shutdown(1, "Error: firm0.bin is invalid or corrupted");
|
||||
//Read FIRM0
|
||||
path = "a9lh/firm0.bin";
|
||||
if(fileSize(path) != FIRM_SIZE)
|
||||
shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size");
|
||||
fileRead((void *)FIRM0_OFFSET, path, FIRM_SIZE);
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, FIRM_SIZE, firm0Hash))
|
||||
shutdown(1, "Error: firm0.bin is invalid or corrupted");
|
||||
|
||||
//Read FIRM1
|
||||
path = "a9lh/firm1.bin";
|
||||
u32 firm1Size = fileSize(path);
|
||||
if(!firm1Size)
|
||||
shutdown(1, "Error: firm1.bin doesn't exist");
|
||||
fileRead((void *)FIRM1_OFFSET, path, firm1Size);
|
||||
if(!verifyHash((void *)FIRM1_OFFSET, firm1Size, firm1Hash))
|
||||
shutdown(1, "Error: firm1.bin is invalid or corrupted");
|
||||
//Read FIRM1
|
||||
path = "a9lh/firm1.bin";
|
||||
if(fileSize(path) != FIRM_SIZE)
|
||||
shutdown(1, "Error: firm1.bin doesn't exist or has a wrong size");
|
||||
fileRead((void *)FIRM1_OFFSET, path, FIRM_SIZE);
|
||||
if(!verifyHash((void *)FIRM1_OFFSET, FIRM_SIZE, firm1Hash))
|
||||
shutdown(1, "Error: firm1.bin is invalid or corrupted");
|
||||
}
|
||||
else if(!verifyHash((void *)FIRM0_OFFSET, STAGE1_POSITION, firm0A9lhHash))
|
||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
||||
|
||||
//Inject stage1
|
||||
path = "a9lh/payload_stage1.bin";
|
||||
u32 size = fileSize(path);
|
||||
if(!size || size > MAX_STAGE1_SIZE)
|
||||
shutdown(1, "Error: stage1.bin doesn't exist or exceeds\nmax size");
|
||||
memset((void *)STAGE1_OFFSET, 0, MAX_STAGE1_SIZE);
|
||||
fileRead((void *)STAGE1_OFFSET, path, size);
|
||||
|
||||
installStage2(1);
|
||||
//Read stage2
|
||||
path = "a9lh/payload_stage2.bin";
|
||||
size = fileSize(path);
|
||||
if(!size || size > MAX_STAGE2_SIZE)
|
||||
shutdown(1, "Error: stage2.bin doesn't exist or exceeds\nmax size");
|
||||
memset((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE);
|
||||
fileRead((void *)STAGE2_OFFSET, path, size);
|
||||
|
||||
pos_y = drawString("All checks passed, installing...", 10, pos_y + SPACING_VERT, COLOR_WHITE);
|
||||
|
||||
//Point of no return, install stuff in the safest order
|
||||
sdmmc_nand_writesectors(0x5C000, 0x20, (vu8 *)STAGE2_OFFSET);
|
||||
writeFirm((u8 *)FIRM1_OFFSET, 1, firm1Size);
|
||||
writeFirm((u8 *)FIRM0_OFFSET, 0, firm0Size);
|
||||
sdmmc_nand_writesectors(0x96, 0x1, (vu8 *)SECTOR_OFFSET);
|
||||
if(!a9lhBoot){ writeFirm((u8 *)FIRM1_OFFSET, 1, FIRM_SIZE);
|
||||
sdmmc_nand_writesectors(0x96, 1, (vu8 *)SECTOR_OFFSET); }
|
||||
writeFirm((u8 *)FIRM0_OFFSET, 0, FIRM_SIZE);
|
||||
|
||||
shutdown(1, "Full install: success!");
|
||||
shutdown(1, a9lhBoot ? "Update: success!" : "Full install: success!");
|
||||
}
|
@ -9,10 +9,11 @@
|
||||
#define SECTOR_OFFSET 0x24100000
|
||||
#define FIRM0_OFFSET 0x24200000
|
||||
#define FIRM1_OFFSET 0x24300000
|
||||
#define STAGE1_OFFSET FIRM0_OFFSET + 0xF0400
|
||||
#define FIRM_SIZE 0xF2000
|
||||
#define STAGE1_POSITION 0xF0400
|
||||
#define STAGE1_OFFSET FIRM0_OFFSET + STAGE1_POSITION
|
||||
#define STAGE2_OFFSET 0x24400000
|
||||
#define MAX_STAGE1_SIZE 0x1C00
|
||||
#define MAX_STAGE2_SIZE 0x2800
|
||||
#define TEMP_OFFSET 0x24500000
|
||||
|
||||
void installer(void);
|
@ -1,8 +1,10 @@
|
||||
#include "installer.h"
|
||||
#include "fs.h"
|
||||
#include "screeninit.h"
|
||||
#include "types.h"
|
||||
|
||||
void main(void){
|
||||
mountSD();
|
||||
initScreens();
|
||||
installer();
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include "screeninit.h"
|
||||
#include "draw.h"
|
||||
#include "i2c.h"
|
||||
|
||||
void initScreens(void){
|
||||
@ -107,4 +108,6 @@ void initScreens(void){
|
||||
*arm11 = (u32)ARM11;
|
||||
while(*arm11);
|
||||
}
|
||||
|
||||
clearScreens();
|
||||
}
|
@ -28,14 +28,8 @@ u16 waitInput(void){
|
||||
void shutdown(u32 mode, char *message){
|
||||
if(mode){
|
||||
pos_y = drawString(message, 10, pos_y + SPACING_VERT, COLOR_WHITE);
|
||||
if(mode == 1) drawString("Press any button to shutdown", 10, pos_y, COLOR_WHITE);
|
||||
else {
|
||||
pos_y = drawString("Press START or SELECT to return to menu", 10, pos_y, COLOR_RED);
|
||||
pos_y = drawString("(SD will be unmounted until the next install)", 10, pos_y, COLOR_RED);
|
||||
pos_y = drawString("Press any other button to shutdown", 10, pos_y, COLOR_WHITE);
|
||||
}
|
||||
u16 pressed = waitInput();
|
||||
if(mode == 2 && (pressed & (BUTTON_START | BUTTON_SELECT))) return;
|
||||
drawString("Press any button to shutdown", 10, pos_y, COLOR_WHITE);
|
||||
waitInput();
|
||||
}
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1);
|
||||
while(1);
|
||||
|
@ -3,17 +3,9 @@
|
||||
#include "types.h"
|
||||
|
||||
#define HID_PAD (*(vu16 *)0x10146000 ^ 0xFFF)
|
||||
#define BUTTON_START (1 << 3)
|
||||
#define BUTTON_SELECT (1 << 2)
|
||||
#define BUTTON_A 1
|
||||
#define BUTTON_B (1 << 1)
|
||||
#define BUTTON_RIGHT (1 << 4)
|
||||
#define BUTTON_LEFT (1 << 5)
|
||||
#define BUTTON_UP (1 << 6)
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
|
||||
#define COLOR_TITLE 0xFF9900
|
||||
#define COLOR_WHITE 0xFFFFFF
|
||||
#define COLOR_RED 0x0000FF
|
||||
|
||||
extern int pos_y;
|
||||
|
Loading…
x
Reference in New Issue
Block a user