Skip to content

Instantly share code, notes, and snippets.

@patois
Created April 12, 2015 17:22

Revisions

  1. patois created this gist Apr 12, 2015.
    30 changes: 30 additions & 0 deletions firm.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    #pragma once
    #include "firm.h"

    int get_section_by_address (void *address) {
    int i;
    struct firm_section_header volatile *sh;

    for (i=0; i < FIRM_MAX_SECTION_COUNT; i++) {
    sh = &firm->section_headers[i];
    if ((sh->address <= address) && (address < (sh->address + sh->size))) {
    return i;
    }
    }
    return -1;
    }

    /* this could/should be added a few more checks */
    int is_valid_firm (void) {
    return (firm->magic == FIRM_MAGIC);
    }

    void dump_section_header (int index) {
    struct firm_section_header volatile *sh;

    sh = &firm->section_headers[index];
    Debug("Section %02d/%02d (ARM%s):", index, FIRM_MAX_SECTION_COUNT, sh->type ? "11" : "9");
    Debug("%08X - %08X", sh->address, sh->address + sh->size);
    Debug("");
    return;
    }
    31 changes: 31 additions & 0 deletions firm.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    #pragma once

    #include "common.h"

    int get_section_by_address (void *address);
    int is_valid_firm (void);
    void dump_section_header (int index);

    #define PA_FIRM_IMAGE 0x24000000
    #define FIRM_MAGIC 0x4D524946
    #define FIRM_MAX_SECTION_COUNT 0x4

    typedef struct firm_section_header {
    u32 offset;
    void *address;
    u32 size;
    u32 type;
    u8 hash[0x20];
    } _firm_section_header;

    typedef struct firm_header {
    u32 magic;
    u32 reserved1;
    void *arm11_ep;
    void *arm9_ep;
    u8 reserved2[0x30];
    struct firm_section_header section_headers[4];
    u8 rsa_sig[0x100];
    } _firm_header;

    static struct firm_header volatile *const firm = (struct firm_header *)PA_FIRM_IMAGE;
    19 changes: 19 additions & 0 deletions hid.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    #include "draw.h"
    #include "hid.h"

    u32 InputWait() {
    u32 pad_state_old = HID_STATE;
    while (true) {
    u32 pad_state = HID_STATE;
    if (pad_state ^ pad_state_old)
    return ~pad_state;
    }
    }

    u32 wait_key (char *msg) {
    if (msg) {
    Debug("");
    Debug("%s", msg);
    }
    return InputWait();
    }
    21 changes: 21 additions & 0 deletions hid.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    #pragma once

    #include "common.h"

    u32 InputWait();
    u32 wait_key (char *msg);

    #define HID_STATE (*(volatile u32*)0x10146000)

    #define BUTTON_A (1 << 0)
    #define BUTTON_B (1 << 1)
    #define BUTTON_SELECT (1 << 2)
    #define BUTTON_START (1 << 3)
    #define BUTTON_RIGHT (1 << 4)
    #define BUTTON_LEFT (1 << 5)
    #define BUTTON_UP (1 << 6)
    #define BUTTON_DOWN (1 << 7)
    #define BUTTON_R1 (1 << 8)
    #define BUTTON_L1 (1 << 9)
    #define BUTTON_X (1 << 10)
    #define BUTTON_Y (1 << 11)
    121 changes: 121 additions & 0 deletions main.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,121 @@
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>

    #include "draw.h"
    #include "hid.h"
    #include "firm.h"


    void ClearTop (void) {
    ClearScreen(TOP_SCREEN0, RGB(255, 255, 255));
    ClearScreen(TOP_SCREEN1, RGB(255, 255, 255));
    current_y = 0;
    return;
    }

    int patch (void *address, u8 *sig, u32 size) {
    int result = 0;

    if (address && sig && size) {
    memcpy(address, sig, size);
    result = 1;
    }
    return result;
    }

    void *find_signature (void *start_addr, void *end_addr, u8 *sig, u32 size) {
    u8 *p = start_addr;
    void *max_addr = 0;

    if (sig &&
    (u32)start_addr < (u32)end_addr &&
    (u32)end_addr > size) {
    max_addr = end_addr - size;
    while (p < (u8 *)max_addr) {
    if ((*p == *sig) && (memcmp(p, sig, size) == 0)) {
    return p;
    }
    p++;
    }
    }
    return 0;
    }

    void fatal(void) {
    Debug("Signature not found! Please Turn off your 3DS");
    while (1)
    ;
    }

    int main (void) {
    ClearTop();
    Debug("");
    int sect;
    u8 *sig_addr = 0;
    u32 key;

    u8 sig[32] = {
    0x20,0xA0,0xDA,0xE5, 0x07,0xE0,0x09,0xE2,
    0x1B,0x0E,0x1A,0xE1, 0x31,0xA0,0xDD,0xE5,
    0xEA,0xFF,0xFF,0x0A, 0x00,0x00,0x5A,0xE3,
    0x0D,0x00,0x00,0x0A, 0x00,0xE0,0xA0,0xE3};

    u8 sig_patch[32] = {
    0x20,0xA0,0xDA,0xE5, 0x07,0xE0,0x09,0xE2,
    0x1B,0x0E,0x1A,0xE1, 0x31,0xA0,0xDD,0xE5,
    0x00,0xF0,0x20,0xE3, 0x00,0x00,0x5A,0xE3,
    0x00,0xF0,0x20,0xE3, 0x00,0xE0,0xA0,0xE3};

    Debug("Simple memory patcher example");
    Debug("=============================");
    Debug("");
    Debug("Finds a byte signature in memory and");
    Debug("replaces it with a new signature");
    Debug("");

    if (is_valid_firm()) {
    sect = get_section_by_address(firm->arm11_ep);
    if (sect != -1) {
    Debug("ARM11 entry point found in:");
    dump_section_header(sect);

    if (wait_key("Press START to find signature in memory") & BUTTON_START) {
    sig_addr = firm->section_headers[sect].address;
    while ((sig_addr = find_signature(sig_addr,
    firm->section_headers[sect].address +
    firm->section_headers[sect].size,
    (u8 *)&sig,
    sizeof(sig))) != 0) {

    ClearTop();
    Debug("Signature found at %08X", sig_addr);
    Debug("X : Confirm patch (at your own risk)");
    Debug("B : Skip/Cancel");

    while (1) {
    key = wait_key("");
    if (key & BUTTON_X) {
    patch(sig_addr, (u8 *)&sig_patch, sizeof(sig_patch));
    Debug("Signature patched");
    break;
    } else if (key & BUTTON_B) {
    break;
    }
    }
    sig_addr++;
    }
    } else {
    Debug("Aborted");
    }
    } else { /* ARM11 entry point outside of section headers */
    fatal();
    }
    } else { /* invalid FIRM header */
    fatal();
    }

    wait_key("Press any key to launch firm.");
    // return control to FIRM ARM9 code (performs firmlaunch)
    return 0;
    }