vtil_parser/
lib.rs

1// BSD 3-Clause License
2//
3// Copyright © 2020-2021 Keegan Saunders
4// Copyright © 2020-2021 VTIL Project
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are met:
9//
10// 1. Redistributions of source code must retain the above copyright notice, this
11//    list of conditions and the following disclaimer.
12//
13// 2. Redistributions in binary form must reproduce the above copyright notice,
14//    this list of conditions and the following disclaimer in the documentation
15//    and/or other materials provided with the distribution.
16//
17// 3. Neither the name of the copyright holder nor the names of its
18//    contributors may be used to endorse or promote products derived from
19//    this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//
32//! # VTIL-RustParser
33//!
34//! Read/write VTIL files in Rust.
35//!
36//! You can learn more about VTIL [here](https://github.com/vtil-project/VTIL-Core#introduction)
37//! on the main GitHub page.
38//!
39//! # Examples
40//! For a simple example of loading a VTIL routine and reading out some basic data:
41//! ```
42//! # use vtil_parser::Result;
43//! use vtil_parser::{Routine, ArchitectureIdentifier};
44//!
45//! # fn main() -> Result<()> {
46//! let routine = Routine::from_path("resources/big.vtil")?;
47//! assert_eq!(routine.header.arch_id, ArchitectureIdentifier::Amd64);
48//! # Ok(())
49//! # }
50//! ```
51//!
52//! For a more complex example, iterating over IL instructions:
53//! ```
54//! # use vtil_parser::Result;
55//! use vtil_parser::{Routine, Op, Operand, RegisterDesc, ImmediateDesc, RegisterFlags};
56//!
57//! # fn main() -> Result<()> {
58//! let routine = Routine::from_path("resources/big.vtil")?;
59//!
60//! for (_, basic_block) in routine.explored_blocks.iter().take(1) {
61//!     for instr in basic_block.instructions.iter().take(1) {
62//!         match &instr.op {
63//!             Op::Ldd(_, Operand::RegisterDesc(op2), Operand::ImmediateDesc(op3)) => {
64//!                 assert!(op2.flags.contains(RegisterFlags::PHYSICAL));
65//!                 assert!(op3.i64() == 0);
66//!             }
67//!             _ => assert!(false)
68//!         }
69//!
70//!         assert_eq!(instr.vip.0, 0x9b833);
71//!     }
72//! }
73//! # Ok(())
74//! # }
75//! ```
76
77#![allow(clippy::upper_case_acronyms)]
78#![allow(clippy::useless_conversion)]
79#![deny(missing_docs)]
80
81use memmap::MmapOptions;
82use scroll::{ctx::SizeWith, Pread, Pwrite};
83
84use indexmap::map::IndexMap;
85use std::fs::File;
86use std::path::Path;
87
88#[macro_use]
89extern crate bitflags;
90
91mod error;
92pub use error::Error;
93
94mod arch_info;
95
96mod pod;
97pub use pod::*;
98
99mod serialize;
100pub use serialize::*;
101
102mod instr_builder;
103pub use instr_builder::*;
104
105/// Helpers for dumping VTIL structures
106pub mod dump;
107
108#[doc(hidden)]
109pub type Result<T> = std::result::Result<T, error::Error>;
110
111/// VTIL routine container
112impl Routine {
113    /// Build a new VTIL routine container
114    pub fn new(arch_id: ArchitectureIdentifier) -> Routine {
115        let (routine_convention, subroutine_convention) = match arch_id {
116            ArchitectureIdentifier::Virtual => {
117                let routine_convention = RoutineConvention {
118                    volatile_registers: vec![],
119                    param_registers: vec![],
120                    retval_registers: vec![],
121                    // Not used, so it doesn't matter
122                    frame_register: RegisterDesc {
123                        flags: RegisterFlags::VIRTUAL,
124                        combined_id: 0,
125                        bit_count: 0,
126                        bit_offset: 0,
127                    },
128                    shadow_space: 0,
129                    purge_stack: true,
130                };
131                (routine_convention.clone(), routine_convention)
132            }
133            _ => unimplemented!(),
134        };
135        Routine {
136            header: Header { arch_id },
137            vip: Vip(0),
138            routine_convention,
139            subroutine_convention,
140            spec_subroutine_conventions: vec![],
141            explored_blocks: IndexMap::new(),
142        }
143    }
144
145    /// Tries to create a [`BasicBlock`], returns `None` if a block already
146    /// exists at the given address
147    pub fn create_block(&mut self, vip: Vip) -> Option<&mut BasicBlock> {
148        if !self.explored_blocks.contains_key(&vip) {
149            let basic_block = BasicBlock {
150                vip,
151                sp_offset: 0,
152                sp_index: 0,
153                last_temporary_index: 0,
154                instructions: vec![],
155                prev_vip: vec![],
156                next_vip: vec![],
157            };
158
159            self.explored_blocks.insert(vip, basic_block);
160            Some(self.explored_blocks.get_mut(&vip).unwrap())
161        } else {
162            None
163        }
164    }
165
166    /// Tries to remove a [`BasicBlock`] from the [`Routine`]
167    pub fn remove_block(&mut self, vip: Vip) -> Option<BasicBlock> {
168        self.explored_blocks.remove(&vip)
169    }
170
171    /// Tries to load VTIL routine from the given path
172    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Routine> {
173        let source = Box::new(unsafe { MmapOptions::new().map(&File::open(path.as_ref())?)? });
174        source.pread_with::<Routine>(0, scroll::LE)
175    }
176
177    /// Loads VTIL routine from a `Vec<u8>`
178    pub fn from_vec(source: &[u8]) -> Result<Routine> {
179        source.as_ref().pread_with::<Routine>(0, scroll::LE)
180    }
181
182    /// Serialize the VTIL routine container, consuming it
183    pub fn into_bytes(self) -> Result<Vec<u8>> {
184        let size = Routine::size_with(&self);
185        let mut buffer = vec![0; size];
186        buffer.pwrite_with::<Routine>(self, 0, scroll::LE)?;
187        Ok(buffer)
188    }
189}