avm1: Reader reads from &[u8]

This commit is contained in:
Mike Welsh 2019-09-09 12:47:54 -07:00
parent 1f85a779ac
commit f5db446fe1
4 changed files with 194 additions and 169 deletions

View File

@ -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<R: Read> {
inner: R,
#[allow(dead_code)]
pub struct Reader<'a> {
inner: Cursor<&'a [u8]>,
version: u8,
}
impl<R: Read> SwfRead<R> for Reader<R> {
fn get_inner(&mut self) -> &mut R {
impl<'a> SwfRead<Cursor<&'a [u8]>> for Reader<'a> {
fn get_inner(&mut self) -> &mut Cursor<&'a [u8]> {
&mut self.inner
}
}
impl<R: Read> Reader<R> {
pub fn new(inner: R, version: u8) -> Reader<R> {
Reader { inner, version }
impl<'a> Reader<'a> {
pub fn new(input: &'a [u8], version: u8) -> Self {
Self {
inner: Cursor::new(input),
version,
}
}
pub fn read_action_list(&mut self) -> Result<Vec<Action>> {
let mut actions = Vec::new();
while let Some(action) = self.read_action()? {
actions.push(action);
}
Ok(actions)
#[inline]
pub fn pos(&self) -> usize {
self.inner.position() as usize
}
pub fn read_action(&mut self) -> Result<Option<Action>> {
#[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<Option<Action<'a>>> {
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<R: Read> Reader<R> {
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<R: Read> Reader<R> {
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<R: Read> Reader<R> {
}
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<R: Read> Reader<R> {
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<R: Read> Reader<R> {
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<R: Read> Reader<R> {
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<R: Read> Reader<R> {
Ok((opcode, length))
}
fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result<Action> {
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<Action<'a>> {
Ok(Action::Unknown {
opcode,
data: self.read_slice(length),
})
}
fn read_push_value(&mut self) -> Result<Value> {
fn read_push(&mut self, length: usize) -> Result<Action<'a>> {
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<Value<'a>> {
let value = match self.read_u8()? {
0 => Value::Str(self.read_c_string()?),
1 => Value::Float(self.read_f32()?),
@ -249,26 +282,23 @@ impl<R: Read> Reader<R> {
Ok(value)
}
fn read_define_function(&mut self) -> Result<Action> {
fn read_define_function(&mut self) -> Result<Action<'a>> {
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<Action> {
fn read_define_function_2(&mut self) -> Result<Action<'a>> {
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<R: Read> Reader<R> {
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<R: Read> Reader<R> {
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<Action> {
fn read_try(&mut self) -> Result<Action<'a>> {
let flags = self.read_u8()?;
let try_length = self.read_u16()?;
let catch_length = self.read_u16()?;
@ -312,27 +339,9 @@ impl<R: Read> Reader<R> {
} 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]));
}
}

View File

@ -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<String>),
ConstantPool(Vec<&'a str>),
Decrement,
DefineFunction {
name: String,
params: Vec<String>,
actions: Vec<Action>,
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<Value>),
Push(Vec<Value<'a>>),
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<Action>,
actions: &'a [u8],
},
Unknown {
opcode: u8,
data: Vec<u8>,
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<FunctionParam>,
pub struct Function<'a> {
pub name: &'a str,
pub params: Vec<FunctionParam<'a>>,
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<Action>,
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<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TryBlock {
pub try_actions: Vec<Action>,
pub catch: Option<(CatchVar, Vec<Action>)>,
pub finally: Option<Vec<Action>>,
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),
}

View File

@ -5,6 +5,7 @@ use crate::avm1::types::*;
use crate::write::SwfWrite;
use std::io::{Result, Write};
#[allow(dead_code)]
pub struct Writer<W: Write> {
inner: W,
version: u8,
@ -21,14 +22,6 @@ impl<W: Write> Writer<W> {
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<W: Write> Writer<W> {
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::<usize>()
+ 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<W: Write> Writer<W> {
.map(|p| p.name.len() + 2)
.sum::<usize>()
+ 4
+ action_buf.len();
+ function.actions.len();
let num_registers = function
.params
.iter()
@ -134,8 +117,8 @@ impl<W: Write> Writer<W> {
})?;
self.write_c_string(&param.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<W: Write> Writer<W> {
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<W: Write> Writer<W> {
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())?;

View File

@ -21,7 +21,7 @@ pub fn echo_swf(filename: &str) {
pub type TestData<T> = (u8, T, Vec<u8>);
pub type TagTestData = TestData<Tag>;
pub type Avm1TestData = TestData<Action>;
pub type Avm1TestData = TestData<Action<'static>>;
pub type Avm2TestData = TestData<AbcFile>;
pub fn tag_tests() -> Vec<TagTestData> {
@ -2339,8 +2339,8 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
(
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<Avm1TestData> {
),
(
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<Avm1TestData> {
(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<Avm1TestData> {
(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<Avm1TestData> {
1,
Action::Unknown {
opcode: 0x79,
data: vec![],
data: &[],
},
vec![0x79],
),
@ -2498,7 +2498,7 @@ pub fn avm1_tests() -> Vec<Avm1TestData> {
1,
Action::Unknown {
opcode: 0xA0,
data: vec![2, 3],
data: &[2, 3],
},
vec![0xA0, 2, 0, 2, 3],
),