From f5db446fe1eca2a0cb3cc2897c4faef7a2bd4468 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Mon, 9 Sep 2019 12:47:54 -0700 Subject: [PATCH] avm1: Reader reads from &[u8] --- src/avm1/read.rs | 226 +++++++++++++++++++++++++++------------------- src/avm1/types.rs | 56 ++++++------ src/avm1/write.rs | 65 +++++-------- src/test_data.rs | 16 ++-- 4 files changed, 194 insertions(+), 169 deletions(-) diff --git a/src/avm1/read.rs b/src/avm1/read.rs index 614c5a24e..251e44493 100644 --- a/src/avm1/read.rs +++ b/src/avm1/read.rs @@ -3,39 +3,72 @@ use crate::avm1::opcode::OpCode; use crate::avm1::types::*; use crate::read::SwfRead; -use std::io::{Error, ErrorKind, Read, Result}; +use std::io::{Cursor, Error, ErrorKind, Result}; -pub struct Reader { - inner: R, +#[allow(dead_code)] +pub struct Reader<'a> { + inner: Cursor<&'a [u8]>, version: u8, } -impl SwfRead for Reader { - fn get_inner(&mut self) -> &mut R { +impl<'a> SwfRead> for Reader<'a> { + fn get_inner(&mut self) -> &mut Cursor<&'a [u8]> { &mut self.inner } } -impl Reader { - pub fn new(inner: R, version: u8) -> Reader { - Reader { inner, version } - } - - pub fn read_action_list(&mut self) -> Result> { - let mut actions = Vec::new(); - while let Some(action) = self.read_action()? { - actions.push(action); +impl<'a> Reader<'a> { + pub fn new(input: &'a [u8], version: u8) -> Self { + Self { + inner: Cursor::new(input), + version, } - Ok(actions) } - pub fn read_action(&mut self) -> Result> { + #[inline] + pub fn pos(&self) -> usize { + self.inner.position() as usize + } + + #[inline] + pub fn seek(&mut self, relative_offset: isize) { + let new_pos = self.inner.position() as i64 + relative_offset as i64; + self.inner.set_position(new_pos as u64); + } + + #[inline] + fn read_slice(&mut self, len: usize) -> &'a [u8] { + let pos = self.pos(); + let slice = &self.inner.get_ref()[pos..pos + len]; + self.inner.set_position(pos as u64 + len as u64); + slice + } + + #[inline] + fn read_c_string(&mut self) -> Result<&'a str> { + // Find zero terminator. + let str_slice = { + let start_pos = self.pos(); + loop { + let byte = self.read_u8()?; + if byte == 0 { + break; + } + } + &self.inner.get_ref()[start_pos..self.pos() - 1] + }; + // TODO: What does Flash do on invalid UTF8? + // Do we silently let it pass? + // TODO: Verify ANSI for SWF 5 and earlier. + std::str::from_utf8(str_slice) + .map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid string data")) + } + + #[inline] + pub fn read_action(&mut self) -> Result>> { use num_traits::FromPrimitive; let (opcode, length) = self.read_opcode_and_length()?; - - let mut action_reader = Reader::new(self.inner.by_ref().take(length as u64), self.version); - let action = if let Some(op) = OpCode::from_u8(opcode) { match op { OpCode::End => return Ok(None), @@ -58,14 +91,14 @@ impl Reader { OpCode::CloneSprite => Action::CloneSprite, OpCode::ConstantPool => { let mut constants = vec![]; - for _ in 0..action_reader.read_u16()? { - constants.push(action_reader.read_c_string()?); + for _ in 0..self.read_u16()? { + constants.push(self.read_c_string()?); } Action::ConstantPool(constants) } OpCode::Decrement => Action::Decrement, - OpCode::DefineFunction => action_reader.read_define_function()?, - OpCode::DefineFunction2 => action_reader.read_define_function_2()?, + OpCode::DefineFunction => self.read_define_function()?, + OpCode::DefineFunction2 => self.read_define_function_2()?, OpCode::DefineLocal => Action::DefineLocal, OpCode::DefineLocal2 => Action::DefineLocal2, OpCode::Delete => Action::Delete, @@ -81,11 +114,11 @@ impl Reader { OpCode::GetProperty => Action::GetProperty, OpCode::GetTime => Action::GetTime, OpCode::GetUrl => Action::GetUrl { - url: action_reader.read_c_string()?, - target: action_reader.read_c_string()?, + url: self.read_c_string()?, + target: self.read_c_string()?, }, OpCode::GetUrl2 => { - let flags = action_reader.read_u8()?; + let flags = self.read_u8()?; Action::GetUrl2 { is_target_sprite: flags & 0b10 != 0, is_load_vars: flags & 0b1 != 0, @@ -104,24 +137,24 @@ impl Reader { } OpCode::GetVariable => Action::GetVariable, OpCode::GotoFrame => { - let frame = action_reader.read_u16()?; + let frame = self.read_u16()?; Action::GotoFrame(frame) } OpCode::GotoFrame2 => { - let flags = action_reader.read_u8()?; + let flags = self.read_u8()?; Action::GotoFrame2 { set_playing: flags & 0b1 != 0, scene_offset: if flags & 0b10 != 0 { - action_reader.read_u16()? + self.read_u16()? } else { 0 }, } } - OpCode::GotoLabel => Action::GotoLabel(action_reader.read_c_string()?), + OpCode::GotoLabel => Action::GotoLabel(self.read_c_string()?), OpCode::Greater => Action::Greater, OpCode::If => Action::If { - offset: action_reader.read_i16()?, + offset: self.read_i16()?, }, OpCode::ImplementsOp => Action::ImplementsOp, OpCode::Increment => Action::Increment, @@ -129,7 +162,7 @@ impl Reader { OpCode::InitObject => Action::InitObject, OpCode::InstanceOf => Action::InstanceOf, OpCode::Jump => Action::Jump { - offset: action_reader.read_i16()?, + offset: self.read_i16()?, }, OpCode::Less => Action::Less, OpCode::Less2 => Action::Less2, @@ -148,27 +181,21 @@ impl Reader { OpCode::Pop => Action::Pop, OpCode::PreviousFrame => Action::PreviousFrame, // TODO: Verify correct version for complex types. - OpCode::Push => { - let mut values = vec![]; - while let Ok(value) = action_reader.read_push_value() { - values.push(value); - } - Action::Push(values) - } + OpCode::Push => self.read_push(length)?, OpCode::PushDuplicate => Action::PushDuplicate, OpCode::RandomNumber => Action::RandomNumber, OpCode::RemoveSprite => Action::RemoveSprite, OpCode::Return => Action::Return, OpCode::SetMember => Action::SetMember, OpCode::SetProperty => Action::SetProperty, - OpCode::SetTarget => Action::SetTarget(action_reader.read_c_string()?), + OpCode::SetTarget => Action::SetTarget(self.read_c_string()?), OpCode::SetTarget2 => Action::SetTarget2, OpCode::SetVariable => Action::SetVariable, OpCode::StackSwap => Action::StackSwap, OpCode::StartDrag => Action::StartDrag, OpCode::Stop => Action::Stop, OpCode::StopSounds => Action::StopSounds, - OpCode::StoreRegister => Action::StoreRegister(action_reader.read_u8()?), + OpCode::StoreRegister => Action::StoreRegister(self.read_u8()?), OpCode::StrictEquals => Action::StrictEquals, OpCode::StringAdd => Action::StringAdd, OpCode::StringEquals => Action::StringEquals, @@ -184,28 +211,24 @@ impl Reader { OpCode::ToNumber => Action::ToNumber, OpCode::ToString => Action::ToString, OpCode::Trace => Action::Trace, - OpCode::Try => action_reader.read_try()?, + OpCode::Try => self.read_try()?, OpCode::TypeOf => Action::TypeOf, OpCode::WaitForFrame => Action::WaitForFrame { - frame: action_reader.read_u16()?, - num_actions_to_skip: action_reader.read_u8()?, + frame: self.read_u16()?, + num_actions_to_skip: self.read_u8()?, }, OpCode::With => { - let code_length = action_reader.read_u16()?; - let mut with_reader = Reader::new( - (&mut action_reader.inner as &mut dyn Read).take(code_length.into()), - self.version, - ); + let code_length = self.read_u16()?; Action::With { - actions: with_reader.read_action_list()?, + actions: self.read_slice(code_length.into()), } } OpCode::WaitForFrame2 => Action::WaitForFrame2 { - num_actions_to_skip: action_reader.read_u8()?, + num_actions_to_skip: self.read_u8()?, }, } } else { - action_reader.read_unknown_action(opcode, length)? + self.read_unknown_action(opcode, length)? }; Ok(Some(action)) @@ -221,13 +244,23 @@ impl Reader { Ok((opcode, length)) } - fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result { - let mut data = vec![0u8; length]; - self.inner.read_exact(&mut data)?; - Ok(Action::Unknown { opcode, data }) + fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result> { + Ok(Action::Unknown { + opcode, + data: self.read_slice(length), + }) } - fn read_push_value(&mut self) -> Result { + fn read_push(&mut self, length: usize) -> Result> { + let end_pos = self.pos() + length; + let mut values = Vec::with_capacity(end_pos); + while self.pos() < end_pos { + values.push(self.read_push_value()?); + } + Ok(Action::Push(values)) + } + + fn read_push_value(&mut self) -> Result> { let value = match self.read_u8()? { 0 => Value::Str(self.read_c_string()?), 1 => Value::Float(self.read_f32()?), @@ -249,26 +282,23 @@ impl Reader { Ok(value) } - fn read_define_function(&mut self) -> Result { + fn read_define_function(&mut self) -> Result> { let name = self.read_c_string()?; let num_params = self.read_u16()?; let mut params = Vec::with_capacity(num_params as usize); for _ in 0..num_params { params.push(self.read_c_string()?); } + // code_length isn't included in the DefineFunction's action length. let code_length = self.read_u16()?; - let mut fn_reader = Reader::new( - (&mut self.inner as &mut dyn Read).take(code_length.into()), - self.version, - ); Ok(Action::DefineFunction { name, params, - actions: fn_reader.read_action_list()?, + actions: self.read_slice(code_length.into()), }) } - fn read_define_function_2(&mut self) -> Result { + fn read_define_function_2(&mut self) -> Result> { let name = self.read_c_string()?; let num_params = self.read_u16()?; let num_registers = self.read_u8()?; // Number of registers @@ -281,11 +311,8 @@ impl Reader { register_index: if register == 0 { None } else { Some(register) }, }); } + // code_length isn't included in the DefineFunction's length. let code_length = self.read_u16()?; - let mut fn_reader = Reader::new( - (&mut self.inner as &mut dyn Read).take(code_length.into()), - self.version, - ); Ok(Action::DefineFunction2(Function { name, params, @@ -298,11 +325,11 @@ impl Reader { preload_arguments: flags & 0b100 != 0, suppress_this: flags & 0b10 != 0, preload_this: flags & 0b1 != 0, - actions: fn_reader.read_action_list()?, + actions: self.read_slice(code_length.into()), })) } - fn read_try(&mut self) -> Result { + fn read_try(&mut self) -> Result> { let flags = self.read_u8()?; let try_length = self.read_u16()?; let catch_length = self.read_u16()?; @@ -312,27 +339,9 @@ impl Reader { } else { CatchVar::Register(self.read_u8()?) }; - let try_actions = { - let mut fn_reader = Reader::new( - (&mut self.inner as &mut dyn Read).take(try_length.into()), - self.version, - ); - fn_reader.read_action_list()? - }; - let catch_actions = { - let mut fn_reader = Reader::new( - (&mut self.inner as &mut dyn Read).take(catch_length.into()), - self.version, - ); - fn_reader.read_action_list()? - }; - let finally_actions = { - let mut fn_reader = Reader::new( - (&mut self.inner as &mut dyn Read).take(finally_length.into()), - self.version, - ); - fn_reader.read_action_list()? - }; + let try_actions = self.read_slice(try_length.into()); + let catch_actions = self.read_slice(catch_length.into()); + let finally_actions = self.read_slice(finally_length.into()); Ok(Action::Try(TryBlock { try_actions, catch: if flags & 0b1 != 0 { @@ -368,4 +377,39 @@ pub mod tests { } } } + + #[test] + fn read_define_function() { + // Ensure we read a function properly along with the function data. + let action_bytes = vec![ + 0x9b, 0x08, 0x00, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x96, 0x06, 0x00, + 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26, 0x00, + ]; + let mut reader = Reader::new(&action_bytes[..], 5); + let action = reader.read_action().unwrap().unwrap(); + assert_eq!( + action, + Action::DefineFunction { + name: "foo", + params: vec![], + actions: &[0x96, 0x06, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26], + } + ); + + if let Action::DefineFunction { actions, .. } = action { + let mut reader = Reader::new(actions, 5); + let action = reader.read_action().unwrap().unwrap(); + assert_eq!(action, Action::Push(vec![Value::Str("test")])); + } + } + + #[test] + fn read_push_to_end_of_action() { + // ActionPush doesn't provide an explicit # of values, but instead reads values + // until the end of the action. Ensure we don't read extra values. + let action_bytes = [0x96, 2, 0, 2, 3, 3]; // Extra 3 at the end shouldn't be read. + let mut reader = Reader::new(&action_bytes[..], 5); + let action = reader.read_action().unwrap().unwrap(); + assert_eq!(action, Action::Push(vec![Value::Null, Value::Undefined])); + } } diff --git a/src/avm1/types.rs b/src/avm1/types.rs index 1c81535f3..21806c904 100644 --- a/src/avm1/types.rs +++ b/src/avm1/types.rs @@ -1,5 +1,5 @@ #[derive(Clone, Debug, PartialEq)] -pub enum Action { +pub enum Action<'a> { Add, Add2, And, @@ -16,14 +16,14 @@ pub enum Action { CastOp, CharToAscii, CloneSprite, - ConstantPool(Vec), + ConstantPool(Vec<&'a str>), Decrement, DefineFunction { - name: String, - params: Vec, - actions: Vec, + name: &'a str, + params: Vec<&'a str>, + actions: &'a [u8], }, - DefineFunction2(Function), + DefineFunction2(Function<'a>), DefineLocal, DefineLocal2, Delete, @@ -39,8 +39,8 @@ pub enum Action { GetProperty, GetTime, GetUrl { - url: String, - target: String, + url: &'a str, + target: &'a str, }, GetUrl2 { send_vars_method: SendVarsMethod, @@ -53,7 +53,7 @@ pub enum Action { set_playing: bool, scene_offset: u16, }, - GotoLabel(String), + GotoLabel(&'a str), Greater, If { offset: i16, @@ -82,14 +82,14 @@ pub enum Action { Play, Pop, PreviousFrame, - Push(Vec), + Push(Vec>), PushDuplicate, RandomNumber, RemoveSprite, Return, SetMember, SetProperty, - SetTarget(String), + SetTarget(&'a str), SetTarget2, SetVariable, StackSwap, @@ -112,7 +112,7 @@ pub enum Action { ToString, ToggleQuality, Trace, - Try(TryBlock), + Try(TryBlock<'a>), TypeOf, WaitForFrame { frame: u16, @@ -122,23 +122,23 @@ pub enum Action { num_actions_to_skip: u8, }, With { - actions: Vec, + actions: &'a [u8], }, Unknown { opcode: u8, - data: Vec, + data: &'a [u8], }, } #[derive(Clone, Debug, PartialEq)] -pub enum Value { +pub enum Value<'a> { Undefined, Null, Bool(bool), Int(i32), Float(f32), Double(f64), - Str(String), + Str(&'a str), Register(u8), ConstantPool(u16), } @@ -151,9 +151,9 @@ pub enum SendVarsMethod { } #[derive(Clone, Debug, PartialEq)] -pub struct Function { - pub name: String, - pub params: Vec, +pub struct Function<'a> { + pub name: &'a str, + pub params: Vec>, pub preload_parent: bool, pub preload_root: bool, pub suppress_super: bool, @@ -163,24 +163,24 @@ pub struct Function { pub suppress_this: bool, pub preload_this: bool, pub preload_global: bool, - pub actions: Vec, + pub actions: &'a [u8], } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct FunctionParam { - pub name: String, +pub struct FunctionParam<'a> { + pub name: &'a str, pub register_index: Option, } #[derive(Clone, Debug, PartialEq)] -pub struct TryBlock { - pub try_actions: Vec, - pub catch: Option<(CatchVar, Vec)>, - pub finally: Option>, +pub struct TryBlock<'a> { + pub try_actions: &'a [u8], + pub catch: Option<(CatchVar<'a>, &'a [u8])>, + pub finally: Option<&'a [u8]>, } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum CatchVar { - Var(String), +pub enum CatchVar<'a> { + Var(&'a str), Register(u8), } diff --git a/src/avm1/write.rs b/src/avm1/write.rs index f69551dd5..b665320c4 100644 --- a/src/avm1/write.rs +++ b/src/avm1/write.rs @@ -5,6 +5,7 @@ use crate::avm1::types::*; use crate::write::SwfWrite; use std::io::{Result, Write}; +#[allow(dead_code)] pub struct Writer { inner: W, version: u8, @@ -21,14 +22,6 @@ impl Writer { Writer { inner, version } } - pub fn write_action_list(&mut self, actions: &[Action]) -> Result<()> { - for action in actions { - self.write_action(action)?; - } - self.write_u8(0)?; // End - Ok(()) - } - pub fn write_action(&mut self, action: &Action) -> Result<()> { match *action { Action::Add => self.write_action_header(OpCode::Add, 0)?, @@ -61,32 +54,22 @@ impl Writer { ref params, ref actions, } => { - let mut action_buf = vec![]; - { - let mut fn_writer = Writer::new(&mut action_buf, self.version); - fn_writer.write_action_list(actions)?; - } let len = name.len() + 1 + 2 + params.iter().map(|p| p.len() + 1).sum::() + 2 - + action_buf.len(); + + actions.len(); self.write_action_header(OpCode::DefineFunction, len)?; self.write_c_string(name)?; self.write_u16(params.len() as u16)?; for param in params { self.write_c_string(param)?; } - self.write_u16(action_buf.len() as u16)?; - self.inner.write_all(&action_buf)?; + self.write_u16(actions.len() as u16)?; + self.inner.write_all(actions)?; } Action::DefineFunction2(ref function) => { - let mut action_buf = vec![]; - { - let mut fn_writer = Writer::new(&mut action_buf, self.version); - fn_writer.write_action_list(&function.actions)?; - } let len = function.name.len() + 1 + 3 @@ -96,7 +79,7 @@ impl Writer { .map(|p| p.name.len() + 2) .sum::() + 4 - + action_buf.len(); + + function.actions.len(); let num_registers = function .params .iter() @@ -134,8 +117,8 @@ impl Writer { })?; self.write_c_string(¶m.name)?; } - self.write_u16(action_buf.len() as u16)?; - self.inner.write_all(&action_buf)?; + self.write_u16(function.actions.len() as u16)?; + self.inner.write_all(&function.actions)?; } Action::DefineLocal => self.write_action_header(OpCode::DefineLocal, 0)?, Action::DefineLocal2 => self.write_action_header(OpCode::DefineLocal2, 0)?, @@ -290,17 +273,20 @@ impl Writer { let finally_length; let mut action_buf = vec![]; { - let mut fn_writer = Writer::new(&mut action_buf, self.version); - fn_writer.write_action_list(&try_block.try_actions)?; - try_length = fn_writer.inner.len(); - if let Some((_, ref catch)) = try_block.catch { - fn_writer.write_action_list(catch)?; - } - catch_length = fn_writer.inner.len() - try_length; - if let Some(ref finally) = try_block.finally { - fn_writer.write_action_list(finally)?; - } - finally_length = fn_writer.inner.len() - (try_length + catch_length); + action_buf.write_all(&try_block.try_actions)?; + try_length = try_block.try_actions.len(); + catch_length = if let Some((_, ref catch)) = try_block.catch { + action_buf.write_all(catch)?; + catch.len() + } else { + 0 + }; + finally_length = if let Some(ref finally) = try_block.finally { + action_buf.write_all(finally)?; + finally.len() + } else { + 0 + }; } let len = 7 + action_buf.len() @@ -344,13 +330,8 @@ impl Writer { self.write_u8(num_actions_to_skip)?; } Action::With { ref actions } => { - let mut action_buf = vec![]; - { - let mut fn_writer = Writer::new(&mut action_buf, self.version); - fn_writer.write_action_list(actions)?; - } - self.write_action_header(OpCode::With, action_buf.len())?; - self.inner.write_all(&action_buf)?; + self.write_action_header(OpCode::With, actions.len())?; + self.inner.write_all(&actions)?; } Action::Unknown { opcode, ref data } => { self.write_opcode_and_length(opcode, data.len())?; diff --git a/src/test_data.rs b/src/test_data.rs index 0a307be18..79bff2772 100644 --- a/src/test_data.rs +++ b/src/test_data.rs @@ -21,7 +21,7 @@ pub fn echo_swf(filename: &str) { pub type TestData = (u8, T, Vec); pub type TagTestData = TestData; -pub type Avm1TestData = TestData; +pub type Avm1TestData = TestData>; pub type Avm2TestData = TestData; pub fn tag_tests() -> Vec { @@ -2339,8 +2339,8 @@ pub fn avm1_tests() -> Vec { ( 3, Action::GetUrl { - url: String::from("a"), - target: String::from("b"), + url: "a", + target: "b", }, vec![0x83, 4, 0, 97, 0, 98, 0], ), @@ -2373,7 +2373,7 @@ pub fn avm1_tests() -> Vec { ), ( 3, - Action::GotoLabel("testb".to_string()), + Action::GotoLabel("testb"), vec![0x8C, 6, 0, 116, 101, 115, 116, 98, 0], ), (4, Action::If { offset: 1 }, vec![0x9D, 2, 0, 1, 0]), @@ -2393,7 +2393,7 @@ pub fn avm1_tests() -> Vec { (3, Action::PreviousFrame, vec![0x05]), ( 4, - Action::Push(vec![Value::Str("test".to_string())]), + Action::Push(vec![Value::Str("test")]), vec![0x96, 6, 0, 0, 116, 101, 115, 116, 0], ), ( @@ -2456,7 +2456,7 @@ pub fn avm1_tests() -> Vec { (4, Action::RandomNumber, vec![0x30]), ( 3, - Action::SetTarget("test".to_string()), + Action::SetTarget("test"), vec![0x8B, 5, 0, 116, 101, 115, 116, 0], ), (4, Action::SetVariable, vec![0x1D]), @@ -2490,7 +2490,7 @@ pub fn avm1_tests() -> Vec { 1, Action::Unknown { opcode: 0x79, - data: vec![], + data: &[], }, vec![0x79], ), @@ -2498,7 +2498,7 @@ pub fn avm1_tests() -> Vec { 1, Action::Unknown { opcode: 0xA0, - data: vec![2, 3], + data: &[2, 3], }, vec![0xA0, 2, 0, 2, 3], ),