--- /dev/null
+You're probably wondering what the heck corbenik does differently from cakes,
+considering it seems similar.
+
+Patches are actually code for whatever processor they're intended to run on.
+They're loaded to a static offset in memory, and executed.
+
+Patches should have a declaration of this sort somewhere in them:
+ { 0xc0, 0x9b, 0xe5, 0x1c }
+Followed by a table large enough to fill with all usable functions.
+
+See template/ for how this works.
+
+When the loader finds that magic value, it fills the table with
+the offsets of important variables and functions for use by the patcher, much
+like how an ELF loader sets up references. In other words; patches are binaries
+that are relocated relative to a 'standard library'.
+
+There's some key differences, obviously, from running just arm9loader code.
+Namely:
+ 1) Patches must not clobber the previous state.
+ 2) Patches must properly return.
+ 3) Patches must have a symbol table with the appropriate magic.
+ 4) Patches shouldn't do anything aside from change data.
+ 5) _start must be at offset 0x24400000.
+
+Basically, don't get fancy in _start. Just do a bl main; bx lr. You can implement
+shit yourself, but it's an utter waste of memory. Try not to.
--- /dev/null
+rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
+
+PATH := $(PATH):$(DEVKITARM)/bin
+
+HOST_CC := gcc
+
+CC := arm-none-eabi-gcc
+AS := arm-none-eabi-as
+LD := arm-none-eabi-ld
+OC := arm-none-eabi-objcopy
+
+dir_source := src
+dir_build := build
+dir_out := out
+
+ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
+CFLAGS := -MMD -MP -Wall -Wextra -Werror -O0 $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\"
+FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
+LDFLAGS := -nostdlib -Wl,-z,defs -lgcc
+
+objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
+ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
+ $(call rwildcard, $(dir_source), *.s *.c)))
+
+.PHONY: all
+all: patchbin
+
+.PHONY: patchbin
+patchbin: tool $(dir_out)/patch.bin
+ ./compile_header
+
+.PHONY: tool
+tool:
+ $(HOST_CC) -o compile_header tool/compile_header.c
+
+
+.PHONY: clean
+clean:
+ rm -rf $(dir_out) $(dir_build) compile_header
+
+.PHONY: $(dir_out)/patch.bin
+$(dir_out)/patch.bin: $(dir_build)/main.bin
+ @mkdir -p "$(dir_out)"
+ @cp -av $< $@
+
+$(dir_build)/main.bin: $(dir_build)/main.elf
+ $(OC) -S -O binary $< $@
+
+$(dir_build)/main.elf: $(objects_cfw)
+ $(CC) -T linker.ld $(OUTPUT_OPTION) $^ $(LDFLAGS)
+
+$(dir_build)/%.o: $(dir_source)/%.c
+ @mkdir -p "$(@D)"
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+$(dir_build)/%.o: $(dir_source)/%.s
+ @mkdir -p "$(@D)"
+ $(COMPILE.s) $(OUTPUT_OPTION) $<
+
+include $(call rwildcard, $(dir_build), *.d)
--- /dev/null
+/* This memory map is mainly to assist in doing stuff in code.
+MEMORY
+{
+ INSTRUCTION_TCM (rw) : ORIGIN = 0x00000000, LENGTH = 0x08000000
+ ARM_INTERNAL (rw) : ORIGIN = 0x08000000, LENGTH = 0x00100000
+ NEW_INTERNAL (rw) : ORIGIN = 0x08100000, LENGTH = 0x00080000
+ IO_MEMORY (rw) : ORIGIN = 0x10000000, LENGTH = 0x08000000
+ VRAM (rw) : ORIGIN = 0x18000000, LENGTH = 0x00600000
+ DSP_MEMORY (rw) : ORIGIN = 0x1FF00000, LENGTH = 0x00080000
+ AXI_WRAM (rw) : ORIGIN = 0x1FF80000, LENGTH = 0x00080000
+ FCRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x08000000
+ NEW_FCRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x08000000
+ DATA_TCM (rw) : ORIGIN = 0xFFF00000, LENGTH = 0x00004000
+ BOOTROM (rw) : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000
+} */
+
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x24400000;
+
+ START_SECTION = .;
+ .text.start : {
+ *(.text.start)
+ }
+ START_SECTION_END = .;
+
+ TABLE_SECTION = .;
+ .text.table : {
+ *(.text.table)
+ }
+ TABLE_SECTION_END = .;
+
+ TEXT_SECTION = .;
+ .text : {
+ *(.text)
+ }
+ TEXT_SECTION_END = .;
+
+ DATA_SECTION = .;
+ .data : {
+ *(.data)
+ }
+ DATA_SECTION_END = .;
+
+ BSS_SECTION = .;
+ .bss : {
+ *(.bss COMMON)
+ }
+ BSS_SECTION_END = .;
+
+ RODATA_SECTION = .;
+ .rodata : {
+ *(.rodata)
+ }
+ RODATA_SECTION_END = .;
+
+ . = ALIGN(4);
+}
--- /dev/null
+Prints "Hello World!"
--- /dev/null
+Example Patch
--- /dev/null
+#ifndef EXPORTED_H
+#define EXPORTED_H
+
+#define stdout (void*)0
+#define stderr (void*)1
+
+extern uint8_t* memory_offset;
+extern uint32_t* memory_len;
+
+extern int strlen(const char *string);
+extern int isprint(char c);
+extern void memcpy(void *dest, const void *src, size_t size);
+extern void memmove(void *dest, const void *src, size_t size);
+extern void memset(void *dest, const int filler, size_t size);
+extern int memcmp(const void *buf1, const void *buf2, const size_t size);
+extern void strncpy(void *dest, const void *src, const size_t size);
+extern int strncmp(const void *buf1, const void *buf2, const size_t size);
+extern int atoi(const char *str);
+extern uint8_t* memfind (uint8_t *string, uint32_t stringlen, uint8_t *pat, uint32_t patlen);
+
+extern void putc(void* buf, const int c);
+extern void puts(void* buf, const char *string);
+extern void fprintf(void* channel, const char* format, ...);
+
+#endif
--- /dev/null
+.section .text.table
+.align 4
+
+.macro stub name
+ .global \name
+ \name :
+ .byte 0
+ .byte 0
+ .byte 0
+ .byte 0
+.endm
+
+.global MAGIC_START
+MAGIC_START:
+ .byte 0xc0
+ .byte 0x9b
+ .byte 0xe5
+ .byte 0x1c
+
+// Memory to patch as specified by the header. (uint8_t*)
+stub memory_offset
+
+// Size of memory offset. (uint32_t*)
+stub memory_len
+
+// Exported functions.
+
+// memory.c
+stub strlen
+stub isprint
+stub memcpy
+stub memmove
+stub memset
+stub memcmp
+stub strncpy
+stub strncmp
+stub atoi
+stub memfind
+
+// draw.c
+stub putc
+stub puts
+stub fprintf
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include "exported.h"
+
+int main() {
+ //Look for signature checks
+ uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
+ uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
+
+ uint8_t *firm_mem = (uint8_t*)memory_offset;
+ uint32_t size = *(uint32_t*)memory_len;
+
+ uint8_t *off = memfind(firm_mem, size, pat1, 4);
+ uint8_t *off2 = memfind(firm_mem, size, pat2, 4) - 1;
+
+ if (off == NULL) {
+ fprintf(stderr, "Signature patch failed on P0.\n");
+ return 1; // Failed to find sigpatch. Ugh.
+ }
+
+ if (off2 == NULL) {
+ fprintf(stderr, "Signature patch failed on P1.\n");
+ return 2; // Failed to find sigpatch. Ugh.
+ }
+
+ fprintf(stderr, "Signatures[0]: 0x%x\n", (uint32_t)off);
+ fprintf(stderr, "Signatures[1]: 0x%x\n", (uint32_t)off2);
+
+ uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
+
+ memcpy(off, sigpatch, 2);
+ memcpy(off2, sigpatch, 4);
+ fprintf(stderr, "Signature patch succeded.\n");
+
+ return 0;
+}
--- /dev/null
+#include <stdint.h>
+#include "std/unused.h"
+#include "std/memory.h"
+#include "firm/firm.h"
+#include "config.h"
+#include "common.h"
+
+uint32_t wait_key();
+
+// A portion of this file is inherited from Luma3DS.
+/*
+u16 *getFirmWrite(u8 *pos, u32 size) {
+ //Look for FIRM writing code
+ u8 *const off = memsearch(pos, "exe:", size, 4);
+ const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
+
+ return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
+}
+
+u16 *getFirmWriteSafe(u8 *pos, u32 size) {
+ //Look for FIRM writing code
+ const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
+
+ return (u16 *)memsearch(pos, pattern, size, 4);
+}
+
+u32 getLoader(u8 *pos, u32 *loaderSize) {
+ u8 *off = pos;
+ u32 size;
+
+ while(1)
+ {
+ size = *(u32 *)(off + 0x104) * 0x200;
+ if(*(u32 *)(off + 0x200) == 0x64616F6C) break;
+ off += size;
+ }
+
+ *loaderSize = size;
+
+ return (u32)(off - pos);
+}
+
+
+// patch_location = (void *)((uintptr_t)firm + section->offset + (version->offset - section->address));
+
+u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
+{
+ u8 *off = memsearch(pos, "ess9", size, 4);
+
+ *process9Size = *(u32 *)(off - 0x60) * 0x200;
+ *process9MemAddr = *(u32 *)(off + 0xC);
+
+ //Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
+ return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
+}
+*/
+
+int patch_signatures() {
+ //Look for signature checks
+
+ uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
+ uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
+
+ uint8_t *firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
+ uint32_t size = firm_p9_exefs->fileHeaders[0].size;
+
+ uint8_t *off = memfind(firm_mem, size, pat1, 4);
+ uint8_t *off2 = memfind(firm_mem, size, pat2, 4) - 1;
+
+ if (off == NULL) {
+ fprintf(stderr, "Signature patch failed on P0.\n");
+ return 1; // Failed to find sigpatch. Ugh.
+ }
+
+ if (off2 == NULL) {
+ fprintf(stderr, "Signature patch failed on P1.\n");
+ return 2; // Failed to find sigpatch. Ugh.
+ }
+
+ fprintf(stderr, "Signatures[0]: 0x%x\n", (uint32_t)off);
+ fprintf(stderr, "Signatures[1]: 0x%x\n", (uint32_t)off2);
+
+ uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
+
+ memcpy(off, sigpatch, 2);
+ memcpy(off2, sigpatch, 4);
+ fprintf(stderr, "Signature patch succeded.\n");
+
+ return 0;
+}
+
+int patch_firmprot() {
+ uint8_t *firm_mem = (uint8_t*)firm_p9_exefs + sizeof(exefs_h) + firm_p9_exefs->fileHeaders[0].offset;
+ uint32_t size = firm_p9_exefs->fileHeaders[0].size;
+
+ //Look for FIRM writing code
+ uint8_t* off = memfind(firm_mem, size, (uint8_t*)"exe:", 4);
+
+ if(off == NULL) {
+ fprintf(stderr, "Couldn't find 'exe:' string.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Firmprot: 'exe:' string @ %x\n", (uint32_t)off);
+
+ uint8_t pattern[] = {0x00, 0x28, 0x01, 0xDA};
+
+ uint8_t* firmprot = memfind(off - 0x100, 0x100, pattern, 4);
+
+ if(firmprot == NULL) {
+ fprintf(stderr, "Couldn't find firmprot code.\n");
+ return 2;
+ }
+
+ fprintf(stderr, "Firmprot: %x\n", (uint32_t)firmprot);
+
+ uint8_t patch[] = {0x00, 0x20, 0xC0, 0x46};
+ memcpy(firmprot, patch, 4);
+
+ fprintf(stderr, "Applied firmprot patch.\n");
+
+ return 0;
+}
+
+void wait() {
+ if (config.options[OPTION_TRACE]) {
+ fprintf(stderr, "Pausing because trace is on.\n");
+ wait_key();
+ }
+}
+
+int patch_firm_all() {
+ // Use builtin signature patcher?
+
+ 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]) {
+ if(patch_signatures()) {
+ abort("Fatal. Sigpatch has failed.");
+ }
+ }
+
+ wait();
+
+ if (config.options[OPTION_FIRMPROT]) {
+ if(patch_firmprot()) {
+ abort("Fatal. Firmprot has failed.");
+ }
+ }
+
+ wait();
+
+ // Replace loader?
+ if (config.options[OPTION_LOADER]) {
+ // Yes.
+
+ // This requires OPTION_SIGPATCH.
+ }
+
+ // Use ARM9 hook thread?
+ if (config.options[OPTION_ARM9THREAD]) {
+ // Yes.
+
+ // FIXME - NYI
+ }
+
+ return 0;
+}
--- /dev/null
+.section .text.start
+.align 4
+.global _start
+_start:
+ bl main
+ bx lr @ return from patch.
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../../source/patch_format.h"
+
+void read_file_u64(char* name, uint64_t* to) {
+ FILE* hdl = fopen(name, "rb");
+ fscanf(hdl, "%llu", to);
+ fclose(hdl);
+}
+
+
+void read_file_u32(char* name, uint32_t* to) {
+ FILE* hdl = fopen(name, "rb");
+ fscanf(hdl, "%u", to);
+ fclose(hdl);
+}
+
+void read_str(char* name, char* to, size_t len) {
+ FILE* hdl = fopen(name, "rb");
+ int r = fread(to, 1, len-1, hdl);
+ fclose(hdl);
+}
+
+uint32_t size = 0;
+
+uint8_t* read_file_mem(char* name) {
+ FILE* hdl = fopen(name, "rb");
+
+ fseek(hdl, 0, SEEK_END);
+ size = ftell(hdl);
+ rewind(hdl);
+
+ uint8_t* mem = malloc(size);
+ memset(mem, 0, size);
+
+ int r = fread(mem, 1, size, hdl);
+ fclose(hdl);
+
+ printf("%d\n", size);
+
+ return mem;
+}
+
+int main(int c, char** v) {
+ struct system_patch patch;
+ int at = 0;
+
+ memset(&patch, 0, sizeof(patch));
+
+ // Set magic.
+ patch.magic[0] = 'A';
+ patch.magic[1] = 'I';
+ patch.magic[2] = 'D';
+ patch.magic[3] = 'A';
+
+ read_file_u32("meta/patch_version", & patch.patch_ver);
+ read_file_u32("meta/cfw_version", & patch.load_ver);
+
+ read_file_u64("meta/uuid", & patch.patch_id);
+ read_file_u64("meta/title", & patch.tid);
+
+ read_str("meta/name", patch.name, sizeof(patch.name));
+ read_str("meta/desc", patch.desc, sizeof(patch.desc));
+
+ uint8_t* code = read_file_mem("out/patch.bin");
+
+ patch.patch_size = size;
+
+ FILE* out = fopen("out/patch.vco", "wb");
+ fwrite(&patch, 1, sizeof(patch), out);
+ fwrite(code, 1, patch.patch_size, out);
+ fclose(out);
+
+ free(code);
+
+ printf("Ver: %u\n"
+ "CFW: %u\n"
+ "UUID: %llu\n"
+ "TID: %llu\n"
+ "Name: %s\n"
+ "Desc: %s\n"
+ "Size: %u\n",
+ patch.patch_ver, patch.load_ver, patch.patch_id,
+ patch.tid, patch.name, patch.desc, patch.patch_size);
+
+ return 0;
+}
--- /dev/null
+rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
+
+PATH := $(PATH):$(DEVKITARM)/bin
+
+HOST_CC := gcc
+
+CC := arm-none-eabi-gcc
+AS := arm-none-eabi-as
+LD := arm-none-eabi-ld
+OC := arm-none-eabi-objcopy
+
+dir_source := src
+dir_build := build
+dir_out := out
+
+ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
+CFLAGS := -MMD -MP -Wall -Wextra -Werror -Os $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\"
+FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
+LDFLAGS := -nostdlib -Wl,-z,defs -lgcc
+
+objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
+ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
+ $(call rwildcard, $(dir_source), *.s *.c)))
+
+.PHONY: all
+all: patchbin
+
+.PHONY: patchbin
+patchbin: tool $(dir_out)/patch.bin
+ ./compile_header
+
+.PHONY: tool
+tool:
+ $(HOST_CC) -o compile_header tool/compile_header.c
+
+
+.PHONY: clean
+clean:
+ rm -rf $(dir_out) $(dir_build) compile_header
+
+.PHONY: $(dir_out)/patch.bin
+$(dir_out)/patch.bin: $(dir_build)/main.bin
+ @mkdir -p "$(dir_out)"
+ @cp -av $< $@
+
+$(dir_build)/main.bin: $(dir_build)/main.elf
+ $(OC) -S -O binary $< $@
+
+$(dir_build)/main.elf: $(objects_cfw)
+ $(CC) -T linker.ld $(OUTPUT_OPTION) $^ $(LDFLAGS)
+
+$(dir_build)/%.o: $(dir_source)/%.c
+ @mkdir -p "$(@D)"
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+$(dir_build)/%.o: $(dir_source)/%.s
+ @mkdir -p "$(@D)"
+ $(COMPILE.s) $(OUTPUT_OPTION) $<
+
+include $(call rwildcard, $(dir_build), *.d)
--- /dev/null
+/* This memory map is mainly to assist in doing stuff in code.
+MEMORY
+{
+ INSTRUCTION_TCM (rw) : ORIGIN = 0x00000000, LENGTH = 0x08000000
+ ARM_INTERNAL (rw) : ORIGIN = 0x08000000, LENGTH = 0x00100000
+ NEW_INTERNAL (rw) : ORIGIN = 0x08100000, LENGTH = 0x00080000
+ IO_MEMORY (rw) : ORIGIN = 0x10000000, LENGTH = 0x08000000
+ VRAM (rw) : ORIGIN = 0x18000000, LENGTH = 0x00600000
+ DSP_MEMORY (rw) : ORIGIN = 0x1FF00000, LENGTH = 0x00080000
+ AXI_WRAM (rw) : ORIGIN = 0x1FF80000, LENGTH = 0x00080000
+ FCRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x08000000
+ NEW_FCRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x08000000
+ DATA_TCM (rw) : ORIGIN = 0xFFF00000, LENGTH = 0x00004000
+ BOOTROM (rw) : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000
+} */
+
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x24400000;
+
+ START_SECTION = .;
+ .text.start : {
+ *(.text.start)
+ }
+ START_SECTION_END = .;
+
+ TABLE_SECTION = .;
+ .text.table : {
+ *(.text.table)
+ }
+ TABLE_SECTION_END = .;
+
+ TEXT_SECTION = .;
+ .text : {
+ *(.text)
+ }
+ TEXT_SECTION_END = .;
+
+ DATA_SECTION = .;
+ .data : {
+ *(.data)
+ }
+ DATA_SECTION_END = .;
+
+ BSS_SECTION = .;
+ .bss : {
+ *(.bss COMMON)
+ }
+ BSS_SECTION_END = .;
+
+ RODATA_SECTION = .;
+ .rodata : {
+ *(.rodata)
+ }
+ RODATA_SECTION_END = .;
+
+ . = ALIGN(4);
+}
--- /dev/null
+Patches signatures in NATIVE_FIRM.
--- /dev/null
+NATIVE_FIRM Signature Patch
--- /dev/null
+#ifndef EXPORTED_H
+#define EXPORTED_H
+
+#define stdout (void*)0
+#define stderr (void*)1
+
+extern uint8_t* memory_offset;
+extern uint32_t* memory_len;
+
+extern int strlen(const char *string);
+extern int isprint(char c);
+extern void memcpy(void *dest, const void *src, size_t size);
+extern void memmove(void *dest, const void *src, size_t size);
+extern void memset(void *dest, const int filler, size_t size);
+extern int memcmp(const void *buf1, const void *buf2, const size_t size);
+extern void strncpy(void *dest, const void *src, const size_t size);
+extern int strncmp(const void *buf1, const void *buf2, const size_t size);
+extern int atoi(const char *str);
+extern uint8_t* memfind (uint8_t *string, uint32_t stringlen, uint8_t *pat, uint32_t patlen);
+
+extern void putc(void* buf, const int c);
+extern void puts(void* buf, const char *string);
+extern void fprintf(void* channel, const char* format, ...);
+
+#endif
--- /dev/null
+.section .text.table
+.align 4
+
+.macro stub name
+ .global \name
+ \name :
+ .byte 0
+ .byte 0
+ .byte 0
+ .byte 0
+.endm
+
+.global MAGIC_START
+MAGIC_START:
+ .byte 0xc0
+ .byte 0x9b
+ .byte 0xe5
+ .byte 0x1c
+
+// Memory to patch as specified by the header. (uint8_t*)
+stub memory_offset
+
+// Size of memory offset. (uint32_t*)
+stub memory_len
+
+// Exported functions.
+
+// memory.c
+stub strlen
+stub isprint
+stub memcpy
+stub memmove
+stub memset
+stub memcmp
+stub strncpy
+stub strncmp
+stub atoi
+stub memfind
+
+// draw.c
+stub putc
+stub puts
+stub fprintf
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include "exported.h"
+
+int main() {
+ //Look for signature checks
+ uint8_t pat1[] = {0xC0, 0x1C, 0x76, 0xE7};
+ uint8_t pat2[] = {0xB5, 0x22, 0x4D, 0x0C};
+
+ uint8_t *firm_mem = (uint8_t*)memory_offset;
+ uint32_t size = *(uint32_t*)memory_len;
+
+ uint8_t *off = memfind(firm_mem, size, pat1, 4);
+ uint8_t *off2 = memfind(firm_mem, size, pat2, 4) - 1;
+
+ if (off == NULL) {
+ fprintf(stderr, "Signature patch failed on P0.\n");
+ return 1; // Failed to find sigpatch. Ugh.
+ }
+
+ if (off2 == NULL) {
+ fprintf(stderr, "Signature patch failed on P1.\n");
+ return 2; // Failed to find sigpatch. Ugh.
+ }
+
+ fprintf(stderr, "Signatures[0]: 0x%x\n", (uint32_t)off);
+ fprintf(stderr, "Signatures[1]: 0x%x\n", (uint32_t)off2);
+
+ uint8_t sigpatch[] = {0x00, 0x20, 0x70, 0x47};
+
+ memcpy(off, sigpatch, 2);
+ memcpy(off2, sigpatch, 4);
+ fprintf(stderr, "Signature patch succeded.\n");
+
+ return 0;
+}
--- /dev/null
+.section .text.start
+.align 4
+.global _start
+_start:
+ bl main
+ bx lr @ return from patch.
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../../source/patch_format.h"
+
+void read_file_u64(char* name, uint64_t* to) {
+ FILE* hdl = fopen(name, "rb");
+ fscanf(hdl, "%llu", to);
+ fclose(hdl);
+}
+
+
+void read_file_u32(char* name, uint32_t* to) {
+ FILE* hdl = fopen(name, "rb");
+ fscanf(hdl, "%u", to);
+ fclose(hdl);
+}
+
+void read_str(char* name, char* to, size_t len) {
+ FILE* hdl = fopen(name, "rb");
+ int r = fread(to, 1, len-1, hdl);
+ fclose(hdl);
+}
+
+uint32_t size = 0;
+
+uint8_t* read_file_mem(char* name) {
+ FILE* hdl = fopen(name, "rb");
+
+ fseek(hdl, 0, SEEK_END);
+ size = ftell(hdl);
+ rewind(hdl);
+
+ uint8_t* mem = malloc(size);
+ memset(mem, 0, size);
+
+ int r = fread(mem, 1, size, hdl);
+ fclose(hdl);
+
+ printf("%d\n", size);
+
+ return mem;
+}
+
+int main(int c, char** v) {
+ struct system_patch patch;
+ int at = 0;
+
+ memset(&patch, 0, sizeof(patch));
+
+ // Set magic.
+ patch.magic[0] = 'A';
+ patch.magic[1] = 'I';
+ patch.magic[2] = 'D';
+ patch.magic[3] = 'A';
+
+ read_file_u32("meta/patch_version", & patch.patch_ver);
+ read_file_u32("meta/cfw_version", & patch.load_ver);
+
+ read_file_u64("meta/uuid", & patch.patch_id);
+ read_file_u64("meta/title", & patch.tid);
+
+ read_str("meta/name", patch.name, sizeof(patch.name));
+ read_str("meta/desc", patch.desc, sizeof(patch.desc));
+
+ uint8_t* code = read_file_mem("out/patch.bin");
+
+ patch.patch_size = size;
+
+ FILE* out = fopen("out/patch.vco", "wb");
+ fwrite(&patch, 1, sizeof(patch), out);
+ fwrite(code, 1, patch.patch_size, out);
+ fclose(out);
+
+ free(code);
+
+ printf("Ver: %u\n"
+ "CFW: %u\n"
+ "UUID: %llu\n"
+ "TID: %llu\n"
+ "Name: %s\n"
+ "Desc: %s\n"
+ "Size: %u\n",
+ patch.patch_ver, patch.load_ver, patch.patch_id,
+ patch.tid, patch.name, patch.desc, patch.patch_size);
+
+ return 0;
+}
--- /dev/null
+rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
+
+PATH := $(PATH):$(DEVKITARM)/bin
+
+HOST_CC := gcc
+
+CC := arm-none-eabi-gcc
+AS := arm-none-eabi-as
+LD := arm-none-eabi-ld
+OC := arm-none-eabi-objcopy
+
+dir_source := src
+dir_build := build
+dir_out := out
+
+ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
+CFLAGS := -MMD -MP -Wall -Wextra -Werror -Os $(ASFLAGS) -fno-builtin -std=c11 -DVERSION=\"$(REVISION)\"
+FLAGS := dir_out=$(abspath $(dir_out)) --no-print-directory
+LDFLAGS := -nostdlib -Wl,-z,defs
+
+objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
+ $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
+ $(call rwildcard, $(dir_source), *.s *.c)))
+
+.PHONY: all
+all: patchbin
+
+.PHONY: patchbin
+patchbin: tool $(dir_out)/patch.bin
+ ./compile_header
+
+.PHONY: tool
+tool:
+ $(HOST_CC) -o compile_header tool/compile_header.c
+
+
+.PHONY: clean
+clean:
+ rm -rf $(dir_out) $(dir_build) compile_header
+
+.PHONY: $(dir_out)/patch.bin
+$(dir_out)/patch.bin: $(dir_build)/main.bin
+ @mkdir -p "$(dir_out)"
+ @cp -av $< $@
+
+$(dir_build)/main.bin: $(dir_build)/main.elf
+ $(OC) -S -O binary $< $@
+
+$(dir_build)/main.elf: $(objects_cfw)
+ $(CC) -T linker.ld $(OUTPUT_OPTION) $^ $(LDFLAGS)
+
+$(dir_build)/%.o: $(dir_source)/%.c
+ @mkdir -p "$(@D)"
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+
+$(dir_build)/%.o: $(dir_source)/%.s
+ @mkdir -p "$(@D)"
+ $(COMPILE.s) $(OUTPUT_OPTION) $<
+
+include $(call rwildcard, $(dir_build), *.d)
--- /dev/null
+/* This memory map is mainly to assist in doing stuff in code.
+MEMORY
+{
+ INSTRUCTION_TCM (rw) : ORIGIN = 0x00000000, LENGTH = 0x08000000
+ ARM_INTERNAL (rw) : ORIGIN = 0x08000000, LENGTH = 0x00100000
+ NEW_INTERNAL (rw) : ORIGIN = 0x08100000, LENGTH = 0x00080000
+ IO_MEMORY (rw) : ORIGIN = 0x10000000, LENGTH = 0x08000000
+ VRAM (rw) : ORIGIN = 0x18000000, LENGTH = 0x00600000
+ DSP_MEMORY (rw) : ORIGIN = 0x1FF00000, LENGTH = 0x00080000
+ AXI_WRAM (rw) : ORIGIN = 0x1FF80000, LENGTH = 0x00080000
+ FCRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x08000000
+ NEW_FCRAM (rwx) : ORIGIN = 0x28000000, LENGTH = 0x08000000
+ DATA_TCM (rw) : ORIGIN = 0xFFF00000, LENGTH = 0x00004000
+ BOOTROM (rw) : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000
+} */
+
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x24400000;
+
+ START_SECTION = .;
+ .text.start : {
+ *(.text.start)
+ }
+ START_SECTION_END = .;
+
+ TABLE_SECTION = .;
+ .text.table : {
+ *(.text.table)
+ }
+ TABLE_SECTION_END = .;
+
+ TEXT_SECTION = .;
+ .text : {
+ *(.text)
+ }
+ TEXT_SECTION_END = .;
+
+ DATA_SECTION = .;
+ .data : {
+ *(.data)
+ }
+ DATA_SECTION_END = .;
+
+ BSS_SECTION = .;
+ .bss : {
+ *(.bss COMMON)
+ }
+ BSS_SECTION_END = .;
+
+ RODATA_SECTION = .;
+ .rodata : {
+ *(.rodata)
+ }
+ RODATA_SECTION_END = .;
+
+ . = ALIGN(4);
+}
--- /dev/null
+Prints "Hello World!"
--- /dev/null
+Example Patch
--- /dev/null
+#ifndef EXPORTED_H
+#define EXPORTED_H
+
+#define stdout (void*)0
+#define stderr (void*)1
+
+extern uint8_t* memory_offset;
+extern uint32_t* memory_len;
+
+extern int strlen(const char *string);
+extern int isprint(char c);
+extern void memcpy(void *dest, const void *src, size_t size);
+extern void memmove(void *dest, const void *src, size_t size);
+extern void memset(void *dest, const int filler, size_t size);
+extern int memcmp(const void *buf1, const void *buf2, const size_t size);
+extern void strncpy(void *dest, const void *src, const size_t size);
+extern int strncmp(const void *buf1, const void *buf2, const size_t size);
+extern int atoi(const char *str);
+extern uint8_t* memfind (uint8_t *string, uint32_t stringlen, uint8_t *pat, uint32_t patlen);
+
+extern void putc(void* buf, const int c);
+extern void puts(void* buf, const char *string);
+extern void fprintf(void* channel, const char* format, ...);
+
+#endif
--- /dev/null
+.section .text.table
+.align 4
+
+.macro stub name
+ .global \name
+ \name :
+ ldr pc, [pc, #-4] // Load the data after this to the PC. return will be before this call.
+ .byte 0 // Read; pc relative in arm mode is pc+8, so pc-4 is the correct offset
+ .byte 0
+ .byte 0
+ .byte 0
+.endm
+
+.macro ref name
+ .global \name
+ \name :
+ .byte 0
+ .byte 0
+ .byte 0
+ .byte 0
+.endm
+
+.global MAGIC_START
+MAGIC_START:
+ .byte 0xc0
+ .byte 0x9b
+ .byte 0xe5
+ .byte 0x1c
+
+// Memory to patch as specified by the header. (uint8_t*)
+ref memory_offset
+
+// Size of memory offset. (uint32_t*)
+ref memory_len
+
+// Exported functions.
+
+// memory.c
+stub strlen
+stub isprint
+stub memcpy
+stub memmove
+stub memset
+stub memcmp
+stub strncpy
+stub strncmp
+stub atoi
+stub memfind
+
+// draw.c
+stub putc
+stub puts
+stub fprintf
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include "exported.h"
+
+int main() {
+ // Your patch goes here.
+ fprintf(stderr, "Hi, I'm a patch!\n");
+
+ return 0;
+}
--- /dev/null
+.section .text.start
+.align 4
+.global _start
+_start:
+ stmdb sp!, {lr}
+ bl main
+ ldmia sp!, {lr}
+ bx lr // return from patch.
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../../source/patch_format.h"
+
+void read_file_u64(char* name, uint64_t* to) {
+ FILE* hdl = fopen(name, "rb");
+ fscanf(hdl, "%llu", to);
+ fclose(hdl);
+}
+
+
+void read_file_u32(char* name, uint32_t* to) {
+ FILE* hdl = fopen(name, "rb");
+ fscanf(hdl, "%u", to);
+ fclose(hdl);
+}
+
+void read_str(char* name, char* to, size_t len) {
+ FILE* hdl = fopen(name, "rb");
+ int r = fread(to, 1, len-1, hdl);
+ fclose(hdl);
+}
+
+uint32_t size = 0;
+
+uint8_t* read_file_mem(char* name) {
+ FILE* hdl = fopen(name, "rb");
+
+ fseek(hdl, 0, SEEK_END);
+ size = ftell(hdl);
+ rewind(hdl);
+
+ uint8_t* mem = malloc(size);
+ memset(mem, 0, size);
+
+ int r = fread(mem, 1, size, hdl);
+ fclose(hdl);
+
+ printf("%d\n", size);
+
+ return mem;
+}
+
+int main(int c, char** v) {
+ struct system_patch patch;
+ int at = 0;
+
+ memset(&patch, 0, sizeof(patch));
+
+ // Set magic.
+ patch.magic[0] = 'A';
+ patch.magic[1] = 'I';
+ patch.magic[2] = 'D';
+ patch.magic[3] = 'A';
+
+ read_file_u32("meta/patch_version", & patch.patch_ver);
+ read_file_u32("meta/cfw_version", & patch.load_ver);
+
+ read_file_u64("meta/uuid", & patch.patch_id);
+ read_file_u64("meta/title", & patch.tid);
+
+ read_str("meta/name", patch.name, sizeof(patch.name));
+ read_str("meta/desc", patch.desc, sizeof(patch.desc));
+
+ uint8_t* code = read_file_mem("out/patch.bin");
+
+ patch.patch_size = size;
+
+ FILE* out = fopen("out/patch.vco", "wb");
+ fwrite(&patch, 1, sizeof(patch), out);
+ fwrite(code, 1, patch.patch_size, out);
+ fclose(out);
+
+ free(code);
+
+ printf("Ver: %u\n"
+ "CFW: %u\n"
+ "UUID: %llu\n"
+ "TID: %llu\n"
+ "Name: %s\n"
+ "Desc: %s\n"
+ "Size: %u\n",
+ patch.patch_ver, patch.load_ver, patch.patch_id,
+ patch.tid, patch.name, patch.desc, patch.patch_size);
+
+ return 0;
+}
#define FCRAM_AGB_FIRM_LOC (FCRAM_START + FCRAM_SPACING * 3)
// patch.c
-#define FCRAM_MEMORY_LOC (FCRAM_START + FCRAM_SPACING * 4)
-
-_UNUSED static uint8_t *memory_loc = (uint8_t *)FCRAM_MEMORY_LOC;
-_UNUSED static void *current_memory_loc;
+#define FCRAM_PATCHBIN_EXEC_LOC (FCRAM_START + FCRAM_SPACING * 4)
#endif
--- /dev/null
+#include "common.h"
+#include "firm/fcram.h"
+
+int link_and_load_patchbin(char* path) {
+ fprintf(stderr, "Prep to relocate and execute patch.\n");
+
+ struct system_patch patch;
+
+ fprintf(stderr, "Loading patch vco.\n");
+
+ // Load patch from path.
+ FILE* f = fopen(path, "r");
+ fread(&patch, 1, sizeof(patch), f);
+
+ fprintf(stderr, "Patch payload is %d bytes.\n", patch.patch_size);
+
+ fread((uint8_t*)FCRAM_PATCHBIN_EXEC_LOC, 1, patch.patch_size, f);
+
+ fclose(f);
+
+ fprintf(stderr, "Finding relocation table.\n");
+ // Now then...find the magical number.
+ uint8_t magic[] = { 0xc0, 0x9b, 0xe5, 0x1c };
+ uint8_t* off = memfind((uint8_t*)FCRAM_PATCHBIN_EXEC_LOC, patch.patch_size, magic, 4);
+
+ if (off == NULL) {
+ fprintf(stderr, "Relocation table missing. Abort.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Relocation table is at %x\n", (uint32_t)off);
+
+ uint32_t* link_table = (uint32_t*)(off + 4);
+
+ // memory.c
+ link_table[3] = (uint32_t)strlen;
+ link_table[5] = (uint32_t)isprint;
+ link_table[7] = (uint32_t)memcpy;
+ link_table[9] = (uint32_t)memmove;
+ link_table[11] = (uint32_t)memset;
+ link_table[13] = (uint32_t)memcmp;
+ link_table[15] = (uint32_t)strncpy;
+ link_table[17] = (uint32_t)strncmp;
+ link_table[19] = (uint32_t)atoi;
+ link_table[21] = (uint32_t)memfind;
+
+ // draw.c
+ link_table[23] = (uint32_t)putc;
+ link_table[25] = (uint32_t)puts;
+ link_table[27] = (uint32_t)fprintf;
+
+ fprintf(stderr, "Copied relocations. Running binary.\n");
+
+ int (*exec)() = (void*)FCRAM_PATCHBIN_EXEC_LOC ;
+
+ return (*exec)();
+}
"Inject Loader",
"Enable ARM9 Thread",
- "Autoboot",
- "Silence debug output",
- "Trace steps with (A)",
+ "Autoboot (NYI)",
+ "Silence debug output (NYI)",
+ "Pause for input as trace",
- "Don't draw background color",
- "Preserve current framebuffer",
+ "Don't draw background color (NYI)",
+ "Preserve current framebuffer (NYI)",
"Hide Readme/Help from menu",
- "Ignore dependencies",
- "Allow enabling broken",
+ "Ignore dependencies (NYI)",
+ "Allow enabling broken (NYI)",
};
const int menu_max = 11;
uint64_t depends[16]; // What patches need to be applied for this patch to be applied; as unique IDs
uint32_t patch_size; // Size of the following patch data.
- uint8_t patch_data[]; // The data for the patch. This is a sort of interpreted code...see below.
+// uint8_t patch_data[]; // The data for the patch. This is a compiled binary for ARM9/11.
} __attribute__((packed));
struct patch_opcode {
#include "common.h"
uint32_t wait_key();
+int link_and_load_patchbin(char* path);
// A portion of this file is inherited from Luma3DS.
/*
}
int patch_firm_all() {
+ link_and_load_patchbin(PATH_PATCHES "/patch.vco");
+
+ wait();
+
// Use builtin signature patcher?
fprintf(stderr, "Sigpatch: %s\n", ((config.options[OPTION_SIGPATCH]) ? "yes" : "no" ));
- fprintf(stderr, "Protect: %s\n", ((config.options[OPTION_FIRMPROT]) ? "yes" : "no" ));
+ fprintf(stderr, "Protect: %s\n", ((config.options[OPTION_FIRMPROT]) ? "yes" : "no" ));
wait();