Bytecode format
===================
-All instructions are dwords (four bytes) - this is for optimization reasons.
-
-Registers
--------------------
-r1-r4 - General
-pc - Current offset in bytecode
-mem - Memory offset set.
-
-Instruction List
--------------------
-1) call index
-2) add reg, reg
-3) sub reg, reg
-4) mul reg, reg
-5) div reg, reg
-6) mov reg/ref/imm, reg/ref/imm
-7) push reg
-8) pop <reg>
-9) .byte byte
+Instructions are one byte and have a variable number of bytes afterwards.
+Unless otherwise noted, if an instruction doesn't succeed, it will abort.
+
+rel <mode> : 2 bytes : Opcode 0x01
+ Chooses firmware relativity.
+
+ <mode> : The location and size to operate in.
+ 0: NATIVE_FIRM (whole size)
+ 1: AGB_FIRM (whole size)
+ 2: TWL_FIRM (whole size)
+
+ 3: Native Proc9 ExeFS
+ 4: AGB Proc9 ExeFS
+ 5: TWL Proc9 ExeFS
+
+ 6: Native Section 0
+ 7: Native Section 1
+ 8: Native Section 2
+ 9: Native Section 3
+
+ 10: AGB Section 0
+ 11: AGB Section 1
+ 12: AGB Section 2
+ 13: AGB Section 3
+
+ 14: TWL Section 0
+ 15: TWL Section 1
+ 16: TWL Section 2
+ 17: TWL Section 3
+
+find <size> <pattern...> : 2 + size bytes : opcode 0x02
+ Finds a pattern in memory. On success, operations
+ will be performed relative to the beginning of the found pattern.
+
+ <size> : 1 byte
+ How many bytes the pattern is.
+ <pattern> : <size> bytes
+ data to find
+
+back <count> : 5 bytes : opcode 0x03
+ Moves back <count> bytes from current position.
+
+ <count> : 4 bytes
+ How many bytes to rewind.
+
+fwd <count> : 5 bytes : opcode 0x04
+ Moves forward <count> bytes from current position.
+
+ <count> : 4 bytes
+ How many bytes to rewind.
+
+set <size> <data...> : 2 + size bytes : opcode 0x05
+ Copies the bytes in <data> to the current location pointed to,
+ and increments the current location by <size> bytes copied.
+
+ <size> : 1 byte
+ How many bytes to copy.
+ <data> : <size> bytes
+ Data to copy.
+
+test <size> <data...> : 2 bytes : opcode 0x06
+ Tests if the current location's data is equivalent to <data>.
+ If equivalent, goes to the next instruction. If not, skips
+ one operation.
+
+ NO ABORT ON FAIL
+
+ <size> : 1 byte
+ Size of data to test against.
+ <data> : <size> bytes
+ Pattern to test.
+
+jmp <offset> : 3 bytes : opcode 0x07
+ Jumps to <offset> within the bytecode, and resumes execution from there.
+
+ <offset> : 2 bytes
+ Offset to jump to.
+
+rewind : 1 byte : opcode 0x08
+ Resets the location to the beginning of the space we're working off.
+
+and <size> <data...> : 2 + <size> bytes : opcode 0x09
+ Performs an AND operation bitwise using data as a mask.
+
+ <size> : 1 byte
+ Size of <data>.
+
+ <data> : <size> bytes
+ Data to bitwise and with relative data.
#include "patch_file.h"
-// Do you like examples?
-
-/* In bytecode assembly:
-
- aadowngrade:
- rel firm_mem
- mov4 r1, pattern
- mov4 r2, 6
- call memfind
- jmpz notfound
-
- found:
- add r1, 5
- mov1 [r1], 0xE0
- mov4 r1, 0
- return
-
- notfound:
- mov4 r1, 1
- return
-
- pattern:
- .byte 0x89
- .byte 0x0A
- .byte 0x81
- .byte 0x42
- .byte 0x02
- .byte 0xD2
+/*
+ rel p9_exefs
+ find 6, 0x89, 0x0a, 0x81, 0x42, 0x02, 0xD2
+ fwd 5
+ set 1, 0xE0
*/
PATCH(aadowngrade)
// Do you like examples?
-/* Bytecode assembly:
-
- example:
- mov r1, 2
- mov r2, string
- call fprintf
- mov r1, 0
- return
-
- string:
- .str "Testing, testing, 1, 2, 3...\n"
- */
-
PATCH(example)
{
fprintf(stderr, "Testing, testing, 1, 2, 3, 4..\n");
// behavioral change that can be used maliciously and/or to
// detect CFW use rather easily.
+/*
+ rel native_s1
+ find 8, 0x97, 0x05, 0x00, 0x00, 0x15, 0xE4, 0x00, 0x00
+ back 1
+ test 4, 0x00016416
+ jmp 7
+ jmp 3
+ and ~(1<<4)
+ */
+
PATCH(memexec)
{
firm_section_h* arm11_section = & firm_loc->section[1]; // Section 1, please.
#include "patch_file.h"
+/* Not possible to be implemented as bytecode. Hey, can't win em all. */
+
PATCH(modules)
{
// TODO - load other module cxis here
// This patch applies the FIRM protection code needed for safe a9lh usage.
-/* Bytecode assembly:
-
- firmprot:
- rel firm_mem
- mov r1, exe_str
- mov r2, 4
- call memfind
- jmpz nostr
-
- mov r3, r1
- mov r1, 2
- mov r2, str_atoff
- call fprintf
-
-
-
- nostr:
- mov r1, 2
- mov r2, noexe_str_str
- call fprintf
- mov r1, 0
- return
-
- exe_str:
- .str "exe:"
- noexe_str_str:
- .str "Couldn't find 'exe' string.\n"
- str_atoff:
- .str "Firmprot: 'exe:' string @ %x\n"
- firmprot_code:
- .byte 0x00
- .byte 0x28
- .byte 0x01
- .byte 0xDA
+/*
+ rel p9_exefs
+ find 4, "exe:"
+ back 0x1
+ back 0xff
+ find 4, 0x00, 0x28, 0x01, 0xDA
+ set 4, 0x00, 0x20, 0xC0, 0x46
*/
PATCH(firmprot)
// This patch is responsible for fixing signature checks for the firmware.
+/*
+ rel p9_exefs
+ find 4, 0xc0, 0x1c, 0x76, 0xe7
+ set 2, 0x00, 0x20
+ rewind
+ find 4, 0xb5, 0x22, 0x4d, 0x0c
+ set 4, 0x00, 0x20, 0x70, 0x47
+ */
+
PATCH(signatures)
{
exefs_h* firm_p9_exefs = get_firm_proc9_exefs();
#include "patch_file.h"
+/* Not possible to do bytecode here, sadly. */
+
// This patch handles replacement of services. This includes backdoor, but not
// just backdoor.
// Any service can be replaced provided there's enough space within the
// usually the ErrDisp patch in loader should be good enough for
// debugging crashes.
+/*
+ rel native_arm9
+ find 4, 0x01, 0x10, 0xA0, 0x13
+ fwd 3
+ set 1, 0xE3
+ */
+
PATCH(unitinfo)
{
firm_section_h* arm9_section;