Merge branch 'master' into developer
Conflicts: Makefile source/firm.c source/patches.c source/patches.h
This commit is contained in:
commit
2fab0be5e8
24
Makefile
24
Makefile
@ -12,7 +12,7 @@ LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := Luma3DS
|
||||
version := $(shell git describe --abbrev=0 --tags)
|
||||
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i')
|
||||
|
||||
dir_source := source
|
||||
dir_patches := patches
|
||||
@ -34,8 +34,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
bundled = $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/arm9_exceptions.h $(dir_build)/screeninit.h
|
||||
|
||||
bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/arm9_exceptions.h $(dir_build)/injector.h $(dir_build)/loader.h $(dir_build)/screeninit.h
|
||||
.PHONY: all
|
||||
all: launcher a9lh ninjhax
|
||||
|
||||
@ -86,17 +85,23 @@ $(dir_build)/main.bin: $(dir_build)/main.elf
|
||||
$(dir_build)/main.elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/patches.h: $(dir_patches)/emunand.s $(dir_patches)/reboot.s $(dir_injector)/Makefile
|
||||
$(dir_build)/emunandpatch.h: $(dir_patches)/emunand.s $(dir_injector)/Makefile
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@armips $(word 2,$^)
|
||||
@bin2c -o $@ -n emunand $(@D)/emunand.bin
|
||||
|
||||
$(dir_build)/rebootpatch.h: $(dir_patches)/reboot.s
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@bin2c -o $@ -n reboot $(@D)/reboot.bin
|
||||
|
||||
$(dir_build)/injector.h: $(dir_injector)/Makefile
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $(dir_injector)
|
||||
@mv emunand.bin reboot.bin $(dir_injector)/injector.cxi $(@D)
|
||||
@bin2c -o $@ -n emunand $(@D)/emunand.bin -n reboot $(@D)/reboot.bin -n injector $(@D)/injector.cxi
|
||||
@bin2c -o $@ -n injector $(@D)/injector.cxi
|
||||
|
||||
$(dir_build)/loader.h: $(dir_loader)/Makefile
|
||||
@$(MAKE) -C $(dir_loader)
|
||||
@mv $(dir_loader)/loader.bin $(@D)
|
||||
@bin2c -o $@ -n loader $(@D)/loader.bin
|
||||
|
||||
$(dir_build)/arm9_exceptions.h: $(dir_arm9_exceptions)/Makefile
|
||||
@ -106,11 +111,10 @@ $(dir_build)/arm9_exceptions.h: $(dir_arm9_exceptions)/Makefile
|
||||
|
||||
$(dir_build)/screeninit.h: $(dir_screeninit)/Makefile
|
||||
@$(MAKE) -C $(dir_screeninit)
|
||||
@mv $(dir_screeninit)/screeninit.bin $(@D)
|
||||
@bin2c -o $@ -n screeninit $(@D)/screeninit.bin
|
||||
|
||||
$(dir_build)/memory.o: CFLAGS += -O3
|
||||
$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(version) configuration\""
|
||||
$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\""
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(bundled)
|
||||
@mkdir -p "$(@D)"
|
||||
|
@ -31,13 +31,13 @@ objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.c))
|
||||
|
||||
.PHONY: all
|
||||
all: $(name).cxi
|
||||
all: ../$(dir_build)/$(name).cxi
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(name).cxi: $(dir_build)/$(name).elf
|
||||
../$(dir_build)/$(name).cxi: $(dir_build)/$(name).elf
|
||||
@makerom -f ncch -rsf loader.rsf -nocodepadding -o $@ -elf $<
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
|
@ -472,6 +472,48 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0004013000003702LL: // RO
|
||||
{
|
||||
static const u8 sigCheckPattern[] = {
|
||||
0x30, 0x40, 0x2D, 0xE9, 0x02, 0x50, 0xA0, 0xE1
|
||||
};
|
||||
static const u8 sha256ChecksPattern1[] = {
|
||||
0x30, 0x40, 0x2D, 0xE9, 0x24, 0xD0, 0x4D, 0xE2
|
||||
};
|
||||
static const u8 sha256ChecksPattern2[] = {
|
||||
0xF8, 0x4F, 0x2D, 0xE9, 0x01, 0x70, 0xA0, 0xE1
|
||||
};
|
||||
|
||||
static const u8 stub[] = {
|
||||
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 // mov r0, #0; bx lr
|
||||
};
|
||||
|
||||
//Disable CRR0 signature (RSA2048 with SHA256) check
|
||||
patchMemory(code, size,
|
||||
sigCheckPattern,
|
||||
sizeof(sigCheckPattern), 0,
|
||||
stub,
|
||||
sizeof(stub), 1
|
||||
);
|
||||
|
||||
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||
patchMemory(code, size,
|
||||
sha256ChecksPattern1,
|
||||
sizeof(sha256ChecksPattern1), 0,
|
||||
stub,
|
||||
sizeof(stub), 1
|
||||
);
|
||||
|
||||
patchMemory(code, size,
|
||||
sha256ChecksPattern2,
|
||||
sizeof(sha256ChecksPattern2), 0,
|
||||
stub,
|
||||
sizeof(stub), 1
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if(CONFIG(4))
|
||||
|
@ -25,13 +25,13 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(name).bin
|
||||
all: ../$(dir_build)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(name).bin: $(dir_build)/$(name).elf
|
||||
../$(dir_build)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
|
@ -1,6 +1,6 @@
|
||||
.arm.little
|
||||
|
||||
.create "emunand.bin", 0
|
||||
.create "build/emunand.bin", 0
|
||||
.arm
|
||||
nand_sd:
|
||||
; Original code that still needs to be executed.
|
||||
|
@ -3,7 +3,7 @@
|
||||
payload_addr equ 0x23F00000 ; Brahma payload address.
|
||||
payload_maxsize equ 0x20000 ; Maximum size for the payload (200 KB will do).
|
||||
|
||||
.create "reboot.bin", 0
|
||||
.create "build/reboot.bin", 0
|
||||
.arm
|
||||
; Interesting registers and locations to keep in mind, set before this code is ran:
|
||||
; - sp + 0x3A8 - 0x70: FIRM path in exefs.
|
||||
|
@ -25,13 +25,13 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(name).bin
|
||||
all: ../$(dir_build)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(name).bin: $(dir_build)/$(name).elf
|
||||
../$(dir_build)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
|
@ -5,8 +5,9 @@
|
||||
#include "emunand.h"
|
||||
#include "memory.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
#include "../build/emunandpatch.h"
|
||||
|
||||
void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND)
|
||||
void locateEmuNAND(u32 *off, u32 *head, u32 *emuNAND)
|
||||
{
|
||||
static u8 *const temp = (u8 *)0x24300000;
|
||||
|
||||
@ -35,11 +36,19 @@ void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND)
|
||||
else
|
||||
{
|
||||
(*emuNAND)--;
|
||||
if(*emuNAND) getEmunandSect(off, head, emuNAND);
|
||||
if(*emuNAND) locateEmuNAND(off, head, emuNAND);
|
||||
}
|
||||
}
|
||||
|
||||
u32 getSDMMC(u8 *pos, u32 size)
|
||||
static inline void *getEmuCode(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
|
||||
//Looking for the last free space before Process9
|
||||
return memsearch(pos + 0x13500, pattern, size - 0x13500, 6) + 0x455;
|
||||
}
|
||||
|
||||
static inline u32 getSDMMC(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for struct code
|
||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||
@ -48,27 +57,58 @@ u32 getSDMMC(u8 *pos, u32 size)
|
||||
return *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
||||
}
|
||||
|
||||
void getEmuRW(u8 *pos, u32 size, u16 **readOffset, u16 **writeOffset)
|
||||
static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset)
|
||||
{
|
||||
const u16 nandRedir[2] = {0x4C00, 0x47A0};
|
||||
|
||||
//Look for read/write code
|
||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
|
||||
*readOffset = (u16 *)memsearch(pos, pattern, size, 4) - 3;
|
||||
*writeOffset = (u16 *)memsearch((u8 *)(*readOffset + 5), pattern, 0x100, 4) - 3;
|
||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, 4) - 3;
|
||||
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, 4) - 3;
|
||||
|
||||
*readOffset = nandRedir[0];
|
||||
readOffset[1] = nandRedir[1];
|
||||
((u32 *)readOffset)[1] = branchOffset;
|
||||
*writeOffset = nandRedir[0];
|
||||
writeOffset[1] = nandRedir[1];
|
||||
((u32 *)writeOffset)[1] = branchOffset;
|
||||
}
|
||||
|
||||
u32 *getMPU(u8 *pos, u32 size)
|
||||
static inline void patchMPU(u8 *pos, u32 size)
|
||||
{
|
||||
const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603};
|
||||
|
||||
//Look for MPU pattern
|
||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||
|
||||
return (u32 *)memsearch(pos, pattern, size, 4);
|
||||
u32 *off = (u32 *)memsearch(pos, pattern, size, 4);
|
||||
|
||||
off[0] = mpuPatch[0];
|
||||
off[6] = mpuPatch[1];
|
||||
off[9] = mpuPatch[2];
|
||||
}
|
||||
|
||||
void *getEmuCode(u8 *pos)
|
||||
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
//Copy emuNAND code
|
||||
void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize);
|
||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||
|
||||
//Looking for the last free space before Process9
|
||||
return memsearch(pos + 0x13500, pattern, 0x1000, 6) + 0x455;
|
||||
//Add the data of the found emuNAND
|
||||
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4);
|
||||
u32 *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4);
|
||||
*pos_offset = emuOffset;
|
||||
*pos_header = emuHeader;
|
||||
|
||||
//Find and add the SDMMC struct
|
||||
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4);
|
||||
*pos_sdmmc = getSDMMC(process9Offset, process9Size);
|
||||
|
||||
//Add emuNAND hooks
|
||||
u32 branchOffset = (u32)emuCodeOffset - branchAdditive;
|
||||
patchNANDRW(process9Offset, process9Size, branchOffset);
|
||||
|
||||
//Set MPU for emu code region
|
||||
patchMPU(arm9Section, arm9SectionSize);
|
||||
}
|
@ -8,8 +8,5 @@
|
||||
|
||||
#define NCSD_MAGIC 0x4453434E
|
||||
|
||||
void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND);
|
||||
u32 getSDMMC(u8 *pos, u32 size);
|
||||
void getEmuRW(u8 *pos, u32 size, u16 **readOffset, u16 **writeOffset);
|
||||
u32 *getMPU(u8 *pos, u32 size);
|
||||
void *getEmuCode(u8 *pos);
|
||||
void locateEmuNAND(u32 *off, u32 *head, u32 *emuNAND);
|
||||
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive);
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "diskio.h" /* FatFs lower layer API */
|
||||
#include "sdmmc/sdmmc.h"
|
||||
#include "../crypto.h"
|
||||
|
||||
/* Definitions of physical drive number for each media */
|
||||
#define SDCARD 0
|
||||
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../../types.h"
|
||||
#include "../../crypto.h"
|
||||
#include "../../types.h"
|
208
source/firm.c
208
source/firm.c
@ -14,7 +14,7 @@
|
||||
#include "draw.h"
|
||||
#include "screeninit.h"
|
||||
#include "buttons.h"
|
||||
#include "../build/patches.h"
|
||||
#include "../build/injector.h"
|
||||
|
||||
static firmHeader *const firm = (firmHeader *)0x24000000;
|
||||
static const firmSectionHeader *section;
|
||||
@ -222,13 +222,13 @@ void main(void)
|
||||
//If we need to boot emuNAND, make sure it exists
|
||||
if(nandType)
|
||||
{
|
||||
getEmunandSect(&emuOffset, &emuHeader, &nandType);
|
||||
locateEmuNAND(&emuOffset, &emuHeader, &nandType);
|
||||
if(!nandType) firmSource = 0;
|
||||
}
|
||||
|
||||
//Same if we're using emuNAND as the FIRM source
|
||||
else if(firmSource)
|
||||
getEmunandSect(&emuOffset, &emuHeader, &firmSource);
|
||||
locateEmuNAND(&emuOffset, &emuHeader, &firmSource);
|
||||
|
||||
if(!bootType)
|
||||
{
|
||||
@ -268,7 +268,7 @@ void main(void)
|
||||
stopChrono();
|
||||
}
|
||||
|
||||
launchFirm(!firmType, bootType);
|
||||
launchFirm(firmType, bootType);
|
||||
}
|
||||
|
||||
static inline void loadFirm(u32 firmType, u32 externalFirm)
|
||||
@ -346,24 +346,27 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode)
|
||||
process9MemAddr;
|
||||
u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr);
|
||||
|
||||
//Apply signature patches
|
||||
patchSignatureChecks(process9Offset, process9Size);
|
||||
|
||||
//Apply anti-anti-DG patches for >= 11.0 firmwares
|
||||
if(nativeFirmType == 1) patchTitleInstallMinVersionCheck(process9Offset, process9Size);
|
||||
|
||||
//Apply emuNAND patches
|
||||
if(nandType) patchEmuNAND(arm9Section, process9Offset, process9Size, emuHeader);
|
||||
if(nandType)
|
||||
{
|
||||
u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address;
|
||||
patchEmuNAND(arm9Section, section[2].size, process9Offset, process9Size, emuOffset, emuHeader, branchAdditive);
|
||||
}
|
||||
|
||||
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
||||
else if(a9lhMode) patchFirmWrites(process9Offset, process9Size, 1);
|
||||
else if(a9lhMode) patchFirmWrites(process9Offset, process9Size);
|
||||
|
||||
//Apply FIRM reboot patches, not on 9.0 FIRM as it breaks firmlaunchhax
|
||||
if(nativeFirmType || a9lhMode == 2) patchReboots(process9Offset, process9Size, process9MemAddr);
|
||||
|
||||
//Apply signature checks patches
|
||||
u16 *sigOffset,
|
||||
*sigOffset2;
|
||||
getSigChecks(process9Offset, process9Size, &sigOffset, &sigOffset2);
|
||||
*sigOffset = sigPatch[0];
|
||||
sigOffset2[0] = sigPatch[0];
|
||||
sigOffset2[1] = sigPatch[1];
|
||||
//Apply firmlaunch patches, not on 9.0 FIRM as it breaks firmlaunchhax
|
||||
if(nativeFirmType || a9lhMode == 2) patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
|
||||
|
||||
//Does nothing if svcBackdoor is still there
|
||||
if(nativeFirmType == 1) reimplementSvcBackdoor((u8 *)firm + section[1].offset, section[1].size);
|
||||
if(nativeFirmType == 1) reimplementSvcBackdoor();
|
||||
|
||||
if(DEVMODE)
|
||||
@ -375,79 +378,32 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode)
|
||||
//Make FCRAM (and VRAM as a side effect) globally executable from arm11 kernel
|
||||
patchKernelFCRAMAndVRAMMappingPermissions();
|
||||
}
|
||||
//Replace the FIRM loader with the injector while copying section0
|
||||
copySection0AndInjectLoader();
|
||||
}
|
||||
|
||||
static inline void patchEmuNAND(u8 *arm9Section, u8 *process9Offset, u32 process9Size, u32 emuHeader)
|
||||
static inline void patchLegacyFirm(u32 firmType)
|
||||
{
|
||||
//Copy emuNAND code
|
||||
void *emuCodeOffset = getEmuCode(arm9Section);
|
||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||
|
||||
//Add the data of the found emuNAND
|
||||
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4);
|
||||
u32 *pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4);
|
||||
*pos_offset = emuOffset;
|
||||
*pos_header = emuHeader;
|
||||
|
||||
//Find and add the SDMMC struct
|
||||
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4);
|
||||
*pos_sdmmc = getSDMMC(process9Offset, process9Size);
|
||||
|
||||
//Calculate offset for the hooks
|
||||
u32 branchOffset = (u32)emuCodeOffset - (u32)firm -
|
||||
section[2].offset + (u32)section[2].address;
|
||||
|
||||
//Add emuNAND hooks
|
||||
u16 *emuRead,
|
||||
*emuWrite;
|
||||
|
||||
getEmuRW(process9Offset, process9Size, &emuRead, &emuWrite);
|
||||
*emuRead = nandRedir[0];
|
||||
emuRead[1] = nandRedir[1];
|
||||
((u32 *)emuRead)[1] = branchOffset;
|
||||
*emuWrite = nandRedir[0];
|
||||
emuWrite[1] = nandRedir[1];
|
||||
((u32 *)emuWrite)[1] = branchOffset;
|
||||
|
||||
//Set MPU for emu code region
|
||||
u32 *mpuOffset = getMPU(arm9Section, section[2].size);
|
||||
*mpuOffset = mpuPatch[0];
|
||||
mpuOffset[6] = mpuPatch[1];
|
||||
mpuOffset[9] = mpuPatch[2];
|
||||
}
|
||||
|
||||
static inline void patchReboots(u8 *process9Offset, u32 process9Size, u32 process9MemAddr)
|
||||
{
|
||||
//Calculate offset for the firmlaunch code and fOpen
|
||||
u32 fOpenOffset;
|
||||
void *rebootOffset = getReboot(process9Offset, process9Size, process9MemAddr, &fOpenOffset);
|
||||
|
||||
//Copy firmlaunch code
|
||||
memcpy(rebootOffset, reboot, reboot_size);
|
||||
|
||||
//Put the fOpen offset in the right location
|
||||
u32 *pos_fopen = (u32 *)memsearch(rebootOffset, "OPEN", reboot_size, 4);
|
||||
*pos_fopen = fOpenOffset;
|
||||
}
|
||||
|
||||
static inline void reimplementSvcBackdoor(void)
|
||||
{
|
||||
u8 *arm11Section1 = (u8 *)firm + section[1].offset;
|
||||
|
||||
u32 *exceptionsPage;
|
||||
u32 *svcTable = getSvcAndExceptions(arm11Section1, section[1].size, &exceptionsPage);
|
||||
|
||||
if(!svcTable[0x7B])
|
||||
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
if(console)
|
||||
{
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||
|
||||
memcpy(freeSpace, svcBackdoor, 40);
|
||||
|
||||
svcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)exceptionsPage);
|
||||
arm9Loader((u8 *)firm + section[3].offset, 0);
|
||||
firm->arm9Entry = (u8 *)0x801301C;
|
||||
}
|
||||
|
||||
applyLegacyFirmPatches((u8 *)firm, firmType, console);}
|
||||
|
||||
static inline void patchSafeFirm(void)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
||||
|
||||
if(console)
|
||||
{
|
||||
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
arm9Loader(arm9Section, 0);
|
||||
firm->arm9Entry = (u8 *)0x801B01C;
|
||||
|
||||
patchFirmWrites(arm9Section, section[2].size);
|
||||
}
|
||||
else patchFirmWriteSafe(arm9Section, section[2].size);
|
||||
}
|
||||
|
||||
static inline void copySection0AndInjectLoader(void)
|
||||
@ -462,87 +418,17 @@ static inline void copySection0AndInjectLoader(void)
|
||||
memcpy(section[0].address + loaderOffset + injector_size, arm11Section0 + loaderOffset + loaderSize, section[0].size - (loaderOffset + loaderSize));
|
||||
}
|
||||
|
||||
static inline void patchSafeFirm(void)
|
||||
static inline void launchFirm(u32 firmType, u32 bootType)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
||||
|
||||
if(console)
|
||||
//If we're booting NATIVE_FIRM, section0 needs to be copied separately to inject 3ds_injector
|
||||
u32 sectionNum;
|
||||
if(!firmType)
|
||||
{
|
||||
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
arm9Loader(arm9Section, 0);
|
||||
firm->arm9Entry = (u8 *)0x801B01C;
|
||||
copySection0AndInjectLoader();
|
||||
sectionNum = 1;
|
||||
}
|
||||
else sectionNum = 0;
|
||||
|
||||
//Apply FIRM0/1 writes patches to protect A9LH
|
||||
patchFirmWrites(arm9Section, section[2].size, console);
|
||||
}
|
||||
|
||||
static void patchFirmWrites(u8 *offset, u32 size, u32 mode)
|
||||
{
|
||||
if(mode)
|
||||
{
|
||||
u16 *writeOffset = getFirmWrite(offset, size);
|
||||
*writeOffset = writeBlock[0];
|
||||
*(writeOffset + 1) = writeBlock[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 *writeOffset = getFirmWriteSafe(offset, size);
|
||||
*writeOffset = writeBlockSafe[0];
|
||||
*(writeOffset + 1) = writeBlockSafe[1];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void patchLegacyFirm(u32 firmType)
|
||||
{
|
||||
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
if(console)
|
||||
{
|
||||
arm9Loader((u8 *)firm + section[3].offset, 0);
|
||||
firm->arm9Entry = (u8 *)0x801301C;
|
||||
}
|
||||
|
||||
const patchData twlPatches[] = {
|
||||
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||
{{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
|
||||
if the matching option was enabled (keep it as last) */
|
||||
u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) :
|
||||
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6));
|
||||
const patchData *patches = firmType == 1 ? twlPatches : agbPatches;
|
||||
|
||||
//Patch
|
||||
for(u32 i = 0; i < numPatches; i++)
|
||||
{
|
||||
switch(patches[i].type)
|
||||
{
|
||||
case 0:
|
||||
memcpy((u8 *)firm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
||||
break;
|
||||
case 2:
|
||||
*(u16 *)((u8 *)firm + patches[i].offset[console] + 2) = 0;
|
||||
case 1:
|
||||
*(u16 *)((u8 *)firm + patches[i].offset[console]) = patches[i].patch.type1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void launchFirm(u32 sectionNum, u32 bootType)
|
||||
{
|
||||
//Copy FIRM sections to respective memory locations
|
||||
for(; sectionNum < 4 && section[sectionNum].size; sectionNum++)
|
||||
memcpy(section[sectionNum].address, (u8 *)firm + section[sectionNum].offset, section[sectionNum].size);
|
||||
|
@ -28,23 +28,10 @@ typedef struct firmHeader {
|
||||
firmSectionHeader section[4];
|
||||
} firmHeader;
|
||||
|
||||
typedef struct patchData {
|
||||
u32 offset[2];
|
||||
union {
|
||||
u8 type0[8];
|
||||
u16 type1;
|
||||
} patch;
|
||||
u32 type;
|
||||
} patchData;
|
||||
|
||||
static inline void loadFirm(u32 firmType, u32 externalFirm);
|
||||
static inline void patchKernelFCRAMAndVRAMMappingPermissions(void);
|
||||
static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode);
|
||||
static inline void patchEmuNAND(u8 *arm9Section, u8 *process9Offset, u32 process9Size, u32 emuHeader);
|
||||
static inline void patchReboots(u8 *process9Offset, u32 process9Size, u32 process9MemAddr);
|
||||
static inline void reimplementSvcBackdoor(void);
|
||||
static inline void copySection0AndInjectLoader(void);
|
||||
static inline void patchSafeFirm(void);
|
||||
static void patchFirmWrites(u8 *offset, u32 size, u32 mode);
|
||||
static inline void patchLegacyFirm(u32 firmType);
|
||||
static inline void patchSafeFirm(void);
|
||||
static inline void copySection0AndInjectLoader(void);
|
||||
static inline void launchFirm(u32 sectionNum, u32 bootType);
|
168
source/patches.c
168
source/patches.c
@ -4,36 +4,11 @@
|
||||
|
||||
#include "patches.h"
|
||||
#include "memory.h"
|
||||
|
||||
/**************************************************
|
||||
* Patches
|
||||
**************************************************/
|
||||
|
||||
const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603};
|
||||
|
||||
const u16 nandRedir[2] = {0x4C00, 0x47A0},
|
||||
sigPatch[2] = {0x2000, 0x4770},
|
||||
writeBlock[2] = {0x2000, 0x46C0},
|
||||
writeBlockSafe[2] = {0x2400, 0xE01D};
|
||||
#include "config.h"
|
||||
#include "../build/rebootpatch.h"
|
||||
|
||||
const u8 unitInfoPatch = 0xE3;
|
||||
|
||||
//Official implementation of svcBackdoor
|
||||
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
||||
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
|
||||
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
|
||||
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
|
||||
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
|
||||
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
|
||||
0x30, 0xFF, 0x2F, 0xE1, //blx r0
|
||||
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
|
||||
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
||||
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
||||
|
||||
/**************************************************
|
||||
* Functions
|
||||
**************************************************/
|
||||
|
||||
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
{
|
||||
u8 *off = memsearch(pos, "ess9", size, 4);
|
||||
@ -45,17 +20,32 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
|
||||
}
|
||||
|
||||
void getSigChecks(u8 *pos, u32 size, u16 **off, u16 **off2)
|
||||
void patchSignatureChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u16 sigPatch[2] = {0x2000, 0x4770};
|
||||
|
||||
//Look for signature checks
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||
|
||||
*off = (u16 *)memsearch(pos, pattern, size, 4);
|
||||
*off2 = (u16 *)(memsearch(pos, pattern2, size, 4) - 1);
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, 4),
|
||||
*off2 = (u16 *)(memsearch(pos, pattern2, size, 4) - 1);
|
||||
|
||||
*off = sigPatch[0];
|
||||
off2[0] = sigPatch[0];
|
||||
off2[1] = sigPatch[1];
|
||||
}
|
||||
|
||||
void *getReboot(u8 *pos, u32 size, u32 process9MemAddr, u32 *fOpenOffset)
|
||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x89, 0x0A, 0x81, 0x42, 0x02, 0xD2};
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, 6);
|
||||
|
||||
if(off != NULL) off[5] = 0xE0;
|
||||
}
|
||||
|
||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||
{
|
||||
//Look for FIRM reboot code
|
||||
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
||||
@ -63,26 +53,41 @@ void *getReboot(u8 *pos, u32 size, u32 process9MemAddr, u32 *fOpenOffset)
|
||||
u8 *off = memsearch(pos, pattern, size, 4) - 0x10;
|
||||
|
||||
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
||||
*fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
||||
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
||||
|
||||
return off;
|
||||
//Copy firmlaunch code
|
||||
memcpy(off, reboot, reboot_size);
|
||||
|
||||
//Put the fOpen offset in the right location
|
||||
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_size, 4);
|
||||
*pos_fopen = fOpenOffset;
|
||||
}
|
||||
|
||||
u16 *getFirmWrite(u8 *pos, u32 size)
|
||||
void patchFirmWrites(u8 *pos, u32 size)
|
||||
{
|
||||
const u16 writeBlock[2] = {0x2000, 0x46C0};
|
||||
|
||||
//Look for FIRM writing code
|
||||
u8 *const off = memsearch(pos, "exe:", size, 4);
|
||||
u8 *const off1 = memsearch(pos, "exe:", size, 4);
|
||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||
|
||||
return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
|
||||
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, 4);
|
||||
|
||||
off2[0] = writeBlock[0];
|
||||
off2[1] = writeBlock[1];
|
||||
}
|
||||
|
||||
u16 *getFirmWriteSafe(u8 *pos, u32 size)
|
||||
void patchFirmWriteSafe(u8 *pos, u32 size)
|
||||
{
|
||||
const u16 writeBlockSafe[2] = {0x2400, 0xE01D};
|
||||
|
||||
//Look for FIRM writing code
|
||||
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
||||
|
||||
return (u16 *)memsearch(pos, pattern, size, 4);
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, 4);
|
||||
|
||||
off[0] = writeBlockSafe[0];
|
||||
off[1] = writeBlockSafe[1];
|
||||
}
|
||||
|
||||
u8 *getUnitInfoValueSet(u8 *pos, u32 size)
|
||||
@ -93,6 +98,80 @@ u8 *getUnitInfoValueSet(u8 *pos, u32 size)
|
||||
return memsearch(pos, pattern, size, 4) + 3;
|
||||
}
|
||||
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 size)
|
||||
{
|
||||
//Official implementation of svcBackdoor
|
||||
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
||||
0x0F, 0x1C, 0x81, 0xE3, //orr r1, r1, #0xf00
|
||||
0x28, 0x10, 0x81, 0xE2, //add r1, r1, #0x28
|
||||
0x00, 0x20, 0x91, 0xE5, //ldr r2, [r1]
|
||||
0x00, 0x60, 0x22, 0xE9, //stmdb r2!, {sp, lr}
|
||||
0x02, 0xD0, 0xA0, 0xE1, //mov sp, r2
|
||||
0x30, 0xFF, 0x2F, 0xE1, //blx r0
|
||||
0x03, 0x00, 0xBD, 0xE8, //pop {r0, r1}
|
||||
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
||||
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
||||
|
||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; //cpsid aif
|
||||
|
||||
u32 *exceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB;
|
||||
|
||||
u32 svcOffset = (-((exceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
u32 *svcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
|
||||
while(*svcTable) svcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
if(!svcTable[0x7B])
|
||||
{
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||
|
||||
memcpy(freeSpace, svcBackdoor, 40);
|
||||
|
||||
svcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)exceptionsPage);
|
||||
}
|
||||
}
|
||||
|
||||
void applyLegacyFirmPatches(u8 *pos, u32 firmType, u32 console)
|
||||
{
|
||||
const patchData twlPatches[] = {
|
||||
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||
{{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
|
||||
if the matching option was enabled (keep it as last) */
|
||||
u32 numPatches = firmType == 1 ? (sizeof(twlPatches) / sizeof(patchData)) :
|
||||
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(6));
|
||||
const patchData *patches = firmType == 1 ? twlPatches : agbPatches;
|
||||
|
||||
//Patch
|
||||
for(u32 i = 0; i < numPatches; i++)
|
||||
{
|
||||
switch(patches[i].type)
|
||||
{
|
||||
case 0:
|
||||
memcpy(pos + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
||||
break;
|
||||
case 2:
|
||||
*(u16 *)(pos + patches[i].offset[console] + 2) = 0;
|
||||
case 1:
|
||||
*(u16 *)(pos + patches[i].offset[console]) = patches[i].patch.type1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 getLoader(u8 *pos, u32 *loaderSize)
|
||||
{
|
||||
u8 *off = pos;
|
||||
@ -108,17 +187,4 @@ u32 getLoader(u8 *pos, u32 *loaderSize)
|
||||
*loaderSize = size;
|
||||
|
||||
return (u32)(off - pos);
|
||||
}
|
||||
|
||||
u32 *getSvcAndExceptions(u8 *pos, u32 size, u32 **exceptionsPage)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; //cpsid aif
|
||||
|
||||
*exceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB;
|
||||
|
||||
u32 svcOffset = (-(((*exceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
u32 *svcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
|
||||
while(*svcTable) svcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
return svcTable;
|
||||
}
|
@ -6,25 +6,21 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/**************************************************
|
||||
* Patches
|
||||
**************************************************/
|
||||
const u32 mpuPatch[3];
|
||||
const u16 nandRedir[2],
|
||||
sigPatch[2],
|
||||
writeBlock[2],
|
||||
writeBlockSafe[2];
|
||||
typedef struct patchData {
|
||||
u32 offset[2];
|
||||
union {
|
||||
u8 type0[8];
|
||||
u16 type1;
|
||||
} patch;
|
||||
u32 type;
|
||||
} patchData;
|
||||
const u8 unitInfoPatch;
|
||||
const u8 svcBackdoor[40];
|
||||
|
||||
/**************************************************
|
||||
* Functions
|
||||
**************************************************/
|
||||
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
|
||||
void getSigChecks(u8 *pos, u32 size, u16 **off, u16 **off2);
|
||||
void *getReboot(u8 *pos, u32 size, u32 process9MemAddr, u32 *fOpenOffset);
|
||||
u16 *getFirmWrite(u8 *pos, u32 size);
|
||||
u16 *getFirmWriteSafe(u8 *pos, u32 size);
|
||||
u8 *getUnitInfoValueSet(u8 *pos, u32 size);
|
||||
u32 getLoader(u8 *pos, u32 *loaderSize);
|
||||
u32 *getSvcAndExceptions(u8 *pos, u32 size, u32 **exceptionsPage);
|
||||
void patchSignatureChecks(u8 *pos, u32 size);
|
||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
|
||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||
void patchFirmWrites(u8 *pos, u32 size);
|
||||
void patchFirmWriteSafe(u8 *pos, u32 size);
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 size);
|
||||
void applyLegacyFirmPatches(u8 *pos, u32 firmType, u32 console);
|
||||
u8 *getUnitInfoValueSet(u8 *pos, u32 size);
|
Reference in New Issue
Block a user