Refactor the codebase to limit nested if/elses
This commit is contained in:
parent
141c7817a0
commit
9332b9eb33
@ -51,30 +51,28 @@ static inline void loadCFWInfo(void)
|
|||||||
{
|
{
|
||||||
static bool infoLoaded = false;
|
static bool infoLoaded = false;
|
||||||
|
|
||||||
if(!infoLoaded)
|
if(infoLoaded) return;
|
||||||
{
|
|
||||||
svcGetCFWInfo(&info);
|
|
||||||
|
|
||||||
IFile file;
|
svcGetCFWInfo(&info);
|
||||||
if(LOADERFLAG(ISSAFEMODE) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
|
|
||||||
IFile_Close(&file);
|
|
||||||
|
|
||||||
infoLoaded = true;
|
IFile file;
|
||||||
}
|
if(LOADERFLAG(ISSAFEMODE) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
|
||||||
|
IFile_Close(&file);
|
||||||
|
|
||||||
|
infoLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool secureInfoExists(void)
|
static inline bool secureInfoExists(void)
|
||||||
{
|
{
|
||||||
static bool exists = false;
|
static bool exists = false;
|
||||||
|
|
||||||
if(!exists)
|
if(exists) return true;
|
||||||
|
|
||||||
|
IFile file;
|
||||||
|
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ)))
|
||||||
{
|
{
|
||||||
IFile file;
|
exists = true;
|
||||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ)))
|
IFile_Close(&file);
|
||||||
{
|
|
||||||
exists = true;
|
|
||||||
IFile_Close(&file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return exists;
|
return exists;
|
||||||
@ -90,52 +88,50 @@ static inline void loadCustomVerString(u16 *out, u32 *verStringSize, u32 current
|
|||||||
|
|
||||||
IFile file;
|
IFile file;
|
||||||
|
|
||||||
if(R_SUCCEEDED(openLumaFile(&file, paths[currentNand])))
|
if(R_FAILED(openLumaFile(&file, paths[currentNand]))) return;
|
||||||
|
|
||||||
|
u64 fileSize;
|
||||||
|
|
||||||
|
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > 62) goto exit;
|
||||||
|
|
||||||
|
u8 buf[62];
|
||||||
|
u64 total;
|
||||||
|
|
||||||
|
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) goto exit;
|
||||||
|
|
||||||
|
static const u8 bom[] = {0xEF, 0xBB, 0xBF};
|
||||||
|
u32 finalSize = 0;
|
||||||
|
|
||||||
|
//Convert from UTF-8 to UTF-16 (Nintendo doesn't support 4-byte UTF-16, so 4-byte UTF-8 is unsupported)
|
||||||
|
for(u32 increase, fileSizeTmp = (u32)fileSize, i = (fileSizeTmp > 2 && memcmp(buf, bom, 3) == 0) ? 3 : 0;
|
||||||
|
i < fileSizeTmp && finalSize < 19; i += increase, finalSize++)
|
||||||
{
|
{
|
||||||
u64 fileSize;
|
if((buf[i] & 0x80) == 0 && !(buf[i] == 0xA || buf[i] == 0xD))
|
||||||
|
|
||||||
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= 62)
|
|
||||||
{
|
{
|
||||||
u8 buf[fileSize];
|
increase = 1;
|
||||||
u64 total;
|
out[finalSize] = (u16)buf[i];
|
||||||
|
|
||||||
if(R_SUCCEEDED(IFile_Read(&file, &total, buf, fileSize)))
|
|
||||||
{
|
|
||||||
static const u8 bom[] = {0xEF, 0xBB, 0xBF};
|
|
||||||
u32 finalSize = 0;
|
|
||||||
|
|
||||||
//Convert from UTF-8 to UTF-16 (Nintendo doesn't support 4-byte UTF-16, so 4-byte UTF-8 is unsupported)
|
|
||||||
for(u32 increase, fileSizeTmp = (u32)fileSize, i = (fileSizeTmp > 2 && memcmp(buf, bom, 3) == 0) ? 3 : 0;
|
|
||||||
i < fileSizeTmp && finalSize < 19; i += increase, finalSize++)
|
|
||||||
{
|
|
||||||
if((buf[i] & 0x80) == 0 && !(buf[i] == 0xA || buf[i] == 0xD))
|
|
||||||
{
|
|
||||||
increase = 1;
|
|
||||||
out[finalSize] = (u16)buf[i];
|
|
||||||
}
|
|
||||||
else if((buf[i] & 0xE0) == 0xC0 && i + 1 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80)
|
|
||||||
{
|
|
||||||
increase = 2;
|
|
||||||
out[finalSize] = (u16)(((buf[i] & 0x1F) << 6) | (buf[i + 1] & 0x3F));
|
|
||||||
}
|
|
||||||
else if((buf[i] & 0xF0) == 0xE0 && i + 2 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80 && (buf[i + 2] & 0xC0) == 0x80)
|
|
||||||
{
|
|
||||||
increase = 3;
|
|
||||||
out[finalSize] = (u16)(((buf[i] & 0xF) << 12) | ((buf[i + 1] & 0x3F) << 6) | (buf[i + 2] & 0x3F));
|
|
||||||
}
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(finalSize > 0)
|
|
||||||
{
|
|
||||||
if(finalSize > 5 && finalSize < 19) out[finalSize++] = 0;
|
|
||||||
*verStringSize = finalSize * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if((buf[i] & 0xE0) == 0xC0 && i + 1 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80)
|
||||||
IFile_Close(&file);
|
{
|
||||||
|
increase = 2;
|
||||||
|
out[finalSize] = (u16)(((buf[i] & 0x1F) << 6) | (buf[i + 1] & 0x3F));
|
||||||
|
}
|
||||||
|
else if((buf[i] & 0xF0) == 0xE0 && i + 2 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80 && (buf[i + 2] & 0xC0) == 0x80)
|
||||||
|
{
|
||||||
|
increase = 3;
|
||||||
|
out[finalSize] = (u16)(((buf[i] & 0xF) << 12) | ((buf[i + 1] & 0x3F) << 6) | (buf[i + 2] & 0x3F));
|
||||||
|
}
|
||||||
|
else break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(finalSize > 0)
|
||||||
|
{
|
||||||
|
if(finalSize > 5 && finalSize < 19) out[finalSize++] = 0;
|
||||||
|
*verStringSize = finalSize * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
IFile_Close(&file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
static inline u32 loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||||
@ -147,23 +143,23 @@ static inline u32 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_FAILED(openLumaFile(&file, path))) return 0;
|
||||||
|
|
||||||
|
u32 ret;
|
||||||
|
u64 fileSize;
|
||||||
|
|
||||||
|
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = 1;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
u64 fileSize;
|
u64 total;
|
||||||
|
|
||||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize > size) ret = 1;
|
if(R_FAILED(IFile_Read(&file, &total, code, fileSize)) || total != fileSize) ret = 1;
|
||||||
else
|
else ret = 0;
|
||||||
{
|
|
||||||
u64 total;
|
|
||||||
|
|
||||||
if(R_FAILED(IFile_Read(&file, &total, code, fileSize)) || total != fileSize) ret = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
IFile_Close(&file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IFile_Close(&file);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,53 +172,58 @@ static inline u32 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_FAILED(openLumaFile(&file, path))) return 0;
|
||||||
|
|
||||||
|
u32 ret;
|
||||||
|
u64 fileSize;
|
||||||
|
|
||||||
|
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8)
|
||||||
{
|
{
|
||||||
u64 fileSize;
|
ret = 1;
|
||||||
|
goto exit;
|
||||||
if(R_FAILED(IFile_GetSize(&file, &fileSize)) || fileSize < 6 || fileSize > 8) ret = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char buf[fileSize];
|
|
||||||
u64 total;
|
|
||||||
|
|
||||||
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize))) ret = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 i,
|
|
||||||
j;
|
|
||||||
|
|
||||||
for(i = 0; i < 7; i++)
|
|
||||||
{
|
|
||||||
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
|
|
||||||
|
|
||||||
if(memcmp(buf, regions[i], 3) == 0)
|
|
||||||
{
|
|
||||||
*regionId = (u8)i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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[j], 2) == 0)
|
|
||||||
{
|
|
||||||
*languageId = (u8)j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i == 7 || j == 12) ret = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IFile_Close(&file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char buf[8];
|
||||||
|
u64 total;
|
||||||
|
|
||||||
|
if(R_FAILED(IFile_Read(&file, &total, buf, fileSize)))
|
||||||
|
{
|
||||||
|
ret = 1;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 i,
|
||||||
|
j;
|
||||||
|
|
||||||
|
for(i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
|
||||||
|
|
||||||
|
if(memcmp(buf, regions[i], 3) == 0)
|
||||||
|
{
|
||||||
|
*regionId = (u8)i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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[j], 2) == 0)
|
||||||
|
{
|
||||||
|
*languageId = (u8)j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i == 7 || j == 12) ret = 1;
|
||||||
|
else ret = 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
IFile_Close(&file);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,32 +239,29 @@ static inline u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
|||||||
|
|
||||||
for(u8 *pos = code + 4; n < 24 && pos < code + size - 4; pos += 4)
|
for(u8 *pos = code + 4; n < 24 && pos < code + size - 4; pos += 4)
|
||||||
{
|
{
|
||||||
if(*(u32 *)pos == 0xD8A103F9)
|
if(*(u32 *)pos != 0xD8A103F9) continue;
|
||||||
{
|
|
||||||
for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
|
for(u32 *l = (u32 *)pos - 4; n < 24 && l < (u32 *)pos + 4; l++)
|
||||||
if(*l <= 0x10000000) possible[n++] = *l;
|
if(*l <= 0x10000000) possible[n++] = *l;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(n > 0)
|
if(!n) return NULL;
|
||||||
|
|
||||||
|
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
|
||||||
{
|
{
|
||||||
for(u8 *CFGU_GetConfigInfoBlk2_endPos = code; CFGU_GetConfigInfoBlk2_endPos < code + size - 8; CFGU_GetConfigInfoBlk2_endPos += 4)
|
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
||||||
{
|
|
||||||
static const u32 CFGU_GetConfigInfoBlk2_endPattern[] = {0xE8BD8010, 0x00010082};
|
|
||||||
|
|
||||||
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
//There might be multiple implementations of GetConfigInfoBlk2 but let's search for the one we want
|
||||||
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
u32 *cmp = (u32 *)CFGU_GetConfigInfoBlk2_endPos;
|
||||||
|
|
||||||
if(cmp[0] == CFGU_GetConfigInfoBlk2_endPattern[0] && cmp[1] == CFGU_GetConfigInfoBlk2_endPattern[1])
|
if(cmp[0] != CFGU_GetConfigInfoBlk2_endPattern[0] || cmp[1] != CFGU_GetConfigInfoBlk2_endPattern[1]) continue;
|
||||||
{
|
|
||||||
*CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
|
|
||||||
|
|
||||||
for(u32 i = 0; i < n; i++)
|
*CFGUHandleOffset = *((u32 *)CFGU_GetConfigInfoBlk2_endPos + 2);
|
||||||
if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
|
|
||||||
|
|
||||||
CFGU_GetConfigInfoBlk2_endPos += 4;
|
for(u32 i = 0; i < n; i++)
|
||||||
}
|
if(possible[i] == *CFGUHandleOffset) return CFGU_GetConfigInfoBlk2_endPos;
|
||||||
}
|
|
||||||
|
CFGU_GetConfigInfoBlk2_endPos += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -277,45 +275,41 @@ static inline u32 patchCfgGetLanguage(u8 *code, u32 size, u8 languageId, u8 *CFG
|
|||||||
CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
|
CFGU_GetConfigInfoBlk2_startPos >= code && *((u16 *)CFGU_GetConfigInfoBlk2_startPos + 1) != 0xE92D;
|
||||||
CFGU_GetConfigInfoBlk2_startPos -= 4);
|
CFGU_GetConfigInfoBlk2_startPos -= 4);
|
||||||
|
|
||||||
if(CFGU_GetConfigInfoBlk2_startPos >= code)
|
if(CFGU_GetConfigInfoBlk2_startPos < code) return 1;
|
||||||
|
|
||||||
|
for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
|
||||||
{
|
{
|
||||||
for(u8 *languageBlkIdPos = code; languageBlkIdPos < code + size; languageBlkIdPos += 4)
|
if(*(u32 *)languageBlkIdPos != 0xA0002) continue;
|
||||||
|
|
||||||
|
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
||||||
{
|
{
|
||||||
if(*(u32 *)languageBlkIdPos == 0xA0002)
|
if(instr[3] != 0xEB) continue; //We're looking for BL
|
||||||
|
|
||||||
|
u8 *calledFunction = instr;
|
||||||
|
u32 i = 0;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
do
|
||||||
{
|
{
|
||||||
for(u8 *instr = languageBlkIdPos - 8; instr >= languageBlkIdPos - 0x1008 && instr >= code + 4; instr -= 4) //Should be enough
|
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
|
||||||
|
|
||||||
|
calledFunction += offset;
|
||||||
|
|
||||||
|
if(calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos)
|
||||||
{
|
{
|
||||||
if(instr[3] == 0xEB) //We're looking for BL
|
*((u32 *)instr - 1) = 0xE3A00000 | languageId; //mov r0, sp => mov r0, =languageId
|
||||||
{
|
*(u32 *)instr = 0xE5CD0000; //bl CFGU_GetConfigInfoBlk2 => strb r0, [sp]
|
||||||
u8 *calledFunction = instr;
|
*((u32 *)instr + 1) = 0xE3B00000; //(1 or 2 instructions) => movs r0, 0 (result code)
|
||||||
u32 i = 0;
|
|
||||||
bool found;
|
|
||||||
|
|
||||||
do
|
//We're done
|
||||||
{
|
return 0;
|
||||||
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
|
|
||||||
|
|
||||||
calledFunction += offset;
|
|
||||||
|
|
||||||
found = calledFunction >= CFGU_GetConfigInfoBlk2_startPos - 4 && calledFunction <= CFGU_GetConfigInfoBlk2_endPos;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
while(i < 2 && !found && calledFunction[3] == 0xEA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,20 +324,19 @@ static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHa
|
|||||||
|
|
||||||
u32 *cmp = (u32 *)cmdPos;
|
u32 *cmp = (u32 *)cmdPos;
|
||||||
|
|
||||||
if(*cmp == cfgSecureInfoGetRegionCmdPattern[1])
|
if(*cmp != cfgSecureInfoGetRegionCmdPattern[1]) continue;
|
||||||
{
|
|
||||||
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
|
for(u32 i = 1; i < 3; i++)
|
||||||
return;
|
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
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,7 +344,6 @@ static inline void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHa
|
|||||||
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
|
||||||
@ -375,7 +367,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
sizeof(pattern), -31,
|
sizeof(pattern), -31,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch), 1
|
sizeof(patch), 1
|
||||||
)) res++;
|
)) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000003202LL) //FRIENDS
|
else if(progId == 0x0004013000003202LL) //FRIENDS
|
||||||
@ -388,10 +380,10 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
|
|
||||||
u8 *off = memsearch(code, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(code, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) res++;
|
if(off == NULL) goto error;
|
||||||
|
|
||||||
//Allow online access to work with old friends modules
|
//Allow online access to work with old friends modules
|
||||||
else if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
if(off[0xA] < mostRecentFpdVer) off[0xA] = mostRecentFpdVer;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if((progId == 0x0004001000021000LL || //USA MSET
|
else if((progId == 0x0004001000021000LL || //USA MSET
|
||||||
@ -442,7 +434,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
sizeof(pattern) - 2, 0,
|
sizeof(pattern) - 2, 0,
|
||||||
patch,
|
patch,
|
||||||
patchSize, 1
|
patchSize, 1
|
||||||
)) res++;
|
)) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004013000008002LL) //NS
|
else if(progId == 0x0004013000008002LL) //NS
|
||||||
@ -464,7 +456,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
sizeof(patch), 2
|
sizeof(patch), 2
|
||||||
);
|
);
|
||||||
|
|
||||||
if(ret == 0 || (ret == 1 && progVer > 0xB)) res++;
|
if(ret == 0 || (ret == 1 && progVer > 0xB)) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(LOADERFLAG(ISN3DS))
|
if(LOADERFLAG(ISN3DS))
|
||||||
@ -479,15 +471,13 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
|
|
||||||
u32 *off = (u32 *)memsearch(code, pattern, size, sizeof(pattern));
|
u32 *off = (u32 *)memsearch(code, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) res++;
|
if(off == NULL) goto error;
|
||||||
else
|
|
||||||
{
|
//Patch N3DS CPU Clock and L2 cache setting
|
||||||
//Patch N3DS CPU Clock and L2 cache setting
|
*(off - 4) = *(off - 3);
|
||||||
*(off - 4) = *(off - 3);
|
*(off - 3) = *(off - 1);
|
||||||
*(off - 3) = *(off - 1);
|
memcpy(off - 1, off, 16);
|
||||||
memcpy(off - 1, off, 16);
|
*(off + 3) = 0xE3800000 | cpuSetting;
|
||||||
*(off + 3) = 0xE3800000 | cpuSetting;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -507,7 +497,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
sizeof(pattern), 0,
|
sizeof(pattern), 0,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch), 1
|
sizeof(patch), 1
|
||||||
)) res++;
|
)) goto error;
|
||||||
|
|
||||||
if(secureInfoExists())
|
if(secureInfoExists())
|
||||||
{
|
{
|
||||||
@ -520,7 +510,7 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
sizeof(pattern) - 2, 22,
|
sizeof(pattern) - 2, 22,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch) - 2, 2
|
sizeof(patch) - 2, 2
|
||||||
) != 2) res++;
|
) != 2) goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,28 +529,25 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
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 and CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
||||||
if(!patchMemory(code, size,
|
if(!patchMemory(code, size,
|
||||||
pattern,
|
pattern,
|
||||||
sizeof(pattern), -9,
|
sizeof(pattern), -9,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch), 1
|
sizeof(patch), 1
|
||||||
)) res++;
|
) ||
|
||||||
|
!patchMemory(code, size,
|
||||||
//Disable CRO0/CRR0 SHA256 hash checks (section hashes, and hash table)
|
|
||||||
if(!patchMemory(code, size,
|
|
||||||
pattern2,
|
pattern2,
|
||||||
sizeof(pattern2), 1,
|
sizeof(pattern2), 1,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch), 1
|
sizeof(patch), 1
|
||||||
)) res++;
|
) ||
|
||||||
|
!patchMemory(code, size,
|
||||||
if(!patchMemory(code, size,
|
|
||||||
pattern3,
|
pattern3,
|
||||||
sizeof(pattern3), -2,
|
sizeof(pattern3), -2,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch), 1
|
sizeof(patch), 1
|
||||||
)) res++;
|
)) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
|
else if(progId == 0x0004003000008A02LL && MULTICONFIG(DEVOPTIONS) == 1) //ErrDisp
|
||||||
@ -581,39 +568,37 @@ void patchCode(u64 progId, u16 progVer, u8 *code, u32 size)
|
|||||||
sizeof(pattern), -1,
|
sizeof(pattern), -1,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch), 1
|
sizeof(patch), 1
|
||||||
)) res++;
|
) ||
|
||||||
|
patchMemory(code, size,
|
||||||
if(patchMemory(code, size,
|
|
||||||
pattern2,
|
pattern2,
|
||||||
sizeof(pattern2), 0,
|
sizeof(pattern2), 0,
|
||||||
patch,
|
patch,
|
||||||
sizeof(patch), 3
|
sizeof(patch), 3
|
||||||
) != 3) res++;
|
) != 3) goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
else if(CONFIG(USELANGEMUANDCODE) && (u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
||||||
{
|
{
|
||||||
//External .code section loading
|
|
||||||
res += loadTitleCodeSection(progId, code, size);
|
|
||||||
|
|
||||||
//Language emulation
|
|
||||||
u8 regionId = 0xFF,
|
u8 regionId = 0xFF,
|
||||||
languageId;
|
languageId;
|
||||||
res += loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
if(!loadTitleLocaleConfig(progId, ®ionId, &languageId) ||
|
||||||
|
!loadTitleCodeSection(progId, code, size)) goto error;
|
||||||
|
|
||||||
if(!res && regionId != 0xFF)
|
if(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) res++;
|
if(CFGU_GetConfigInfoBlk2_endPos == NULL ||
|
||||||
else
|
patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos)) goto error;
|
||||||
{
|
|
||||||
res += patchCfgGetLanguage(code, size, languageId, CFGU_GetConfigInfoBlk2_endPos);
|
patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
||||||
patchCfgGetRegion(code, size, regionId, CFGUHandleOffset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(res != 0) svcBreak(USERBREAK_ASSERT);
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
svcBreak(USERBREAK_ASSERT);
|
||||||
|
while(true);
|
||||||
}
|
}
|
@ -33,40 +33,37 @@ CfgData configData;
|
|||||||
|
|
||||||
bool readConfig(void)
|
bool readConfig(void)
|
||||||
{
|
{
|
||||||
bool ret;
|
|
||||||
|
|
||||||
if(fileRead(&configData, CONFIG_FILE, sizeof(CfgData)) != sizeof(CfgData) ||
|
if(fileRead(&configData, CONFIG_FILE, sizeof(CfgData)) != sizeof(CfgData) ||
|
||||||
memcmp(configData.magic, "CONF", 4) != 0 ||
|
memcmp(configData.magic, "CONF", 4) != 0 ||
|
||||||
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
|
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
|
||||||
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
|
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
|
||||||
{
|
{
|
||||||
configData.config = 0;
|
configData.config = 0;
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
else ret = true;
|
|
||||||
|
|
||||||
return ret;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
|
void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
|
||||||
{
|
{
|
||||||
/* If the configuration is different from previously, overwrite it.
|
/* If the configuration is different from previously, overwrite it.
|
||||||
Just the no-forcing flag being set is not enough */
|
Just the no-forcing flag being set is not enough */
|
||||||
if(needConfig == CREATE_CONFIGURATION || (configTemp & 0xFFFFFF7F) != configData.config)
|
if(needConfig != CREATE_CONFIGURATION && (configTemp & 0xFFFFFF7F) == configData.config) return;
|
||||||
|
|
||||||
|
if(needConfig == CREATE_CONFIGURATION)
|
||||||
{
|
{
|
||||||
if(needConfig == CREATE_CONFIGURATION)
|
memcpy(configData.magic, "CONF", 4);
|
||||||
{
|
configData.formatVersionMajor = CONFIG_VERSIONMAJOR;
|
||||||
memcpy(configData.magic, "CONF", 4);
|
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
|
||||||
configData.formatVersionMajor = CONFIG_VERSIONMAJOR;
|
|
||||||
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Merge the new options and new boot configuration
|
|
||||||
configData.config = (configData.config & 0xFFFFFF00) | (configTemp & 0xFF);
|
|
||||||
|
|
||||||
if(!fileWrite(&configData, CONFIG_FILE, sizeof(CfgData)))
|
|
||||||
error("Error writing the configuration file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Merge the new options and new boot configuration
|
||||||
|
configData.config = (configData.config & 0xFFFFFF00) | (configTemp & 0xFF);
|
||||||
|
|
||||||
|
if(!fileWrite(&configData, CONFIG_FILE, sizeof(CfgData)))
|
||||||
|
error("Error writing the configuration file");
|
||||||
}
|
}
|
||||||
|
|
||||||
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
||||||
@ -249,12 +246,11 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
//Display all the multiple choice options in white
|
//Display all the multiple choice options in white
|
||||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||||
{
|
{
|
||||||
if(multiOptions[i].visible)
|
if(!multiOptions[i].visible) continue;
|
||||||
{
|
|
||||||
multiOptions[i].posY = endPos + SPACING_Y;
|
multiOptions[i].posY = endPos + SPACING_Y;
|
||||||
endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE);
|
endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE);
|
||||||
drawCharacter(selected, true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
|
drawCharacter(selected, true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endPos += SPACING_Y / 2;
|
endPos += SPACING_Y / 2;
|
||||||
@ -262,34 +258,34 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
//Display all the normal options in white except for the first one
|
//Display all the normal options in white except for the first one
|
||||||
for(u32 i = 0, color = COLOR_RED; i < singleOptionsAmount; i++)
|
for(u32 i = 0, color = COLOR_RED; i < singleOptionsAmount; i++)
|
||||||
{
|
{
|
||||||
if(singleOptions[i].visible)
|
if(!singleOptions[i].visible) continue;
|
||||||
{
|
|
||||||
singleOptions[i].posY = endPos + SPACING_Y;
|
|
||||||
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
|
|
||||||
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
|
|
||||||
|
|
||||||
if(color == COLOR_RED)
|
singleOptions[i].posY = endPos + SPACING_Y;
|
||||||
{
|
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
|
||||||
singleSelected = i;
|
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
|
||||||
selectedOption = i + multiOptionsAmount;
|
|
||||||
color = COLOR_WHITE;
|
if(color == COLOR_RED)
|
||||||
}
|
{
|
||||||
|
singleSelected = i;
|
||||||
|
selectedOption = i + multiOptionsAmount;
|
||||||
|
color = COLOR_WHITE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
||||||
|
|
||||||
u32 pressed = 0;
|
|
||||||
|
|
||||||
//Boring configuration menu
|
//Boring configuration menu
|
||||||
while(pressed != BUTTON_START)
|
while(true)
|
||||||
{
|
{
|
||||||
|
u32 pressed;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
pressed = waitInput(true);
|
pressed = waitInput(true);
|
||||||
}
|
}
|
||||||
while(!(pressed & MENU_BUTTONS));
|
while(!(pressed & MENU_BUTTONS));
|
||||||
|
|
||||||
|
if(pressed == BUTTON_START) break;
|
||||||
|
|
||||||
if(pressed != BUTTON_A)
|
if(pressed != BUTTON_A)
|
||||||
{
|
{
|
||||||
//Remember the previously selected option
|
//Remember the previously selected option
|
||||||
@ -319,21 +315,19 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode)
|
|||||||
|
|
||||||
if(selectedOption < multiOptionsAmount)
|
if(selectedOption < multiOptionsAmount)
|
||||||
{
|
{
|
||||||
if(multiOptions[selectedOption].visible)
|
if(!multiOptions[selectedOption].visible) continue;
|
||||||
{
|
|
||||||
isMultiOption = true;
|
isMultiOption = true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
singleSelected = selectedOption - multiOptionsAmount;
|
singleSelected = selectedOption - multiOptionsAmount;
|
||||||
|
|
||||||
if(singleOptions[singleSelected].visible)
|
if(!singleOptions[singleSelected].visible) continue;
|
||||||
{
|
|
||||||
isMultiOption = false;
|
isMultiOption = false;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,57 +401,45 @@ void set6x7xKeys(void)
|
|||||||
|
|
||||||
bool decryptExeFs(Cxi *cxi)
|
bool decryptExeFs(Cxi *cxi)
|
||||||
{
|
{
|
||||||
bool isCxi;
|
if(memcmp(cxi->ncch.magic, "NCCH", 4) != 0) return false;
|
||||||
|
|
||||||
if(memcmp(cxi->ncch.magic, "NCCH", 4) == 0)
|
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||||
{
|
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
||||||
isCxi = true;
|
__attribute__((aligned(4))) u8 ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||||
|
|
||||||
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
for(u32 i = 0; i < 8; i++)
|
||||||
u32 exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||||
__attribute__((aligned(4))) u8 ncchCtr[AES_BLOCK_SIZE] = {0};
|
ncchCtr[8] = 2;
|
||||||
|
|
||||||
for(u32 i = 0; i < 8; i++)
|
aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
ncchCtr[8] = 2;
|
aes_use_keyslot(0x2C);
|
||||||
|
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
|
|
||||||
aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
return memcmp(cxi, "FIRM", 4) == 0;
|
||||||
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
|
||||||
aes_use_keyslot(0x2C);
|
|
||||||
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
|
||||||
}
|
|
||||||
else isCxi = false;
|
|
||||||
|
|
||||||
return isCxi && memcmp(cxi, "FIRM", 4) == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
bool decryptNusFirm(const Ticket *ticket, Cxi *cxi, u32 ncchSize)
|
||||||
{
|
{
|
||||||
bool isTicket;
|
if(memcmp(ticket->sigIssuer, "Root", 4) != 0) return false;
|
||||||
|
|
||||||
if(memcmp(ticket->sigIssuer, "Root", 4) == 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],
|
||||||
isTicket = true;
|
|
||||||
|
|
||||||
__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};
|
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));
|
||||||
|
|
||||||
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(0x3D, keyY0x3D, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
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);
|
||||||
|
|
||||||
__attribute__((aligned(4))) u8 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);
|
||||||
aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes(cxi, cxi, ncchSize / AES_BLOCK_SIZE, ncchIv, AES_CBC_DECRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
}
|
|
||||||
else isTicket = false;
|
|
||||||
|
|
||||||
return isTicket && decryptExeFs(cxi);
|
return decryptExeFs(cxi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void kernel9Loader(Arm9Bin *arm9Section)
|
void kernel9Loader(Arm9Bin *arm9Section)
|
||||||
@ -550,19 +538,18 @@ void computePinHash(u8 *outbuf, const u8 *inbuf)
|
|||||||
|
|
||||||
void backupAndRestoreShaHash(bool isRestore)
|
void backupAndRestoreShaHash(bool isRestore)
|
||||||
{
|
{
|
||||||
|
if(!ISA9LH) return;
|
||||||
|
|
||||||
static bool didShaHashBackup = false;
|
static bool didShaHashBackup = false;
|
||||||
__attribute__((aligned(4))) static u8 shaHashBackup[SHA_256_HASH_SIZE];
|
__attribute__((aligned(4))) static u8 shaHashBackup[SHA_256_HASH_SIZE];
|
||||||
|
|
||||||
if(ISA9LH)
|
if(isRestore)
|
||||||
{
|
{
|
||||||
if(isRestore)
|
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
|
||||||
{
|
}
|
||||||
if(didShaHashBackup) memcpy((void *)REG_SHA_HASH, shaHashBackup, sizeof(shaHashBackup));
|
else if(!didShaHashBackup)
|
||||||
}
|
{
|
||||||
else if(!didShaHashBackup)
|
memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
|
||||||
{
|
didShaHashBackup = true;
|
||||||
memcpy(shaHashBackup, (void *)REG_SHA_HASH, sizeof(shaHashBackup));
|
|
||||||
didShaHashBackup = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -38,30 +38,23 @@ bool loadSplash(void)
|
|||||||
*bottomSplashFile = "splashbottom.bin";
|
*bottomSplashFile = "splashbottom.bin";
|
||||||
|
|
||||||
bool isTopSplashValid = getFileSize(topSplashFile) == SCREEN_TOP_FBSIZE,
|
bool isTopSplashValid = getFileSize(topSplashFile) == SCREEN_TOP_FBSIZE,
|
||||||
isBottomSplashValid = getFileSize(bottomSplashFile) == SCREEN_BOTTOM_FBSIZE,
|
isBottomSplashValid = getFileSize(bottomSplashFile) == SCREEN_BOTTOM_FBSIZE;
|
||||||
ret;
|
|
||||||
|
|
||||||
//Don't delay boot nor init the screens if no splash images or invalid splash images are on the SD
|
//Don't delay boot nor init the screens if no splash images or invalid splash images are on the SD
|
||||||
if(!isTopSplashValid && !isBottomSplashValid) ret = false;
|
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||||
else
|
|
||||||
{
|
|
||||||
initScreens();
|
|
||||||
clearScreens(true);
|
|
||||||
|
|
||||||
if(isTopSplashValid) isTopSplashValid = fileRead(fbs[1].top_left, topSplashFile, SCREEN_TOP_FBSIZE) == SCREEN_TOP_FBSIZE;
|
initScreens();
|
||||||
if(isBottomSplashValid) isBottomSplashValid = fileRead(fbs[1].bottom, bottomSplashFile, SCREEN_BOTTOM_FBSIZE) == SCREEN_BOTTOM_FBSIZE;
|
clearScreens(true);
|
||||||
|
|
||||||
if(!isTopSplashValid && !isBottomSplashValid) ret = false;
|
if(isTopSplashValid) isTopSplashValid = fileRead(fbs[1].top_left, topSplashFile, SCREEN_TOP_FBSIZE) == SCREEN_TOP_FBSIZE;
|
||||||
else
|
if(isBottomSplashValid) isBottomSplashValid = fileRead(fbs[1].bottom, bottomSplashFile, SCREEN_BOTTOM_FBSIZE) == SCREEN_BOTTOM_FBSIZE;
|
||||||
{
|
|
||||||
swapFramebuffers(true);
|
|
||||||
wait(false, 3ULL);
|
|
||||||
|
|
||||||
ret = true;
|
if(!isTopSplashValid && !isBottomSplashValid) return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
swapFramebuffers(true);
|
||||||
|
wait(false, 3ULL);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color)
|
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color)
|
||||||
|
125
source/emunand.c
125
source/emunand.c
@ -36,7 +36,6 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
static u8 __attribute__((aligned(4))) temp[0x200];
|
static u8 __attribute__((aligned(4))) temp[0x200];
|
||||||
static u32 nandSize = 0,
|
static u32 nandSize = 0,
|
||||||
fatStart;
|
fatStart;
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
if(!nandSize)
|
if(!nandSize)
|
||||||
{
|
{
|
||||||
@ -45,7 +44,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
fatStart = *(u32 *)(temp + 0x1C6); //First sector of the FAT partition
|
fatStart = *(u32 *)(temp + 0x1C6); //First sector of the FAT partition
|
||||||
}
|
}
|
||||||
|
|
||||||
for(u32 i = 0; i < 3 && !found; i++)
|
for(u32 i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
static const u32 roundedMinsizes[] = {0x1D8000, 0x26E000};
|
static const u32 roundedMinsizes[] = {0x1D8000, 0x26E000};
|
||||||
|
|
||||||
@ -72,7 +71,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
{
|
{
|
||||||
emuOffset = nandOffset + 1;
|
emuOffset = nandOffset + 1;
|
||||||
*emuHeader = nandOffset + 1;
|
*emuHeader = nandOffset + 1;
|
||||||
found = true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check for Gateway EmuNAND
|
//Check for Gateway EmuNAND
|
||||||
@ -80,7 +79,7 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
{
|
{
|
||||||
emuOffset = nandOffset;
|
emuOffset = nandOffset;
|
||||||
*emuHeader = nandOffset + nandSize;
|
*emuHeader = nandOffset + nandSize;
|
||||||
found = true;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,136 +87,108 @@ void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Fallback to the first EmuNAND if there's no second/third/fourth one, or to SysNAND if there isn't any
|
//Fallback to the first EmuNAND if there's no second/third/fourth one, or to SysNAND if there isn't any
|
||||||
if(!found)
|
if(*nandType != FIRMWARE_EMUNAND)
|
||||||
{
|
{
|
||||||
if(*nandType != FIRMWARE_EMUNAND)
|
*nandType = FIRMWARE_EMUNAND;
|
||||||
{
|
locateEmuNand(emuHeader, nandType);
|
||||||
*nandType = FIRMWARE_EMUNAND;
|
|
||||||
locateEmuNand(emuHeader, nandType);
|
|
||||||
}
|
|
||||||
else *nandType = FIRMWARE_SYSNAND;
|
|
||||||
}
|
}
|
||||||
|
else *nandType = FIRMWARE_SYSNAND;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
|
static inline bool getFreeK9Space(u8 *pos, u32 size, u8 **freeK9Space)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
//Looking for the last free space before Process9
|
//Looking for the last free space before Process9
|
||||||
*freeK9Space = memsearch(pos, pattern, size, sizeof(pattern));
|
*freeK9Space = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(*freeK9Space == NULL) ret = 1;
|
if(*freeK9Space == NULL) return false;
|
||||||
else
|
|
||||||
{
|
|
||||||
*freeK9Space += 0x455;
|
|
||||||
|
|
||||||
ret = 0;
|
*freeK9Space += 0x455;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc)
|
static inline u32 getSdmmc(u8 *pos, u32 size, u32 *sdmmc)
|
||||||
{
|
{
|
||||||
//Look for struct code
|
//Look for struct code
|
||||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
*sdmmc = *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
|
||||||
|
|
||||||
ret = 0;
|
*sdmmc = *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset)
|
static inline u32 patchNandRw(u8 *pos, u32 size, u32 branchOffset)
|
||||||
{
|
{
|
||||||
//Look for read/write code
|
//Look for read/write code
|
||||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(readOffset == NULL) ret = 1;
|
if(readOffset == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
readOffset -= 3;
|
|
||||||
|
|
||||||
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
|
readOffset -= 3;
|
||||||
|
|
||||||
if(writeOffset == NULL) ret = 1;
|
u16 *writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern));
|
||||||
else
|
|
||||||
{
|
|
||||||
writeOffset -= 3;
|
|
||||||
*readOffset = *writeOffset = 0x4C00;
|
|
||||||
readOffset[1] = writeOffset[1] = 0x47A0;
|
|
||||||
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
|
||||||
|
|
||||||
ret = 0;
|
if(writeOffset == NULL) return 1;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
writeOffset -= 3;
|
||||||
|
*readOffset = *writeOffset = 0x4C00;
|
||||||
|
readOffset[1] = writeOffset[1] = 0x47A0;
|
||||||
|
((u32 *)writeOffset)[1] = ((u32 *)readOffset)[1] = branchOffset;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 patchMpu(u8 *pos, u32 size)
|
static inline u32 patchMpu(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for MPU pattern
|
//Look for MPU pattern
|
||||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[1] = 0x0036;
|
|
||||||
off[0xC] = off[0x12] = 0x0603;
|
|
||||||
|
|
||||||
ret = 0;
|
off[1] = 0x0036;
|
||||||
}
|
off[0xC] = off[0x12] = 0x0603;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u32 emuHeader, u8 *kernel9Address)
|
u32 patchEmuNand(u8 *arm9Section, u32 kernel9Size, u8 *process9Offset, u32 process9Size, u32 emuHeader, u8 *kernel9Address)
|
||||||
{
|
{
|
||||||
|
u8 *freeK9Space;
|
||||||
|
if(!getFreeK9Space(arm9Section, kernel9Size, &freeK9Space)) return 1;
|
||||||
|
|
||||||
u32 ret = 0;
|
u32 ret = 0;
|
||||||
|
|
||||||
u8 *freeK9Space;
|
//Copy EmuNAND code
|
||||||
ret += getFreeK9Space(arm9Section, kernel9Size, &freeK9Space);
|
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
||||||
|
|
||||||
if(!ret)
|
//Add the data of the found EmuNAND
|
||||||
{
|
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
|
||||||
//Copy EmuNAND code
|
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
|
||||||
memcpy(freeK9Space, emunand_bin, emunand_bin_size);
|
*posOffset = emuOffset;
|
||||||
|
*posHeader = emuHeader;
|
||||||
|
|
||||||
//Add the data of the found EmuNAND
|
//Find and add the SDMMC struct
|
||||||
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_bin_size, 4),
|
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
|
||||||
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_bin_size, 4);
|
u32 sdmmc;
|
||||||
*posOffset = emuOffset;
|
ret += getSdmmc(process9Offset, process9Size, &sdmmc);
|
||||||
*posHeader = emuHeader;
|
if(!ret) *posSdmmc = sdmmc;
|
||||||
|
|
||||||
//Find and add the SDMMC struct
|
//Add EmuNAND hooks
|
||||||
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_bin_size, 4);
|
u32 branchOffset = (u32)(freeK9Space - arm9Section + kernel9Address);
|
||||||
u32 sdmmc;
|
ret += patchNandRw(process9Offset, process9Size, branchOffset);
|
||||||
ret += getSdmmc(process9Offset, process9Size, &sdmmc);
|
|
||||||
if(!ret) *posSdmmc = sdmmc;
|
|
||||||
|
|
||||||
//Add EmuNAND hooks
|
//Set MPU
|
||||||
u32 branchOffset = (u32)(freeK9Space - arm9Section + kernel9Address);
|
ret += patchMpu(arm9Section, kernel9Size);
|
||||||
ret += patchNandRw(process9Offset, process9Size, branchOffset);
|
|
||||||
|
|
||||||
//Set MPU
|
|
||||||
ret += patchMpu(arm9Section, kernel9Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
@ -47,7 +47,6 @@ void installArm9Handlers(void)
|
|||||||
|
|
||||||
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
||||||
{
|
{
|
||||||
u32 ret;
|
|
||||||
u32 *endPos = exceptionsPage + 0x400;
|
u32 *endPos = exceptionsPage + 0x400;
|
||||||
|
|
||||||
u32 *initFPU;
|
u32 *initFPU;
|
||||||
@ -59,152 +58,147 @@ u32 installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffse
|
|||||||
u32 *mcuReboot;
|
u32 *mcuReboot;
|
||||||
for(mcuReboot = exceptionsPage; mcuReboot < endPos && *mcuReboot != 0xE3A0A0C2; 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) return 1;
|
||||||
else
|
|
||||||
|
initFPU += 3;
|
||||||
|
mcuReboot -= 2;
|
||||||
|
|
||||||
|
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
|
||||||
|
|
||||||
|
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
|
||||||
|
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
|
||||||
|
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 16) - 32); //Data Abort
|
||||||
|
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
|
||||||
|
|
||||||
|
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
|
||||||
{
|
{
|
||||||
initFPU += 3;
|
switch(*pos) //Perform relocations
|
||||||
mcuReboot -= 2;
|
|
||||||
|
|
||||||
memcpy(freeSpace, arm11_exceptions_bin + 32, arm11_exceptions_bin_size - 32);
|
|
||||||
|
|
||||||
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 8) - 32); //Undefined Instruction
|
|
||||||
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 12) - 32); //Prefetch Abort
|
|
||||||
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 16) - 32); //Data Abort
|
|
||||||
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions_bin + 4) - 32); //FIQ
|
|
||||||
|
|
||||||
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_bin_size - 32); pos++)
|
|
||||||
{
|
{
|
||||||
switch(*pos) //Perform relocations
|
case 0xFFFF3000: *pos = stackAddress; break;
|
||||||
{
|
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
|
||||||
case 0xFFFF3000: *pos = stackAddress; break;
|
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
|
||||||
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
|
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
|
||||||
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
|
case 0xBEEFBEEF: *pos = codeSetOffset; break;
|
||||||
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
|
|
||||||
case 0xBEEFBEEF: *pos = codeSetOffset; break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void detectAndProcessExceptionDumps(void)
|
void detectAndProcessExceptionDumps(void)
|
||||||
{
|
{
|
||||||
volatile ExceptionDumpHeader *dumpHeader = (volatile ExceptionDumpHeader *)0x25000000;
|
volatile ExceptionDumpHeader *dumpHeader = (volatile ExceptionDumpHeader *)0x25000000;
|
||||||
|
|
||||||
if(dumpHeader->magic[0] == 0xDEADC0DE && dumpHeader->magic[1] == 0xDEADCAFE && (dumpHeader->processor == 9 || dumpHeader->processor == 11))
|
if(dumpHeader->magic[0] != 0xDEADC0DE || dumpHeader->magic[1] == 0xDEADCAFE || (dumpHeader->processor != 9 && dumpHeader->processor != 11)) return;
|
||||||
|
|
||||||
|
const vu32 *regs = (vu32 *)((vu8 *)dumpHeader + sizeof(ExceptionDumpHeader));
|
||||||
|
const vu8 *stackDump = (vu8 *)regs + dumpHeader->registerDumpSize + dumpHeader->codeDumpSize;
|
||||||
|
const vu8 *additionalData = stackDump + dumpHeader->stackDumpSize;
|
||||||
|
|
||||||
|
const char *handledExceptionNames[] = {
|
||||||
|
"FIQ", "undefined instruction", "prefetch abort", "data abort"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *specialExceptions[] = {
|
||||||
|
"(kernel panic)", "(svcBreak)"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *registerNames[] = {
|
||||||
|
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12",
|
||||||
|
"SP", "LR", "PC", "CPSR", "FPEXC"
|
||||||
|
};
|
||||||
|
|
||||||
|
char hexString[] = "00000000";
|
||||||
|
|
||||||
|
initScreens();
|
||||||
|
|
||||||
|
drawString("An exception occurred", true, 10, 10, COLOR_RED);
|
||||||
|
u32 posY = drawString(dumpHeader->processor == 11 ? "Processor: ARM11 (core )" : "Processor: ARM9", true, 10, 30, COLOR_WHITE);
|
||||||
|
if(dumpHeader->processor == 11) drawCharacter('0' + dumpHeader->core, true, 10 + 29 * SPACING_X, 30, COLOR_WHITE);
|
||||||
|
|
||||||
|
posY = drawString("Exception type: ", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||||
|
drawString(handledExceptionNames[dumpHeader->type], true, 10 + 17 * SPACING_X, posY, COLOR_WHITE);
|
||||||
|
|
||||||
|
if(dumpHeader->type == 2)
|
||||||
{
|
{
|
||||||
const vu32 *regs = (vu32 *)((vu8 *)dumpHeader + sizeof(ExceptionDumpHeader));
|
if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 4)
|
||||||
const vu8 *stackDump = (vu8 *)regs + dumpHeader->registerDumpSize + dumpHeader->codeDumpSize;
|
|
||||||
const vu8 *additionalData = stackDump + dumpHeader->stackDumpSize;
|
|
||||||
|
|
||||||
const char *handledExceptionNames[] = {
|
|
||||||
"FIQ", "undefined instruction", "prefetch abort", "data abort"
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *specialExceptions[] = {
|
|
||||||
"(kernel panic)", "(svcBreak)"
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *registerNames[] = {
|
|
||||||
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12",
|
|
||||||
"SP", "LR", "PC", "CPSR", "FPEXC"
|
|
||||||
};
|
|
||||||
|
|
||||||
char hexString[] = "00000000";
|
|
||||||
|
|
||||||
initScreens();
|
|
||||||
|
|
||||||
drawString("An exception occurred", true, 10, 10, COLOR_RED);
|
|
||||||
u32 posY = drawString(dumpHeader->processor == 11 ? "Processor: ARM11 (core )" : "Processor: ARM9", true, 10, 30, COLOR_WHITE);
|
|
||||||
if(dumpHeader->processor == 11) drawCharacter('0' + dumpHeader->core, true, 10 + 29 * SPACING_X, 30, COLOR_WHITE);
|
|
||||||
|
|
||||||
posY = drawString("Exception type: ", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
|
||||||
drawString(handledExceptionNames[dumpHeader->type], true, 10 + 17 * SPACING_X, posY, COLOR_WHITE);
|
|
||||||
|
|
||||||
if(dumpHeader->type == 2)
|
|
||||||
{
|
{
|
||||||
if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 4)
|
u32 instr = *(vu32 *)(stackDump - 4);
|
||||||
{
|
if(instr == 0xE12FFF7E) drawString(specialExceptions[0], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||||
u32 instr = *(vu32 *)(stackDump - 4);
|
else if(instr == 0xEF00003C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||||
if(instr == 0xE12FFF7E) drawString(specialExceptions[0], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
|
||||||
else if(instr == 0xEF00003C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
else if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 2)
|
|
||||||
{
|
|
||||||
u16 instr = *(vu16 *)(stackDump - 2);
|
|
||||||
if(instr == 0xDF3C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 2)
|
||||||
if(dumpHeader->processor == 11 && dumpHeader->additionalDataSize != 0)
|
|
||||||
{
|
{
|
||||||
char processName[] = "Current process: ";
|
u16 instr = *(vu16 *)(stackDump - 2);
|
||||||
memcpy(processName + sizeof(processName) - 9, (void *)additionalData, 8);
|
if(instr == 0xDF3C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||||
posY = drawString(processName, true, 10, posY + SPACING_Y, COLOR_WHITE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
posY += SPACING_Y;
|
|
||||||
|
|
||||||
for(u32 i = 0; i < 17; i += 2)
|
|
||||||
{
|
|
||||||
posY = drawString(registerNames[i], true, 10, posY + SPACING_Y, COLOR_WHITE);
|
|
||||||
hexItoa(regs[i], hexString, 8, true);
|
|
||||||
drawString(hexString, true, 10 + 7 * SPACING_X, posY, COLOR_WHITE);
|
|
||||||
|
|
||||||
if(i != 16 || dumpHeader->processor != 9)
|
|
||||||
{
|
|
||||||
drawString(registerNames[i + 1], true, 10 + 22 * SPACING_X, posY, COLOR_WHITE);
|
|
||||||
hexItoa(i == 16 ? regs[20] : regs[i + 1], hexString, 8, true);
|
|
||||||
drawString(hexString, true, 10 + 29 * SPACING_X, posY, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
posY += SPACING_Y;
|
|
||||||
|
|
||||||
u32 mode = regs[16] & 0xF;
|
|
||||||
if(dumpHeader->type == 3 && (mode == 7 || mode == 11))
|
|
||||||
posY = drawString("Incorrect dump: failed to dump code and/or stack", true, 10, posY + SPACING_Y, COLOR_YELLOW) + SPACING_Y;
|
|
||||||
|
|
||||||
u32 posYBottom = drawString("Stack dump:", false, 10, 10, COLOR_WHITE) + SPACING_Y;
|
|
||||||
|
|
||||||
for(u32 line = 0; line < 19 && stackDump < additionalData; line++)
|
|
||||||
{
|
|
||||||
hexItoa(regs[13] + 8 * line, hexString, 8, true);
|
|
||||||
posYBottom = drawString(hexString, false, 10, posYBottom + SPACING_Y, COLOR_WHITE);
|
|
||||||
drawCharacter(':', false, 10 + 8 * SPACING_X, posYBottom, COLOR_WHITE);
|
|
||||||
|
|
||||||
for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++)
|
|
||||||
{
|
|
||||||
char byteString[] = "00";
|
|
||||||
hexItoa(*stackDump, byteString, 2, false);
|
|
||||||
drawString(byteString, false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char path[36];
|
|
||||||
char fileName[] = "crash_dump_00000000.dmp";
|
|
||||||
const char *pathFolder = dumpHeader->processor == 9 ? "dumps/arm9" : "dumps/arm11";
|
|
||||||
|
|
||||||
findDumpFile(pathFolder, fileName);
|
|
||||||
memcpy(path, pathFolder, strlen(pathFolder) + 1);
|
|
||||||
concatenateStrings(path, "/");
|
|
||||||
concatenateStrings(path, fileName);
|
|
||||||
|
|
||||||
if(fileWrite((void *)dumpHeader, path, dumpHeader->totalSize))
|
|
||||||
{
|
|
||||||
posY = drawString("You can find a dump in the following file:", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
|
||||||
posY = drawString(path, true, 10, posY + SPACING_Y, COLOR_WHITE) + SPACING_Y;
|
|
||||||
}
|
|
||||||
else posY = drawString("Error writing the dump file", true, 10, posY + SPACING_Y, COLOR_RED);
|
|
||||||
|
|
||||||
drawString("Press any button to shutdown", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
|
||||||
|
|
||||||
memset32((void *)dumpHeader, 0, dumpHeader->totalSize);
|
|
||||||
|
|
||||||
waitInput(false);
|
|
||||||
mcuPowerOff();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(dumpHeader->processor == 11 && dumpHeader->additionalDataSize != 0)
|
||||||
|
{
|
||||||
|
char processName[] = "Current process: ";
|
||||||
|
memcpy(processName + sizeof(processName) - 9, (void *)additionalData, 8);
|
||||||
|
posY = drawString(processName, true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
posY += SPACING_Y;
|
||||||
|
|
||||||
|
for(u32 i = 0; i < 17; i += 2)
|
||||||
|
{
|
||||||
|
posY = drawString(registerNames[i], true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||||
|
hexItoa(regs[i], hexString, 8, true);
|
||||||
|
drawString(hexString, true, 10 + 7 * SPACING_X, posY, COLOR_WHITE);
|
||||||
|
|
||||||
|
if(i != 16 || dumpHeader->processor != 9)
|
||||||
|
{
|
||||||
|
drawString(registerNames[i + 1], true, 10 + 22 * SPACING_X, posY, COLOR_WHITE);
|
||||||
|
hexItoa(i == 16 ? regs[20] : regs[i + 1], hexString, 8, true);
|
||||||
|
drawString(hexString, true, 10 + 29 * SPACING_X, posY, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
posY += SPACING_Y;
|
||||||
|
|
||||||
|
u32 mode = regs[16] & 0xF;
|
||||||
|
if(dumpHeader->type == 3 && (mode == 7 || mode == 11))
|
||||||
|
posY = drawString("Incorrect dump: failed to dump code and/or stack", true, 10, posY + SPACING_Y, COLOR_YELLOW) + SPACING_Y;
|
||||||
|
|
||||||
|
u32 posYBottom = drawString("Stack dump:", false, 10, 10, COLOR_WHITE) + SPACING_Y;
|
||||||
|
|
||||||
|
for(u32 line = 0; line < 19 && stackDump < additionalData; line++)
|
||||||
|
{
|
||||||
|
hexItoa(regs[13] + 8 * line, hexString, 8, true);
|
||||||
|
posYBottom = drawString(hexString, false, 10, posYBottom + SPACING_Y, COLOR_WHITE);
|
||||||
|
drawCharacter(':', false, 10 + 8 * SPACING_X, posYBottom, COLOR_WHITE);
|
||||||
|
|
||||||
|
for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++)
|
||||||
|
{
|
||||||
|
char byteString[] = "00";
|
||||||
|
hexItoa(*stackDump, byteString, 2, false);
|
||||||
|
drawString(byteString, false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[36];
|
||||||
|
char fileName[] = "crash_dump_00000000.dmp";
|
||||||
|
const char *pathFolder = dumpHeader->processor == 9 ? "dumps/arm9" : "dumps/arm11";
|
||||||
|
|
||||||
|
findDumpFile(pathFolder, fileName);
|
||||||
|
memcpy(path, pathFolder, strlen(pathFolder) + 1);
|
||||||
|
concatenateStrings(path, "/");
|
||||||
|
concatenateStrings(path, fileName);
|
||||||
|
|
||||||
|
if(fileWrite((void *)dumpHeader, path, dumpHeader->totalSize))
|
||||||
|
{
|
||||||
|
posY = drawString("You can find a dump in the following file:", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||||
|
posY = drawString(path, true, 10, posY + SPACING_Y, COLOR_WHITE) + SPACING_Y;
|
||||||
|
}
|
||||||
|
else posY = drawString("Error writing the dump file", true, 10, posY + SPACING_Y, COLOR_RED);
|
||||||
|
|
||||||
|
drawString("Press any button to shutdown", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||||
|
|
||||||
|
memset32((void *)dumpHeader, 0, dumpHeader->totalSize);
|
||||||
|
|
||||||
|
waitInput(false);
|
||||||
|
mcuPowerOff();
|
||||||
}
|
}
|
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
static Firm *firm = (Firm *)0x24000000;
|
static Firm *firm = (Firm *)0x24000000;
|
||||||
|
|
||||||
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
|
static inline bool loadFirmFromStorage(FirmwareType firmType)
|
||||||
{
|
{
|
||||||
const char *firmwareFiles[] = {
|
const char *firmwareFiles[] = {
|
||||||
"firmware.bin",
|
"firmware.bin",
|
||||||
@ -53,6 +53,30 @@ u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStora
|
|||||||
"cetk_sysupdater"
|
"cetk_sysupdater"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u32 firmSize = fileRead(firm, firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)firmType], 0x400000 + sizeof(Cxi) + 0x200);
|
||||||
|
|
||||||
|
if(!firmSize) return false;
|
||||||
|
|
||||||
|
if(firmSize <= sizeof(Cxi) + 0x200) error("The FIRM in /luma is not valid.");
|
||||||
|
|
||||||
|
if(memcmp(firm, "FIRM", 4) != 0)
|
||||||
|
{
|
||||||
|
u8 cetk[0xA50];
|
||||||
|
|
||||||
|
if(fileRead(cetk, firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)firmType], sizeof(cetk)) != sizeof(cetk) ||
|
||||||
|
!decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
|
||||||
|
error("The FIRM in /luma is encrypted or corrupted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check that the FIRM is right for the console from the ARM9 section address
|
||||||
|
if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
||||||
|
error("The FIRM in /luma is not for this console.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode)
|
||||||
|
{
|
||||||
//Load FIRM from CTRNAND
|
//Load FIRM from CTRNAND
|
||||||
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
||||||
|
|
||||||
@ -77,32 +101,8 @@ u32 loadFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStora
|
|||||||
else if(firmVersion < 0x25) mustLoadFromStorage = true;
|
else if(firmVersion < 0x25) mustLoadFromStorage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(loadFromStorage || mustLoadFromStorage)
|
if((loadFromStorage || mustLoadFromStorage) && loadFirmFromStorage(*firmType)) firmVersion = 0xFFFFFFFF;
|
||||||
{
|
else
|
||||||
u32 firmSize = fileRead(firm, *firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)*firmType], 0x400000 + sizeof(Cxi) + 0x200);
|
|
||||||
|
|
||||||
if(firmSize > 0)
|
|
||||||
{
|
|
||||||
if(firmSize <= sizeof(Cxi) + 0x200) error("The FIRM in /luma is not valid.");
|
|
||||||
|
|
||||||
if(memcmp(firm, "FIRM", 4) != 0)
|
|
||||||
{
|
|
||||||
u8 cetk[0xA50];
|
|
||||||
|
|
||||||
if(fileRead(cetk, *firmType == NATIVE_FIRM1X2X ? cetkFiles[0] : cetkFiles[(u32)*firmType], sizeof(cetk)) != sizeof(cetk) ||
|
|
||||||
!decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize))
|
|
||||||
error("The FIRM in /luma is encrypted or corrupted.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check that the FIRM is right for the console from the ARM9 section address
|
|
||||||
if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800))
|
|
||||||
error("The FIRM in /luma is not for this console.");
|
|
||||||
|
|
||||||
firmVersion = 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(firmVersion != 0xFFFFFFFF)
|
|
||||||
{
|
{
|
||||||
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
|
if(mustLoadFromStorage) error("An old unsupported FIRM has been detected.\nCopy a firmware.bin in /luma to boot.");
|
||||||
if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
|
if(!decryptExeFs((Cxi *)firm)) error("The CTRNAND FIRM is corrupted.");
|
||||||
@ -310,10 +310,7 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
|||||||
srcModuleSize = ((Cxi *)src)->ncch.contentSize * 0x200;
|
srcModuleSize = ((Cxi *)src)->ncch.contentSize * 0x200;
|
||||||
const char *moduleName = ((Cxi *)src)->exHeader.systemControlInfo.appTitle;
|
const char *moduleName = ((Cxi *)src)->exHeader.systemControlInfo.appTitle;
|
||||||
|
|
||||||
bool loadedModule;
|
if(loadFromStorage)
|
||||||
|
|
||||||
if(!loadFromStorage) loadedModule = false;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
char fileName[24] = "sysmodules/";
|
char fileName[24] = "sysmodules/";
|
||||||
|
|
||||||
@ -323,8 +320,7 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
|||||||
|
|
||||||
dstModuleSize = getFileSize(fileName);
|
dstModuleSize = getFileSize(fileName);
|
||||||
|
|
||||||
if(dstModuleSize == 0) loadedModule = false;
|
if(dstModuleSize != 0)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
|
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
|
||||||
|
|
||||||
@ -334,29 +330,26 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo
|
|||||||
memcmp(moduleName, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0)
|
memcmp(moduleName, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0)
|
||||||
error("An external FIRM module is invalid or corrupted.");
|
error("An external FIRM module is invalid or corrupted.");
|
||||||
|
|
||||||
loadedModule = true;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!loadedModule)
|
const u8 *module;
|
||||||
|
|
||||||
|
if(firmType == NATIVE_FIRM && memcmp(moduleName, "loader", 6) == 0)
|
||||||
{
|
{
|
||||||
const u8 *module;
|
module = injector_bin;
|
||||||
|
dstModuleSize = injector_bin_size;
|
||||||
if(firmType == NATIVE_FIRM && memcmp(moduleName, "loader", 6) == 0)
|
|
||||||
{
|
|
||||||
module = injector_bin;
|
|
||||||
dstModuleSize = injector_bin_size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
module = src;
|
|
||||||
dstModuleSize = srcModuleSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
|
|
||||||
|
|
||||||
memcpy(dst, module, dstModuleSize);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
module = src;
|
||||||
|
dstModuleSize = srcModuleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dstModuleSize > maxModuleSize) error(extModuleSizeError);
|
||||||
|
|
||||||
|
memcpy(dst, module, dstModuleSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
271
source/fs.c
271
source/fs.c
@ -66,17 +66,16 @@ bool mountFs(bool isSd, bool switchToCtrNand)
|
|||||||
u32 fileRead(void *dest, const char *path, u32 maxSize)
|
u32 fileRead(void *dest, const char *path, u32 maxSize)
|
||||||
{
|
{
|
||||||
FIL file;
|
FIL file;
|
||||||
|
|
||||||
|
if(f_open(&file, path, FA_READ) != FR_OK) return 0;
|
||||||
|
|
||||||
u32 ret;
|
u32 ret;
|
||||||
|
|
||||||
if(f_open(&file, path, FA_READ) != FR_OK) ret = 0;
|
u32 size = f_size(&file);
|
||||||
else
|
if(dest == NULL) ret = size;
|
||||||
{
|
else if(size <= maxSize)
|
||||||
u32 size = f_size(&file);
|
f_read(&file, dest, size, (unsigned int *)&ret);
|
||||||
if(dest == NULL) ret = size;
|
f_close(&file);
|
||||||
else if(size <= maxSize)
|
|
||||||
f_read(&file, dest, size, (unsigned int *)&ret);
|
|
||||||
f_close(&file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -89,7 +88,6 @@ u32 getFileSize(const char *path)
|
|||||||
bool fileWrite(const void *buffer, const char *path, u32 size)
|
bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||||
{
|
{
|
||||||
FIL file;
|
FIL file;
|
||||||
bool ret;
|
|
||||||
|
|
||||||
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
|
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
|
||||||
{
|
{
|
||||||
@ -100,8 +98,7 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
|||||||
f_truncate(&file);
|
f_truncate(&file);
|
||||||
f_close(&file);
|
f_close(&file);
|
||||||
|
|
||||||
ret = (u32)written == size;
|
return (u32)written == size;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FR_NO_PATH:
|
case FR_NO_PATH:
|
||||||
for(u32 i = 1; path[i] != 0; i++)
|
for(u32 i = 1; path[i] != 0; i++)
|
||||||
@ -113,14 +110,10 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
|||||||
f_mkdir(folder);
|
f_mkdir(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = fileWrite(buffer, path, size);
|
return fileWrite(buffer, path, size);
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
ret = false;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fileDelete(const char *path)
|
void fileDelete(const char *path)
|
||||||
@ -158,33 +151,30 @@ void loadPayload(u32 pressed, const char *payloadPath)
|
|||||||
|
|
||||||
result = f_findfirst(&dir, &info, path, pattern);
|
result = f_findfirst(&dir, &info, path, pattern);
|
||||||
|
|
||||||
if(result == FR_OK)
|
if(result != FR_OK) return;
|
||||||
{
|
|
||||||
f_closedir(&dir);
|
|
||||||
|
|
||||||
if(info.fname[0] != 0)
|
f_closedir(&dir);
|
||||||
{
|
|
||||||
concatenateStrings(path, "/");
|
if(!info.fname[0]) return;
|
||||||
concatenateStrings(path, info.altname);
|
|
||||||
payloadSize = fileRead(payloadAddress, path, maxPayloadSize);
|
concatenateStrings(path, "/");
|
||||||
}
|
concatenateStrings(path, info.altname);
|
||||||
}
|
payloadSize = fileRead(payloadAddress, path, maxPayloadSize);
|
||||||
}
|
}
|
||||||
else payloadSize = fileRead(payloadAddress, payloadPath, maxPayloadSize);
|
else payloadSize = fileRead(payloadAddress, payloadPath, maxPayloadSize);
|
||||||
|
|
||||||
if(payloadSize > 0)
|
if(!payloadSize) return;
|
||||||
{
|
|
||||||
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
|
||||||
loaderAddress[1] = payloadSize;
|
|
||||||
|
|
||||||
backupAndRestoreShaHash(true);
|
memcpy(loaderAddress, loader_bin, loader_bin_size);
|
||||||
initScreens();
|
loaderAddress[1] = payloadSize;
|
||||||
|
|
||||||
flushDCacheRange(loaderAddress, loader_bin_size);
|
backupAndRestoreShaHash(true);
|
||||||
flushICacheRange(loaderAddress, loader_bin_size);
|
initScreens();
|
||||||
|
|
||||||
((void (*)())loaderAddress)();
|
flushDCacheRange(loaderAddress, loader_bin_size);
|
||||||
}
|
flushICacheRange(loaderAddress, loader_bin_size);
|
||||||
|
|
||||||
|
((void (*)())loaderAddress)();
|
||||||
}
|
}
|
||||||
|
|
||||||
void payloadMenu(void)
|
void payloadMenu(void)
|
||||||
@ -192,89 +182,87 @@ void payloadMenu(void)
|
|||||||
DIR dir;
|
DIR dir;
|
||||||
char path[62] = "payloads";
|
char path[62] = "payloads";
|
||||||
|
|
||||||
if(f_opendir(&dir, path) == FR_OK)
|
if(f_opendir(&dir, path) != FR_OK) return;
|
||||||
|
|
||||||
|
FILINFO info;
|
||||||
|
u32 payloadNum = 0;
|
||||||
|
char payloadList[20][49];
|
||||||
|
|
||||||
|
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0 && payloadNum < 20)
|
||||||
{
|
{
|
||||||
FILINFO info;
|
if(info.fname[0] == '.' || memcmp(info.altname + 8, ".BIN", 4) != 0) continue;
|
||||||
u32 payloadNum = 0;
|
u32 nameLength = strlen(info.fname) - 4;
|
||||||
char payloadList[20][49];
|
|
||||||
|
|
||||||
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0 && payloadNum < 20)
|
if(nameLength > 48) continue;
|
||||||
if(info.fname[0] != '.' && memcmp(info.altname + 8, ".BIN", 4) == 0)
|
|
||||||
{
|
|
||||||
u32 nameLength = strlen(info.fname) - 4;
|
|
||||||
if(nameLength < 49)
|
|
||||||
{
|
|
||||||
memcpy(payloadList[payloadNum], info.fname, nameLength);
|
|
||||||
payloadList[payloadNum][nameLength] = 0;
|
|
||||||
payloadNum++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f_closedir(&dir);
|
memcpy(payloadList[payloadNum], info.fname, nameLength);
|
||||||
|
payloadList[payloadNum][nameLength] = 0;
|
||||||
if(payloadNum > 0)
|
payloadNum++;
|
||||||
{
|
|
||||||
initScreens();
|
|
||||||
|
|
||||||
drawString("Luma3DS chainloader", true, 10, 10, COLOR_TITLE);
|
|
||||||
drawString("Press A to select, START to quit", true, 10, 10 + SPACING_Y, COLOR_TITLE);
|
|
||||||
|
|
||||||
for(u32 i = 0, posY = 10 + 3 * SPACING_Y, color = COLOR_RED; i < payloadNum; i++, posY += SPACING_Y)
|
|
||||||
{
|
|
||||||
drawString(payloadList[i], true, 10, posY, color);
|
|
||||||
if(color == COLOR_RED) color = COLOR_WHITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 pressed = 0,
|
|
||||||
selectedPayload = 0;
|
|
||||||
|
|
||||||
while(pressed != BUTTON_A && pressed != BUTTON_START)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
pressed = waitInput(true);
|
|
||||||
}
|
|
||||||
while(!(pressed & MENU_BUTTONS));
|
|
||||||
|
|
||||||
u32 oldSelectedPayload = selectedPayload;
|
|
||||||
|
|
||||||
switch(pressed)
|
|
||||||
{
|
|
||||||
case BUTTON_UP:
|
|
||||||
selectedPayload = !selectedPayload ? payloadNum - 1 : selectedPayload - 1;
|
|
||||||
break;
|
|
||||||
case BUTTON_DOWN:
|
|
||||||
selectedPayload = selectedPayload == payloadNum - 1 ? 0 : selectedPayload + 1;
|
|
||||||
break;
|
|
||||||
case BUTTON_LEFT:
|
|
||||||
selectedPayload = 0;
|
|
||||||
break;
|
|
||||||
case BUTTON_RIGHT:
|
|
||||||
selectedPayload = payloadNum - 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(oldSelectedPayload == selectedPayload) continue;
|
|
||||||
|
|
||||||
drawString(payloadList[oldSelectedPayload], true, 10, 10 + (3 + oldSelectedPayload) * SPACING_Y, COLOR_WHITE);
|
|
||||||
drawString(payloadList[selectedPayload], true, 10, 10 + (3 + selectedPayload) * SPACING_Y, COLOR_RED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pressed == BUTTON_A)
|
|
||||||
{
|
|
||||||
concatenateStrings(path, "/");
|
|
||||||
concatenateStrings(path, payloadList[selectedPayload]);
|
|
||||||
concatenateStrings(path, ".bin");
|
|
||||||
loadPayload(0, path);
|
|
||||||
error("The payload is too large or corrupted.");
|
|
||||||
}
|
|
||||||
|
|
||||||
while(HID_PAD & MENU_BUTTONS);
|
|
||||||
wait(false, 2ULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f_closedir(&dir);
|
||||||
|
|
||||||
|
if(!payloadNum) return;
|
||||||
|
|
||||||
|
initScreens();
|
||||||
|
|
||||||
|
drawString("Luma3DS chainloader", true, 10, 10, COLOR_TITLE);
|
||||||
|
drawString("Press A to select, START to quit", true, 10, 10 + SPACING_Y, COLOR_TITLE);
|
||||||
|
|
||||||
|
for(u32 i = 0, posY = 10 + 3 * SPACING_Y, color = COLOR_RED; i < payloadNum; i++, posY += SPACING_Y)
|
||||||
|
{
|
||||||
|
drawString(payloadList[i], true, 10, posY, color);
|
||||||
|
if(color == COLOR_RED) color = COLOR_WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 pressed = 0,
|
||||||
|
selectedPayload = 0;
|
||||||
|
|
||||||
|
while(pressed != BUTTON_A && pressed != BUTTON_START)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
pressed = waitInput(true);
|
||||||
|
}
|
||||||
|
while(!(pressed & MENU_BUTTONS));
|
||||||
|
|
||||||
|
u32 oldSelectedPayload = selectedPayload;
|
||||||
|
|
||||||
|
switch(pressed)
|
||||||
|
{
|
||||||
|
case BUTTON_UP:
|
||||||
|
selectedPayload = !selectedPayload ? payloadNum - 1 : selectedPayload - 1;
|
||||||
|
break;
|
||||||
|
case BUTTON_DOWN:
|
||||||
|
selectedPayload = selectedPayload == payloadNum - 1 ? 0 : selectedPayload + 1;
|
||||||
|
break;
|
||||||
|
case BUTTON_LEFT:
|
||||||
|
selectedPayload = 0;
|
||||||
|
break;
|
||||||
|
case BUTTON_RIGHT:
|
||||||
|
selectedPayload = payloadNum - 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(oldSelectedPayload == selectedPayload) continue;
|
||||||
|
|
||||||
|
drawString(payloadList[oldSelectedPayload], true, 10, 10 + (3 + oldSelectedPayload) * SPACING_Y, COLOR_WHITE);
|
||||||
|
drawString(payloadList[selectedPayload], true, 10, 10 + (3 + selectedPayload) * SPACING_Y, COLOR_RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pressed == BUTTON_A)
|
||||||
|
{
|
||||||
|
concatenateStrings(path, "/");
|
||||||
|
concatenateStrings(path, payloadList[selectedPayload]);
|
||||||
|
concatenateStrings(path, ".bin");
|
||||||
|
loadPayload(0, path);
|
||||||
|
error("The payload is too large or corrupted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
while(HID_PAD & MENU_BUTTONS);
|
||||||
|
wait(false, 2ULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 firmRead(void *dest, u32 firmType)
|
u32 firmRead(void *dest, u32 firmType)
|
||||||
@ -292,36 +280,35 @@ u32 firmRead(void *dest, u32 firmType)
|
|||||||
DIR dir;
|
DIR dir;
|
||||||
u32 firmVersion = 0xFFFFFFFF;
|
u32 firmVersion = 0xFFFFFFFF;
|
||||||
|
|
||||||
if(f_opendir(&dir, path) == FR_OK)
|
if(f_opendir(&dir, path) != FR_OK) goto exit;
|
||||||
|
|
||||||
|
FILINFO info;
|
||||||
|
|
||||||
|
//Parse the target directory
|
||||||
|
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0)
|
||||||
{
|
{
|
||||||
FILINFO info;
|
//Not a cxi
|
||||||
|
if(info.fname[9] == 'a' && strlen(info.fname) != 12) continue;
|
||||||
|
|
||||||
//Parse the target directory
|
u32 tempVersion = hexAtoi(info.altname, 8);
|
||||||
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0)
|
|
||||||
{
|
|
||||||
//Not a cxi
|
|
||||||
if(info.fname[9] != 'a' || strlen(info.fname) != 12) continue;
|
|
||||||
|
|
||||||
u32 tempVersion = hexAtoi(info.altname, 8);
|
//Found an older cxi
|
||||||
|
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
||||||
//Found an older cxi
|
|
||||||
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
f_closedir(&dir);
|
|
||||||
|
|
||||||
if(firmVersion != 0xFFFFFFFF)
|
|
||||||
{
|
|
||||||
//Complete the string with the .app name
|
|
||||||
concatenateStrings(path, "/00000000.app");
|
|
||||||
|
|
||||||
//Convert back the .app name from integer to array
|
|
||||||
hexItoa(firmVersion, path + 35, 8, false);
|
|
||||||
|
|
||||||
if(fileRead(dest, path, 0x400000 + sizeof(Cxi) + 0x200) <= sizeof(Cxi) + 0x200) firmVersion = 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f_closedir(&dir);
|
||||||
|
|
||||||
|
if(firmVersion == 0xFFFFFFFF) goto exit;
|
||||||
|
|
||||||
|
//Complete the string with the .app name
|
||||||
|
concatenateStrings(path, "/00000000.app");
|
||||||
|
|
||||||
|
//Convert back the .app name from integer to array
|
||||||
|
hexItoa(firmVersion, path + 35, 8, false);
|
||||||
|
|
||||||
|
if(fileRead(dest, path, 0x400000 + sizeof(Cxi) + 0x200) <= sizeof(Cxi) + 0x200) firmVersion = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
exit:
|
||||||
return firmVersion;
|
return firmVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
262
source/main.c
262
source/main.c
@ -78,147 +78,147 @@ void main(void)
|
|||||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||||
isA9lhInstalled = BOOTCFG_A9LH != 0;
|
isA9lhInstalled = BOOTCFG_A9LH != 0;
|
||||||
|
|
||||||
|
goto boot;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(ISA9LH)
|
||||||
{
|
{
|
||||||
if(ISA9LH)
|
detectAndProcessExceptionDumps();
|
||||||
|
installArm9Handlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
firmType = NATIVE_FIRM;
|
||||||
|
isA9lhInstalled = ISA9LH;
|
||||||
|
|
||||||
|
//Get pressed buttons
|
||||||
|
u32 pressed = HID_PAD;
|
||||||
|
|
||||||
|
//Save old options and begin saving the new boot configuration
|
||||||
|
configTemp = (configData.config & 0xFFFFFF00) | ((u32)ISA9LH << 6);
|
||||||
|
|
||||||
|
//If it's a MCU reboot, try to force boot options
|
||||||
|
if(ISA9LH && CFG_BOOTENV && needConfig != CREATE_CONFIGURATION)
|
||||||
|
{
|
||||||
|
//Always force a SysNAND boot when quitting AGB_FIRM
|
||||||
|
if(CFG_BOOTENV == 7)
|
||||||
{
|
{
|
||||||
detectAndProcessExceptionDumps();
|
nandType = FIRMWARE_SYSNAND;
|
||||||
installArm9Handlers();
|
firmSource = (BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
||||||
|
|
||||||
|
//Flag to prevent multiple boot options-forcing
|
||||||
|
configTemp |= 1 << 7;
|
||||||
|
|
||||||
|
goto boot;
|
||||||
}
|
}
|
||||||
|
|
||||||
firmType = NATIVE_FIRM;
|
/* Else, force the last used boot options unless a button is pressed
|
||||||
isA9lhInstalled = ISA9LH;
|
or the no-forcing flag is set */
|
||||||
|
if(!pressed && !BOOTCFG_NOFORCEFLAG)
|
||||||
//Get pressed buttons
|
|
||||||
u32 pressed = HID_PAD;
|
|
||||||
|
|
||||||
//Save old options and begin saving the new boot configuration
|
|
||||||
configTemp = (configData.config & 0xFFFFFF00) | ((u32)ISA9LH << 6);
|
|
||||||
|
|
||||||
//If it's a MCU reboot, try to force boot options
|
|
||||||
if(ISA9LH && CFG_BOOTENV && needConfig != CREATE_CONFIGURATION)
|
|
||||||
{
|
{
|
||||||
//Always force a SysNAND boot when quitting AGB_FIRM
|
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||||
if(CFG_BOOTENV == 7)
|
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||||
{
|
|
||||||
nandType = FIRMWARE_SYSNAND;
|
|
||||||
firmSource = (BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
|
||||||
needConfig = DONT_CONFIGURE;
|
|
||||||
|
|
||||||
//Flag to prevent multiple boot options-forcing
|
goto boot;
|
||||||
configTemp |= 1 << 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Else, force the last used boot options unless a button is pressed
|
|
||||||
or the no-forcing flag is set */
|
|
||||||
else if(!pressed && !BOOTCFG_NOFORCEFLAG)
|
|
||||||
{
|
|
||||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
|
||||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
|
||||||
needConfig = DONT_CONFIGURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Boot options aren't being forced
|
|
||||||
if(needConfig != DONT_CONFIGURE)
|
|
||||||
{
|
|
||||||
u32 pinMode = MULTICONFIG(PIN);
|
|
||||||
bool pinExists = pinMode != 0 && verifyPin(pinMode);
|
|
||||||
|
|
||||||
//If no configuration file exists or SELECT is held, load configuration menu
|
|
||||||
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & (BUTTON_SELECT | BUTTON_L1)) == BUTTON_SELECT);
|
|
||||||
|
|
||||||
if(shouldLoadConfigMenu)
|
|
||||||
{
|
|
||||||
configMenu(isSdMode, pinExists, pinMode);
|
|
||||||
|
|
||||||
//Update pressed buttons
|
|
||||||
pressed = HID_PAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ISA9LH && !CFG_BOOTENV && pressed == SAFE_MODE)
|
|
||||||
{
|
|
||||||
nandType = FIRMWARE_SYSNAND;
|
|
||||||
firmSource = FIRMWARE_SYSNAND;
|
|
||||||
|
|
||||||
isSafeMode = true;
|
|
||||||
|
|
||||||
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
|
|
||||||
if(pinExists && !shouldLoadConfigMenu)
|
|
||||||
{
|
|
||||||
while(HID_PAD & PIN_BUTTONS);
|
|
||||||
wait(false, 2ULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 splashMode = MULTICONFIG(SPLASH);
|
|
||||||
|
|
||||||
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
|
||||||
|
|
||||||
if((pressed & (BUTTON_START | BUTTON_L1)) == BUTTON_START)
|
|
||||||
{
|
|
||||||
payloadMenu();
|
|
||||||
pressed = HID_PAD;
|
|
||||||
}
|
|
||||||
else if(((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
|
||||||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1))) loadPayload(pressed, NULL);
|
|
||||||
|
|
||||||
if(splashMode == 2) loadSplash();
|
|
||||||
|
|
||||||
//If booting from CTRNAND, always use SysNAND
|
|
||||||
if(!isSdMode) nandType = FIRMWARE_SYSNAND;
|
|
||||||
|
|
||||||
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
|
|
||||||
else if(pressed & BUTTON_R1)
|
|
||||||
{
|
|
||||||
if(CONFIG(USESYSFIRM))
|
|
||||||
{
|
|
||||||
nandType = FIRMWARE_EMUNAND;
|
|
||||||
firmSource = FIRMWARE_SYSNAND;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nandType = FIRMWARE_SYSNAND;
|
|
||||||
firmSource = FIRMWARE_EMUNAND;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
|
|
||||||
with their own FIRM */
|
|
||||||
else firmSource = nandType = (CONFIG(AUTOBOOTSYS) == ((pressed & BUTTON_L1) == BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
|
||||||
|
|
||||||
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
|
|
||||||
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
|
||||||
{
|
|
||||||
FirmwareSource tempNand;
|
|
||||||
switch(pressed & DPAD_BUTTONS)
|
|
||||||
{
|
|
||||||
case BUTTON_UP:
|
|
||||||
tempNand = FIRMWARE_EMUNAND;
|
|
||||||
break;
|
|
||||||
case BUTTON_RIGHT:
|
|
||||||
tempNand = FIRMWARE_EMUNAND2;
|
|
||||||
break;
|
|
||||||
case BUTTON_DOWN:
|
|
||||||
tempNand = FIRMWARE_EMUNAND3;
|
|
||||||
break;
|
|
||||||
case BUTTON_LEFT:
|
|
||||||
tempNand = FIRMWARE_EMUNAND4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
tempNand = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nandType == FIRMWARE_EMUNAND) nandType = tempNand;
|
|
||||||
else firmSource = tempNand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 pinMode = MULTICONFIG(PIN);
|
||||||
|
bool pinExists = pinMode != 0 && verifyPin(pinMode);
|
||||||
|
|
||||||
|
//If no configuration file exists or SELECT is held, load configuration menu
|
||||||
|
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & (BUTTON_SELECT | BUTTON_L1)) == BUTTON_SELECT);
|
||||||
|
|
||||||
|
if(shouldLoadConfigMenu)
|
||||||
|
{
|
||||||
|
configMenu(isSdMode, pinExists, pinMode);
|
||||||
|
|
||||||
|
//Update pressed buttons
|
||||||
|
pressed = HID_PAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ISA9LH && !CFG_BOOTENV && pressed == SAFE_MODE)
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_SYSNAND;
|
||||||
|
firmSource = FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
isSafeMode = true;
|
||||||
|
|
||||||
|
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
|
||||||
|
if(pinExists && !shouldLoadConfigMenu)
|
||||||
|
{
|
||||||
|
while(HID_PAD & PIN_BUTTONS);
|
||||||
|
wait(false, 2ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto boot;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 splashMode = MULTICONFIG(SPLASH);
|
||||||
|
|
||||||
|
if(splashMode == 1 && loadSplash()) pressed = HID_PAD;
|
||||||
|
|
||||||
|
if((pressed & (BUTTON_START | BUTTON_L1)) == BUTTON_START)
|
||||||
|
{
|
||||||
|
payloadMenu();
|
||||||
|
pressed = HID_PAD;
|
||||||
|
}
|
||||||
|
else if(((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||||
|
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1))) loadPayload(pressed, NULL);
|
||||||
|
|
||||||
|
if(splashMode == 2) loadSplash();
|
||||||
|
|
||||||
|
//If booting from CTRNAND, always use SysNAND
|
||||||
|
if(!isSdMode) nandType = FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
|
||||||
|
else if(pressed & BUTTON_R1)
|
||||||
|
{
|
||||||
|
if(CONFIG(USESYSFIRM))
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_EMUNAND;
|
||||||
|
firmSource = FIRMWARE_SYSNAND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nandType = FIRMWARE_SYSNAND;
|
||||||
|
firmSource = FIRMWARE_EMUNAND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
|
||||||
|
with their own FIRM */
|
||||||
|
else firmSource = nandType = (CONFIG(AUTOBOOTSYS) == ((pressed & BUTTON_L1) == BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||||
|
|
||||||
|
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
|
||||||
|
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
||||||
|
{
|
||||||
|
FirmwareSource tempNand;
|
||||||
|
switch(pressed & DPAD_BUTTONS)
|
||||||
|
{
|
||||||
|
case BUTTON_UP:
|
||||||
|
tempNand = FIRMWARE_EMUNAND;
|
||||||
|
break;
|
||||||
|
case BUTTON_RIGHT:
|
||||||
|
tempNand = FIRMWARE_EMUNAND2;
|
||||||
|
break;
|
||||||
|
case BUTTON_DOWN:
|
||||||
|
tempNand = FIRMWARE_EMUNAND3;
|
||||||
|
break;
|
||||||
|
case BUTTON_LEFT:
|
||||||
|
tempNand = FIRMWARE_EMUNAND4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tempNand = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(nandType == FIRMWARE_EMUNAND) nandType = tempNand;
|
||||||
|
else firmSource = tempNand;
|
||||||
|
}
|
||||||
|
|
||||||
|
boot:
|
||||||
|
|
||||||
//If we need to boot EmuNAND, make sure it exists
|
//If we need to boot EmuNAND, make sure it exists
|
||||||
if(nandType != FIRMWARE_SYSNAND)
|
if(nandType != FIRMWARE_SYSNAND)
|
||||||
{
|
{
|
||||||
|
589
source/patches.c
589
source/patches.c
@ -35,6 +35,29 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "../build/bundled.h"
|
#include "../build/bundled.h"
|
||||||
|
|
||||||
|
static inline void pathChanger(u8 *pos)
|
||||||
|
{
|
||||||
|
const char *pathFile = "path.txt";
|
||||||
|
|
||||||
|
u32 pathSize = getFileSize(pathFile);
|
||||||
|
|
||||||
|
if(pathSize < 6 || pathSize > 57) return;
|
||||||
|
|
||||||
|
u8 path[pathSize];
|
||||||
|
fileRead(path, pathFile, pathSize);
|
||||||
|
if(path[pathSize - 1] == 0xA) pathSize--;
|
||||||
|
if(path[pathSize - 1] == 0xD) pathSize--;
|
||||||
|
|
||||||
|
if(pathSize < 6 || pathSize > 57 || path[0] != '/' || memcmp(&path[pathSize - 4], ".bin", 4) != 0) return;
|
||||||
|
|
||||||
|
u16 finalPath[pathSize];
|
||||||
|
for(u32 i = 0; i < pathSize; i++)
|
||||||
|
finalPath[i] = (u16)path[i];
|
||||||
|
|
||||||
|
u8 *posPath = memsearch(pos, u"sd", reboot_bin_size, 4) + 0xA;
|
||||||
|
memcpy(posPath, finalPath, pathSize * 2);
|
||||||
|
}
|
||||||
|
|
||||||
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
u8 *getProcess9Info(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||||
{
|
{
|
||||||
u8 *temp = memsearch(pos, "NCCH", size, 4);
|
u8 *temp = memsearch(pos, "NCCH", size, 4);
|
||||||
@ -51,34 +74,26 @@ 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)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
|
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5},
|
||||||
bool res = true;
|
pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
|
||||||
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||||
|
|
||||||
|
if(*arm11ExceptionsPage == NULL || *freeK11Space == NULL) error("Failed to get Kernel11 data.");
|
||||||
|
|
||||||
u32 *arm11SvcTable;
|
u32 *arm11SvcTable;
|
||||||
|
|
||||||
if(*arm11ExceptionsPage == NULL) res = false;
|
*arm11ExceptionsPage -= 0xB;
|
||||||
else
|
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||||
{
|
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
||||||
*arm11ExceptionsPage -= 0xB;
|
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
||||||
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
arm11SvcTable = *arm11SvcHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
||||||
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
||||||
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
|
||||||
arm11SvcTable = *arm11SvcHandler = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
|
||||||
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
|
||||||
}
|
|
||||||
|
|
||||||
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
(*freeK11Space)++;
|
||||||
|
|
||||||
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2));
|
return arm11SvcTable;
|
||||||
|
|
||||||
if(*freeK11Space == NULL) res = false;
|
|
||||||
else (*freeK11Space)++;
|
|
||||||
|
|
||||||
if(!res) error("Failed to get Kernel11 data.");
|
|
||||||
|
|
||||||
return arm11SvcTable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchSignatureChecks(u8 *pos, u32 size)
|
u32 patchSignatureChecks(u8 *pos, u32 size)
|
||||||
@ -86,207 +101,139 @@ u32 patchSignatureChecks(u8 *pos, u32 size)
|
|||||||
//Look for signature checks
|
//Look for signature checks
|
||||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||||
|
|
||||||
if(off == NULL || temp == NULL) ret = 1;
|
if(off == NULL || temp == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
u16 *off2 = (u16 *)(temp - 1);
|
|
||||||
|
|
||||||
*off = off2[0] = 0x2000;
|
u16 *off2 = (u16 *)(temp - 1);
|
||||||
off2[1] = 0x4770;
|
*off = off2[0] = 0x2000;
|
||||||
|
off2[1] = 0x4770;
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
u32 patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||||
{
|
{
|
||||||
//Look for firmlaunch code
|
//Look for firmlaunch code
|
||||||
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
|
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off -= 0x13;
|
|
||||||
|
|
||||||
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
off -= 0x13;
|
||||||
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
|
||||||
|
|
||||||
//Copy firmlaunch code
|
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
||||||
memcpy(off, reboot_bin, reboot_bin_size);
|
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
||||||
|
|
||||||
//Put the fOpen offset in the right location
|
//Copy firmlaunch code
|
||||||
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
|
memcpy(off, reboot_bin, reboot_bin_size);
|
||||||
*pos_fopen = fOpenOffset;
|
|
||||||
|
|
||||||
ret = 0;
|
//Put the fOpen offset in the right location
|
||||||
|
u32 *pos_fopen = (u32 *)memsearch(off, "OPEN", reboot_bin_size, 4);
|
||||||
|
*pos_fopen = fOpenOffset;
|
||||||
|
|
||||||
if(CONFIG(USECUSTOMPATH))
|
if(CONFIG(USECUSTOMPATH)) pathChanger(off);
|
||||||
{
|
|
||||||
const char *pathFile = "path.txt";
|
|
||||||
|
|
||||||
u32 pathSize = getFileSize(pathFile);
|
return 0;
|
||||||
|
|
||||||
if(pathSize > 5 && pathSize < 58)
|
|
||||||
{
|
|
||||||
u8 path[pathSize];
|
|
||||||
fileRead(path, pathFile, pathSize);
|
|
||||||
if(path[pathSize - 1] == 0xA) pathSize--;
|
|
||||||
if(path[pathSize - 1] == 0xD) pathSize--;
|
|
||||||
|
|
||||||
if(pathSize > 5 && pathSize < 56 && path[0] == '/' && memcmp(&path[pathSize - 4], ".bin", 4) == 0)
|
|
||||||
{
|
|
||||||
u16 finalPath[pathSize];
|
|
||||||
for(u32 i = 0; i < pathSize; i++)
|
|
||||||
finalPath[i] = (u16)path[i];
|
|
||||||
|
|
||||||
u8 *pos_path = memsearch(off, u"sd", reboot_bin_size, 4) + 0xA;
|
|
||||||
memcpy(pos_path, finalPath, pathSize * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchFirmWrites(u8 *pos, u32 size)
|
u32 patchFirmWrites(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
//Look for FIRM writing code
|
//Look for FIRM writing code
|
||||||
u8 *off = memsearch(pos, "exe:", size, 4);
|
u8 *off = memsearch(pos, "exe:", size, 4);
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
|
||||||
|
|
||||||
u16 *off2 = (u16 *)memsearch(off - 0x100, pattern, 0x100, sizeof(pattern));
|
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||||
|
|
||||||
if(off2 == NULL) ret = 1;
|
u16 *off2 = (u16 *)memsearch(off - 0x100, pattern, 0x100, sizeof(pattern));
|
||||||
else
|
|
||||||
{
|
|
||||||
off2[0] = 0x2000;
|
|
||||||
off2[1] = 0x46C0;
|
|
||||||
|
|
||||||
ret = 0;
|
if(off2 == NULL) return 1;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
off2[0] = 0x2000;
|
||||||
|
off2[1] = 0x46C0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchOldFirmWrites(u8 *pos, u32 size)
|
u32 patchOldFirmWrites(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for FIRM writing code
|
//Look for FIRM writing code
|
||||||
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[0] = 0x2400;
|
|
||||||
off[1] = 0xE01D;
|
|
||||||
|
|
||||||
ret = 0;
|
off[0] = 0x2400;
|
||||||
}
|
off[1] = 0xE01D;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
|
u32 patchTitleInstallMinVersionChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0xFF, 0x00, 0x00, 0x02};
|
const u8 pattern[] = {0xFF, 0x00, 0x00, 0x02};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = firmVersion == 0xFFFFFFFF ? 0 : 1;
|
if(off == NULL) return firmVersion == 0xFFFFFFFF ? 0 : 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off++;
|
|
||||||
|
|
||||||
memset32(off, 0, 8); //Zero out the first TitleID in the list
|
off++;
|
||||||
|
|
||||||
ret = 0;
|
//Zero out the first TitleID in the list
|
||||||
}
|
memset32(off, 0, 8);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size)
|
u32 patchZeroKeyNcchEncryptionCheck(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x28, 0x2A, 0xD0, 0x08};
|
const u8 pattern[] = {0x28, 0x2A, 0xD0, 0x08};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(temp == NULL) ret = 1;
|
if(temp == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
u16 *off = (u16 *)(temp - 1);
|
|
||||||
|
|
||||||
*off = 0x2001; //mov r0, #1
|
u16 *off = (u16 *)(temp - 1);
|
||||||
|
*off = 0x2001; //mov r0, #1
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchNandNcchEncryptionCheck(u8 *pos, u32 size)
|
u32 patchNandNcchEncryptionCheck(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x07, 0xD1, 0x28, 0x7A};
|
const u8 pattern[] = {0x07, 0xD1, 0x28, 0x7A};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off--;
|
|
||||||
|
|
||||||
*off = 0x2001; //mov r0, #1
|
off--;
|
||||||
|
*off = 0x2001; //mov r0, #1
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchCheckForDevCommonKey(u8 *pos, u32 size)
|
u32 patchCheckForDevCommonKey(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x03, 0x7C, 0x28, 0x00};
|
const u8 pattern[] = {0x03, 0x7C, 0x28, 0x00};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
*off = 0x2301; //mov r3, #1
|
|
||||||
|
|
||||||
ret = 0;
|
*off = 0x2301; //mov r3, #1
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||||
{
|
{
|
||||||
u32 ret = 0;
|
if(arm11SvcTable[0x7B] != 0) return 0;
|
||||||
|
|
||||||
//Official implementation of svcBackdoor
|
//Official implementation of svcBackdoor
|
||||||
const u8 svcBackdoor[] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
const u8 svcBackdoor[] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
||||||
@ -300,137 +247,107 @@ u32 reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **free
|
|||||||
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
||||||
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
||||||
|
|
||||||
if(!arm11SvcTable[0x7B])
|
if(*(u32 *)(*freeK11Space + sizeof(svcBackdoor) - 4) != 0xFFFFFFFF) return 1;
|
||||||
{
|
|
||||||
if(*(u32 *)(*freeK11Space + sizeof(svcBackdoor) - 4) != 0xFFFFFFFF) ret = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(*freeK11Space, svcBackdoor, sizeof(svcBackdoor));
|
|
||||||
|
|
||||||
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
|
memcpy(*freeK11Space, svcBackdoor, sizeof(svcBackdoor));
|
||||||
*freeK11Space += sizeof(svcBackdoor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
|
||||||
|
*freeK11Space += sizeof(svcBackdoor);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ret;
|
if(*(u32 *)(*freeK11Space + svcGetCFWInfo_bin_size - 4) != 0xFFFFFFFF) return 1;
|
||||||
|
|
||||||
if(*(u32 *)(*freeK11Space + svcGetCFWInfo_bin_size - 4) != 0xFFFFFFFF) ret = 1;
|
memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
|
||||||
else
|
|
||||||
|
struct CfwInfo
|
||||||
{
|
{
|
||||||
memcpy(*freeK11Space, svcGetCFWInfo_bin, svcGetCFWInfo_bin_size);
|
char magic[4];
|
||||||
|
|
||||||
struct CfwInfo
|
u8 versionMajor;
|
||||||
{
|
u8 versionMinor;
|
||||||
char magic[4];
|
u8 versionBuild;
|
||||||
|
u8 flags;
|
||||||
|
|
||||||
u8 versionMajor;
|
u32 commitHash;
|
||||||
u8 versionMinor;
|
|
||||||
u8 versionBuild;
|
|
||||||
u8 flags;
|
|
||||||
|
|
||||||
u32 commitHash;
|
u32 config;
|
||||||
|
} __attribute__((packed)) *info = (struct CfwInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_bin_size, 4);
|
||||||
|
|
||||||
u32 config;
|
const char *rev = REVISION;
|
||||||
} __attribute__((packed)) *info = (struct CfwInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_bin_size, 4);
|
|
||||||
|
|
||||||
const char *rev = REVISION;
|
info->commitHash = COMMIT_HASH;
|
||||||
|
info->config = configData.config;
|
||||||
|
info->versionMajor = (u8)(rev[1] - '0');
|
||||||
|
info->versionMinor = (u8)(rev[3] - '0');
|
||||||
|
|
||||||
info->commitHash = COMMIT_HASH;
|
bool isRelease;
|
||||||
info->config = configData.config;
|
|
||||||
info->versionMajor = (u8)(rev[1] - '0');
|
|
||||||
info->versionMinor = (u8)(rev[3] - '0');
|
|
||||||
|
|
||||||
bool isRelease;
|
if(rev[4] == '.')
|
||||||
|
{
|
||||||
if(rev[4] == '.')
|
info->versionBuild = (u8)(rev[5] - '0');
|
||||||
{
|
isRelease = rev[6] == 0;
|
||||||
info->versionBuild = (u8)(rev[5] - '0');
|
|
||||||
isRelease = rev[6] == 0;
|
|
||||||
}
|
|
||||||
else isRelease = rev[4] == 0;
|
|
||||||
|
|
||||||
if(isRelease) info->flags = 1;
|
|
||||||
|
|
||||||
if(ISN3DS) info->flags |= 1 << 4;
|
|
||||||
|
|
||||||
if(isSafeMode) info->flags |= 1 << 5;
|
|
||||||
|
|
||||||
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
|
|
||||||
*freeK11Space += svcGetCFWInfo_bin_size;
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
else isRelease = rev[4] == 0;
|
||||||
|
|
||||||
return ret;
|
if(isRelease) info->flags = 1;
|
||||||
|
if(ISN3DS) info->flags |= 1 << 4;
|
||||||
|
if(isSafeMode) info->flags |= 1 << 5;
|
||||||
|
|
||||||
|
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
|
||||||
|
*freeK11Space += svcGetCFWInfo_bin_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(temp == NULL) ret = 1;
|
if(temp == NULL) return 1;
|
||||||
else
|
|
||||||
|
u32 *off = (u32 *)(temp - 0xA);
|
||||||
|
|
||||||
|
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
||||||
{
|
{
|
||||||
u32 *off = (u32 *)(temp - 0xA);
|
//Discard everything that's not str rX, [r0, #imm](!)
|
||||||
|
if((*off & 0xFE5F0000) != 0xE4000000) continue;
|
||||||
|
|
||||||
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
u32 rD = (*off >> 12) & 0xF,
|
||||||
{
|
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
||||||
//Discard everything that's not str rX, [r0, #imm](!)
|
bool writeback = ((*off >> 21) & 1) != 0,
|
||||||
if((*off & 0xFE5F0000) == 0xE4000000)
|
pre = ((*off >> 24) & 1) != 0;
|
||||||
{
|
|
||||||
u32 rD = (*off >> 12) & 0xF,
|
|
||||||
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
|
||||||
bool writeback = ((*off >> 21) & 1) != 0,
|
|
||||||
pre = ((*off >> 24) & 1) != 0;
|
|
||||||
|
|
||||||
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
|
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
|
||||||
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
|
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
|
||||||
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
|
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
|
||||||
|
|
||||||
if(!pre) addr += offset;
|
if(!pre) addr += offset;
|
||||||
if(writeback) r0 = addr;
|
if(writeback) r0 = addr;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
|
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x1B, 0x50, 0xA0, 0xE3}, //Get TitleID from CodeSet
|
const u8 pattern[] = {0x1B, 0x50, 0xA0, 0xE3}, //Get TitleID from CodeSet
|
||||||
pattern2[] = {0xE8, 0x13, 0x00, 0x02}; //Call exception dispatcher
|
pattern2[] = {0xE8, 0x13, 0x00, 0x02}; //Call exception dispatcher
|
||||||
bool ret = true;
|
|
||||||
|
|
||||||
u32 *loadCodeSet = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u32 *loadCodeSet = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(loadCodeSet == NULL) ret = false;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
loadCodeSet -= 2;
|
|
||||||
*codeSetOffset = *loadCodeSet & 0xFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
u8 *temp = memsearch(pos, pattern2, size, sizeof(pattern2));
|
||||||
|
|
||||||
u32 stackAddress;
|
if(loadCodeSet == NULL || temp == NULL) error("Failed to get ARM11 exception handlers data.");
|
||||||
|
|
||||||
if(temp == NULL) ret = false;
|
loadCodeSet -= 2;
|
||||||
else stackAddress = *(u32 *)(temp + 9);
|
*codeSetOffset = *loadCodeSet & 0xFFF;
|
||||||
|
|
||||||
if(!ret) error("Failed to get ARM11 exception handlers data.");
|
return *(u32 *)(temp + 9);
|
||||||
|
|
||||||
return stackAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
||||||
@ -439,22 +356,17 @@ u32 patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
|||||||
|
|
||||||
//Look for the svc handler
|
//Look for the svc handler
|
||||||
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
|
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(arm9SvcTable == NULL) ret = 1;
|
if(arm9SvcTable == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
while(*arm9SvcTable != 0) arm9SvcTable++; //Look for SVC0 (NULL)
|
|
||||||
|
|
||||||
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
while(*arm9SvcTable != 0) arm9SvcTable++; //Look for SVC0 (NULL)
|
||||||
*addr = 0xE12FFF7F;
|
|
||||||
|
|
||||||
ret = 0;
|
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
||||||
}
|
*addr = 0xE12FFF7F;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
||||||
@ -467,76 +379,54 @@ void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
|||||||
u32 patchKernel9Panic(u8 *pos, u32 size)
|
u32 patchKernel9Panic(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
|
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(temp == NULL) ret = 1;
|
if(temp == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
u32 *off = (u32 *)(temp - 0x12);
|
|
||||||
*off = 0xE12FFF7E;
|
|
||||||
|
|
||||||
ret = 0;
|
u32 *off = (u32 *)(temp - 0x12);
|
||||||
}
|
*off = 0xE12FFF7E;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchKernel11Panic(u8 *pos, u32 size)
|
u32 patchKernel11Panic(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
|
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
*off = 0xE12FFF7E;
|
|
||||||
|
|
||||||
ret = 0;
|
*off = 0xE12FFF7E;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchP9AccessChecks(u8 *pos, u32 size)
|
u32 patchP9AccessChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x00, 0x08, 0x49, 0x68};
|
const u8 pattern[] = {0x00, 0x08, 0x49, 0x68};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(temp == NULL) ret = 1;
|
if(temp == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
u16 *off = (u16 *)(temp - 3);
|
|
||||||
|
|
||||||
off[0] = 0x2001; //mov r0, #1
|
u16 *off = (u16 *)(temp - 3);
|
||||||
off[1] = 0x4770; //bx lr
|
off[0] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = 0x4770; //bx lr
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos)
|
u32 patchArm11SvcAccessChecks(u32 *arm11SvcHandler, u32 *endPos)
|
||||||
{
|
{
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
while(*arm11SvcHandler != 0xE11A0E1B && arm11SvcHandler < endPos) arm11SvcHandler++; //TST R10, R11,LSL LR
|
while(*arm11SvcHandler != 0xE11A0E1B && arm11SvcHandler < endPos) arm11SvcHandler++; //TST R10, R11,LSL LR
|
||||||
|
|
||||||
if(arm11SvcHandler == endPos) ret = 1;
|
if(arm11SvcHandler == endPos) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
|
||||||
|
|
||||||
ret = 0;
|
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
||||||
@ -544,214 +434,159 @@ u32 patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
|||||||
/* We have to detour a function in the ARM11 kernel because builtin modules
|
/* We have to detour a function in the ARM11 kernel because builtin modules
|
||||||
are compressed in memory and are only decompressed at runtime */
|
are compressed in memory and are only decompressed at runtime */
|
||||||
|
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
//Check that we have enough free space
|
//Check that we have enough free space
|
||||||
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) ret = 0;
|
if(*(u32 *)(*freeK11Space + k11modules_bin_size - 4) != 0xFFFFFFFF) return 0;
|
||||||
else
|
|
||||||
{
|
|
||||||
//Look for the code that decompresses the .code section of the builtin modules
|
|
||||||
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
//Look for the code that decompresses the .code section of the builtin modules
|
||||||
|
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
||||||
|
|
||||||
if(temp == NULL) ret = 1;
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
else
|
|
||||||
{
|
|
||||||
//Inject our code into the free space
|
|
||||||
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
|
|
||||||
|
|
||||||
u32 *off = (u32 *)(temp - 0xB);
|
if(temp == NULL) return 1;
|
||||||
|
|
||||||
//Inject a jump (BL) instruction to our code at the offset we found
|
//Inject our code into the free space
|
||||||
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
|
memcpy(*freeK11Space, k11modules_bin, k11modules_bin_size);
|
||||||
|
|
||||||
*freeK11Space += k11modules_bin_size;
|
u32 *off = (u32 *)(temp - 0xB);
|
||||||
|
|
||||||
ret = 0;
|
//Inject a jump (BL) instruction to our code at the offset we found
|
||||||
}
|
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
*freeK11Space += k11modules_bin_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchUnitInfoValueSet(u8 *pos, u32 size)
|
u32 patchUnitInfoValueSet(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
//Look for UNITINFO value being set during kernel sync
|
//Look for UNITINFO value being set during kernel sync
|
||||||
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
|
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[0] = ISDEVUNIT ? 0 : 1;
|
|
||||||
off[3] = 0xE3;
|
|
||||||
|
|
||||||
ret = 0;
|
off[0] = ISDEVUNIT ? 0 : 1;
|
||||||
}
|
off[3] = 0xE3;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchLgySignatureChecks(u8 *pos, u32 size)
|
u32 patchLgySignatureChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x47, 0xC1, 0x17, 0x49};
|
const u8 pattern[] = {0x47, 0xC1, 0x17, 0x49};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(temp == NULL) ret = 1;
|
if(temp == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
u16 *off = (u16 *)(temp + 1);
|
|
||||||
|
|
||||||
off[0] = 0x2000;
|
u16 *off = (u16 *)(temp + 1);
|
||||||
off[1] = 0xB04E;
|
off[0] = 0x2000;
|
||||||
off[2] = 0xBD70;
|
off[1] = 0xB04E;
|
||||||
|
off[2] = 0xBD70;
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size)
|
u32 patchTwlInvalidSignatureChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x20, 0xF6, 0xE7, 0x7F};
|
const u8 pattern[] = {0x20, 0xF6, 0xE7, 0x7F};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(temp == NULL) ret = 1;
|
if(temp == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
u16 *off = (u16 *)(temp - 1);
|
|
||||||
|
|
||||||
*off = 0x2001; //mov r0, #1
|
u16 *off = (u16 *)(temp - 1);
|
||||||
|
*off = 0x2001; //mov r0, #1
|
||||||
|
|
||||||
ret = 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size)
|
u32 patchTwlNintendoLogoChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0xC0, 0x30, 0x06, 0xF0};
|
const u8 pattern[] = {0xC0, 0x30, 0x06, 0xF0};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[1] = 0x2000;
|
|
||||||
off[2] = 0;
|
|
||||||
|
|
||||||
ret = 0;
|
off[1] = 0x2000;
|
||||||
}
|
off[2] = 0;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchTwlWhitelistChecks(u8 *pos, u32 size)
|
u32 patchTwlWhitelistChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x22, 0x00, 0x20, 0x30};
|
const u8 pattern[] = {0x22, 0x00, 0x20, 0x30};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[2] = 0x2000;
|
|
||||||
off[3] = 0;
|
|
||||||
|
|
||||||
ret = 0;
|
off[2] = 0x2000;
|
||||||
}
|
off[3] = 0;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion)
|
u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x25, 0x20, 0x00, 0x0E};
|
const u8 pattern[] = {0x25, 0x20, 0x00, 0x0E};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *temp = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(temp == NULL)
|
if(temp == NULL)
|
||||||
{
|
{
|
||||||
if(firmVersion == 0xFFFFFFFF) ret = patchOldTwlFlashcartChecks(pos, size);
|
if(firmVersion == 0xFFFFFFFF) return patchOldTwlFlashcartChecks(pos, size);
|
||||||
else ret = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
u16 *off = (u16 *)(temp + 3);
|
|
||||||
|
|
||||||
off[0] = off[6] = off[0xC] = 0x2001; //mov r0, #1
|
return 1;
|
||||||
off[1] = off[7] = off[0xD] = 0; //nop
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
u16 *off = (u16 *)(temp + 3);
|
||||||
|
off[0] = off[6] = off[0xC] = 0x2001; //mov r0, #1
|
||||||
|
off[1] = off[7] = off[0xD] = 0; //nop
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size)
|
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x06, 0xF0, 0xA0, 0xFD};
|
const u8 pattern[] = {0x06, 0xF0, 0xA0, 0xFD};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[0] = off[6] = 0x2001; //mov r0, #1
|
|
||||||
off[1] = off[7] = 0; //nop
|
|
||||||
|
|
||||||
ret = 0;
|
off[0] = off[6] = 0x2001; //mov r0, #1
|
||||||
}
|
off[1] = off[7] = 0; //nop
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchTwlShaHashChecks(u8 *pos, u32 size)
|
u32 patchTwlShaHashChecks(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x10, 0xB5, 0x14, 0x22};
|
const u8 pattern[] = {0x10, 0xB5, 0x14, 0x22};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[0] = 0x2001; //mov r0, #1
|
|
||||||
off[1] = 0x4770;
|
|
||||||
|
|
||||||
ret = 0;
|
off[0] = 0x2001; //mov r0, #1
|
||||||
}
|
off[1] = 0x4770;
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patchAgbBootSplash(u8 *pos, u32 size)
|
u32 patchAgbBootSplash(u8 *pos, u32 size)
|
||||||
{
|
{
|
||||||
const u8 pattern[] = {0x00, 0x00, 0x01, 0xEF};
|
const u8 pattern[] = {0x00, 0x00, 0x01, 0xEF};
|
||||||
u32 ret;
|
|
||||||
|
|
||||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||||
|
|
||||||
if(off == NULL) ret = 1;
|
if(off == NULL) return 1;
|
||||||
else
|
|
||||||
{
|
|
||||||
off[2] = 0x26;
|
|
||||||
|
|
||||||
ret = 0;
|
off[2] = 0x26;
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
53
source/pin.c
53
source/pin.c
@ -85,16 +85,20 @@ void newPin(bool allowSkipping, u32 pinMode)
|
|||||||
|
|
||||||
if(pressed & BUTTON_START) return;
|
if(pressed & BUTTON_START) return;
|
||||||
|
|
||||||
if(pressed & BUTTON_SELECT) reset = true;
|
if(pressed & BUTTON_SELECT)
|
||||||
else if(pressed != 0)
|
|
||||||
{
|
{
|
||||||
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Add character to password
|
reset = true;
|
||||||
|
continue;
|
||||||
//Visualize character on screen
|
|
||||||
drawCharacter(enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
|
||||||
|
|
||||||
cnt++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!pressed) continue;
|
||||||
|
|
||||||
|
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Add character to password
|
||||||
|
|
||||||
|
//Visualize character on screen
|
||||||
|
drawCharacter(enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||||
|
|
||||||
|
cnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
PinData pin;
|
PinData pin;
|
||||||
@ -188,26 +192,29 @@ bool verifyPin(u32 pinMode)
|
|||||||
|
|
||||||
pressed &= PIN_BUTTONS;
|
pressed &= PIN_BUTTONS;
|
||||||
|
|
||||||
if(pressed & BUTTON_SELECT) reset = true;
|
if(pressed & BUTTON_SELECT)
|
||||||
else if(pressed != 0)
|
|
||||||
{
|
{
|
||||||
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Add character to password
|
reset = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
//Visualize character on screen
|
if(!pressed) continue;
|
||||||
drawCharacter((char)enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
|
||||||
|
|
||||||
if(++cnt >= lengthBlock[0])
|
enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Add character to password
|
||||||
{
|
|
||||||
computePinHash(tmp, enteredPassword);
|
|
||||||
unlock = memcmp(pin.hash, tmp, sizeof(tmp)) == 0;
|
|
||||||
|
|
||||||
if(!unlock)
|
//Visualize character on screen
|
||||||
{
|
drawCharacter((char)enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE);
|
||||||
reset = true;
|
|
||||||
|
|
||||||
drawString("Wrong PIN, try again", true, 10, 10 + 5 * SPACING_Y, COLOR_RED);
|
if(++cnt < lengthBlock[0]) continue;
|
||||||
}
|
|
||||||
}
|
computePinHash(tmp, enteredPassword);
|
||||||
|
unlock = memcmp(pin.hash, tmp, sizeof(tmp)) == 0;
|
||||||
|
|
||||||
|
if(!unlock)
|
||||||
|
{
|
||||||
|
reset = true;
|
||||||
|
|
||||||
|
drawString("Wrong PIN, try again", true, 10, 10 + 5 * SPACING_Y, COLOR_RED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ void __attribute__((naked)) arm11Stub(void)
|
|||||||
static void invokeArm11Function(void (*func)())
|
static void invokeArm11Function(void (*func)())
|
||||||
{
|
{
|
||||||
static bool hasCopiedStub = false;
|
static bool hasCopiedStub = false;
|
||||||
|
|
||||||
if(!hasCopiedStub)
|
if(!hasCopiedStub)
|
||||||
{
|
{
|
||||||
memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x30);
|
memcpy((void *)ARM11_STUB_ADDRESS, arm11Stub, 0x30);
|
||||||
|
@ -56,7 +56,6 @@ static u64 chrono(bool isMilliseconds)
|
|||||||
u32 waitInput(bool isMenu)
|
u32 waitInput(bool isMenu)
|
||||||
{
|
{
|
||||||
static u64 dPadDelay = 0ULL;
|
static u64 dPadDelay = 0ULL;
|
||||||
bool pressedKey = false;
|
|
||||||
u32 key,
|
u32 key,
|
||||||
oldKey = HID_PAD;
|
oldKey = HID_PAD;
|
||||||
|
|
||||||
@ -66,23 +65,24 @@ u32 waitInput(bool isMenu)
|
|||||||
startChrono();
|
startChrono();
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!pressedKey)
|
while(true)
|
||||||
{
|
{
|
||||||
key = HID_PAD;
|
key = HID_PAD;
|
||||||
|
|
||||||
if(!key)
|
if(!key)
|
||||||
{
|
{
|
||||||
if(i2cReadRegister(I2C_DEV_MCU, 0x10) == 1) mcuPowerOff();
|
if(i2cReadRegister(I2C_DEV_MCU, 0x10) == 1) mcuPowerOff();
|
||||||
oldKey = key;
|
oldKey = 0;
|
||||||
dPadDelay = 0;
|
dPadDelay = 0;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
else if((key != oldKey) || (isMenu && (key & DPAD_BUTTONS) != 0 && (chrono(true) >= dPadDelay)))
|
|
||||||
{
|
if(key == oldKey && (!isMenu || (!(key & DPAD_BUTTONS) || chrono(true) < dPadDelay))) continue;
|
||||||
//Make sure the key is pressed
|
|
||||||
u32 i;
|
//Make sure the key is pressed
|
||||||
for(i = 0; i < 0x13000 && key == HID_PAD; i++);
|
u32 i;
|
||||||
if(i == 0x13000) pressedKey = true;
|
for(i = 0; i < 0x13000 && key == HID_PAD; i++);
|
||||||
}
|
if(i == 0x13000) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
|
Reference in New Issue
Block a user