Replace the used key2 as the previous one was faulty (thanks @gemarcano), allow updating from every A9LH setup

This commit is contained in:
Aurora 2016-10-16 20:40:56 +02:00
parent 5f96d42a9c
commit 4ab5ad9a89
13 changed files with 298 additions and 101 deletions

161
source/3dsheaders.h Normal file
View 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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -179,7 +179,7 @@ void initScreens(void)
WAIT_FOR_ARM9();
}
if(PDN_GPU_CNT == 1)
if(!ARESCREENSINITED)
{
invokeArm11Function(initSequence);

View File

@ -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)();

View File

@ -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

View File

@ -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';
}

View File

@ -17,3 +17,11 @@ typedef volatile u8 vu8;
typedef volatile u16 vu16;
typedef volatile u32 vu32;
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"