swf/avm1/
read.rs

1use crate::avm1::{opcode::OpCode, types::*};
2use crate::error::{Error, Result};
3use crate::extensions::ReadSwfExt;
4use std::num::NonZeroU8;
5
6pub struct Reader<'a> {
7    input: &'a [u8],
8    #[allow(dead_code)]
9    version: u8,
10}
11
12impl<'a> ReadSwfExt<'a> for Reader<'a> {
13    #[inline(always)]
14    fn as_mut_slice(&mut self) -> &mut &'a [u8] {
15        &mut self.input
16    }
17
18    #[inline(always)]
19    fn as_slice(&self) -> &'a [u8] {
20        self.input
21    }
22}
23
24impl<'a> Reader<'a> {
25    #[inline]
26    pub const fn new(input: &'a [u8], version: u8) -> Self {
27        Self { input, version }
28    }
29
30    #[inline]
31    pub fn seek(&mut self, data: &'a [u8], jump_offset: i16) {
32        ReadSwfExt::seek(self, data, jump_offset as isize)
33    }
34
35    #[inline]
36    pub const fn get_ref(&self) -> &'a [u8] {
37        self.input
38    }
39
40    #[inline]
41    pub fn get_mut(&mut self) -> &mut &'a [u8] {
42        &mut self.input
43    }
44
45    #[inline]
46    fn read_f64_me(&mut self) -> Result<f64> {
47        // Flash weirdly stores (some?) f64 as two LE 32-bit chunks.
48        // First word is the hi-word, second word is the lo-word.
49        Ok(f64::from_bits(self.read_u64()?.rotate_left(32)))
50    }
51
52    #[inline]
53    pub fn read_action(&mut self) -> Result<Action<'a>> {
54        let (opcode, mut length) = self.read_opcode_and_length()?;
55        let start = self.input;
56
57        let action = self.read_op(opcode, &mut length);
58
59        if let Err(e) = action {
60            return Err(Error::avm1_parse_error_with_source(opcode, e));
61        }
62
63        // Verify that we parsed the correct amount of data.
64        let end_pos = (start.as_ptr() as usize + length) as *const u8;
65        if self.input.as_ptr() != end_pos {
66            // We incorrectly parsed this action.
67            // Re-sync to the expected end of the action and throw an error.
68            self.input = &start[length.min(start.len())..];
69            log::warn!("Length mismatch in AVM1 action: {}", OpCode::format(opcode));
70        }
71
72        action
73    }
74
75    pub fn read_opcode_and_length(&mut self) -> Result<(u8, usize)> {
76        let opcode = self.read_u8()?;
77        let length = if opcode >= 0x80 {
78            self.read_u16()?.into()
79        } else {
80            0
81        };
82        Ok((opcode, length))
83    }
84
85    /// Reads an action with the given opcode.
86    /// `length` is an in-out parameter and will be modified in the case of instructions
87    /// that contain sub-blocks of code, such as `DefineFunction`.
88    /// The `length` passed in should be the length excluding any sub-blocks.
89    /// The final `length` returned will be total length of the action, including sub-blocks.
90    #[inline]
91    fn read_op(&mut self, opcode: u8, length: &mut usize) -> Result<Action<'a>> {
92        let action = if let Some(op) = OpCode::from_u8(opcode) {
93            match op {
94                OpCode::Add => Action::Add,
95                OpCode::Add2 => Action::Add2,
96                OpCode::And => Action::And,
97                OpCode::AsciiToChar => Action::AsciiToChar,
98                OpCode::BitAnd => Action::BitAnd,
99                OpCode::BitLShift => Action::BitLShift,
100                OpCode::BitOr => Action::BitOr,
101                OpCode::BitRShift => Action::BitRShift,
102                OpCode::BitURShift => Action::BitURShift,
103                OpCode::BitXor => Action::BitXor,
104                OpCode::Call => Action::Call,
105                OpCode::CallFunction => Action::CallFunction,
106                OpCode::CallMethod => Action::CallMethod,
107                OpCode::CastOp => Action::CastOp,
108                OpCode::CharToAscii => Action::CharToAscii,
109                OpCode::CloneSprite => Action::CloneSprite,
110                OpCode::ConstantPool => Action::ConstantPool(self.read_constant_pool()?),
111                OpCode::Decrement => Action::Decrement,
112                OpCode::DefineFunction => {
113                    Action::DefineFunction(self.read_define_function(length)?)
114                }
115                OpCode::DefineFunction2 => {
116                    Action::DefineFunction2(self.read_define_function_2(length)?)
117                }
118                OpCode::DefineLocal => Action::DefineLocal,
119                OpCode::DefineLocal2 => Action::DefineLocal2,
120                OpCode::Delete => Action::Delete,
121                OpCode::Delete2 => Action::Delete2,
122                OpCode::Divide => Action::Divide,
123                OpCode::EndDrag => Action::EndDrag,
124                OpCode::Enumerate => Action::Enumerate,
125                OpCode::Enumerate2 => Action::Enumerate2,
126                OpCode::End => Action::End,
127                OpCode::Equals => Action::Equals,
128                OpCode::Equals2 => Action::Equals2,
129                OpCode::Extends => Action::Extends,
130                OpCode::GetMember => Action::GetMember,
131                OpCode::GetProperty => Action::GetProperty,
132                OpCode::GetTime => Action::GetTime,
133                OpCode::GetUrl => Action::GetUrl(self.read_get_url()?),
134                OpCode::GetUrl2 => Action::GetUrl2(self.read_get_url_2()?),
135                OpCode::GetVariable => Action::GetVariable,
136                OpCode::GotoFrame => Action::GotoFrame(self.read_goto_frame()?),
137                OpCode::GotoFrame2 => Action::GotoFrame2(self.read_goto_frame_2()?),
138                OpCode::GotoLabel => Action::GotoLabel(self.read_goto_label()?),
139                OpCode::Greater => Action::Greater,
140                OpCode::If => Action::If(self.read_if()?),
141                OpCode::ImplementsOp => Action::ImplementsOp,
142                OpCode::Increment => Action::Increment,
143                OpCode::InitArray => Action::InitArray,
144                OpCode::InitObject => Action::InitObject,
145                OpCode::InstanceOf => Action::InstanceOf,
146                OpCode::Jump => Action::Jump(self.read_jump()?),
147                OpCode::Less => Action::Less,
148                OpCode::Less2 => Action::Less2,
149                OpCode::MBAsciiToChar => Action::MBAsciiToChar,
150                OpCode::MBCharToAscii => Action::MBCharToAscii,
151                OpCode::MBStringExtract => Action::MBStringExtract,
152                OpCode::MBStringLength => Action::MBStringLength,
153                OpCode::Modulo => Action::Modulo,
154                OpCode::Multiply => Action::Multiply,
155                OpCode::NewMethod => Action::NewMethod,
156                OpCode::NewObject => Action::NewObject,
157                OpCode::NextFrame => Action::NextFrame,
158                OpCode::Not => Action::Not,
159                OpCode::Or => Action::Or,
160                OpCode::Play => Action::Play,
161                OpCode::Pop => Action::Pop,
162                OpCode::PreviousFrame => Action::PreviousFrame,
163                OpCode::Push => Action::Push(self.read_push(*length)?),
164                OpCode::PushDuplicate => Action::PushDuplicate,
165                OpCode::RandomNumber => Action::RandomNumber,
166                OpCode::RemoveSprite => Action::RemoveSprite,
167                OpCode::Return => Action::Return,
168                OpCode::SetMember => Action::SetMember,
169                OpCode::SetProperty => Action::SetProperty,
170                OpCode::SetTarget => Action::SetTarget(self.read_set_target()?),
171                OpCode::SetTarget2 => Action::SetTarget2,
172                OpCode::SetVariable => Action::SetVariable,
173                OpCode::StackSwap => Action::StackSwap,
174                OpCode::StartDrag => Action::StartDrag,
175                OpCode::Stop => Action::Stop,
176                OpCode::StopSounds => Action::StopSounds,
177                OpCode::StoreRegister => Action::StoreRegister(self.read_store_register()?),
178                OpCode::StrictEquals => Action::StrictEquals,
179                OpCode::StringAdd => Action::StringAdd,
180                OpCode::StringEquals => Action::StringEquals,
181                OpCode::StringExtract => Action::StringExtract,
182                OpCode::StringGreater => Action::StringGreater,
183                OpCode::StringLength => Action::StringLength,
184                OpCode::StringLess => Action::StringLess,
185                OpCode::Subtract => Action::Subtract,
186                OpCode::TargetPath => Action::TargetPath,
187                OpCode::Throw => Action::Throw,
188                OpCode::ToggleQuality => Action::ToggleQuality,
189                OpCode::ToInteger => Action::ToInteger,
190                OpCode::ToNumber => Action::ToNumber,
191                OpCode::ToString => Action::ToString,
192                OpCode::Trace => Action::Trace,
193                OpCode::Try => Action::Try(self.read_try(length)?),
194                OpCode::TypeOf => Action::TypeOf,
195                OpCode::WaitForFrame => Action::WaitForFrame(self.read_wait_for_frame()?),
196                OpCode::WaitForFrame2 => Action::WaitForFrame2(self.read_wait_for_frame_2()?),
197                OpCode::With => Action::With(self.read_with(length)?),
198            }
199        } else {
200            Action::Unknown(self.read_unknown_action(opcode, *length)?)
201        };
202
203        Ok(action)
204    }
205
206    fn read_constant_pool(&mut self) -> Result<ConstantPool<'a>> {
207        let count = self.read_u16()?;
208        let mut strings = Vec::with_capacity(count as usize);
209        for _ in 0..count {
210            let string = self.read_str();
211            if let Ok(string) = string {
212                strings.push(string);
213            }
214        }
215        Ok(ConstantPool { strings })
216    }
217
218    fn read_define_function(&mut self, action_length: &mut usize) -> Result<DefineFunction<'a>> {
219        let name = self.read_str()?;
220        let num_params = self.read_u16()?;
221        let mut params = Vec::with_capacity(num_params as usize);
222        for _ in 0..num_params {
223            params.push(self.read_str()?);
224        }
225        // code_length isn't included in the DefineFunction's action length.
226        let code_length: usize = (self.read_u16()?).into();
227        *action_length += code_length;
228        Ok(DefineFunction {
229            name,
230            params,
231            actions: self.read_slice(code_length)?,
232        })
233    }
234
235    fn read_define_function_2(&mut self, action_length: &mut usize) -> Result<DefineFunction2<'a>> {
236        let name = self.read_str()?;
237        let num_params = self.read_u16()?;
238        let register_count = self.read_u8()?;
239        let flags = FunctionFlags::from_bits_truncate(self.read_u16()?);
240        let mut params = Vec::with_capacity(num_params as usize);
241        for _ in 0..num_params {
242            params.push(FunctionParam {
243                register_index: NonZeroU8::new(self.read_u8()?),
244                name: self.read_str()?,
245            });
246        }
247        // code_length isn't included in the DefineFunction's length.
248        let code_length: usize = (self.read_u16()?).into();
249        *action_length += code_length;
250        Ok(DefineFunction2 {
251            name,
252            params,
253            register_count,
254            flags,
255            actions: self.read_slice(code_length)?,
256        })
257    }
258
259    fn read_get_url(&mut self) -> Result<GetUrl<'a>> {
260        Ok(GetUrl {
261            url: self.read_str()?,
262            target: self.read_str()?,
263        })
264    }
265
266    fn read_get_url_2(&mut self) -> Result<GetUrl2> {
267        let mut flags = GetUrlFlags::from_bits_truncate(self.read_u8()?);
268        if flags.contains(GetUrlFlags::METHOD_MASK) {
269            // Flash treats an invalid method the same as "none".
270            log::warn!("Invalid SendVarsMethod in GetUrl2, defaulting to None");
271            flags.set(GetUrlFlags::METHOD_MASK, false);
272        }
273        Ok(GetUrl2(flags))
274    }
275
276    fn read_goto_frame(&mut self) -> Result<GotoFrame> {
277        Ok(GotoFrame {
278            frame: self.read_u16()?,
279        })
280    }
281
282    fn read_goto_frame_2(&mut self) -> Result<GotoFrame2> {
283        let flags = self.read_u8()?;
284        Ok(GotoFrame2 {
285            set_playing: flags & 0b1 != 0,
286            scene_offset: if flags & 0b10 != 0 {
287                self.read_u16()?
288            } else {
289                0
290            },
291        })
292    }
293
294    fn read_goto_label(&mut self) -> Result<GotoLabel<'a>> {
295        Ok(GotoLabel {
296            label: self.read_str()?,
297        })
298    }
299
300    fn read_if(&mut self) -> Result<If> {
301        Ok(If {
302            offset: self.read_i16()?,
303        })
304    }
305
306    fn read_jump(&mut self) -> Result<Jump> {
307        Ok(Jump {
308            offset: self.read_i16()?,
309        })
310    }
311
312    fn read_push(&mut self, length: usize) -> Result<Push<'a>> {
313        // TODO: Verify correct version for complex types.
314        let end_pos = (self.input.as_ptr() as usize + length) as *const u8;
315        let mut values = Vec::with_capacity(4);
316        while self.input.as_ptr() < end_pos {
317            let value = match self.read_u8()? {
318                0 => Value::Str(self.read_str()?),
319                1 => Value::Float(self.read_f32()?),
320                2 => Value::Null,
321                3 => Value::Undefined,
322                4 => Value::Register(self.read_u8()?),
323                5 => Value::Bool(self.read_u8()? != 0),
324                6 => Value::Double(self.read_f64_me()?),
325                7 => Value::Int(self.read_i32()?),
326                8 => Value::ConstantPool(self.read_u8()?.into()),
327                9 => Value::ConstantPool(self.read_u16()?),
328                type_ => {
329                    // Newest Flash Player exits on unknown value types. However, older versions
330                    // (at least FP9) just ignore them and continue to the next value.
331                    // We follow the lenient behavior in order to support more content (e.g. #8389
332                    // doesn't work on newest Flash Player).
333                    // TODO: Return an error if we ever add player version emulation.
334                    log::warn!("Invalid value type in ActionPush: {}", type_);
335                    continue;
336                }
337            };
338            values.push(value);
339        }
340        Ok(Push { values })
341    }
342
343    fn read_set_target(&mut self) -> Result<SetTarget<'a>> {
344        Ok(SetTarget {
345            target: self.read_str()?,
346        })
347    }
348
349    fn read_store_register(&mut self) -> Result<StoreRegister> {
350        Ok(StoreRegister {
351            register: self.read_u8()?,
352        })
353    }
354
355    fn read_try(&mut self, length: &mut usize) -> Result<Try<'a>> {
356        // All Try opcodes must be at least 7 bytes long. If it's shorter, it's a bogus opcode; return an empty Try.
357        if *length < 7 {
358            return Ok(Try {
359                try_body: &[],
360                catch_body: None,
361                finally_body: None,
362            });
363        }
364
365        let flags = TryFlags::from_bits_truncate(self.read_u8()?);
366        let try_size: usize = self.read_u16()?.into();
367        let catch_size: usize = self.read_u16()?.into();
368        let finally_size: usize = self.read_u16()?.into();
369        *length += try_size + catch_size + finally_size;
370        let catch_var = if flags.contains(TryFlags::CATCH_IN_REGISTER) {
371            CatchVar::Register(self.read_u8()?)
372        } else {
373            CatchVar::Var(self.read_str()?)
374        };
375        let try_body = self.read_slice(try_size)?;
376        let catch_body = self.read_slice(catch_size)?;
377        let finally_body = self.read_slice(finally_size)?;
378        Ok(Try {
379            try_body,
380            catch_body: if flags.contains(TryFlags::CATCH_BLOCK) {
381                Some((catch_var, catch_body))
382            } else {
383                None
384            },
385            finally_body: if flags.contains(TryFlags::FINALLY_BLOCK) {
386                Some(finally_body)
387            } else {
388                None
389            },
390        })
391    }
392
393    fn read_wait_for_frame(&mut self) -> Result<WaitForFrame> {
394        Ok(WaitForFrame {
395            frame: self.read_u16()?,
396            num_actions_to_skip: self.read_u8()?,
397        })
398    }
399
400    fn read_wait_for_frame_2(&mut self) -> Result<WaitForFrame2> {
401        Ok(WaitForFrame2 {
402            num_actions_to_skip: self.read_u8()?,
403        })
404    }
405
406    fn read_with(&mut self, action_length: &mut usize) -> Result<With<'a>> {
407        let code_length: usize = (self.read_u16()?).into();
408        *action_length += code_length;
409        Ok(With {
410            actions: self.read_slice(code_length)?,
411        })
412    }
413
414    fn read_unknown_action(&mut self, opcode: u8, length: usize) -> Result<Unknown<'a>> {
415        Ok(Unknown {
416            opcode,
417            data: self.read_slice(length)?,
418        })
419    }
420}
421
422#[cfg(test)]
423pub mod tests {
424    use super::*;
425    use crate::string::{SwfStr, WINDOWS_1252};
426    use crate::test_data;
427
428    #[test]
429    fn read_action() {
430        for (swf_version, expected_action, action_bytes) in test_data::avm1_tests() {
431            let mut reader = Reader::new(&action_bytes[..], swf_version);
432            let parsed_action = reader.read_action().unwrap();
433            assert_eq!(
434                parsed_action, expected_action,
435                "Incorrectly parsed action.\nRead:\n{parsed_action:?}\n\nExpected:\n{expected_action:?}",
436            );
437        }
438    }
439
440    /// Ensure that we return an error on invalid data.
441    #[test]
442    fn read_parse_error() {
443        let action_bytes = [0xff, 0xff, 0xff, 0x00, 0x00];
444        let mut reader = Reader::new(&action_bytes[..], 5);
445        match reader.read_action() {
446            Err(crate::error::Error::Avm1ParseError { .. }) => (),
447            result => {
448                panic!("Expected Avm1ParseError, got {result:?}");
449            }
450        }
451    }
452
453    #[test]
454    fn read_define_function() {
455        // Ensure we read a function properly along with the function data.
456        let action_bytes = [
457            0x9b, 0x08, 0x00, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x96, 0x06, 0x00,
458            0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26, 0x00,
459        ];
460        let mut reader = Reader::new(&action_bytes[..], 5);
461        let action = reader.read_action().unwrap();
462        assert_eq!(
463            action,
464            Action::DefineFunction(DefineFunction {
465                name: SwfStr::from_str_with_encoding("foo", WINDOWS_1252).unwrap(),
466                params: vec![],
467                actions: &[0x96, 0x06, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 0x26],
468            })
469        );
470
471        if let Action::DefineFunction(DefineFunction { actions, .. }) = action {
472            let mut reader = Reader::new(actions, 5);
473            let action = reader.read_action().unwrap();
474            assert_eq!(
475                action,
476                Action::Push(Push {
477                    values: vec![Value::Str(
478                        SwfStr::from_str_with_encoding("test", WINDOWS_1252).unwrap()
479                    )]
480                })
481            );
482        }
483    }
484
485    #[test]
486    fn read_push() {
487        // ActionPush doesn't provide an explicit # of values, but instead reads values
488        // until the end of the action. Ensure we don't read extra values.
489        let action_bytes = [
490            OpCode::Push as u8,
491            3,
492            0,
493            2,  // null
494            10, // Invalid value type.
495            3,  // undefined
496            3,  // Extra byte at the end shouldn't be read.
497        ];
498        let mut reader = Reader::new(&action_bytes, 5);
499        let action = reader.read_action().unwrap();
500        assert_eq!(
501            action,
502            Action::Push(Push {
503                values: vec![Value::Null, Value::Undefined]
504            })
505        );
506    }
507
508    #[test]
509    fn read_length_mismatch() {
510        let action_bytes = [
511            OpCode::ConstantPool as u8,
512            5,
513            0,
514            1,
515            0,
516            b'a',
517            0,
518            OpCode::Add as u8,
519            OpCode::Subtract as u8,
520        ];
521        let mut reader = Reader::new(&action_bytes[..], 5);
522
523        let action = reader.read_action().unwrap();
524        assert_eq!(
525            action,
526            Action::ConstantPool(ConstantPool {
527                strings: vec!["a".into()]
528            })
529        );
530
531        let action = reader.read_action().unwrap();
532        assert_eq!(action, Action::Subtract);
533    }
534}