-.PHONY: all copyout
-all: loader
+.PHONY: all
+all: loader service
mkdir -p ../out/corbenik/lib/module
+ mkdir -p ../out/corbenik/lib/service
cp loader/loader.cxi ../out/corbenik/lib/module/loader.cxi
+ cp service/7b.bin ../out/corbenik/lib/service/7b.bin
.PHONY: clean
-clean: clean_loader
+clean: clean_loader clean_service
rm -rf ../out/corbenik/bin
.PHONY: loader
.PHONY: clean_loader
clean_loader:
make -C loader clean
+
+.PHONY: service
+service:
+ make -C service
+
+.PHONY: clean_service
+clean_service:
+ make -C service clean
*total = cur;
return res;
-}
\ No newline at end of file
+}
+
+Result IFile_Write(IFile *file, u64 *total, void *buffer, u32 len) {
+ return 1; // FIXME - Not yet implemented.
+}
Result IFile_Open(IFile *file, FS_Archive archive, FS_Path path, u32 flags);
Result IFile_Close(IFile *file);
Result IFile_GetSize(IFile *file, u64 *size);
-Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len);
\ No newline at end of file
+Result IFile_Read(IFile *file, u64 *total, void *buffer, u32 len);
+Result IFile_Write(IFile *file, u64 *total, void *buffer, u32 len);
static Result allocate_shared_mem(prog_addrs_t *shared, prog_addrs_t *vaddr, int flags)
{
+ // Somehow, we need to allow reallocating.
+
u32 dummy;
memcpy(shared, vaddr, sizeof(prog_addrs_t));
- shared->text_addr = 0x10000000;
+ shared->text_addr = 0x10000000; // Code is forcibly relocated to this address to kill ASLR (I believe.)
shared->ro_addr = shared->text_addr + (shared->text_size << 12);
shared->data_addr = shared->ro_addr + (shared->ro_size << 12);
return svcControlMemory(&dummy, shared->text_addr, 0, shared->total_size << 12, (flags & 0xF00) | MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
}
-static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int is_compressed)
+static Result load_code(u64 progid, prog_addrs_t *shared, prog_addrs_t *original, u64 prog_handle, int is_compressed)
{
IFile file;
FS_Archive archive;
lzss_decompress((u8 *)shared->text_addr + size);
}
- // patch
- patchCode(progid, (u8 *)shared->text_addr, shared->total_size << 12);
+ // Patch segments
+ patch_text(progid, (u8 *)shared->text_addr, shared->text_size << 12, original->text_size << 12);
+ patch_data(progid, (u8 *)shared->data_addr, shared->data_size << 12, original->data_size << 12);
+ patch_ro (progid, (u8 *)shared->ro_addr, shared->ro_size << 12, original->ro_size << 12);
return 0;
}
u32 dummy;
prog_addrs_t shared_addr;
prog_addrs_t vaddr;
+ prog_addrs_t original_vaddr;
Handle codeset;
CodeSetInfo codesetinfo;
u32 data_mem_size;
u64 progid;
+ u32 text_grow, data_grow, ro_grow;
// make sure the cached info corrosponds to the current prog_handle
if (g_cached_prog_handle != prog_handle)
return MAKERESULT(RL_PERMANENT, RS_INVALIDARG, 1, 2);
}
- // allocate process memory
+ load_config(); // First order of business - we need the config file.
+
+ // What the addressing info would be if not for expansion. This is passed to patchCode.
+ original_vaddr.text_size = (g_exheader.codesetinfo.text.codesize + 4095) >> 12; // (Text size + one page) >> page size
+ original_vaddr.ro_size = (g_exheader.codesetinfo.ro.codesize + 4095) >> 12;
+ original_vaddr.data_size = (g_exheader.codesetinfo.data.codesize + 4095) >> 12;
+ original_vaddr.total_size = original_vaddr.text_size + original_vaddr.ro_size + original_vaddr.data_size;
+
+ // Allow changing code, ro, data sizes to allow adding code
+ text_grow = get_text_extend(progid, g_exheader.codesetinfo.text.codesize);
+ ro_grow = get_ro_extend(progid, g_exheader.codesetinfo.ro.codesize);
+ data_grow = get_data_extend(progid, g_exheader.codesetinfo.data.codesize);
+
+ // One page is 4096 bytes, thus all the 4095 constants.
+
+ // Allocate process memory, growing as needed for extra patches
vaddr.text_addr = g_exheader.codesetinfo.text.address;
- vaddr.text_size = (g_exheader.codesetinfo.text.codesize + 4095) >> 12;
+ vaddr.text_size = (g_exheader.codesetinfo.text.codesize + text_grow + 4095) >> 12; // (Text size + one page) >> page size
vaddr.ro_addr = g_exheader.codesetinfo.ro.address;
- vaddr.ro_size = (g_exheader.codesetinfo.ro.codesize + 4095) >> 12;
+ vaddr.ro_size = (g_exheader.codesetinfo.ro.codesize + ro_grow + 4095) >> 12;
vaddr.data_addr = g_exheader.codesetinfo.data.address;
- vaddr.data_size = (g_exheader.codesetinfo.data.codesize + 4095) >> 12;
- data_mem_size = (g_exheader.codesetinfo.data.codesize + g_exheader.codesetinfo.bsssize + 4095) >> 12;
- vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size;
+ vaddr.data_size = (g_exheader.codesetinfo.data.codesize + data_grow + 4095) >> 12;
+ data_mem_size = (g_exheader.codesetinfo.data.codesize + text_grow + g_exheader.codesetinfo.bsssize + 4095) >> 12;
+ vaddr.total_size = vaddr.text_size + vaddr.ro_size + vaddr.data_size + text_grow + ro_grow + data_grow;
+
if ((res = allocate_shared_mem(&shared_addr, &vaddr, flags)) < 0)
{
return res;
// load code
progid = g_exheader.arm11systemlocalcaps.programid;
- if ((res = load_code(progid, &shared_addr, prog_handle, g_exheader.codesetinfo.flags.flag & 1)) >= 0)
+ if ((res = load_code(progid, &shared_addr, &original_vaddr, prog_handle, g_exheader.codesetinfo.flags.flag & 1)) >= 0)
{
memcpy(&codesetinfo.name, g_exheader.codesetinfo.name, 8);
codesetinfo.program_id = progid;
static struct config_file config;
static int failed_load_config = 1;
-static void load_config() {
+void load_config() {
static IFile file;
static u64 total;
}
void overlay_patch(u64 progId, u8 *code, u32 size) {
- // TODO - Prt
+ // TODO - Implement.
}
-void patchCode(u64 progId, u8 *code, u32 size) {
- load_config();
+// This is only for the .data segment.
+void patch_data(u64 progId, u8 *data, u32 size, u32 orig_size) {
+}
+
+// This is only for the .ro segment.
+void patch_ro(u64 progId, u8 *ro, u32 size, u32 orig_size) {
+}
+// This is only for the .code segment.
+void patch_text(u64 progId, u8 *text, u32 size, u32 orig_size) {
switch(progId)
{
case 0x0004003000008F02LL: // USA Menu
case 0x000400300000A902LL: // KOR Menu
case 0x000400300000B102LL: // TWN Menu
{
- region_patch(progId, code, size);
+ region_patch(progId, text, orig_size);
break;
}
case 0x0004013000002C02LL: // NIM
{
- disable_nim_updates(progId, code, size);
- disable_eshop_updates(progId, code, size);
+ disable_nim_updates(progId, text, orig_size);
+ disable_eshop_updates(progId, text, orig_size);
break;
}
case 0x0004013000003202LL: // FRIENDS
{
- fake_friends_version(progId, code, size);
+ fake_friends_version(progId, text, orig_size);
break;
}
case 0x0004001000027000LL: // KOR MSET
case 0x0004001000028000LL: // TWN MSET
{
- settings_string(progId, code, size);
+ settings_string(progId, text, size);
break;
}
case 0x0004013000008002LL: // NS
{
- disable_cart_updates(progId, code, size);
- adjust_cpu_settings(progId, code, size); // DEFAULT cpu settings that are inherited system-wide. Per-app is handled in default.
+ disable_cart_updates(progId, text, orig_size);
+ adjust_cpu_settings(progId, text, orig_size); // DEFAULT cpu settings that are inherited system-wide. Per-app is handled in default.
break;
}
case 0x0004013000001702LL: // CFG
{
- secureinfo_sigpatch(progId, code, size);
+ secureinfo_sigpatch(progId, text, orig_size);
break;
}
case 0x0004013000003702LL: // RO
{
- ro_sigpatch(progId, code, size);
+ ro_sigpatch(progId, text, orig_size);
break;
}
- case 0x00040000000B8B00LL: // Smash 4
- case 0x00040000000EE000LL:
- case 0x00040000000EDF00LL:
+ default: // Anything else.
{
- saltysd_patch(progId, code, size);
- }
- default:
- {
- language_emu(progId, code, size);
+ language_emu(progId, text, orig_size);
break;
}
}
}
+
+// Gets how many bytes .text must be extended by for patches to fit.
+u32 get_text_extend(u64 progId, u32 size_orig) {
+ return 0; // Stub - nothing needs this yet
+}
+
+// Gets how many bytes .ro must be extended.
+u32 get_ro_extend(u64 progId, u32 size_orig) {
+ return 0; // Stub - nothing needs this yet
+}
+
+// Again, same, but for .data.
+u32 get_data_extend(u64 progId, u32 size_orig) {
+ return 0; // Stub - nothing needs this yet
+}
#include <3ds/types.h>
-void patchCode(u64 progId, u8 *code, u32 size);
\ No newline at end of file
+void patch_text(u64 progId, u8 *text, u32 size, u32 orig_size);
+void patch_data(u64 progId, u8 *data, u32 size, u32 orig_size);
+void patch_ro(u64 progId, u8 *ro, u32 size, u32 orig_size);
+
+u32 get_text_extend(u64 progId, u32 size_orig);
+u32 get_ro_extend(u64 progId, u32 size_orig);
+u32 get_data_extend(u64 progId, u32 size_orig);
+
+void load_config();
// This is svcBackdoor's code from earlier FIRMs
-.arm.little
-.create "backdoor.bin", 0
- // Luckily, no ARM9/ARM11 specific instructions are used here.
- // It can just be assembled via ARM9 gas.
+// Luckily, no ARM9/ARM11 specific instructions are used here.
+// It can just be assembled via ARM9 gas.
+.section .text
+.global _start
+_start:
bic r1, sp, #0xff
orr r1, r1, #0xf00
add r1, r1, #0x28
pop {r0, r1}
mov sp, r0
bx r1
-.close
--- /dev/null
+PATH := $(PATH):$(DEVKITARM)/bin
+
+all: 7b.bin
+
+%.o: %.s
+ arm-none-eabi-as -o $@ $<
+
+%.elf: %.o
+ arm-none-eabi-ld -T link.ld -o $@ $<
+
+%.bin: %.elf
+ arm-none-eabi-objcopy -O binary $< $@
+
+.PHONY: clean
+clean:
+ rm -f *.bin *.elf *.o
--- /dev/null
+SECTIONS
+{
+ .text : {
+ *(.text.*)
+ }
+}
#define OPTION_LOADER_CPU_800MHZ 12 // Enable 800Mhz mode.
#define OPTION_LOADER_LANGEMU 13 // Enable 800Mhz mode.
+#define OPTION_REPLACE_ALLOCATED_SVC 14 // Replace allocated services. Normally you don't want this.
+
#define IGNORE_PATCH_DEPS 14 // Ignore patch UUID dependencies. Not recommended.
#define IGNORE_BROKEN_SHIT 15 // Allow enabling patches which are marked as 'incompatible'. Chances are there's a reason.
{ 12, "Loader: CPU 800Mhz mode", boolean_val, 0, 0 },
{ 13, "Loader: Language Emulation", boolean_val, 0, 0 },
+ { 13, "Svc: Force replace allocated", boolean_val, 0, 0 },
+
{ 14, "No dependency tracking", boolean_val, 0, 0 },
{ 15, "Allow unsafe options", boolean_val, 0, 0 },
if (memcmp(sysmodule->programID, module->programID, 8) == 0) {
// Expand firmware module size if needed to accomodate replacement.
if (module->contentSize > sysmodule->contentSize) {
+ uint32_t need_units = (module->contentSize - sysmodule->contentSize);
+ fprintf(stderr, "Module: Would grow %d units but NYI\n", need_units);
+ continue;
// FIXME - Adjust sysmodule section and FIRM NCCH. This is not correct.
- fprintf(stderr, "Module: Grow %d units\n", module->contentSize - sysmodule->contentSize);
+/* fprintf(stderr, "Module: Grow %d units\n", module->contentSize - sysmodule->contentSize);
// Location to shuffle to.
- uint32_t need_units = (module->contentSize - sysmodule->contentSize);
uint8_t* move_to = ((uint8_t*)sysmodule + (module->contentSize + need_units) * 0x200);
uint8_t* move_from = ((uint8_t*)sysmodule + module->contentSize * 0x200);
uint32_t copy_size = 0x10000; // FIXME - Add a safe way to properly calculate the half of NCCH we need to copy. This is okay for now.
- memmove(move_to, move_from, copy_size);
+ memmove(move_to, move_from, copy_size); */
// TODO - This is hackish and possibly incorrect. It needs testing.
}
fprintf(stderr, "Svc: table at %x\n", (uint32_t)svcTable);
- if(!svcTable[0x7B]) {
- // Firmware is missing svcBackdoor. Fix it.
- fprintf(stderr, "Svc: inject 0x7B (backdoor)\n");
+ char str[] = PATH_SERVICES "/00.bin";
+ char* at = str + (strlen(str) - 6);
+ for(uint32_t i=0; i <= 0xff; i++) {
+ // Get string for svc.
+ at[0] = ("0123456789abcdef")[((i >> 4) & 0xf)]; // This is just hexdump. Nothing complicated.
+ at[1] = ("0123456789abcdef")[(i & 0xf)];
- // See extra/backdoor.s for the code to this.
- const unsigned char svcbackdoor[40] = {
- 0xFF, 0x10, 0xCD, 0xE3, 0x0F, 0x1C, 0x81, 0xE3, 0x28, 0x10, 0x81, 0xE2, 0x00, 0x20, 0x91, 0xE5,
- 0x00, 0x60, 0x22, 0xE9, 0x02, 0xD0, 0xA0, 0xE1, 0x30, 0xFF, 0x2F, 0xE1, 0x03, 0x00, 0xBD, 0xE8,
- 0x00, 0xD0, 0xA0, 0xE1, 0x11, 0xFF, 0x2F, 0xE1
- };
+ FILE* data = fopen(str, "r");
+ if (!data) {
+ continue; // No file for svc. Move on.
+ }
+
+ // Refuse to replace non-NULL services unless the user says to.
+ if (svcTable[i] && !config.options[OPTION_REPLACE_ALLOCATED_SVC]) {
+ fclose(data);
+ fprintf(stderr, "Svc: %x non-null, moving on\n", i);
+ continue;
+ }
+
+ uint32_t size = fsize(data);
+ uint8_t* read_to = (void*)FCRAM_JUNK_LOCATION;
+
+ fprintf(stderr, "Svc: %s, %d bytes\n", at, size);
+
+ fread(read_to, 1, size, data);
+
+ fclose(data);
if (!freeSpace) {
for(freeSpace = exceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++);
fprintf(stderr, "Svc: Copy code to %x\n", (uint32_t)freeSpace);
- memcpy(freeSpace, svcbackdoor, sizeof(svcbackdoor));
- svcTable[0x7B] = 0xFFFF0000 + ((uint8_t *)freeSpace - (uint8_t *)exceptionsPage);
+ memcpy(freeSpace, read_to, size);
+ svcTable[i] = 0xFFFF0000 + ((uint8_t *)freeSpace - (uint8_t *)exceptionsPage);
- freeSpace += sizeof(svcbackdoor); // We keep track of this because there's more than 7B free.
+ freeSpace += size; // We keep track of this because there's more than 7B free.
fprintf(stderr, "Svc: entry set as %x\n", svcTable[0x7B]);
- } else {
- fprintf(stderr, "Svc: no change\n");
}
return 0;
#define PATH_PATCHES PATH_CFW "/bin" // Patch binary folder.
#define PATH_FIRMWARES PATH_CFW "/lib/firmware" // Firmware folder.
#define PATH_MODULES PATH_CFW "/lib/module" // Sysmodule location
-#define PATH_SERVICES PATH_CFW "/lib/svc" // Service code location.
+#define PATH_SERVICES PATH_CFW "/lib/service" // Service code location.
#define PATH_TEMP PATH_CFW "/tmp" // Files that are transient (user can delete them and they will be regenerated)
#define PATH_KEYS PATH_CFW "/share/keys" // Keyfiles will be loaded from this dir, and additionally the root if not found.
#define PATH_EXEFS PATH_CFW "/lib/exe" // ExeFS overrides, named like '<titleid>.exefs'
void wait() {
if (config.options[OPTION_TRACE] && !doing_autoboot) {
- fprintf(stderr, " [WAIT]");
+ fprintf(stderr, " [WAIT]");
wait_key();
}
- fprintf(stderr, "\r \r");
+ fprintf(stderr, "\r \r");
}
int patch_firm_all() {
// Use builtin signature patcher?
- // TODO - Obviously these get moved to external patchers.
- fprintf(stderr, "Sigpatch: %s\n", ((config.options[OPTION_SIGPATCH]) ? "yes" : "no" ));
- fprintf(stderr, "Protect: %s\n", ((config.options[OPTION_FIRMPROT]) ? "yes" : "no" ));
-
wait();
if (config.options[OPTION_SIGPATCH]) {