use crate::errors::FileParseError;
use crate::field::Field;
use crate::pe::header::PEType;
use crate::pe::import::{parse_thunk_table, ImportEntry};
use crate::pe::section::PeSection;
use crate::utils::extract_u32;
pub struct DelayLoadDescriptor {
pub attributes: Field<u32>,
pub dll_name_rva: Field<u32>,
pub module_handle_rva: Field<u32>,
pub delay_import_address_table_rva: Field<u32>,
pub delay_import_name_table_rva: Field<u32>,
pub bound_delay_import_table_rva: Field<u32>,
pub unload_delay_import_table_rva: Field<u32>,
pub time_date_stamp: Field<u32>,
}
pub struct DelayLoadImport {
pub descriptor: DelayLoadDescriptor,
pub dll_name_offset: usize,
pub dll_name: String,
pub entries: Vec<ImportEntry>,
}
pub struct DelayLoadDirectory {
pub offset: usize,
pub descriptors: Vec<DelayLoadDescriptor>,
pub dlls: Vec<DelayLoadImport>,
}
impl DelayLoadDescriptor {
pub const SIZE: usize = 32;
pub fn parse(buffer: &[u8], offset: usize) -> Result<Self, FileParseError> {
if buffer.len() < offset + Self::SIZE {
return Err(FileParseError::BufferOverflow);
}
Ok(DelayLoadDescriptor {
attributes: Field::new(extract_u32(buffer, offset)?, offset, 4),
dll_name_rva: Field::new(extract_u32(buffer, offset + 4)?, offset + 4, 4),
module_handle_rva: Field::new(extract_u32(buffer, offset + 8)?, offset + 8, 4),
delay_import_address_table_rva: Field::new(
extract_u32(buffer, offset + 12)?,
offset + 12,
4,
),
delay_import_name_table_rva: Field::new(
extract_u32(buffer, offset + 16)?,
offset + 16,
4,
),
bound_delay_import_table_rva: Field::new(
extract_u32(buffer, offset + 20)?,
offset + 20,
4,
),
unload_delay_import_table_rva: Field::new(
extract_u32(buffer, offset + 24)?,
offset + 24,
4,
),
time_date_stamp: Field::new(extract_u32(buffer, offset + 28)?, offset + 28, 4),
})
}
pub fn is_null(&self) -> bool {
self.attributes.value == 0
&& self.dll_name_rva.value == 0
&& self.module_handle_rva.value == 0
&& self.delay_import_address_table_rva.value == 0
&& self.delay_import_name_table_rva.value == 0
&& self.bound_delay_import_table_rva.value == 0
&& self.unload_delay_import_table_rva.value == 0
&& self.time_date_stamp.value == 0
}
}
impl DelayLoadDirectory {
pub fn parse(
buffer: &[u8],
sections: &[PeSection],
delay_rva: u32,
pe_type: PEType,
) -> Result<Self, FileParseError> {
if delay_rva == 0 {
return Ok(DelayLoadDirectory {
offset: 0,
descriptors: Vec::new(),
dlls: Vec::new(),
});
}
let offset = crate::pe::import::rva_to_offset(buffer, sections, delay_rva)?;
let mut descriptors = Vec::new();
let mut cursor = offset;
loop {
let desc = DelayLoadDescriptor::parse(buffer, cursor)?;
if desc.is_null() {
break;
}
descriptors.push(desc);
cursor += DelayLoadDescriptor::SIZE;
}
let dlls = descriptors
.iter()
.map(|desc| parse_delay_dll(buffer, sections, desc, pe_type))
.collect::<Result<Vec<_>, _>>()?;
Ok(DelayLoadDirectory {
offset,
descriptors,
dlls,
})
}
}
fn parse_delay_dll(
buffer: &[u8],
sections: &[PeSection],
desc: &DelayLoadDescriptor,
pe_type: PEType,
) -> Result<DelayLoadImport, FileParseError> {
let dll_name_offset =
crate::pe::import::rva_to_offset(buffer, sections, desc.dll_name_rva.value)?;
let dll_name = read_c_string(buffer, dll_name_offset)?;
let thunk_rva = if desc.delay_import_name_table_rva.value != 0 {
desc.delay_import_name_table_rva.value
} else {
desc.delay_import_address_table_rva.value
};
let entries = parse_thunk_table(buffer, sections, thunk_rva, pe_type)?;
Ok(DelayLoadImport {
descriptor: DelayLoadDescriptor {
attributes: desc.attributes.clone(),
dll_name_rva: desc.dll_name_rva.clone(),
module_handle_rva: desc.module_handle_rva.clone(),
delay_import_address_table_rva: desc.delay_import_address_table_rva.clone(),
delay_import_name_table_rva: desc.delay_import_name_table_rva.clone(),
bound_delay_import_table_rva: desc.bound_delay_import_table_rva.clone(),
unload_delay_import_table_rva: desc.unload_delay_import_table_rva.clone(),
time_date_stamp: desc.time_date_stamp.clone(),
},
dll_name_offset,
dll_name,
entries,
})
}
fn read_c_string(buffer: &[u8], offset: usize) -> Result<String, FileParseError> {
let tail = buffer.get(offset..).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())
}