From 65ff189d9f532dae7319f92ee7bbe4ef5394bdfd Mon Sep 17 00:00:00 2001 From: Popax21 Date: Tue, 10 Jan 2023 21:16:45 +0100 Subject: [PATCH] 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 4d7010a07..06c323620 100644 --- a/arm9/source/patches.c +++ b/arm9/source/patches.c @@ -845,3 +845,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);