]> Chaos Git - corbenik/corbenik.git/commitdiff
Now I just need to write the interpreter for the bytecode; I can assemble it, and...
authorchaoskagami <chaos.kagami@gmail.com>
Thu, 2 Jun 2016 07:05:49 +0000 (03:05 -0400)
committerchaoskagami <chaos.kagami@gmail.com>
Thu, 2 Jun 2016 07:05:49 +0000 (03:05 -0400)
18 files changed:
doc/bytecode.md
host/bytecode_asm.py [new file with mode: 0755]
patch/Makefile [new file with mode: 0644]
patch/aadowngrade.pco [new file with mode: 0644]
patch/block_cart_update.pco [new file with mode: 0644]
patch/block_eshop_update.pco [new file with mode: 0644]
patch/block_nim_update.pco [new file with mode: 0644]
patch/errdisp.pco [new file with mode: 0644]
patch/friends_ver.pco [new file with mode: 0644]
patch/memexec.pco [new file with mode: 0644]
patch/mset_str.pco [new file with mode: 0644]
patch/prot.pco [new file with mode: 0644]
patch/regionfree.pco [new file with mode: 0644]
patch/ro_sigs.pco [new file with mode: 0644]
patch/secinfo_sigs.pco [new file with mode: 0644]
patch/sig.pco [new file with mode: 0644]
patch/unitinfo.pco [new file with mode: 0644]
source/patch_format.h

index 56839f4c7ac3cf62df04d89955d2b3fb17dae4f1..5bffbf4b31ab6ca98b27d839e84436df3cb9c2f0 100644 (file)
@@ -3,6 +3,16 @@ Bytecode format
 
 Instructions are one byte and have a variable number of bytes afterwards.
 
+Note that this file describes the instruction set AS THE VM INTERPRETS IT,
+e.g. not as you'd write it for the assembler.
+
+Any integers of greater than one byte are not re-ordered to fit endianness.
+If you specify 0001 as a value, the vm will read this as:
+
+   uint16_t val = *((uint16_t*){0x00, 0x01})
+
+Be aware of this.
+
 Unless otherwise noted, if an instruction doesn't succeed, it will abort.
 
 nop : 1 byte : Opcode 0x00
@@ -36,6 +46,8 @@ rel <mode> : 2 bytes : Opcode 0x01
                16: TWL Section 2
                17: TWL Section 3
 
+               18: Loader title (see info)
+
 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.
@@ -45,16 +57,16 @@ find <size> <pattern...> : 2 + size bytes : opcode 0x02
        <pattern> : <size> bytes
                data to find
 
-back <count> : 5 bytes : opcode 0x03
+back <count> : 2 bytes : opcode 0x03
        Moves back <count> bytes from current position.
 
-       <count> : 4 bytes
+       <count> : 1 byte
                How many bytes to rewind.
 
-fwd <count> : 5 bytes : opcode 0x04
+fwd <count> : 2 bytes : opcode 0x04
        Moves forward <count> bytes from current position.
 
-       <count> : 4 bytes
+       <count> : 1 byte
                How many bytes to rewind.
 
 set <size> <data...> : 2 + size bytes : opcode 0x05
@@ -66,7 +78,7 @@ set <size> <data...> : 2 + size bytes : opcode 0x05
        <data> : <size> bytes
                Data to copy.
 
-test <size> <data...> : 2 bytes : opcode 0x06
+test <size> <data...> : 2 + size 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.
@@ -96,3 +108,18 @@ and <size> <data...> : 2 + <size> bytes : opcode 0x09
 
        <data> : <size> bytes
                Data to bitwise and with relative data.
+
+title <count> <title> : 2 + <count> * 8 bytes : 0x0A
+       Specifies that the following code is applicable only when being applied to
+       a list of titles.
+
+       This allows the creation of generic patches which can be used with multiple
+       titles and share common parts.
+
+       The default state is to apply code on any titleID matches within the header,
+       so unless you have specialized needs you'll almost never need this.
+
+       <count> : 1 byte
+               How many titleIDs to read.
+       <title> : 8 * <count> bytes
+               List of titleIDs as u64.
diff --git a/host/bytecode_asm.py b/host/bytecode_asm.py
new file mode 100755 (executable)
index 0000000..19e6116
--- /dev/null
@@ -0,0 +1,214 @@
+#!/usr/bin/env python2
+# -*- encoding: utf8 -*-
+
+import os
+import sys
+import re
+import struct
+
+in_file  = ""
+out_file = ""
+
+def usage():
+       print("Usage: " + sys.argv[0] + " <input.bas> <output.bco>")
+
+lines = 0
+def syn_err(x):
+       print("error - " + str(line) + " - " + x)
+       exit(2)
+
+def rel_name(x):
+       return {
+               'native'   : "00",
+               'agb'      : "01",
+               'twl'      : "02",
+
+               'native_p9': "03",
+               'agb_p9'   : "04",
+               'twl_p9'   : "05",
+
+               'native_s0': "06",
+               'native_s1': "07",
+               'native_s2': "08",
+               'native_s3': "09",
+
+               'agb_s0'   : "0A",
+               'agb_s1'   : "0B",
+               'agb_s2'   : "0C",
+               'agb_s3'   : "0D",
+
+               'twl_s0'   : "0E",
+               'twl_s1'   : "0F",
+               'twl_s2'   : "10",
+               'twl_s3'   : "11",
+       }.get(x, "-1")
+
+name = "NO NAME"
+desc = "NO DESC"
+title = []
+ver = "01"
+flags = []
+uuid = ""
+deps = []
+
+def cat_list(list):
+       retstr = ""
+       for str in list:
+               retstr += str + " "
+       return retstr
+
+def parse_op(token_list):
+       global title
+       global desc
+       global name
+       global ver
+       global flags
+       global uuid
+       global deps
+       s = len(token_list) # Get size.
+       if s == 0:
+               return bytearray() # Empty.
+
+       if token_list[0] == "#": # Comment.
+               if s < 3:
+                       return bytearray() # Nope.
+               elif token_list[1] == "$name": # Meta: name
+                       name = cat_list(token_list[2:])
+               elif token_list[1] == "$desc": # Description
+                       desc = cat_list(token_list[2:])
+               elif token_list[1] == "$title": # Title list
+                       title = token_list[2:]
+               elif token_list[1] == "$ver": # Version
+                       ver = token_list[2]
+               elif token_list[1] == "$uuid": # UUID
+                       uuid = token_list[2]
+               elif token_list[1] == "$flags": # Flags
+                       flags = token_list[2:]
+               elif token_list[1] == "$deps": # Flags
+                       deps = token_list[2:]
+
+               return bytearray()
+
+
+       if token_list[0] == "nop": # Nop. Expects 0 args.
+               return bytearray.fromhex("00")
+       elif token_list[0] == "rel": # Rel. Expects one argument. Possibly requires mapping.
+               if s != 2:
+                       syn_err("expected one argument")
+
+               index = rel_name(token_list[1])
+               if index == "-1":
+                       # TODO - Check if an integer was passed.
+                       syn_error("invalid argument")
+
+               return bytearray.fromhex("01" + index)
+       elif token_list[0] == "find":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               # We cut corners and calculate stuff manually.
+               return bytearray.fromhex("02") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+       elif token_list[0] == "back":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               return bytearray.fromhex("03" + token_list[1])
+       elif token_list[0] == "fwd":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               return bytearray.fromhex("04" + token_list[1])
+       elif token_list[0] == "set":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               # We cut corners and calculate stuff manually.
+               return bytearray.fromhex("05") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+       elif token_list[0] == "test":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               # We cut corners and calculate stuff manually.
+               return bytearray.fromhex("06") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+       elif token_list[0] == "jmp":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               if len(token_list[1] == 2):
+                       token_list[1] = "00" + token_list[1]
+
+               return bytearray.fromhex("07" + token_list[1])
+       elif token_list[0] == "rewind":
+               return bytearray.fromhex("08")
+       elif token_list[0] == "and":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               # We cut corners and calculate stuff manually.
+               return bytearray.fromhex("09") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+       elif token_list[0] == "title":
+               if s != 2:
+                       syn_err("invalid number of arguments")
+
+               return bytearray.fromhex("0A") + bytearray([len(token_list[1]) / 2 / 8]) + bytearray.fromhex(token_list[1])
+
+def pad_zero_r(x, c):
+       while len(x) < c:
+               x = x + bytearray([0])
+       return x
+
+
+def pad_zero_l(x, c):
+       while len(x) < c:
+               x = bytearray([0]) + x
+       return x
+
+def flag_convert(x):
+       flags = 0
+       for f in x:
+               if f == "require":
+                       flags &= 0x1
+               if f == "devmode":
+                       flags &= 0x2
+               if f == "noabort":
+                       flags &= 0x4
+       return pad_zero_r(bytearray([flags]), 4)
+
+try:
+       # Read input and output files.
+       in_file  = sys.argv[1]
+       out_file = sys.argv[2]
+except:
+       usage()
+       exit(1)
+
+size = 0
+
+with open(in_file, "r") as ins:
+       with open(out_file, "wb") as writ:
+               bytecode = bytearray()
+
+               for line in ins:
+                       lines += 1
+                       tokens = re.split("\s+", line.strip("\n")) # Split by whitespace.
+                       bytes = parse_op(tokens) # Parse.
+                       if bytes:
+                               size += len(bytes)
+                               bytecode += bytes
+
+               data  = bytearray("AIDA")
+               data += bytearray.fromhex(ver)
+               data += pad_zero_r(bytearray(name),         64)
+               data += pad_zero_r(bytearray(desc),         256)
+               data += pad_zero_r(bytearray.fromhex(uuid), 8)
+               data += flag_convert(flags)
+               data += struct.pack('I', len(title))
+               data += struct.pack('I', len(deps))
+               data += struct.pack('I', size)
+               for f in title:
+                       data += bytearray.fromhex(f)
+               for f in deps:
+                       data += pad_zero_r(bytearray.fromhex(f), 8)
+               data += bytecode
+               writ.write(data)
+
diff --git a/patch/Makefile b/patch/Makefile
new file mode 100644 (file)
index 0000000..8d6fd62
--- /dev/null
@@ -0,0 +1,8 @@
+.PHONY: all
+all: $(patsubst %.pco, %.vco, $(wildcard *.pco))
+
+%.vco: %.pco
+       ../host/bytecode_asm.py $< $@
+
+clean:
+       rm -f *.vco
diff --git a/patch/aadowngrade.pco b/patch/aadowngrade.pco
new file mode 100644 (file)
index 0000000..9439f10
--- /dev/null
@@ -0,0 +1,15 @@
+# $name Title Downgrade Fix
+# $desc Removes added checks to prevent downgrade (in 11.0)
+# $ver  01
+# $uuid 00
+# $flags require
+
+# Anti-anti-downgrade fix.
+
+# Relative to process9.
+rel  native_p9
+
+# We want to patch the fifth byte of this pattern.
+find 890a814202D2
+fwd  05
+set  E0
diff --git a/patch/block_cart_update.pco b/patch/block_cart_update.pco
new file mode 100644 (file)
index 0000000..305e5b0
--- /dev/null
@@ -0,0 +1,11 @@
+# $name  Block Cart Updates
+# $desc  Blocks update checks on cartridges (and also allows region free carts, as a byproduct.)
+# $title 0004013000008002
+# $ver   01
+# $uuid  01
+
+find 0C18E1D8
+set  0B1821C8
+
+find 0C18E1D8
+set  0B1821C8
diff --git a/patch/block_eshop_update.pco b/patch/block_eshop_update.pco
new file mode 100644 (file)
index 0000000..1cd18c2
--- /dev/null
@@ -0,0 +1,8 @@
+# $name  Block eShop Updates
+# $desc  Prevents eShop from checking for system updates.
+# $title 0004013000002C02
+# $ver   01
+# $uuid  02
+
+find 30B5F1B0
+set  002008607047
diff --git a/patch/block_nim_update.pco b/patch/block_nim_update.pco
new file mode 100644 (file)
index 0000000..19ecfce
--- /dev/null
@@ -0,0 +1,8 @@
+# $name  Block NIM updates
+# $desc  Prevents NIM from downloading system updates.
+# $title 0004013000002C02
+# $ver   01
+# $uuid  03
+
+find 25790B99
+set  E3A0
diff --git a/patch/errdisp.pco b/patch/errdisp.pco
new file mode 100644 (file)
index 0000000..227bb90
--- /dev/null
@@ -0,0 +1,16 @@
+# $name  ErrDisp devmode
+# $desc  Forces ErrDisp into displaying developer info without developer UNITINFO. Doesn't break eShop.
+# $title 0004003000008A02
+# $ver   01
+# $uuid  04
+# $flags devmode
+
+find 1400D0E5DB9A9FED
+set  0000A0E3
+
+find 1400D0E5010010E3
+set  0000A0E3
+find 1400D0E5010010E3
+set  0000A0E3
+find 1400D0E5010010E3
+set  0000A0E3
diff --git a/patch/friends_ver.pco b/patch/friends_ver.pco
new file mode 100644 (file)
index 0000000..42e8e6f
--- /dev/null
@@ -0,0 +1,10 @@
+# $name  Fake Friends module version
+# $desc  Allows going online without the latest firmware.
+# $title 0004013000003202
+# $ver   01
+# $uuid  05
+# $flags noabort
+
+find E01EFF2FE1010101
+fwd  09
+set  06
diff --git a/patch/memexec.pco b/patch/memexec.pco
new file mode 100644 (file)
index 0000000..d863359
--- /dev/null
@@ -0,0 +1,21 @@
+# $name ARM11 XN Disable
+# $desc Disables the XN bit on the ARM11 kernel to allow executing code from all memory. May be unsafe.
+# $ver  01
+# $uuid 06
+# $flags devmode
+
+# Relative to NATIVE_FIRM, section index [1]
+rel native_s1
+
+# Find this byte string.
+find 9705000015E40000
+
+# Move backwards until we find what we want.
+# LOOP: (3)
+       back 01
+       test 00016416
+       jmp  0007
+       jmp  0003
+
+# END LOOP: (7)
+       and efff
diff --git a/patch/mset_str.pco b/patch/mset_str.pco
new file mode 100644 (file)
index 0000000..a3f2219
--- /dev/null
@@ -0,0 +1,11 @@
+# $name  MSET Version
+# $desc  Replaces 'Ver.' with CFW info.
+# $title 0004001000021000 0004001000020000 0004001000022000 0004001000026000 0004001000027000 0004001000028000
+# $ver   01
+# $uuid  07
+
+# u"Ver."
+find 5600650072002e00
+
+# u".hax"
+set  2e006800610078
diff --git a/patch/prot.pco b/patch/prot.pco
new file mode 100644 (file)
index 0000000..065520a
--- /dev/null
@@ -0,0 +1,13 @@
+# $name FIRM Protect
+# $desc Prevents writing FIRM to the NAND during updates.
+# $ver  01
+# $uuid 08
+# $flags require
+
+rel native_p9
+# String: 'exe:'
+find 6578653a
+back 01
+back ff
+find 002801DA
+set  0020C046
diff --git a/patch/regionfree.pco b/patch/regionfree.pco
new file mode 100644 (file)
index 0000000..35e297e
--- /dev/null
@@ -0,0 +1,10 @@
+# $name  Region free HOME
+# $desc  Allows launching installed software from any region.
+# $title 0004003000008F02 0004003000008202 0004003000009802 000400300000A102 000400300000A902 000400300000B102
+# $ver   01
+# $uuid  09
+
+find 000055E30110A0E3
+# 16
+back 11
+set  0100A0E31EFF2FE1
diff --git a/patch/ro_sigs.pco b/patch/ro_sigs.pco
new file mode 100644 (file)
index 0000000..47f0f68
--- /dev/null
@@ -0,0 +1,14 @@
+# $name  RO signature fix
+# $desc  Allows usage of unsigned CRO files. This is useful for romhacks.
+# $title 0004013000003702
+# $ver   01
+# $uuid  0a
+
+find 30402DE90250A0E1
+set  0000A0E31EFF2FE1
+
+find 30402DE924D04DE2
+set  0000A0E31EFF2FE1
+
+find F84F2DE90170A0E1
+set  0000A0E31EFF2FE1
diff --git a/patch/secinfo_sigs.pco b/patch/secinfo_sigs.pco
new file mode 100644 (file)
index 0000000..12a3dfe
--- /dev/null
@@ -0,0 +1,8 @@
+# $name  SecureInfo_A Signature Fix
+# $desc  Allows using unsigned or improperly signed SecureInfo_A files. Useful for region changes.
+# $title 0004013000001702
+# $ver   01
+# $uuid  0b
+
+find 06461048FC
+set  0026
diff --git a/patch/sig.pco b/patch/sig.pco
new file mode 100644 (file)
index 0000000..73ea077
--- /dev/null
@@ -0,0 +1,21 @@
+# $name Signature Fix
+# $desc Disables signature checks on content.
+# $ver  01
+# $uuid 0c
+# $flags require
+
+# Signature patch.
+
+# Relative to exefs.
+rel  native_p9
+
+# Pattern one.
+find c01c76e7
+set  0020
+
+# Rewind to beginning.
+rewind
+
+# Pattern 2.
+find b5224d0c
+set  00207047
diff --git a/patch/unitinfo.pco b/patch/unitinfo.pco
new file mode 100644 (file)
index 0000000..7b9b40c
--- /dev/null
@@ -0,0 +1,10 @@
+# $name Developer UNITINFO
+# $desc Imitates a panda's configuration. For the average user, loader ErrDisp is enough.
+# $ver  01
+# $uuid 0d
+# $flags devmode
+
+rel  native_s2
+find 0110A013
+fwd  03
+set  E3
index 580cc279be446e4a3c28a3a7379abc769b9275f7..3f83e7815df7ab5c55ebe6447607baa5b0cca24a 100644 (file)
 // Structure of a patch file.
 struct system_patch
 {
-    char magic[4]; // "AIDA" for shits and giggles and because we like .hack.
-    uint32_t patch_ver; // Version of the patch itself.
-    uint32_t load_ver;  // Version of the CFW the patch is intended for.
+    char     magic[4];     // "AIDA" for shits and giggles and because we like .hack.
+    uint32_t version;      // Version of the patch itself.
 
-    char name[64];     // User-readable name for patch in menu.
-    char desc[256];    // User-readable description for patch in menu.
-    uint64_t patch_id; // Unique ID for patch. Each unique patch should provide
-                       // a unique ID.
+    char     name[64];     // User-readable name for patch in menu.
+    char     desc[256];    // User-readable description for patch in menu.
+    uint64_t uuid;         // Unique ID for patch. Each unique patch should provide
+                           // a unique ID.
 
-    uint64_t tid; // What title this patch is intended for. Certain values are
-                  // specially handled.
+    uint8_t  flags;        // Extra flags for patch.
 
-    uint8_t extra_flags; // Extra flags for patch.
+       #define PATCH_FLAG_REQUIRE (1 << 0) // Force enable patch unless 'Unsafe Options' is checked.
+       #define PATCH_FLAG_DEVMODE (1 << 1) // Require 'Developer Options' to be checked.
+       #define PATCH_FLAG_NOABORT (1 << 2) // Don't abort on error.
 
-    uint64_t depends[16]; // What patches need to be applied for this patch to
-                          // be applied; as unique IDs
+    uint32_t titles;       // What title this patch is intended for. Certain values are
+                           // specially handled.
 
-    uint32_t patch_size; // Size of the following patch data.
-    //    uint8_t patch_data[];     // The data for the patch. This is a
-    //    compiled binary for ARM9/11.
-} __attribute__((packed));
+    uint32_t depends;      // How many deps there are.
 
-struct patch_opcode
-{
+    uint32_t size;         // Size of the patch bytecode in bytes.
+
+       // This stuff needs to be read not as part of the struct, but is technically part of it.
+
+    // uint64_t tids[titles]     // TitleIDs.
+    // uint64_t deps[depends]    // Dependencies as uuid refs.
+    // uint8_t  patch_data[size] // Patch data.
 } __attribute__((packed));
 
 #endif