diff --git a/crates/vestal/src/main.rs b/crates/vestal/src/main.rs index 78e75c0..08fbd9a 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -1,5 +1,6 @@ //mod execute; //mod inspect; +mod reloc; mod memrun; //mod parse; mod vst; @@ -55,7 +56,9 @@ fn read_dll (context: &mut Context, name: &str, path: &PathBuf) -> Usually<()> { //println!(" - {:8} + {:?} = {:?}", &export.rva, &export.offset, &export.name); } for import in dll.pe()?.imports.iter() { - imports.push(import.dll.to_lowercase()); + let dll = import.dll.to_lowercase(); + println!(" (-> {} {})", &dll, &import.name); + imports.push(dll); } context.insert(name.to_string(), dll); for name in imports.iter() { diff --git a/crates/vestal/src/reloc.rs b/crates/vestal/src/reloc.rs new file mode 100644 index 0000000..e37b97a --- /dev/null +++ b/crates/vestal/src/reloc.rs @@ -0,0 +1,174 @@ +use crate::*; +/// From https://docs.rs/lancelot/latest/src/lancelot/loader/pe/reloc.rs.html#68-71 +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum RelocType { + /// The base relocation is skipped. This type can be used to pad a block. + Abs = 0, + /// The base relocation adds the high 16 bits of the difference to the + /// 16-bit field at offset. The 16-bit field represents the high value of a + /// 32-bit word. + Hi = 1, + /// The base relocation adds the low 16 bits of the difference to the 16-bit + /// field at offset. The 16-bit field represents the low half of a 32-bit + /// word. + Lo = 2, + /// The base relocation applies all 32 bits of the difference to the 32-bit + /// field at offset. + HiLo = 3, + /// The base relocation adds the high 16 bits of the difference to the + /// 16-bit field at offset. The 16-bit field represents the high value of a + /// 32-bit word. The low 16 bits of the 32-bit value are stored in the + /// 16-bit word that follows this base relocation. This means that this base + /// relocation occupies two slots. + HiAdj = 4, + /// The base relocation applies the difference to the 64-bit field at + /// offset. + Dir64 = 10, +} +impl From for RelocType { + fn from(value: u16) -> Self { + match value { + 0 => Self::Abs, + 1 => Self::Hi, + 2 => Self::Lo, + 3 => Self::HiLo, + 4 => Self::HiAdj, + 10 => Self::Dir64, + _ => panic!("Invalid RelocType value: {}", value), + } + } +} +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Relocation { + type_: RelocType, + address: VA, +} +pub struct RelocSectionData { + base_address: VA, + buf: Vec, +} +pub type VA = u64; +pub type RVA = u64; +impl RelocSectionData { + fn read_u16(&self, offset: usize) -> Usually { + if offset + 2 > self.buf.len() { + return Err("buffer too small".into()) + } + Ok(u16::from_le_bytes([ + self.buf[offset + 0], + self.buf[offset + 1], + ])) + } + fn read_u32(&self, offset: usize) -> Usually { + if offset + 4 > self.buf.len() { + return Err("buffer too small".into()) + } + Ok(u32::from_le_bytes([ + self.buf[offset + 0], + self.buf[offset + 1], + self.buf[offset + 2], + self.buf[offset + 3], + ])) + } + pub fn relocations(&self) -> Usually> { + let mut relocations = vec![]; + let mut offset = 0x0; + while offset < self.buf.len() { + let rva = self.read_u32(offset)? as u64; + let size = self.read_u32(offset + 4)? as usize; + println!("(reloc-block rva={rva:x} size={size:x})"); + const header_size: usize = 8; + const entry_size: usize = 2; + let entry_count = (size - header_size) / entry_size; + for entry_index in 0..entry_count { + let entry = self.read_u16(offset + header_size + (entry_index * entry_size))?; + let entry_type = RelocType::from(entry >> 12); + let entry_value = (entry & 0x0FFF) as u64; + let address = self.base_address + rva + entry_value; + println!("(reloc-entry addr={address:x} type={entry_type:?})"); + relocations.push(Relocation { + type_: entry_type, + address, + }); + } + offset += size; + } + Ok(relocations) + } + pub fn from_pe(pe: &PE) -> Usually> { + let opt_header = match pe.header.optional_header { + None => return Ok(None), + Some(opt_header) => opt_header, + }; + let reloc_table = match opt_header.data_directories.get_base_relocation_table() { + None => return Ok(None), + Some(reloc_table) => reloc_table, + }; + println!( + "(reloc-table {:#x}-{:#x})", + pe.module.address_space.base_address + reloc_table.virtual_address as RVA, + pe.module.address_space.base_address + reloc_table.virtual_address as RVA + reloc_table.size as RVA + ); + let buf = pe.module.address_space.read_bytes( + // goblin calls this a "virtual address", but its actually an RVA. + pe.module.address_space.base_address + reloc_table.virtual_address as RVA, + reloc_table.size as usize, + )?; + Ok(Some(RelocSectionData { + base_address: pe.module.address_space.base_address, + buf, + })) + } +} +pub fn apply_relocations(pe: &mut PE) -> Usually<()> { + let opt_header = match pe.optional_header { + None => return Ok(()), + Some(opt_header) => opt_header, + }; + let wanted = opt_header.windows_fields.image_base; + let found = pe.module.address_space.base_address; + if wanted == found { + println!("# reloc: no relocations necessary"); + return Ok(()); + } + let relocations = if let Ok(Some(reloc_data)) = RelocSectionData::from_pe(pe) { + reloc_data.relocations()? + } else { + println!("# reloc: no relocations found"); + return Ok(()); + }; + let delta = (found as i64) - (wanted as i64); + println!("# reloc: applying {} relocations", relocations.len()); + for relocation in relocations.iter() { + match relocation.type_ { + // https://github.com/abhisek/Pe-Loader-Sample/blob/9aed4b3f6cd33ef75a0e01c21ea9f81608bf96cf/src/PeLdr.cpp#L44 + RelocType::Abs => continue, + RelocType::Dir64 => { + println!("(reloc-dir64 {:x})", relocation.address); + let existing = pe.module.address_space.read_u64(relocation.address)?; + let updated = existing as i64 + delta; + pe.module.address_space.write_u64(relocation.address, updated as u64)?; + } + RelocType::HiLo => { + println!("(reloc-hi-lo)"); + let existing = pe.module.address_space.read_u32(relocation.address)?; + let updated = existing + (delta & 0xFFFF_FFFF) as u32; + pe.module.address_space.write_u32(relocation.address, updated)?; + } + RelocType::Hi => { + println!("(reloc-hi)"); + let existing = pe.module.address_space.read_u16(relocation.address)?; + let updated = existing + (((delta >> 16) & 0xFFFF) as u16); + pe.module.address_space.write_u16(relocation.address, updated)?; + } + RelocType::Lo => { + println!("(reloc-lo)"); + let existing = pe.module.address_space.read_u16(relocation.address)?; + let updated = existing + ((delta & 0xFFFF) as u16); + pe.module.address_space.write_u16(relocation.address, updated)?; + } + _ => unimplemented!(), + }; + } + Ok(()) +}