use goblin::error;
use goblin::pe::section_table::SectionTable;
use tracing::{instrument, Span};
use crate::Result;
const BASE_RELOCATION_SIZE: usize = 2;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub(super) struct BaseRelocation {
pub(super) typ: u8,
pub(super) page_offset: u16,
pub(super) page_base_rva: u32,
}
#[derive(Default)]
struct BaseRelocations<'a> {
offset: usize,
relocations: &'a [u8],
}
impl<'a> BaseRelocations<'a> {
pub(super) fn parse(
bytes: &'a [u8],
offset: usize,
number: usize,
) -> Result<BaseRelocations<'a>> {
let relocations = &bytes
.get(offset..offset + number * BASE_RELOCATION_SIZE)
.ok_or_else(|| {
error::Error::Malformed(format!(
"Failed to read base relocations at offset {}",
offset
))
})?;
Ok(BaseRelocations {
offset: 0,
relocations,
})
}
}
impl<'a> Iterator for BaseRelocations<'a> {
type Item = BaseRelocation;
fn next(&mut self) -> Option<Self::Item> {
if self.offset + 1 >= self.relocations.len() {
return None;
}
let block: [u8; 2] = [
self.relocations[self.offset],
self.relocations[self.offset + 1],
];
let word = u16::from_le_bytes(block);
let typ = u8::try_from(word >> 12).unwrap();
let addr_offset = word & 0b0000111111111111;
self.offset += 2;
Some(BaseRelocation {
typ,
page_offset: addr_offset,
page_base_rva: 0, })
}
}
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
pub(super) fn get_base_relocations(
payload: &[u8],
reloc_section: &Option<SectionTable>,
) -> Result<Vec<BaseRelocation>> {
let mut base_relocations: Vec<BaseRelocation> = Vec::new();
if let Some(reloc_section) = reloc_section {
let mut next_block_offset = reloc_section.pointer_to_raw_data as usize; let table_size = reloc_section.virtual_size as usize;
let mut size_processed: usize = 0;
while size_processed < table_size {
let block_header =
goblin::pe::data_directories::DataDirectory::parse(payload, &mut next_block_offset)
.expect("oops");
let page_virtual_address = block_header.virtual_address;
let reloc_num = (block_header.size as usize - 8) / BASE_RELOCATION_SIZE;
size_processed += block_header.size as usize;
let relocations = BaseRelocations::parse(payload, next_block_offset, reloc_num)?;
for mut r in relocations {
r.page_base_rva = page_virtual_address;
base_relocations.push(r);
}
next_block_offset += (block_header.size - 8) as usize;
}
}
Ok(base_relocations)
}