1 use crate::alloc::borrow::Cow; 2 use crate::alloc::vec::Vec; 3 use core::fmt::{LowerHex, Debug}; 4 5 use scroll::{Pread, Pwrite, SizeWith}; 6 use scroll::ctx::TryFromCtx; 7 use crate::error; 8 9 use crate::pe::section_table; 10 use crate::pe::utils; 11 use crate::pe::data_directories; 12 13 use log::{debug, warn}; 14 15 pub const IMPORT_BY_ORDINAL_32: u32 = 0x8000_0000; 16 pub const IMPORT_BY_ORDINAL_64: u64 = 0x8000_0000_0000_0000; 17 pub const IMPORT_RVA_MASK_32: u32 = 0x7fff_ffff; 18 pub const IMPORT_RVA_MASK_64: u64 = 0x0000_0000_7fff_ffff; 19 20 pub trait Bitfield<'a>: Into<u64> + PartialEq + Eq + LowerHex + Debug + TryFromCtx<'a, scroll::Endian, Error=scroll::Error, Size=usize> { is_ordinal(&self) -> bool21 fn is_ordinal(&self) -> bool; to_ordinal(&self) -> u1622 fn to_ordinal(&self) -> u16; to_rva(&self) -> u3223 fn to_rva(&self) -> u32; size_of() -> usize24 fn size_of() -> usize; is_zero(&self) -> bool25 fn is_zero(&self) -> bool; 26 } 27 28 impl<'a> Bitfield<'a> for u64 { is_ordinal(&self) -> bool29 fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_64 == IMPORT_BY_ORDINAL_64 } to_ordinal(&self) -> u1630 fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 } to_rva(&self) -> u3231 fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_64) as u32 } size_of() -> usize32 fn size_of() -> usize { 8 } is_zero(&self) -> bool33 fn is_zero(&self) -> bool { *self == 0 } 34 } 35 36 impl<'a> Bitfield<'a> for u32 { is_ordinal(&self) -> bool37 fn is_ordinal(&self) -> bool { self & IMPORT_BY_ORDINAL_32 == IMPORT_BY_ORDINAL_32 } to_ordinal(&self) -> u1638 fn to_ordinal(&self) -> u16 { (0xffff & self) as u16 } to_rva(&self) -> u3239 fn to_rva(&self) -> u32 { (self & IMPORT_RVA_MASK_32) as u32 } size_of() -> usize40 fn size_of() -> usize { 4 } is_zero(&self) -> bool41 fn is_zero(&self) -> bool { *self == 0 } 42 } 43 44 #[derive(Debug, Clone)] 45 pub struct HintNameTableEntry<'a> { 46 pub hint: u16, 47 pub name: &'a str, 48 } 49 50 impl<'a> HintNameTableEntry<'a> { parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self>51 fn parse(bytes: &'a [u8], mut offset: usize) -> error::Result<Self> { 52 let offset = &mut offset; 53 let hint = bytes.gread_with(offset, scroll::LE)?; 54 let name = bytes.pread::<&'a str>(*offset)?; 55 Ok(HintNameTableEntry { hint, name }) 56 } 57 } 58 59 #[derive(Debug, Clone)] 60 pub enum SyntheticImportLookupTableEntry<'a> { 61 OrdinalNumber(u16), 62 HintNameTableRVA ((u32, HintNameTableEntry<'a>)), // [u8; 31] bitfield :/ 63 } 64 65 pub type ImportLookupTable<'a> = Vec<SyntheticImportLookupTableEntry<'a>>; 66 67 impl<'a> SyntheticImportLookupTableEntry<'a> { parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportLookupTable<'a>>68 pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], mut offset: usize, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportLookupTable<'a>> { 69 let le = scroll::LE; 70 let offset = &mut offset; 71 let mut table = Vec::new(); 72 loop { 73 let bitfield: T = bytes.gread_with(offset, le)?; 74 if bitfield.is_zero() { 75 debug!("imports done"); 76 break; 77 } else { 78 let entry = { 79 debug!("bitfield {:#x}", bitfield); 80 use self::SyntheticImportLookupTableEntry::*; 81 if bitfield.is_ordinal() { 82 let ordinal = bitfield.to_ordinal(); 83 debug!("importing by ordinal {:#x}", ordinal); 84 OrdinalNumber(ordinal) 85 } else { 86 let rva = bitfield.to_rva(); 87 let hentry = { 88 debug!("searching for RVA {:#x}", rva); 89 if let Some(offset) = utils::find_offset(rva as usize, sections, file_alignment) { 90 debug!("offset {:#x}", offset); 91 HintNameTableEntry::parse(bytes, offset)? 92 } else { 93 warn!("Entry {} has bad RVA: {:#x}", table.len(), rva); 94 continue 95 } 96 }; 97 HintNameTableRVA ((rva, hentry)) 98 } 99 }; 100 table.push(entry); 101 } 102 } 103 Ok(table) 104 } 105 } 106 107 // get until entry is 0 108 pub type ImportAddressTable = Vec<u64>; 109 110 #[repr(C)] 111 #[derive(Debug)] 112 #[derive(Pread, Pwrite, SizeWith)] 113 pub struct ImportDirectoryEntry { 114 pub import_lookup_table_rva: u32, 115 pub time_date_stamp: u32, 116 pub forwarder_chain: u32, 117 pub name_rva: u32, 118 pub import_address_table_rva: u32, 119 } 120 121 pub const SIZEOF_IMPORT_DIRECTORY_ENTRY: usize = 20; 122 123 impl ImportDirectoryEntry { is_null(&self) -> bool124 pub fn is_null (&self) -> bool { 125 (self.import_lookup_table_rva == 0) && 126 (self.time_date_stamp == 0) && 127 (self.forwarder_chain == 0) && 128 (self.name_rva == 0) && 129 (self.import_address_table_rva == 0) 130 } 131 } 132 133 #[derive(Debug)] 134 pub struct SyntheticImportDirectoryEntry<'a> { 135 pub import_directory_entry: ImportDirectoryEntry, 136 /// Computed 137 pub name: &'a str, 138 /// The import lookup table is a vector of either ordinals, or RVAs + import names 139 pub import_lookup_table: Option<ImportLookupTable<'a>>, 140 /// Computed 141 pub import_address_table: ImportAddressTable, 142 } 143 144 impl<'a> SyntheticImportDirectoryEntry<'a> { parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<SyntheticImportDirectoryEntry<'a>>145 pub fn parse<T: Bitfield<'a>>(bytes: &'a [u8], import_directory_entry: ImportDirectoryEntry, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<SyntheticImportDirectoryEntry<'a>> { 146 const LE: scroll::Endian = scroll::LE; 147 let name_rva = import_directory_entry.name_rva; 148 let name = utils::try_name(bytes, name_rva as usize, sections, file_alignment)?; 149 let import_lookup_table = { 150 let import_lookup_table_rva = import_directory_entry.import_lookup_table_rva; 151 let import_address_table_rva = import_directory_entry.import_address_table_rva; 152 if let Some(import_lookup_table_offset) = utils::find_offset(import_lookup_table_rva as usize, sections, file_alignment) { 153 debug!("Synthesizing lookup table imports for {} lib, with import lookup table rva: {:#x}", name, import_lookup_table_rva); 154 let import_lookup_table = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_lookup_table_offset, sections, file_alignment)?; 155 debug!("Successfully synthesized import lookup table entry from lookup table: {:#?}", import_lookup_table); 156 Some(import_lookup_table) 157 } else if let Some(import_address_table_offset) = utils::find_offset(import_address_table_rva as usize, sections, file_alignment) { 158 debug!("Synthesizing lookup table imports for {} lib, with import address table rva: {:#x}", name, import_lookup_table_rva); 159 let import_address_table = SyntheticImportLookupTableEntry::parse::<T>(bytes, import_address_table_offset, sections, file_alignment)?; 160 debug!("Successfully synthesized import lookup table entry from IAT: {:#?}", import_address_table); 161 Some(import_address_table) 162 } else { 163 None 164 } 165 }; 166 167 let import_address_table_offset = &mut utils::find_offset(import_directory_entry.import_address_table_rva as usize, sections, file_alignment).ok_or_else(|| error::Error::Malformed(format!("Cannot map import_address_table_rva {:#x} into offset for {}", import_directory_entry.import_address_table_rva, name)))?; 168 let mut import_address_table = Vec::new(); 169 loop { 170 let import_address = bytes.gread_with::<T>(import_address_table_offset, LE)?.into(); 171 if import_address == 0 { break } else { import_address_table.push(import_address); } 172 } 173 Ok(SyntheticImportDirectoryEntry { 174 import_directory_entry, 175 name, 176 import_lookup_table, 177 import_address_table 178 }) 179 } 180 } 181 182 #[derive(Debug)] 183 /// Contains a list of synthesized import data for this binary, e.g., which symbols from which libraries it is importing from 184 pub struct ImportData<'a> { 185 pub import_data: Vec<SyntheticImportDirectoryEntry<'a>>, 186 } 187 188 impl<'a> ImportData<'a> { parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportData<'a>>189 pub fn parse<T: Bitfield<'a>>(bytes: &'a[u8], dd: data_directories::DataDirectory, sections: &[section_table::SectionTable], file_alignment: u32) -> error::Result<ImportData<'a>> { 190 let import_directory_table_rva = dd.virtual_address as usize; 191 debug!("import_directory_table_rva {:#x}", import_directory_table_rva); 192 let offset = &mut utils::find_offset(import_directory_table_rva, sections, file_alignment).ok_or_else(|| error::Error::Malformed(format!("Cannot create ImportData; cannot map import_directory_table_rva {:#x} into offset", import_directory_table_rva)))?;; 193 debug!("import data offset {:#x}", offset); 194 let mut import_data = Vec::new(); 195 loop { 196 let import_directory_entry: ImportDirectoryEntry = bytes.gread_with(offset, scroll::LE)?; 197 debug!("{:#?}", import_directory_entry); 198 if import_directory_entry.is_null() { 199 break; 200 } else { 201 let entry = SyntheticImportDirectoryEntry::parse::<T>(bytes, import_directory_entry, sections, file_alignment)?; 202 debug!("entry {:#?}", entry); 203 import_data.push(entry); 204 } 205 } 206 debug!("finished ImportData"); 207 Ok(ImportData { import_data}) 208 } 209 } 210 211 #[derive(Debug)] 212 /// A synthesized symbol import, the name is pre-indexed, and the binary offset is computed, as well as which dll it belongs to 213 pub struct Import<'a> { 214 pub name: Cow<'a, str>, 215 pub dll: &'a str, 216 pub ordinal: u16, 217 pub offset: usize, 218 pub rva: usize, 219 pub size: usize, 220 } 221 222 impl<'a> Import<'a> { parse<T: Bitfield<'a>>(_bytes: &'a [u8], import_data: &ImportData<'a>, _sections: &[section_table::SectionTable]) -> error::Result<Vec<Import<'a>>>223 pub fn parse<T: Bitfield<'a>>(_bytes: &'a [u8], import_data: &ImportData<'a>, _sections: &[section_table::SectionTable]) -> error::Result<Vec<Import<'a>>> { 224 let mut imports = Vec::new(); 225 for data in &import_data.import_data { 226 if let Some(ref import_lookup_table) = data.import_lookup_table { 227 let dll = data.name; 228 let import_base = data.import_directory_entry.import_address_table_rva as usize; 229 debug!("Getting imports from {}", &dll); 230 for (i, entry) in import_lookup_table.iter().enumerate() { 231 let offset = import_base + (i * T::size_of()); 232 use self::SyntheticImportLookupTableEntry::*; 233 let (rva, name, ordinal) = 234 match *entry { 235 HintNameTableRVA ((rva, ref hint_entry)) => { 236 // if hint_entry.name = "" && hint_entry.hint = 0 { 237 // println!("<PE.Import> warning hint/name table rva from {} without hint {:#x}", dll, rva); 238 // } 239 (rva, Cow::Borrowed(hint_entry.name), hint_entry.hint) 240 }, 241 OrdinalNumber(ordinal) => { 242 let name = format!("ORDINAL {}", ordinal); 243 (0x0, Cow::Owned(name), ordinal) 244 }, 245 }; 246 let import = 247 Import { 248 name, 249 ordinal, dll, 250 size: T::size_of(), offset, rva: rva as usize 251 }; 252 imports.push(import); 253 } 254 } 255 } 256 Ok (imports) 257 } 258 } 259