mirror of
https://github.com/AuroraWright/SafeA9LHInstaller.git
synced 2025-06-26 13:42:45 +00:00
Replace the used key2 as the previous one was faulty (thanks @gemarcano), allow updating from every A9LH setup
This commit is contained in:
parent
5f96d42a9c
commit
4ab5ad9a89
161
source/3dsheaders.h
Normal file
161
source/3dsheaders.h
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Adapted from 3DBrew and https://github.com/mid-kid/CakesForeveryWan/blob/master/source/headers.h
|
||||
*/
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 address;
|
||||
u32 phyRegionSize;
|
||||
u32 size;
|
||||
} CodeSetInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 saveDataSize[2];
|
||||
u32 jumpID[2];
|
||||
u8 reserved[0x30];
|
||||
} SystemInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char appTitle[8];
|
||||
u8 reserved1[5];
|
||||
u8 flag;
|
||||
u8 remasterVersion[2];
|
||||
CodeSetInfo textCodeSet;
|
||||
u32 stackSize;
|
||||
CodeSetInfo roCodeSet;
|
||||
u8 reserved2[4];
|
||||
CodeSetInfo dataCodeSet;
|
||||
u32 bssSize;
|
||||
char depends[0x180];
|
||||
SystemInfo systemInfo;
|
||||
} SystemControlInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
SystemControlInfo systemControlInfo;
|
||||
u8 aci[0x200];
|
||||
u8 accessDescSig[0x100];
|
||||
u8 ncchPubKey[0x100];
|
||||
u8 aciLim[0x200];
|
||||
} ExHeader;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u8 sig[0x100]; //RSA-2048 signature of the NCCH header, using SHA-256
|
||||
char magic[4]; //NCCH
|
||||
u32 contentSize; //Media unit
|
||||
u8 partitionId[8];
|
||||
u8 makerCode[2];
|
||||
u16 version;
|
||||
u8 reserved1[4];
|
||||
u8 programID[8];
|
||||
u8 reserved2[0x10];
|
||||
u8 logoHash[0x20]; //Logo Region SHA-256 hash
|
||||
char productCode[0x10];
|
||||
u8 exHeaderHash[0x20]; //Extended header SHA-256 hash
|
||||
u32 exHeaderSize; //Extended header size
|
||||
u32 reserved3;
|
||||
u8 flags[8];
|
||||
u32 plainOffset; //Media unit
|
||||
u32 plainSize; //Media unit
|
||||
u32 logoOffset; //Media unit
|
||||
u32 logoSize; //Media unit
|
||||
u32 exeFsOffset; //Media unit
|
||||
u32 exeFsSize; //Media unit
|
||||
u32 exeFsHashSize; //Media unit
|
||||
u32 reserved4;
|
||||
u32 romFsOffset; //Media unit
|
||||
u32 romFsSize; //Media unit
|
||||
u32 romFsHashSize; //Media unit
|
||||
u32 reserved5;
|
||||
u8 exeFsHash[0x20]; //ExeFS superblock SHA-256 hash
|
||||
u8 romFsHash[0x20]; //RomFS superblock SHA-256 hash
|
||||
} Ncch;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
Ncch ncch;
|
||||
ExHeader exHeader;
|
||||
} Cxi;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char sigIssuer[0x40];
|
||||
u8 eccPubKey[0x3C];
|
||||
u8 version;
|
||||
u8 caCrlVersion;
|
||||
u8 signerCrlVersion;
|
||||
u8 titleKey[0x10];
|
||||
u8 reserved1;
|
||||
u8 ticketId[8];
|
||||
u8 consoleId[4];
|
||||
u8 titleId[8];
|
||||
u8 reserved2[2];
|
||||
u16 ticketTitleVersion;
|
||||
u8 reserved3[8];
|
||||
u8 licenseType;
|
||||
u8 ticketCommonKeyYIndex; //Ticket common keyY index, usually 0x1 for retail system titles.
|
||||
u8 reserved4[0x2A];
|
||||
u8 unk[4]; //eShop Account ID?
|
||||
u8 reserved5;
|
||||
u8 audit;
|
||||
u8 reserved6[0x42];
|
||||
u8 limits[0x40];
|
||||
u8 contentIndex[0xAC];
|
||||
} Ticket;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 offset;
|
||||
u8 *address;
|
||||
u32 size;
|
||||
u32 procType;
|
||||
u8 hash[0x20];
|
||||
} FirmSection;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 magic;
|
||||
u32 reserved1;
|
||||
u8 *arm11Entry;
|
||||
u8 *arm9Entry;
|
||||
u8 reserved2[0x30];
|
||||
FirmSection section[4];
|
||||
} Firm;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u8 keyX[0x10];
|
||||
u8 keyY[0x10];
|
||||
u8 ctr[0x10];
|
||||
char size[8];
|
||||
u8 reserved[8];
|
||||
u8 ctlBlock[0x10];
|
||||
char magic[4];
|
||||
u8 reserved2[0xC];
|
||||
u8 slot0x16keyX[0x10];
|
||||
} Arm9Bin;
|
@ -275,10 +275,12 @@ static void sha(void *res, const void *src, u32 size, u32 mode)
|
||||
static u8 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE];
|
||||
static u8 nandSlot;
|
||||
static u32 fatStart;
|
||||
const u8 __attribute__((aligned(4))) key2s[3][AES_BLOCK_SIZE] = {
|
||||
const u8 __attribute__((aligned(4))) key2s[5][AES_BLOCK_SIZE] = {
|
||||
{0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0},
|
||||
{0x08, 0x24, 0xD3, 0xCB, 0x4A, 0xE9, 0x4D, 0x62, 0x4D, 0xAA, 0x52, 0x60, 0x47, 0xC5, 0x93, 0x94},
|
||||
{0x65, 0x29, 0x3E, 0x12, 0x56, 0x0C, 0x0B, 0xD1, 0xDD, 0xB5, 0x63, 0x1C, 0xB6, 0xD9, 0x52, 0x75},
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xF5, 0xF6},
|
||||
{0x65, 0x29, 0x3E, 0x12, 0x56, 0x0C, 0x0B, 0xD1, 0xDD, 0xB5, 0x63, 0x1C, 0xB6, 0xD9, 0x52, 0x75}
|
||||
{0xA5, 0x25, 0x87, 0x11, 0x8F, 0x42, 0x9F, 0x3D, 0x65, 0x1D, 0xDD, 0x58, 0x0B, 0x6D, 0xF2, 0x17}
|
||||
};
|
||||
|
||||
void getNandCtr(void)
|
||||
@ -295,7 +297,7 @@ void ctrNandInit(void)
|
||||
{
|
||||
getNandCtr();
|
||||
|
||||
if(isN3DS)
|
||||
if(ISN3DS)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
|
||||
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
@ -382,7 +384,7 @@ void generateSector(u8 *keySector, u32 mode)
|
||||
memcpy(keySector + AES_BLOCK_SIZE, key2s[0], AES_BLOCK_SIZE);
|
||||
break;
|
||||
default:
|
||||
memcpy(keySector + AES_BLOCK_SIZE, key2s[2], AES_BLOCK_SIZE);
|
||||
memcpy(keySector + AES_BLOCK_SIZE, key2s[1], AES_BLOCK_SIZE);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -417,20 +419,27 @@ bool verifyHash(const void *data, u32 size, const u8 *hash)
|
||||
return memcmp(shasum, hash, sizeof(shasum)) == 0;
|
||||
}
|
||||
|
||||
u32 decryptExeFs(u8 *inbuf)
|
||||
u32 decryptExeFs(Cxi *cxi)
|
||||
{
|
||||
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
|
||||
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200;
|
||||
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||
u32 exeFsSize = 0;
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
ncchCtr[7 - i] = *(inbuf + 0x108 + i);
|
||||
ncchCtr[8] = 2;
|
||||
if(memcmp(cxi->ncch.magic, "NCCH", 4) == 0)
|
||||
{
|
||||
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||
exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
||||
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x2C);
|
||||
aes(inbuf, exeFsOffset + 0x200, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||
ncchCtr[8] = 2;
|
||||
|
||||
return exeFsSize - 0x200;
|
||||
aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x2C);
|
||||
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
if(memcmp(cxi, "FIRM", 4) != 0) exeFsSize = 0;
|
||||
}
|
||||
|
||||
return exeFsSize;
|
||||
}
|
@ -79,9 +79,6 @@
|
||||
#define SHA_224_HASH_SIZE (224 / 8)
|
||||
#define SHA_1_HASH_SIZE (160 / 8)
|
||||
|
||||
extern bool isN3DS;
|
||||
const u8 key2s[3][AES_BLOCK_SIZE];
|
||||
|
||||
void getNandCtr(void);
|
||||
void ctrNandInit(void);
|
||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||
@ -91,4 +88,4 @@ void setupKeyslot0x11(const void *otp, bool isA9lh);
|
||||
void generateSector(u8 *keySector, u32 mode);
|
||||
void getSector(u8 *keySector, bool isA9lh);
|
||||
bool verifyHash(const void *data, u32 size, const u8 *hash);
|
||||
u32 decryptExeFs(u8 *inbuf);
|
||||
u32 decryptExeFs(Cxi *cxi);
|
@ -39,7 +39,7 @@ DSTATUS disk_initialize (
|
||||
{
|
||||
if(pdrv == CTRNAND) ctrNandInit();
|
||||
|
||||
return RES_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -55,8 +55,8 @@ DRESULT disk_read (
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, (BYTE *)buff)) ||
|
||||
(pdrv == CTRNAND && !ctrNandRead(sector, count, (BYTE *)buff))) ? RES_OK : RES_PARERR;
|
||||
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, buff)) ||
|
||||
(pdrv == CTRNAND && !ctrNandRead(sector, count, buff))) ? RES_OK : RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ DRESULT disk_write (
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
return (pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) ? RES_OK : RES_PARERR;
|
||||
return (pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, buff)) ? RES_OK : RES_PARERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
50
source/fs.c
50
source/fs.c
@ -53,32 +53,34 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
FIL file;
|
||||
bool ret;
|
||||
|
||||
FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS);
|
||||
|
||||
if(result == FR_OK)
|
||||
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
|
||||
{
|
||||
unsigned int written;
|
||||
f_write(&file, buffer, size, &written);
|
||||
f_truncate(&file);
|
||||
f_close(&file);
|
||||
case FR_OK:
|
||||
{
|
||||
unsigned int written;
|
||||
f_write(&file, buffer, size, &written);
|
||||
f_truncate(&file);
|
||||
f_close(&file);
|
||||
|
||||
ret = (u32)written == size;
|
||||
}
|
||||
else if(result == FR_NO_PATH)
|
||||
{
|
||||
for(u32 i = 1; path[i] != 0; i++)
|
||||
if(path[i] == '/')
|
||||
{
|
||||
char folder[i + 1];
|
||||
memcpy(folder, path, i);
|
||||
folder[i] = 0;
|
||||
ret = f_mkdir(folder) == FR_OK;
|
||||
if(!ret) break;
|
||||
}
|
||||
ret = (u32)written == size;
|
||||
break;
|
||||
}
|
||||
case FR_NO_PATH:
|
||||
for(u32 i = 1; path[i] != 0; i++)
|
||||
if(path[i] == '/')
|
||||
{
|
||||
char folder[i + 1];
|
||||
memcpy(folder, path, i);
|
||||
folder[i] = 0;
|
||||
f_mkdir(folder);
|
||||
}
|
||||
|
||||
if(ret) ret = fileWrite(buffer, path, size);
|
||||
ret = fileWrite(buffer, path, size);
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
else ret = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -87,7 +89,7 @@ u32 firmRead(void *dest)
|
||||
{
|
||||
const char *firmFolders[] = { "00000002", "20000002" };
|
||||
char path[48] = "1:/title/00040138/";
|
||||
concatenateStrings(path, firmFolders[isN3DS ? 1 : 0]);
|
||||
concatenateStrings(path, firmFolders[ISN3DS ? 1 : 0]);
|
||||
concatenateStrings(path, "/content");
|
||||
|
||||
DIR dir;
|
||||
@ -116,7 +118,7 @@ u32 firmRead(void *dest)
|
||||
}
|
||||
|
||||
//FIRM is equal or newer than 11.0
|
||||
if(tempVersion >= (isN3DS ? 0x21 : 0x52)) ret = 2;
|
||||
if(tempVersion >= (ISN3DS ? 0x21 : 0x52)) ret = 2;
|
||||
|
||||
//Found an older cxi
|
||||
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
||||
|
@ -24,8 +24,6 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
extern bool isN3DS;
|
||||
|
||||
bool mountFs(bool isSd);
|
||||
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||
|
@ -2,7 +2,6 @@
|
||||
* installer.c
|
||||
*/
|
||||
|
||||
#include "installer.h"
|
||||
#include "memory.h"
|
||||
#include "fs.h"
|
||||
#include "crypto.h"
|
||||
@ -10,6 +9,7 @@
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "types.h"
|
||||
#include "installer.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
@ -24,6 +24,10 @@ static const u8 sectorHash[SHA_256_HASH_SIZE] = {
|
||||
firm0A9lhHash[SHA_256_HASH_SIZE] = {
|
||||
0x79, 0x3D, 0x35, 0x7B, 0x8F, 0xF1, 0xFC, 0xF0, 0x8F, 0xB6, 0xDB, 0x51, 0x31, 0xD4, 0xA7, 0x74,
|
||||
0x8E, 0xF0, 0x4A, 0xB1, 0xA6, 0x7F, 0xCD, 0xAB, 0x0C, 0x0A, 0xC0, 0x69, 0xA7, 0x9D, 0xC5, 0x04
|
||||
},
|
||||
firm090A9lhHash[SHA_256_HASH_SIZE] = {
|
||||
0x68, 0x52, 0xCC, 0x21, 0x89, 0xAE, 0x28, 0x38, 0x1A, 0x75, 0x90, 0xE7, 0x38, 0x23, 0x48, 0x41,
|
||||
0x8E, 0x80, 0x78, 0x75, 0x27, 0x64, 0x04, 0xD6, 0x28, 0xD6, 0xFA, 0x39, 0xA8, 0x6F, 0xB0, 0x3F
|
||||
},
|
||||
firm0100Hash[SHA_256_HASH_SIZE] = {
|
||||
0xD8, 0x2D, 0xB7, 0xB4, 0x38, 0x2B, 0x07, 0x88, 0x99, 0x77, 0x91, 0x0C, 0xC6, 0xEC, 0x6D, 0x87,
|
||||
@ -35,18 +39,11 @@ static const u8 sectorHash[SHA_256_HASH_SIZE] = {
|
||||
};
|
||||
|
||||
u32 posY;
|
||||
bool isN3DS;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
//Determine if booting with A9LH
|
||||
bool isA9lh = !PDN_SPI_CNT;
|
||||
|
||||
//Detect the console being used
|
||||
isN3DS = PDN_MPCORE_CFG == 7;
|
||||
|
||||
vu32 *magic = (vu32 *)0x25000000;
|
||||
bool isOtpless = isA9lh && magic[0] == 0xABADCAFE && magic[1] == 0xDEADCAFE;
|
||||
bool isOtpless = ISA9LH && magic[0] == 0xABADCAFE && magic[1] == 0xDEADCAFE;
|
||||
|
||||
initScreens();
|
||||
|
||||
@ -60,7 +57,7 @@ void main(void)
|
||||
|
||||
if(!isOtpless)
|
||||
{
|
||||
posY = drawString(isA9lh ? "Press SELECT to update A9LH, START to uninstall" : "Press SELECT for a full install", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
posY = drawString(ISA9LH ? "Press SELECT to update A9LH, START to uninstall" : "Press SELECT for a full install", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
posY = drawString("Press any other button to shutdown", 10, posY, COLOR_WHITE);
|
||||
pressed = waitInput();
|
||||
}
|
||||
@ -71,15 +68,17 @@ void main(void)
|
||||
pressed = 0;
|
||||
}
|
||||
|
||||
if(isOtpless || pressed == BUTTON_SELECT) installer(isA9lh, isOtpless);
|
||||
if(pressed == BUTTON_START && isA9lh) uninstaller();
|
||||
if(isOtpless || pressed == BUTTON_SELECT) installer(isOtpless);
|
||||
if(pressed == BUTTON_START && ISA9LH) uninstaller();
|
||||
|
||||
shutdown(0, NULL);
|
||||
}
|
||||
|
||||
static inline void installer(bool isA9lh, bool isOtpless)
|
||||
static inline void installer(bool isOtpless)
|
||||
{
|
||||
bool updateA9lh = false;
|
||||
bool updateKey2 = false,
|
||||
updateFirm0 = false,
|
||||
updateFirm1 = false;
|
||||
u8 otp[256] = {0},
|
||||
keySector[512];
|
||||
|
||||
@ -87,7 +86,7 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
shutdown(1, "Error: failed to mount the SD card");
|
||||
|
||||
//If making a first install on O3DS, we need the OTP
|
||||
if(!isA9lh && !isN3DS)
|
||||
if(!ISA9LH && !ISN3DS)
|
||||
{
|
||||
const char otpPath[] = "a9lh/otp.bin";
|
||||
|
||||
@ -107,7 +106,7 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
}
|
||||
|
||||
//Setup the key sector de/encryption with the SHA register or otp.bin
|
||||
if(isA9lh || !isN3DS) setupKeyslot0x11(otp, isA9lh);
|
||||
if(ISA9LH || !ISN3DS) setupKeyslot0x11(otp, ISA9LH);
|
||||
|
||||
//Calculate the CTR for the 3DS partitions
|
||||
getNandCtr();
|
||||
@ -121,7 +120,7 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
}
|
||||
|
||||
//If booting from A9LH or on N3DS, we can use the key sector from NAND
|
||||
if(isA9lh || isN3DS) getSector(keySector, isA9lh);
|
||||
if(ISA9LH || ISN3DS) getSector(keySector, ISA9LH);
|
||||
else
|
||||
{
|
||||
//Read decrypted key sector
|
||||
@ -131,19 +130,50 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
||||
}
|
||||
|
||||
if(isA9lh && !isOtpless)
|
||||
if(ISA9LH && !isOtpless)
|
||||
{
|
||||
u32 i;
|
||||
for(i = 1; i < 3; i++)
|
||||
if(memcmp(keySector + AES_BLOCK_SIZE, key2s[i], AES_BLOCK_SIZE) == 0) break;
|
||||
u32 i;
|
||||
for(i = 1; i < 5; i++)
|
||||
if(memcmp(keySector + AES_BLOCK_SIZE, key2s[i], AES_BLOCK_SIZE) == 0) break;
|
||||
|
||||
if(i == 3) shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||
if(i == 1) updateA9lh = true;
|
||||
switch(i)
|
||||
{
|
||||
case 5:
|
||||
shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
updateKey2 = true;
|
||||
break;
|
||||
case 4:
|
||||
updateFirm1 = true;
|
||||
updateKey2 = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash))
|
||||
{
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm090A9lhHash))
|
||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
||||
|
||||
updateFirm0 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isA9lh || updateA9lh || isOtpless) generateSector(keySector, (!isA9lh && isN3DS) ? 1 : 0);
|
||||
if(!ISA9LH || updateKey2 || isOtpless) generateSector(keySector, (!ISA9LH && ISN3DS) ? 1 : 0);
|
||||
|
||||
if(!isA9lh || updateA9lh)
|
||||
if(!ISA9LH && ISN3DS)
|
||||
{
|
||||
//Read 10.0 FIRM0
|
||||
if(fileRead((void *)FIRM0_100_OFFSET, "a9lh/firm0_100.bin", FIRM0100_SIZE) != FIRM0100_SIZE)
|
||||
shutdown(1, "Error: firm0_100.bin doesn't exist or has a wrong size");
|
||||
if(!verifyHash((void *)FIRM0_100_OFFSET, FIRM0100_SIZE, firm0100Hash))
|
||||
shutdown(1, "Error: firm0_100.bin is invalid or corrupted");
|
||||
}
|
||||
|
||||
if(!ISA9LH || updateFirm0)
|
||||
{
|
||||
//Read FIRM0
|
||||
if(fileRead((void *)FIRM0_OFFSET, "a9lh/firm0.bin", FIRM0_SIZE) != FIRM0_SIZE)
|
||||
@ -151,20 +181,9 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, firm0Hash))
|
||||
shutdown(1, "Error: firm0.bin is invalid or corrupted");
|
||||
}
|
||||
else if(!isOtpless && !verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash))
|
||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
||||
|
||||
if(!isA9lh)
|
||||
if(!ISA9LH || updateFirm1)
|
||||
{
|
||||
if(isN3DS)
|
||||
{
|
||||
//Read 10.0 FIRM0
|
||||
if(fileRead((void *)FIRM0_100_OFFSET, "a9lh/firm0_100.bin", FIRM0100_SIZE) != FIRM0100_SIZE)
|
||||
shutdown(1, "Error: firm0_100.bin doesn't exist or has a wrong size");
|
||||
if(!verifyHash((void *)FIRM0_100_OFFSET, FIRM0100_SIZE, firm0100Hash))
|
||||
shutdown(1, "Error: firm0_100.bin is invalid or corrupted");
|
||||
}
|
||||
|
||||
//Read FIRM1
|
||||
if(fileRead((void *)FIRM1_OFFSET, "a9lh/firm1.bin", FIRM1_SIZE) != FIRM1_SIZE)
|
||||
shutdown(1, "Error: firm1.bin doesn't exist or has a wrong size");
|
||||
@ -226,10 +245,10 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET);
|
||||
}
|
||||
|
||||
if(!isA9lh) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE);
|
||||
if(!isA9lh || updateA9lh || isOtpless) sdmmc_nand_writesectors(0x96, 1, keySector);
|
||||
if(!ISA9LH || updateFirm1) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE);
|
||||
if(!ISA9LH || updateKey2 || isOtpless) sdmmc_nand_writesectors(0x96, 1, keySector);
|
||||
|
||||
if(!isA9lh && isN3DS)
|
||||
if(!ISA9LH && ISN3DS)
|
||||
{
|
||||
*(vu32 *)0x80FD0FC = 0xEAFFCBBF; //B 0x80F0000
|
||||
memcpy((void *)0x80F0000, loader_bin, loader_bin_size);
|
||||
@ -241,7 +260,7 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
|
||||
writeFirm((u8 *)FIRM0_OFFSET, false, FIRM0_SIZE);
|
||||
|
||||
shutdown(2, isA9lh && !isOtpless ? "Update: success!" : "Full install: success!");
|
||||
shutdown(2, ISA9LH && !isOtpless ? "Update: success!" : "Full install: success!");
|
||||
}
|
||||
|
||||
static inline void uninstaller(void)
|
||||
@ -253,7 +272,7 @@ static inline void uninstaller(void)
|
||||
inputSequence();
|
||||
|
||||
//New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector
|
||||
if(isN3DS)
|
||||
if(ISN3DS)
|
||||
{
|
||||
setupKeyslot0x11(NULL, true);
|
||||
getSector(keySector, true);
|
||||
@ -286,7 +305,8 @@ static inline void uninstaller(void)
|
||||
}
|
||||
|
||||
//Decrypt it and get its size
|
||||
u32 firmSize = decryptExeFs((u8 *)FIRM0_OFFSET);
|
||||
u32 firmSize = decryptExeFs((Cxi *)FIRM0_OFFSET);
|
||||
if(firmSize == 0) shutdown(1, "Error: couldn't decrypt the CTRNAND FIRM");
|
||||
|
||||
//writeFirm encrypts in-place, so we need two copies
|
||||
memcpy((void *)FIRM1_OFFSET, (void *)FIRM0_OFFSET, firmSize);
|
||||
|
@ -23,5 +23,7 @@
|
||||
#define MAX_STAGE1_SIZE 0x1E70
|
||||
#define MAX_STAGE2_SIZE 0x89A00
|
||||
|
||||
static inline void installer(bool isA9lh, bool isOtpless);
|
||||
extern const u8 key2s[5][AES_BLOCK_SIZE];
|
||||
|
||||
static inline void installer(bool isOtpless);
|
||||
static inline void uninstaller(void);
|
@ -179,7 +179,7 @@ void initScreens(void)
|
||||
WAIT_FOR_ARM9();
|
||||
}
|
||||
|
||||
if(PDN_GPU_CNT == 1)
|
||||
if(!ARESCREENSINITED)
|
||||
{
|
||||
invokeArm11Function(initSequence);
|
||||
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||
|
||||
#define ARESCREENSINITED (PDN_GPU_CNT != 1)
|
||||
|
||||
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
|
||||
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
||||
|
||||
|
@ -24,14 +24,14 @@
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
@ Change the stack pointer
|
||||
mov sp, #0x27000000
|
||||
|
||||
@ Disable interrupts
|
||||
mrs r0, cpsr
|
||||
orr r0, #0x1C0
|
||||
msr cpsr_cx, r0
|
||||
|
||||
@ Change the stack pointer
|
||||
mov sp, #0x27000000
|
||||
|
||||
@ Disable caches / MPU
|
||||
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||
bic r0, #(1<<12) @ - instruction cache disable
|
||||
|
@ -50,6 +50,4 @@ void hexItoa(u32 number, char *out, u32 digits)
|
||||
out[digits - 1 - i++] = hexDigits[number & 0xF];
|
||||
number >>= 4;
|
||||
}
|
||||
|
||||
while(i < digits) out[digits - 1 - i++] = '0';
|
||||
}
|
@ -16,4 +16,12 @@ typedef uint64_t u64;
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
typedef volatile u64 vu64;
|
||||
|
||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||
|
||||
#define ISN3DS (PDN_MPCORE_CFG == 7)
|
||||
#define ISA9LH (!PDN_SPI_CNT)
|
||||
|
||||
#include "3dsheaders.h"
|
Loading…
x
Reference in New Issue
Block a user