Lots of refactoring, main() has its own file yet again, properly handle failed patches/decryption steps, support TWL and AGB FIRM since 3.0
This commit is contained in:
parent
aa422914bd
commit
5b4712644a
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;
|
@ -29,6 +29,8 @@
|
|||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
#include "pin.h"
|
#include "pin.h"
|
||||||
|
|
||||||
|
CfgData configData;
|
||||||
|
|
||||||
bool readConfig(void)
|
bool readConfig(void)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
@ -67,7 +69,7 @@ void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void configMenu(Fs fsStatus, bool oldPinStatus, u32 oldPinMode)
|
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||||
{
|
{
|
||||||
const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
|
const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
|
||||||
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
||||||
@ -116,7 +118,8 @@ void configMenu(Fs fsStatus, bool oldPinStatus, u32 oldPinMode)
|
|||||||
"games.",
|
"games.",
|
||||||
|
|
||||||
"Select the developer features.\n\n"
|
"Select the developer features.\n\n"
|
||||||
"\t* 'Off' disables exception handlers.\n"
|
"\t* 'Off' disables exception handlers\n"
|
||||||
|
"in FIRM.\n"
|
||||||
"\t* 'ErrDisp' displays debug info\n"
|
"\t* 'ErrDisp' displays debug info\n"
|
||||||
"on the 'An error has occurred' screen.\n"
|
"on the 'An error has occurred' screen.\n"
|
||||||
"\t* 'UNITINFO' makes the console be\n"
|
"\t* 'UNITINFO' makes the console be\n"
|
||||||
@ -194,11 +197,11 @@ void configMenu(Fs fsStatus, bool oldPinStatus, u32 oldPinMode)
|
|||||||
u32 enabled;
|
u32 enabled;
|
||||||
bool visible;
|
bool visible;
|
||||||
} multiOptions[] = {
|
} multiOptions[] = {
|
||||||
{ .posXs = {19, 24, 29, 34}, .visible = fsStatus == SD_CARD },
|
{ .posXs = {19, 24, 29, 34}, .visible = isSdMode },
|
||||||
{ .posXs = {21, 26, 31, 36}, .visible = true },
|
{ .posXs = {21, 26, 31, 36}, .visible = true },
|
||||||
{ .posXs = {12, 22, 31, 0}, .visible = true },
|
{ .posXs = {12, 22, 31, 0}, .visible = true },
|
||||||
{ .posXs = {14, 19, 24, 29}, .visible = true },
|
{ .posXs = {14, 19, 24, 29}, .visible = true },
|
||||||
{ .posXs = {17, 26, 32, 44}, .visible = isN3DS },
|
{ .posXs = {17, 26, 32, 44}, .visible = ISN3DS },
|
||||||
{ .posXs = {19, 30, 42, 0}, .visible = true }
|
{ .posXs = {19, 30, 42, 0}, .visible = true }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -207,10 +210,10 @@ void configMenu(Fs fsStatus, bool oldPinStatus, u32 oldPinMode)
|
|||||||
bool enabled;
|
bool enabled;
|
||||||
bool visible;
|
bool visible;
|
||||||
} singleOptions[] = {
|
} singleOptions[] = {
|
||||||
{ .visible = fsStatus == SD_CARD },
|
{ .visible = isSdMode },
|
||||||
{ .visible = fsStatus == SD_CARD },
|
{ .visible = isSdMode },
|
||||||
{ .visible = true },
|
{ .visible = true },
|
||||||
{ .visible = fsStatus == SD_CARD },
|
{ .visible = isSdMode },
|
||||||
{ .visible = true },
|
{ .visible = true },
|
||||||
{ .visible = true },
|
{ .visible = true },
|
||||||
{ .visible = true },
|
{ .visible = true },
|
||||||
|
@ -60,14 +60,6 @@ enum singleOptions
|
|||||||
PATCHACCESS
|
PATCHACCESS
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct __attribute__((packed))
|
|
||||||
{
|
|
||||||
char magic[4];
|
|
||||||
u16 formatVersionMajor, formatVersionMinor;
|
|
||||||
|
|
||||||
u32 config;
|
|
||||||
} CfgData;
|
|
||||||
|
|
||||||
typedef enum ConfigurationStatus
|
typedef enum ConfigurationStatus
|
||||||
{
|
{
|
||||||
DONT_CONFIGURE = 0,
|
DONT_CONFIGURE = 0,
|
||||||
@ -75,9 +67,6 @@ typedef enum ConfigurationStatus
|
|||||||
CREATE_CONFIGURATION
|
CREATE_CONFIGURATION
|
||||||
} ConfigurationStatus;
|
} ConfigurationStatus;
|
||||||
|
|
||||||
extern CfgData configData;
|
|
||||||
extern bool isN3DS;
|
|
||||||
|
|
||||||
bool readConfig(void);
|
bool readConfig(void);
|
||||||
void writeConfig(ConfigurationStatus needConfig, u32 configTemp);
|
void writeConfig(ConfigurationStatus needConfig, u32 configTemp);
|
||||||
void configMenu(Fs fsStatus, bool oldPinStatus, u32 oldPinMode);
|
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode);
|
103
source/crypto.c
103
source/crypto.c
@ -24,11 +24,11 @@
|
|||||||
* Crypto libs from http://github.com/b1l1s/ctr
|
* Crypto libs from http://github.com/b1l1s/ctr
|
||||||
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
||||||
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
|
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
|
||||||
* 3ds type structs adapted from 3DBrew and https://github.com/mid-kid/CakesForeveryWan/blob/master/source/headers.h
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "fatfs/sdmmc/sdmmc.h"
|
#include "fatfs/sdmmc/sdmmc.h"
|
||||||
|
|
||||||
/****************************************************************
|
/****************************************************************
|
||||||
@ -257,6 +257,8 @@ static void sha_wait_idle()
|
|||||||
|
|
||||||
static void sha(void *res, const void *src, u32 size, u32 mode)
|
static void sha(void *res, const void *src, u32 size, u32 mode)
|
||||||
{
|
{
|
||||||
|
backupAndRestoreShaHash(false);
|
||||||
|
|
||||||
sha_wait_idle();
|
sha_wait_idle();
|
||||||
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
|
*REG_SHA_CNT = mode | SHA_CNT_OUTPUT_ENDIAN | SHA_NORMAL_ROUND;
|
||||||
|
|
||||||
@ -301,6 +303,8 @@ static u32 fatStart;
|
|||||||
static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE];
|
static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE];
|
||||||
static bool didShaHashBackup = false;
|
static bool didShaHashBackup = false;
|
||||||
|
|
||||||
|
FirmwareSource firmSource;
|
||||||
|
|
||||||
void ctrNandInit(void)
|
void ctrNandInit(void)
|
||||||
{
|
{
|
||||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||||
@ -310,7 +314,7 @@ void ctrNandInit(void)
|
|||||||
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
||||||
memcpy(nandCtr, shaSum, sizeof(nandCtr));
|
memcpy(nandCtr, shaSum, sizeof(nandCtr));
|
||||||
|
|
||||||
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);
|
||||||
@ -376,41 +380,59 @@ void set6x7xKeys(void)
|
|||||||
memset32((void *)0x01FFCD00, 0, 0x10);
|
memset32((void *)0x01FFCD00, 0, 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void decryptExeFs(Ncch *ncch)
|
bool decryptExeFs(Cxi *cxi)
|
||||||
{
|
{
|
||||||
u8 *exeFsOffset = (u8 *)ncch + ncch->exeFsOffset * 0x200;
|
bool isCxi;
|
||||||
u32 exeFsSize = ncch->exeFsSize * 0x200;
|
|
||||||
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
|
||||||
|
|
||||||
for(u32 i = 0; i < 8; i++)
|
if(memcmp(cxi->ncch.magic, "NCCH", 4) == 0)
|
||||||
ncchCtr[7 - i] = ncch->partitionId[i];
|
{
|
||||||
ncchCtr[8] = 2;
|
isCxi = true;
|
||||||
|
|
||||||
aes_setkey(0x2C, ncch, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
u8 *exeFsOffset = (u8 *)cxi + cxi->ncch.exeFsOffset * 0x200;
|
||||||
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
u32 exeFsSize = cxi->ncch.exeFsSize * 0x200;
|
||||||
aes_use_keyslot(0x2C);
|
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||||
aes(ncch, 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;
|
||||||
|
|
||||||
|
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 + 0x200, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
}
|
||||||
|
else isCxi = false;
|
||||||
|
|
||||||
|
return isCxi && memcmp(cxi, "FIRM", 4) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void decryptNusFirm(const Ticket *ticket, Ncch *ncch, u32 ncchSize)
|
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||||
{
|
{
|
||||||
const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
|
bool isTicket;
|
||||||
u8 __attribute__((aligned(4))) titleKey[AES_BLOCK_SIZE];
|
|
||||||
u8 __attribute__((aligned(4))) cetkIv[AES_BLOCK_SIZE] = {0};
|
|
||||||
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
|
|
||||||
memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId));
|
|
||||||
|
|
||||||
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
if(memcmp(ticket->sigIssuer, "Root", 4) == 0)
|
||||||
aes_use_keyslot(0x3D);
|
{
|
||||||
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
isTicket = true;
|
||||||
|
|
||||||
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0};
|
const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
|
||||||
|
u8 __attribute__((aligned(4))) titleKey[AES_BLOCK_SIZE];
|
||||||
|
u8 __attribute__((aligned(4))) cetkIv[AES_BLOCK_SIZE] = {0};
|
||||||
|
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
|
||||||
|
memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId));
|
||||||
|
|
||||||
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
aes_use_keyslot(0x16);
|
aes_use_keyslot(0x3D);
|
||||||
aes(ncch, ncch, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
decryptExeFs(ncch);
|
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0};
|
||||||
|
|
||||||
|
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
aes_use_keyslot(0x16);
|
||||||
|
aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
}
|
||||||
|
else isTicket = false;
|
||||||
|
|
||||||
|
return isTicket && decryptExeFs(cxi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kernel9Loader(Arm9Bin *arm9Section)
|
void kernel9Loader(Arm9Bin *arm9Section)
|
||||||
@ -430,10 +452,10 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 startOfArm9Bin = *(u32 *)((u8 *)arm9Section + 0x800);
|
u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
|
||||||
bool needToDecrypt = startOfArm9Bin != 0x47704770 && startOfArm9Bin != 0xB0862000;
|
bool needToDecrypt = *startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000;
|
||||||
|
|
||||||
if(!isDevUnit && (k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt)))
|
if(!ISDEVUNIT && (k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt)))
|
||||||
{
|
{
|
||||||
//Set 0x11 keyslot
|
//Set 0x11 keyslot
|
||||||
const u8 __attribute__((aligned(4))) key1[AES_BLOCK_SIZE] = {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8};
|
const u8 __attribute__((aligned(4))) key1[AES_BLOCK_SIZE] = {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8};
|
||||||
@ -474,7 +496,9 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
|||||||
|
|
||||||
//Decrypt ARM9 binary
|
//Decrypt ARM9 binary
|
||||||
aes_use_keyslot(arm9BinSlot);
|
aes_use_keyslot(arm9BinSlot);
|
||||||
aes((u8 *)arm9Section + 0x800, (u8 *)arm9Section + 0x800, arm9BinSize / AES_BLOCK_SIZE, arm9BinCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes(startOfArm9Bin, startOfArm9Bin, arm9BinSize / AES_BLOCK_SIZE, arm9BinCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
|
if(*startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000) error("Error decrypting New 3DS ARM9 Binary.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set >=9.6 KeyXs
|
//Set >=9.6 KeyXs
|
||||||
@ -498,19 +522,20 @@ void computePinHash(u8 *outbuf, const u8 *inbuf)
|
|||||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||||
u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE];
|
u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE];
|
||||||
|
|
||||||
if(isA9lh && !didShaHashBackup)
|
|
||||||
{
|
|
||||||
memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
|
|
||||||
didShaHashBackup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
sdmmc_get_cid(1, (u32 *)cid);
|
sdmmc_get_cid(1, (u32 *)cid);
|
||||||
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
||||||
aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
|
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreShaHashBackup(void)
|
void backupAndRestoreShaHash(bool isRestore)
|
||||||
{
|
{
|
||||||
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
|
if(ISA9LH)
|
||||||
|
{
|
||||||
|
if(isRestore)
|
||||||
|
{
|
||||||
|
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
|
||||||
|
}
|
||||||
|
else if(!didShaHashBackup) memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
|
||||||
|
}
|
||||||
}
|
}
|
@ -24,7 +24,6 @@
|
|||||||
* Crypto libs from http://github.com/b1l1s/ctr
|
* Crypto libs from http://github.com/b1l1s/ctr
|
||||||
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
* kernel9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
||||||
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
|
* decryptNusFirm code adapted from https://github.com/mid-kid/CakesForeveryWan/blob/master/source/firm.c
|
||||||
* 3ds type structs adapted from 3DBrew and https://github.com/mid-kid/CakesForeveryWan/blob/master/source/headers.h
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@ -102,86 +101,15 @@
|
|||||||
#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)
|
||||||
|
|
||||||
typedef struct Ncch {
|
|
||||||
uint8_t sig[0x100]; //RSA-2048 signature of the NCCH header, using SHA-256
|
|
||||||
char magic[4]; //NCCH
|
|
||||||
uint32_t contentSize; //Media unit
|
|
||||||
uint8_t partitionId[8];
|
|
||||||
uint8_t makerCode[2];
|
|
||||||
uint16_t version;
|
|
||||||
uint8_t reserved1[4];
|
|
||||||
uint8_t programID[8];
|
|
||||||
uint8_t reserved2[0x10];
|
|
||||||
uint8_t logoHash[0x20]; //Logo Region SHA-256 hash
|
|
||||||
char productCode[0x10];
|
|
||||||
uint8_t exHeaderHash[0x20]; //Extended header SHA-256 hash
|
|
||||||
uint32_t exHeaderSize; //Extended header size
|
|
||||||
uint32_t reserved3;
|
|
||||||
uint8_t flags[8];
|
|
||||||
uint32_t plainOffset; //Media unit
|
|
||||||
uint32_t plainSize; //Media unit
|
|
||||||
uint32_t logoOffset; //Media unit
|
|
||||||
uint32_t logoSize; //Media unit
|
|
||||||
uint32_t exeFsOffset; //Media unit
|
|
||||||
uint32_t exeFsSize; //Media unit
|
|
||||||
uint32_t exeFsHashSize; //Media unit
|
|
||||||
uint32_t reserved4;
|
|
||||||
uint32_t romFsOffset; //Media unit
|
|
||||||
uint32_t romFsSize; //Media unit
|
|
||||||
uint32_t romFsHashSize; //Media unit
|
|
||||||
uint32_t reserved5;
|
|
||||||
uint8_t exeFsHash[0x20]; //ExeFS superblock SHA-256 hash
|
|
||||||
uint8_t romFsHash[0x20]; //RomFS superblock SHA-256 hash
|
|
||||||
} Ncch;
|
|
||||||
|
|
||||||
typedef struct Ticket
|
|
||||||
{
|
|
||||||
char sigIssuer[0x40];
|
|
||||||
uint8_t eccPubKey[0x3C];
|
|
||||||
uint8_t version;
|
|
||||||
uint8_t caCrlVersion;
|
|
||||||
uint8_t signerCrlVersion;
|
|
||||||
uint8_t titleKey[0x10];
|
|
||||||
uint8_t reserved1;
|
|
||||||
uint8_t ticketId[8];
|
|
||||||
uint8_t consoleId[4];
|
|
||||||
uint8_t titleId[8];
|
|
||||||
uint8_t reserved2[2];
|
|
||||||
uint16_t ticketTitleVersion;
|
|
||||||
uint8_t reserved3[8];
|
|
||||||
uint8_t licenseType;
|
|
||||||
uint8_t ticketCommonKeyYIndex; //Ticket common keyY index, usually 0x1 for retail system titles.
|
|
||||||
uint8_t reserved4[0x2A];
|
|
||||||
uint8_t unk[4]; //eShop Account ID?
|
|
||||||
uint8_t reserved5;
|
|
||||||
uint8_t audit;
|
|
||||||
uint8_t reserved6[0x42];
|
|
||||||
uint8_t limits[0x40];
|
|
||||||
uint8_t contentIndex[0xAC];
|
|
||||||
} Ticket;
|
|
||||||
|
|
||||||
typedef struct Arm9Bin {
|
|
||||||
uint8_t keyX[0x10];
|
|
||||||
uint8_t keyY[0x10];
|
|
||||||
uint8_t ctr[0x10];
|
|
||||||
char size[8];
|
|
||||||
uint8_t reserved[8];
|
|
||||||
uint8_t ctlBlock[0x10];
|
|
||||||
char magic[4];
|
|
||||||
uint8_t reserved2[0xC];
|
|
||||||
uint8_t slot0x16keyX[0x10];
|
|
||||||
} Arm9Bin;
|
|
||||||
|
|
||||||
extern u32 emuOffset;
|
extern u32 emuOffset;
|
||||||
extern bool isN3DS, isDevUnit, isA9lh;
|
|
||||||
extern FirmwareSource firmSource;
|
extern FirmwareSource firmSource;
|
||||||
|
|
||||||
void ctrNandInit(void);
|
void ctrNandInit(void);
|
||||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||||
int ctrNandWrite(u32 sector, u32 sectorCount, u8 *inbuf);
|
int ctrNandWrite(u32 sector, u32 sectorCount, u8 *inbuf);
|
||||||
void set6x7xKeys(void);
|
void set6x7xKeys(void);
|
||||||
void decryptExeFs(Ncch *ncch);
|
bool decryptExeFs(Cxi *cxi);
|
||||||
void decryptNusFirm(const Ticket *ticket, Ncch *ncch, u32 ncchSize);
|
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize);
|
||||||
void kernel9Loader(Arm9Bin *arm9Section);
|
void kernel9Loader(Arm9Bin *arm9Section);
|
||||||
void computePinHash(u8 *outbuf, const u8 *inbuf);
|
void computePinHash(u8 *outbuf, const u8 *inbuf);
|
||||||
void restoreShaHashBackup(void);
|
void backupAndRestoreShaHash(bool isRestore);
|
113
source/emunand.c
113
source/emunand.c
@ -25,6 +25,8 @@
|
|||||||
#include "fatfs/sdmmc/sdmmc.h"
|
#include "fatfs/sdmmc/sdmmc.h"
|
||||||
#include "../build/bundled.h"
|
#include "../build/bundled.h"
|
||||||
|
|
||||||
|
u32 emuOffset;
|
||||||
|
|
||||||
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||||
{
|
{
|
||||||
static u8 __attribute__((aligned(4))) temp[0x200];
|
static u8 __attribute__((aligned(4))) temp[0x200];
|
||||||
@ -50,7 +52,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
nandOffset = ROUND_TO_4MB(nandSize + 1); //"Default" layout
|
nandOffset = ROUND_TO_4MB(nandSize + 1); //"Default" layout
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
nandOffset = roundedMinsizes[isN3DS ? 1 : 0]; //"Minsize" layout
|
nandOffset = roundedMinsizes[ISN3DS ? 1 : 0]; //"Minsize" layout
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
nandOffset = *nandType == FIRMWARE_EMUNAND ? 0 : (nandSize > 0x200000 ? 0x400000 : 0x200000); //"Legacy" layout
|
nandOffset = *nandType == FIRMWARE_EMUNAND ? 0 : (nandSize > 0x200000 ? 0x400000 : 0x200000); //"Legacy" layout
|
||||||
@ -59,10 +61,10 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
|
|
||||||
if(*nandType != FIRMWARE_EMUNAND) nandOffset *= ((u32)*nandType - 1);
|
if(*nandType != FIRMWARE_EMUNAND) nandOffset *= ((u32)*nandType - 1);
|
||||||
|
|
||||||
if(fatStart >= nandOffset + roundedMinsizes[isN3DS ? 1 : 0])
|
if(fatStart >= nandOffset + roundedMinsizes[ISN3DS ? 1 : 0])
|
||||||
{
|
{
|
||||||
//Check for RedNAND
|
//Check for RedNAND
|
||||||
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) && *(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
|
||||||
{
|
{
|
||||||
emuOffset = nandOffset + 1;
|
emuOffset = nandOffset + 1;
|
||||||
*emuHeader = nandOffset + 1;
|
*emuHeader = nandOffset + 1;
|
||||||
@ -70,7 +72,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Check for Gateway EmuNAND
|
//Check for Gateway EmuNAND
|
||||||
else if(i != 2 && !sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) && *(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
else if(i != 2 && !sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) && memcmp(temp + 0x100, "NCSD", 4) == 0)
|
||||||
{
|
{
|
||||||
emuOffset = nandOffset;
|
emuOffset = nandOffset;
|
||||||
*emuHeader = nandOffset + nandSize;
|
*emuHeader = nandOffset + nandSize;
|
||||||
@ -93,68 +95,111 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 *getFreeK9Space(u8 *pos, u32 size)
|
static inline u32 getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||||
|
|
||||||
//Looking for the last free space before Process9
|
//Looking for the last free space before Process9
|
||||||
return memsearch(pos + 0x13500, pattern, size - 0x13500, sizeof(pattern)) + 0x455;
|
*freeK9Space = memsearch(pos + 0x13500, pattern, size - 0x13500, sizeof(pattern)) + 0x455;
|
||||||
|
|
||||||
|
return *freeK9Space == NULL ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 getSdmmc(u8 *pos, u32 size)
|
static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc)
|
||||||
{
|
{
|
||||||
//Look for struct code
|
//Look for struct code
|
||||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
return *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*sdmmc = *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void patchNandRw(u8 *pos, u32 size, u32 branchOffset)
|
static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset)
|
||||||
{
|
{
|
||||||
//Look for read/write code
|
//Look for read/write code
|
||||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 3,
|
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
*writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern)) - 3;
|
|
||||||
|
|
||||||
*readOffset = *writeOffset = 0x4C00;
|
if(readOffset == NULL) ret = 1;
|
||||||
readOffset[1] = writeOffset[1] = 0x47A0;
|
else
|
||||||
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
{
|
||||||
|
readOffset -= 3;
|
||||||
|
|
||||||
|
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
|
||||||
|
|
||||||
|
if(writeOffset == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeOffset -= 3;
|
||||||
|
*readOffset = *writeOffset = 0x4C00;
|
||||||
|
readOffset[1] = writeOffset[1] = 0x47A0;
|
||||||
|
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void patchMpu(u8 *pos, u32 size)
|
static inline u32 patchMpu(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for MPU pattern
|
//Look for MPU pattern
|
||||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
off[0] = 0x00360003;
|
if(off == NULL) ret = 1;
|
||||||
off[6] = 0x00200603;
|
else
|
||||||
off[9] = 0x001C0603;
|
{
|
||||||
|
off[0] = 0x00360003;
|
||||||
|
off[6] = 0x00200603;
|
||||||
|
off[9] = 0x001C0603;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive)
|
u32 patchEmuNand(u8 *arm9Section, u8 *process9Offset, u32 process9Size, u32 emuHeader)
|
||||||
{
|
{
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
//Copy EmuNAND code
|
//Copy EmuNAND code
|
||||||
u8 *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize);
|
u8 *freeK9Space;
|
||||||
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
ret += getFreeK9Space(arm9Section, firm->section[2].size, &freeK9Space);
|
||||||
|
if(!ret)
|
||||||
|
{
|
||||||
|
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
||||||
|
|
||||||
//Add the data of the found EmuNAND
|
//Add the data of the found EmuNAND
|
||||||
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
|
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
|
||||||
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
|
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
|
||||||
*posOffset = emuOffset;
|
*posOffset = emuOffset;
|
||||||
*posHeader = emuHeader;
|
*posHeader = emuHeader;
|
||||||
|
|
||||||
//Find and add the SDMMC struct
|
//Find and add the SDMMC struct
|
||||||
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
|
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
|
||||||
*posSdmmc = getSdmmc(process9Offset, process9Size);
|
u32 sdmmc;
|
||||||
|
ret += getSdmmc(process9Offset, process9Size, &sdmmc);
|
||||||
|
if(!ret) *posSdmmc = sdmmc;
|
||||||
|
|
||||||
//Add EmuNAND hooks
|
//Add EmuNAND hooks
|
||||||
u32 branchOffset = (u32)freeK9Space - branchAdditive;
|
ret += patchNandRw(process9Offset, process9Size, (u32)(freeK9Space - arm9Section + firm->section[2].address));
|
||||||
patchNandRw(process9Offset, process9Size, branchOffset);
|
|
||||||
|
|
||||||
//Set MPU
|
//Set MPU
|
||||||
patchMpu(arm9Section, arm9SectionSize);
|
ret += patchMpu(arm9Section, firm->section[2].size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
@ -24,11 +24,7 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define NCSD_MAGIC 0x4453434E
|
|
||||||
#define ROUND_TO_4MB(a) (((a) + 0x2000 - 1) & (~(0x2000 - 1)))
|
#define ROUND_TO_4MB(a) (((a) + 0x2000 - 1) & (~(0x2000 - 1)))
|
||||||
|
|
||||||
extern u32 emuOffset;
|
|
||||||
extern bool isN3DS;
|
|
||||||
|
|
||||||
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType);
|
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType);
|
||||||
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive);
|
u32 patchEmuNand(u8 *arm9Section, u8 *process9Offset, u32 process9Size, u32 emuHeader);
|
@ -45,17 +45,22 @@ void installArm9Handlers(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
||||||
{
|
{
|
||||||
u32 *initFPU;
|
u32 ret = 0;
|
||||||
for(initFPU = exceptionsPage; initFPU < (exceptionsPage + 0x400) && (initFPU[0] != 0xE59F0008 || initFPU[1] != 0xE5900000); initFPU++);
|
|
||||||
|
|
||||||
u32 *mcuReboot;
|
u32 *initFPU;
|
||||||
for(mcuReboot = exceptionsPage; mcuReboot < (exceptionsPage + 0x400) && (mcuReboot[0] != 0xE59F4104 || mcuReboot[1] != 0xE3A0A0C2); mcuReboot++);
|
for(initFPU = exceptionsPage; initFPU < exceptionsPage + 0x400 && (initFPU[0] != 0xE59F0008 || initFPU[1] != 0xE5900000); initFPU++);
|
||||||
mcuReboot--;
|
if(initFPU == exceptionsPage + 0x400) ret = 1;
|
||||||
|
|
||||||
u32 *freeSpace;
|
u32 *freeSpace;
|
||||||
for(freeSpace = initFPU; freeSpace < (exceptionsPage + 0x400) && (freeSpace[0] != 0xFFFFFFFF || freeSpace[1] != 0xFFFFFFFF); freeSpace++);
|
for(freeSpace = initFPU; freeSpace < exceptionsPage + 0x400 && (freeSpace[0] != 0xFFFFFFFF || freeSpace[1] != 0xFFFFFFFF); freeSpace++);
|
||||||
|
if(initFPU == exceptionsPage + 0x400) ret = 1;
|
||||||
|
|
||||||
|
u32 *mcuReboot;
|
||||||
|
for(mcuReboot = exceptionsPage; mcuReboot < exceptionsPage + 0x400 && (mcuReboot[0] != 0xE59F4104 || mcuReboot[1] != 0xE3A0A0C2); mcuReboot++);
|
||||||
|
if(initFPU == exceptionsPage + 0x400) ret = 1;
|
||||||
|
mcuReboot--;
|
||||||
|
|
||||||
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
|
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
|
||||||
|
|
||||||
@ -76,6 +81,8 @@ void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffs
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void detectAndProcessExceptionDumps(void)
|
void detectAndProcessExceptionDumps(void)
|
||||||
|
@ -43,5 +43,5 @@ typedef struct __attribute__((packed))
|
|||||||
} ExceptionDumpHeader;
|
} ExceptionDumpHeader;
|
||||||
|
|
||||||
void installArm9Handlers(void);
|
void installArm9Handlers(void);
|
||||||
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset);
|
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset);
|
||||||
void detectAndProcessExceptionDumps(void);
|
void detectAndProcessExceptionDumps(void);
|
395
source/firm.c
395
source/firm.c
@ -31,256 +31,11 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "emunand.h"
|
#include "emunand.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "draw.h"
|
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "buttons.h"
|
|
||||||
#include "pin.h"
|
|
||||||
#include "../build/bundled.h"
|
#include "../build/bundled.h"
|
||||||
|
|
||||||
extern u16 launchedFirmTidLow[8]; //Defined in start.s
|
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSdMode)
|
||||||
|
|
||||||
static firmHeader *firm = (firmHeader *)0x24000000;
|
|
||||||
static const firmSectionHeader *section;
|
|
||||||
|
|
||||||
u32 emuOffset;
|
|
||||||
bool isN3DS,
|
|
||||||
isDevUnit,
|
|
||||||
isA9lh,
|
|
||||||
isFirmlaunch;
|
|
||||||
CfgData configData;
|
|
||||||
FirmwareSource firmSource;
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
{
|
||||||
u32 configTemp,
|
|
||||||
emuHeader;
|
|
||||||
FirmwareType firmType;
|
|
||||||
FirmwareSource nandType;
|
|
||||||
ConfigurationStatus needConfig;
|
|
||||||
|
|
||||||
//Detect the console being used
|
|
||||||
isN3DS = PDN_MPCORE_CFG == 7;
|
|
||||||
|
|
||||||
//Detect dev units
|
|
||||||
isDevUnit = CFG_UNITINFO != 0;
|
|
||||||
|
|
||||||
//Mount SD or CTRNAND
|
|
||||||
Fs fsStatus;
|
|
||||||
if(mountFs(true)) fsStatus = SD_CARD;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
firmSource = FIRMWARE_SYSNAND;
|
|
||||||
fsStatus = (mountFs(false) && switchToCtrNand()) ? CTRNAND : NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Attempt to read the configuration file
|
|
||||||
needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
|
|
||||||
|
|
||||||
u32 devMode = MULTICONFIG(DEVOPTIONS);
|
|
||||||
|
|
||||||
//Determine if this is a firmlaunch boot
|
|
||||||
if(launchedFirmTidLow[5] != 0)
|
|
||||||
{
|
|
||||||
isFirmlaunch = true;
|
|
||||||
|
|
||||||
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
|
|
||||||
|
|
||||||
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
|
||||||
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
|
|
||||||
|
|
||||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
|
||||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
|
||||||
isA9lh = BOOTCFG_A9LH != 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
isFirmlaunch = false;
|
|
||||||
firmType = NATIVE_FIRM;
|
|
||||||
|
|
||||||
//Determine if booting with A9LH
|
|
||||||
isA9lh = !PDN_SPI_CNT;
|
|
||||||
|
|
||||||
if(fsStatus == NONE) error("Error mounting SD and CTRNAND.");
|
|
||||||
|
|
||||||
if(devMode != 0 && isA9lh) detectAndProcessExceptionDumps();
|
|
||||||
|
|
||||||
//Get pressed buttons
|
|
||||||
u32 pressed = HID_PAD;
|
|
||||||
|
|
||||||
//Save old options and begin saving the new boot configuration
|
|
||||||
configTemp = (configData.config & 0xFFFFFE00) | ((u32)isA9lh << 6);
|
|
||||||
|
|
||||||
//If it's a MCU reboot, try to force boot options
|
|
||||||
if(isA9lh && CFG_BOOTENV)
|
|
||||||
{
|
|
||||||
//Always force a sysNAND boot when quitting AGB_FIRM
|
|
||||||
if(CFG_BOOTENV == 7)
|
|
||||||
{
|
|
||||||
nandType = FIRMWARE_SYSNAND;
|
|
||||||
firmSource = CONFIG(USESYSFIRM) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
|
||||||
needConfig = DONT_CONFIGURE;
|
|
||||||
|
|
||||||
//Flag to prevent multiple boot options-forcing
|
|
||||||
configTemp |= 1 << 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Else, force the last used boot options unless a button is pressed
|
|
||||||
or the no-forcing flag is set */
|
|
||||||
else if(needConfig != CREATE_CONFIGURATION && !pressed && !BOOTCFG_NOFORCEFLAG)
|
|
||||||
{
|
|
||||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
|
||||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
|
||||||
needConfig = DONT_CONFIGURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(needConfig == DONT_CONFIGURE)
|
|
||||||
{
|
|
||||||
if(devMode != 0 && isA9lh) installArm9Handlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Boot options aren't being forced
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 pinMode = MULTICONFIG(PIN);
|
|
||||||
bool pinExists = pinMode != 0 && verifyPin(pinMode);
|
|
||||||
|
|
||||||
//If no configuration file exists or SELECT is held, load configuration menu
|
|
||||||
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1));
|
|
||||||
|
|
||||||
if(shouldLoadConfigMenu)
|
|
||||||
{
|
|
||||||
configMenu(fsStatus, pinExists, pinMode);
|
|
||||||
|
|
||||||
//Update pressed buttons
|
|
||||||
pressed = HID_PAD;
|
|
||||||
|
|
||||||
devMode = MULTICONFIG(DEVOPTIONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(devMode != 0 && isA9lh) installArm9Handlers();
|
|
||||||
|
|
||||||
if(isA9lh && !CFG_BOOTENV && pressed == SAFE_MODE)
|
|
||||||
{
|
|
||||||
nandType = FIRMWARE_SYSNAND;
|
|
||||||
firmSource = FIRMWARE_SYSNAND;
|
|
||||||
|
|
||||||
//Flag to tell loader to init SD
|
|
||||||
configTemp |= 1 << 8;
|
|
||||||
|
|
||||||
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
|
|
||||||
if(pinExists && !shouldLoadConfigMenu)
|
|
||||||
{
|
|
||||||
while(HID_PAD & PIN_BUTTONS);
|
|
||||||
chrono(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 splashMode = MULTICONFIG(SPLASH);
|
|
||||||
|
|
||||||
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
|
||||||
|
|
||||||
/* If L and R/A/Select or one of the single payload buttons are pressed,
|
|
||||||
chainload an external payload */
|
|
||||||
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
|
||||||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
|
|
||||||
|
|
||||||
if(shouldLoadPayload) loadPayload(pressed);
|
|
||||||
|
|
||||||
if(splashMode == 2) loadSplash();
|
|
||||||
|
|
||||||
if(fsStatus == CTRNAND) nandType = FIRMWARE_SYSNAND;
|
|
||||||
|
|
||||||
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
|
|
||||||
else if(pressed & BUTTON_R1)
|
|
||||||
{
|
|
||||||
//Determine if the user chose to use the SysNAND FIRM as default for a R boot
|
|
||||||
bool useSysAsDefault = isA9lh ? CONFIG(USESYSFIRM) : false;
|
|
||||||
|
|
||||||
nandType = useSysAsDefault ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
|
||||||
firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
|
|
||||||
with their own FIRM */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nandType = (CONFIG(AUTOBOOTSYS) != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
|
||||||
firmSource = nandType;
|
|
||||||
}
|
|
||||||
|
|
||||||
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
|
|
||||||
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
|
||||||
{
|
|
||||||
FirmwareSource temp;
|
|
||||||
switch(pressed & EMUNAND_BUTTONS)
|
|
||||||
{
|
|
||||||
case BUTTON_UP:
|
|
||||||
temp = FIRMWARE_EMUNAND;
|
|
||||||
break;
|
|
||||||
case BUTTON_RIGHT:
|
|
||||||
temp = FIRMWARE_EMUNAND2;
|
|
||||||
break;
|
|
||||||
case BUTTON_DOWN:
|
|
||||||
temp = FIRMWARE_EMUNAND3;
|
|
||||||
break;
|
|
||||||
case BUTTON_LEFT:
|
|
||||||
temp = FIRMWARE_EMUNAND4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
temp = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nandType == FIRMWARE_EMUNAND) nandType = temp;
|
|
||||||
else firmSource = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If we need to boot EmuNAND, make sure it exists
|
|
||||||
if(nandType != FIRMWARE_SYSNAND)
|
|
||||||
{
|
|
||||||
locateEmuNand(&emuHeader, &nandType);
|
|
||||||
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Same if we're using EmuNAND as the FIRM source
|
|
||||||
else if(firmSource != FIRMWARE_SYSNAND)
|
|
||||||
locateEmuNand(&emuHeader, &firmSource);
|
|
||||||
|
|
||||||
if(!isFirmlaunch)
|
|
||||||
{
|
|
||||||
configTemp |= (u32)nandType | ((u32)firmSource << 3);
|
|
||||||
writeConfig(needConfig, configTemp);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool loadFromStorage = CONFIG(LOADEXTFIRMSANDMODULES);
|
|
||||||
u32 firmVersion = loadFirm(&firmType, firmSource, loadFromStorage, fsStatus);
|
|
||||||
|
|
||||||
switch(firmType)
|
|
||||||
{
|
|
||||||
case NATIVE_FIRM:
|
|
||||||
patchNativeFirm(firmVersion, nandType, emuHeader, devMode);
|
|
||||||
break;
|
|
||||||
case SAFE_FIRM:
|
|
||||||
case NATIVE_FIRM1X2X:
|
|
||||||
if(isA9lh) patch1x2xNativeAndSafeFirm(devMode);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
patchLegacyFirm(firmType, firmVersion, devMode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
launchFirm(firmType, loadFromStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bool loadFromStorage, Fs fsStatus)
|
|
||||||
{
|
|
||||||
section = firm->section;
|
|
||||||
|
|
||||||
const char *firmwareFiles[] = {
|
const char *firmwareFiles[] = {
|
||||||
"luma/firmware.bin",
|
"luma/firmware.bin",
|
||||||
"luma/firmware_twl.bin",
|
"luma/firmware_twl.bin",
|
||||||
@ -294,7 +49,7 @@ static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bo
|
|||||||
"luma/cetk_safe"
|
"luma/cetk_safe"
|
||||||
};
|
};
|
||||||
|
|
||||||
if(fsStatus == SD_CARD && !mountFs(false)) error("Error mounting CTRNAND.");
|
if(isSdMode && !mountFs(false)) error("Error mounting CTRNAND.");
|
||||||
|
|
||||||
//Load FIRM from CTRNAND
|
//Load FIRM from CTRNAND
|
||||||
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
||||||
@ -303,12 +58,12 @@ static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bo
|
|||||||
|
|
||||||
bool mustLoadFromStorage = false;
|
bool mustLoadFromStorage = false;
|
||||||
|
|
||||||
if(!isN3DS && *firmType == NATIVE_FIRM)
|
if(!ISN3DS && *firmType == NATIVE_FIRM)
|
||||||
{
|
{
|
||||||
if(firmVersion < 0x18)
|
if(firmVersion < 0x18)
|
||||||
{
|
{
|
||||||
//We can't boot < 3.x EmuNANDs
|
//We can't boot < 3.x EmuNANDs
|
||||||
if(firmSource != FIRMWARE_SYSNAND)
|
if(nandType != FIRMWARE_SYSNAND)
|
||||||
error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it.");
|
error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it.");
|
||||||
|
|
||||||
if(BOOTCFG_SAFEMODE != 0) error("SAFE_MODE is not supported on 1.x/2.x FIRM.");
|
if(BOOTCFG_SAFEMODE != 0) error("SAFE_MODE is not supported on 1.x/2.x FIRM.");
|
||||||
@ -324,19 +79,19 @@ static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bo
|
|||||||
{
|
{
|
||||||
u32 firmSize = fileRead(firm, *firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)*firmType], 0x400000);
|
u32 firmSize = fileRead(firm, *firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)*firmType], 0x400000);
|
||||||
|
|
||||||
if(firmSize > 0)
|
if(firmSize > sizeof(Cxi))
|
||||||
{
|
{
|
||||||
if(memcmp(firm, "FIRM", 4) != 0)
|
if(memcmp(firm, "FIRM", 4) != 0)
|
||||||
{
|
{
|
||||||
u8 cetk[0xA50];
|
u8 cetk[0xA50];
|
||||||
|
|
||||||
if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) == sizeof(cetk))
|
if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) != sizeof(cetk) ||
|
||||||
decryptNusFirm((Ticket *)&cetk[0x140], (Ncch *)firm, firmSize);
|
!decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
|
||||||
else error("The firmware.bin in /luma is encrypted\nor corrupted.");
|
error("The firmware.bin in /luma is encrypted\nor corrupted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check that the FIRM is right for the console from the ARM9 section address
|
//Check that the FIRM is right for the console from the ARM9 section address
|
||||||
if((section[3].offset ? section[3].address : section[2].address) != (isN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
if((firm->section[3].offset ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
||||||
error("The firmware.bin in /luma is not valid for this\nconsole.");
|
error("The firmware.bin in /luma is not valid for this\nconsole.");
|
||||||
|
|
||||||
firmVersion = 0xFFFFFFFF;
|
firmVersion = 0xFFFFFFFF;
|
||||||
@ -346,18 +101,18 @@ static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bo
|
|||||||
if(firmVersion != 0xFFFFFFFF)
|
if(firmVersion != 0xFFFFFFFF)
|
||||||
{
|
{
|
||||||
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
|
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
|
||||||
decryptExeFs((Ncch *)firm);
|
if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return firmVersion;
|
return firmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, u32 devMode)
|
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, u32 devMode)
|
||||||
{
|
{
|
||||||
u8 *arm9Section = (u8 *)firm + section[2].offset,
|
u8 *arm9Section = (u8 *)firm + firm->section[2].offset,
|
||||||
*arm11Section1 = (u8 *)firm + section[1].offset;
|
*arm11Section1 = (u8 *)firm + firm->section[1].offset;
|
||||||
|
|
||||||
if(isN3DS)
|
if(ISN3DS)
|
||||||
{
|
{
|
||||||
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
||||||
kernel9Loader((Arm9Bin *)arm9Section);
|
kernel9Loader((Arm9Bin *)arm9Section);
|
||||||
@ -365,41 +120,39 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Sets the 7.x NCCH KeyX and the 6.x gamecard save data KeyY on >= 6.0 O3DS FIRMs, if not using A9LH or a dev unit
|
//Sets the 7.x NCCH KeyX and the 6.x gamecard save data KeyY on >= 6.0 O3DS FIRMs, if not using A9LH or a dev unit
|
||||||
else if(!isA9lh && firmVersion >= 0x29 && !isDevUnit) set6x7xKeys();
|
else if(!ISA9LH && !ISFIRMLAUNCH && firmVersion >= 0x29 && !ISDEVUNIT) set6x7xKeys();
|
||||||
|
|
||||||
//Find the Process9 .code location, size and memory address
|
//Find the Process9 .code location, size and memory address
|
||||||
u32 process9Size,
|
u32 process9Size,
|
||||||
process9MemAddr;
|
process9MemAddr;
|
||||||
u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr);
|
u8 *process9Offset = getProcess9Info(arm9Section + 0x15000, firm->section[2].size - 0x15000, &process9Size, &process9MemAddr);
|
||||||
|
|
||||||
//Find Kernel11 SVC table and handler, exceptions page and free space locations
|
//Find the Kernel11 SVC table and handler, exceptions page and free space locations
|
||||||
u32 baseK11VA;
|
u32 baseK11VA;
|
||||||
u8 *freeK11Space;
|
u8 *freeK11Space;
|
||||||
u32 *arm11SvcHandler,
|
u32 *arm11SvcHandler,
|
||||||
*arm11ExceptionsPage,
|
*arm11ExceptionsPage,
|
||||||
*arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
|
*arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
|
||||||
|
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
//Apply signature patches
|
//Apply signature patches
|
||||||
patchSignatureChecks(process9Offset, process9Size);
|
ret += patchSignatureChecks(process9Offset, process9Size);
|
||||||
|
|
||||||
//Apply EmuNAND patches
|
//Apply EmuNAND patches
|
||||||
if(nandType != FIRMWARE_SYSNAND)
|
if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, process9Offset, process9Size, emuHeader);
|
||||||
{
|
|
||||||
u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address;
|
|
||||||
patchEmuNand(arm9Section, section[2].size, process9Offset, process9Size, emuHeader, branchAdditive);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
||||||
else if(isA9lh) patchFirmWrites(process9Offset, process9Size);
|
else if(ISA9LH || (ISFIRMLAUNCH && BOOTCFG_A9LH != 0)) ret += patchFirmWrites(process9Offset, process9Size);
|
||||||
|
|
||||||
//Apply firmlaunch patches
|
//Apply firmlaunch patches
|
||||||
patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
|
ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
|
||||||
|
|
||||||
//11.0 FIRM patches
|
//11.0 FIRM patches
|
||||||
if(firmVersion >= (isN3DS ? 0x21 : 0x52))
|
if(firmVersion >= (ISN3DS ? 0x21 : 0x52))
|
||||||
{
|
{
|
||||||
//Apply anti-anti-DG patches
|
//Apply anti-anti-DG patches
|
||||||
patchTitleInstallMinVersionCheck(process9Offset, process9Size);
|
ret += patchTitleInstallMinVersionChecks(process9Offset, process9Size, firmVersion);
|
||||||
|
|
||||||
//Restore svcBackdoor
|
//Restore svcBackdoor
|
||||||
reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
||||||
@ -408,69 +161,103 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
|
|||||||
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
||||||
|
|
||||||
//Apply UNITINFO patch
|
//Apply UNITINFO patch
|
||||||
if(devMode == 2) patchUnitInfoValueSet(arm9Section, section[2].size);
|
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, firm->section[2].size);
|
||||||
|
|
||||||
if(devMode != 0 && isA9lh)
|
if(devMode != 0 && ISA9LH)
|
||||||
{
|
{
|
||||||
//ARM11 exception handlers
|
//ARM11 exception handlers
|
||||||
u32 codeSetOffset,
|
u32 codeSetOffset,
|
||||||
stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &codeSetOffset);
|
stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, firm->section[1].size, &codeSetOffset);
|
||||||
installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
|
ret += installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
|
||||||
patchSvcBreak11(arm11Section1, arm11SvcTable);
|
patchSvcBreak11(arm11Section1, arm11SvcTable);
|
||||||
patchKernel11Panic(arm11Section1, section[1].size);
|
ret += patchKernel11Panic(arm11Section1, firm->section[1].size);
|
||||||
|
|
||||||
//ARM9 exception handlers
|
//ARM9 exception handlers
|
||||||
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
|
ret += patchArm9ExceptionHandlersInstall(arm9Section, firm->section[2].size);
|
||||||
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address);
|
ret += patchSvcBreak9(arm9Section, firm->section[2].size, (u32)firm->section[2].address);
|
||||||
patchKernel9Panic(arm9Section, section[2].size);
|
ret += patchKernel9Panic(arm9Section, firm->section[2].size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(CONFIG(PATCHACCESS))
|
if(CONFIG(PATCHACCESS))
|
||||||
{
|
{
|
||||||
patchArm11SvcAccessChecks(arm11SvcHandler);
|
ret += patchArm11SvcAccessChecks(arm11SvcHandler, (u32 *)(arm11Section1 + firm->section[1].size));
|
||||||
patchK11ModuleChecks(arm11Section1, section[1].size, &freeK11Space);
|
ret += patchK11ModuleChecks(arm11Section1, firm->section[1].size, &freeK11Space);
|
||||||
patchP9AccessChecks(process9Offset, process9Size);
|
ret += patchP9AccessChecks(process9Offset, process9Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void patchLegacyFirm(FirmwareType firmType, u32 firmVersion, u32 devMode)
|
u32 patchTwlFirm(u32 firmVersion, u32 devMode)
|
||||||
{
|
{
|
||||||
u8 *arm9Section = (u8 *)firm + section[3].offset;
|
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
||||||
if(isN3DS)
|
if(ISN3DS)
|
||||||
{
|
{
|
||||||
kernel9Loader((Arm9Bin *)arm9Section);
|
kernel9Loader((Arm9Bin *)arm9Section);
|
||||||
firm->arm9Entry = (u8 *)0x801301C;
|
firm->arm9Entry = (u8 *)0x801301C;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isN3DS || firmVersion >= (firmType == TWL_FIRM ? 0x16 : 0xB))
|
ret += patchLgySignatureChecks(arm9Section, firm->section[3].size);
|
||||||
applyLegacyFirmPatches((u8 *)firm, firmType);
|
ret += patchTwlInvalidSignatureChecks(arm9Section, firm->section[3].size);
|
||||||
|
ret += patchTwlNintendoLogoChecks(arm9Section, firm->section[3].size);
|
||||||
|
ret += patchTwlWhitelistChecks(arm9Section, firm->section[3].size);
|
||||||
|
if(!ISN3DS && firmVersion == 0x11) ret += patchOldTwlFlashcartChecks(arm9Section, firm->section[3].size);
|
||||||
|
else ret += patchTwlFlashcartChecks(arm9Section, firm->section[3].size, firmVersion);
|
||||||
|
ret += patchTwlShaHashChecks(arm9Section, firm->section[3].size);
|
||||||
|
|
||||||
//Apply UNITINFO patch
|
//Apply UNITINFO patch
|
||||||
if(devMode == 2) patchUnitInfoValueSet(arm9Section, section[3].size);
|
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, firm->section[3].size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void patch1x2xNativeAndSafeFirm(u32 devMode)
|
u32 patchAgbFirm(u32 devMode)
|
||||||
{
|
{
|
||||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
u8 *arm9Section = (u8 *)firm + firm->section[3].offset;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
|
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
||||||
|
if(ISN3DS)
|
||||||
|
{
|
||||||
|
kernel9Loader((Arm9Bin *)arm9Section);
|
||||||
|
firm->arm9Entry = (u8 *)0x801301C;
|
||||||
|
}
|
||||||
|
|
||||||
if(isN3DS)
|
ret += patchLgySignatureChecks(arm9Section, firm->section[3].size);
|
||||||
|
if(CONFIG(SHOWGBABOOT)) ret += patchAgbBootSplash(arm9Section, firm->section[3].size);
|
||||||
|
|
||||||
|
//Apply UNITINFO patch
|
||||||
|
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, firm->section[3].size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patch1x2xNativeAndSafeFirm(u32 devMode)
|
||||||
|
{
|
||||||
|
u8 *arm9Section = (u8 *)firm + firm->section[2].offset;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
|
if(ISN3DS)
|
||||||
{
|
{
|
||||||
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader
|
||||||
kernel9Loader((Arm9Bin *)arm9Section);
|
kernel9Loader((Arm9Bin *)arm9Section);
|
||||||
firm->arm9Entry = (u8 *)0x801B01C;
|
firm->arm9Entry = (u8 *)0x801B01C;
|
||||||
|
|
||||||
patchFirmWrites(arm9Section, section[2].size);
|
ret += patchFirmWrites(arm9Section, firm->section[2].size);
|
||||||
}
|
}
|
||||||
else patchOldFirmWrites(arm9Section, section[2].size);
|
else ret += patchOldFirmWrites(arm9Section, firm->section[2].size);
|
||||||
|
|
||||||
if(devMode != 0)
|
if(devMode != 0)
|
||||||
{
|
{
|
||||||
//ARM9 exception handlers
|
//ARM9 exception handlers
|
||||||
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
|
ret += patchArm9ExceptionHandlersInstall(arm9Section, firm->section[2].size);
|
||||||
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address);
|
ret += patchSvcBreak9(arm9Section, firm->section[2].size, (u32)firm->section[2].address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void copySection0AndInjectSystemModules(FirmwareType firmType, bool loadFromStorage)
|
static inline void copySection0AndInjectSystemModules(FirmwareType firmType, bool loadFromStorage)
|
||||||
@ -478,11 +265,11 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
|||||||
u32 srcModuleSize,
|
u32 srcModuleSize,
|
||||||
dstModuleSize;
|
dstModuleSize;
|
||||||
|
|
||||||
for(u8 *src = (u8 *)firm + section[0].offset, *srcEnd = src + section[0].size, *dst = section[0].address;
|
for(u8 *src = (u8 *)firm + firm->section[0].offset, *srcEnd = src + firm->section[0].size, *dst = firm->section[0].address;
|
||||||
src < srcEnd; src += srcModuleSize, dst += dstModuleSize)
|
src < srcEnd; src += srcModuleSize, dst += dstModuleSize)
|
||||||
{
|
{
|
||||||
srcModuleSize = ((Ncch *)src)->contentSize * 0x200;
|
srcModuleSize = ((Cxi *)src)->ncch.contentSize * 0x200;
|
||||||
const char *moduleName = (char *)(src + 0x200);
|
const char *moduleName = ((Cxi *)src)->exHeader.systemControlInfo.appTitle;
|
||||||
|
|
||||||
u32 fileSize;
|
u32 fileSize;
|
||||||
|
|
||||||
@ -520,7 +307,7 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void launchFirm(FirmwareType firmType, bool loadFromStorage)
|
void launchFirm(FirmwareType firmType, bool loadFromStorage)
|
||||||
{
|
{
|
||||||
//Allow module injection and/or inject 3ds_injector on new NATIVE_FIRMs and LGY FIRMs
|
//Allow module injection and/or inject 3ds_injector on new NATIVE_FIRMs and LGY FIRMs
|
||||||
u32 sectionNum;
|
u32 sectionNum;
|
||||||
@ -532,12 +319,12 @@ static inline void launchFirm(FirmwareType firmType, bool loadFromStorage)
|
|||||||
else sectionNum = 0;
|
else sectionNum = 0;
|
||||||
|
|
||||||
//Copy FIRM sections to respective memory locations
|
//Copy FIRM sections to respective memory locations
|
||||||
for(; sectionNum < 4 && section[sectionNum].size != 0; sectionNum++)
|
for(; sectionNum < 4 && firm->section[sectionNum].size != 0; sectionNum++)
|
||||||
memcpy(section[sectionNum].address, (u8 *)firm + section[sectionNum].offset, section[sectionNum].size);
|
memcpy(firm->section[sectionNum].address, (u8 *)firm + firm->section[sectionNum].offset, firm->section[sectionNum].size);
|
||||||
|
|
||||||
//Determine the ARM11 entry to use
|
//Determine the ARM11 entry to use
|
||||||
vu32 *arm11;
|
vu32 *arm11;
|
||||||
if(isFirmlaunch) arm11 = (vu32 *)0x1FFFFFFC;
|
if(ISFIRMLAUNCH) arm11 = (vu32 *)0x1FFFFFFC;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
deinitScreens();
|
deinitScreens();
|
||||||
|
@ -24,32 +24,9 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSdMode);
|
||||||
#define CFG_UNITINFO (*(vu8 *)0x10010010)
|
u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, u32 devMode);
|
||||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
u32 patchTwlFirm(u32 firmVersion, u32 devMode);
|
||||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
u32 patchAgbFirm(u32 devMode);
|
||||||
|
u32 patch1x2xNativeAndSafeFirm(u32 devMode);
|
||||||
//FIRM Header layout
|
void launchFirm(FirmwareType firmType, bool loadFromStorage);
|
||||||
typedef struct firmSectionHeader {
|
|
||||||
u32 offset;
|
|
||||||
u8 *address;
|
|
||||||
u32 size;
|
|
||||||
u32 procType;
|
|
||||||
u8 hash[0x20];
|
|
||||||
} firmSectionHeader;
|
|
||||||
|
|
||||||
typedef struct firmHeader {
|
|
||||||
u32 magic;
|
|
||||||
u32 reserved1;
|
|
||||||
u8 *arm11Entry;
|
|
||||||
u8 *arm9Entry;
|
|
||||||
u8 reserved2[0x30];
|
|
||||||
firmSectionHeader section[4];
|
|
||||||
} firmHeader;
|
|
||||||
|
|
||||||
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource, bool loadFromStorage, Fs fsStatus);
|
|
||||||
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, u32 devMode);
|
|
||||||
static inline void patchLegacyFirm(FirmwareType firmType, u32 firmVersion, u32 devMode);
|
|
||||||
static inline void patch1x2xNativeAndSafeFirm(u32 devMode);
|
|
||||||
static inline void copySection0AndInjectSystemModules(FirmwareType firmType, bool loadFromSd);
|
|
||||||
static inline void launchFirm(FirmwareType firmType, bool loadFromStorage);
|
|
@ -144,7 +144,7 @@ void loadPayload(u32 pressed)
|
|||||||
{
|
{
|
||||||
loaderAddress[1] = payloadSize;
|
loaderAddress[1] = payloadSize;
|
||||||
|
|
||||||
if(isA9lh) restoreShaHashBackup();
|
backupAndRestoreShaHash(true);
|
||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
flushDCacheRange(loaderAddress, loader_bin_size);
|
flushDCacheRange(loaderAddress, loader_bin_size);
|
||||||
@ -163,7 +163,7 @@ u32 firmRead(void *dest, u32 firmType)
|
|||||||
{ "00000003", "20000003" }};
|
{ "00000003", "20000003" }};
|
||||||
|
|
||||||
char path[48] = "1:/title/00040138/";
|
char path[48] = "1:/title/00040138/";
|
||||||
concatenateStrings(path, firmFolders[firmType][isN3DS ? 1 : 0]);
|
concatenateStrings(path, firmFolders[firmType][ISN3DS ? 1 : 0]);
|
||||||
concatenateStrings(path, "/content");
|
concatenateStrings(path, "/content");
|
||||||
|
|
||||||
DIR dir;
|
DIR dir;
|
||||||
|
@ -26,8 +26,6 @@
|
|||||||
|
|
||||||
#define PATTERN(a) a "_*.bin"
|
#define PATTERN(a) a "_*.bin"
|
||||||
|
|
||||||
extern bool isN3DS, isA9lh;
|
|
||||||
|
|
||||||
bool mountFs(bool isSd);
|
bool mountFs(bool isSd);
|
||||||
bool switchToCtrNand(void);
|
bool switchToCtrNand(void);
|
||||||
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||||
|
249
source/main.c
Normal file
249
source/main.c
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "emunand.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "firm.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "exceptions.h"
|
||||||
|
#include "draw.h"
|
||||||
|
#include "buttons.h"
|
||||||
|
#include "pin.h"
|
||||||
|
|
||||||
|
extern CfgData configData;
|
||||||
|
extern FirmwareSource firmSource;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
u32 configTemp,
|
||||||
|
emuHeader;
|
||||||
|
FirmwareType firmType;
|
||||||
|
FirmwareSource nandType;
|
||||||
|
ConfigurationStatus needConfig;
|
||||||
|
|
||||||
|
//Mount SD or CTRNAND
|
||||||
|
bool isSdMode;
|
||||||
|
if(mountFs(true)) isSdMode = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
firmSource = FIRMWARE_SYSNAND;
|
||||||
|
if(!mountFs(false) || !switchToCtrNand()) error("Error mounting SD and CTRNAND.");
|
||||||
|
isSdMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Attempt to read the configuration file
|
||||||
|
needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
|
||||||
|
|
||||||
|
//Determine if this is a firmlaunch boot
|
||||||
|
if(ISFIRMLAUNCH)
|
||||||
|
{
|
||||||
|
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
|
||||||
|
|
||||||
|
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
||||||
|
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
|
||||||
|
|
||||||
|
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||||
|
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(ISA9LH)
|
||||||
|
{
|
||||||
|
detectAndProcessExceptionDumps();
|
||||||
|
installArm9Handlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
firmType = NATIVE_FIRM;
|
||||||
|
|
||||||
|
//Get pressed buttons
|
||||||
|
u32 pressed = HID_PAD;
|
||||||
|
|
||||||
|
//Save old options and begin saving the new boot configuration
|
||||||
|
configTemp = (configData.config & 0xFFFFFE00) | ((u32)ISA9LH << 6);
|
||||||
|
|
||||||
|
//If it's a MCU reboot, try to force boot options
|
||||||
|
if(ISA9LH && CFG_BOOTENV)
|
||||||
|
{
|
||||||
|
//Always force a SysNAND boot when quitting AGB_FIRM
|
||||||
|
if(CFG_BOOTENV == 7)
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_SYSNAND;
|
||||||
|
firmSource = CONFIG(USESYSFIRM) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
||||||
|
needConfig = DONT_CONFIGURE;
|
||||||
|
|
||||||
|
//Flag to prevent multiple boot options-forcing
|
||||||
|
configTemp |= 1 << 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else, force the last used boot options unless a button is pressed
|
||||||
|
or the no-forcing flag is set */
|
||||||
|
else if(needConfig != CREATE_CONFIGURATION && !pressed && !BOOTCFG_NOFORCEFLAG)
|
||||||
|
{
|
||||||
|
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||||
|
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||||
|
needConfig = DONT_CONFIGURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Boot options aren't being forced
|
||||||
|
if(needConfig != DONT_CONFIGURE)
|
||||||
|
{
|
||||||
|
u32 pinMode = MULTICONFIG(PIN);
|
||||||
|
bool pinExists = pinMode != 0 && verifyPin(pinMode);
|
||||||
|
|
||||||
|
//If no configuration file exists or SELECT is held, load configuration menu
|
||||||
|
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1));
|
||||||
|
|
||||||
|
if(shouldLoadConfigMenu)
|
||||||
|
{
|
||||||
|
configMenu(isSdMode, pinExists, pinMode);
|
||||||
|
|
||||||
|
//Update pressed buttons
|
||||||
|
pressed = HID_PAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ISA9LH && !CFG_BOOTENV && pressed == SAFE_MODE)
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_SYSNAND;
|
||||||
|
firmSource = FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
//Flag to tell loader to init SD
|
||||||
|
configTemp |= 1 << 8;
|
||||||
|
|
||||||
|
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
|
||||||
|
if(pinExists && !shouldLoadConfigMenu)
|
||||||
|
{
|
||||||
|
while(HID_PAD & PIN_BUTTONS);
|
||||||
|
chrono(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 splashMode = MULTICONFIG(SPLASH);
|
||||||
|
|
||||||
|
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
||||||
|
|
||||||
|
/* If L and R/A/Select or one of the single payload buttons are pressed,
|
||||||
|
chainload an external payload */
|
||||||
|
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||||
|
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
|
||||||
|
|
||||||
|
if(shouldLoadPayload) loadPayload(pressed);
|
||||||
|
|
||||||
|
if(splashMode == 2) loadSplash();
|
||||||
|
|
||||||
|
//If booting from CTRNAND, always use SysNAND
|
||||||
|
if(!isSdMode) nandType = FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
|
||||||
|
else if(pressed & BUTTON_R1)
|
||||||
|
{
|
||||||
|
//Determine if the user chose to use the SysNAND FIRM as default for a R boot
|
||||||
|
bool useSysAsDefault = ISA9LH ? CONFIG(USESYSFIRM) : false;
|
||||||
|
|
||||||
|
nandType = useSysAsDefault ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||||
|
firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
|
||||||
|
with their own FIRM */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nandType = (CONFIG(AUTOBOOTSYS) != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||||
|
firmSource = nandType;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
|
||||||
|
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
||||||
|
{
|
||||||
|
FirmwareSource temp;
|
||||||
|
switch(pressed & EMUNAND_BUTTONS)
|
||||||
|
{
|
||||||
|
case BUTTON_UP:
|
||||||
|
temp = FIRMWARE_EMUNAND;
|
||||||
|
break;
|
||||||
|
case BUTTON_RIGHT:
|
||||||
|
temp = FIRMWARE_EMUNAND2;
|
||||||
|
break;
|
||||||
|
case BUTTON_DOWN:
|
||||||
|
temp = FIRMWARE_EMUNAND3;
|
||||||
|
break;
|
||||||
|
case BUTTON_LEFT:
|
||||||
|
temp = FIRMWARE_EMUNAND4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
temp = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nandType == FIRMWARE_EMUNAND) nandType = temp;
|
||||||
|
else firmSource = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we need to boot EmuNAND, make sure it exists
|
||||||
|
if(nandType != FIRMWARE_SYSNAND)
|
||||||
|
{
|
||||||
|
locateEmuNand(&emuHeader, &nandType);
|
||||||
|
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Same if we're using EmuNAND as the FIRM source
|
||||||
|
else if(firmSource != FIRMWARE_SYSNAND)
|
||||||
|
locateEmuNand(&emuHeader, &firmSource);
|
||||||
|
|
||||||
|
if(!ISFIRMLAUNCH)
|
||||||
|
{
|
||||||
|
configTemp |= (u32)nandType | ((u32)firmSource << 3);
|
||||||
|
writeConfig(needConfig, configTemp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loadFromStorage = CONFIG(LOADEXTFIRMSANDMODULES);
|
||||||
|
u32 firmVersion = loadFirm(&firmType, firmSource, loadFromStorage, isSdMode);
|
||||||
|
|
||||||
|
u32 devMode = MULTICONFIG(DEVOPTIONS);
|
||||||
|
|
||||||
|
u32 res;
|
||||||
|
switch(firmType)
|
||||||
|
{
|
||||||
|
case NATIVE_FIRM:
|
||||||
|
res = patchNativeFirm(firmVersion, nandType, emuHeader, devMode);
|
||||||
|
break;
|
||||||
|
case SAFE_FIRM:
|
||||||
|
case NATIVE_FIRM1X2X:
|
||||||
|
res = ISA9LH ? patch1x2xNativeAndSafeFirm(devMode) : 0;
|
||||||
|
break;
|
||||||
|
case TWL_FIRM:
|
||||||
|
res = patchTwlFirm(firmVersion, devMode);
|
||||||
|
break;
|
||||||
|
case AGB_FIRM:
|
||||||
|
res = patchAgbFirm(devMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res != 0) error("Error applying FIRM patches.");
|
||||||
|
|
||||||
|
launchFirm(firmType, loadFromStorage);
|
||||||
|
}
|
592
source/patches.c
592
source/patches.c
@ -28,117 +28,179 @@
|
|||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "../build/bundled.h"
|
#include "../build/bundled.h"
|
||||||
|
|
||||||
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||||
{
|
{
|
||||||
u8 *off = memsearch(pos, "ess9", size, 4);
|
u8 *temp = memsearch(pos, "NCCH", size, 4);
|
||||||
|
|
||||||
*process9Size = *(u32 *)(off - 0x60) * 0x200;
|
if(temp == NULL) error("Error getting Process9 data.");
|
||||||
*process9MemAddr = *(u32 *)(off + 0xC);
|
|
||||||
|
|
||||||
//Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
|
Cxi *off = (Cxi *)(temp - 0x100);
|
||||||
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
|
|
||||||
|
*process9Size = off->ncch.contentSize * 0x200;
|
||||||
|
*process9MemAddr = off->exHeader.systemControlInfo.textCodeSet.address;
|
||||||
|
|
||||||
|
return (u8 *)off + (off->ncch.exeFsOffset * 0x200) + 0x200;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
|
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
|
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)) - 0xB;
|
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
u32 *arm11SvcTable;
|
||||||
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
|
||||||
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
if(*arm11ExceptionsPage == NULL) ret = false;
|
||||||
u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
else
|
||||||
*arm11SvcHandler = arm11SvcTable;
|
{
|
||||||
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
*arm11ExceptionsPage -= 0xB;
|
||||||
|
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||||
|
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
||||||
|
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
||||||
|
arm11SvcTable = *arm11SvcHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
||||||
|
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
||||||
|
}
|
||||||
|
|
||||||
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
|
||||||
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2)) + 1;
|
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||||
|
|
||||||
|
if(*freeK11Space == NULL) ret = false;
|
||||||
|
else (*freeK11Space)++;
|
||||||
|
|
||||||
|
if(!ret) error("Error getting Kernel11 data.");
|
||||||
|
|
||||||
return arm11SvcTable;
|
return arm11SvcTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchSignatureChecks(u8 *pos, u32 size)
|
u32 patchSignatureChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for signature checks
|
//Look for signature checks
|
||||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)),
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
*off2 = (u16 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) - 1);
|
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||||
|
|
||||||
*off = off2[0] = 0x2000;
|
if(off == NULL || temp == NULL) ret = 1;
|
||||||
off2[1] = 0x4770;
|
else
|
||||||
|
{
|
||||||
|
u16 *off2 = (u16 *)(temp - 1);
|
||||||
|
|
||||||
|
*off = off2[0] = 0x2000;
|
||||||
|
off2[1] = 0x4770;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||||
{
|
{
|
||||||
//Look for firmlaunch code
|
//Look for firmlaunch code
|
||||||
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
|
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern)) - 0x13;
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
if(off == NULL) ret = 1;
|
||||||
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
else
|
||||||
|
|
||||||
//Copy firmlaunch code
|
|
||||||
memcpy(off, reboot_bin, reboot_bin_size);
|
|
||||||
|
|
||||||
//Put the fOpen offset in the right location
|
|
||||||
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
|
|
||||||
*pos_fopen = fOpenOffset;
|
|
||||||
|
|
||||||
if(CONFIG(USECUSTOMPATH))
|
|
||||||
{
|
{
|
||||||
const char pathPath[] = "luma/path.txt";
|
off -= 0x13;
|
||||||
|
|
||||||
u32 pathSize = getFileSize(pathPath);
|
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
||||||
|
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
||||||
|
|
||||||
if(pathSize > 5 && pathSize < 58)
|
//Copy firmlaunch code
|
||||||
|
memcpy(off, reboot_bin, reboot_bin_size);
|
||||||
|
|
||||||
|
//Put the fOpen offset in the right location
|
||||||
|
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
|
||||||
|
*pos_fopen = fOpenOffset;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
if(CONFIG(USECUSTOMPATH))
|
||||||
{
|
{
|
||||||
u8 path[pathSize];
|
const char pathPath[] = "luma/path.txt";
|
||||||
fileRead(path, pathPath, pathSize);
|
|
||||||
if(path[pathSize - 1] == 0xA) pathSize--;
|
|
||||||
if(path[pathSize - 1] == 0xD) pathSize--;
|
|
||||||
|
|
||||||
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
|
u32 pathSize = getFileSize(pathPath);
|
||||||
|
|
||||||
|
if(pathSize > 5 && pathSize < 58)
|
||||||
{
|
{
|
||||||
u16 finalPath[pathSize + 1];
|
u8 path[pathSize];
|
||||||
for(u32 i = 0; i < pathSize; i++)
|
fileRead(path, pathPath, pathSize);
|
||||||
finalPath[i] = (u16)path[i];
|
if(path[pathSize - 1] == 0xA) pathSize--;
|
||||||
finalPath[pathSize] = 0;
|
if(path[pathSize - 1] == 0xD) pathSize--;
|
||||||
|
|
||||||
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
|
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
|
||||||
memcpy(pos_path, finalPath, (pathSize + 1) * 2);
|
{
|
||||||
|
u16 finalPath[pathSize + 1];
|
||||||
|
for(u32 i = 0; i < pathSize; i++)
|
||||||
|
finalPath[i] = (u16)path[i];
|
||||||
|
finalPath[pathSize] = 0;
|
||||||
|
|
||||||
|
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
|
||||||
|
memcpy(pos_path, finalPath, (pathSize + 1) * 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchFirmWrites(u8 *pos, u32 size)
|
u32 patchFirmWrites(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
//Look for FIRM writing code
|
//Look for FIRM writing code
|
||||||
u8 *off1 = memsearch(pos, "exe:", size, 4);
|
u8 *off = memsearch(pos, "exe:", size, 4);
|
||||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
|
||||||
|
|
||||||
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, sizeof(pattern));
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||||
|
|
||||||
off2[0] = 0x2000;
|
u16 *off2 = (u16 *)memsearch(off - 0x100, pattern, 0x100, sizeof(pattern));
|
||||||
off2[1] = 0x46C0;
|
|
||||||
|
if(off2 == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off2[0] = 0x2000;
|
||||||
|
off2[1] = 0x46C0;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchOldFirmWrites(u8 *pos, u32 size)
|
u32 patchOldFirmWrites(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for FIRM writing code
|
//Look for FIRM writing code
|
||||||
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
off[0] = 0x2400;
|
if(off == NULL) ret = 1;
|
||||||
off[1] = 0xE01D;
|
else
|
||||||
|
{
|
||||||
|
off[0] = 0x2400;
|
||||||
|
off[1] = 0xE01D;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||||
@ -192,106 +254,109 @@ void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **fre
|
|||||||
*freeK11Space += svcGetCFWInfo_bin_size;
|
*freeK11Space += svcGetCFWInfo_bin_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
|
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02};
|
const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02};
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off != NULL) off[4] = 0xE0;
|
if(off == NULL)
|
||||||
|
{
|
||||||
|
if(firmVersion != 0xFFFFFFFF) ret = 1;
|
||||||
|
}
|
||||||
|
else off[4] = 0xE0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
|
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const patchData twlPatches[] = {
|
const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
|
||||||
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
u32 ret;
|
||||||
{{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1},
|
|
||||||
{{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2},
|
|
||||||
{{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2},
|
|
||||||
{{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2},
|
|
||||||
{{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2},
|
|
||||||
{{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2},
|
|
||||||
{{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1},
|
|
||||||
{{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1}
|
|
||||||
},
|
|
||||||
agbPatches[] = {
|
|
||||||
{{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
|
||||||
{{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
if the matching option was enabled (keep it as last) */
|
|
||||||
u32 numPatches = firmType == TWL_FIRM ? (sizeof(twlPatches) / sizeof(patchData)) :
|
|
||||||
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(SHOWGBABOOT));
|
|
||||||
const patchData *patches = firmType == TWL_FIRM ? twlPatches : agbPatches;
|
|
||||||
|
|
||||||
//Patch
|
if(temp == NULL) ret = 1;
|
||||||
for(u32 i = 0; i < numPatches; i++)
|
else
|
||||||
{
|
{
|
||||||
switch(patches[i].type)
|
u32 *off = (u32 *)(temp - 0xA);
|
||||||
|
|
||||||
|
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
||||||
{
|
{
|
||||||
case 0:
|
//Discard everything that's not str rX, [r0, #imm](!)
|
||||||
memcpy(pos + patches[i].offset[isN3DS ? 1 : 0], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
if((*off & 0xFE5F0000) == 0xE4000000)
|
||||||
break;
|
{
|
||||||
case 2:
|
u32 rD = (*off >> 12) & 0xF,
|
||||||
*(u16 *)(pos + patches[i].offset[isN3DS ? 1 : 0] + 2) = 0;
|
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
||||||
case 1:
|
bool writeback = ((*off >> 21) & 1) != 0,
|
||||||
*(u16 *)(pos + patches[i].offset[isN3DS ? 1 : 0]) = patches[i].patch.type1;
|
pre = ((*off >> 24) & 1) != 0;
|
||||||
break;
|
|
||||||
|
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
|
||||||
|
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
|
||||||
|
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
|
||||||
|
|
||||||
|
if(!pre) addr += offset;
|
||||||
|
if(writeback) r0 = addr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
return ret;
|
||||||
{
|
|
||||||
const u8 pattern[] = {0x03, 0xA0, 0xE3, 0x18};
|
|
||||||
|
|
||||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) + 0x13);
|
|
||||||
|
|
||||||
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
|
||||||
{
|
|
||||||
//Discard everything that's not str rX, [r0, #imm](!)
|
|
||||||
if((*off & 0xFE5F0000) != 0xE4000000) continue;
|
|
||||||
|
|
||||||
u32 rD = (*off >> 12) & 0xF,
|
|
||||||
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
|
||||||
bool writeback = ((*off >> 21) & 1) != 0,
|
|
||||||
pre = ((*off >> 24) & 1) != 0;
|
|
||||||
|
|
||||||
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
|
|
||||||
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
|
|
||||||
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
|
|
||||||
|
|
||||||
if(!pre) addr += offset;
|
|
||||||
if(writeback) r0 = addr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
|
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0xE3, 0xDC, 0x05, 0xC0}, //Get TitleID from CodeSet
|
const u8 pattern[] = {0x1B, 0x50, 0xA0, 0xE3}, //Get TitleID from CodeSet
|
||||||
pattern2[] = {0xE1, 0x0F, 0x00, 0xBD}; //Call exception dispatcher
|
pattern2[] = {0xE8, 0x13, 0x00, 0x02}; //Call exception dispatcher
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
u32 *loadCodeSet = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
|
u32 *loadCodeSet = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
*codeSetOffset = *loadCodeSet & 0xFFF;
|
if(loadCodeSet == NULL) ret = false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadCodeSet -= 2;
|
||||||
|
*codeSetOffset = *loadCodeSet & 0xFFF;
|
||||||
|
}
|
||||||
|
|
||||||
return *(u32 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) + 0xD);
|
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||||
|
|
||||||
|
u32 stackAddress;
|
||||||
|
|
||||||
|
if(temp == NULL) ret = false;
|
||||||
|
else stackAddress = *(u32 *)(temp + 9);
|
||||||
|
|
||||||
|
if(!ret) error("Error getting ARM11 exception handlers data.");
|
||||||
|
|
||||||
|
return stackAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
||||||
{
|
{
|
||||||
/* Stub svcBreak with "bkpt 65535" so we can debug the panic.
|
/* Stub svcBreak with "bkpt 65535" so we can debug the panic.
|
||||||
Thanks @yellows8 and others for mentioning this idea on #3dsdev */
|
Thanks @yellows8 and others for mentioning this idea on #3dsdev */
|
||||||
|
|
||||||
//Look for the svc handler
|
//Look for the svc handler
|
||||||
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
|
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL)
|
|
||||||
|
|
||||||
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
if(arm9SvcTable == NULL) ret = 1;
|
||||||
*addr = 0xE12FFF7F;
|
else
|
||||||
|
{
|
||||||
|
while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL)
|
||||||
|
|
||||||
|
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
||||||
|
*addr = 0xE12FFF7F;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
||||||
@ -301,68 +366,301 @@ void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
|||||||
*addr = 0xE12FFF7F;
|
*addr = 0xE12FFF7F;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchKernel9Panic(u8 *pos, u32 size)
|
u32 patchKernel9Panic(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
|
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0x12);
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
*off = 0xE12FFF7E;
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 *off = (u32 *)(temp - 0x12);
|
||||||
|
*off = 0xE12FFF7E;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchKernel11Panic(u8 *pos, u32 size)
|
u32 patchKernel11Panic(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
|
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
*off = 0xE12FFF7E;
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*off = 0xE12FFF7E;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchP9AccessChecks(u8 *pos, u32 size)
|
u32 patchP9AccessChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39};
|
const u8 pattern[] = {0x00, 0x08, 0x49, 0x68};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 7;
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
off[0] = 0x2001; //mov r0, #1
|
if(temp == NULL) ret = 1;
|
||||||
off[1] = 0x4770; //bx lr
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp - 3);
|
||||||
|
|
||||||
|
off[0] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = 0x4770; //bx lr
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler)
|
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos)
|
||||||
{
|
{
|
||||||
while(*arm11SvcHandler != 0xE11A0E1B) arm11SvcHandler++; //TST R10, R11,LSL LR
|
u32 ret;
|
||||||
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
|
||||||
|
while(*arm11SvcHandler != 0xE11A0E1B && arm11SvcHandler < endPos) arm11SvcHandler++; //TST R10, R11,LSL LR
|
||||||
|
|
||||||
|
if(arm11SvcHandler == endPos) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
||||||
{
|
{
|
||||||
/* We have to detour a function in the ARM11 kernel because builtin modules
|
/* We have to detour a function in the ARM11 kernel because builtin modules
|
||||||
are compressed in memory and are only decompressed at runtime */
|
are compressed in memory and are only decompressed at runtime */
|
||||||
|
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
//Check that we have enough free space
|
//Check that we have enough free space
|
||||||
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) == 0xFFFFFFFF)
|
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) == 0xFFFFFFFF)
|
||||||
{
|
{
|
||||||
//Inject our code into the free space
|
|
||||||
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
|
|
||||||
|
|
||||||
//Look for the code that decompresses the .code section of the builtin modules
|
//Look for the code that decompresses the .code section of the builtin modules
|
||||||
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
||||||
|
|
||||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
//Inject a jump (BL) instruction to our code at the offset we found
|
if(temp == NULL) ret = 1;
|
||||||
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
|
else
|
||||||
|
{
|
||||||
|
//Inject our code into the free space
|
||||||
|
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
|
||||||
|
|
||||||
*freeK11Space += k11modules_bin_size;
|
u32 *off = (u32 *)(temp - 0xB);
|
||||||
|
|
||||||
|
//Inject a jump (BL) instruction to our code at the offset we found
|
||||||
|
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
|
||||||
|
|
||||||
|
*freeK11Space += k11modules_bin_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchUnitInfoValueSet(u8 *pos, u32 size)
|
u32 patchUnitInfoValueSet(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for UNITINFO value being set during kernel sync
|
//Look for UNITINFO value being set during kernel sync
|
||||||
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
|
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
off[0] = isDevUnit ? 0 : 1;
|
if(off == NULL) ret = 1;
|
||||||
off[3] = 0xE3;
|
else
|
||||||
|
{
|
||||||
|
off[0] = ISDEVUNIT ? 0 : 1;
|
||||||
|
off[3] = 0xE3;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchLgySignatureChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x47, 0xC1, 0x17, 0x49};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp + 1);
|
||||||
|
|
||||||
|
off[0] = 0x2000;
|
||||||
|
off[1] = 0xB04E;
|
||||||
|
off[2] = 0xBD70;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x20, 0xF6, 0xE7, 0x7F};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp - 1);
|
||||||
|
|
||||||
|
*off = 0x2001; //mov r0, #1
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0xC0, 0x30, 0x06, 0xF0};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)temp + 1;
|
||||||
|
|
||||||
|
off[0] = 0x2000;
|
||||||
|
off[1] = 0;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlWhitelistChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x22, 0x00, 0x20, 0x30};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)temp + 2;
|
||||||
|
|
||||||
|
off[0] = 0x2000;
|
||||||
|
off[1] = 0;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x25, 0x20, 0x00, 0x0E};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(temp == NULL)
|
||||||
|
{
|
||||||
|
if(firmVersion == 0xFFFFFFFF) ret = patchOldTwlFlashcartChecks(pos, size);
|
||||||
|
else ret = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u16 *off = (u16 *)(temp + 3);
|
||||||
|
|
||||||
|
off[0] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = 0; //nop
|
||||||
|
off[6] = 0x2001; //mov r0, #1
|
||||||
|
off[7] = 0; //nop
|
||||||
|
off[0xC] = 0x2001; //mov r0, #1
|
||||||
|
off[0xD] = 0; //nop
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x06, 0xF0, 0xA0, 0xFD};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[0] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = 0; //nop
|
||||||
|
off[6] = 0x2001; //mov r0, #1
|
||||||
|
off[7] = 0; //nop
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchTwlShaHashChecks(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x10, 0xB5, 0x14, 0x22};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
off[0] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = 0x4770;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 patchAgbBootSplash(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
|
const u8 pattern[] = {0x00, 0x00, 0x01, 0xEF};
|
||||||
|
u32 ret;
|
||||||
|
|
||||||
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
|
if(off == NULL) ret = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*off = 0x26;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
@ -28,15 +28,6 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
typedef struct patchData {
|
|
||||||
u32 offset[2];
|
|
||||||
union {
|
|
||||||
u8 type0[8];
|
|
||||||
u16 type1;
|
|
||||||
} patch;
|
|
||||||
u32 type;
|
|
||||||
} patchData;
|
|
||||||
|
|
||||||
typedef struct __attribute__((packed))
|
typedef struct __attribute__((packed))
|
||||||
{
|
{
|
||||||
char magic[4];
|
char magic[4];
|
||||||
@ -51,25 +42,32 @@ typedef struct __attribute__((packed))
|
|||||||
u32 config;
|
u32 config;
|
||||||
} CFWInfo;
|
} CFWInfo;
|
||||||
|
|
||||||
extern bool isDevUnit;
|
extern CfgData configData;
|
||||||
|
|
||||||
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
|
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
|
||||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
|
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
|
||||||
void patchSignatureChecks(u8 *pos, u32 size);
|
u32 patchSignatureChecks(u8 *pos, u32 size);
|
||||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
|
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion);
|
||||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||||
void patchFirmWrites(u8 *pos, u32 size);
|
u32 patchFirmWrites(u8 *pos, u32 size);
|
||||||
void patchOldFirmWrites(u8 *pos, u32 size);
|
u32 patchOldFirmWrites(u8 *pos, u32 size);
|
||||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||||
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||||
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
|
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
|
||||||
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
|
|
||||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset);
|
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset);
|
||||||
void patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address);
|
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address);
|
||||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable);
|
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable);
|
||||||
void patchKernel9Panic(u8 *pos, u32 size);
|
u32 patchKernel9Panic(u8 *pos, u32 size);
|
||||||
void patchKernel11Panic(u8 *pos, u32 size);
|
u32 patchKernel11Panic(u8 *pos, u32 size);
|
||||||
void patchP9AccessChecks(u8 *pos, u32 size);
|
u32 patchP9AccessChecks(u8 *pos, u32 size);
|
||||||
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler);
|
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos);
|
||||||
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
|
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
|
||||||
void patchUnitInfoValueSet(u8 *pos, u32 size);
|
u32 patchUnitInfoValueSet(u8 *pos, u32 size);
|
||||||
|
u32 patchLgySignatureChecks(u8 *pos, u32 size);
|
||||||
|
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size);
|
||||||
|
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size);
|
||||||
|
u32 patchTwlWhitelistChecks(u8 *pos, u32 size);
|
||||||
|
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion);
|
||||||
|
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size);
|
||||||
|
u32 patchTwlShaHashChecks(u8 *pos, u32 size);
|
||||||
|
u32 patchAgbBootSplash(u8 *pos, u32 size);
|
@ -32,14 +32,5 @@
|
|||||||
#define PIN_VERSIONMAJOR 1
|
#define PIN_VERSIONMAJOR 1
|
||||||
#define PIN_VERSIONMINOR 3
|
#define PIN_VERSIONMINOR 3
|
||||||
|
|
||||||
typedef struct __attribute__((packed))
|
|
||||||
{
|
|
||||||
char magic[4];
|
|
||||||
u16 formatVersionMajor, formatVersionMinor;
|
|
||||||
|
|
||||||
u8 lengthHash[32];
|
|
||||||
u8 hash[32];
|
|
||||||
} PinData;
|
|
||||||
|
|
||||||
void newPin(bool allowSkipping, u32 pinMode);
|
void newPin(bool allowSkipping, u32 pinMode);
|
||||||
bool verifyPin(u32 pinMode);
|
bool verifyPin(u32 pinMode);
|
@ -44,7 +44,9 @@ static volatile struct fb {
|
|||||||
u8 *top_left;
|
u8 *top_left;
|
||||||
u8 *top_right;
|
u8 *top_right;
|
||||||
u8 *bottom;
|
u8 *bottom;
|
||||||
} *const fbs = (volatile struct fb *)0x23FFFE00;
|
} __attribute__((packed)) *const fbs = (volatile struct fb *)0x23FFFE00;
|
||||||
|
|
||||||
|
extern CfgData configData;
|
||||||
|
|
||||||
void deinitScreens(void);
|
void deinitScreens(void);
|
||||||
void swapFramebuffers(bool isAlternate);
|
void swapFramebuffers(bool isAlternate);
|
||||||
|
@ -36,9 +36,37 @@ typedef volatile u16 vu16;
|
|||||||
typedef volatile u32 vu32;
|
typedef volatile u32 vu32;
|
||||||
typedef volatile u64 vu64;
|
typedef volatile u64 vu64;
|
||||||
|
|
||||||
//Used by multiple files
|
#include "3dsheaders.h"
|
||||||
|
|
||||||
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
|
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
|
||||||
|
|
||||||
|
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
||||||
|
#define CFG_UNITINFO (*(vu8 *)0x10010010)
|
||||||
|
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||||
|
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||||
|
|
||||||
|
#define ISN3DS (PDN_MPCORE_CFG == 7)
|
||||||
|
#define ISDEVUNIT (CFG_UNITINFO != 0)
|
||||||
|
#define ISA9LH (!PDN_SPI_CNT)
|
||||||
|
#define ISFIRMLAUNCH (launchedFirmTidLow[5] != 0)
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
char magic[4];
|
||||||
|
u16 formatVersionMajor, formatVersionMinor;
|
||||||
|
|
||||||
|
u32 config;
|
||||||
|
} CfgData;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed))
|
||||||
|
{
|
||||||
|
char magic[4];
|
||||||
|
u16 formatVersionMajor, formatVersionMinor;
|
||||||
|
|
||||||
|
u8 lengthHash[32];
|
||||||
|
u8 hash[32];
|
||||||
|
} PinData;
|
||||||
|
|
||||||
typedef enum FirmwareSource
|
typedef enum FirmwareSource
|
||||||
{
|
{
|
||||||
FIRMWARE_SYSNAND = 0,
|
FIRMWARE_SYSNAND = 0,
|
||||||
@ -57,9 +85,5 @@ typedef enum FirmwareType
|
|||||||
NATIVE_FIRM1X2X
|
NATIVE_FIRM1X2X
|
||||||
} FirmwareType;
|
} FirmwareType;
|
||||||
|
|
||||||
typedef enum Fs
|
extern u16 launchedFirmTidLow[8]; //Defined in start.s
|
||||||
{
|
static Firm *const firm = (Firm *)0x24000000;
|
||||||
SD_CARD = 0,
|
|
||||||
CTRNAND,
|
|
||||||
NONE
|
|
||||||
} Fs;
|
|
@ -56,7 +56,7 @@ u32 waitInput(void)
|
|||||||
|
|
||||||
void mcuReboot(void)
|
void mcuReboot(void)
|
||||||
{
|
{
|
||||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true, false);
|
if(ISFIRMLAUNCH && PDN_GPU_CNT != 1) clearScreens(true, true, false);
|
||||||
|
|
||||||
//Ensure that all memory transfers have completed and that the data cache has been flushed
|
//Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||||
flushEntireDCache();
|
flushEntireDCache();
|
||||||
@ -67,7 +67,7 @@ void mcuReboot(void)
|
|||||||
|
|
||||||
void mcuPowerOff(void)
|
void mcuPowerOff(void)
|
||||||
{
|
{
|
||||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true, false);
|
if(ISFIRMLAUNCH && PDN_GPU_CNT != 1) clearScreens(true, true, false);
|
||||||
|
|
||||||
//Ensure that all memory transfers have completed and that the data cache has been flushed
|
//Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||||
flushEntireDCache();
|
flushEntireDCache();
|
||||||
@ -112,7 +112,7 @@ void chrono(u32 seconds)
|
|||||||
|
|
||||||
void error(const char *message)
|
void error(const char *message)
|
||||||
{
|
{
|
||||||
if(isFirmlaunch) mcuReboot();
|
if(ISFIRMLAUNCH) mcuReboot();
|
||||||
|
|
||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
|
@ -28,8 +28,6 @@
|
|||||||
#define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i)
|
#define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i)
|
||||||
#define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i)
|
#define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i)
|
||||||
|
|
||||||
extern bool isFirmlaunch;
|
|
||||||
|
|
||||||
u32 waitInput(void);
|
u32 waitInput(void);
|
||||||
void mcuReboot(void);
|
void mcuReboot(void);
|
||||||
void mcuPowerOff(void);
|
void mcuPowerOff(void);
|
||||||
|
Reference in New Issue
Block a user