111 lines
3.2 KiB
C

#include <string.h>
#include <3ds.h>
#include "patcher.h"
#pragma pack(1)
typedef struct KBlockInfo {
u32 section_start;
u32 page_count;
} KBlockInfo;
typedef struct KLinkedListNode {
struct KLinkedListNode* next;
struct KLinkedListNode* prev;
void* data;
} KLinkedListNode;
typedef struct MemSectionInfo {
u8 padding[0x0C - 0x00];
KLinkedListNode* first_node;
KLinkedListNode* last_node;
} MemSectionInfo;
typedef struct KCodeSet {
u8 padding0[0x08 - 0x00];
MemSectionInfo text_info;
u8 padding1[0x64 - 0x1C];
} KCodeSet;
#pragma pack(0)
static u32 kernel_version = 0;
static bool n3ds = false;
s32 kernel_patch_fs() {
asm volatile("cpsid aif");
u32 processSize = 0;
u32 processCodeSetOffset = 0;
u32 processPidOffset = 0;
if(kernel_version < 0x022C0600) {
processSize = 0x260;
processCodeSetOffset = 0xA8;
processPidOffset = 0xAC;
} else if(n3ds) {
processSize = 0x270;
processCodeSetOffset = 0xB8;
processPidOffset = 0xBC;
} else {
processSize = 0x268;
processCodeSetOffset = 0xB0;
processPidOffset = 0xB4;
}
u32 currProcessPtr = *(u32*) 0xFFFF9004;
u32 vtablePtr = *(u32*) currProcessPtr;
for(u32 processPtr = currProcessPtr; *(u32*) processPtr == vtablePtr; processPtr -= processSize) {
if(*(u32*) (processPtr + processPidOffset) == 0) {
KCodeSet* codeSet = *(KCodeSet**) (processPtr + processCodeSetOffset);
if(codeSet != NULL) {
// Patches out an archive access check.
u8 original[] = {0x0C, 0x05, 0x0C, 0x33, 0x46, 0xF7, 0xF7, 0x49, 0xF8, 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF8};
u8 patched[] = {0x0C, 0x05, 0x0C, 0x33, 0x46, 0x01, 0x20, 0x00, 0x00, 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF8};
for(KLinkedListNode* node = codeSet->text_info.first_node; node != NULL; node = node->next) {
KBlockInfo* blockInfo = (KBlockInfo*) node->data;
u32 blockSize = blockInfo->page_count * 0x1000;
bool done = false;
for(u32 i = 0; i <= blockSize - sizeof(original); i++) {
u8* dst = (u8*) (blockInfo->section_start + i);
bool equal = true;
for(u32 b = 0; b < sizeof(original); b++) {
if(dst[b] != original[b]) {
equal = false;
break;
}
}
if(equal) {
for(u32 b = 0; b < sizeof(patched); b++) {
dst[b] = patched[b];
}
done = true;
break;
}
}
if(done || node == codeSet->text_info.last_node) {
break;
}
}
}
break;
}
}
return 0;
}
void patch_fs() {
kernel_version = *(vu32*) 0x1FF80000;
APT_CheckNew3DS((u8*) &n3ds);
svcBackdoor(kernel_patch_fs);
}