From 43d241cee19e48e0fb8f7271c4bc5f32bd764dca Mon Sep 17 00:00:00 2001 From: Popax21 Date: Tue, 10 Jan 2023 21:16:45 +0100 Subject: [PATCH 1/3] Fix FSPXI:ReadFileSHA256 error when reading exactly one block from NoCrypto NCCH ExeFS files Process9's implementation of FSPXI:ReadFileSHA256 uses an auxiliary buffer object when exactly one block of data is being read, which reads the data and hashes it at the same time. This object's vtable[11] is stubbed (returning error 0xe0c046f8), but can still end up being called when reading from an NCCH ExeFS file with the NoCrypto flag set. This patch addresses this by replacing the stubbed implementation with a custom working one (see issue #1827 + related PR for more details) --- arm9/linker.ld | 1 + arm9/source/firm.c | 3 ++ arm9/source/large_patches.h | 4 +++ arm9/source/large_patches.s | 57 +++++++++++++++++++++++++++++++ arm9/source/patches.c | 68 +++++++++++++++++++++++++++++++++++++ arm9/source/patches.h | 1 + 6 files changed, 134 insertions(+) diff --git a/arm9/linker.ld b/arm9/linker.ld index aa5ac8288..ee0f66ced 100644 --- a/arm9/linker.ld +++ b/arm9/linker.ld @@ -46,6 +46,7 @@ SECTIONS chainloader.o(.text*) i2c.o(.text*) arm9_exception_handlers.o(.text*) + *(.large_patch.readFileSHA256Vtab11) KEEP (*(.emunand_patch)) *(.arm9_exception_handlers.rodata*) diff --git a/arm9/source/firm.c b/arm9/source/firm.c index 60044e454..fe18026f3 100755 --- a/arm9/source/firm.c +++ b/arm9/source/firm.c @@ -587,6 +587,9 @@ u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStora ret += patchP9AccessChecks(process9Offset, process9Size); + //Patch stubbed vtable function 11 of an FSPXI:ReadFileSHA256 auxiliary object + ret += patchReadFileSHA256Vtab11(process9Offset, process9Size, process9MemAddr); + mergeSection0(NATIVE_FIRM, firmVersion, loadFromStorage); firm->section[0].size = 0; diff --git a/arm9/source/large_patches.h b/arm9/source/large_patches.h index 6c99243b4..0af83ac04 100644 --- a/arm9/source/large_patches.h +++ b/arm9/source/large_patches.h @@ -36,3 +36,7 @@ extern const u8 rebootPatch[]; extern const u32 rebootPatchSize; extern u32 rebootPatchFopenPtr; extern u16 rebootPatchFileName[80+1]; + +extern const u8 readFileSHA256Vtab11Patch[]; +extern const u32 readFileSHA256Vtab11PatchSize; +extern u32 readFileSHA256Vtab11PatchCtorPtr, readFileSHA256Vtab11PatchInitPtr, readFileSHA256Vtab11PatchProcessPtr; \ No newline at end of file diff --git a/arm9/source/large_patches.s b/arm9/source/large_patches.s index c66f63b5f..4a4119e58 100644 --- a/arm9/source/large_patches.s +++ b/arm9/source/large_patches.s @@ -225,3 +225,60 @@ _rebootPatchEnd: .global rebootPatchSize rebootPatchSize: .word _rebootPatchEnd - rebootPatch + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +.section .large_patch.readFileSHA256Vtab11, "aw", %progbits +.arm +.align 4 + +.global readFileSHA256Vtab11Patch +readFileSHA256Vtab11Patch: //Result Write(this, u32 off, u8 *data, u32 size) + push {r0-r4, lr} + + sub sp, #0x14 + push {sp} + + @ DataChainProcessor::DataChainProcessor(&proc); + ldr r0, [sp] + ldr r4, readFileSHA256Vtab11PatchCtorPtr + blx r4 + + @ DataChainProcessor::Init(&proc, data, size); + ldr r0, [sp] + ldr r1, [sp, #0x20] + ldr r2, [sp, #0x24] + ldr r4, readFileSHA256Vtab11PatchInitPtr + blx r4 + + @ DataChainProcessor::Process(&proc, this, off, 0, size); + ldr r0, [sp] + + ldr r2, [sp, #0x24] + str r2, [sp] @ Set size field on stack + + ldr r1, [sp, #0x18] + ldr r2, [sp, #0x1c] + mov r3, #0 + ldr r4, readFileSHA256Vtab11PatchProcessPtr + blx r4 + + add sp, #0x18 + pop {r0-r4, pc} + +.global readFileSHA256Vtab11PatchCtorPtr +.global readFileSHA256Vtab11PatchInitPtr +.global readFileSHA256Vtab11PatchProcessPtr + +readFileSHA256Vtab11PatchCtorPtr: .word 0 @ Pointer to DataChainProcessor::DataChainProcessor +readFileSHA256Vtab11PatchInitPtr: .word 0 @ Pointer to DataChainProcessor::Init +readFileSHA256Vtab11PatchProcessPtr: .word 0 @ Pointer to DataChainProcessor::ProcessBytes + +.pool +.balign 4 + +_readFileSHA256Vtab11PatchEnd: + +.global readFileSHA256Vtab11PatchSize +readFileSHA256Vtab11PatchSize: + .word _readFileSHA256Vtab11PatchEnd - readFileSHA256Vtab11Patch \ No newline at end of file diff --git a/arm9/source/patches.c b/arm9/source/patches.c index f1d7589ff..1e1d5e0d9 100644 --- a/arm9/source/patches.c +++ b/arm9/source/patches.c @@ -847,3 +847,71 @@ u32 patchLgyK11(u8 *section1, u32 section1Size, u8 *section2, u32 section2Size) return 0; } + +static bool getVtableAddress(u8 *pos, u32 size, const u8 *pattern, u32 patternSize, s32 patternOff, u32 memAddr, u32 *vtableAddr) +{ + u8 *tmp = memsearch(pos, pattern, size, patternSize); + + if(tmp == NULL) return false; + + u16 *instrPtr = (u16 *)(tmp + patternOff); //ldr rX, [PTR_TO_VTABLE] + if((*instrPtr & 0xf800) != 0x4800) return false; //Check the instruction opcode + + *vtableAddr = *(u32 *)(((u32)instrPtr + 4 + 4*(*instrPtr & 0xff)) & ~0x3); + + if(*vtableAddr < memAddr || *vtableAddr >= memAddr + size) return false; + + return true; +} + +u32 patchReadFileSHA256Vtab11(u8 *pos, u32 size, u32 process9MemAddr) +{ + static const u8 ncchVtableRefPattern[] = {0x77, 0x46, 0x06, 0x74, 0x47, 0x74}; + static const u8 shaVtableRefPattern[] = {0x00, 0x1f, 0x01, 0x60, 0x20, 0x30}; + + u32 ncchVtableAddr; + if(!getVtableAddress(pos, size, ncchVtableRefPattern, sizeof(ncchVtableRefPattern), sizeof(ncchVtableRefPattern), process9MemAddr, &ncchVtableAddr)) return 1; + + u32 shaVtableAddr; + if(!getVtableAddress(pos, size, shaVtableRefPattern, sizeof(shaVtableRefPattern), -2, process9MemAddr, &shaVtableAddr)) return 1; + + u32 *ncchVtable11Ptr = (u32 *)(pos + (ncchVtableAddr - process9MemAddr)) + 11, + *shaVtable11Ptr = (u32 *)(pos + (shaVtableAddr - process9MemAddr)) + 11; + + if((*ncchVtable11Ptr & 0x1) == 0 || (*shaVtable11Ptr & 0x1) == 0) return 1; //Must be Thumb + + //Find our function address by inspecting all bl branch targets + u16 *ncchWriteFnc = (u16 *)(pos + ((*ncchVtable11Ptr & ~0x1) - process9MemAddr)); + if(*(u32 *)ncchWriteFnc != 0x0005b5f0) return 1; //Check if we got the right function + + readFileSHA256Vtab11PatchCtorPtr = readFileSHA256Vtab11PatchInitPtr = readFileSHA256Vtab11PatchProcessPtr = 0; + + for(; ((*ncchWriteFnc) & 0xff00) != 0xbd00; ncchWriteFnc++) { + if((ncchWriteFnc[0] & 0xf800) != 0xf000 || (ncchWriteFnc[1] & 0xf800) != 0xf800) continue; //Check the instruction opcode + + s32 callOff = ((ncchWriteFnc[0] & 0x07ff) << 11) | (ncchWriteFnc[1] & 0x07ff); + callOff = (callOff & 0x1fffff) - (callOff & 0x200000); + + u32 callTargetAddr = process9MemAddr + ((u8 *)ncchWriteFnc - pos) + 4 + 2*callOff; + + if(callTargetAddr < process9MemAddr || callTargetAddr >= process9MemAddr + size) return false; + + switch(*(u32 *)(pos + (callTargetAddr - process9MemAddr))) { + case 0x60422201: //DataChainProcessor::DataChainProcessor + readFileSHA256Vtab11PatchCtorPtr = callTargetAddr | 0x1; + break; + case 0x000db538: //DataChainProcessor::Init + readFileSHA256Vtab11PatchInitPtr = callTargetAddr | 0x1; + break; + case 0x0006b5f0: //DataChainProcessor::ProcessBytes + readFileSHA256Vtab11PatchProcessPtr = callTargetAddr | 0x1; + break; + } + } + + if(readFileSHA256Vtab11PatchCtorPtr == 0 || readFileSHA256Vtab11PatchInitPtr == 0 || readFileSHA256Vtab11PatchProcessPtr == 0) return 1; + + *shaVtable11Ptr = (u32) &readFileSHA256Vtab11Patch; //The patched vtable11 function is in ITCM, so we don't have to copy it somewhere else + + return 0; +} \ No newline at end of file diff --git a/arm9/source/patches.h b/arm9/source/patches.h index b77d092a2..57545f657 100644 --- a/arm9/source/patches.h +++ b/arm9/source/patches.h @@ -68,3 +68,4 @@ u32 patchTwlShaHashChecks(u8 *pos, u32 size); u32 patchAgbBootSplash(u8 *pos, u32 size); void patchTwlBg(u8 *pos, u32 size); // silently fails u32 patchLgyK11(u8 *section1, u32 section1Size, u8 *section2, u32 section2Size); +u32 patchReadFileSHA256Vtab11(u8 *pos, u32 size, u32 process9MemAddr); From da7740e6ec17b08f4381e8299bca53398f178b53 Mon Sep 17 00:00:00 2001 From: Popax21 Date: Wed, 11 Jan 2023 12:06:43 +0100 Subject: [PATCH 2/3] Clean up ReadFileSHA256 patch comments --- arm9/source/large_patches.s | 5 +++-- arm9/source/patches.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arm9/source/large_patches.s b/arm9/source/large_patches.s index 4a4119e58..74f1ae07b 100644 --- a/arm9/source/large_patches.s +++ b/arm9/source/large_patches.s @@ -236,6 +236,7 @@ rebootPatchSize: readFileSHA256Vtab11Patch: //Result Write(this, u32 off, u8 *data, u32 size) push {r0-r4, lr} + @ Allocate memory for DataChainProcessor struct sub sp, #0x14 push {sp} @@ -254,8 +255,8 @@ readFileSHA256Vtab11Patch: //Result Write(this, u32 off, u8 *data, u32 size) @ DataChainProcessor::Process(&proc, this, off, 0, size); ldr r0, [sp] - ldr r2, [sp, #0x24] - str r2, [sp] @ Set size field on stack + ldr r1, [sp, #0x24] + str r1, [sp] @ size argument ldr r1, [sp, #0x18] ldr r2, [sp, #0x1c] diff --git a/arm9/source/patches.c b/arm9/source/patches.c index 1e1d5e0d9..96358dea6 100644 --- a/arm9/source/patches.c +++ b/arm9/source/patches.c @@ -880,13 +880,13 @@ u32 patchReadFileSHA256Vtab11(u8 *pos, u32 size, u32 process9MemAddr) if((*ncchVtable11Ptr & 0x1) == 0 || (*shaVtable11Ptr & 0x1) == 0) return 1; //Must be Thumb - //Find our function address by inspecting all bl branch targets + //Find needed function addresses by inspecting all bl branch targets u16 *ncchWriteFnc = (u16 *)(pos + ((*ncchVtable11Ptr & ~0x1) - process9MemAddr)); if(*(u32 *)ncchWriteFnc != 0x0005b5f0) return 1; //Check if we got the right function readFileSHA256Vtab11PatchCtorPtr = readFileSHA256Vtab11PatchInitPtr = readFileSHA256Vtab11PatchProcessPtr = 0; - for(; ((*ncchWriteFnc) & 0xff00) != 0xbd00; ncchWriteFnc++) { + for(; ((*ncchWriteFnc) & 0xff00) != 0xbd00; ncchWriteFnc++) { //Stop whe encountering a pop {..., pc} if((ncchWriteFnc[0] & 0xf800) != 0xf000 || (ncchWriteFnc[1] & 0xf800) != 0xf800) continue; //Check the instruction opcode s32 callOff = ((ncchWriteFnc[0] & 0x07ff) << 11) | (ncchWriteFnc[1] & 0x07ff); From 3d3f05961c0f50d3d81987b3324fce82bb3f55cd Mon Sep 17 00:00:00 2001 From: Popax21 Date: Mon, 16 Jan 2023 16:03:08 +0100 Subject: [PATCH 3/3] Patch additional ReadFileSHA256 vtable method --- arm9/source/patches.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/arm9/source/patches.c b/arm9/source/patches.c index 96358dea6..e97605d78 100644 --- a/arm9/source/patches.c +++ b/arm9/source/patches.c @@ -867,18 +867,23 @@ static bool getVtableAddress(u8 *pos, u32 size, const u8 *pattern, u32 patternSi u32 patchReadFileSHA256Vtab11(u8 *pos, u32 size, u32 process9MemAddr) { static const u8 ncchVtableRefPattern[] = {0x77, 0x46, 0x06, 0x74, 0x47, 0x74}; - static const u8 shaVtableRefPattern[] = {0x00, 0x1f, 0x01, 0x60, 0x20, 0x30}; + static const u8 shaVtable1RefPattern[] = {0x00, 0x1f, 0x01, 0x60, 0x20, 0x30}; + static const u8 shaVtable2RefPattern[] = {0x00, 0x1f, 0x01, 0x60, 0x01, 0x00, 0x20, 0x31}; u32 ncchVtableAddr; if(!getVtableAddress(pos, size, ncchVtableRefPattern, sizeof(ncchVtableRefPattern), sizeof(ncchVtableRefPattern), process9MemAddr, &ncchVtableAddr)) return 1; - u32 shaVtableAddr; - if(!getVtableAddress(pos, size, shaVtableRefPattern, sizeof(shaVtableRefPattern), -2, process9MemAddr, &shaVtableAddr)) return 1; + u32 shaVtable1Addr; + if(!getVtableAddress(pos, size, shaVtable1RefPattern, sizeof(shaVtable1RefPattern), -2, process9MemAddr, &shaVtable1Addr)) return 1; + + u32 shaVtable2Addr; + if(!getVtableAddress(pos, size, shaVtable2RefPattern, sizeof(shaVtable2RefPattern), -2, process9MemAddr, &shaVtable2Addr)) return 1; u32 *ncchVtable11Ptr = (u32 *)(pos + (ncchVtableAddr - process9MemAddr)) + 11, - *shaVtable11Ptr = (u32 *)(pos + (shaVtableAddr - process9MemAddr)) + 11; + *shaVtable1_11Ptr = (u32 *)(pos + (shaVtable1Addr - process9MemAddr)) + 11, + *shaVtable2_11Ptr = (u32 *)(pos + (shaVtable2Addr - process9MemAddr)) + 11; - if((*ncchVtable11Ptr & 0x1) == 0 || (*shaVtable11Ptr & 0x1) == 0) return 1; //Must be Thumb + if((*ncchVtable11Ptr & 0x1) == 0 || (*shaVtable1_11Ptr & 0x1) == 0 || (*shaVtable2_11Ptr & 0x1) == 0) return 1; //Must be Thumb //Find needed function addresses by inspecting all bl branch targets u16 *ncchWriteFnc = (u16 *)(pos + ((*ncchVtable11Ptr & ~0x1) - process9MemAddr)); @@ -911,7 +916,7 @@ u32 patchReadFileSHA256Vtab11(u8 *pos, u32 size, u32 process9MemAddr) if(readFileSHA256Vtab11PatchCtorPtr == 0 || readFileSHA256Vtab11PatchInitPtr == 0 || readFileSHA256Vtab11PatchProcessPtr == 0) return 1; - *shaVtable11Ptr = (u32) &readFileSHA256Vtab11Patch; //The patched vtable11 function is in ITCM, so we don't have to copy it somewhere else + *shaVtable1_11Ptr = *shaVtable2_11Ptr = (u32) &readFileSHA256Vtab11Patch; //The patched vtable11 function is in ITCM, so we don't have to copy it somewhere else return 0; } \ No newline at end of file