From b517a2f6d7dabfdf1523ae66937b2d852e338e3b Mon Sep 17 00:00:00 2001 From: root Date: Tue, 17 May 2016 16:17:05 -0400 Subject: [PATCH] Now we have injector. Good night, cakes. --- .gitignore | 5 +- copy.sh | 1 + external/Makefile | 6 +- external/loader/Makefile | 1 + external/loader/source/config.h | 97 ++++++-- external/loader/source/memory.h | 5 +- external/loader/source/patcher.c | 334 ++++++++++++++------------- {asm => external/service}/backdoor.s | 0 source/config.c | 2 +- source/config.h | 25 +- source/firm/fcram.h | 3 + source/menu.c | 72 +++--- source/patch/base.c | 13 ++ source/patch/module.c | 76 ++++++ source/patch_format.h | 68 +++--- source/patcher.c | 12 +- 16 files changed, 468 insertions(+), 252 deletions(-) rename {asm => external/service}/backdoor.s (100%) create mode 100644 source/patch/base.c create mode 100644 source/patch/module.c diff --git a/.gitignore b/.gitignore index ec5ff8a..c9c9684 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ out -CakeHax -CakeBrah build *.bin *.3dsx @@ -9,4 +7,5 @@ build *.o *.d *.elf -*.bat \ No newline at end of file +*.bat +input diff --git a/copy.sh b/copy.sh index 2872f61..53b5e83 100644 --- a/copy.sh +++ b/copy.sh @@ -4,5 +4,6 @@ cp out/arm9loaderhax.bin /media/cd/anim/boot/none.bin || exit 0 cp out/arm9loaderhax.bin /media/cd/anim/boot/r.bin || exit 0 cp out/arm9loaderhax.bin /media/cd/anim/boot/l.bin || exit 0 cp -r out/corbenik /media/cd/ || exit 0 +cp -r input/corbenik /media/cd/ || exit 0 umount /media/cd || exit 0 eject /dev/sdb || exit 0 diff --git a/external/Makefile b/external/Makefile index 087842f..c623d56 100644 --- a/external/Makefile +++ b/external/Makefile @@ -1,11 +1,11 @@ .PHONY: all copyout all: loader - mkdir -p ../out/corbenik/module - cp loader/loader.cxi ../out/corbenik/module/loader.cxi + mkdir -p ../out/corbenik/lib/module + cp loader/loader.cxi ../out/corbenik/lib/module/loader.cxi .PHONY: clean clean: clean_loader - rm -rf ../out/corbenik/module + rm -rf ../out/corbenik/bin .PHONY: loader loader: diff --git a/external/loader/Makefile b/external/loader/Makefile index 43eb971..decb4e0 100644 --- a/external/loader/Makefile +++ b/external/loader/Makefile @@ -31,6 +31,7 @@ INCLUDES := include #--------------------------------------------------------------------------------- ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft +ERROR := -Werror CFLAGS := -flto -Wall -Os -mword-relocations \ -fomit-frame-pointer -ffunction-sections -fdata-sections -fshort-wchar \ $(ARCH) diff --git a/external/loader/source/config.h b/external/loader/source/config.h index aee78c0..c40d2f3 100644 --- a/external/loader/source/config.h +++ b/external/loader/source/config.h @@ -1,20 +1,89 @@ -#pragma once +#ifndef __CONFIG_H +#define __CONFIG_H -#include +static unsigned int config_version = 1; +#define CONFIG_MAGIC "OVAN" + +// Structure of config file struct config_file { - unsigned int config_ver; - unsigned int firm_ver; - uint8_t firm_console; - uint32_t emunand_location; + char magic[4]; // "OVAN" for shits and giggles again. + uint32_t config_ver; // Config file version. + + uint8_t options[256]; // Options in the menu - deliberately large to avoid config version bumps. + + uint64_t patch_ids[256]; // What patches are enabled by UUID. 256 is an arbitrary limit - contact me if you hit it. +}__attribute__((packed)); + +/* +extern struct config_file config; + +enum type { + boolean_val, // Toggle + ranged_val // N1 - N2, left and right to pick. + mask_val // Bitmask allowed values. +}; + +struct range_str { + int a, b; +}; + +struct option { + int config_index; + char name_text[64]; + enum type allowed; + uint32_t a, b; +}__attribute__((packed)); + + +static struct options[] = { + { 0, "Signature Patch", boolean_val, 0 }, + { 1, "FIRM Protection", boolean_val, 0 }, + { 2, "SysModule Replacement", boolean_val, 0 }, + { 3, "Service Replacement", boolean_val, 0 }, + { 4, "ARM9 Thread", boolean_val, 0 }, + + { 5, "Autoboot", boolean_val, 0 }, + { 6, "Silence w/ Autoboot", boolean_val, 0 }, + { 7, "Step through with button", boolean_val, 0 }, + + { 8, "Don't draw background color", boolean_val, 0 }, + { 9, "Preserve framebuffer data", boolean_val, 0 }, + + { 10, "Hide Help from menu", boolean_val, 0 }, + + { 11, "Loader: CPU L2 enable", boolean_val, 0 }, + { 12, "Loader: CPU 800Mhz mode", boolean_val, 0 }, + { 13, "Loader: Language Emulation", boolean_val, 0 }, + + { 14, "No dependency tracking", boolean_val, 0 }, + { 15, "Allow unsafe options", boolean_val, 0 }, +} +*/ +#define OPTION_SIGPATCH 0 // Use builtin signature patch. +#define OPTION_FIRMPROT 1 // Protect firmware from writes. +#define OPTION_LOADER 2 // Use builtin loader module replacer. +#define OPTION_SERVICES 3 // Inject services (including backdoor for 11) +#define OPTION_ARM9THREAD 4 // Use builtin ARM9 thread injector. + +#define OPTION_AUTOBOOT 5 // Skip menu unless L is held. +#define OPTION_SILENCE 6 // Don't print debug information. +#define OPTION_TRACE 7 // Pause for A key on each step. + +#define OPTION_TRANSP_BG 8 // Background color is not drawn under text. +#define OPTION_NO_CLEAR_BG 9 // Framebuffer is preserved from whatever ran before us. +#define OPTION_READ_ME 10 // Remove Help/Readme from menu. + +#define OPTION_LOADER_CPU_L2 11 // Enable L2 cache. +#define OPTION_LOADER_CPU_800MHZ 12 // Enable 800Mhz mode. +#define OPTION_LOADER_LANGEMU 13 // Enable 800Mhz mode. - unsigned int autoboot_enabled: 1; - unsigned int n3ds_clock: 1; - unsigned int n3ds_l2: 1; - unsigned int language_emu: 1; +#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. - /* The cakes_count and cakes data - is excluded because loader has - no use for it. */ -} __attribute__((packed)); +//#define HEADER_COLOR 12 // Color of header text. +//#define BG_COLOR 13 // Color of background. +//#define TEXT_COLOR 14 // Color of most text. +//#define ARROW_COLOR 15 // Color of Arrow. +#endif diff --git a/external/loader/source/memory.h b/external/loader/source/memory.h index 76d58c7..2653063 100644 --- a/external/loader/source/memory.h +++ b/external/loader/source/memory.h @@ -1,5 +1,8 @@ -#pragma once +#ifndef __MEMORY_H__ +#define __MEMORY_H__ #include <3ds/types.h> void memcpy(void *dest, const void *src, u32 size); + +#endif diff --git a/external/loader/source/patcher.c b/external/loader/source/patcher.c index 35ee5c8..bd21e93 100644 --- a/external/loader/source/patcher.c +++ b/external/loader/source/patcher.c @@ -8,6 +8,7 @@ #define _MAX_LFN 255 #endif #include "config.h" +#include "../../../source/patch_format.h" static int memcmp(const void *buf1, const void *buf2, u32 size) { @@ -59,6 +60,7 @@ static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, in if(found == NULL) break; + // FIXME - This is throwing on Werror. memcpy(found + offset, replace, repSize); u32 at = (u32)(found - start); @@ -115,50 +117,74 @@ static u32 secureInfoExists(void) return secureInfoExists; } -static unsigned int config_ver = 4; -static struct config_file config; -static int failed_load_config = 1; -void load_config() { - // Make sure we don't get random values if the config file doesn't load - for(int i=0;i 0) { @@ -181,6 +207,9 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId) for(u32 i = 0; i < 7; ++i) { + // TODO - Permit alternative names. They're using fixed strings for ease of use; + // we need to permit names like 'Japan', 'Europe', etc + // Maybe read locale data from the FS? http://cldr.unicode.org/ static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"}; if(memcmp(buf, regions[i], 3) == 0) @@ -192,6 +221,7 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId) for(u32 i = 0; i < 12; ++i) { + // TODO - Same as above. static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"}; if(memcmp(buf + 4, languages[i], 2) == 0) @@ -316,9 +346,124 @@ static void patchCfgGetRegion(u8 *code, u32 size, u8 regionId, u32 CFGUHandleOff } } +void region_patch(u64 progId, u8 *code, u32 size) { + static const u8 regionFreePattern[] = {0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3}; + static const u8 regionFreePatch[] = {0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1}; + + //Patch SMDH region checks + patchMemory(code, size, + regionFreePattern, + sizeof(regionFreePattern), -16, + regionFreePatch, + sizeof(regionFreePatch), 1 + ); +} + +void disable_nim_updates(u64 progId, u8 *code, u32 size) { + static const u8 blockAutoUpdatesPattern[] = {0x25, 0x79, 0x0B, 0x99}; + static const u8 blockAutoUpdatesPatch[] = {0xE3, 0xA0}; + + //Block silent auto-updates + patchMemory(code, size, + blockAutoUpdatesPattern, + sizeof(blockAutoUpdatesPattern), 0, + blockAutoUpdatesPatch, + sizeof(blockAutoUpdatesPatch), 1 + ); +} + +void disable_eshop_updates(u64 progId, u8 *code, u32 size) { + static const u8 skipEshopUpdateCheckPattern[] = {0x30, 0xB5, 0xF1, 0xB0}; + static const u8 skipEshopUpdateCheckPatch[] = {0x00, 0x20, 0x08, 0x60, 0x70, 0x47}; + + //Skip update checks to access the EShop + patchMemory(code, size, + skipEshopUpdateCheckPattern, + sizeof(skipEshopUpdateCheckPattern), 0, + skipEshopUpdateCheckPatch, + sizeof(skipEshopUpdateCheckPatch), 1 + ); +} + +void fake_friends_version(u64 progId, u8 *code, u32 size) { + static const u8 fpdVerPattern[] = {0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01}; + static const u8 fpdVerPatch = 0x06; // Latest version. + + //Allow online access to work with old friends modules + patchMemory(code, size, + fpdVerPattern, + sizeof(fpdVerPattern), 9, + &fpdVerPatch, + sizeof(fpdVerPatch), 1 + ); +} + +void settings_string(u64 progId, u8 *code, u32 size) { + static const u16 verPattern[] = u"Ver."; + static const u16 verPatch[] = u".hax"; + + //Patch Ver. string + patchMemory(code, size, + verPattern, + sizeof(verPattern) - sizeof(u16), 0, + verPatch, + sizeof(verPatch) - sizeof(u16), 1 + ); +} + +void disable_cart_updates(u64 progId, u8 *code, u32 size) { + static const u8 stopCartUpdatesPattern[] = {0x0C, 0x18, 0xE1, 0xD8}; + static const u8 stopCartUpdatesPatch[] = {0x0B, 0x18, 0x21, 0xC8}; + + //Disable updates from foreign carts (makes carts region-free) + patchMemory(code, size, + stopCartUpdatesPattern, + sizeof(stopCartUpdatesPattern), 0, + stopCartUpdatesPatch, + sizeof(stopCartUpdatesPatch), 2 + ); +} + +void adjust_cpu_settings(u64 progId, u8 *code, u32 size) { + if (!failed_load_config) { + u32 cpuSetting = 0; + // L2 + cpuSetting |= config.options[OPTION_LOADER_CPU_L2]; + // Speed + cpuSetting |= config.options[OPTION_LOADER_CPU_800MHZ] << 1; + + if(cpuSetting) { + static const u8 cfgN3dsCpuPattern[] = {0x00, 0x40, 0xA0, 0xE1, 0x07, 0x00}; + + u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern)); + + //Patch N3DS CPU Clock and L2 cache setting + if(cfgN3dsCpuLoc != NULL) { + *(cfgN3dsCpuLoc + 1) = 0xE1A00000; + *(cfgN3dsCpuLoc + 8) = 0xE3A00000 | cpuSetting; + } + } + } +} + +void secureinfo_sigpatch(u64 progId, u8 *code, u32 size) { + static const u8 secureinfoSigCheckPattern[] = {0x06, 0x46, 0x10, 0x48, 0xFC}; + static const u8 secureinfoSigCheckPatch[] = {0x00, 0x26}; + + //Disable SecureInfo signature check + patchMemory(code, size, + secureinfoSigCheckPattern, + sizeof(secureinfoSigCheckPattern), 0, + secureinfoSigCheckPatch, + sizeof(secureinfoSigCheckPatch), 1 + ); +} + void patchCode(u64 progId, u8 *code, u32 size) { - load_config(); // Load cakes.dat + // FIXME - Config loading breaks loader. WTF is this? + // Maybe the memcpy? + // load_config(); switch(progId) { @@ -329,74 +474,20 @@ void patchCode(u64 progId, u8 *code, u32 size) case 0x000400300000A902LL: // KOR Menu case 0x000400300000B102LL: // TWN Menu { - static const u8 regionFreePattern[] = { - 0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3 - }; - static const u8 regionFreePatch[] = { - 0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 - }; - - //Patch SMDH region checks - patchMemory(code, size, - regionFreePattern, - sizeof(regionFreePattern), -16, - regionFreePatch, - sizeof(regionFreePatch), 1 - ); - + region_patch(progId, code, size); break; } case 0x0004013000002C02LL: // NIM { - static const u8 blockAutoUpdatesPattern[] = { - 0x25, 0x79, 0x0B, 0x99 - }; - static const u8 blockAutoUpdatesPatch[] = { - 0xE3, 0xA0 - }; - static const u8 skipEshopUpdateCheckPattern[] = { - 0x30, 0xB5, 0xF1, 0xB0 - }; - static const u8 skipEshopUpdateCheckPatch[] = { - 0x00, 0x20, 0x08, 0x60, 0x70, 0x47 - }; - - //Block silent auto-updates - patchMemory(code, size, - blockAutoUpdatesPattern, - sizeof(blockAutoUpdatesPattern), 0, - blockAutoUpdatesPatch, - sizeof(blockAutoUpdatesPatch), 1 - ); - - //Skip update checks to access the EShop - patchMemory(code, size, - skipEshopUpdateCheckPattern, - sizeof(skipEshopUpdateCheckPattern), 0, - skipEshopUpdateCheckPatch, - sizeof(skipEshopUpdateCheckPatch), 1 - ); - + disable_nim_updates(progId, code, size); + disable_eshop_updates(progId, code, size); break; } case 0x0004013000003202LL: // FRIENDS { - static const u8 fpdVerPattern[] = { - 0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01 - }; - - static const u8 fpdVerPatch = 0x05; - - //Allow online access to work with old friends modules - patchMemory(code, size, - fpdVerPattern, - sizeof(fpdVerPattern), 9, - &fpdVerPatch, - sizeof(fpdVerPatch), 1 - ); - + fake_friends_version(progId, code, size); break; } @@ -407,96 +498,24 @@ void patchCode(u64 progId, u8 *code, u32 size) case 0x0004001000027000LL: // KOR MSET case 0x0004001000028000LL: // TWN MSET { - static const u16 verPattern[] = u"Ver."; - - //Patch Ver. string - patchMemory(code, size, - verPattern, - sizeof(verPattern) - sizeof(u16), 0, - u".hax", - sizeof(verPattern) - sizeof(u16), 1 - ); + settings_string(progId, code, size); break; } - case 0x0004013000008002LL: // NS { - static const u8 stopCartUpdatesPattern[] = { - 0x0C, 0x18, 0xE1, 0xD8 - }; - static const u8 stopCartUpdatesPatch[] = { - 0x0B, 0x18, 0x21, 0xC8 - }; - - //Disable updates from foreign carts (makes carts region-free) - patchMemory(code, size, - stopCartUpdatesPattern, - sizeof(stopCartUpdatesPattern), 0, - stopCartUpdatesPatch, - sizeof(stopCartUpdatesPatch), 2 - ); - - if (!failed_load_config) { - u32 cpuSetting = 0; - cpuSetting += config.n3ds_clock ? 0x1 : 0x0; - cpuSetting += config.n3ds_l2 ? 0x2 : 0x0; - - if(cpuSetting) - { - static const u8 cfgN3dsCpuPattern[] = { - 0x00, 0x40, 0xA0, 0xE1, 0x07, 0x00 - }; - - u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern)); - - //Patch N3DS CPU Clock and L2 cache setting - if(cfgN3dsCpuLoc != NULL) - { - *(cfgN3dsCpuLoc + 1) = 0xE1A00000; - *(cfgN3dsCpuLoc + 8) = 0xE3A00000 | cpuSetting; - } - } - } + disable_cart_updates(progId, code, size); + adjust_cpu_settings(progId, code, size); break; } case 0x0004013000001702LL: // CFG { - static const u8 secureinfoSigCheckPattern[] = { - 0x06, 0x46, 0x10, 0x48, 0xFC - }; - static const u8 secureinfoSigCheckPatch[] = { - 0x00, 0x26 - }; - - //Disable SecureInfo signature check - patchMemory(code, size, - secureinfoSigCheckPattern, - sizeof(secureinfoSigCheckPattern), 0, - secureinfoSigCheckPatch, - sizeof(secureinfoSigCheckPatch), 1 - ); - - if(secureInfoExists()) - { - static const u16 secureinfoFilenamePattern[] = u"SecureInfo_"; - static const u16 secureinfoFilenamePatch[] = u"C"; - - //Use SecureInfo_C - patchMemory(code, size, - secureinfoFilenamePattern, - sizeof(secureinfoFilenamePattern) - sizeof(u16), - sizeof(secureinfoFilenamePattern) - sizeof(u16), - secureinfoFilenamePatch, - sizeof(secureinfoFilenamePatch) - sizeof(u16), 2 - ); - } - + secureinfo_sigpatch(progId, code, size); break; } - default: +/* default: { - if(!failed_load_config && config.language_emu) + if(!failed_load_config && config.options[OPTION_LOADER_LANGEMU]) { u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24; @@ -522,8 +541,7 @@ void patchCode(u64 progId, u8 *code, u32 size) } } } - break; - } + } */ } } diff --git a/asm/backdoor.s b/external/service/backdoor.s similarity index 100% rename from asm/backdoor.s rename to external/service/backdoor.s diff --git a/source/config.c b/source/config.c index 0b692da..b8687bf 100644 --- a/source/config.c +++ b/source/config.c @@ -6,9 +6,9 @@ struct config_file config; void regenerate_config() { f_mkdir(PATH_CFW); + f_mkdir(PATH_CFW "/lib"); f_mkdir(PATH_FIRMWARES); f_mkdir(PATH_PATCHES); - f_mkdir(PATH_LOCEMU); f_mkdir(PATH_TEMP); f_mkdir(PATH_KEYS); f_mkdir(PATH_EXEFS); diff --git a/source/config.h b/source/config.h index bb2a949..261ac4d 100644 --- a/source/config.h +++ b/source/config.h @@ -17,6 +17,23 @@ struct config_file { extern struct config_file config; +enum type { + boolean_val, // Toggle + ranged_val, // N1 - N2, left and right to pick. + mask_val // Bitmask allowed values. +}; + +struct range_str { + int a, b; +}; + +struct options_s { + int index; + char name[64]; + enum type allowed; + uint32_t a, b; +}__attribute__((packed)); + #define OPTION_SIGPATCH 0 // Use builtin signature patch. #define OPTION_FIRMPROT 1 // Protect firmware from writes. #define OPTION_LOADER 2 // Use builtin loader module replacer. @@ -31,8 +48,12 @@ extern struct config_file config; #define OPTION_NO_CLEAR_BG 9 // Framebuffer is preserved from whatever ran before us. #define OPTION_READ_ME 10 // Remove Help/Readme from menu. -#define IGNORE_PATCH_DEPS 11 // Ignore patch UUID dependencies. Not recommended. -#define IGNORE_BROKEN_SHIT 12 // Allow enabling patches which are marked as 'incompatible'. Chances are there's a reason. +#define OPTION_LOADER_CPU_L2 11 // Enable L2 cache. +#define OPTION_LOADER_CPU_800MHZ 12 // Enable 800Mhz mode. +#define OPTION_LOADER_LANGEMU 13 // Enable 800Mhz mode. + +#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. //#define HEADER_COLOR 12 // Color of header text. //#define BG_COLOR 13 // Color of background. diff --git a/source/firm/fcram.h b/source/firm/fcram.h index e82a8ee..80f7b57 100644 --- a/source/firm/fcram.h +++ b/source/firm/fcram.h @@ -24,4 +24,7 @@ extern void *fcram_temp; // patch.c #define FCRAM_PATCHBIN_EXEC_LOC (FCRAM_START + FCRAM_SPACING * 4) +// Throwaway temporary space. Don't expect it to stay sane. +#define FCRAM_JUNK_LOCATION (FCRAM_START + FCRAM_SPACING * 5) + #endif diff --git a/source/menu.c b/source/menu.c index aa07f6c..d891c24 100644 --- a/source/menu.c +++ b/source/menu.c @@ -1,7 +1,6 @@ #include "common.h" #include "firm/firm.h" #include "firm/headers.h" -#define MENU_BOOTME -1 #define MENU_MAIN 1 #define MENU_OPTIONS 2 @@ -10,6 +9,32 @@ #define MENU_HELP 5 #define MENU_RESET 6 #define MENU_POWER 7 +#define MENU_BOOTME 8 + +static struct options_s options[] = { + { 0, "Signature Patch", boolean_val, 0, 0 }, + { 1, "FIRM Protection", boolean_val, 0, 0 }, + { 2, "SysModule Replacement", boolean_val, 0, 0 }, + { 3, "Service Replacement", boolean_val, 0, 0 }, + { 4, "ARM9 Thread", boolean_val, 0, 0 }, + + { 5, "Autoboot", boolean_val, 0, 0 }, + { 6, "Silence w/ Autoboot", boolean_val, 0, 0 }, + { 7, "Step through with button", boolean_val, 0, 0 }, + + { 8, "Don't draw background color", boolean_val, 0, 0 }, + { 9, "Preserve framebuffer data", boolean_val, 0, 0 }, + + { 10, "Hide Help from menu", boolean_val, 0, 0 }, + + { 11, "Loader: CPU L2 enable", boolean_val, 0, 0 }, + { 12, "Loader: CPU 800Mhz mode", boolean_val, 0, 0 }, + { 13, "Loader: Language Emulation", boolean_val, 0, 0 }, + + { 14, "No dependency tracking", boolean_val, 0, 0 }, + { 15, "Allow unsafe options", boolean_val, 0, 0 }, + { -1, "", 0, 0, 0}, +}; static int cursor_y = 0; static int which_menu = 1; @@ -40,44 +65,26 @@ int menu_patches() { return MENU_MAIN; } int menu_options() { set_cursor(TOP_SCREEN, 0, 0); - const char *list[] = { - "Signature Patch", - "FIRM Write Protection", - "Inject Loader (NYI)", - "Inject Services", - "Enable ARM9 Thread", - - "Autoboot", - "Silence debug w/ autoboot", - "Pause for input on steps", - - "Don't draw background color (NYI)", - "Preserve current framebuffer (NYI)", - "Hide Readme/Help from menu", - - "Ignore dependencies (NYI)", - "Allow enabling broken (NYI)", - }; - const int menu_max = 12; - header(); - for(int i=0; i < menu_max; i++) { + int i = 0; + while(options[i].index != -1) { // -1 Sentinel. if (cursor_y == i) fprintf(TOP_SCREEN, "\x1b[32m>>\x1b[0m "); else fprintf(TOP_SCREEN, " "); if (need_redraw) - fprintf(TOP_SCREEN, "[%c] %s\n", (config.options[i] ? 'X' : ' '), list[i]); + fprintf(TOP_SCREEN, "[%c] %s\n", (config.options[options[i].index] ? 'X' : ' '), options[i].name); else { // Yes, this is weird. printf does a large number of extra things we don't // want computed at the moment; this is faster. putc(TOP_SCREEN, '['); - putc(TOP_SCREEN, (config.options[i] ? 'X' : ' ')); + putc(TOP_SCREEN, (config.options[options[i].index] ? 'X' : ' ')); putc(TOP_SCREEN, ']'); putc(TOP_SCREEN, '\n'); } + ++i; } need_redraw = 0; @@ -87,13 +94,17 @@ int menu_options() { switch(key) { case BUTTON_UP: cursor_y -= 1; + if (cursor_y < 0) + cursor_y = 0; break; case BUTTON_DOWN: cursor_y += 1; + if (options[cursor_y].index == -1) + cursor_y -= 1; break; case BUTTON_A: // TODO - Value options - config.options[cursor_y] = !config.options[cursor_y]; + config.options[options[cursor_y].index] = !config.options[options[cursor_y].index]; break; case BUTTON_B: need_redraw = 1; @@ -103,12 +114,6 @@ int menu_options() { break; } - // Loop around the cursor. - if (cursor_y < 0) - cursor_y = menu_max - 1; - if (cursor_y > menu_max - 1) - cursor_y = 0; - return 0; } @@ -246,7 +251,7 @@ int menu_main() { case BUTTON_A: need_redraw = 1; cursor_y = 0; - if (ret == menu_max + 2) + if (ret == MENU_BOOTME) return MENU_BOOTME; // Boot meh, damnit! return ret; } @@ -284,6 +289,9 @@ int menu_handler() { menu_reset(); case MENU_POWER: menu_poweroff(); + default: + fprintf(stderr, "Attempt to enter wrong menu!\n"); + to_menu = MENU_MAIN; } if (to_menu != 0) diff --git a/source/patch/base.c b/source/patch/base.c new file mode 100644 index 0000000..fd12e79 --- /dev/null +++ b/source/patch/base.c @@ -0,0 +1,13 @@ +#include +#include "../std/unused.h" +#include "../std/memory.h" +#include "../firm/firm.h" +#include "../config.h" +#include "../common.h" + +int patch_test() { + fprintf(stderr, "Testing, testing, 1, 2, 3, 4..\n"); + + return 0; +} + diff --git a/source/patch/module.c b/source/patch/module.c new file mode 100644 index 0000000..fecadf1 --- /dev/null +++ b/source/patch/module.c @@ -0,0 +1,76 @@ +#include +#include "../std/unused.h" +#include "../std/memory.h" +#include "../firm/firm.h" +#include "../firm/fcram.h" +#include "../config.h" +#include "../common.h" + +int patch_modules() { + // TODO - load module cxi here + FILE* f = fopen(PATH_MODULES "/loader.cxi", "r"); + if (!f) { + fprintf(stderr, "Module: loader.cxi not found on FS\n"); + return 2; + } + + uint32_t size = fsize(f); + fread((uint8_t*)FCRAM_JUNK_LOCATION, 1, size, f); + fclose(f); + + // Look for the section that holds all the sysmodules + firm_section_h *sysmodule_section = NULL; + for (firm_section_h *section = firm_loc->section; section < firm_loc->section + 4; section++) { + if (section->address == 0x1FF00000 && section->type == FIRM_TYPE_ARM11) { + sysmodule_section = section; + break; + } + } + + if (!sysmodule_section) { + fprintf(stderr, "Module: sysmodule section not found\n"); + return 1; + } + + ncch_h *module = (ncch_h*)FCRAM_JUNK_LOCATION; + ncch_h *sysmodule = (ncch_h *)((uint32_t)firm_loc + sysmodule_section->offset); + + // Check if we want to replace an existing sysmodule + while (sysmodule->magic == NCCH_MAGIC) { + if (memcmp(sysmodule->programID, module->programID, 8) == 0) { + // Expand firmware module size if needed to accomodate replacement. + if (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); + // TODO - This is hackish and possibly incorrect. It needs testing. + } + + // Move the remaining modules closer + if (module->contentSize < sysmodule->contentSize) { + int remaining_size = + sysmodule_section->size - + (((uint32_t)sysmodule + sysmodule->contentSize * 0x200) - + ((uint32_t)firm_loc + sysmodule_section->offset)); + // Sysmodule section size - (End location of this sysmodule - Sysmodule section) => + memmove((uint8_t*)sysmodule + module->contentSize * 0x200, (uint8_t*)sysmodule + sysmodule->contentSize * 0x200, remaining_size); + // Move end of section to be adjacent + } + + // Copy the module into the firm + memcpy(sysmodule, module, module->contentSize * 0x200); + break; + } + sysmodule = (ncch_h *)((uintptr_t)sysmodule + sysmodule->contentSize * 0x200); + } + + fprintf(stderr, "Module: injected modules.\n"); + + return 0; +} + diff --git a/source/patch_format.h b/source/patch_format.h index 301f4e8..b0ae99d 100644 --- a/source/patch_format.h +++ b/source/patch_format.h @@ -2,6 +2,8 @@ #define __PATCH_FORMAT_H // The following are titleids which are handled specially for one reason or another. +// We use titleIDs to be generic; it ensures that patches can share the same format +// regardless of whether they're intended for loader or not. Simple logistics. #define NATIVE_FIRM_TITLEID 0x0004013800000002llu // NATIVE_FIRM #define NATIVE_FIRM_N3DS_TITLEID 0x0004013820000002llu // NATIVE_FIRM, n3ds @@ -17,38 +19,49 @@ #define PATCH_FAIL_ABORT (1 << 1) // If patch fails to apply, abort and show an error. #define PATCH_DISABLED (1 << 2) // Do not allow changing this patch's status. With PATCH_MANDATORY, this prevents disabling it. -#define PATH_CFW "/corbenik" // CFW root directory. -#define PATH_CONFIG PATH_CFW "/main.conf" // Config file. -#define PATH_LOCEMU PATH_CFW "/locale.conf" // Locale emulation config -#define PATH_CPU_CFG PATH_CFW "/cpu.conf" // CPU settings config +// You can redefine this in the Makefile, if you'd like. +// Recommended names for being silly: +// Windows +// system +#ifndef PATH_CFW + #define PATH_CFW "/corbenik" // CFW root directory. +#endif + +#define PATH_CONFIG_DIR PATH_CFW "/etc" // Config file directory. +#define PATH_CONFIG PATH_CONFIG_DIR "/main.conf" // Config file. +#define PATH_LOCEMU PATH_CONFIG_DIR "/locale.conf" // Locale emulation config +#define PATH_CPU_CFG PATH_CONFIG_DIR "/cpu.conf" // CPU settings config -#define PATH_FIRMWARES PATH_CFW "/firm" // Firmware folder. #define PATH_PATCHES PATH_CFW "/bin" // Patch binary folder. -#define PATH_TEMP PATH_CFW "/temp" // Files that are transient (user can delete them and they will be regenerated) -#define PATH_KEYS PATH_CFW "/keys" // Keyfiles will be loaded from this dir, and additionally the root if not found. -#define PATH_EXEFS PATH_CFW "/exefs" // ExeFS overrides, named like '.exefs' +#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_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 '.exefs' #define PATH_NATIVE_F PATH_FIRMWARES "/native" #define PATH_AGB_F PATH_FIRMWARES "/agb" #define PATH_TWL_F PATH_FIRMWARES "/twl" -#define PATH_NATIVE_CETK PATH_KEYS "/native.cetk" -#define PATH_NATIVE_FIRMKEY PATH_KEYS "/native.key" +#define PATH_NATIVE_CETK PATH_FIRMWARES "/native.cetk" + +#define PATH_TWL_CETK PATH_FIRMWARES "/twl.cetk" -#define PATH_TWL_CETK PATH_KEYS "/twl.cetk" -#define PATH_TWL_FIRMKEY PATH_KEYS "/twl.key" +#define PATH_AGB_CETK PATH_FIRMWARES "/agb.cetk" -#define PATH_AGB_CETK PATH_KEYS "/agb.cetk" -#define PATH_AGB_FIRMKEY PATH_KEYS "/agb.key" +#define PATH_NATIVE_FIRMKEY PATH_KEYS "/native.key" +#define PATH_TWL_FIRMKEY PATH_KEYS "/twl.key" +#define PATH_AGB_FIRMKEY PATH_KEYS "/agb.key" -#define PATH_SLOT0X11KEY96 PATH_KEYS "/11.key" +// These are used with O3DS units. Keep in mind I have no way to test this. +#define PATH_NATIVE_FIRMKEY_2 PATH_KEYS "/native_old.key" +#define PATH_TWL_FIRMKEY_2 PATH_KEYS "/twl_old.key" +#define PATH_AGB_FIRMKEY_2 PATH_KEYS "/agb_old.key" -#define PATH_ALT_SLOT0X11KEY96 "/slot0x11key96.bin" // Hey, your perrogative, buddy. I like cleaned up paths. +#define PATH_SLOT0X11KEY96 PATH_KEYS "/11.key" -#define PATH_MEMBIN PATH_TEMP "/memory_p" // Memory binary -#define PATH_NATIVE_P PATH_TEMP "/native_p" // Native FIRM patched -#define PATH_AGB_P PATH_TEMP "/agb_firm_p" // AGB FIRM patched -#define PATH_TWL_P PATH_TEMP "/twl_firm_p" // TWL FIRM patched +#define PATH_ALT_SLOT0X11KEY96 "/slot0x11key96.bin" // Hey, your perrogative, buddy. I like cleaned up paths. // Structure of a patch file. struct system_patch { @@ -73,20 +86,5 @@ struct system_patch { struct patch_opcode { } __attribute__((packed)); -/* -[PATCH] -version=1 -cfw_version=1 - -name=Signature Check -desc=Disables firmware signature checking. -uuid=1 -title_id=0004013800000002 -[DEPENDS] -none -[DATA] - -*/ - #endif diff --git a/source/patcher.c b/source/patcher.c index 5cccb03..f8782ca 100644 --- a/source/patcher.c +++ b/source/patcher.c @@ -13,6 +13,7 @@ int execp(char* path); extern int patch_signatures(); extern int patch_firmprot(); extern int patch_services(); +extern int patch_modules(); // A portion of this file is inherited from Luma3DS. /*u32 getLoader(u8 *pos, u32 *loaderSize) { @@ -75,12 +76,15 @@ int patch_firm_all() { // Replace loader? if (config.options[OPTION_LOADER]) { - // Yes. - + if(patch_modules()) { + abort("Fatal. Service patch has failed."); + } // This requires OPTION_SIGPATCH. } - // Replace loader? + wait(); + + // Inject services? if (config.options[OPTION_SERVICES]) { if(patch_services()) { abort("Fatal. Service patch has failed."); @@ -94,5 +98,7 @@ int patch_firm_all() { // FIXME - NYI } + wait(); + return 0; } -- 2.39.5