Note that while latest is usually greatest, if you MUST use 11.0 FIRMs, please enable
both the loader replacement and service fix. Nintendo broke quite a bit, and it was
-quite literally a anti-hack.
+quite literally a anti-hack update.
-Also, I may reimplement a token based svcBackdoor since it can now be used to detect
-CFW's presence.
+I may implement a token based svcBackdoor since it can now be used to detect CFW's
+presence.
* Make config file for corbenik plaintext. Nobody likes binary configs. They suck. Massively. Especially when you fuck up a setting and need to change it on something that isn't a 3ds.
* In place firmware loading/patching.
+ * Weird hack central - Developer 11.4 leaked. Allow injecting debug module? Ehehehehe. No clue what the point of this would be, but hey, it'd be FUCKING cool, man.
Shortterm
-------------
* Rewrite all hardcoded constants that are machine code as assembly.
* Read: all the patches.
* Change some stdlib functions to more closely imitate their userland counterparts
+ * EmuNAND. By this, I mean any non-physical NAND, not just gateway style.
+ * I'd like to implement a loop-mount-like NAND. The NAND would be a file off the SD card rather than a region before partition 1 in this setup, and on the upside near unlimited NANDs could be accomodated without moving partitions. On the downside, any user stupid enough to move the NAND bin while the system is running would be in for severe consequences.
--- /dev/null
+#include "patch.h"
+
+void errdisp_devpatch(u64 progId, u8* code, u32 size) {
+ static const u8 unitinfoCheckPattern1[] = {0x14, 0x00, 0xD0, 0xE5, 0xDB, 0x9A, 0x9F, 0xED};
+ static const u8 unitinfoCheckPattern2[] = {0x14, 0x00, 0xD0, 0xE5, 0x01, 0x00, 0x10, 0xE3} ;
+ static const u8 unitinfoCheckPatch[] = {0x00, 0x00, 0xA0, 0xE3} ;
+
+ patchMemory(code, size,
+ unitinfoCheckPattern1,
+ sizeof(unitinfoCheckPattern1), 0,
+ unitinfoCheckPatch,
+ sizeof(unitinfoCheckPatch), 1
+ );
+
+ patchMemory(code, size,
+ unitinfoCheckPattern2,
+ sizeof(unitinfoCheckPattern2), 0,
+ unitinfoCheckPatch,
+ sizeof(unitinfoCheckPatch), 3
+ );
+}
void region_patch(u64 progId, u8* code, u32 size);
void ro_sigpatch(u64 progId, u8* code, u32 size);
void secureinfo_sigpatch(u64 progId, u8* code, u32 size);
+void errdisp_devpatch(u64 progId, u8* code, u32 size);
#endif
ro_sigpatch(progId, text, orig_size);
break;
}
+ case 0x0004003000008A02LL: // ErrDisp
+ {
+ errdisp_devpatch(progId, text, orig_size);
+ break;
+ }
default: // Anything else.
{
language_emu(progId, text, orig_size);
cp -r out/corbenik $mnt/ || exit 0
cp -r input/corbenik $mnt/ || exit 0
umount $mnt || exit 0
+sync || exit 0
eject ${dev} || exit 0
// there's a reason.
#define OPTION_AADOWNGRADE 16 // Anti-anti-downgrade.
+#define OPTION_MEMEXEC 17 // Prevent MPU from disabling execute permissions.
+#define OPTION_UNITINFO 18 // Dev UNITINFO. Note that this is overkill.
//#define HEADER_COLOR 12 // Color of header text.
//#define BG_COLOR 13 // Color of background.
{ OPTION_FIRMPROT, "FIRM Protection", boolean_val, 0, 0 },
- { OPTION_LOADER, "System Modules (loader)", boolean_val, 0, 0 },
+ { OPTION_LOADER, "System Modules", boolean_val, 0, 0 },
{ OPTION_LOADER_CPU_L2, " CPU - L2 cache", boolean_val, 0, 0 },
{ OPTION_LOADER_CPU_800MHZ, " CPU - 800Mhz", boolean_val, 0, 0 },
{ OPTION_LOADER_LANGEMU, " Language Emulation", boolean_val, 0, 0 },
- { OPTION_SERVICES, "Service Replacement", boolean_val, 0, 0 },
- { OPTION_REPLACE_ALLOCATED_SVC, " Force replacement (unsafe)", boolean_val, 0, 0 },
+ { OPTION_SERVICES, "Service Replacement", boolean_val, 0, 0 },
{ OPTION_AADOWNGRADE, "Anti-anti-downgrade", boolean_val, 0, 0 },
+ // space
+ { 0, "", not_option, 0, 0 },
+ // Patches.
+ { 0, "\x1b[32;40mPatches (Developer)\x1b[0m", not_option, 0, 0 },
+
+ { OPTION_UNITINFO, "Developer UNITINFO", boolean_val, 0, 0 },
+ { OPTION_MEMEXEC, "Disable XN on MPU", boolean_val, 0, 0 },
+ { OPTION_REPLACE_ALLOCATED_SVC, "Force service replace", boolean_val, 0, 0 },
+
// { OPTION_ARM9THREAD, "ARM9 Thread", boolean_val, 0, 0 },
// space
--- /dev/null
+#include "patch_file.h"
+
+// This patch clears MPU settings which lock down memory
+// execution from userland. You should NOT enable this
+// unless you know you need it, because it makes an obvious
+// behavioral change that can be used maliciously and/or to
+// detect CFW use rather easily.
+
+PATCH(memexec)
+{
+ firm_section_h* arm11_section = & firm_loc->section[1]; // Section 1, please.
+
+ uint8_t* firm_mem = (uint8_t*)firm_loc + arm11_section->offset;
+ uint32_t size = arm11_section->size;
+
+ const uint8_t pattern[] = {0x97, 0x05, 0x00, 0x00, 0x15, 0xE4, 0x00, 0x00};
+
+ // We look for 'exe:' first; this string is close to what we patch
+ uint32_t* off = (uint32_t*)memfind(firm_mem, size, pattern, 8);
+
+ if (off == NULL) {
+ fprintf(stderr, "memexec: couldn't find pattern.\n");
+ return 1;
+ }
+
+ // NOTE - Luma3DS' check here was incoherent.
+ // It read as 'decrement until 0x00000000, which means on
+ // failure it would cause lockup due to read of unreadable mapped areas.
+ // Even worse, it could end up patching something that isn't in FCRAM.
+ while(off > (uint32_t*)firm_mem && *off != 0x16416)
+ off--;
+
+ if(off == (uint32_t*)firm_mem) {
+ fprintf(stderr, "memexec: beginning missing.\n");
+ return 1;
+ }
+
+ *off &= ~(1 << 4); //Clear XN bit
+
+ fprintf(stderr, "memexec: Cleared XN bit.\n");
+
+ return 0;
+}
PATCH(modules)
{
- // TODO - load module cxi here
+ // TODO - load other module cxis here
FILE* f = fopen(PATH_MODULES "/loader.cxi", "r");
if (!f) {
fprintf(stderr, "Module: loader.cxi not found on FS\n");
--- /dev/null
+#include "patch_file.h"
+
+// This patch makes the console think it is a developer unit.
+// Note that this is generally invasive and not useful to users;
+// usually the ErrDisp patch in loader should be good enough for
+// debugging crashes.
+
+PATCH(unitinfo)
+{
+ firm_section_h* arm9_section;
+ int found_sect = 0;
+
+ for (arm9_section = firm_loc->section;
+ arm9_section < firm_loc->section + 4; arm9_section++) {
+ if (arm9_section->type == FIRM_TYPE_ARM9) {
+ found_sect = 1;
+ break;
+ }
+ }
+
+ if (!found_sect) {
+ fprintf(stderr, "unitinfo: no arm9 section?\n");
+ return 1;
+ }
+
+ uint8_t* firm_mem = (uint8_t*)firm_loc + arm9_section->offset;
+ uint32_t size = arm9_section->size;
+
+ const uint8_t pattern[] = {0x01, 0x10, 0xA0, 0x13};
+
+ // We look for 'exe:' first; this string is close to what we patch
+ uint8_t* off = memfind(firm_mem, size, pattern, 4);
+
+ if (off == NULL) {
+ fprintf(stderr, "unitinfo: Couldn't find UNITINFO.\n");
+ return 1;
+ }
+
+ off[3] = 0xE3;
+
+ fprintf(stderr, "unitinfo: Applied\n");
+
+ return 0;
+}
extern int patch_services();
extern int patch_modules();
extern int patch_aadowngrade();
+extern int patch_memexec();
+extern int patch_unitinfo();
extern int doing_autoboot;
wait();
}
+ if (config.options[OPTION_UNITINFO]) {
+ if (patch_unitinfo()) {
+ abort("UNITINFO patch failed.");
+ }
+
+ wait();
+ }
+
+ if (config.options[OPTION_MEMEXEC]) {
+ if (patch_memexec()) {
+ abort("MPU execution patch failed.");
+ }
+
+ wait();
+ }
+
return 0;
}
}
}
+int disable_format = 0;
+
void
vfprintf(void* channel, const char* format, va_list ap)
{
// terminates an ANSI sequence
while (!(*ref >= 0x40 && *ref <= 0x7E))
ref++;
- } else if (*ref == '%') {
+ } else if (*ref == '%' && !disable_format) {
int type_size = 0;
int length = -1;
check_format:
}
break;
case 's':
- puts(channel, va_arg(ap, char*));
+ // Using puts isn't correct here...
+ disable_format = 1; // Disable format strings.
+ fprintf(channel, va_arg(ap, char*));
+ disable_format = 0; // Reenable.
break;
case 'c':
putc(channel, va_arg(ap, int));