Skip to content

Commit

Permalink
Fix FSPXI:ReadFileSHA256 error when reading exactly one block from No…
Browse files Browse the repository at this point in the history
…Crypto 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)
  • Loading branch information
Popax21 committed Mar 17, 2023
1 parent ef1072f commit d8fed46
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 0 deletions.
1 change: 1 addition & 0 deletions arm9/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ SECTIONS
chainloader.o(.text*)
i2c.o(.text*)
arm9_exception_handlers.o(.text*)
*(.large_patch.readFileSHA256Vtab11)

*(.arm9_exception_handlers.rodata*)
chainloader.o(.rodata*)
Expand Down
3 changes: 3 additions & 0 deletions arm9/source/firm.c
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,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;

Expand Down
4 changes: 4 additions & 0 deletions arm9/source/large_patches.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
57 changes: 57 additions & 0 deletions arm9/source/large_patches.s
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,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
68 changes: 68 additions & 0 deletions arm9/source/patches.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,3 +734,71 @@ u32 patchAgbBootSplash(u8 *pos, u32 size)

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;
}
1 change: 1 addition & 0 deletions arm9/source/patches.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ u32 patchTwlFlashcartChecks(u8 *pos, u32 size, u32 firmVersion);
u32 patchOldTwlFlashcartChecks(u8 *pos, u32 size);
u32 patchTwlShaHashChecks(u8 *pos, u32 size);
u32 patchAgbBootSplash(u8 *pos, u32 size);
u32 patchReadFileSHA256Vtab11(u8 *pos, u32 size, u32 process9MemAddr);

0 comments on commit d8fed46

Please sign in to comment.