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 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE];
|
||||||
static u8 nandSlot;
|
static u8 nandSlot;
|
||||||
static u32 fatStart;
|
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},
|
{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},
|
{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)
|
void getNandCtr(void)
|
||||||
@ -295,7 +297,7 @@ void ctrNandInit(void)
|
|||||||
{
|
{
|
||||||
getNandCtr();
|
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};
|
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);
|
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);
|
memcpy(keySector + AES_BLOCK_SIZE, key2s[0], AES_BLOCK_SIZE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
memcpy(keySector + AES_BLOCK_SIZE, key2s[2], AES_BLOCK_SIZE);
|
memcpy(keySector + AES_BLOCK_SIZE, key2s[1], AES_BLOCK_SIZE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,20 +419,27 @@ bool verifyHash(const void *data, u32 size, const u8 *hash)
|
|||||||
return memcmp(shasum, hash, sizeof(shasum)) == 0;
|
return memcmp(shasum, hash, sizeof(shasum)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 decryptExeFs(u8 *inbuf)
|
u32 decryptExeFs(Cxi *cxi)
|
||||||
{
|
{
|
||||||
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
|
u32 exeFsSize = 0;
|
||||||
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200;
|
|
||||||
|
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};
|
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||||
|
|
||||||
for(u32 i = 0; i < 8; i++)
|
for(u32 i = 0; i < 8; i++)
|
||||||
ncchCtr[7 - i] = *(inbuf + 0x108 + i);
|
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||||
ncchCtr[8] = 2;
|
ncchCtr[8] = 2;
|
||||||
|
|
||||||
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
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_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
aes_use_keyslot(0x2C);
|
aes_use_keyslot(0x2C);
|
||||||
aes(inbuf, exeFsOffset + 0x200, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
return exeFsSize - 0x200;
|
if(memcmp(cxi, "FIRM", 4) != 0) exeFsSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exeFsSize;
|
||||||
}
|
}
|
@ -79,9 +79,6 @@
|
|||||||
#define SHA_224_HASH_SIZE (224 / 8)
|
#define SHA_224_HASH_SIZE (224 / 8)
|
||||||
#define SHA_1_HASH_SIZE (160 / 8)
|
#define SHA_1_HASH_SIZE (160 / 8)
|
||||||
|
|
||||||
extern bool isN3DS;
|
|
||||||
const u8 key2s[3][AES_BLOCK_SIZE];
|
|
||||||
|
|
||||||
void getNandCtr(void);
|
void getNandCtr(void);
|
||||||
void ctrNandInit(void);
|
void ctrNandInit(void);
|
||||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
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 generateSector(u8 *keySector, u32 mode);
|
||||||
void getSector(u8 *keySector, bool isA9lh);
|
void getSector(u8 *keySector, bool isA9lh);
|
||||||
bool verifyHash(const void *data, u32 size, const u8 *hash);
|
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();
|
if(pdrv == CTRNAND) ctrNandInit();
|
||||||
|
|
||||||
return RES_OK;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ DRESULT disk_read (
|
|||||||
UINT count /* Number of sectors to read */
|
UINT count /* Number of sectors to read */
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, (BYTE *)buff)) ||
|
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, buff)) ||
|
||||||
(pdrv == CTRNAND && !ctrNandRead(sector, count, (BYTE *)buff))) ? RES_OK : RES_PARERR;
|
(pdrv == CTRNAND && !ctrNandRead(sector, count, buff))) ? RES_OK : RES_PARERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ DRESULT disk_write (
|
|||||||
UINT count /* Number of sectors to 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
|
#endif
|
||||||
|
|
||||||
|
24
source/fs.c
24
source/fs.c
@ -53,9 +53,9 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
|||||||
FIL file;
|
FIL file;
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS);
|
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
|
||||||
|
{
|
||||||
if(result == FR_OK)
|
case FR_OK:
|
||||||
{
|
{
|
||||||
unsigned int written;
|
unsigned int written;
|
||||||
f_write(&file, buffer, size, &written);
|
f_write(&file, buffer, size, &written);
|
||||||
@ -63,22 +63,24 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
|||||||
f_close(&file);
|
f_close(&file);
|
||||||
|
|
||||||
ret = (u32)written == size;
|
ret = (u32)written == size;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if(result == FR_NO_PATH)
|
case FR_NO_PATH:
|
||||||
{
|
|
||||||
for(u32 i = 1; path[i] != 0; i++)
|
for(u32 i = 1; path[i] != 0; i++)
|
||||||
if(path[i] == '/')
|
if(path[i] == '/')
|
||||||
{
|
{
|
||||||
char folder[i + 1];
|
char folder[i + 1];
|
||||||
memcpy(folder, path, i);
|
memcpy(folder, path, i);
|
||||||
folder[i] = 0;
|
folder[i] = 0;
|
||||||
ret = f_mkdir(folder) == FR_OK;
|
f_mkdir(folder);
|
||||||
if(!ret) break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ret) ret = fileWrite(buffer, path, size);
|
ret = fileWrite(buffer, path, size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else ret = false;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -87,7 +89,7 @@ u32 firmRead(void *dest)
|
|||||||
{
|
{
|
||||||
const char *firmFolders[] = { "00000002", "20000002" };
|
const char *firmFolders[] = { "00000002", "20000002" };
|
||||||
char path[48] = "1:/title/00040138/";
|
char path[48] = "1:/title/00040138/";
|
||||||
concatenateStrings(path, firmFolders[isN3DS ? 1 : 0]);
|
concatenateStrings(path, firmFolders[ISN3DS ? 1 : 0]);
|
||||||
concatenateStrings(path, "/content");
|
concatenateStrings(path, "/content");
|
||||||
|
|
||||||
DIR dir;
|
DIR dir;
|
||||||
@ -116,7 +118,7 @@ u32 firmRead(void *dest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//FIRM is equal or newer than 11.0
|
//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
|
//Found an older cxi
|
||||||
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
||||||
|
@ -24,8 +24,6 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
extern bool isN3DS;
|
|
||||||
|
|
||||||
bool mountFs(bool isSd);
|
bool mountFs(bool isSd);
|
||||||
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
* installer.c
|
* installer.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "installer.h"
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
@ -10,6 +9,7 @@
|
|||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "installer.h"
|
||||||
#include "fatfs/sdmmc/sdmmc.h"
|
#include "fatfs/sdmmc/sdmmc.h"
|
||||||
#include "../build/bundled.h"
|
#include "../build/bundled.h"
|
||||||
|
|
||||||
@ -24,6 +24,10 @@ static const u8 sectorHash[SHA_256_HASH_SIZE] = {
|
|||||||
firm0A9lhHash[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,
|
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
|
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] = {
|
firm0100Hash[SHA_256_HASH_SIZE] = {
|
||||||
0xD8, 0x2D, 0xB7, 0xB4, 0x38, 0x2B, 0x07, 0x88, 0x99, 0x77, 0x91, 0x0C, 0xC6, 0xEC, 0x6D, 0x87,
|
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;
|
u32 posY;
|
||||||
bool isN3DS;
|
|
||||||
|
|
||||||
void main(void)
|
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;
|
vu32 *magic = (vu32 *)0x25000000;
|
||||||
bool isOtpless = isA9lh && magic[0] == 0xABADCAFE && magic[1] == 0xDEADCAFE;
|
bool isOtpless = ISA9LH && magic[0] == 0xABADCAFE && magic[1] == 0xDEADCAFE;
|
||||||
|
|
||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
@ -60,7 +57,7 @@ void main(void)
|
|||||||
|
|
||||||
if(!isOtpless)
|
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);
|
posY = drawString("Press any other button to shutdown", 10, posY, COLOR_WHITE);
|
||||||
pressed = waitInput();
|
pressed = waitInput();
|
||||||
}
|
}
|
||||||
@ -71,15 +68,17 @@ void main(void)
|
|||||||
pressed = 0;
|
pressed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isOtpless || pressed == BUTTON_SELECT) installer(isA9lh, isOtpless);
|
if(isOtpless || pressed == BUTTON_SELECT) installer(isOtpless);
|
||||||
if(pressed == BUTTON_START && isA9lh) uninstaller();
|
if(pressed == BUTTON_START && ISA9LH) uninstaller();
|
||||||
|
|
||||||
shutdown(0, NULL);
|
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},
|
u8 otp[256] = {0},
|
||||||
keySector[512];
|
keySector[512];
|
||||||
|
|
||||||
@ -87,7 +86,7 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
|||||||
shutdown(1, "Error: failed to mount the SD card");
|
shutdown(1, "Error: failed to mount the SD card");
|
||||||
|
|
||||||
//If making a first install on O3DS, we need the OTP
|
//If making a first install on O3DS, we need the OTP
|
||||||
if(!isA9lh && !isN3DS)
|
if(!ISA9LH && !ISN3DS)
|
||||||
{
|
{
|
||||||
const char otpPath[] = "a9lh/otp.bin";
|
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
|
//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
|
//Calculate the CTR for the 3DS partitions
|
||||||
getNandCtr();
|
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 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
|
else
|
||||||
{
|
{
|
||||||
//Read decrypted key sector
|
//Read decrypted key sector
|
||||||
@ -131,32 +130,41 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
|||||||
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isA9lh && !isOtpless)
|
if(ISA9LH && !isOtpless)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
for(i = 1; i < 3; i++)
|
for(i = 1; i < 5; i++)
|
||||||
if(memcmp(keySector + AES_BLOCK_SIZE, key2s[i], AES_BLOCK_SIZE) == 0) break;
|
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");
|
switch(i)
|
||||||
if(i == 1) updateA9lh = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!isA9lh || updateA9lh || isOtpless) generateSector(keySector, (!isA9lh && isN3DS) ? 1 : 0);
|
|
||||||
|
|
||||||
if(!isA9lh || updateA9lh)
|
|
||||||
{
|
{
|
||||||
//Read FIRM0
|
case 5:
|
||||||
if(fileRead((void *)FIRM0_OFFSET, "a9lh/firm0.bin", FIRM0_SIZE) != FIRM0_SIZE)
|
shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||||
shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size");
|
break;
|
||||||
if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, firm0Hash))
|
case 2:
|
||||||
shutdown(1, "Error: firm0.bin is invalid or corrupted");
|
case 3:
|
||||||
|
updateKey2 = true;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
updateFirm1 = true;
|
||||||
|
updateKey2 = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if(!isOtpless && !verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash))
|
|
||||||
|
if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash))
|
||||||
|
{
|
||||||
|
if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm090A9lhHash))
|
||||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
shutdown(1, "Error: NAND FIRM0 is invalid");
|
||||||
|
|
||||||
if(!isA9lh)
|
updateFirm0 = true;
|
||||||
{
|
}
|
||||||
if(isN3DS)
|
}
|
||||||
|
|
||||||
|
if(!ISA9LH || updateKey2 || isOtpless) generateSector(keySector, (!ISA9LH && ISN3DS) ? 1 : 0);
|
||||||
|
|
||||||
|
if(!ISA9LH && ISN3DS)
|
||||||
{
|
{
|
||||||
//Read 10.0 FIRM0
|
//Read 10.0 FIRM0
|
||||||
if(fileRead((void *)FIRM0_100_OFFSET, "a9lh/firm0_100.bin", FIRM0100_SIZE) != FIRM0100_SIZE)
|
if(fileRead((void *)FIRM0_100_OFFSET, "a9lh/firm0_100.bin", FIRM0100_SIZE) != FIRM0100_SIZE)
|
||||||
@ -165,6 +173,17 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
|||||||
shutdown(1, "Error: firm0_100.bin is invalid or corrupted");
|
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)
|
||||||
|
shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size");
|
||||||
|
if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, firm0Hash))
|
||||||
|
shutdown(1, "Error: firm0.bin is invalid or corrupted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ISA9LH || updateFirm1)
|
||||||
|
{
|
||||||
//Read FIRM1
|
//Read FIRM1
|
||||||
if(fileRead((void *)FIRM1_OFFSET, "a9lh/firm1.bin", FIRM1_SIZE) != FIRM1_SIZE)
|
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");
|
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);
|
sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isA9lh) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE);
|
if(!ISA9LH || updateFirm1) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE);
|
||||||
if(!isA9lh || updateA9lh || isOtpless) sdmmc_nand_writesectors(0x96, 1, keySector);
|
if(!ISA9LH || updateKey2 || isOtpless) sdmmc_nand_writesectors(0x96, 1, keySector);
|
||||||
|
|
||||||
if(!isA9lh && isN3DS)
|
if(!ISA9LH && ISN3DS)
|
||||||
{
|
{
|
||||||
*(vu32 *)0x80FD0FC = 0xEAFFCBBF; //B 0x80F0000
|
*(vu32 *)0x80FD0FC = 0xEAFFCBBF; //B 0x80F0000
|
||||||
memcpy((void *)0x80F0000, loader_bin, loader_bin_size);
|
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);
|
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)
|
static inline void uninstaller(void)
|
||||||
@ -253,7 +272,7 @@ static inline void uninstaller(void)
|
|||||||
inputSequence();
|
inputSequence();
|
||||||
|
|
||||||
//New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector
|
//New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector
|
||||||
if(isN3DS)
|
if(ISN3DS)
|
||||||
{
|
{
|
||||||
setupKeyslot0x11(NULL, true);
|
setupKeyslot0x11(NULL, true);
|
||||||
getSector(keySector, true);
|
getSector(keySector, true);
|
||||||
@ -286,7 +305,8 @@ static inline void uninstaller(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Decrypt it and get its size
|
//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
|
//writeFirm encrypts in-place, so we need two copies
|
||||||
memcpy((void *)FIRM1_OFFSET, (void *)FIRM0_OFFSET, firmSize);
|
memcpy((void *)FIRM1_OFFSET, (void *)FIRM0_OFFSET, firmSize);
|
||||||
|
@ -23,5 +23,7 @@
|
|||||||
#define MAX_STAGE1_SIZE 0x1E70
|
#define MAX_STAGE1_SIZE 0x1E70
|
||||||
#define MAX_STAGE2_SIZE 0x89A00
|
#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);
|
static inline void uninstaller(void);
|
@ -179,7 +179,7 @@ void initScreens(void)
|
|||||||
WAIT_FOR_ARM9();
|
WAIT_FOR_ARM9();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(PDN_GPU_CNT == 1)
|
if(!ARESCREENSINITED)
|
||||||
{
|
{
|
||||||
invokeArm11Function(initSequence);
|
invokeArm11Function(initSequence);
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||||
|
|
||||||
|
#define ARESCREENSINITED (PDN_GPU_CNT != 1)
|
||||||
|
|
||||||
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
|
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
|
||||||
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
||||||
|
|
||||||
|
@ -24,14 +24,14 @@
|
|||||||
.align 4
|
.align 4
|
||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
@ Change the stack pointer
|
|
||||||
mov sp, #0x27000000
|
|
||||||
|
|
||||||
@ Disable interrupts
|
@ Disable interrupts
|
||||||
mrs r0, cpsr
|
mrs r0, cpsr
|
||||||
orr r0, #0x1C0
|
orr r0, #0x1C0
|
||||||
msr cpsr_cx, r0
|
msr cpsr_cx, r0
|
||||||
|
|
||||||
|
@ Change the stack pointer
|
||||||
|
mov sp, #0x27000000
|
||||||
|
|
||||||
@ Disable caches / MPU
|
@ Disable caches / MPU
|
||||||
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||||
bic r0, #(1<<12) @ - instruction cache disable
|
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];
|
out[digits - 1 - i++] = hexDigits[number & 0xF];
|
||||||
number >>= 4;
|
number >>= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(i < digits) out[digits - 1 - i++] = '0';
|
|
||||||
}
|
}
|
@ -17,3 +17,11 @@ typedef volatile u8 vu8;
|
|||||||
typedef volatile u16 vu16;
|
typedef volatile u16 vu16;
|
||||||
typedef volatile u32 vu32;
|
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