commit 6cbdb42dbfbb24cee56b65c2145f0f3f21d29af2 Author: 5225225 <5225225@mailbox.org> Date: Sat Jun 6 13:24:05 2020 +0100 initial work on decoding diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..12dfb20 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,198 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" +dependencies = [ + "jobserver", +] + +[[package]] +name = "derivative" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "jobserver" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "num_enum" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f1e2d7c9c4282839fc56f549bc006a54e6f28a851a7de7adcf3f50575751760" +dependencies = [ + "derivative", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9f19dafa80d8af21ede328f2c4ed836604a2eb1c309d688f89a7cc40568923" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" + +[[package]] +name = "shockwave" +version = "0.1.0" +dependencies = [ + "bitfield", + "bitflags", + "byteorder", + "num_enum", + "zstd", +] + +[[package]] +name = "syn" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "zstd" +version = "0.5.2+zstd.1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644352b10ce7f333d6e0af85bd4f5322dc449416dc1211c6308e95bca8923db4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "2.0.4+zstd.1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7113c0c9aed2c55181f2d9f5b0a36e7d2c0183b11c058ab40b35987479efe4d7" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.4.16+zstd.1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c442965efc45353be5a9b9969c9b0872fff6828c7e06d118dda2cb2d0bb11d5a" +dependencies = [ + "cc", + "glob", + "itertools", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..af964dd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "shockwave" +version = "0.1.0" +authors = ["5225225 <5225225@mailbox.org>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags = "1.2.1" +bitfield = "0.13.2" +zstd = "0.5.2" +byteorder = "1.3.4" +num_enum = "0.5.0" diff --git a/challenge b/challenge new file mode 100644 index 0000000..5096c31 Binary files /dev/null and b/challenge differ diff --git a/challenge.mctf b/challenge.mctf new file mode 100644 index 0000000..1132ed9 Binary files /dev/null and b/challenge.mctf differ diff --git a/challenge.zst b/challenge.zst new file mode 100644 index 0000000..dc04cd0 Binary files /dev/null and b/challenge.zst differ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..5d94a19 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,433 @@ +use byteorder::{ReadBytesExt, LE}; +use std::io::Cursor; +use std::convert::TryInto; + +struct Machine { + RX: u16, + RY: u16, + RZ: u16, + RTRGT: u16, + RSTAT: Flags, + RCALL: u16, + RSK: u16, + RSR: u16, + SSK: Vec, + SIN: Vec, + SMAIN: Vec, + SCODE: Vec, + IP: u16, +} + +#[derive(Debug, Eq, PartialEq)] +enum Register { + RX, + RY, + RZ, + RTRGT, + RSTAT, + RCALL, + NULL, +} + +impl Register { + fn into_register(x: u16) -> Self { + match x { + 1 => Self::RX, + 2 => Self::RY, + 3 => Self::RZ, + 4 => Self::RTRGT, + 5 => Self::RSTAT, + 6 => Self::RCALL, + _ => Self::NULL, + } + } +} + + +bitflags::bitflags! { + struct Flags: u16 { + const FZERO = 0b0000000000000001; + const FEQUL = 0b0000000000000010; + const FLT = 0b0000000000000100; + const FGT = 0b0000000000001000; + const FCRRY = 0b0000000000010000; + const FINF = 0b0000000000100000; + const FSE = 0b0000000001000000; + const FSF = 0b0000000010000000; + } +} + +#[derive(Debug, Eq, PartialEq)] +enum OP { +HALT = 0x000 , +NOOP = 0x001 , +INC = 0x002 , +DEC = 0x003 , +ADD = 0x004 , +SUB = 0x005 , +MUL = 0x006 , +DIV = 0x007 , +ADDC = 0x008 , +SUBC = 0x009 , +READ = 0x00A , +WRIT = 0x00B , +CPY = 0x00C , +MCPY = 0x00D , +ICPY = 0x00E , +CMP = 0x00F , +AND = 0x010 , +OR = 0x011 , +CMPL = 0x012 , +LSHF = 0x013 , +RSHF = 0x014 , +PUSH = 0x015 , +POP = 0x016 , +CFLG = 0x017 , +CALL = 0x018 , +RTRN = 0x019 , +RTRV = 0x01A , +RTL = 0x01B , +RTR = 0x01C , +CIP = 0x01D , +BSWP = 0x01E , +JUMP = 0x01F , +JZRO = 0x020 , +JEQU = 0x021 , +JLT = 0x022 , +JGT = 0x023 , +JCRY = 0x024 , +JINF = 0x025 , +JSE = 0x026 , +JSF = 0x027 , +CZRO = 0x030 , +CCRY = 0x034 , +XOR = 0x040 , +SWAP = 0x041 , +RCPT = 0x042 , +RCPF = 0x043 , +} + +impl std::convert::Into for u16 { + fn into(self) -> OP { + match self { + + +0x000 => OP::HALT, +0x001 => OP::NOOP, +0x002 => OP::INC, +0x003 => OP::DEC, +0x004 => OP::ADD, +0x005 => OP::SUB, +0x006 => OP::MUL, +0x007 => OP::DIV, +0x008 => OP::ADDC, +0x009 => OP::SUBC, +0x00A => OP::READ, +0x00B => OP::WRIT, +0x00C => OP::CPY, +0x00D => OP::MCPY, +0x00E => OP::ICPY, +0x00F => OP::CMP, +0x010 => OP::AND, +0x011 => OP::OR, +0x012 => OP::CMPL, +0x013 => OP::LSHF, +0x014 => OP::RSHF, +0x015 => OP::PUSH, +0x016 => OP::POP, +0x017 => OP::CFLG, +0x018 => OP::CALL, +0x019 => OP::RTRN, +0x01A => OP::RTRV, +0x01B => OP::RTL, +0x01C => OP::RTR, +0x01D => OP::CIP, +0x01E => OP::BSWP, +0x01F => OP::JUMP, +0x020 => OP::JZRO, +0x021 => OP::JEQU, +0x022 => OP::JLT, +0x023 => OP::JGT, +0x024 => OP::JCRY, +0x025 => OP::JINF, +0x026 => OP::JSE, +0x027 => OP::JSF, +0x030 => OP::CZRO, +0x034 => OP::CCRY, +0x040 => OP::XOR, +0x041 => OP::SWAP, +0x042 => OP::RCPT, +0x043 => OP::RCPF, +_ => panic!(), + } + } +} + +#[derive(Debug, PartialEq, Eq)] +enum AFLG { + Register, + SIN, + SMAIN, +} + +impl From for AFLG { + fn from(x: u16) -> AFLG { + match x { + 0b00 => AFLG::Register, + 0b01 => AFLG::Register, + 0b10 => AFLG::SIN, + 0b11 => AFLG::SMAIN, + _ => panic!(), + } + } +} + +bitfield::bitfield! { + struct DefinitionWord(u16); + impl Debug; + into OP, opcode, set_opcode: 8, 0; + sign, set_sign: 9, 9; + into AFLG, aflg2, set_aflg2: 11, 10; + into AFLG, aflg1, set_aflg1: 13, 12; + into AFLG, aflg0, set_aflg0: 15, 14; +} + +bitfield::bitfield! { + struct ArgumentWord(u16); + impl Debug; + reg2, set_reg2: 3, 0; + reg1, set_reg1: 7, 4; + reg0, set_reg0: 11, 8; +} + +#[derive(Debug)] +struct Instruction { + def: DefinitionWord, + args: Vec, +} + +impl Default for Machine { + fn default() -> Self { + Self { + SSK: vec![0_u16; 65536_usize], + SIN: vec![0_u16; 65536_usize], + SMAIN: vec![0_u16; 65536_usize], + SCODE: vec![0_u16; 65536_usize], + + RSTAT: Flags::FSE, + + IP: 1, + RCALL: 0, + RSK: 0, + RSR: 0, + RTRGT: 0, + RX: 0, + RY: 0, + RZ: 0, + } + } +} + +#[derive(Debug)] +enum Chunk { + SIN(Vec), + SCODE(Vec), + SMAIN(Vec), +} + +impl Chunk { + fn inner(&self) -> &[u16] { + match self { + Chunk::SIN(x) => x, + Chunk::SCODE(x) => x, + Chunk::SMAIN(x) => x, + } + } +} + +fn parse_chunk(mut input: impl std::io::Read) -> Chunk { + let space_index = input.read_u8().unwrap(); + let length = input.read_u16::().unwrap(); + + let mut data = Vec::new(); + for _ in 0..length { + data.push(input.read_u16::().unwrap()); + } + + match space_index { + 1 => Chunk::SIN(data), + 2 => Chunk::SCODE(data), + 3 => Chunk::SMAIN(data), + _ => panic!("unknown space index {}", space_index), + } +} + +fn arg_count(opcode: OP) -> usize { + match opcode { +OP::HALT => 0, +OP::NOOP => 0, +OP::INC => 1, +OP::DEC => 1, +OP::ADD => 2, +OP::SUB => 2, +OP::MUL => 2, +OP::DIV => 2, +OP::ADDC => 2, +OP::SUBC => 2, +OP::READ => 1, +OP::WRIT => 1, +OP::CPY => 2, +OP::MCPY => 3, +OP::ICPY => 2, +OP::CMP => 2, +OP::AND => 2, +OP::OR => 2, +OP::CMPL => 1, +OP::LSHF => 2, +OP::RSHF => 2, +OP::PUSH => 1, +OP::POP => 1, +OP::CFLG => 0, +OP::CALL => 3, +OP::RTRN => 0, +OP::RTRV => 0, +OP::RTL => 2, +OP::RTR => 2, +OP::CIP => 1, +OP::BSWP => 1, +OP::JUMP => 0, +OP::JZRO => 0, +OP::JEQU => 0, +OP::JLT => 0, +OP::JGT => 0, +OP::JCRY => 0, +OP::JINF => 0, +OP::JSE => 0, +OP::JSF => 0, +OP::CZRO => 0, +OP::CCRY => 0, +OP::XOR => 2, +OP::SWAP => 2, +OP::RCPT => 2, +OP::RCPF => 2, + } +} + +fn parse_instruction(mut bytes: &[u16]) -> (Instruction, usize) { + let mut consumed = 0; + + let def = DefinitionWord(bytes[0]); + bytes = &bytes[1..]; + consumed += 1; + + let ac = arg_count(def.opcode()); + + let mut args = Vec::new(); + + let mut registers_found = 0; + + let mut register_count = 0; + + if ac > 0 && def.aflg0() == AFLG::Register { + register_count += 1; + } + if ac > 1 && def.aflg1() == AFLG::Register { + register_count += 1; + } + if ac > 2 && def.aflg2() == AFLG::Register { + register_count += 1; + } + + let mut registers = Vec::new(); + if register_count > 0 { + let mut aw = bytes[0]; + bytes = &bytes[1..]; + consumed += 1; + + for i in 0..register_count { + registers.insert(0_usize, Register::into_register(aw & 0b1111)); + aw >>= 4; + } + } + + for idx in 0..ac { + let aflg = match idx { + 0 => def.aflg0(), + 1 => def.aflg1(), + 2 => def.aflg2(), + _ => unreachable!(), + }; + + match aflg { + AFLG::Register => { + args.push(Arg::Register(registers.remove(0))) + }, + AFLG::SIN => { + args.push(Arg::SIN(bytes[0])); + bytes = &bytes[1..]; + consumed += 1; + } + AFLG::SMAIN => { + args.push(Arg::SMAIN(bytes[0])); + bytes = &bytes[1..]; + consumed += 1; + } + } + } + + + + (Instruction { def, args }, consumed) +} + +#[derive(Debug)] +enum Arg { + Register(Register), + SIN(u16), + SMAIN(u16), +} + +impl Machine { + fn step(&mut self) -> bool { + let (cur_instruction, inc_by) = parse_instruction(&self.SCODE[(self.IP as usize)..]); + + let oc = cur_instruction.def.opcode(); + + match oc { + OP::HALT => return false, + OP::NOOP => {}, + _ => eprintln!("unsupported opcode {:?}, continuing", oc), + } + + self.IP += inc_by as u16; + + true + } +} + +fn dump_instructions(mut x: &[u16]) { + loop { + let (i, inc) = parse_instruction(x); + x = &x[inc..]; + println!("{:?}", i); + } +} + +fn main() { + let bin = include_bytes!("../challenge.mctf"); + let (header, body) = bin.split_at(4); + assert_eq!(header, b"mCTZ"); + let decompressed = zstd::decode_all(body).unwrap(); + let mut csr = Cursor::new(decompressed); + + let code = parse_chunk(&mut csr); + let sin = parse_chunk(&mut csr); + + let mut codeinner = code.inner(); + + let mut m = Machine::default(); + m.SIN = sin.inner().to_vec(); + m.SCODE = code.inner().to_vec(); + + dump_instructions(&m.SCODE); +}