use crate::errors::FileParseError;
use crate::field::Field;
use crate::pe::section::PeSection;
use crate::utils::{extract_u16, extract_u32};
pub struct BoundForwarderRef {
pub time_date_stamp: Field<u32>,
pub offset_module_name: Field<u16>,
pub reserved: Field<u16>,
}
pub struct BoundImportDescriptor {
pub time_date_stamp: Field<u32>,
pub offset_module_name: Field<u16>,
pub number_of_module_forwarder_refs: Field<u16>,
}
pub struct BoundImportModule {
pub descriptor: BoundImportDescriptor,
pub module_name: String,
pub forwarder_refs: Vec<BoundForwarderRef>,
}
pub struct BoundImportDirectory {
pub offset: usize,
pub modules: Vec<BoundImportModule>,
}
impl BoundForwarderRef {
pub const SIZE: usize = 8;
pub fn parse(buffer: &[u8], offset: usize) -> Result<Self, FileParseError> {
if buffer.len() < offset + Self::SIZE {
return Err(FileParseError::BufferOverflow);
}
Ok(BoundForwarderRef {
time_date_stamp: Field::new(extract_u32(buffer, offset)?, offset, 4),
offset_module_name: Field::new(extract_u16(buffer, offset + 4)?, offset + 4, 2),
reserved: Field::new(extract_u16(buffer, offset + 6)?, offset + 6, 2),
})
}
}
impl BoundImportDescriptor {
pub const SIZE: usize = 8;
pub fn parse(buffer: &[u8], offset: usize) -> Result<Self, FileParseError> {
if buffer.len() < offset + Self::SIZE {
return Err(FileParseError::BufferOverflow);
}
Ok(BoundImportDescriptor {
time_date_stamp: Field::new(extract_u32(buffer, offset)?, offset, 4),
offset_module_name: Field::new(extract_u16(buffer, offset + 4)?, offset + 4, 2),
number_of_module_forwarder_refs: Field::new(
extract_u16(buffer, offset + 6)?,
offset + 6,
2,
),
})
}
pub fn is_null(&self) -> bool {
self.time_date_stamp.value == 0
&& self.offset_module_name.value == 0
&& self.number_of_module_forwarder_refs.value == 0
}
}
impl BoundImportDirectory {
pub fn parse(
buffer: &[u8],
sections: &[PeSection],
bound_rva: u32,
) -> Result<Self, FileParseError> {
if bound_rva == 0 {
return Ok(BoundImportDirectory {
offset: 0,
modules: Vec::new(),
});
}
let offset = crate::pe::import::rva_to_offset(buffer, sections, bound_rva)?;
let mut modules = Vec::new();
let mut cursor = offset;
loop {
if buffer.len() < cursor + BoundImportDescriptor::SIZE {
return Err(FileParseError::BufferOverflow);
}
let descriptor = BoundImportDescriptor::parse(buffer, cursor)?;
if descriptor.is_null() {
break;
}
let module_name = read_name(buffer, offset, descriptor.offset_module_name.value)?;
cursor += BoundImportDescriptor::SIZE;
let mut forwarder_refs = Vec::new();
for _ in 0..descriptor.number_of_module_forwarder_refs.value {
forwarder_refs.push(BoundForwarderRef::parse(buffer, cursor)?);
cursor += BoundForwarderRef::SIZE;
}
modules.push(BoundImportModule {
descriptor,
module_name,
forwarder_refs,
});
}
Ok(BoundImportDirectory { offset, modules })
}
}
fn read_name(buffer: &[u8], table_base: usize, name_offset: u16) -> Result<String, FileParseError> {
let off = table_base
.checked_add(name_offset as usize)
.ok_or(FileParseError::BufferOverflow)?;
let tail = buffer.get(off..).ok_or(FileParseError::BufferOverflow)?;
let end = tail
.iter()
.position(|&b| b == 0)
.ok_or(FileParseError::InvalidFileFormat)?;
Ok(String::from_utf8_lossy(&tail[..end]).into_owned())
}