Merge branch 'master' into developer

Conflicts:
	source/firm.c
	source/firm.h
	source/patches.c
	source/patches.h
This commit is contained in:
TuxSH 2016-08-29 20:12:23 +02:00
commit cb9576b10e
26 changed files with 433 additions and 411 deletions

View File

@ -22,13 +22,13 @@ LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include) INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft ASFLAGS := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \ CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \
-ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS -ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS
LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ARCH) LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ASFLAGS) -L$(DEVKITPRO)/libctru/lib
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.c)) $(call rwildcard, $(dir_source), *.s *.c))
.PHONY: all .PHONY: all
all: ../$(dir_build)/$(name).cxi all: ../$(dir_build)/$(name).cxi
@ -48,4 +48,8 @@ $(dir_build)/memory.o : CFLAGS += -O3
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/%.o: $(dir_source)/%.c
@mkdir -p "$(@D)" @mkdir -p "$(@D)"
$(COMPILE.c) $(OUTPUT_OPTION) $< $(COMPILE.c) $(OUTPUT_OPTION) $<
$(dir_build)/%.o: $(dir_source)/%.s
@mkdir -p "$(@D)"
$(COMPILE.s) $(OUTPUT_OPTION) $<
include $(call rwildcard, $(dir_build), *.d) include $(call rwildcard, $(dir_build), *.d)

19
injector/source/CFWInfo.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <3ds/types.h>
typedef struct __attribute__((packed))
{
char magic[4];
u8 versionMajor;
u8 versionMinor;
u8 versionBuild;
u8 flags; /* bit 0: dev branch; bit 1: is release */
u32 commitHash;
u32 config;
} CFWInfo;
int svcGetCFWInfo(CFWInfo *info);

View File

@ -0,0 +1,9 @@
.text
.arm
.align 4
.global svcGetCFWInfo
.type svcGetCFWInfo, %function
svcGetCFWInfo:
svc 0x2e
bx lr

View File

@ -2,8 +2,9 @@
#include "memory.h" #include "memory.h"
#include "patcher.h" #include "patcher.h"
#include "ifile.h" #include "ifile.h"
#include "CFWInfo.h"
static CFWInfo info = {0}; static CFWInfo info;
static int memcmp(const void *buf1, const void *buf2, u32 size) static int memcmp(const void *buf1, const void *buf2, u32 size)
{ {
@ -85,11 +86,6 @@ static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int f
return IFile_Open(file, archiveId, archivePath, filePath, flags); return IFile_Open(file, archiveId, archivePath, filePath, flags);
} }
int __attribute__((naked)) svcGetCFWInfo(CFWInfo __attribute__((unused)) *out)
{
__asm__ volatile("svc 0x2E; bx lr");
}
static void loadCFWInfo(void) static void loadCFWInfo(void)
{ {
static bool infoLoaded = false; static bool infoLoaded = false;
@ -97,11 +93,10 @@ static void loadCFWInfo(void)
if(!infoLoaded) if(!infoLoaded)
{ {
svcGetCFWInfo(&info); svcGetCFWInfo(&info);
IFile file; IFile file;
if(BOOTCONFIG(5, 1) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted if(BOOTCONFIG(5, 1) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
{
IFile_Close(&file); IFile_Close(&file);
}
infoLoaded = true; infoLoaded = true;
} }

View File

@ -8,18 +8,4 @@
#define MULTICONFIG(a) ((info.config >> (a * 2 + 6)) & 3) #define MULTICONFIG(a) ((info.config >> (a * 2 + 6)) & 3)
#define BOOTCONFIG(a, b) ((info.config >> a) & b) #define BOOTCONFIG(a, b) ((info.config >> a) & b)
typedef struct __attribute__((packed))
{
char magic[4];
u8 versionMajor;
u8 versionMinor;
u8 versionBuild;
u8 flags; /* bit 0: dev branch; bit 1: is release */
u32 commitHash;
u32 config;
} CFWInfo;
void patchCode(u64 progId, u8 *code, u32 size); void patchCode(u64 progId, u8 *code, u32 size);

View File

@ -23,7 +23,7 @@
#include "memory.h" #include "memory.h"
#include "cache.h" #include "cache.h"
extern u32 payloadSize; //defined in start.s extern u32 payloadSize; //Defined in start.s
void main(void) void main(void)
{ {

View File

@ -43,4 +43,4 @@ nand_sd:
sdmmc: .ascii "SDMC" sdmmc: .ascii "SDMC"
nand_offset: .ascii "NAND" ; for rednand this should be 1 nand_offset: .ascii "NAND" ; for rednand this should be 1
ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED) ncsd_header_offset: .ascii "NCSD" ; depends on nand manufacturer + emunand type (GW/RED)
.close .close

View File

@ -125,4 +125,4 @@ dat_fname: .dcw "sdmc:/Luma3DS.dat"
bx r0 bx r0
.pool .pool
.close .close

View File

@ -143,4 +143,4 @@ patchesEnd:
.pool .pool
.close .close

View File

@ -27,6 +27,7 @@
#include "screen.h" #include "screen.h"
#include "draw.h" #include "draw.h"
#include "buttons.h" #include "buttons.h"
#include "pin.h"
bool readConfig(const char *configPath) bool readConfig(const char *configPath)
{ {
@ -64,7 +65,7 @@ void writeConfig(const char *configPath, u32 configTemp)
} }
} }
void configure(void) void configMenu(bool oldPinStatus)
{ {
initScreens(); initScreens();
@ -234,6 +235,11 @@ void configure(void)
for(u32 i = 0; i < singleOptionsAmount; i++) for(u32 i = 0; i < singleOptionsAmount; i++)
configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16); configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16);
if(CONFIG(8)) newPin(oldPinStatus);
else if(oldPinStatus) fileDelete("/luma/pin.bin");
//Wait for the pressed buttons to change //Wait for the pressed buttons to change
while(HID_PAD == BUTTON_START); while(HID_PAD & PIN_BUTTONS);
chrono(2);
} }

View File

@ -44,4 +44,4 @@ extern cfgData configData;
bool readConfig(const char *configPath); bool readConfig(const char *configPath);
void writeConfig(const char *configPath, u32 configTemp); void writeConfig(const char *configPath, u32 configTemp);
void configure(void); void configMenu(bool oldPinStatus);

View File

@ -457,13 +457,13 @@ void arm9Loader(u8 *arm9Section)
} }
} }
void computePINHash(u8 out[32], u8 *in, u32 blockCount) void computePinHash(u8 *out, u8 *in, u32 blockCount)
{ {
u8 __attribute__((aligned(4))) cid[0x10]; u8 __attribute__((aligned(4))) cid[0x10];
u8 __attribute__((aligned(4))) cipherText[0x10]; u8 __attribute__((aligned(4))) cipherText[0x10];
sdmmc_get_cid(1, (u32 *)cid); sdmmc_get_cid(1, (u32 *)cid);
aes_use_keyslot(4); // console-unique keyslot which keys are set by the Arm9 bootROM aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
aes(cipherText, in, blockCount, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL); aes(cipherText, in, blockCount, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
sha(out, cipherText, 0x10, SHA_256_MODE); sha(out, cipherText, 0x10, SHA_256_MODE);

View File

@ -100,8 +100,7 @@
#define SHA_1_HASH_SIZE (160 / 8) #define SHA_1_HASH_SIZE (160 / 8)
extern u32 emuOffset; extern u32 emuOffset;
extern bool isN3DS; extern bool isN3DS, isDevUnit;
extern bool isDevUnit;
extern FirmwareSource firmSource; extern FirmwareSource firmSource;
void ctrNandInit(void); void ctrNandInit(void);
@ -109,5 +108,4 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
void setRSAMod0DerivedKeys(void); void setRSAMod0DerivedKeys(void);
void decryptExeFs(u8 *inbuf); void decryptExeFs(u8 *inbuf);
void arm9Loader(u8 *arm9Section); void arm9Loader(u8 *arm9Section);
void computePinHash(u8 *out, u8 *in, u32 blockCount);
void computePINHash(u8 out[32], u8 *in, u32 blockCount);

View File

@ -25,12 +25,12 @@
#include "fatfs/sdmmc/sdmmc.h" #include "fatfs/sdmmc/sdmmc.h"
#include "../build/emunandpatch.h" #include "../build/emunandpatch.h"
void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND) void locateEmuNand(u32 *off, u32 *head, FirmwareSource *emuNand)
{ {
static u8 temp[0x200]; static u8 temp[0x200];
const u32 nandSize = getMMCDevice(0)->total_size; const u32 nandSize = getMMCDevice(0)->total_size;
u32 nandOffset = *emuNAND == FIRMWARE_EMUNAND ? 0 : u32 nandOffset = *emuNand == FIRMWARE_EMUNAND ? 0 :
(nandSize > 0x200000 ? 0x400000 : 0x200000); (nandSize > 0x200000 ? 0x400000 : 0x200000);
//Check for RedNAND //Check for RedNAND
@ -53,12 +53,12 @@ void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND)
or to SysNAND if there isn't any */ or to SysNAND if there isn't any */
else else
{ {
*emuNAND = (*emuNAND == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; *emuNand = (*emuNand == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
if(*emuNAND) locateEmuNAND(off, head, emuNAND); if(*emuNand) locateEmuNand(off, head, emuNand);
} }
} }
static inline void *getEmuCode(u8 *pos, u32 size) static inline void *getFreeK9Space(u8 *pos, u32 size)
{ {
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
@ -66,7 +66,7 @@ static inline void *getEmuCode(u8 *pos, u32 size)
return memsearch(pos + 0x13500, pattern, size - 0x13500, 6) + 0x455; return memsearch(pos + 0x13500, pattern, size - 0x13500, 6) + 0x455;
} }
static inline u32 getSDMMC(u8 *pos, u32 size) static inline u32 getSdmmc(u8 *pos, u32 size)
{ {
//Look for struct code //Look for struct code
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20}; const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
@ -75,7 +75,7 @@ static inline u32 getSDMMC(u8 *pos, u32 size)
return *(u32 *)(off + 9) + *(u32 *)(off + 0xD); return *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
} }
static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset) static inline void patchNandRw(u8 *pos, u32 size, u32 branchOffset)
{ {
const u16 nandRedir[2] = {0x4C00, 0x47A0}; const u16 nandRedir[2] = {0x4C00, 0x47A0};
@ -93,7 +93,7 @@ static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset)
((u32 *)writeOffset)[1] = branchOffset; ((u32 *)writeOffset)[1] = branchOffset;
} }
static inline void patchMPU(u8 *pos, u32 size) static inline void patchMpu(u8 *pos, u32 size)
{ {
const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603}; const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603};
@ -107,26 +107,26 @@ static inline void patchMPU(u8 *pos, u32 size)
off[9] = mpuPatch[2]; off[9] = mpuPatch[2];
} }
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive) void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive)
{ {
//Copy emuNAND code //Copy emuNAND code
void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize); void *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize);
memcpy(emuCodeOffset, emunand, emunand_size); memcpy(freeK9Space, emunand, emunand_size);
//Add the data of the found emuNAND //Add the data of the found emuNAND
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4), u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_size, 4),
*pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4); *posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_size, 4);
*pos_offset = emuOffset; *posOffset = emuOffset;
*pos_header = emuHeader; *posHeader = emuHeader;
//Find and add the SDMMC struct //Find and add the SDMMC struct
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4); u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_size, 4);
*pos_sdmmc = getSDMMC(process9Offset, process9Size); *posSdmmc = getSdmmc(process9Offset, process9Size);
//Add emuNAND hooks //Add emuNAND hooks
u32 branchOffset = (u32)emuCodeOffset - branchAdditive; u32 branchOffset = (u32)freeK9Space - branchAdditive;
patchNANDRW(process9Offset, process9Size, branchOffset); patchNandRw(process9Offset, process9Size, branchOffset);
//Set MPU for emu code region //Set MPU for emu code region
patchMPU(arm9Section, arm9SectionSize); patchMpu(arm9Section, arm9SectionSize);
} }

View File

@ -26,5 +26,7 @@
#define NCSD_MAGIC 0x4453434E #define NCSD_MAGIC 0x4453434E
void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND); extern u32 emuOffset;
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive);
void locateEmuNand(u32 *off, u32 *head, FirmwareSource *emuNand);
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive);

View File

@ -1,4 +1,3 @@
#pragma once #pragma once
#include <stdbool.h>
#include "../../types.h" #include "../../types.h"

View File

@ -36,7 +36,7 @@
#include "pin.h" #include "pin.h"
#include "../build/injector.h" #include "../build/injector.h"
extern u16 launchedFirmTIDLow[8]; //Defined in start.s extern u16 launchedFirmTidLow[8]; //Defined in start.s
static firmHeader *const firm = (firmHeader *)0x24000000; static firmHeader *const firm = (firmHeader *)0x24000000;
static const firmSectionHeader *section; static const firmSectionHeader *section;
@ -80,14 +80,14 @@ void main(void)
} }
//Determine if this is a firmlaunch boot //Determine if this is a firmlaunch boot
if(launchedFirmTIDLow[5] != 0) if(launchedFirmTidLow[5] != 0)
{ {
if(needConfig == CREATE_CONFIGURATION) mcuReboot(); if(needConfig == CREATE_CONFIGURATION) mcuReboot();
isFirmlaunch = true; isFirmlaunch = true;
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM //'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
firmType = launchedFirmTIDLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTIDLow[5] - u'0'); firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
nandType = (FirmwareSource)BOOTCONFIG(0, 3); nandType = (FirmwareSource)BOOTCONFIG(0, 3);
firmSource = (FirmwareSource)BOOTCONFIG(2, 1); firmSource = (FirmwareSource)BOOTCONFIG(2, 1);
@ -134,23 +134,14 @@ void main(void)
//Boot options aren't being forced //Boot options aren't being forced
if(needConfig != DONT_CONFIGURE) if(needConfig != DONT_CONFIGURE)
{ {
PINData pin; bool pinExists = CONFIG(8) && verifyPin();
bool pinExists = CONFIG(8) && readPin(&pin);
//If we get here we should check the PIN (if it exists) in all cases
if(pinExists) verifyPin(&pin);
//If no configuration file exists or SELECT is held, load configuration menu //If no configuration file exists or SELECT is held, load configuration menu
bool shouldLoadConfigurationMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1)); bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1));
if(shouldLoadConfigurationMenu) if(shouldLoadConfigMenu)
{ {
configure(); configMenu(pinExists);
if(!pinExists && CONFIG(8)) newPin();
chrono(2);
//Update pressed buttons //Update pressed buttons
pressed = HID_PAD; pressed = HID_PAD;
@ -163,13 +154,20 @@ void main(void)
//Flag to tell loader to init SD //Flag to tell loader to init SD
configTemp |= 1 << 5; configTemp |= 1 << 5;
//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 else
{ {
if(CONFIG(7) && loadSplash()) pressed = HID_PAD; if(CONFIG(7) && loadSplash()) pressed = HID_PAD;
/* If L and R/A/Select or one of the single payload buttons are pressed, /* If L and R/A/Select or one of the single payload buttons are pressed,
chainload an external payload (the PIN, if any, has been verified)*/ chainload an external payload */
bool shouldLoadPayload = (pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS)); bool shouldLoadPayload = (pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS));
if(shouldLoadPayload) loadPayload(pressed); if(shouldLoadPayload) loadPayload(pressed);
@ -182,8 +180,8 @@ void main(void)
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one //If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
if(pressed & BUTTON_R1) if(pressed & BUTTON_R1)
{ {
nandType = (useSysAsDefault) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND; nandType = useSysAsDefault ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
firmSource = (useSysAsDefault) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND; firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
} }
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L, /* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
@ -204,13 +202,13 @@ void main(void)
//If we need to boot emuNAND, make sure it exists //If we need to boot emuNAND, make sure it exists
if(nandType != FIRMWARE_SYSNAND) if(nandType != FIRMWARE_SYSNAND)
{ {
locateEmuNAND(&emuOffset, &emuHeader, &nandType); locateEmuNand(&emuOffset, &emuHeader, &nandType);
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND; if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
} }
//Same if we're using emuNAND as the FIRM source //Same if we're using emuNAND as the FIRM source
else if(firmSource != FIRMWARE_SYSNAND) else if(firmSource != FIRMWARE_SYSNAND)
locateEmuNAND(&emuOffset, &emuHeader, &firmSource); locateEmuNand(&emuOffset, &emuHeader, &firmSource);
if(!isFirmlaunch) if(!isFirmlaunch)
{ {
@ -218,7 +216,7 @@ void main(void)
writeConfig(configPath, configTemp); writeConfig(configPath, configTemp);
} }
u32 firmVersion = loadFirm(firmType); u32 firmVersion = loadFirm(&firmType, firmSource);
switch(firmType) switch(firmType)
{ {
@ -226,7 +224,8 @@ void main(void)
patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh); patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh);
break; break;
case SAFE_FIRM: case SAFE_FIRM:
patchSafeFirm(); case NATIVE_FIRM2X:
if(isA9lh) patch2xNativeAndSafeFirm();
break; break;
default: default:
//Skip patching on unsupported O3DS AGB/TWL FIRMs //Skip patching on unsupported O3DS AGB/TWL FIRMs
@ -237,7 +236,7 @@ void main(void)
launchFirm(firmType); launchFirm(firmType);
} }
static inline u32 loadFirm(FirmwareType firmType) static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource)
{ {
section = firm->section; section = firm->section;
const char *firmwareFiles[4] = { const char *firmwareFiles[4] = {
@ -251,25 +250,39 @@ static inline u32 loadFirm(FirmwareType firmType)
if(fileRead(firm, firmwareFiles[(u32)firmType])) if(fileRead(firm, firmwareFiles[(u32)firmType]))
{ {
firmVersion = 0xffffffff; firmVersion = 0xFFFFFFFF;
} }
else else
{ {
firmVersion = firmRead(firm, (u32)firmType); firmVersion = firmRead(firm, (u32)firmType);
if(firmType == NATIVE_FIRM && !isN3DS) if(!isN3DS && *firmType == NATIVE_FIRM)
{ {
//We can't boot < 3.x NANDs (if firmware.bin is in the /luma folder, booting will fail) //We can't boot < 2.x SysNANDs and < 3.x EmuNANDs
if(firmVersion < 0x18) if(firmVersion < 0x18)
error("An old unsupported NAND has been detected.\nLuma3DS is unable to boot it."); {
if(firmSource != FIRMWARE_SYSNAND || firmVersion < 9)
//We can't boot a 4.x NATIVE_FIRM error("An old unsupported NAND has been detected.\nLuma3DS is unable to boot it");
if(firmVersion < 0x25)
error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /luma to boot"); if(BOOTCONFIG(5, 1)) error("SAFE_MODE is not supported on 2.x FIRM");
*firmType = NATIVE_FIRM2X;
}
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD
else if(firmVersion < 0x25)
{
if(!fileRead(firm, "/luma/firmware.bin") || (((u32)section[2].address >> 8) & 0xFF) != 0x68)
error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /luma to boot");
//No assumption regarding FIRM version
firmVersion = 0xFFFFFFFF;
}
} }
decryptExeFs((u8 *)firm);
} }
if(firmVersion != 0xFFFFFFFF) decryptExeFs((u8 *)firm);
return firmVersion; return firmVersion;
} }
@ -293,6 +306,13 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
process9MemAddr; process9MemAddr;
u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr); u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr);
//Find Kernel11 SVC table and free space locations
u8 *freeK11Space;
u32 *arm11SvcHandler,
*arm11ExceptionsPage;
u32 *arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
//Apply signature patches //Apply signature patches
patchSignatureChecks(process9Offset, process9Size); patchSignatureChecks(process9Offset, process9Size);
@ -300,7 +320,7 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
if(nandType != FIRMWARE_SYSNAND) if(nandType != FIRMWARE_SYSNAND)
{ {
u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address; u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address;
patchEmuNAND(arm9Section, section[2].size, process9Offset, process9Size, emuOffset, emuHeader, branchAdditive); 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
@ -316,9 +336,11 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
patchTitleInstallMinVersionCheck(process9Offset, process9Size); patchTitleInstallMinVersionCheck(process9Offset, process9Size);
//Restore svcBackdoor //Restore svcBackdoor
reimplementSvcBackdoor(arm11Section1, section[1].size); reimplementSvcBackdoor(arm11Section1, arm11SvcTable, &freeK11Space);
} }
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, &freeK11Space);
//Apply UNITINFO patch //Apply UNITINFO patch
if(DEV_OPTIONS == 1) patchUnitInfoValueSet(arm9Section, section[2].size); if(DEV_OPTIONS == 1) patchUnitInfoValueSet(arm9Section, section[2].size);
@ -326,28 +348,27 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
{ {
//Install arm11 exception handlers //Install arm11 exception handlers
u32 stackAddress, codeSetOffset; u32 stackAddress, codeSetOffset;
u32 *exceptionsPage = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &stackAddress, &codeSetOffset); getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &stackAddress, &codeSetOffset);
installArm11Handlers(exceptionsPage, stackAddress, codeSetOffset); installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
//Kernel9/Process9 debugging //Kernel9/Process9 debugging
patchExceptionHandlersInstall(arm9Section, section[2].size); patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
patchSvcBreak9(arm9Section, section[2].size, (u32)(section[2].address)); patchSvcBreak9(arm9Section, section[2].size, (u32)(section[2].address));
patchKernel9Panic(arm9Section, section[2].size, NATIVE_FIRM); patchKernel9Panic(arm9Section, section[2].size, NATIVE_FIRM);
//Stub svcBreak11 with "bkpt 65535" //Stub svcBreak11 with "bkpt 65535"
patchSvcBreak11(arm11Section1, section[1].size); patchSvcBreak11(arm11Section1, arm11SvcTable);
//Stub kernel11panic with "bkpt 65534" //Stub kernel11panic with "bkpt 65534"
patchKernel11Panic(arm11Section1, section[1].size); patchKernel11Panic(arm11Section1, section[1].size);
} }
if(CONFIG(9)) if(CONFIG(9))
{ {
patchArm11SvcAccessChecks(arm11Section1, section[1].size); patchArm11SvcAccessChecks(arm11SvcHandler);
patchK11ModuleChecks(arm11Section1, section[1].size); patchK11ModuleChecks(arm11Section1, section[1].size, &freeK11Space);
patchP9AccessChecks(process9Offset, process9Size); patchP9AccessChecks(process9Offset, process9Size);
} }
implementSvcGetCFWInfo(arm11Section1, section[1].size);
} }
static inline void patchLegacyFirm(FirmwareType firmType) static inline void patchLegacyFirm(FirmwareType firmType)
@ -367,18 +388,17 @@ static inline void patchLegacyFirm(FirmwareType firmType)
if(DEV_OPTIONS != 2) if(DEV_OPTIONS != 2)
{ {
//Kernel9/Process9 debugging //Kernel9/Process9 debugging
patchExceptionHandlersInstall(arm9Section, section[3].size); patchArm9ExceptionHandlersInstall(arm9Section, section[3].size);
patchSvcBreak9(arm9Section, section[3].size, (u32)(section[3].address)); patchSvcBreak9(arm9Section, section[3].size, (u32)(section[3].address));
patchKernel9Panic(arm9Section, section[3].size, firmType); patchKernel9Panic(arm9Section, section[3].size, firmType);
} }
applyLegacyFirmPatches((u8 *)firm, firmType); applyLegacyFirmPatches((u8 *)firm, firmType);
if(firmType == TWL_FIRM && CONFIG(5)) if(firmType == TWL_FIRM && CONFIG(5)) patchTwlBg((u8 *)firm + section[1].offset);
patchTwlBg((u8 *)firm + section[1].offset);
} }
static inline void patchSafeFirm(void) static inline void patch2xNativeAndSafeFirm(void)
{ {
u8 *arm9Section = (u8 *)firm + section[2].offset; u8 *arm9Section = (u8 *)firm + section[2].offset;
@ -390,12 +410,12 @@ static inline void patchSafeFirm(void)
patchFirmWrites(arm9Section, section[2].size); patchFirmWrites(arm9Section, section[2].size);
} }
else patchFirmWriteSafe(arm9Section, section[2].size); else patchOldFirmWrites(arm9Section, section[2].size);
if(DEV_OPTIONS != 2) if(DEV_OPTIONS != 2)
{ {
//Kernel9/Process9 debugging //Kernel9/Process9 debugging
patchExceptionHandlersInstall(arm9Section, section[2].size); patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
patchSvcBreak9(arm9Section, section[2].size, (u32)(section[2].address)); patchSvcBreak9(arm9Section, section[2].size, (u32)(section[2].address));
} }
} }

View File

@ -54,9 +54,9 @@ typedef enum ConfigurationStatus
CREATE_CONFIGURATION = 2 CREATE_CONFIGURATION = 2
} ConfigurationStatus; } ConfigurationStatus;
static inline u32 loadFirm(FirmwareType firmType); static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource);
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh); static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh);
static inline void patchLegacyFirm(FirmwareType firmType); static inline void patchLegacyFirm(FirmwareType firmType);
static inline void patchSafeFirm(void); static inline void patch2xNativeAndSafeFirm(void);
static inline void copySection0AndInjectSystemModules(FirmwareType firmType); static inline void copySection0AndInjectSystemModules(FirmwareType firmType);
static inline void launchFirm(FirmwareType firmType); static inline void launchFirm(FirmwareType firmType);

View File

@ -76,6 +76,11 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
return false; return false;
} }
void fileDelete(const char *path)
{
f_unlink(path);
}
void createDirectory(const char *path) void createDirectory(const char *path)
{ {
f_mkdir(path); f_mkdir(path);

View File

@ -32,6 +32,7 @@ void mountFs(void);
u32 fileRead(void *dest, const char *path); u32 fileRead(void *dest, const char *path);
u32 getFileSize(const char *path); u32 getFileSize(const char *path);
bool fileWrite(const void *buffer, const char *path, u32 size); bool fileWrite(const void *buffer, const char *path, u32 size);
void fileDelete(const char *path);
void createDirectory(const char *path); void createDirectory(const char *path);
void findDumpFile(const char *path, char *fileName); void findDumpFile(const char *path, char *fileName);
void loadPayload(u32 pressed);u32 firmRead(void *dest, u32 firmType); void loadPayload(u32 pressed);u32 firmRead(void *dest, u32 firmType);

View File

@ -28,35 +28,6 @@
#include "../build/k11modulespatch.h" #include "../build/k11modulespatch.h"
#include "../build/twl_k11modulespatch.h" #include "../build/twl_k11modulespatch.h"
static u32 *arm11ExceptionsPage = NULL;
static u32 *arm11SvcTable = NULL;
static u32 *arm11SvcHandler = NULL;
static u8 *freeK11Space = NULL;
static void findArm11ExceptionsPageAndSvcHandlerAndTable(u8 *pos, u32 size)
{
const u8 arm11ExceptionsPagePattern[] = {0x00, 0xB0, 0x9C, 0xE5};
if(arm11ExceptionsPage == NULL) arm11ExceptionsPage = (u32 *)memsearch(pos, arm11ExceptionsPagePattern, size, 4) - 0xB;
if((arm11SvcTable == NULL || arm11SvcHandler == NULL) && arm11ExceptionsPage != NULL)
{
u32 svcOffset = (-((arm11ExceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
arm11SvcHandler = arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
}
}
static void findFreeK11Space(u8 *pos, u32 size)
{
if(freeK11Space == NULL)
{
const u8 pattern[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
freeK11Space = memsearch(pos, pattern, size, 5) + 1;
}
}
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr) u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
{ {
u8 *off = memsearch(pos, "ess9", size, 4); u8 *off = memsearch(pos, "ess9", size, 4);
@ -68,22 +39,21 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200; return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
} }
u32* getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset) u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
{ {
//This function has to succeed. Crash if it doesn't (we'll get an exception dump of it anyways) const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
const u8 callExceptionDispatcherPattern[] = {0x0F, 0x00, 0xBD, 0xE8, 0x13, 0x00, 0x02, 0xF1}; *arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB;
const u8 getTitleIDFromCodeSetPattern[] = {0xDC, 0x05, 0xC0, 0xE1, 0x20, 0x04, 0xA0, 0xE1}; u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
*stackAddr = *((u32 *)memsearch(pos, callExceptionDispatcherPattern, size, 8) + 3); *arm11SvcHandler = arm11SvcTable;
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
u32 *loadCodeSet = (u32 *)memsearch(pos, getTitleIDFromCodeSetPattern, size, 8);
while((*loadCodeSet >> 20) != 0xE59 || ((*loadCodeSet >> 12) & 0xF) != 0) //ldr r0, [rX, #offset] const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
loadCodeSet--;
*codeSetOffset = *loadCodeSet & 0xFFF; *freeK11Space = memsearch(pos, pattern2, size, 5) + 1;
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); return arm11SvcTable;
return arm11ExceptionsPage;
} }
void patchSignatureChecks(u8 *pos, u32 size) void patchSignatureChecks(u8 *pos, u32 size)
@ -134,201 +104,48 @@ void patchFirmWrites(u8 *pos, u32 size)
off2[1] = writeBlock[1]; off2[1] = writeBlock[1];
} }
void patchFirmWriteSafe(u8 *pos, u32 size) void patchOldFirmWrites(u8 *pos, u32 size)
{ {
const u16 writeBlockSafe[2] = {0x2400, 0xE01D}; const u16 writeBlockOld[2] = {0x2400, 0xE01D};
//Look for FIRM writing code //Look for FIRM writing code
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB}; const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
u16 *off = (u16 *)memsearch(pos, pattern, size, 4); u16 *off = (u16 *)memsearch(pos, pattern, size, 4);
off[0] = writeBlockSafe[0]; off[0] = writeBlockOld[0];
off[1] = writeBlockSafe[1]; off[1] = writeBlockOld[1];
} }
void patchExceptionHandlersInstall(u8 *pos, u32 size) void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space)
{
const u8 pattern[] = {
0x18, 0x10, 0x80, 0xE5,
0x10, 0x10, 0x80, 0xE5,
0x20, 0x10, 0x80, 0xE5,
0x28, 0x10, 0x80, 0xE5,
}; //i.e when it stores ldr pc, [pc, #-4]
u32* off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)));
if(off == NULL) return;
off += sizeof(pattern)/4;
u32 r0 = 0x08000000;
for(; *off != 0xE3A01040; off++) //Until mov r1, #0x40
{
if((*off >> 26) != 0x39 || ((*off >> 16) & 0xF) != 0 || ((*off >> 25) & 1) != 0 || ((*off >> 20) & 5) != 0)
continue; //Discard everything that's not str rX, [r0, #imm](!)
int rD = (*off >> 12) & 0xF,
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1),
writeback = (*off >> 21) & 1,
pre = (*off >> 24) & 1;
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;
}
}
void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr)
{
//Stub svcBreak with "bkpt 65535" so we can debug the panic.
//Thanks @yellows8 and others for mentioning this idea on #3dsdev.
const u8 svcHandlerPattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
u32 *arm9SvcTable = (u32 *)memsearch(pos, svcHandlerPattern, size, 4);
while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL)
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - k9addr);
*addr = 0xE12FFF7F;
}
void patchSvcBreak11(u8 *pos, u32 size)
{
//Same as above, for NFIRM arm11
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size);
u32 *addr = (u32 *)(pos + arm11SvcTable[0x3C] - 0xFFF00000);
*addr = 0xE12FFF7F;
}
void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType)
{
if(firmType == TWL_FIRM || firmType == AGB_FIRM)
{
u8 *off = pos + ((isN3DS) ? 0x723C : 0x69A8);
*(u16 *)off = 0x4778; //bx pc
*(u16 *)(off + 2) = 0x46C0; //nop
*(u32 *)(off + 4) = 0xE12FFF7E; //bkpt 65534
}
else
{
const u8 pattern[] = {0x00, 0x20, 0xA0, 0xE3, 0x02, 0x30, 0xA0, 0xE1, 0x02, 0x10, 0xA0, 0xE1, 0x05, 0x00, 0xA0, 0xE3};
u32 *off = (u32 *)memsearch(pos, pattern, size, 16);
*off = 0xE12FFF7E;
}
}
void patchKernel11Panic(u8 *pos, u32 size)
{
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2, 0x00, 0x10, 0x90, 0xE5};
u32 *off = (u32 *)memsearch(pos, pattern, size, 8);
*off = 0xE12FFF7E;
}
void patchArm11SvcAccessChecks(u8 *pos, u32 size)
{
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size);
u32 *off = arm11SvcHandler;
while(*off != 0xE11A0E1B) off++; //TST R10, R11,LSL LR
*off = 0xE3B0A001; //MOVS R10, #1
}
//It's mainly Subv's code here:
void patchK11ModuleChecks(u8 *pos, u32 size)
{
// We have to detour a function in the ARM11 kernel because builtin modules
// are compressed in memory and are only decompressed at runtime.
findFreeK11Space(pos, size);
u8 *freeSpace = freeK11Space;
freeK11Space += k11modules_size;
// Inject our code into the free space
memcpy(freeSpace, k11modules, k11modules_size);
// Find the code that decompresses the .code section of the builtin modules and detour it with a jump to our code
const u8 pattern[] = { 0x00, 0x00, 0x94, 0xE5, 0x18, 0x10, 0x90, 0xE5, 0x28, 0x20,
0x90, 0xE5, 0x48, 0x00, 0x9D, 0xE5 };
u8 *off = memsearch(pos, pattern, size, 16);
// We couldn't find the code that decompresses the module
if (off == NULL)
return;
// Inject a jump instruction to our code at the offset we found
// Construct a jump (BL) instruction to our code
u32 offset = ((((u32)freeSpace) - ((u32)off + 8)) >> 2) & 0xFFFFFF;
u32 instruction = offset | (1 << 24) | (0x5 << 25) | (0xE << 28);
// Write our jump
memcpy(off, &instruction, 4);
}
void patchP9AccessChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39, 0x08, 0x58};
u16 *off = (u16 *)memsearch(pos, pattern, size, 6) - 7;
off[0] = 0x2001; //mov r0, #1
off[1] = 0x4770; //bx lr
}
void patchUnitInfoValueSet(u8 *pos, u32 size)
{
//Look for UNITINFO value being set during kernel sync
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
u8 *off = memsearch(pos, pattern, size, 4);
off[0] = (isDevUnit) ? 0 : 1;
off[3] = 0xE3;
}
void reimplementSvcBackdoor(u8 *pos, u32 size)
{ {
//Official implementation of svcBackdoor //Official implementation of svcBackdoor
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00 0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28 0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1] 0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr} 0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2 0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
0x30, 0xFF, 0x2F, 0xE1, //blx r0 0x30, 0xFF, 0x2F, 0xE1, //blx r0
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1} 0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0 0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
0x11, 0xFF, 0x2F, 0xE1}; //bx r1 0x11, 0xFF, 0x2F, 0xE1}; //bx r1
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size);
if(!arm11SvcTable[0x7B]) if(!arm11SvcTable[0x7B])
{ {
findFreeK11Space(pos, size); memcpy(*freeK11Space, svcBackdoor, 40);
memcpy(freeK11Space, svcBackdoor, 40); arm11SvcTable[0x7B] = 0xFFF00000 + *freeK11Space - pos;
(*freeK11Space) += 40;
arm11SvcTable[0x7B] = 0xFFF00000 + freeK11Space - pos;
freeK11Space += 40;
} }
} }
void implementSvcGetCFWInfo(u8 *pos, u32 size)
{
findFreeK11Space(pos, size);
memcpy(freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size);
CFWInfo *info = (CFWInfo *)memsearch(freeK11Space, "LUMA", svcGetCFWInfo_size, 4); void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space)
{
memcpy(*freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size);
CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_size, 4);
const char *rev = REVISION; const char *rev = REVISION;
bool isRelease; bool isRelease;
@ -344,12 +161,10 @@ void implementSvcGetCFWInfo(u8 *pos, u32 size)
} }
else isRelease = rev[4] == 0; else isRelease = rev[4] == 0;
info->flags = 1 /* dev branch */ | (((isRelease) ? 1 : 0) << 1) /* is release */; info->flags = 1 /* dev branch */ | ((isRelease ? 1 : 0) << 1) /* is release */;
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size); arm11SvcTable[0x2E] = 0xFFF00000 + *freeK11Space - pos; //Stubbed svc
(*freeK11Space) += svcGetCFWInfo_size;
arm11SvcTable[0x2E] = 0xFFF00000 + freeK11Space - pos; //Stubbed svc
freeK11Space += svcGetCFWInfo_size;
} }
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size) void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
@ -404,15 +219,15 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
void patchTwlBg(u8 *pos) void patchTwlBg(u8 *pos)
{ {
u8 *dst = pos + ((isN3DS) ? 0xFEA4 : 0xFCA0); u8 *dst = pos + (isN3DS ? 0xFEA4 : 0xFCA0);
memcpy(dst, twl_k11modules, twl_k11modules_size); //Install K11 hook memcpy(dst, twl_k11modules, twl_k11modules_size); //Install K11 hook
u32 *off = (u32 *)memsearch(dst, "LAUN", twl_k11modules_size, 4); u32 *off = (u32 *)memsearch(dst, "LAUN", twl_k11modules_size, 4);
*off = (isN3DS) ? 0xCDE88 : 0xCD5F8; //Dev SRL launcher offset *off = isN3DS ? 0xCDE88 : 0xCD5F8; //Dev SRL launcher offset
u16 *src1 = (u16 *)(pos + ((isN3DS) ? 0xE38 : 0xE3C)), u16 *src1 = (u16 *)(pos + (isN3DS ? 0xE38 : 0xE3C)),
*src2 = (u16 *)(pos + ((isN3DS) ? 0xE54 : 0xE58)); *src2 = (u16 *)(pos + (isN3DS ? 0xE54 : 0xE58));
//Construct BLX instructions: //Construct BLX instructions:
src1[0] = 0xF000 | ((((u32)dst - (u32)src1 - 4) & (0xFFF << 11)) >> 12); src1[0] = 0xF000 | ((((u32)dst - (u32)src1 - 4) & (0xFFF << 11)) >> 12);
@ -420,4 +235,163 @@ void patchTwlBg(u8 *pos)
src2[0] = 0xF000 | ((((u32)dst - (u32)src2 - 4) & (0xFFF << 11)) >> 12); src2[0] = 0xF000 | ((((u32)dst - (u32)src2 - 4) & (0xFFF << 11)) >> 12);
src2[1] = 0xE800 | ((((u32)dst - (u32)src2 - 4) & 0xFFF) >> 1); src2[1] = 0xE800 | ((((u32)dst - (u32)src2 - 4) & 0xFFF) >> 1);
}
void getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset)
{
//This function has to succeed. Crash if it doesn't (we'll get an exception dump of it anyways)
const u8 callExceptionDispatcherPattern[] = {0x0F, 0x00, 0xBD, 0xE8, 0x13, 0x00, 0x02, 0xF1};
const u8 getTitleIDFromCodeSetPattern[] = {0xDC, 0x05, 0xC0, 0xE1, 0x20, 0x04, 0xA0, 0xE1};
*stackAddr = *((u32 *)memsearch(pos, callExceptionDispatcherPattern, size, 8) + 3);
u32 *loadCodeSet = (u32 *)memsearch(pos, getTitleIDFromCodeSetPattern, size, 8);
while((*loadCodeSet >> 20) != 0xE59 || ((*loadCodeSet >> 12) & 0xF) != 0) //ldr r0, [rX, #offset]
loadCodeSet--;
*codeSetOffset = *loadCodeSet & 0xFFF;
}
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
{
const u8 pattern[] = {
0x18, 0x10, 0x80, 0xE5,
0x10, 0x10, 0x80, 0xE5,
0x20, 0x10, 0x80, 0xE5,
0x28, 0x10, 0x80, 0xE5,
}; //i.e when it stores ldr pc, [pc, #-4]
u32* off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)));
if(off == NULL) return;
off += sizeof(pattern)/4;
u32 r0 = 0x08000000;
for(; *off != 0xE3A01040; off++) //Until mov r1, #0x40
{
if((*off >> 26) != 0x39 || ((*off >> 16) & 0xF) != 0 || ((*off >> 25) & 1) != 0 || ((*off >> 20) & 5) != 0)
continue; //Discard everything that's not str rX, [r0, #imm](!)
int rD = (*off >> 12) & 0xF,
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1),
writeback = (*off >> 21) & 1,
pre = (*off >> 24) & 1;
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;
}
}
void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr)
{
//Stub svcBreak with "bkpt 65535" so we can debug the panic.
//Thanks @yellows8 and others for mentioning this idea on #3dsdev.
const u8 svcHandlerPattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
u32 *arm9SvcTable = (u32 *)memsearch(pos, svcHandlerPattern, size, 4);
while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL)
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - k9addr);
*addr = 0xE12FFF7F;
}
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
{
//Same as above, for NFIRM arm11
u32 *addr = (u32 *)(pos + arm11SvcTable[0x3C] - 0xFFF00000);
*addr = 0xE12FFF7F;
}
void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType)
{
if(firmType == TWL_FIRM || firmType == AGB_FIRM)
{
u8 *off = pos + ((isN3DS) ? 0x723C : 0x69A8);
*(u16 *)off = 0x4778; //bx pc
*(u16 *)(off + 2) = 0x46C0; //nop
*(u32 *)(off + 4) = 0xE12FFF7E; //bkpt 65534
}
else
{
const u8 pattern[] = {0x00, 0x20, 0xA0, 0xE3, 0x02, 0x30, 0xA0, 0xE1, 0x02, 0x10, 0xA0, 0xE1, 0x05, 0x00, 0xA0, 0xE3};
u32 *off = (u32 *)memsearch(pos, pattern, size, 16);
*off = 0xE12FFF7E;
}
}
void patchKernel11Panic(u8 *pos, u32 size)
{
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2, 0x00, 0x10, 0x90, 0xE5};
u32 *off = (u32 *)memsearch(pos, pattern, size, 8);
*off = 0xE12FFF7E;
}
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler)
{
u32 *off = arm11SvcHandler;
while(*off != 0xE11A0E1B) off++; //TST R10, R11,LSL LR
*off = 0xE3B0A001; //MOVS R10, #1
}
//It's mainly Subv's code here:
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
{
// We have to detour a function in the ARM11 kernel because builtin modules
// are compressed in memory and are only decompressed at runtime.
u8 *freeSpace = *freeK11Space;
*freeK11Space += k11modules_size;
// Inject our code into the free space
memcpy(freeSpace, k11modules, k11modules_size);
// Find the code that decompresses the .code section of the builtin modules and detour it with a jump to our code
const u8 pattern[] = { 0x00, 0x00, 0x94, 0xE5, 0x18, 0x10, 0x90, 0xE5, 0x28, 0x20,
0x90, 0xE5, 0x48, 0x00, 0x9D, 0xE5 };
u8 *off = memsearch(pos, pattern, size, 16);
// We couldn't find the code that decompresses the module
if (off == NULL)
return;
// Inject a jump instruction to our code at the offset we found
// Construct a jump (BL) instruction to our code
u32 offset = ((((u32)freeSpace) - ((u32)off + 8)) >> 2) & 0xFFFFFF;
u32 instruction = offset | (1 << 24) | (0x5 << 25) | (0xE << 28);
// Write our jump
memcpy(off, &instruction, 4);
}
void patchP9AccessChecks(u8 *pos, u32 size)
{
const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39, 0x08, 0x58};
u16 *off = (u16 *)memsearch(pos, pattern, size, 6) - 7;
off[0] = 0x2001; //mov r0, #1
off[1] = 0x4770; //bx lr
}
void patchUnitInfoValueSet(u8 *pos, u32 size)
{
//Look for UNITINFO value being set during kernel sync
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
u8 *off = memsearch(pos, pattern, size, 4);
off[0] = (isDevUnit) ? 0 : 1;
off[3] = 0xE3;
} }

View File

@ -50,22 +50,24 @@ typedef struct __attribute__((packed))
extern bool isN3DS, isDevUnit; extern bool isN3DS, isDevUnit;
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr); u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
u32* getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset); u32 *getKernel11Info(u8 *pos, u32 size, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
void patchSignatureChecks(u8 *pos, u32 size); void patchSignatureChecks(u8 *pos, u32 size);
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size); void patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr); void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
void patchFirmWrites(u8 *pos, u32 size); void patchFirmWrites(u8 *pos, u32 size);
void patchFirmWriteSafe(u8 *pos, u32 size); void patchOldFirmWrites(u8 *pos, u32 size);
void patchExceptionHandlersInstall(u8 *pos, u32 size); void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space);
void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr); void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u8 **freeK11Space);
void patchSvcBreak11(u8 *pos, u32 size);
void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType);
void patchKernel11Panic(u8 *pos, u32 size);
void patchArm11SvcAccessChecks(u8 *pos, u32 size);
void patchK11ModuleChecks(u8 *pos, u32 size);
void patchP9AccessChecks(u8 *pos, u32 size);
void patchUnitInfoValueSet(u8 *pos, u32 size);
void reimplementSvcBackdoor(u8 *pos, u32 size);
void implementSvcGetCFWInfo(u8 *pos, u32 size);
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType); void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
void patchTwlBg(u8 *pos); void patchTwlBg(u8 *pos);
void getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr, u32 *codeSetOffset);
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
void patchSvcBreak9(u8 *pos, u32 size, u32 k9addr);
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable);
void patchKernel9Panic(u8 *pos, u32 size, FirmwareType firmType);
void patchKernel11Panic(u8 *pos, u32 size);
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler);
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
void patchP9AccessChecks(u8 *pos, u32 size);
void patchUnitInfoValueSet(u8 *pos, u32 size);

View File

@ -34,43 +34,29 @@
#include "pin.h" #include "pin.h"
#include "crypto.h" #include "crypto.h"
bool readPin(PINData *out) static char pinKeyToLetter(u32 pressed)
{
if(fileRead(out, "/luma/pin.bin") != sizeof(PINData) ||
memcmp(out->magic, "PINF", 4) != 0 ||
out->formatVersionMajor != PIN_VERSIONMAJOR ||
out->formatVersionMinor != PIN_VERSIONMINOR)
return false;
u8 __attribute__((aligned(4))) zeroes[16] = {0};
u8 __attribute__((aligned(4))) tmp[32];
computePINHash(tmp, zeroes, 1);
return memcmp(out->testHash, tmp, 32) == 0; //test vector verification (SD card has (or hasn't) been used on another console)
}
static inline char PINKeyToLetter(u32 pressed)
{ {
const char keys[] = "AB--------XY"; const char keys[] = "AB--------XY";
u32 i; u32 i;
__asm__ volatile("clz %[i], %[pressed]" : [i] "=r" (i) : [pressed] "r" (pressed)); for(i = 31; pressed > 1; i--) pressed /= 2;
return keys[31 - i]; return keys[31 - i];
} }
void newPin(void) void newPin(bool allowSkipping)
{ {
clearScreens(); clearScreens();
drawString("Enter your NEW PIN: ", 10, 10, COLOR_WHITE); char *title = allowSkipping ? "Press START to skip or enter a new PIN" : "Enter a new PIN to proceed";
drawString(title, 10, 10, COLOR_TITLE);
drawString("PIN: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
// Set the default value as 0x00 so we can check if there are any unentered characters. //Pad to AES block length with zeroes
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; // pad to AES block length u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0};
u32 cnt = 0; u32 cnt = 0;
int charDrawPos = 20 * SPACING_X; int charDrawPos = 5 * SPACING_X;
while(cnt < PIN_LENGTH) while(cnt < PIN_LENGTH)
{ {
@ -79,16 +65,19 @@ void newPin(void)
{ {
pressed = waitInput(); pressed = waitInput();
} }
while(!(pressed & PIN_BUTTONS & ~BUTTON_START)); while(!(pressed & PIN_BUTTONS));
pressed &= PIN_BUTTONS & ~BUTTON_START; pressed &= PIN_BUTTONS;
if(!allowSkipping) pressed &= ~BUTTON_START;
if(pressed & BUTTON_START) return;
if(!pressed) continue; if(!pressed) continue;
char key = PINKeyToLetter(pressed);
enteredPassword[cnt++] = (u8)key; // add character to password.
// visualize character on screen. char key = pinKeyToLetter(pressed);
drawCharacter(key, 10 + charDrawPos, 10, COLOR_WHITE); enteredPassword[cnt++] = (u8)key; //Add character to password
//Visualize character on screen
drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
charDrawPos += 2 * SPACING_X; charDrawPos += 2 * SPACING_X;
} }
@ -100,10 +89,10 @@ void newPin(void)
pin.formatVersionMajor = PIN_VERSIONMAJOR; pin.formatVersionMajor = PIN_VERSIONMAJOR;
pin.formatVersionMinor = PIN_VERSIONMINOR; pin.formatVersionMinor = PIN_VERSIONMINOR;
computePINHash(tmp, zeroes, 1); computePinHash(tmp, zeroes, 1);
memcpy(pin.testHash, tmp, 32); memcpy(pin.testHash, tmp, 32);
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16); computePinHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
memcpy(pin.hash, tmp, 32); memcpy(pin.hash, tmp, 32);
if(!fileWrite(&pin, "/luma/pin.bin", sizeof(PINData))) if(!fileWrite(&pin, "/luma/pin.bin", sizeof(PINData)))
@ -112,18 +101,29 @@ void newPin(void)
if(!fileWrite(&pin, "/luma/pin.bin", sizeof(PINData))) if(!fileWrite(&pin, "/luma/pin.bin", sizeof(PINData)))
error("Error writing the PIN file"); error("Error writing the PIN file");
} }
while(HID_PAD & PIN_BUTTONS);
} }
void verifyPin(PINData *in) bool verifyPin(void)
{ {
initScreens(); initScreens();
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); PINData pin;
drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
// Set the default characters as 0x00 so we can check if there are any unentered characters. if(fileRead(&pin, "/luma/pin.bin") != sizeof(PINData) ||
memcmp(pin.magic, "PINF", 4) != 0 ||
pin.formatVersionMajor != PIN_VERSIONMAJOR ||
pin.formatVersionMinor != PIN_VERSIONMINOR)
return false;
u8 __attribute__((aligned(4))) zeroes[16] = {0};
u8 __attribute__((aligned(4))) tmp[32];
computePinHash(tmp, zeroes, 1);
//Test vector verification (SD card has, or hasn't been used on another console)
if(memcmp(pin.testHash, tmp, 32) != 0) return false;
//Pad to AES block length with zeroes
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0};
u32 cnt = 0; u32 cnt = 0;
@ -132,6 +132,9 @@ void verifyPin(PINData *in)
while(!unlock) while(!unlock)
{ {
drawString("Press START to shutdown or enter PIN to proceed", 10, 10, COLOR_TITLE);
drawString("PIN: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
u32 pressed; u32 pressed;
do do
{ {
@ -141,22 +144,21 @@ void verifyPin(PINData *in)
if(pressed & BUTTON_START) mcuPowerOff(); if(pressed & BUTTON_START) mcuPowerOff();
pressed &= PIN_BUTTONS & ~BUTTON_START; pressed &= PIN_BUTTONS;
if(!pressed) continue; if(!pressed) continue;
char key = PINKeyToLetter(pressed); char key = pinKeyToLetter(pressed);
enteredPassword[cnt++] = (u8)key; // add character to password. enteredPassword[cnt++] = (u8)key; //Add character to password
// visualize character on screen. //Visualize character on screen
drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE); drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
charDrawPos += 2 * SPACING_X; charDrawPos += 2 * SPACING_X;
if(cnt >= PIN_LENGTH) if(cnt >= PIN_LENGTH)
{ {
u8 __attribute__((aligned(4))) tmp[32]; computePinHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
unlock = memcmp(pin.hash, tmp, 32) == 0;
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
unlock = memcmp(in->hash, tmp, 32) == 0;
if(!unlock) if(!unlock)
{ {
@ -165,10 +167,10 @@ void verifyPin(PINData *in)
clearScreens(); clearScreens();
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE); drawString("Wrong PIN, try again", 10, 10 + 4 * SPACING_Y, COLOR_RED);
drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
drawString("Wrong pin! Try again!", 10, 10 + 3 * SPACING_Y, COLOR_RED);
} }
} }
} }
return true;
} }

View File

@ -43,6 +43,5 @@ typedef struct __attribute__((packed))
u8 hash[32]; u8 hash[32];
} PINData; } PINData;
bool readPin(PINData* out); void newPin(bool allowSkipping);
void newPin(void); bool verifyPin(void);
void verifyPin(PINData *in);

View File

@ -26,8 +26,8 @@
_start: _start:
b start b start
.global launchedFirmTIDLow .global launchedFirmTidLow
launchedFirmTIDLow: launchedFirmTidLow:
.hword 0, 0, 0, 0, 0, 0, 0, 0 .hword 0, 0, 0, 0, 0, 0, 0, 0
start: start:

View File

@ -49,5 +49,6 @@ typedef enum FirmwareType
NATIVE_FIRM = 0, NATIVE_FIRM = 0,
TWL_FIRM = 1, TWL_FIRM = 1,
AGB_FIRM = 2, AGB_FIRM = 2,
SAFE_FIRM = 3 SAFE_FIRM = 3,
NATIVE_FIRM2X = 4
} FirmwareType; } FirmwareType;