From: chaoskagami Date: Thu, 2 Jun 2016 07:05:49 +0000 (-0400) Subject: Now I just need to write the interpreter for the bytecode; I can assemble it, and... X-Git-Tag: stable-1~18 X-Git-Url: https://chaos.moe/g/?a=commitdiff_plain;h=cd9d6af770c9f0471c5661b7dd89c19427257f6a;p=corbenik%2Fcorbenik.git Now I just need to write the interpreter for the bytecode; I can assemble it, and theoretically there's no flaws --- diff --git a/doc/bytecode.md b/doc/bytecode.md index 56839f4..5bffbf4 100644 --- a/doc/bytecode.md +++ b/doc/bytecode.md @@ -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 : 2 bytes : Opcode 0x01 16: TWL Section 2 17: TWL Section 3 + 18: Loader title (see info) + find : 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 : 2 + size bytes : opcode 0x02 : bytes data to find -back : 5 bytes : opcode 0x03 +back : 2 bytes : opcode 0x03 Moves back bytes from current position. - : 4 bytes + : 1 byte How many bytes to rewind. -fwd : 5 bytes : opcode 0x04 +fwd : 2 bytes : opcode 0x04 Moves forward bytes from current position. - : 4 bytes + : 1 byte How many bytes to rewind. set : 2 + size bytes : opcode 0x05 @@ -66,7 +78,7 @@ set : 2 + size bytes : opcode 0x05 : bytes Data to copy. -test : 2 bytes : opcode 0x06 +test : 2 + size bytes : opcode 0x06 Tests if the current location's data is equivalent to . If equivalent, goes to the next instruction. If not, skips one operation. @@ -96,3 +108,18 @@ and : 2 + bytes : opcode 0x09 : bytes Data to bitwise and with relative data. + +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 index 0000000..19e6116 --- /dev/null +++ b/host/bytecode_asm.py @@ -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 index 0000000..8d6fd62 --- /dev/null +++ b/patch/Makefile @@ -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 index 0000000..9439f10 --- /dev/null +++ b/patch/aadowngrade.pco @@ -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 index 0000000..305e5b0 --- /dev/null +++ b/patch/block_cart_update.pco @@ -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 index 0000000..1cd18c2 --- /dev/null +++ b/patch/block_eshop_update.pco @@ -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 index 0000000..19ecfce --- /dev/null +++ b/patch/block_nim_update.pco @@ -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 index 0000000..227bb90 --- /dev/null +++ b/patch/errdisp.pco @@ -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 index 0000000..42e8e6f --- /dev/null +++ b/patch/friends_ver.pco @@ -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 index 0000000..d863359 --- /dev/null +++ b/patch/memexec.pco @@ -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 index 0000000..a3f2219 --- /dev/null +++ b/patch/mset_str.pco @@ -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 index 0000000..065520a --- /dev/null +++ b/patch/prot.pco @@ -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 index 0000000..35e297e --- /dev/null +++ b/patch/regionfree.pco @@ -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 index 0000000..47f0f68 --- /dev/null +++ b/patch/ro_sigs.pco @@ -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 index 0000000..12a3dfe --- /dev/null +++ b/patch/secinfo_sigs.pco @@ -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 index 0000000..73ea077 --- /dev/null +++ b/patch/sig.pco @@ -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 index 0000000..7b9b40c --- /dev/null +++ b/patch/unitinfo.pco @@ -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 diff --git a/source/patch_format.h b/source/patch_format.h index 580cc27..3f83e78 100644 --- a/source/patch_format.h +++ b/source/patch_format.h @@ -79,30 +79,32 @@ // 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