Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b16d88756 | ||
|
|
796cb31ed7 | ||
|
|
d7fd2f26c1 | ||
|
|
5adb8749de | ||
|
|
3474faa4a2 | ||
|
|
acd9c04ff6 | ||
|
|
833c9406b0 | ||
|
|
2f1253e27f | ||
|
|
53b847e31c | ||
|
|
7efa33dd7f | ||
|
|
4011970a57 | ||
|
|
a2cfa2be16 | ||
|
|
c4b691d688 | ||
|
|
72a7a8eee5 | ||
|
|
52d352385f | ||
|
|
c1f85650bd | ||
|
|
b830909504 | ||
|
|
4ad6b1c220 | ||
|
|
429488a4ba | ||
|
|
40c6cc11a5 | ||
|
|
594881c6ce | ||
|
|
1cc64a0fbc | ||
|
|
f492318e3c | ||
|
|
4b06bb7795 | ||
|
|
f7e570383a | ||
|
|
896a088199 | ||
|
|
f3322bd003 |
@@ -83,7 +83,8 @@ _commonHandler:
|
||||
cps #0x13 @ switch to supervisor mode
|
||||
cmp r10, #0
|
||||
addne sp, #0x28
|
||||
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
||||
ldmfd sp, {r8-r11}^ @ implementation details of the official svc handler
|
||||
ldr r2, [sp, #0x1c]
|
||||
ldr r4, [sp, #0x18]
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
tst r2, #0x20
|
||||
|
||||
@@ -62,6 +62,7 @@ _commonHandler:
|
||||
bic r5, r3, #0xf
|
||||
orr r5, #0x3
|
||||
msr cpsr_c, r5 @ switch to supervisor mode
|
||||
ldmfd sp, {r8-r11}^
|
||||
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
||||
ldr r4, [sp, #0x18]
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
|
||||
@@ -7,13 +7,15 @@
|
||||
|
||||
static CFWInfo info;
|
||||
|
||||
static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count)
|
||||
static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count)
|
||||
{
|
||||
for(u32 i = 0; i < count; i++)
|
||||
u32 i;
|
||||
|
||||
for(i = 0; i < count; i++)
|
||||
{
|
||||
u8 *found = memsearch(start, pattern, size, patSize);
|
||||
|
||||
if(found == NULL) svcBreak(USERBREAK_ASSERT);
|
||||
if(found == NULL) break;
|
||||
|
||||
memcpy(found + offset, replace, repSize);
|
||||
|
||||
@@ -24,6 +26,8 @@ static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, i
|
||||
size -= at + patSize;
|
||||
start = found + patSize;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
||||
@@ -59,7 +63,7 @@ static inline void loadCFWInfo(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool secureInfoExists(void)
|
||||
static inline bool secureInfoExists(void)
|
||||
{
|
||||
static bool exists = false;
|
||||
|
||||
@@ -76,7 +80,7 @@ static bool secureInfoExists(void)
|
||||
return exists;
|
||||
}
|
||||
|
||||
static void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
|
||||
static inline void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
|
||||
{
|
||||
static const char *paths[] = { "/luma/customversion_sys.txt",
|
||||
"/luma/customversion_emu.txt",
|
||||
@@ -134,7 +138,7 @@ static void loadCustomVerString(u16 *out, u32 *verStringSize, u32 currentNand)
|
||||
}
|
||||
}
|
||||
|
||||
static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
static inline u32 loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
{
|
||||
/* Here we look for "/luma/code_sections/[u64 titleID in hex, uppercase].bin"
|
||||
If it exists it should be a decompressed binary code file */
|
||||
@@ -143,24 +147,27 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
progIdToStr(path + 35, progId);
|
||||
|
||||
IFile file;
|
||||
u32 ret = 0;
|
||||
|
||||
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||
{
|
||||
u64 fileSize;
|
||||
|
||||
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= size)
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = 1;
|
||||
else
|
||||
{
|
||||
u64 total;
|
||||
IFile_Read(&file, &total, code, fileSize);
|
||||
|
||||
if(total != fileSize) svcBreak(USERBREAK_ASSERT);
|
||||
if(R_FAILED(IFile_Read(&file, &total, code, fileSize)) || total != fileSize) ret = 1;
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
static inline u32 loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
{
|
||||
/* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt"
|
||||
If it exists it should contain, for example, "EUR IT" */
|
||||
@@ -169,19 +176,25 @@ static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
progIdToStr(path + 29, progId);
|
||||
|
||||
IFile file;
|
||||
u32 ret = 0;
|
||||
|
||||
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||
{
|
||||
u64 fileSize;
|
||||
|
||||
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize > 5 && fileSize < 9)
|
||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) ret = 1;
|
||||
else
|
||||
{
|
||||
char buf[fileSize];
|
||||
u64 total;
|
||||
|
||||
if(R_SUCCEEDED(IFile_Read(&file, &total, buf, fileSize)))
|
||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) ret = 1;
|
||||
else
|
||||
{
|
||||
for(u32 i = 0; i < 7; i++)
|
||||
u32 i,
|
||||
j;
|
||||
|
||||
for(i = 0; i < 7; i++)
|
||||
{
|
||||
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
|
||||
|
||||
@@ -192,24 +205,28 @@ static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
}
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < 12; i++)
|
||||
for(j = 0; j < 12; j++)
|
||||
{
|
||||
static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
|
||||
|
||||
if(memcmp(buf + 4, languages[i], 2) == 0)
|
||||
if(memcmp(buf + 4, languages[j], 2) == 0)
|
||||
{
|
||||
*languageId = (u8)i;
|
||||
*languageId = (u8)j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i == 7 || j == 12) ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
{
|
||||
/* HANS:
|
||||
Look for error code which is known to be stored near cfg:u handle
|
||||
@@ -228,94 +245,105 @@ static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
}
|
||||
}
|
||||
|
||||
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||
if(n > 0)
|
||||
{
|
||||
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
||||
|
||||
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
if(cmp[0] == CFGU_GetConfigInfoBlk2_endPattern[0] && cmp[1] == CFGU_GetConfigInfoBlk2_endPattern[1])
|
||||
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||
{
|
||||
*CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
|
||||
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
||||
|
||||
for(u32 i = 0; i < n; i++)
|
||||
if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
|
||||
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
CFGU_GetConfigInfoBlk2_endPos += 4;
|
||||
if(cmp[0] == CFGU_GetConfigInfoBlk2_endPattern[0] && cmp[1] == CFGU_GetConfigInfoBlk2_endPattern[1])
|
||||
{
|
||||
*CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
|
||||
|
||||
for(u32 i = 0; i < n; i++)
|
||||
if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
|
||||
|
||||
CFGU_GetConfigInfoBlk2_endPos += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
|
||||
static inline u32 patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFGU_GetConfigInfoBlk2_endPos)
|
||||
{
|
||||
u8 *CFGU_GetConfigInfoBlk2_startPos; //Let's find STMFD SP (there might be a NOP before, but nevermind)
|
||||
|
||||
for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
|
||||
CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
|
||||
CFGU_GetConfigInfoBlk2_startPos -= 2);
|
||||
CFGU_GetConfigInfoBlk2_startPos -= 4);
|
||||
|
||||
for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
|
||||
if(CFGU_GetConfigInfoBlk2_startPos >= code)
|
||||
{
|
||||
if(*(u32 *)languageBlkIdPos == 0xA0002)
|
||||
for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
|
||||
{
|
||||
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
||||
if(*(u32 *)languageBlkIdPos == 0xA0002)
|
||||
{
|
||||
if(instr[3] == 0xEB) //We're looking for BL
|
||||
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
||||
{
|
||||
u8 *calledFunction = instr;
|
||||
u32 i = 0;
|
||||
bool found;
|
||||
|
||||
do
|
||||
if(instr[3] == 0xEB) //We're looking for BL
|
||||
{
|
||||
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
||||
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
||||
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
||||
u8 *calledFunction = instr;
|
||||
u32 i = 0;
|
||||
bool found;
|
||||
|
||||
calledFunction += offset;
|
||||
do
|
||||
{
|
||||
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
||||
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
||||
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
||||
|
||||
found = calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos;
|
||||
i++;
|
||||
}
|
||||
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
||||
calledFunction += offset;
|
||||
|
||||
if(found)
|
||||
{
|
||||
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
|
||||
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
||||
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
|
||||
found = calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos;
|
||||
i++;
|
||||
}
|
||||
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
||||
|
||||
//We're done
|
||||
return;
|
||||
if(found)
|
||||
{
|
||||
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
|
||||
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
||||
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
|
||||
|
||||
//We're done
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
|
||||
static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOffset)
|
||||
{
|
||||
for(u8 *cmdPos = code; cmdPos < code + size - 28; cmdPos += 4)
|
||||
{
|
||||
static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D4F70, 0xE3A00802, 0xE5A40080};
|
||||
static const u32 cfgSecureInfoGetRegionCmdPattern[] = {0xEE1D0F70, 0xE3A00802};
|
||||
|
||||
u32 *cmp = (u32 *)cmdPos;
|
||||
|
||||
if(cmp[0] == cfgSecureInfoGetRegionCmdPattern[0] && cmp[1] == cfgSecureInfoGetRegionCmdPattern[1] &&
|
||||
cmp[2] == cfgSecureInfoGetRegionCmdPattern[2] && *((u16 *)cmdPos + 7) == 0xE59F &&
|
||||
*(u32 *)(cmdPos + 20 + *((u16 *)cmdPos + 6)) == CFGUHandleOffset)
|
||||
if(*cmp == cfgSecureInfoGetRegionCmdPattern[1])
|
||||
{
|
||||
*((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; //mov r0, =regionId
|
||||
*((u32 *)cmdPos + 5) = 0xE5C40008; //strb r0, [r4, 8]
|
||||
*((u32 *)cmdPos + 6) = 0xE3B00000; //movs r0, 0 (result code) ('s' not needed but nvm)
|
||||
*((u32 *)cmdPos + 7) = 0xE5840004; //str r0, [r4, 4]
|
||||
for(u32 i = 1; i < 3; i++)
|
||||
if((*(cmp - i) & 0xFFFF0FFF) == cfgSecureInfoGetRegionCmdPattern[0] && *((u16 *)cmdPos + 5) == 0xE59F &&
|
||||
*(u32 *)(cmdPos + 16 + *((u16 *)cmdPos + 4)) == CFGUHandleOffset)
|
||||
{
|
||||
cmp[3] = 0xE3A00000 | regionId; //mov r0, =regionId
|
||||
cmp[4] = 0xE5C40008; //strb r0, [r4, #8]
|
||||
cmp[5] = 0xE3A00000; //mov r0, #0 (result code)
|
||||
cmp[6] = 0xE5840004; //str r0, [r4, #4]
|
||||
|
||||
//The remaining, not patched, function code will do the rest for us
|
||||
break;
|
||||
//The remaining, not patched, function code will do the rest for us
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,6 +351,7 @@ static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOff
|
||||
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
{
|
||||
loadCFWInfo();
|
||||
u32 res = 0;
|
||||
|
||||
if(((progId == 0x0004003000008F02LL || //USA Home Menu
|
||||
progId == 0x0004003000008202LL || //JPN Home Menu
|
||||
@@ -333,36 +362,36 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
progId == 0x000400300000A102LL || //CHN Home Menu
|
||||
progId == 0x000400300000B102LL) //TWN Home Menu
|
||||
{
|
||||
static const u8 regionFreePattern[] = {
|
||||
static const u8 pattern[] = {
|
||||
0x0A, 0x0C, 0x00, 0x10
|
||||
},
|
||||
regionFreePatch[] = {
|
||||
patch[] = {
|
||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||
};
|
||||
|
||||
//Patch SMDH region checks
|
||||
patchMemory(code, size,
|
||||
regionFreePattern,
|
||||
sizeof(regionFreePattern), -31,
|
||||
regionFreePatch,
|
||||
sizeof(regionFreePatch), 1
|
||||
);
|
||||
if(!patchMemory(code, size,
|
||||
pattern,
|
||||
sizeof(pattern), -31,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000003202LL) //FRIENDS
|
||||
{
|
||||
static const u8 fpdVerPattern[] = {
|
||||
static const u8 pattern[] = {
|
||||
0x42, 0xE0, 0x1E, 0xFF
|
||||
};
|
||||
|
||||
u8 mostRecentFpdVer = 8;
|
||||
|
||||
u8 *off = memsearch(code, fpdVerPattern, size, sizeof(fpdVerPattern));
|
||||
u8 *off = memsearch(code, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) svcBreak(USERBREAK_ASSERT);
|
||||
if(off == NULL) res++;
|
||||
|
||||
//Allow online access to work with old friends modules
|
||||
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||
else if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||
}
|
||||
|
||||
else if((progId == 0x0004001000021000LL || //USA MSET
|
||||
@@ -373,18 +402,18 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
progId == 0x0004001000028000LL) //TWN MSET
|
||||
&& CONFIG(PATCHVERSTRING))
|
||||
{
|
||||
static const u16 verPattern[] = u"Ve";
|
||||
static u16 *verString;
|
||||
u32 verStringSize = 0,
|
||||
static const u16 pattern[] = u"Ve";
|
||||
static u16 *patch;
|
||||
u32 patchSize = 0,
|
||||
currentNand = BOOTCFG_NAND;
|
||||
|
||||
u16 customVerString[19];
|
||||
loadCustomVerString(customVerString, &verStringSize, currentNand);
|
||||
loadCustomVerString(customVerString, &patchSize, currentNand);
|
||||
|
||||
if(verStringSize != 0) verString = customVerString;
|
||||
if(patchSize != 0) patch = customVerString;
|
||||
else
|
||||
{
|
||||
verStringSize = 8;
|
||||
patchSize = 8;
|
||||
u32 currentFirm = BOOTCFG_FIRM;
|
||||
|
||||
static u16 *verStringsNands[] = { u" Sys",
|
||||
@@ -403,37 +432,39 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
u"SyE3",
|
||||
u"SyE4" };
|
||||
|
||||
verString = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
||||
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
||||
patch = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
||||
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
||||
}
|
||||
|
||||
//Patch Ver. string
|
||||
patchMemory(code, size,
|
||||
verPattern,
|
||||
sizeof(verPattern) - 2, 0,
|
||||
verString,
|
||||
verStringSize, 1
|
||||
);
|
||||
if(!patchMemory(code, size,
|
||||
pattern,
|
||||
sizeof(pattern) - 2, 0,
|
||||
patch,
|
||||
patchSize, 1
|
||||
)) res++;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000008002LL) //NS
|
||||
{
|
||||
if(progVer > 4)
|
||||
{
|
||||
static const u8 stopCartUpdatesPattern[] = {
|
||||
static const u8 pattern[] = {
|
||||
0x0C, 0x18, 0xE1, 0xD8
|
||||
},
|
||||
stopCartUpdatesPatch[] = {
|
||||
patch[] = {
|
||||
0x0B, 0x18, 0x21, 0xC8
|
||||
};
|
||||
|
||||
//Disable updates from foreign carts (makes carts region-free)
|
||||
patchMemory(code, size,
|
||||
stopCartUpdatesPattern,
|
||||
sizeof(stopCartUpdatesPattern), 0,
|
||||
stopCartUpdatesPatch,
|
||||
sizeof(stopCartUpdatesPatch), 2
|
||||
);
|
||||
u32 ret = patchMemory(code, size,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 2
|
||||
);
|
||||
|
||||
if(ret == 0 || (ret == 1 && progVer > 0xB)) res++;
|
||||
}
|
||||
|
||||
if(LOADERFLAG(ISN3DS))
|
||||
@@ -442,140 +473,147 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||
|
||||
if(cpuSetting != 0)
|
||||
{
|
||||
static const u8 cfgN3dsCpuPattern[] = {
|
||||
static const u8 pattern[] = {
|
||||
0x0C, 0x00, 0x94, 0x15
|
||||
};
|
||||
|
||||
u32 *off = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
|
||||
u32 *off = (u32 *)memsearch(code, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) svcBreak(USERBREAK_ASSERT);
|
||||
|
||||
//Patch N3DS CPU Clock and L2 cache setting
|
||||
*(off - 4) = 0xE1A00000;
|
||||
*(off + 3) = 0xE3A00000 | cpuSetting;
|
||||
if(off == NULL) res++;
|
||||
else
|
||||
{
|
||||
//Patch N3DS CPU Clock and L2 cache setting
|
||||
*(off - 4) = *(off - 3);
|
||||
*(off - 3) = *(off - 1);
|
||||
memcpy(off - 1, off, 16);
|
||||
*(off + 3) = 0xE3800000 | cpuSetting;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if(progId == 0x0004013000001702LL) //CFG
|
||||
{
|
||||
static const u8 secureinfoSigCheckPattern[] = {
|
||||
static const u8 pattern[] = {
|
||||
0x06, 0x46, 0x10, 0x48
|
||||
},
|
||||
secureinfoSigCheckPatch[] = {
|
||||
patch[] = {
|
||||
0x00, 0x26
|
||||
};
|
||||
|
||||
//Disable SecureInfo signature check
|
||||
patchMemory(code, size,
|
||||
secureinfoSigCheckPattern,
|
||||
sizeof(secureinfoSigCheckPattern), 0,
|
||||
secureinfoSigCheckPatch,
|
||||
sizeof(secureinfoSigCheckPatch), 1
|
||||
);
|
||||
if(!patchMemory(code, size,
|
||||
pattern,
|
||||
sizeof(pattern), 0,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
|
||||
if(secureInfoExists())
|
||||
{
|
||||
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_",
|
||||
secureinfoFilenamePatch[] = u"C";
|
||||
static const u16 pattern[] = u"Sec",
|
||||
patch[] = u"C";
|
||||
|
||||
//Use SecureInfo_C
|
||||
patchMemory(code, size,
|
||||
secureinfoFilenamePattern,
|
||||
sizeof(secureinfoFilenamePattern) - 2,
|
||||
sizeof(secureinfoFilenamePattern) - 2,
|
||||
secureinfoFilenamePatch,
|
||||
sizeof(secureinfoFilenamePatch) - 2, 2
|
||||
);
|
||||
if(patchMemory(code, size,
|
||||
pattern,
|
||||
sizeof(pattern) - 2, 22,
|
||||
patch,
|
||||
sizeof(patch) - 2, 2
|
||||
) != 2) res++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
else if(progId == 0x0004013000003702LL && progVer > 0) //RO
|
||||
{
|
||||
static const u8 sigCheckPattern[] = {
|
||||
static const u8 pattern[] = {
|
||||
0x20, 0xA0, 0xE1, 0x8B
|
||||
},
|
||||
sha256ChecksPattern1[] = {
|
||||
pattern2[] = {
|
||||
0xE1, 0x30, 0x40, 0x2D
|
||||
},
|
||||
sha256ChecksPattern2[] = {
|
||||
pattern3[] = {
|
||||
0x2D, 0xE9, 0x01, 0x70
|
||||
},
|
||||
stub[] = {
|
||||
patch[] = {
|
||||
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), -9,
|
||||
stub,
|
||||
sizeof(stub), 1
|
||||
);
|
||||
if(!patchMemory(code, size,
|
||||
pattern,
|
||||
sizeof(pattern), -9,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
|
||||
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||
patchMemory(code, size,
|
||||
sha256ChecksPattern1,
|
||||
sizeof(sha256ChecksPattern1), 1,
|
||||
stub,
|
||||
sizeof(stub), 1
|
||||
);
|
||||
if(!patchMemory(code, size,
|
||||
pattern2,
|
||||
sizeof(pattern2), 1,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
|
||||
patchMemory(code, size,
|
||||
sha256ChecksPattern2,
|
||||
sizeof(sha256ChecksPattern2), -2,
|
||||
stub,
|
||||
sizeof(stub), 1
|
||||
);
|
||||
if(!patchMemory(code, size,
|
||||
pattern3,
|
||||
sizeof(pattern3), -2,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
}
|
||||
|
||||
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
|
||||
{
|
||||
static const u8 unitinfoCheckPattern1[] = {
|
||||
static const u8 pattern[] = {
|
||||
0x00, 0xD0, 0xE5, 0xDB
|
||||
},
|
||||
unitinfoCheckPattern2[] = {
|
||||
pattern2[] = {
|
||||
0x14, 0x00, 0xD0, 0xE5, 0x01
|
||||
},
|
||||
unitinfoCheckPatch[] = {
|
||||
patch[] = {
|
||||
0x00, 0x00, 0xA0, 0xE3
|
||||
};
|
||||
|
||||
patchMemory(code, size,
|
||||
unitinfoCheckPattern1,
|
||||
sizeof(unitinfoCheckPattern1), -1,
|
||||
unitinfoCheckPatch,
|
||||
sizeof(unitinfoCheckPatch), 1
|
||||
);
|
||||
//Patch UNITINFO checks to make ErrDisp more verbose
|
||||
if(!patchMemory(code, size,
|
||||
pattern,
|
||||
sizeof(pattern), -1,
|
||||
patch,
|
||||
sizeof(patch), 1
|
||||
)) res++;
|
||||
|
||||
patchMemory(code, size,
|
||||
unitinfoCheckPattern2,
|
||||
sizeof(unitinfoCheckPattern2), 0,
|
||||
unitinfoCheckPatch,
|
||||
sizeof(unitinfoCheckPatch), 3
|
||||
);
|
||||
if(patchMemory(code, size,
|
||||
pattern2,
|
||||
sizeof(pattern2), 0,
|
||||
patch,
|
||||
sizeof(patch), 3
|
||||
) != 3) res++;
|
||||
}
|
||||
|
||||
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
||||
{
|
||||
//External .code section loading
|
||||
loadTitleCodeSection(progId, code, size);
|
||||
res += loadTitleCodeSection(progId, code, size);
|
||||
|
||||
//Language emulation
|
||||
u8 regionId = 0xFF,
|
||||
languageId = 0xFF;
|
||||
loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
||||
languageId;
|
||||
res += loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
||||
|
||||
if(regionId != 0xFF || regionId != 0xFF)
|
||||
if(!res && regionId != 0xFF)
|
||||
{
|
||||
u32 CFGUHandleOffset;
|
||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
|
||||
|
||||
if(CFGU_GetConfigInfoBlk2_endPos != NULL)
|
||||
if(CFGU_GetConfigInfoBlk2_endPos == NULL) res++;
|
||||
else
|
||||
{
|
||||
if(languageId != 0xFF) patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
||||
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
||||
res += patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
||||
patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(res != 0) svcBreak(USERBREAK_ASSERT);
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
payload_addr equ 0x23F00000 ; Brahma payload address
|
||||
payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeBrah supports)
|
||||
sd_notmounted equ 0xC8804465 ; Error code returned when SD is not mounted
|
||||
|
||||
.create "build/reboot.bin", 0
|
||||
.arm
|
||||
@@ -28,6 +27,8 @@ sd_notmounted equ 0xC8804465 ; Error code returned when SD is not mounted
|
||||
cmp r0, r2
|
||||
bne pxi_wait_recv
|
||||
|
||||
mov r4, #2
|
||||
|
||||
open_payload:
|
||||
; Open file
|
||||
add r0, r7, #8
|
||||
@@ -38,9 +39,8 @@ sd_notmounted equ 0xC8804465 ; Error code returned when SD is not mounted
|
||||
blx r6
|
||||
cmp r0, #0
|
||||
beq read_payload
|
||||
ldr r2, =sd_notmounted
|
||||
cmp r0, r2
|
||||
bne panic
|
||||
subs r4, r4, #1
|
||||
beq panic
|
||||
adr r0, fname
|
||||
adr r1, nand_mount
|
||||
mov r2, #8
|
||||
|
||||
@@ -113,7 +113,8 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
"(refer to the wiki for instructions).",
|
||||
|
||||
"Select the New 3DS CPU mode.\n\n"
|
||||
"It will be always enabled.\n\n"
|
||||
"This won't apply to\n"
|
||||
"New 3DS exclusive/enhanced games.\n\n"
|
||||
"'Clock+L2' can cause issues with some\n"
|
||||
"games.",
|
||||
|
||||
@@ -126,7 +127,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
"always detected as a development unit\n"
|
||||
"(which breaks online features and\n"
|
||||
"allows booting some developer\n"
|
||||
"software).\n\n"
|
||||
"software and installing dev CIAs).\n\n"
|
||||
"Only change this if you know what you\n"
|
||||
"are doing!",
|
||||
|
||||
@@ -373,7 +374,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||
}
|
||||
|
||||
//Preserve the last-used boot options (first 9 bits)
|
||||
configData.config &= 0x1FF;
|
||||
configData.config &= 0xFF;
|
||||
|
||||
//Parse and write the new configuration
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
|
||||
@@ -299,18 +299,18 @@ static void sha(void *res, const void *src, u32 size, u32 mode)
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
static u8 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE];
|
||||
__attribute__((aligned(4))) static u8 nandCtr[AES_BLOCK_SIZE],
|
||||
shaHashBackup[SHA_256_HASH_SIZE];
|
||||
static u8 nandSlot;
|
||||
static u32 fatStart;
|
||||
static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE];
|
||||
static bool didShaHashBackup = false;
|
||||
|
||||
FirmwareSource firmSource;
|
||||
|
||||
void ctrNandInit(void)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||
u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE];
|
||||
__attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
|
||||
shaSum[SHA_256_HASH_SIZE];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
||||
@@ -318,7 +318,7 @@ void ctrNandInit(void)
|
||||
|
||||
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};
|
||||
__attribute__((aligned(4))) u8 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);
|
||||
|
||||
nandSlot = 0x05;
|
||||
@@ -333,7 +333,7 @@ void ctrNandInit(void)
|
||||
|
||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
|
||||
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
@@ -359,7 +359,7 @@ int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf)
|
||||
u8 *buffer = (u8 *)0x23000000;
|
||||
u32 bufferSize = 0xF00000;
|
||||
|
||||
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
|
||||
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(nandSlot);
|
||||
@@ -383,15 +383,21 @@ int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf)
|
||||
|
||||
void set6x7xKeys(void)
|
||||
{
|
||||
const u8 __attribute__((aligned(4))) keyX0x25[AES_BLOCK_SIZE] = {0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3};
|
||||
const u8 __attribute__((aligned(4))) keyY0x2F[AES_BLOCK_SIZE] = {0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16};
|
||||
__attribute__((aligned(4))) const u8 keyX0x25s[2][AES_BLOCK_SIZE] = {
|
||||
{0xCE, 0xE7, 0xD8, 0xAB, 0x30, 0xC0, 0x0D, 0xAE, 0x85, 0x0E, 0xF5, 0xE3, 0x82, 0xAC, 0x5A, 0xF3},
|
||||
{0x81, 0x90, 0x7A, 0x4B, 0x6F, 0x1B, 0x47, 0x32, 0x3A, 0x67, 0x79, 0x74, 0xCE, 0x4A, 0xD7, 0x1B}
|
||||
},
|
||||
keyY0x2Fs[2][AES_BLOCK_SIZE] = {
|
||||
{0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16},
|
||||
{0x73, 0x25, 0xC4, 0xEB, 0x14, 0x3A, 0x0D, 0x5F, 0x5D, 0xB6, 0xE5, 0xC5, 0x7A, 0x21, 0x95, 0xAC}
|
||||
};
|
||||
|
||||
aes_setkey(0x25, keyX0x25, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setkey(0x2F, keyY0x2F, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setkey(0x25, keyX0x25s[ISDEVUNIT ? 1 : 0], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setkey(0x2F, keyY0x2Fs[ISDEVUNIT ? 1 : 0], AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
/* [3dbrew] The first 0x10-bytes are checked by the v6.0/v7.0 NATIVE_FIRM keyinit function,
|
||||
when non-zero it clears this block and continues to do the key generation.
|
||||
Otherwise when this block was already all-zero, it immediately returns. */
|
||||
when non-zero it clears this block and continues to do the key generation.
|
||||
Otherwise when this block was already all-zero, it immediately returns. */
|
||||
memset32((void *)0x01FFCD00, 0, 0x10);
|
||||
}
|
||||
|
||||
@@ -405,7 +411,7 @@ bool decryptExeFs(Cxi *cxi)
|
||||
|
||||
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
||||
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||
__attribute__((aligned(4))) u8 ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||
@@ -429,9 +435,9 @@ bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||
{
|
||||
isTicket = true;
|
||||
|
||||
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};
|
||||
__attribute__((aligned(4))) const u8 keyY0x3D[AES_BLOCK_SIZE] = {0x0C, 0x76, 0x72, 0x30, 0xF0, 0x99, 0x8F, 0x1C, 0x46, 0x82, 0x82, 0x02, 0xFA, 0xAC, 0xBE, 0x4C};
|
||||
__attribute__((aligned(4))) u8 titleKey[AES_BLOCK_SIZE],
|
||||
cetkIv[AES_BLOCK_SIZE] = {0};
|
||||
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
|
||||
memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId));
|
||||
|
||||
@@ -439,7 +445,7 @@ bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||
aes_use_keyslot(0x3D);
|
||||
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
u8 __attribute__((aligned(4))) ncchIv[AES_BLOCK_SIZE] = {0};
|
||||
__attribute__((aligned(4))) u8 ncchIv[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
aes_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x16);
|
||||
@@ -470,12 +476,19 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
||||
u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
|
||||
bool needToDecrypt = *startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000;
|
||||
|
||||
if(!ISDEVUNIT && (k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt)))
|
||||
if(k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt))
|
||||
{
|
||||
//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))) key2[AES_BLOCK_SIZE] = {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0};
|
||||
aes_setkey(0x11, k9lVersion == 2 ? key2 : key1, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
__attribute__((aligned(4))) const u8 key1s[2][AES_BLOCK_SIZE] = {
|
||||
{0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8},
|
||||
{0xA2, 0xF4, 0x00, 0x3C, 0x7A, 0x95, 0x10, 0x25, 0xDF, 0x4E, 0x9E, 0x74, 0xE3, 0x0C, 0x92, 0x99}
|
||||
},
|
||||
key2s[2][AES_BLOCK_SIZE] = {
|
||||
{0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0},
|
||||
{0xFF, 0x77, 0xA0, 0x9A, 0x99, 0x81, 0xE9, 0x48, 0xEC, 0x51, 0xC9, 0x32, 0x5D, 0x14, 0xEC, 0x25}
|
||||
};
|
||||
|
||||
aes_setkey(0x11, k9lVersion == 2 ? key2s[ISDEVUNIT ? 1 : 0] : key1s[ISDEVUNIT ? 1 : 0], AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
if(needToDecrypt)
|
||||
@@ -488,19 +501,19 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
||||
arm9BinSlot = 0x16;
|
||||
|
||||
//Set keyX
|
||||
u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE];
|
||||
__attribute__((aligned(4))) u8 keyX[AES_BLOCK_SIZE];
|
||||
aes_use_keyslot(0x11);
|
||||
aes(keyX, arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
aes_setkey(0x16, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
//Set keyY
|
||||
u8 __attribute__((aligned(4))) keyY[AES_BLOCK_SIZE];
|
||||
__attribute__((aligned(4))) u8 keyY[AES_BLOCK_SIZE];
|
||||
memcpy(keyY, arm9Section->keyY, sizeof(keyY));
|
||||
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Set CTR
|
||||
u8 __attribute__((aligned(4))) arm9BinCtr[AES_BLOCK_SIZE];
|
||||
__attribute__((aligned(4))) u8 arm9BinCtr[AES_BLOCK_SIZE];
|
||||
memcpy(arm9BinCtr, arm9Section->ctr, sizeof(arm9BinCtr));
|
||||
|
||||
//Decrypt ARM9 binary
|
||||
@@ -513,8 +526,8 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
||||
//Set >=9.6 KeyXs
|
||||
if(k9lVersion == 2)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) keyData[AES_BLOCK_SIZE] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98};
|
||||
u8 __attribute__((aligned(4))) decKey[sizeof(keyData)];
|
||||
__attribute__((aligned(4))) u8 keyData[AES_BLOCK_SIZE] = {0xDD, 0xDA, 0xA4, 0xC6, 0x2C, 0xC4, 0x50, 0xE9, 0xDA, 0xB6, 0x9B, 0x0D, 0x9D, 0x2A, 0x21, 0x98},
|
||||
decKey[sizeof(keyData)];
|
||||
|
||||
//Set keys 0x19..0x1F keyXs
|
||||
aes_use_keyslot(0x11);
|
||||
@@ -528,11 +541,11 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
||||
|
||||
void computePinHash(u8 *outbuf, const u8 *inbuf)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||
u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE];
|
||||
__attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
|
||||
cipherText[AES_BLOCK_SIZE];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
||||
aes_use_keyslot(0x04); //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);
|
||||
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
|
||||
}
|
||||
|
||||
@@ -51,13 +51,13 @@ u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffse
|
||||
u32 *endPos = exceptionsPage + 0x400;
|
||||
|
||||
u32 *initFPU;
|
||||
for(initFPU = exceptionsPage; *initFPU != 0xE1A0D002 && initFPU < endPos; initFPU++);
|
||||
for(initFPU = exceptionsPage; initFPU < endPos && *initFPU != 0xE1A0D002; initFPU++);
|
||||
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = initFPU; *freeSpace != 0xFFFFFFFF && freeSpace < endPos; freeSpace++);
|
||||
for(freeSpace = initFPU; freeSpace < endPos && *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||
|
||||
u32 *mcuReboot;
|
||||
for(mcuReboot = exceptionsPage; *mcuReboot != 0xE3A0A0C2 && mcuReboot < endPos; mcuReboot++);
|
||||
for(mcuReboot = exceptionsPage; mcuReboot < endPos && *mcuReboot != 0xE3A0A0C2; mcuReboot++);
|
||||
|
||||
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) ret = 1;
|
||||
else
|
||||
|
||||
@@ -58,7 +58,7 @@ u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStora
|
||||
|
||||
bool mustLoadFromStorage = false;
|
||||
|
||||
if(!ISN3DS && *firmType == NATIVE_FIRM)
|
||||
if(!ISN3DS && *firmType == NATIVE_FIRM && !ISDEVUNIT)
|
||||
{
|
||||
if(firmVersion < 0x18)
|
||||
{
|
||||
@@ -104,6 +104,7 @@ u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStora
|
||||
{
|
||||
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
|
||||
if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
|
||||
if(ISDEVUNIT) firmVersion = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return firmVersion;
|
||||
@@ -121,8 +122,8 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
||||
firm->arm9Entry = (u8 *)0x801B01C;
|
||||
}
|
||||
|
||||
//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 && !ISFIRMLAUNCH && firmVersion >= 0x29 && !ISDEVUNIT) set6x7xKeys();
|
||||
//Sets the 7.x NCCH KeyX and the 6.x gamecard save data KeyY on >= 6.0 O3DS FIRMs, if not using A9LH
|
||||
else if(!ISA9LH && !ISFIRMLAUNCH && firmVersion >= 0x29) set6x7xKeys();
|
||||
|
||||
//Find the Process9 .code location, size and memory address
|
||||
u32 process9Size,
|
||||
@@ -145,12 +146,19 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
||||
//Apply EmuNAND patches
|
||||
if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, emuHeader, firm->section[2].address);
|
||||
|
||||
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
||||
//Apply FIRM0/1 writes patches on SysNAND to protect A9LH
|
||||
else if(isA9lhInstalled) ret += patchFirmWrites(process9Offset, process9Size);
|
||||
|
||||
//Apply firmlaunch patches
|
||||
ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
|
||||
|
||||
//Apply dev unit check patches related to NCCH encryption
|
||||
if(!ISDEVUNIT)
|
||||
{
|
||||
ret += patchZeroKeyNcchEncryptionCheck(process9Offset, process9Size);
|
||||
ret += patchNandNcchEncryptionCheck(process9Offset, process9Size);
|
||||
}
|
||||
|
||||
//11.0 FIRM patches
|
||||
if(firmVersion >= (ISN3DS ? 0x21 : 0x52))
|
||||
{
|
||||
@@ -163,8 +171,12 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
||||
|
||||
ret += implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space, isSafeMode);
|
||||
|
||||
//Apply UNITINFO patch
|
||||
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||
//Apply UNITINFO patches
|
||||
if(devMode == 2)
|
||||
{
|
||||
ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||
if(!ISDEVUNIT) ret += patchCheckForDevCommonKey(process9Offset, process9Size);
|
||||
}
|
||||
|
||||
if(devMode != 0 && isA9lhInstalled)
|
||||
{
|
||||
|
||||
@@ -185,7 +185,8 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
const char *firmFolders[][2] = {{ "00000002", "20000002" },
|
||||
{ "00000102", "20000102" },
|
||||
{ "00000202", "20000202" },
|
||||
{ "00000003", "20000003" }};
|
||||
{ "00000003", "20000003" },
|
||||
{ "00000001", "20000001" }};
|
||||
|
||||
char path[48] = "1:/title/00040138/";
|
||||
concatenateStrings(path, firmFolders[firmType][ISN3DS ? 1 : 0]);
|
||||
|
||||
@@ -62,8 +62,18 @@ void main(void)
|
||||
{
|
||||
if(needConfig == CREATE_CONFIGURATION) mcuPowerOff();
|
||||
|
||||
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
||||
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
|
||||
switch(launchedFirmTidLow[7])
|
||||
{
|
||||
case u'2':
|
||||
firmType = (FirmwareType)(launchedFirmTidLow[5] - u'0');
|
||||
break;
|
||||
case u'3':
|
||||
firmType = SAFE_FIRM;
|
||||
break;
|
||||
case u'1':
|
||||
firmType = SYSUPDATER_FIRM;
|
||||
break;
|
||||
}
|
||||
|
||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||
@@ -238,16 +248,17 @@ void main(void)
|
||||
case NATIVE_FIRM:
|
||||
res = patchNativeFirm(firmVersion, nandType, emuHeader, isA9lhInstalled, isSafeMode, devMode);
|
||||
break;
|
||||
case SAFE_FIRM:
|
||||
case NATIVE_FIRM1X2X:
|
||||
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
|
||||
break;
|
||||
case TWL_FIRM:
|
||||
res = patchTwlFirm(firmVersion, devMode);
|
||||
break;
|
||||
case AGB_FIRM:
|
||||
res = patchAgbFirm(devMode);
|
||||
break;
|
||||
case SAFE_FIRM:
|
||||
case SYSUPDATER_FIRM:
|
||||
case NATIVE_FIRM1X2X:
|
||||
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(res != 0)
|
||||
|
||||
@@ -206,6 +206,84 @@ u32 patchOldFirmWrites(u8 *pos, u32 size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||
{
|
||||
const u8 pattern[] = {0xFF, 0x00, 0x00, 0x02};
|
||||
u32 ret;
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = firmVersion == 0xFFFFFFFF ? 0 : 1;
|
||||
else
|
||||
{
|
||||
off++;
|
||||
|
||||
memset32(off, 0, 8); //Zero out the first TitleID in the list
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x28, 0x2A, 0xD0, 0x08};
|
||||
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 patchNandNcchEncryptionCheck(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x07, 0xD1, 0x28, 0x7A};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
off--;
|
||||
|
||||
*off = 0x2001; //mov r0, #1
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchCheckForDevCommonKey(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x03, 0x7C, 0x28, 0x00};
|
||||
u32 ret;
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = 1;
|
||||
else
|
||||
{
|
||||
*off = 0x2301; //mov r3, #1
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||
{
|
||||
u32 ret = 0;
|
||||
@@ -291,26 +369,6 @@ u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **free
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||
{
|
||||
const u8 pattern[] = {0xFF, 0x00, 0x00, 0x02};
|
||||
u32 ret;
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off == NULL) ret = firmVersion == 0xFFFFFFFF ? 0 : 1;
|
||||
else
|
||||
{
|
||||
off++;
|
||||
|
||||
memset32(off, 0, 8); //Zero out the first TitleID in the list
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
|
||||
|
||||
@@ -37,10 +37,13 @@ extern CfgData configData;
|
||||
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 patchSignatureChecks(u8 *pos, u32 size);
|
||||
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion);
|
||||
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||
u32 patchFirmWrites(u8 *pos, u32 size);
|
||||
u32 patchOldFirmWrites(u8 *pos, u32 size);
|
||||
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion);
|
||||
u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size);
|
||||
u32 patchNandNcchEncryptionCheck(u8 *pos, u32 size);
|
||||
u32 patchCheckForDevCommonKey(u8 *pos, u32 size);
|
||||
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode);
|
||||
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
|
||||
|
||||
12
source/pin.c
12
source/pin.c
@@ -56,7 +56,7 @@ void newPin(bool allowSkipping, u32 pinMode)
|
||||
drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
//Pad to AES block length with zeroes
|
||||
u8 __attribute__((aligned(4))) enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
__attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
u8 cnt = 0;
|
||||
u32 charDrawPos = 16 * SPACING_X;
|
||||
@@ -90,8 +90,8 @@ void newPin(bool allowSkipping, u32 pinMode)
|
||||
pin.formatVersionMajor = PIN_VERSIONMAJOR;
|
||||
pin.formatVersionMinor = PIN_VERSIONMINOR;
|
||||
|
||||
u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE];
|
||||
u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0};
|
||||
__attribute__((aligned(4))) u8 tmp[SHA_256_HASH_SIZE],
|
||||
lengthBlock[AES_BLOCK_SIZE] = {0};
|
||||
lengthBlock[0] = length;
|
||||
|
||||
computePinHash(tmp, lengthBlock);
|
||||
@@ -114,8 +114,8 @@ bool verifyPin(u32 pinMode)
|
||||
pin.formatVersionMinor != PIN_VERSIONMINOR)
|
||||
return false;
|
||||
|
||||
u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE];
|
||||
u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0};
|
||||
__attribute__((aligned(4))) u8 tmp[SHA_256_HASH_SIZE],
|
||||
lengthBlock[AES_BLOCK_SIZE] = {0};
|
||||
lengthBlock[0] = 4 + 2 * (pinMode - 1);
|
||||
|
||||
computePinHash(tmp, lengthBlock);
|
||||
@@ -126,7 +126,7 @@ bool verifyPin(u32 pinMode)
|
||||
initScreens();
|
||||
|
||||
//Pad to AES block length with zeroes
|
||||
u8 __attribute__((aligned(4))) enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
__attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
bool unlock = false;
|
||||
u8 cnt = 0;
|
||||
|
||||
@@ -97,6 +97,7 @@ typedef enum FirmwareType
|
||||
TWL_FIRM,
|
||||
AGB_FIRM,
|
||||
SAFE_FIRM,
|
||||
SYSUPDATER_FIRM,
|
||||
NATIVE_FIRM1X2X
|
||||
} FirmwareType;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user