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
|
cps #0x13 @ switch to supervisor mode
|
||||||
cmp r10, #0
|
cmp r10, #0
|
||||||
addne sp, #0x28
|
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]
|
ldr r4, [sp, #0x18]
|
||||||
msr cpsr_c, r3 @ restore processor mode
|
msr cpsr_c, r3 @ restore processor mode
|
||||||
tst r2, #0x20
|
tst r2, #0x20
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ _commonHandler:
|
|||||||
bic r5, r3, #0xf
|
bic r5, r3, #0xf
|
||||||
orr r5, #0x3
|
orr r5, #0x3
|
||||||
msr cpsr_c, r5 @ switch to supervisor mode
|
msr cpsr_c, r5 @ switch to supervisor mode
|
||||||
|
ldmfd sp, {r8-r11}^
|
||||||
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
||||||
ldr r4, [sp, #0x18]
|
ldr r4, [sp, #0x18]
|
||||||
msr cpsr_c, r3 @ restore processor mode
|
msr cpsr_c, r3 @ restore processor mode
|
||||||
|
|||||||
@@ -7,13 +7,15 @@
|
|||||||
|
|
||||||
static CFWInfo info;
|
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);
|
u8 *found = memsearch(start, pattern, size, patSize);
|
||||||
|
|
||||||
if(found == NULL) svcBreak(USERBREAK_ASSERT);
|
if(found == NULL) break;
|
||||||
|
|
||||||
memcpy(found + offset, replace, repSize);
|
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;
|
size -= at + patSize;
|
||||||
start = found + patSize;
|
start = found + patSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
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;
|
static bool exists = false;
|
||||||
|
|
||||||
@@ -76,7 +80,7 @@ static bool secureInfoExists(void)
|
|||||||
return exists;
|
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",
|
static const char *paths[] = { "/luma/customversion_sys.txt",
|
||||||
"/luma/customversion_emu.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"
|
/* Here we look for "/luma/code_sections/[u64 titleID in hex, uppercase].bin"
|
||||||
If it exists it should be a decompressed binary code file */
|
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);
|
progIdToStr(path + 35, progId);
|
||||||
|
|
||||||
IFile file;
|
IFile file;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||||
{
|
{
|
||||||
u64 fileSize;
|
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;
|
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);
|
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"
|
/* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt"
|
||||||
If it exists it should contain, for example, "EUR IT" */
|
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);
|
progIdToStr(path + 29, progId);
|
||||||
|
|
||||||
IFile file;
|
IFile file;
|
||||||
|
u32 ret = 0;
|
||||||
|
|
||||||
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
if(R_SUCCEEDED(openLumaFile(&file, path)))
|
||||||
{
|
{
|
||||||
u64 fileSize;
|
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];
|
char buf[fileSize];
|
||||||
u64 total;
|
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"};
|
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"};
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(i == 7 || j == 12) ret = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IFile_Close(&file);
|
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:
|
/* HANS:
|
||||||
Look for error code which is known to be stored near cfg:u handle
|
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};
|
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||||
|
|
||||||
//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])
|
|
||||||
{
|
{
|
||||||
*CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
|
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
||||||
|
|
||||||
for(u32 i = 0; i < n; i++)
|
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||||
if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
|
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;
|
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)
|
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;
|
for(CFGU_GetConfigInfoBlk2_startPos = CFGU_GetConfigInfoBlk2_endPos - 4;
|
||||||
CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
|
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;
|
if(instr[3] == 0xEB) //We're looking for BL
|
||||||
u32 i = 0;
|
|
||||||
bool found;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
u32 low24 = (*(u32 *)calledFunction & 0x00FFFFFF) << 2;
|
u8 *calledFunction = instr;
|
||||||
u32 signMask = (u32)(-(low24 >> 25)) & 0xFC000000; //Sign extension
|
u32 i = 0;
|
||||||
s32 offset = (s32)(low24 | signMask) + 8; //Branch offset + 8 for prefetch
|
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;
|
calledFunction += offset;
|
||||||
i++;
|
|
||||||
}
|
|
||||||
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
|
||||||
|
|
||||||
if(found)
|
found = calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos;
|
||||||
{
|
i++;
|
||||||
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
|
}
|
||||||
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
||||||
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
|
|
||||||
|
|
||||||
//We're done
|
if(found)
|
||||||
return;
|
{
|
||||||
|
*((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)
|
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;
|
u32 *cmp = (u32 *)cmdPos;
|
||||||
|
|
||||||
if(cmp[0] == cfgSecureInfoGetRegionCmdPattern[0] && cmp[1] == cfgSecureInfoGetRegionCmdPattern[1] &&
|
if(*cmp == cfgSecureInfoGetRegionCmdPattern[1])
|
||||||
cmp[2] == cfgSecureInfoGetRegionCmdPattern[2] && *((u16 *)cmdPos + 7) == 0xE59F &&
|
|
||||||
*(u32 *)(cmdPos + 20 + *((u16 *)cmdPos + 6)) == CFGUHandleOffset)
|
|
||||||
{
|
{
|
||||||
*((u32 *)cmdPos + 4) = 0xE3A00000 | regionId; //mov r0, =regionId
|
for(u32 i = 1; i < 3; i++)
|
||||||
*((u32 *)cmdPos + 5) = 0xE5C40008; //strb r0, [r4, 8]
|
if((*(cmp - i) & 0xFFFF0FFF) == cfgSecureInfoGetRegionCmdPattern[0] && *((u16 *)cmdPos + 5) == 0xE59F &&
|
||||||
*((u32 *)cmdPos + 6) = 0xE3B00000; //movs r0, 0 (result code) ('s' not needed but nvm)
|
*(u32 *)(cmdPos + 16 + *((u16 *)cmdPos + 4)) == CFGUHandleOffset)
|
||||||
*((u32 *)cmdPos + 7) = 0xE5840004; //str r0, [r4, 4]
|
{
|
||||||
|
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
|
//The remaining, not patched, function code will do the rest for us
|
||||||
break;
|
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)
|
void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
||||||
{
|
{
|
||||||
loadCFWInfo();
|
loadCFWInfo();
|
||||||
|
u32 res = 0;
|
||||||
|
|
||||||
if(((progId == 0x0004003000008F02LL || //USA Home Menu
|
if(((progId == 0x0004003000008F02LL || //USA Home Menu
|
||||||
progId == 0x0004003000008202LL || //JPN 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 == 0x000400300000A102LL || //CHN Home Menu
|
||||||
progId == 0x000400300000B102LL) //TWN Home Menu
|
progId == 0x000400300000B102LL) //TWN Home Menu
|
||||||
{
|
{
|
||||||
static const u8 regionFreePattern[] = {
|
static const u8 pattern[] = {
|
||||||
0x0A, 0x0C, 0x00, 0x10
|
0x0A, 0x0C, 0x00, 0x10
|
||||||
},
|
},
|
||||||
regionFreePatch[] = {
|
patch[] = {
|
||||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||||
};
|
};
|
||||||
|
|
||||||
//Patch SMDH region checks
|
//Patch SMDH region checks
|
||||||
patchMemory(code, size,
|
if(!patchMemory(code, size,
|
||||||
regionFreePattern,
|
pattern,
|
||||||
sizeof(regionFreePattern), -31,
|
sizeof(pattern), -31,
|
||||||
regionFreePatch,
|
patch,
|
||||||
sizeof(regionFreePatch), 1
|
sizeof(patch), 1
|
||||||
);
|
)) res++;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000003202LL) //FRIENDS
|
else if(progId == 0x0004013000003202LL) //FRIENDS
|
||||||
{
|
{
|
||||||
static const u8 fpdVerPattern[] = {
|
static const u8 pattern[] = {
|
||||||
0x42, 0xE0, 0x1E, 0xFF
|
0x42, 0xE0, 0x1E, 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
u8 mostRecentFpdVer = 8;
|
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
|
//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
|
else if((progId == 0x0004001000021000LL || //USA MSET
|
||||||
@@ -373,18 +402,18 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
progId == 0x0004001000028000LL) //TWN MSET
|
progId == 0x0004001000028000LL) //TWN MSET
|
||||||
&& CONFIG(PATCHVERSTRING))
|
&& CONFIG(PATCHVERSTRING))
|
||||||
{
|
{
|
||||||
static const u16 verPattern[] = u"Ve";
|
static const u16 pattern[] = u"Ve";
|
||||||
static u16 *verString;
|
static u16 *patch;
|
||||||
u32 verStringSize = 0,
|
u32 patchSize = 0,
|
||||||
currentNand = BOOTCFG_NAND;
|
currentNand = BOOTCFG_NAND;
|
||||||
|
|
||||||
u16 customVerString[19];
|
u16 customVerString[19];
|
||||||
loadCustomVerString(customVerString, &verStringSize, currentNand);
|
loadCustomVerString(customVerString, &patchSize, currentNand);
|
||||||
|
|
||||||
if(verStringSize != 0) verString = customVerString;
|
if(patchSize != 0) patch = customVerString;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
verStringSize = 8;
|
patchSize = 8;
|
||||||
u32 currentFirm = BOOTCFG_FIRM;
|
u32 currentFirm = BOOTCFG_FIRM;
|
||||||
|
|
||||||
static u16 *verStringsNands[] = { u" Sys",
|
static u16 *verStringsNands[] = { u" Sys",
|
||||||
@@ -403,37 +432,39 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
u"SyE3",
|
u"SyE3",
|
||||||
u"SyE4" };
|
u"SyE4" };
|
||||||
|
|
||||||
verString = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
patch = (currentFirm != 0) == (currentNand != 0) ? verStringsNands[currentNand] :
|
||||||
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
(!currentNand ? verStringsSysEmu[currentFirm - 1] : verStringsEmuSys[currentNand - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Patch Ver. string
|
//Patch Ver. string
|
||||||
patchMemory(code, size,
|
if(!patchMemory(code, size,
|
||||||
verPattern,
|
pattern,
|
||||||
sizeof(verPattern) - 2, 0,
|
sizeof(pattern) - 2, 0,
|
||||||
verString,
|
patch,
|
||||||
verStringSize, 1
|
patchSize, 1
|
||||||
);
|
)) res++;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000008002LL) //NS
|
else if(progId == 0x0004013000008002LL) //NS
|
||||||
{
|
{
|
||||||
if(progVer > 4)
|
if(progVer > 4)
|
||||||
{
|
{
|
||||||
static const u8 stopCartUpdatesPattern[] = {
|
static const u8 pattern[] = {
|
||||||
0x0C, 0x18, 0xE1, 0xD8
|
0x0C, 0x18, 0xE1, 0xD8
|
||||||
},
|
},
|
||||||
stopCartUpdatesPatch[] = {
|
patch[] = {
|
||||||
0x0B, 0x18, 0x21, 0xC8
|
0x0B, 0x18, 0x21, 0xC8
|
||||||
};
|
};
|
||||||
|
|
||||||
//Disable updates from foreign carts (makes carts region-free)
|
//Disable updates from foreign carts (makes carts region-free)
|
||||||
patchMemory(code, size,
|
u32 ret = patchMemory(code, size,
|
||||||
stopCartUpdatesPattern,
|
pattern,
|
||||||
sizeof(stopCartUpdatesPattern), 0,
|
sizeof(pattern), 0,
|
||||||
stopCartUpdatesPatch,
|
patch,
|
||||||
sizeof(stopCartUpdatesPatch), 2
|
sizeof(patch), 2
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if(ret == 0 || (ret == 1 && progVer > 0xB)) res++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LOADERFLAG(ISN3DS))
|
if(LOADERFLAG(ISN3DS))
|
||||||
@@ -442,140 +473,147 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
|
|
||||||
if(cpuSetting != 0)
|
if(cpuSetting != 0)
|
||||||
{
|
{
|
||||||
static const u8 cfgN3dsCpuPattern[] = {
|
static const u8 pattern[] = {
|
||||||
0x0C, 0x00, 0x94, 0x15
|
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);
|
if(off == NULL) res++;
|
||||||
|
else
|
||||||
//Patch N3DS CPU Clock and L2 cache setting
|
{
|
||||||
*(off - 4) = 0xE1A00000;
|
//Patch N3DS CPU Clock and L2 cache setting
|
||||||
*(off + 3) = 0xE3A00000 | cpuSetting;
|
*(off - 4) = *(off - 3);
|
||||||
|
*(off - 3) = *(off - 1);
|
||||||
|
memcpy(off - 1, off, 16);
|
||||||
|
*(off + 3) = 0xE3800000 | cpuSetting;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000001702LL) //CFG
|
else if(progId == 0x0004013000001702LL) //CFG
|
||||||
{
|
{
|
||||||
static const u8 secureinfoSigCheckPattern[] = {
|
static const u8 pattern[] = {
|
||||||
0x06, 0x46, 0x10, 0x48
|
0x06, 0x46, 0x10, 0x48
|
||||||
},
|
},
|
||||||
secureinfoSigCheckPatch[] = {
|
patch[] = {
|
||||||
0x00, 0x26
|
0x00, 0x26
|
||||||
};
|
};
|
||||||
|
|
||||||
//Disable SecureInfo signature check
|
//Disable SecureInfo signature check
|
||||||
patchMemory(code, size,
|
if(!patchMemory(code, size,
|
||||||
secureinfoSigCheckPattern,
|
pattern,
|
||||||
sizeof(secureinfoSigCheckPattern), 0,
|
sizeof(pattern), 0,
|
||||||
secureinfoSigCheckPatch,
|
patch,
|
||||||
sizeof(secureinfoSigCheckPatch), 1
|
sizeof(patch), 1
|
||||||
);
|
)) res++;
|
||||||
|
|
||||||
if(secureInfoExists())
|
if(secureInfoExists())
|
||||||
{
|
{
|
||||||
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_",
|
static const u16 pattern[] = u"Sec",
|
||||||
secureinfoFilenamePatch[] = u"C";
|
patch[] = u"C";
|
||||||
|
|
||||||
//Use SecureInfo_C
|
//Use SecureInfo_C
|
||||||
patchMemory(code, size,
|
if(patchMemory(code, size,
|
||||||
secureinfoFilenamePattern,
|
pattern,
|
||||||
sizeof(secureinfoFilenamePattern) - 2,
|
sizeof(pattern) - 2, 22,
|
||||||
sizeof(secureinfoFilenamePattern) - 2,
|
patch,
|
||||||
secureinfoFilenamePatch,
|
sizeof(patch) - 2, 2
|
||||||
sizeof(secureinfoFilenamePatch) - 2, 2
|
) != 2) res++;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000003702LL && progVer > 0) //RO
|
else if(progId == 0x0004013000003702LL && progVer > 0) //RO
|
||||||
{
|
{
|
||||||
static const u8 sigCheckPattern[] = {
|
static const u8 pattern[] = {
|
||||||
0x20, 0xA0, 0xE1, 0x8B
|
0x20, 0xA0, 0xE1, 0x8B
|
||||||
},
|
},
|
||||||
sha256ChecksPattern1[] = {
|
pattern2[] = {
|
||||||
0xE1, 0x30, 0x40, 0x2D
|
0xE1, 0x30, 0x40, 0x2D
|
||||||
},
|
},
|
||||||
sha256ChecksPattern2[] = {
|
pattern3[] = {
|
||||||
0x2D, 0xE9, 0x01, 0x70
|
0x2D, 0xE9, 0x01, 0x70
|
||||||
},
|
},
|
||||||
stub[] = {
|
patch[] = {
|
||||||
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
|
0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 //mov r0, #0; bx lr
|
||||||
};
|
};
|
||||||
|
|
||||||
//Disable CRR0 signature (RSA2048 with SHA256) check
|
//Disable CRR0 signature (RSA2048 with SHA256) check
|
||||||
patchMemory(code, size,
|
if(!patchMemory(code, size,
|
||||||
sigCheckPattern,
|
pattern,
|
||||||
sizeof(sigCheckPattern), -9,
|
sizeof(pattern), -9,
|
||||||
stub,
|
patch,
|
||||||
sizeof(stub), 1
|
sizeof(patch), 1
|
||||||
);
|
)) res++;
|
||||||
|
|
||||||
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||||
patchMemory(code, size,
|
if(!patchMemory(code, size,
|
||||||
sha256ChecksPattern1,
|
pattern2,
|
||||||
sizeof(sha256ChecksPattern1), 1,
|
sizeof(pattern2), 1,
|
||||||
stub,
|
patch,
|
||||||
sizeof(stub), 1
|
sizeof(patch), 1
|
||||||
);
|
)) res++;
|
||||||
|
|
||||||
patchMemory(code, size,
|
if(!patchMemory(code, size,
|
||||||
sha256ChecksPattern2,
|
pattern3,
|
||||||
sizeof(sha256ChecksPattern2), -2,
|
sizeof(pattern3), -2,
|
||||||
stub,
|
patch,
|
||||||
sizeof(stub), 1
|
sizeof(patch), 1
|
||||||
);
|
)) res++;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
|
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
|
||||||
{
|
{
|
||||||
static const u8 unitinfoCheckPattern1[] = {
|
static const u8 pattern[] = {
|
||||||
0x00, 0xD0, 0xE5, 0xDB
|
0x00, 0xD0, 0xE5, 0xDB
|
||||||
},
|
},
|
||||||
unitinfoCheckPattern2[] = {
|
pattern2[] = {
|
||||||
0x14, 0x00, 0xD0, 0xE5, 0x01
|
0x14, 0x00, 0xD0, 0xE5, 0x01
|
||||||
},
|
},
|
||||||
unitinfoCheckPatch[] = {
|
patch[] = {
|
||||||
0x00, 0x00, 0xA0, 0xE3
|
0x00, 0x00, 0xA0, 0xE3
|
||||||
};
|
};
|
||||||
|
|
||||||
patchMemory(code, size,
|
//Patch UNITINFO checks to make ErrDisp more verbose
|
||||||
unitinfoCheckPattern1,
|
if(!patchMemory(code, size,
|
||||||
sizeof(unitinfoCheckPattern1), -1,
|
pattern,
|
||||||
unitinfoCheckPatch,
|
sizeof(pattern), -1,
|
||||||
sizeof(unitinfoCheckPatch), 1
|
patch,
|
||||||
);
|
sizeof(patch), 1
|
||||||
|
)) res++;
|
||||||
|
|
||||||
patchMemory(code, size,
|
if(patchMemory(code, size,
|
||||||
unitinfoCheckPattern2,
|
pattern2,
|
||||||
sizeof(unitinfoCheckPattern2), 0,
|
sizeof(pattern2), 0,
|
||||||
unitinfoCheckPatch,
|
patch,
|
||||||
sizeof(unitinfoCheckPatch), 3
|
sizeof(patch), 3
|
||||||
);
|
) != 3) res++;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
||||||
{
|
{
|
||||||
//External .code section loading
|
//External .code section loading
|
||||||
loadTitleCodeSection(progId, code, size);
|
res += loadTitleCodeSection(progId, code, size);
|
||||||
|
|
||||||
//Language emulation
|
//Language emulation
|
||||||
u8 regionId = 0xFF,
|
u8 regionId = 0xFF,
|
||||||
languageId = 0xFF;
|
languageId;
|
||||||
loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
res += loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
||||||
|
|
||||||
if(regionId != 0xFF || regionId != 0xFF)
|
if(!res && regionId != 0xFF)
|
||||||
{
|
{
|
||||||
u32 CFGUHandleOffset;
|
u32 CFGUHandleOffset;
|
||||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &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);
|
res += patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
||||||
if(regionId != 0xFF) patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(res != 0) svcBreak(USERBREAK_ASSERT);
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
payload_addr equ 0x23F00000 ; Brahma payload address
|
payload_addr equ 0x23F00000 ; Brahma payload address
|
||||||
payload_maxsize equ 0x100000 ; Maximum size for the payload (maximum that CakeBrah supports)
|
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
|
.create "build/reboot.bin", 0
|
||||||
.arm
|
.arm
|
||||||
@@ -28,6 +27,8 @@ sd_notmounted equ 0xC8804465 ; Error code returned when SD is not mounted
|
|||||||
cmp r0, r2
|
cmp r0, r2
|
||||||
bne pxi_wait_recv
|
bne pxi_wait_recv
|
||||||
|
|
||||||
|
mov r4, #2
|
||||||
|
|
||||||
open_payload:
|
open_payload:
|
||||||
; Open file
|
; Open file
|
||||||
add r0, r7, #8
|
add r0, r7, #8
|
||||||
@@ -38,9 +39,8 @@ sd_notmounted equ 0xC8804465 ; Error code returned when SD is not mounted
|
|||||||
blx r6
|
blx r6
|
||||||
cmp r0, #0
|
cmp r0, #0
|
||||||
beq read_payload
|
beq read_payload
|
||||||
ldr r2, =sd_notmounted
|
subs r4, r4, #1
|
||||||
cmp r0, r2
|
beq panic
|
||||||
bne panic
|
|
||||||
adr r0, fname
|
adr r0, fname
|
||||||
adr r1, nand_mount
|
adr r1, nand_mount
|
||||||
mov r2, #8
|
mov r2, #8
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
"(refer to the wiki for instructions).",
|
"(refer to the wiki for instructions).",
|
||||||
|
|
||||||
"Select the New 3DS CPU mode.\n\n"
|
"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"
|
"'Clock+L2' can cause issues with some\n"
|
||||||
"games.",
|
"games.",
|
||||||
|
|
||||||
@@ -126,7 +127,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
"always detected as a development unit\n"
|
"always detected as a development unit\n"
|
||||||
"(which breaks online features and\n"
|
"(which breaks online features and\n"
|
||||||
"allows booting some developer\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"
|
"Only change this if you know what you\n"
|
||||||
"are doing!",
|
"are doing!",
|
||||||
|
|
||||||
@@ -373,7 +374,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Preserve the last-used boot options (first 9 bits)
|
//Preserve the last-used boot options (first 9 bits)
|
||||||
configData.config &= 0x1FF;
|
configData.config &= 0xFF;
|
||||||
|
|
||||||
//Parse and write the new configuration
|
//Parse and write the new configuration
|
||||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
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 u8 nandSlot;
|
||||||
static u32 fatStart;
|
static u32 fatStart;
|
||||||
static u8 __attribute__((aligned(4))) shaHashBackup[SHA_256_HASH_SIZE];
|
|
||||||
static bool didShaHashBackup = false;
|
static bool didShaHashBackup = false;
|
||||||
|
|
||||||
FirmwareSource firmSource;
|
FirmwareSource firmSource;
|
||||||
|
|
||||||
void ctrNandInit(void)
|
void ctrNandInit(void)
|
||||||
{
|
{
|
||||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
__attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
|
||||||
u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE];
|
shaSum[SHA_256_HASH_SIZE];
|
||||||
|
|
||||||
sdmmc_get_cid(1, (u32 *)cid);
|
sdmmc_get_cid(1, (u32 *)cid);
|
||||||
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
||||||
@@ -318,7 +318,7 @@ void ctrNandInit(void)
|
|||||||
|
|
||||||
if(ISN3DS)
|
if(ISN3DS)
|
||||||
{
|
{
|
||||||
u8 __attribute__((aligned(4))) keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
|
__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);
|
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
nandSlot = 0x05;
|
nandSlot = 0x05;
|
||||||
@@ -333,7 +333,7 @@ void ctrNandInit(void)
|
|||||||
|
|
||||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
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));
|
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||||
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
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;
|
u8 *buffer = (u8 *)0x23000000;
|
||||||
u32 bufferSize = 0xF00000;
|
u32 bufferSize = 0xF00000;
|
||||||
|
|
||||||
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
|
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||||
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
aes_use_keyslot(nandSlot);
|
aes_use_keyslot(nandSlot);
|
||||||
@@ -383,15 +383,21 @@ int ctrNandWrite(u32 sector, u32 sectorCount, const u8 *inbuf)
|
|||||||
|
|
||||||
void set6x7xKeys(void)
|
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};
|
__attribute__((aligned(4))) const u8 keyX0x25s[2][AES_BLOCK_SIZE] = {
|
||||||
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};
|
{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(0x25, keyX0x25s[ISDEVUNIT ? 1 : 0], AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
aes_setkey(0x2F, keyY0x2F, AES_KEYY, 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,
|
/* [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.
|
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. */
|
Otherwise when this block was already all-zero, it immediately returns. */
|
||||||
memset32((void *)0x01FFCD00, 0, 0x10);
|
memset32((void *)0x01FFCD00, 0, 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +411,7 @@ bool decryptExeFs(Cxi *cxi)
|
|||||||
|
|
||||||
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||||
u32 exeFsSize = (cxi->ncch.exeFsSize - 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++)
|
for(u32 i = 0; i < 8; i++)
|
||||||
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||||
@@ -429,9 +435,9 @@ bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
|||||||
{
|
{
|
||||||
isTicket = true;
|
isTicket = true;
|
||||||
|
|
||||||
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))) 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];
|
__attribute__((aligned(4))) u8 titleKey[AES_BLOCK_SIZE],
|
||||||
u8 __attribute__((aligned(4))) cetkIv[AES_BLOCK_SIZE] = {0};
|
cetkIv[AES_BLOCK_SIZE] = {0};
|
||||||
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
|
memcpy(titleKey, ticket->titleKey, sizeof(titleKey));
|
||||||
memcpy(cetkIv, ticket->titleId, sizeof(ticket->titleId));
|
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_use_keyslot(0x3D);
|
||||||
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes(titleKey, titleKey, 1, cetkIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
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_setkey(0x16, titleKey, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
aes_use_keyslot(0x16);
|
aes_use_keyslot(0x16);
|
||||||
@@ -470,12 +476,19 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
|||||||
u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
|
u32 *startOfArm9Bin = (u32 *)((u8 *)arm9Section + 0x800);
|
||||||
bool needToDecrypt = *startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000;
|
bool needToDecrypt = *startOfArm9Bin != 0x47704770 && *startOfArm9Bin != 0xB0862000;
|
||||||
|
|
||||||
if(!ISDEVUNIT && (k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt)))
|
if(k9lVersion == 2 || (k9lVersion == 1 && needToDecrypt))
|
||||||
{
|
{
|
||||||
//Set 0x11 keyslot
|
//Set 0x11 keyslot
|
||||||
const u8 __attribute__((aligned(4))) key1[AES_BLOCK_SIZE] = {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8};
|
__attribute__((aligned(4))) const u8 key1s[2][AES_BLOCK_SIZE] = {
|
||||||
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};
|
{0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8},
|
||||||
aes_setkey(0x11, k9lVersion == 2 ? key2 : key1, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
{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)
|
if(needToDecrypt)
|
||||||
@@ -488,19 +501,19 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
|||||||
arm9BinSlot = 0x16;
|
arm9BinSlot = 0x16;
|
||||||
|
|
||||||
//Set keyX
|
//Set keyX
|
||||||
u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE];
|
__attribute__((aligned(4))) u8 keyX[AES_BLOCK_SIZE];
|
||||||
aes_use_keyslot(0x11);
|
aes_use_keyslot(0x11);
|
||||||
aes(keyX, arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
aes(keyX, arm9Section->slot0x16keyX, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||||
aes_setkey(0x16, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(0x16, keyX, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set keyY
|
//Set keyY
|
||||||
u8 __attribute__((aligned(4))) keyY[AES_BLOCK_SIZE];
|
__attribute__((aligned(4))) u8 keyY[AES_BLOCK_SIZE];
|
||||||
memcpy(keyY, arm9Section->keyY, sizeof(keyY));
|
memcpy(keyY, arm9Section->keyY, sizeof(keyY));
|
||||||
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(arm9BinSlot, keyY, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
//Set CTR
|
//Set CTR
|
||||||
u8 __attribute__((aligned(4))) arm9BinCtr[AES_BLOCK_SIZE];
|
__attribute__((aligned(4))) u8 arm9BinCtr[AES_BLOCK_SIZE];
|
||||||
memcpy(arm9BinCtr, arm9Section->ctr, sizeof(arm9BinCtr));
|
memcpy(arm9BinCtr, arm9Section->ctr, sizeof(arm9BinCtr));
|
||||||
|
|
||||||
//Decrypt ARM9 binary
|
//Decrypt ARM9 binary
|
||||||
@@ -513,8 +526,8 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
|||||||
//Set >=9.6 KeyXs
|
//Set >=9.6 KeyXs
|
||||||
if(k9lVersion == 2)
|
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};
|
__attribute__((aligned(4))) u8 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)];
|
decKey[sizeof(keyData)];
|
||||||
|
|
||||||
//Set keys 0x19..0x1F keyXs
|
//Set keys 0x19..0x1F keyXs
|
||||||
aes_use_keyslot(0x11);
|
aes_use_keyslot(0x11);
|
||||||
@@ -528,11 +541,11 @@ void kernel9Loader(Arm9Bin *arm9Section)
|
|||||||
|
|
||||||
void computePinHash(u8 *outbuf, const u8 *inbuf)
|
void computePinHash(u8 *outbuf, const u8 *inbuf)
|
||||||
{
|
{
|
||||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
__attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
|
||||||
u8 __attribute__((aligned(4))) cipherText[AES_BLOCK_SIZE];
|
cipherText[AES_BLOCK_SIZE];
|
||||||
|
|
||||||
sdmmc_get_cid(1, (u32 *)cid);
|
sdmmc_get_cid(1, (u32 *)cid);
|
||||||
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
aes_use_keyslot(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);
|
aes(cipherText, inbuf, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
|
sha(outbuf, cipherText, sizeof(cipherText), SHA_256_MODE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffse
|
|||||||
u32 *endPos = exceptionsPage + 0x400;
|
u32 *endPos = exceptionsPage + 0x400;
|
||||||
|
|
||||||
u32 *initFPU;
|
u32 *initFPU;
|
||||||
for(initFPU = exceptionsPage; *initFPU != 0xE1A0D002 && initFPU < endPos; initFPU++);
|
for(initFPU = exceptionsPage; initFPU < endPos && *initFPU != 0xE1A0D002; initFPU++);
|
||||||
|
|
||||||
u32 *freeSpace;
|
u32 *freeSpace;
|
||||||
for(freeSpace = initFPU; *freeSpace != 0xFFFFFFFF && freeSpace < endPos; freeSpace++);
|
for(freeSpace = initFPU; freeSpace < endPos && *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||||
|
|
||||||
u32 *mcuReboot;
|
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;
|
if(initFPU == endPos || freeSpace == endPos || mcuReboot == endPos || *(u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 36) != 0xFFFFFFFF) ret = 1;
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStora
|
|||||||
|
|
||||||
bool mustLoadFromStorage = false;
|
bool mustLoadFromStorage = false;
|
||||||
|
|
||||||
if(!ISN3DS && *firmType == NATIVE_FIRM)
|
if(!ISN3DS && *firmType == NATIVE_FIRM && !ISDEVUNIT)
|
||||||
{
|
{
|
||||||
if(firmVersion < 0x18)
|
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(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(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
|
||||||
|
if(ISDEVUNIT) firmVersion = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return firmVersion;
|
return firmVersion;
|
||||||
@@ -121,8 +122,8 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
|||||||
firm->arm9Entry = (u8 *)0x801B01C;
|
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
|
//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 && !ISDEVUNIT) set6x7xKeys();
|
else if(!ISA9LH && !ISFIRMLAUNCH && firmVersion >= 0x29) set6x7xKeys();
|
||||||
|
|
||||||
//Find the Process9 .code location, size and memory address
|
//Find the Process9 .code location, size and memory address
|
||||||
u32 process9Size,
|
u32 process9Size,
|
||||||
@@ -145,12 +146,19 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, boo
|
|||||||
//Apply EmuNAND patches
|
//Apply EmuNAND patches
|
||||||
if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, emuHeader, firm->section[2].address);
|
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);
|
else if(isA9lhInstalled) ret += patchFirmWrites(process9Offset, process9Size);
|
||||||
|
|
||||||
//Apply firmlaunch patches
|
//Apply firmlaunch patches
|
||||||
ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr);
|
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
|
//11.0 FIRM patches
|
||||||
if(firmVersion >= (ISN3DS ? 0x21 : 0x52))
|
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);
|
ret += implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space, isSafeMode);
|
||||||
|
|
||||||
//Apply UNITINFO patch
|
//Apply UNITINFO patches
|
||||||
if(devMode == 2) ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
if(devMode == 2)
|
||||||
|
{
|
||||||
|
ret += patchUnitInfoValueSet(arm9Section, kernel9Size);
|
||||||
|
if(!ISDEVUNIT) ret += patchCheckForDevCommonKey(process9Offset, process9Size);
|
||||||
|
}
|
||||||
|
|
||||||
if(devMode != 0 && isA9lhInstalled)
|
if(devMode != 0 && isA9lhInstalled)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -185,7 +185,8 @@ u32 firmRead(void *dest, u32 firmType)
|
|||||||
const char *firmFolders[][2] = {{ "00000002", "20000002" },
|
const char *firmFolders[][2] = {{ "00000002", "20000002" },
|
||||||
{ "00000102", "20000102" },
|
{ "00000102", "20000102" },
|
||||||
{ "00000202", "20000202" },
|
{ "00000202", "20000202" },
|
||||||
{ "00000003", "20000003" }};
|
{ "00000003", "20000003" },
|
||||||
|
{ "00000001", "20000001" }};
|
||||||
|
|
||||||
char path[48] = "1:/title/00040138/";
|
char path[48] = "1:/title/00040138/";
|
||||||
concatenateStrings(path, firmFolders[firmType][ISN3DS ? 1 : 0]);
|
concatenateStrings(path, firmFolders[firmType][ISN3DS ? 1 : 0]);
|
||||||
|
|||||||
@@ -62,8 +62,18 @@ void main(void)
|
|||||||
{
|
{
|
||||||
if(needConfig == CREATE_CONFIGURATION) mcuPowerOff();
|
if(needConfig == CREATE_CONFIGURATION) mcuPowerOff();
|
||||||
|
|
||||||
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
switch(launchedFirmTidLow[7])
|
||||||
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
|
{
|
||||||
|
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;
|
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||||
@@ -238,16 +248,17 @@ void main(void)
|
|||||||
case NATIVE_FIRM:
|
case NATIVE_FIRM:
|
||||||
res = patchNativeFirm(firmVersion, nandType, emuHeader, isA9lhInstalled, isSafeMode, devMode);
|
res = patchNativeFirm(firmVersion, nandType, emuHeader, isA9lhInstalled, isSafeMode, devMode);
|
||||||
break;
|
break;
|
||||||
case SAFE_FIRM:
|
|
||||||
case NATIVE_FIRM1X2X:
|
|
||||||
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
|
|
||||||
break;
|
|
||||||
case TWL_FIRM:
|
case TWL_FIRM:
|
||||||
res = patchTwlFirm(firmVersion, devMode);
|
res = patchTwlFirm(firmVersion, devMode);
|
||||||
break;
|
break;
|
||||||
case AGB_FIRM:
|
case AGB_FIRM:
|
||||||
res = patchAgbFirm(devMode);
|
res = patchAgbFirm(devMode);
|
||||||
break;
|
break;
|
||||||
|
case SAFE_FIRM:
|
||||||
|
case SYSUPDATER_FIRM:
|
||||||
|
case NATIVE_FIRM1X2X:
|
||||||
|
res = isA9lhInstalled ? patch1x2xNativeAndSafeFirm(devMode) : 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(res != 0)
|
if(res != 0)
|
||||||
|
|||||||
@@ -206,6 +206,84 @@ u32 patchOldFirmWrites(u8 *pos, u32 size)
|
|||||||
return ret;
|
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 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||||
{
|
{
|
||||||
u32 ret = 0;
|
u32 ret = 0;
|
||||||
@@ -291,26 +369,6 @@ u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **free
|
|||||||
return ret;
|
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)
|
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
|
const u8 pattern[] = {0x80, 0xE5, 0x40, 0x1C};
|
||||||
|
|||||||
@@ -37,10 +37,13 @@ extern CfgData configData;
|
|||||||
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
|
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
|
||||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
|
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
|
||||||
u32 patchSignatureChecks(u8 *pos, u32 size);
|
u32 patchSignatureChecks(u8 *pos, u32 size);
|
||||||
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion);
|
|
||||||
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||||
u32 patchFirmWrites(u8 *pos, u32 size);
|
u32 patchFirmWrites(u8 *pos, u32 size);
|
||||||
u32 patchOldFirmWrites(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 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||||
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode);
|
u32 implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space, bool isSafeMode);
|
||||||
u32 patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
|
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);
|
drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||||
|
|
||||||
//Pad to AES block length with zeroes
|
//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;
|
u8 cnt = 0;
|
||||||
u32 charDrawPos = 16 * SPACING_X;
|
u32 charDrawPos = 16 * SPACING_X;
|
||||||
@@ -90,8 +90,8 @@ void newPin(bool allowSkipping, u32 pinMode)
|
|||||||
pin.formatVersionMajor = PIN_VERSIONMAJOR;
|
pin.formatVersionMajor = PIN_VERSIONMAJOR;
|
||||||
pin.formatVersionMinor = PIN_VERSIONMINOR;
|
pin.formatVersionMinor = PIN_VERSIONMINOR;
|
||||||
|
|
||||||
u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE];
|
__attribute__((aligned(4))) u8 tmp[SHA_256_HASH_SIZE],
|
||||||
u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0};
|
lengthBlock[AES_BLOCK_SIZE] = {0};
|
||||||
lengthBlock[0] = length;
|
lengthBlock[0] = length;
|
||||||
|
|
||||||
computePinHash(tmp, lengthBlock);
|
computePinHash(tmp, lengthBlock);
|
||||||
@@ -114,8 +114,8 @@ bool verifyPin(u32 pinMode)
|
|||||||
pin.formatVersionMinor != PIN_VERSIONMINOR)
|
pin.formatVersionMinor != PIN_VERSIONMINOR)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
u8 __attribute__((aligned(4))) tmp[SHA_256_HASH_SIZE];
|
__attribute__((aligned(4))) u8 tmp[SHA_256_HASH_SIZE],
|
||||||
u8 __attribute__((aligned(4))) lengthBlock[AES_BLOCK_SIZE] = {0};
|
lengthBlock[AES_BLOCK_SIZE] = {0};
|
||||||
lengthBlock[0] = 4 + 2 * (pinMode - 1);
|
lengthBlock[0] = 4 + 2 * (pinMode - 1);
|
||||||
|
|
||||||
computePinHash(tmp, lengthBlock);
|
computePinHash(tmp, lengthBlock);
|
||||||
@@ -126,7 +126,7 @@ bool verifyPin(u32 pinMode)
|
|||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
//Pad to AES block length with zeroes
|
//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;
|
bool unlock = false;
|
||||||
u8 cnt = 0;
|
u8 cnt = 0;
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ typedef enum FirmwareType
|
|||||||
TWL_FIRM,
|
TWL_FIRM,
|
||||||
AGB_FIRM,
|
AGB_FIRM,
|
||||||
SAFE_FIRM,
|
SAFE_FIRM,
|
||||||
|
SYSUPDATER_FIRM,
|
||||||
NATIVE_FIRM1X2X
|
NATIVE_FIRM1X2X
|
||||||
} FirmwareType;
|
} FirmwareType;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user