use bitflags::bitflags;
use pkbuffer::{Buffer, VecBuffer};
use std::collections::HashMap;
use std::mem;
use widestring::utfstr::Utf16Str;
use widestring::utfstring::Utf16String;
use crate::*;
use crate::headers::*;
use crate::align;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Arch {
X86,
X64,
}
#[repr(C)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
pub struct CChar(pub u8);
impl From<u8> for CChar {
fn from(c: u8) -> Self {
Self(c)
}
}
impl From<CChar> for u8 {
fn from(c: CChar) -> Self {
c.0
}
}
pub trait CCharString {
fn zero_terminated(&self) -> Option<&Self>;
fn as_str(&self) -> Result<&str, Error>;
}
impl CCharString for [CChar] {
fn zero_terminated(&self) -> Option<&Self> {
self.iter()
.position(|&CChar(x)| x == 0)
.map(|p| &self[..p])
}
fn as_str(&self) -> Result<&str, Error> {
let cstr = self.zero_terminated().unwrap_or(&self);
let array: Vec<u8> = self.iter().map(|x| x.0).collect();
let result = std::str::from_utf8(array.as_slice());
if result.is_err() {
return Err(Error::Utf8Error(result.unwrap_err()));
}
Ok(unsafe { mem::transmute::<&[CChar],&str>(cstr) })
}
}
#[repr(C)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
pub struct WChar(pub u16);
impl From<u16> for WChar {
fn from(c: u16) -> Self {
Self(c)
}
}
impl From<WChar> for u16 {
fn from(c: WChar) -> Self {
c.0
}
}
pub trait WCharString {
fn zero_terminated(&self) -> Option<&Self>;
fn as_u16_str(&self) -> Result<&Utf16Str, Error>;
}
impl WCharString for [WChar] {
fn zero_terminated(&self) -> Option<&Self> {
self.iter()
.position(|&WChar(x)| x == 0)
.map(|p| &self[..p])
}
fn as_u16_str(&self) -> Result<&Utf16Str, Error> {
let u16str = self.zero_terminated().unwrap_or(&self);
let u16vec: Vec<u16> = u16str.iter().map(|x| x.0).collect();
let result = Utf16String::from_vec(u16vec);
if result.is_err() {
return Err(Error::Utf16Error(result.unwrap_err()));
}
Ok(unsafe { mem::transmute::<&[WChar],&Utf16Str>(u16str) })
}
}
pub trait Address {
fn as_offset<P: PE>(&self, pe: &P) -> Result<Offset, Error>;
fn as_rva<P: PE>(&self, pe: &P) -> Result<RVA, Error>;
fn as_va<P: PE>(&self, pe: &P) -> Result<VA, Error>;
fn as_ptr<P: PE>(&self, pe: &P) -> Result<*const u8, Error>;
}
#[repr(C)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
pub struct Offset(pub u32);
impl Offset {
pub fn read_val<T: Copy, P: PE>(&self, pe: &P) -> Result<T, Error> {
pe.read_val::<T>((*self).into()).map_err(|e| Error::from(e))
}
pub fn write_val<T: Copy, P: PE>(&self, pe: &mut P, data: &T) -> Result<(), Error> {
pe.write_val::<T>((*self).into(), data).map_err(|e| Error::from(e))
}
pub fn get_cstring_size<P: PE>(&self, pe: &P, thunk: bool, max_size: Option<usize>) -> Result<usize, Error> {
let result = pe.get_cstring_size((*self).into(), thunk, max_size)?; Ok(result)
}
pub fn get_widestring_size<P: PE>(&self, pe: &P, max_size: Option<usize>) -> Result<usize, Error> {
let result = pe.get_widestring_size((*self).into(), max_size)?; Ok(result)
}
pub fn get_cstring<'data, P: PE>(&self, pe: &'data P, thunk: bool, max_size: Option<usize>) -> Result<&'data [CChar], Error> {
let result = pe.get_cstring((*self).into(), thunk, max_size)?; Ok(result)
}
pub fn get_mut_cstring<'data, P: PE>(&self, pe: &'data mut P, thunk: bool, max_size: Option<usize>) -> Result<&'data mut [CChar], Error> {
let result = pe.get_mut_cstring((*self).into(), thunk, max_size)?; Ok(result)
}
pub fn get_widestring<'data, P: PE>(&self, pe: &'data P, max_size: Option<usize>) -> Result<&'data [WChar], Error> {
let result = pe.get_widestring((*self).into(), max_size)?; Ok(result)
}
pub fn get_mut_widestring<'data, P: PE>(&self, pe: &'data mut P, max_size: Option<usize>) -> Result<&'data mut [WChar], Error> {
let result = pe.get_mut_widestring((*self).into(), max_size)?; Ok(result)
}
pub fn read<'data, P: PE>(&self, pe: &'data P, size: usize) -> Result<&'data [u8], Error> {
let result = pe.read((*self).into(), size)?; Ok(result)
}
pub fn read_mut<'data, P: PE>(&self, pe: &'data mut P, size: usize) -> Result<&'data mut [u8], Error> {
let result = pe.read_mut((*self).into(), size)?; Ok(result)
}
pub fn write<P: PE, B: AsRef<[u8]>>(&self, pe: &mut P, data: B) -> Result<(), Error> {
pe.write((*self).into(), data).map_err(Error::from)
}
}
impl Address for Offset {
fn as_offset<P: PE>(&self, _: &P) -> Result<Offset, Error> {
Ok(self.clone())
}
fn as_rva<P: PE>(&self, pe: &P) -> Result<RVA, Error> {
pe.offset_to_rva(*self)
}
fn as_va<P: PE>(&self, pe: &P) -> Result<VA, Error> {
pe.offset_to_va(*self)
}
fn as_ptr<P: PE>(&self, pe: &P) -> Result<*const u8, Error> {
let corrected_offset = pe.translate(PETranslation::Disk(*self))?;
let result = pe.offset_to_ptr(corrected_offset)?;
Ok(result)
}
}
impl std::convert::Into<usize> for Offset {
fn into(self) -> usize {
self.0 as usize
}
}
impl From<u32> for Offset {
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<Offset> for u32 {
fn from(v: Offset) -> Self {
v.0
}
}
#[repr(C)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
pub struct RVA(pub u32);
impl Address for RVA {
fn as_offset<P: PE>(&self, pe: &P) -> Result<Offset, Error> {
pe.rva_to_offset(*self)
}
fn as_rva<P: PE>(&self, _: &P) -> Result<RVA, Error> {
Ok(self.clone())
}
fn as_va<P: PE>(&self, pe: &P) -> Result<VA, Error> {
pe.rva_to_va(*self)
}
fn as_ptr<P: PE>(&self, pe: &P) -> Result<*const u8, Error> {
let offset = pe.translate(PETranslation::Memory(*self))?;
let result = pe.offset_to_ptr(offset)?;
Ok(result)
}
}
impl std::convert::Into<usize> for RVA {
fn into(self) -> usize {
self.0 as usize
}
}
impl From<u32> for RVA {
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<RVA> for u32 {
fn from(v: RVA) -> Self {
v.0
}
}
#[repr(C)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
pub struct VA32(pub u32);
impl Address for VA32 {
fn as_offset<P: PE>(&self, pe: &P) -> Result<Offset, Error> {
pe.va_to_offset(VA::VA32(*self))
}
fn as_rva<P: PE>(&self, pe: &P) -> Result<RVA, Error> {
pe.va_to_rva(VA::VA32(*self))
}
fn as_va<P: PE>(&self, _: &P) -> Result<VA, Error> {
Ok(VA::VA32(self.clone()))
}
fn as_ptr<P: PE>(&self, pe: &P) -> Result<*const u8, Error> {
let rva = self.as_rva(pe)?;
rva.as_ptr(pe)
}
}
impl From<u32> for VA32 {
fn from(v: u32) -> Self {
Self(v)
}
}
impl From<VA32> for u32 {
fn from(v: VA32) -> Self {
v.0
}
}
#[repr(C)]
#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
pub struct VA64(pub u64);
impl Address for VA64 {
fn as_offset<P: PE>(&self, pe: &P) -> Result<Offset, Error> {
pe.va_to_offset(VA::VA64(*self))
}
fn as_rva<P: PE>(&self, pe: &P) -> Result<RVA, Error> {
pe.va_to_rva(VA::VA64(*self))
}
fn as_va<P: PE>(&self, _: &P) -> Result<VA, Error> {
Ok(VA::VA64(self.clone()))
}
fn as_ptr<P: PE>(&self, pe: &P) -> Result<*const u8, Error> {
let rva = self.as_rva(pe)?;
rva.as_ptr(pe)
}
}
impl From<u64> for VA64 {
fn from(v: u64) -> Self {
Self(v)
}
}
impl From<VA64> for u64 {
fn from(v: VA64) -> Self {
v.0
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum VA {
VA32(VA32),
VA64(VA64),
}
impl Address for VA {
fn as_offset<P: PE>(&self, pe: &P) -> Result<Offset, Error> {
pe.va_to_offset(*self)
}
fn as_rva<P: PE>(&self, pe: &P) -> Result<RVA, Error> {
pe.va_to_rva(*self)
}
fn as_va<P: PE>(&self, _: &P) -> Result<VA, Error> {
Ok(self.clone())
}
fn as_ptr<P: PE>(&self, pe: &P) -> Result<*const u8, Error> {
let rva = self.as_rva(pe)?;
rva.as_ptr(pe)
}
}
impl From<u32> for VA {
fn from(v: u32) -> Self {
Self::VA32(v.into())
}
}
impl From<u64> for VA {
fn from(v: u64) -> Self {
Self::VA64(v.into())
}
}
#[derive(Clone, Debug)]
pub enum NTHeaders {
NTHeaders32(ImageNTHeaders32),
NTHeaders64(ImageNTHeaders64),
}
#[derive(Clone, Debug)]
pub enum NTHeadersMut {
NTHeaders32(ImageNTHeaders32),
NTHeaders64(ImageNTHeaders64),
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ThunkData {
ForwarderString(RVA),
Function(RVA),
ImportByName(RVA),
Ordinal(u32),
}
pub trait ThunkFunctions {
fn is_ordinal(&self) -> bool;
fn parse_export(&self, start: RVA, end: RVA) -> ThunkData;
fn parse_import(&self) -> ThunkData;
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Thunk32(pub u32);
impl ThunkFunctions for Thunk32 {
fn is_ordinal(&self) -> bool {
(self.0 & 0x80000000) != 0
}
fn parse_export(&self, start: RVA, end: RVA) -> ThunkData {
if self.is_ordinal() {
ThunkData::Ordinal((self.0 & 0xFFFF) as u32)
}
else {
let value = self.0 as u32;
if start.0 <= value && value < end.0 {
ThunkData::ForwarderString(RVA(value))
}
else {
ThunkData::Function(RVA(value))
}
}
}
fn parse_import(&self) -> ThunkData {
if self.is_ordinal() {
ThunkData::Ordinal((self.0 & 0xFFFF) as u32)
}
else {
ThunkData::ImportByName(RVA(self.0 as u32))
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Thunk64(pub u64);
impl ThunkFunctions for Thunk64 {
fn is_ordinal(&self) -> bool {
(self.0 & 0x8000000000000000) != 0
}
fn parse_export(&self, start: RVA, end: RVA) -> ThunkData {
if self.is_ordinal() {
ThunkData::Ordinal((self.0 & 0xFFFFFFFF) as u32)
}
else {
let value = self.0 as u32;
if start.0 <= value && value < end.0 {
ThunkData::ForwarderString(RVA(value))
}
else {
ThunkData::Function(RVA(value))
}
}
}
fn parse_import(&self) -> ThunkData {
if self.is_ordinal() {
ThunkData::Ordinal((self.0 & 0xFFFFFFFF) as u32)
}
else {
ThunkData::ImportByName(RVA(self.0 as u32))
}
}
}
pub enum Thunk {
Thunk32(Thunk32),
Thunk64(Thunk64),
}
pub enum ThunkMut {
Thunk32(Thunk32),
Thunk64(Thunk64),
}
pub type ExportDirectory = ImageExportDirectory;
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum ImportData {
Ordinal(u32),
ImportByName(String),
}
pub struct ImportDirectory<'data> {
pub descriptors: &'data [ImageImportDescriptor]
}
impl<'data> ImportDirectory<'data> {
pub fn parse_size<P: PE>(pe: &'data P) -> Result<usize, Error> {
let dir = pe.get_data_directory(ImageDirectoryEntry::Import)?;
if dir.virtual_address.0 == 0 || !pe.validate_rva(dir.virtual_address) {
return Err(Error::InvalidRVA(dir.virtual_address));
}
let mut address = pe.translate(PETranslation::Memory(dir.virtual_address))?;
let mut imports = 0usize;
loop {
match pe.read_val::<ImageImportDescriptor>(address) {
Ok(x) => { if x.original_first_thunk.0 == 0 && x.first_thunk.0 == 0 { break; } },
Err(e) => return Err(Error::from(e)),
}
imports += 1;
address += mem::size_of::<ImageImportDescriptor>();
}
Ok(imports)
}
pub fn parse<P: PE>(pe: &'data P) -> Result<Self, Error> {
let dir = pe.get_data_directory(ImageDirectoryEntry::Import)?;
if dir.virtual_address.0 == 0 || !pe.validate_rva(dir.virtual_address) {
return Err(Error::InvalidRVA(dir.virtual_address));
}
let offset = pe.translate(PETranslation::Memory(dir.virtual_address))?;
let size = Self::parse_size(pe)?;
let descriptors = pe.get_slice_ref::<ImageImportDescriptor>(offset, size)?;
Ok(Self { descriptors } )
}
pub fn get_import_map<P: PE>(&self, pe: &P) -> Result<HashMap<String, Vec<ImportData>>, Error> {
let mut results = HashMap::<String, Vec<ImportData>>::new();
for import in self.descriptors {
let name = match import.get_name(pe) {
Ok(n) => match n.as_str() {
Ok(s) => s.to_string(),
Err(e) => return Err(e),
},
Err(e) => return Err(e),
};
let imports = import.get_imports(pe)?;
results.insert(name, imports);
}
Ok(results)
}
#[cfg(feature="win32")]
pub fn resolve_iat<P: PE>(&self, pe: &mut P) -> Result<(), Error> {
for import in self.descriptors.iter() {
match import.resolve_iat(pe) {
Ok(()) => (),
Err(e) => return Err(e),
}
}
Ok(())
}
}
pub struct ImportDirectoryMut<'data> {
pub descriptors: &'data mut [ImageImportDescriptor]
}
impl<'data> ImportDirectoryMut<'data> {
pub fn parse<P: PE>(pe: &'data mut P) -> Result<Self, Error> {
let dir = pe.get_data_directory(ImageDirectoryEntry::Import)?;
if dir.virtual_address.0 == 0 || !pe.validate_rva(dir.virtual_address) {
return Err(Error::InvalidRVA(dir.virtual_address));
}
let offset = pe.translate(PETranslation::Memory(dir.virtual_address))?;
let size = ImportDirectory::parse_size(pe)?;
let descriptors = pe.get_mut_slice_ref::<ImageImportDescriptor>(offset, size)?;
Ok(Self { descriptors } )
}
pub fn get_import_map<P: PE>(&self, pe: &P) -> Result<HashMap<String, Vec<ImportData>>, Error> {
let mut results = HashMap::<String, Vec<ImportData>>::new();
for import in self.descriptors.iter() {
let name = match import.get_name(pe) {
Ok(n) => match n.as_str() {
Ok(s) => s.to_string(),
Err(e) => return Err(e),
},
Err(e) => return Err(e),
};
let imports = import.get_imports(pe)?;
results.insert(name, imports);
}
Ok(results)
}
#[cfg(feature="win32")]
pub fn resolve_iat<P: PE>(&self, pe: &mut P) -> Result<(), Error> {
for import in self.descriptors.iter() {
match import.resolve_iat(pe) {
Ok(()) => (),
Err(e) => return Err(e),
}
}
Ok(())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum RelocationValue {
Relocation16(u16),
Relocation32(u32),
Relocation64(u64),
None,
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Relocation(pub u16);
impl Relocation {
pub fn new(relocation_type: ImageRelBased, offset: u16) -> Self {
let mut result = Self(0);
result.set_type(relocation_type);
result.set_offset(offset);
result
}
pub fn get_type(&self) -> ImageRelBased {
match self.0 >> 12 {
0 => ImageRelBased::Absolute,
1 => ImageRelBased::High,
2 => ImageRelBased::Low,
3 => ImageRelBased::HighLow,
4 => ImageRelBased::HighAdj,
5 => ImageRelBased::MachineSpecific5,
6 => ImageRelBased::Reserved,
7 => ImageRelBased::MachineSpecific7,
8 => ImageRelBased::MachineSpecific8,
9 => ImageRelBased::MachineSpecific9,
10 => ImageRelBased::Dir64,
_ => ImageRelBased::Unknown,
}
}
pub fn set_type(&mut self, value: ImageRelBased) {
let enum_val = match value {
ImageRelBased::Unknown => return,
_ => value as u16,
};
self.0 = (self.0 & 0xFFF) | (enum_val << 12);
}
pub fn get_offset(&self) -> u16 {
self.0 & 0xFFF
}
pub fn set_offset(&mut self, offset: u16) {
self.0 = (self.0 & 0xF000) | (offset & 0xFFF)
}
pub fn get_address(&self, base: RVA) -> RVA {
RVA(base.0 + self.get_offset() as u32)
}
pub fn relocate<P: PE>(&self, pe: &P, base_rva: RVA, new_base: u64, next_relocation: Option<Relocation>) -> Result<RelocationValue, Error> {
let headers = pe.get_valid_nt_headers()?;
let offset = pe.translate(PETranslation::Memory(self.get_address(base_rva)))?;
let image_base = match headers {
NTHeaders::NTHeaders32(h32) => h32.optional_header.image_base as u64,
NTHeaders::NTHeaders64(h64) => h64.optional_header.image_base,
};
let delta = (new_base as i64) - (image_base as i64);
match self.get_type() {
ImageRelBased::High => {
let high = delta & 0xFFFF0000;
let current = pe.read_val::<i32>(offset)?;
Ok(RelocationValue::Relocation32(((current as i64) + high) as u32))
},
ImageRelBased::Low => {
let low = delta & 0xFFFF;
let current = pe.read_val::<i32>(offset)?;
Ok(RelocationValue::Relocation32(((current as i64) + low) as u32))
},
ImageRelBased::HighLow => {
let current = pe.read_val::<i32>(offset)?;
Ok(RelocationValue::Relocation32(((current as i64) + delta) as u32))
},
ImageRelBased::HighAdj => {
if next_relocation.is_none() {
return Err(Error::InvalidRelocation);
}
let next_entry = next_relocation.unwrap();
let next_rva = next_entry.get_address(base_rva);
let current = pe.read_val::<i16>(offset)?;
let high = delta & 0xFFFF0000;
let mut value = (current as i64) << 16;
value += next_rva.0 as i64;
value += high;
value >>= 16;
Ok(RelocationValue::Relocation16(value as u16))
},
ImageRelBased::Dir64 => {
let current = pe.read_val::<i64>(offset)?;
Ok(RelocationValue::Relocation64(((current as i128) + (delta as i128)) as u64))
},
_ => Ok(RelocationValue::None),
}
}
}
#[derive(Debug)]
pub struct RelocationEntry {
pub base_relocation: ImageBaseRelocation,
pub relocations: Vec<Relocation>
}
impl RelocationEntry {
pub fn parse<P: PE>(pe: &P, rva: RVA) -> Result<Self, Error> {
let relocation_size = mem::size_of::<ImageBaseRelocation>();
let offset = pe.translate(PETranslation::Memory(rva))?;
let base_relocation = pe.read_val::<ImageBaseRelocation>(offset)?;
let block_addr = offset + relocation_size;
let block_size = base_relocation.relocations();
let relocations = pe.read_val_array::<Relocation>(block_addr, block_size)?;
Ok(Self { base_relocation, relocations })
}
pub fn create<P: PE>(pe: &mut P, rva: RVA, base_relocation: &ImageBaseRelocation, relocations: &[Relocation]) -> Result<Self, Error> {
let mut offset = pe.translate(PETranslation::Memory(rva))?;
pe.write_val(offset, base_relocation).map_err(Error::from)?;
offset += mem::size_of::<ImageBaseRelocation>();
let reloc_bytes = unsafe { std::slice::from_raw_parts(relocations.as_ptr() as *const u8, relocations.len() * mem::size_of::<Relocation>()) };
pe.write(offset, reloc_bytes)?;
let result = Self::parse(pe, rva);
result
}
pub fn block_size(&self) -> u32 {
ImageBaseRelocation::calculate_block_size(self.relocations.len())
}
}
pub struct RelocationEntryMut {
pub base_relocation: ImageBaseRelocation,
pub relocations: Vec<Relocation>,
}
impl RelocationEntryMut {
pub fn parse<P: PE>(pe: &mut P, rva: RVA) -> Result<Self, Error> {
let relocation_size = mem::size_of::<ImageBaseRelocation>();
let offset = pe.translate(PETranslation::Memory(rva))?;
let base_relocation = pe.read_val::<ImageBaseRelocation>(offset)?;
let block_addr = offset + relocation_size;
let block_size = base_relocation.relocations();
let relocations = pe.read_val_array::<Relocation>(block_addr, block_size)?;
Ok(Self { base_relocation, relocations })
}
pub fn create<P: PE>(pe: &mut P, rva: RVA, base_relocation: &ImageBaseRelocation, relocations: &[Relocation]) -> Result<Self, Error> {
let mut offset = pe.translate(PETranslation::Memory(rva))?;
pe.write_val(offset, base_relocation).map_err(Error::from)?;
offset += mem::size_of::<ImageBaseRelocation>();
let reloc_bytes = unsafe { std::slice::from_raw_parts(relocations.as_ptr() as *const u8, relocations.len() * mem::size_of::<Relocation>()) };
pe.write(offset, reloc_bytes)?;
Self::parse(pe, rva)
}
pub fn block_size(&self) -> u32 {
ImageBaseRelocation::calculate_block_size(self.relocations.len())
}
}
pub struct RelocationDirectory {
pub entries: Vec<RelocationEntry>,
}
impl RelocationDirectory {
pub fn parse<P: PE>(pe: &P) -> Result<Self, Error> {
let dir = pe.get_data_directory(ImageDirectoryEntry::BaseReloc)?;
if dir.virtual_address.0 == 0 || !pe.validate_rva(dir.virtual_address) {
return Err(Error::InvalidRVA(dir.virtual_address));
}
let mut start_addr = dir.virtual_address.clone();
let end_addr = RVA(start_addr.0 + dir.size);
if !pe.validate_rva(end_addr) {
return Err(Error::InvalidRVA(end_addr));
}
let mut entries = Vec::<RelocationEntry>::new();
while start_addr.0 < end_addr.0 {
let entry = RelocationEntry::parse(pe, start_addr)?;
let size = entry.block_size();
entries.push(entry);
start_addr.0 += size as u32;
}
Ok(Self { entries })
}
pub fn relocations<P: PE>(&self, pe: &P, new_base: u64) -> Result<Vec<(RVA, RelocationValue)>, Error> {
let mut result = Vec::<(RVA, RelocationValue)>::new();
for entry in &self.entries {
let base_rva = entry.base_relocation.virtual_address;
let len = entry.relocations.len();
for i in 0..len {
let current = entry.relocations[i];
let mut next: Option<Relocation> = None;
if (i+1) < len {
next = Some(entry.relocations[i+1]);
}
let value = match current.relocate(pe, base_rva, new_base, next) {
Ok(v) => v,
Err(e) => return Err(e),
};
result.push((current.get_address(base_rva), value));
}
}
Ok(result)
}
pub fn relocate<P: PE>(&self, pe: &mut P, new_base: u64) -> Result<(), Error> {
let relocations = self.relocations(pe, new_base)?;
let ptr = pe.as_mut_ptr();
for (rva, value) in relocations {
let offset = pe.translate(PETranslation::Memory(rva))?;
let offset_ptr = unsafe { ptr.add(offset) };
if !pe.validate_ptr(offset_ptr) {
return Err(Error::BadPointer(offset_ptr));
}
unsafe {
match value {
RelocationValue::Relocation16(r16) => *(offset_ptr as *mut u16) = r16,
RelocationValue::Relocation32(r32) => *(offset_ptr as *mut u32) = r32,
RelocationValue::Relocation64(r64) => *(offset_ptr as *mut u64) = r64,
RelocationValue::None => (),
}
}
}
Ok(())
}
pub fn add_relocation<P: PE>(&mut self, pe: &mut P, rva: RVA) -> Result<(), Error> {
let dir = pe.get_data_directory(ImageDirectoryEntry::BaseReloc)?;
if dir.virtual_address.0 == 0 || !pe.validate_rva(dir.virtual_address) {
return Err(Error::InvalidRVA(dir.virtual_address));
}
let mut owned_data = self.entries
.iter()
.map(|x| (x.base_relocation.clone(), x.relocations.clone()))
.collect::<Vec::<(ImageBaseRelocation, Vec<Relocation>)>>();
let reloc_address = RVA(rva.0 & 0xFFFFF000);
let relocation = match pe.get_arch() {
Ok(a) => match a {
Arch::X86 => Relocation::new(ImageRelBased::HighLow, (rva.0 & 0xFFF) as u16),
Arch::X64 => Relocation::new(ImageRelBased::Dir64, (rva.0 & 0xFFF) as u16),
},
Err(e) => return Err(e),
};
let mut found_entry = false;
for reloc_pair in &mut owned_data {
if reloc_pair.0.virtual_address != reloc_address { continue; }
reloc_pair.1.push(relocation);
reloc_pair.0.size_of_block = ImageBaseRelocation::calculate_block_size(reloc_pair.1.len());
found_entry = true;
break;
}
if !found_entry {
owned_data.push((ImageBaseRelocation { virtual_address: reloc_address, size_of_block: ImageBaseRelocation::calculate_block_size(1) },
vec![relocation]));
}
owned_data.sort_by(|a,b| a.0.virtual_address.0.cmp(&b.0.virtual_address.0));
let base_addr = dir.virtual_address.clone();
let dir_size = dir.size;
let base_offset = pe.translate(PETranslation::Memory(base_addr))?;
pe.write(base_offset, &vec![0u8; dir_size as usize])?;
let mut write_addr = base_addr.clone();
let mut new_relocations = Vec::<RelocationEntry>::new();
for (base_reloc, relocations) in owned_data {
let new_relocation = RelocationEntry::create(pe, write_addr, &base_reloc, relocations.as_slice())?;
write_addr.0 += base_reloc.size_of_block;
new_relocations.push(new_relocation);
}
let new_size = write_addr.0 - base_addr.0;
self.entries = new_relocations;
let mut mut_dir = pe.get_mut_data_directory(ImageDirectoryEntry::BaseReloc)?;
mut_dir.size = new_size;
Ok(())
}
}
pub struct RelocationDirectoryMut {
pub entries: Vec<RelocationEntry>,
}
impl RelocationDirectoryMut {
pub fn parse<P: PE>(pe: &mut P) -> Result<Self, Error> {
let dir = pe.get_data_directory(ImageDirectoryEntry::BaseReloc)?;
if dir.virtual_address.0 == 0 || !pe.validate_rva(dir.virtual_address) {
return Err(Error::InvalidRVA(dir.virtual_address));
}
let start_addr = dir.virtual_address.clone();
let end_addr = RVA(start_addr.0 + dir.size);
if !pe.validate_rva(end_addr) {
return Err(Error::InvalidRVA(end_addr));
}
let _start_offset = pe.translate(PETranslation::Memory(start_addr))?;
let _end_offset = pe.translate(PETranslation::Memory(end_addr))?;
let mut entries = Vec::<RelocationEntry>::new();
let mut current_addr = start_addr;
while current_addr.0 < end_addr.0 {
let entry = RelocationEntry::parse(pe, current_addr)?;
let size = entry.block_size();
entries.push(entry);
current_addr.0 += size as u32;
}
Ok(Self { entries })
}
pub fn relocations<P: PE>(&self, pe: &P, new_base: u64) -> Result<Vec<(RVA, RelocationValue)>, Error> {
let mut result = Vec::<(RVA, RelocationValue)>::new();
for entry in &self.entries {
let base_rva = entry.base_relocation.virtual_address;
let len = entry.relocations.len();
for i in 0..len {
let current = entry.relocations[i];
let mut next: Option<Relocation> = None;
if (i+1) < len {
next = Some(entry.relocations[i+1]);
}
let value = match current.relocate(pe, base_rva, new_base, next) {
Ok(v) => v,
Err(e) => return Err(e),
};
result.push((current.get_address(base_rva), value));
}
}
Ok(result)
}
pub fn relocate<P: PE>(&self, pe: &mut P, new_base: u64) -> Result<(), Error> {
let relocations = self.relocations(pe, new_base)?;
let ptr = pe.as_mut_ptr();
for (rva, value) in relocations {
let offset = pe.translate(PETranslation::Memory(rva))?;
let offset_ptr = unsafe { ptr.add(offset) };
if !pe.validate_ptr(offset_ptr) {
return Err(Error::BadPointer(offset_ptr));
}
unsafe {
match value {
RelocationValue::Relocation16(r16) => *(offset_ptr as *mut u16) = r16,
RelocationValue::Relocation32(r32) => *(offset_ptr as *mut u32) = r32,
RelocationValue::Relocation64(r64) => *(offset_ptr as *mut u64) = r64,
RelocationValue::None => (),
}
}
}
Ok(())
}
pub fn add_relocation<P: PE>(&mut self, pe: &mut P, rva: RVA) -> Result<(), Error> {
let dir = pe.get_data_directory(ImageDirectoryEntry::BaseReloc)?;
if dir.virtual_address.0 == 0 || !pe.validate_rva(dir.virtual_address) {
return Err(Error::InvalidRVA(dir.virtual_address));
}
let mut owned_data = self.entries
.iter()
.map(|x| (x.base_relocation.clone(), x.relocations.clone()))
.collect::<Vec::<(ImageBaseRelocation, Vec<Relocation>)>>();
let reloc_address = RVA(rva.0 & 0xFFFFF000);
let relocation = match pe.get_arch() {
Ok(a) => match a {
Arch::X86 => Relocation::new(ImageRelBased::HighLow, (rva.0 & 0xFFF) as u16),
Arch::X64 => Relocation::new(ImageRelBased::Dir64, (rva.0 & 0xFFF) as u16),
},
Err(e) => return Err(e),
};
let mut found_entry = false;
for reloc_pair in &mut owned_data {
if reloc_pair.0.virtual_address != reloc_address { continue; }
reloc_pair.1.push(relocation);
reloc_pair.0.size_of_block = ImageBaseRelocation::calculate_block_size(reloc_pair.1.len());
found_entry = true;
break;
}
if !found_entry {
owned_data.push((ImageBaseRelocation { virtual_address: reloc_address, size_of_block: ImageBaseRelocation::calculate_block_size(1) },
vec![relocation]));
}
owned_data.sort_by(|a,b| a.0.virtual_address.0.cmp(&b.0.virtual_address.0));
let base_addr = dir.virtual_address.clone();
let dir_size = dir.size;
let base_offset = pe.translate(PETranslation::Memory(base_addr))?;
pe.write(base_offset, &vec![0u8; dir_size as usize])?;
let mut write_addr = base_addr.clone();
let mut new_relocations = Vec::<RelocationEntry>::new();
for (base_reloc, relocations) in owned_data {
let new_relocation = RelocationEntry::create(pe, write_addr, &base_reloc, relocations.as_slice())?;
write_addr.0 += base_reloc.size_of_block;
new_relocations.push(new_relocation);
}
let new_size = write_addr.0 - base_addr.0;
self.entries = new_relocations;
let mut mut_dir = pe.get_mut_data_directory(ImageDirectoryEntry::BaseReloc)?;
mut_dir.size = new_size;
Ok(())
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct FlaggedDword(pub u32);
impl FlaggedDword {
pub fn get_flag(&self) -> bool {
(self.0 & 0x80000000) > 0
}
pub fn get_dword(&self) -> u32 {
if self.get_flag() {
self.0 & 0x7FFFFFFF
}
else {
self.0
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct ResourceOffset(pub u32);
impl ResourceOffset {
pub fn resolve<P: PE>(&self, pe: &P) -> Result<RVA, Error> {
pe.get_resource_address(*self)
}
}
impl Address for ResourceOffset {
fn as_offset<P: PE>(&self, pe: &P) -> Result<Offset, Error> {
let rva = self.resolve(pe)?;
rva.as_offset(pe)
}
fn as_rva<P: PE>(&self, pe: &P) -> Result<RVA, Error> {
self.resolve(pe)
}
fn as_va<P: PE>(&self, pe: &P) -> Result<VA, Error> {
let rva = self.resolve(pe)?;
rva.as_va(pe)
}
fn as_ptr<P: PE>(&self, pe: &P) -> Result<*const u8, Error> {
let offset = self.as_offset(pe)?;
offset.as_ptr(pe)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ResourceID {
Cursor = 1,
Bitmap = 2,
Icon = 3,
Menu = 4,
Dialog = 5,
String = 6,
FontDir = 7,
Font = 8,
Accelerator = 9,
RCData = 10,
MessageTable = 11,
GroupCursor = 12,
Reserved = 13,
GroupIcon = 14,
Reserved2 = 15,
Version = 16,
DlgInclude = 17,
Reserved3 = 18,
PlugPlay = 19,
VXD = 20,
AniCursor = 21,
AniIcon = 22,
HTML = 23,
Manifest = 24,
Unknown,
}
impl ResourceID {
pub fn from_u32(u: u32) -> Self {
match u {
1 => Self::Cursor,
2 => Self::Bitmap,
3 => Self::Icon,
4 => Self::Menu,
5 => Self::Dialog,
6 => Self::String,
7 => Self::FontDir,
8 => Self::Font,
9 => Self::Accelerator,
10 => Self::RCData,
11 => Self::MessageTable,
12 => Self::GroupCursor,
13 => Self::Reserved,
14 => Self::GroupIcon,
15 => Self::Reserved2,
16 => Self::Version,
17 => Self::DlgInclude,
18 => Self::Reserved3,
19 => Self::PlugPlay,
20 => Self::VXD,
21 => Self::AniCursor,
22 => Self::AniIcon,
23 => Self::HTML,
24 => Self::Manifest,
_ => Self::Unknown
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ResourceDirectoryID {
ID(u32),
Name(ResourceOffset),
}
impl ResourceDirectoryID {
fn resolve<P: PE>(&self, pe: &P) -> Result<ResolvedDirectoryID, Error> {
match *self {
ResourceDirectoryID::ID(id) => Ok(ResolvedDirectoryID::ID(id)),
ResourceDirectoryID::Name(offset) => {
let resolved = offset.resolve(pe)?;
let dir_string = ImageResourceDirStringU::parse(pe, resolved)?;
let string_data = dir_string.name.as_u16_str()?;
Ok(ResolvedDirectoryID::Name(string_data.to_string()))
},
}
}
}
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub enum ResolvedDirectoryID {
ID(u32),
Name(String),
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ResourceDirectoryData {
Directory(ResourceOffset),
Data(ResourceOffset),
}
impl ResourceDirectoryData {
pub fn resolve<P: PE>(&self, pe: &P) -> Result<ResolvedDirectoryData, Error> {
match *self {
ResourceDirectoryData::Directory(dir_offset) => {
let result = ResourceNode::parse(pe, dir_offset)?;
Ok(ResolvedDirectoryData::Directory(result))
},
ResourceDirectoryData::Data(data_offset) => {
let resolved = data_offset.resolve(pe)?;
let offset = pe.translate(PETranslation::Memory(resolved))?;
let result = pe.read_val::<ImageResourceDataEntry>(offset)?;
Ok(ResolvedDirectoryData::Data(result))
},
}
}
pub fn resolve_mut<P: PE>(&self, pe: &mut P) -> Result<ResolvedDirectoryDataMut, Error> {
match *self {
ResourceDirectoryData::Directory(dir_offset) => {
let result = ResourceNodeMut::parse(pe, dir_offset)?;
Ok(ResolvedDirectoryDataMut::Directory(result))
},
ResourceDirectoryData::Data(data_offset) => {
let resolved = data_offset.resolve(pe)?;
let offset = pe.translate(PETranslation::Memory(resolved))?;
let result = pe.read_val::<ImageResourceDataEntry>(offset)?;
Ok(ResolvedDirectoryDataMut::Data(result))
},
}
}
}
#[derive(Clone)]
pub enum ResolvedDirectoryData {
Directory(ResourceNode),
Data(ImageResourceDataEntry),
}
pub enum ResolvedDirectoryDataMut {
Directory(ResourceNodeMut),
Data(ImageResourceDataEntry),
}
#[derive(Clone)]
pub struct ResourceNode {
pub directory: ImageResourceDirectory,
pub entries: Vec<ImageResourceDirectoryEntry>,
}
impl ResourceNode {
pub fn parse<P: PE>(pe: &P, offset: ResourceOffset) -> Result<Self, Error> {
let resolved_offset = offset.resolve(pe)?;
let mut image_offset = pe.translate(PETranslation::Memory(resolved_offset))?;
let directory = pe.read_val::<ImageResourceDirectory>(image_offset)?;
image_offset += mem::size_of::<ImageResourceDirectory>();
let entries_count = directory.entries();
let entries = pe.read_val_array::<ImageResourceDirectoryEntry>(image_offset, entries_count)?;
Ok(Self { directory, entries })
}
pub fn entry_by_id<P: PE>(&self, pe: &P, id: &ResolvedDirectoryID) -> Result<ResourceDirectoryData, Error> {
for entry in &self.entries {
let resolved_id = entry.get_id().resolve(pe)?;
if resolved_id != *id { continue; }
return Ok(entry.get_data());
}
Err(Error::ResourceNotFound)
}
pub fn entry_by_offset(&self, offset: usize) -> Result<ResourceDirectoryData, Error> {
if offset >= self.entries.len() { return Err(Error::OutOfBounds(self.entries.len(), offset)); }
Ok(self.entries[offset].get_data())
}
}
pub struct ResourceNodeMut {
pub directory: ImageResourceDirectory,
pub entries: Vec<ImageResourceDirectoryEntry>,
}
impl ResourceNodeMut {
pub fn parse<P: PE>(pe: &mut P, offset: ResourceOffset) -> Result<Self, Error> {
let resolved_offset = offset.resolve(pe)?;
let mut image_offset = pe.translate(PETranslation::Memory(resolved_offset))?;
let directory = pe.read_val::<ImageResourceDirectory>(image_offset)?;
image_offset += mem::size_of::<ImageResourceDirectory>();
let entries_count = directory.entries();
let entries = pe.read_val_array::<ImageResourceDirectoryEntry>(image_offset, entries_count)?;
Ok(Self { directory, entries })
}
pub fn entry_by_id<P: PE>(&self, pe: &P, id: &ResolvedDirectoryID) -> Result<ResourceDirectoryData, Error> {
for entry in &self.entries {
let resolved_id = entry.get_id().resolve(pe)?;
if resolved_id != *id { continue; }
return Ok(entry.get_data());
}
Err(Error::ResourceNotFound)
}
pub fn entry_by_offset(&self, offset: usize) -> Result<ResourceDirectoryData, Error> {
if offset >= self.entries.len() { return Err(Error::OutOfBounds(self.entries.len(), offset)); }
Ok(self.entries[offset].get_data())
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct FlattenedResourceDataEntry {
pub type_id: ResolvedDirectoryID,
pub rsrc_id: ResolvedDirectoryID,
pub lang_id: ResolvedDirectoryID,
pub data: ResourceOffset,
}
impl FlattenedResourceDataEntry {
pub fn get_data_entry<P: PE>(&self, pe: &P) -> Result<ImageResourceDataEntry, Error> {
let rva = self.data.resolve(pe)?;
let offset = pe.translate(PETranslation::Memory(rva))?;
let result = pe.read_val::<ImageResourceDataEntry>(offset)?;
Ok(result)
}
pub fn get_mut_data_entry<P: PE>(&self, pe: &mut P) -> Result<ImageResourceDataEntry, Error> {
let rva = self.data.resolve(pe)?;
let offset = pe.translate(PETranslation::Memory(rva))?;
let result = pe.read_val::<ImageResourceDataEntry>(offset)?;
Ok(result)
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct IconDirEntry {
pub width: u8,
pub height: u8,
pub color_count: u8,
pub reserved: u8,
pub planes: u16,
pub bit_count: u16,
pub bytes_in_res: u32,
pub image_offset: u32,
}
#[derive(Clone)]
pub struct IconDir {
pub reserved: u16,
pub icon_type: u16,
pub count: u16,
pub entries: Vec<IconDirEntry>,
}
impl IconDir {
pub fn parse<B: Buffer>(buf: &B) -> Result<Self, Error> {
let reserved = buf.read_val::<u16>(0).map_err(Error::from)?;
let icon_type = buf.read_val::<u16>(2).map_err(Error::from)?;
let count = buf.read_val::<u16>(4).map_err(Error::from)?;
let entries = buf.read_val_array::<IconDirEntry>(6, count as usize).map_err(Error::from)?;
Ok(Self { reserved, icon_type, count, entries })
}
pub fn to_vec_buffer(&self) -> Result<VecBuffer, Error> {
let mut result = VecBuffer::new();
result.append_val::<u16>(&self.reserved);
result.append_val::<u16>(&self.icon_type);
result.append_val::<u16>(&self.count);
result.append_slice_val::<IconDirEntry>(self.entries.as_slice());
Ok(result)
}
}
pub struct IconDirMut {
pub reserved: u16,
pub icon_type: u16,
pub count: u16,
pub entries: Vec<IconDirEntry>,
}
impl IconDirMut {
pub fn parse<B: Buffer>(buf: &mut B) -> Result<Self, Error> {
let reserved = buf.read_val::<u16>(0).map_err(Error::from)?;
let icon_type = buf.read_val::<u16>(2).map_err(Error::from)?;
let count = buf.read_val::<u16>(4).map_err(Error::from)?;
let entries = buf.read_val_array::<IconDirEntry>(6, count as usize).map_err(Error::from)?;
Ok(Self { reserved, icon_type, count, entries })
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct IconDirVec {
pub reserved: u16,
pub icon_type: u16,
pub count: u16,
pub entries: Vec<IconDirEntry>
}
impl IconDirVec {
pub fn to_vec_buffer(&self) -> Result<VecBuffer, Error> {
let mut result = VecBuffer::new();
result.append_val::<u16>(&self.reserved);
result.append_val::<u16>(&self.icon_type);
result.append_val::<u16>(&self.count);
result.append_slice_val::<IconDirEntry>(self.entries.as_slice());
Ok(result)
}
}
pub struct ResourceDirectory {
pub root_node: ResourceNode,
pub resources: Vec<FlattenedResourceDataEntry>,
}
impl ResourceDirectory {
pub fn parse<P: PE>(pe: &P) -> Result<Self, Error> {
let mut resources = Vec::<FlattenedResourceDataEntry>::new();
let root_node = ResourceNode::parse(pe, ResourceOffset(0))?;
for type_entry in &root_node.entries {
let id_offset = match type_entry.get_data() {
ResourceDirectoryData::Data(_) => return Err(Error::CorruptDataDirectory),
ResourceDirectoryData::Directory(d) => d,
};
let id_node = ResourceNode::parse(pe, id_offset)?;
for id_entry in &id_node.entries {
let lang_offset = match id_entry.get_data() {
ResourceDirectoryData::Data(_) => return Err(Error::CorruptDataDirectory),
ResourceDirectoryData::Directory(d) => d,
};
let lang_node = ResourceNode::parse(pe, lang_offset)?;
for lang_entry in &lang_node.entries {
let data_offset = match lang_entry.get_data() {
ResourceDirectoryData::Directory(_) => return Err(Error::CorruptDataDirectory),
ResourceDirectoryData::Data(d) => d,
};
let type_resolved = type_entry.get_id().resolve(pe)?;
let rsrc_resolved = id_entry.get_id().resolve(pe)?;
let lang_resolved = lang_entry.get_id().resolve(pe)?;
resources.push(FlattenedResourceDataEntry {
type_id: type_resolved,
rsrc_id: rsrc_resolved,
lang_id: lang_resolved,
data: data_offset,
});
}
}
}
Ok(Self { root_node, resources })
}
pub fn filter(
&self,
type_id: Option<ResolvedDirectoryID>,
rsrc_id: Option<ResolvedDirectoryID>,
lang_id: Option<ResolvedDirectoryID>
) -> Vec<FlattenedResourceDataEntry> {
let mut resources = self.resources.clone();
if type_id.is_some() {
let type_search = type_id.unwrap();
resources = resources.iter()
.filter(|x| x.type_id == type_search)
.cloned()
.collect();
}
if rsrc_id.is_some() {
let rsrc_search = rsrc_id.unwrap();
resources = resources.iter()
.filter(|x| x.rsrc_id == rsrc_search)
.cloned()
.collect();
}
if lang_id.is_some() {
let lang_search = lang_id.unwrap();
resources = resources.iter()
.filter(|x| x.lang_id == lang_search)
.cloned()
.collect();
}
resources
}
pub fn icon_groups<P: PE>(&self, pe: &P) -> Result<HashMap<ResolvedDirectoryID, GrpIconDir>, Error> {
let groups = self.filter(Some(ResolvedDirectoryID::ID(ResourceID::GroupIcon as u32)), None, None);
let mut result = HashMap::<ResolvedDirectoryID, GrpIconDir>::new();
for rsrc in &groups {
let id = rsrc.rsrc_id.clone();
let entry = rsrc.get_data_entry(pe)?;
let dir = GrpIconDir::parse(pe, entry.offset_to_data)?;
result.insert(id, dir);
}
Ok(result)
}
}
pub struct ResourceDirectoryMut {
pub root_node: ResourceNodeMut,
pub resources: Vec<FlattenedResourceDataEntry>,
}
impl ResourceDirectoryMut {
pub fn parse<P: PE>(pe: &mut P) -> Result<Self, Error> {
let mut resources = Vec::<FlattenedResourceDataEntry>::new();
let dir_size = match pe.get_data_directory(ImageDirectoryEntry::Resource) {
Ok(d) => d.size,
Err(e) => return Err(e),
};
let _rva = ResourceOffset(0).resolve(pe)?;
let root_node = ResourceNodeMut::parse(pe, ResourceOffset(0))?;
for type_entry in &root_node.entries {
let id_offset = match type_entry.get_data() {
ResourceDirectoryData::Data(_) => return Err(Error::CorruptDataDirectory),
ResourceDirectoryData::Directory(d) => d,
};
if id_offset.0 > dir_size {
return Err(Error::OutOfBounds(dir_size as usize, id_offset.0 as usize));
}
let id_node = ResourceNodeMut::parse(pe, id_offset)?;
for id_entry in &id_node.entries {
let lang_offset = match id_entry.get_data() {
ResourceDirectoryData::Data(_) => return Err(Error::CorruptDataDirectory),
ResourceDirectoryData::Directory(d) => d,
};
if lang_offset.0 > dir_size {
return Err(Error::OutOfBounds(dir_size as usize, lang_offset.0 as usize));
}
let lang_node = ResourceNodeMut::parse(pe, lang_offset)?;
for lang_entry in &lang_node.entries {
let data_offset = match lang_entry.get_data() {
ResourceDirectoryData::Directory(_) => return Err(Error::CorruptDataDirectory),
ResourceDirectoryData::Data(d) => d,
};
let type_resolved = type_entry.get_id().resolve(pe)?;
let rsrc_resolved = id_entry.get_id().resolve(pe)?;
let lang_resolved = lang_entry.get_id().resolve(pe)?;
resources.push(FlattenedResourceDataEntry {
type_id: type_resolved,
rsrc_id: rsrc_resolved,
lang_id: lang_resolved,
data: data_offset,
});
}
}
}
Ok(Self { root_node, resources })
}
pub fn filter(
&self,
type_id: Option<ResolvedDirectoryID>,
rsrc_id: Option<ResolvedDirectoryID>,
lang_id: Option<ResolvedDirectoryID>
) -> Vec<FlattenedResourceDataEntry> {
let mut resources = self.resources.clone();
if type_id.is_some() {
let type_search = type_id.unwrap();
resources = resources.iter()
.filter(|x| x.type_id == type_search)
.cloned()
.collect();
}
if rsrc_id.is_some() {
let rsrc_search = rsrc_id.unwrap();
resources = resources.iter()
.filter(|x| x.rsrc_id == rsrc_search)
.cloned()
.collect();
}
if lang_id.is_some() {
let lang_search = lang_id.unwrap();
resources = resources.iter()
.filter(|x| x.lang_id == lang_search)
.cloned()
.collect();
}
resources
}
pub fn icon_groups<P: PE>(&self, pe: &P) -> Result<HashMap<ResolvedDirectoryID, GrpIconDir>, Error> {
let groups = self.filter(Some(ResolvedDirectoryID::ID(ResourceID::GroupIcon as u32)), None, None);
let mut result = HashMap::<ResolvedDirectoryID, GrpIconDir>::new();
for rsrc in &groups {
let id = rsrc.rsrc_id.clone();
let entry = rsrc.get_data_entry(pe)?;
let dir = GrpIconDir::parse(pe, entry.offset_to_data)?;
result.insert(id, dir);
}
Ok(result)
}
}
pub type DebugDirectory = ImageDebugDirectory;
pub enum TLSDirectory {
TLS32(ImageTLSDirectory32),
TLS64(ImageTLSDirectory64),
}
impl TLSDirectory {
pub fn parse<P: PE>(pe: &P) -> Result<Self, Error> {
let arch = pe.get_arch()?;
match arch {
Arch::X86 => match ImageTLSDirectory32::parse(pe) {
Ok(tls32) => Ok(TLSDirectory::TLS32(tls32)),
Err(e) => return Err(e),
},
Arch::X64 => match ImageTLSDirectory64::parse(pe) {
Ok(tls64) => Ok(TLSDirectory::TLS64(tls64)),
Err(e) => return Err(e),
},
}
}
}
pub enum TLSDirectoryMut {
TLS32(ImageTLSDirectory32),
TLS64(ImageTLSDirectory64),
}
impl TLSDirectoryMut {
pub fn parse<P: PE>(pe: &mut P) -> Result<Self, Error> {
let arch = pe.get_arch()?;
match arch {
Arch::X86 => match ImageTLSDirectory32::parse_mut(pe) {
Ok(tls32) => Ok(TLSDirectoryMut::TLS32(tls32)),
Err(e) => return Err(e),
},
Arch::X64 => match ImageTLSDirectory64::parse_mut(pe) {
Ok(tls64) => Ok(TLSDirectoryMut::TLS64(tls64)),
Err(e) => return Err(e),
},
}
}
}
bitflags! {
#[repr(C)]
pub struct VSFileFlags: u32 {
const DEBUG = 0x00000001;
const PRERELEASE = 0x00000002;
const PATCHED = 0x00000004;
const PRIVATEBUILD = 0x00000008;
const INFOINFERRED = 0x00000010;
const SPECIALBUILD = 0x00000020;
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum VSFileOS {
Unknown = 0x00000000,
Windows16 = 0x00000001,
PM16 = 0x00000002,
PM32 = 0x00000003,
Windows32 = 0x00000004,
DOS = 0x00010000,
DOSWindows16 = 0x00010001,
DOSWindows32 = 0x00010004,
OS216 = 0x00020000,
OS216PM16 = 0x00020002,
OS232 = 0x00030000,
OS232PM32 = 0x00030003,
NT = 0x00040000,
NTWindows32 = 0x00040004,
}
impl VSFileOS {
pub fn from_u32(u: u32) -> Self {
match u {
0x00000001 => Self::Windows16,
0x00000002 => Self::PM16,
0x00000003 => Self::PM32,
0x00000004 => Self::Windows32,
0x00010000 => Self::DOS,
0x00010001 => Self::DOSWindows16,
0x00010004 => Self::DOSWindows32,
0x00020000 => Self::OS216,
0x00020002 => Self::OS216PM16,
0x00030000 => Self::OS232,
0x00030003 => Self::OS232PM32,
0x00040000 => Self::NT,
0x00040004 => Self::NTWindows32,
_ => Self::Unknown,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum VSFileType {
Unknown = 0x00000000,
App = 0x00000001,
DLL = 0x00000002,
Drv = 0x00000003,
Font = 0x00000004,
VXD = 0x00000005,
StaticLib = 0x00000007,
}
impl VSFileType {
pub fn from_u32(u: u32) -> Self {
match u {
0x00000001 => Self::App,
0x00000002 => Self::DLL,
0x00000003 => Self::Drv,
0x00000004 => Self::Font,
0x00000005 => Self::VXD,
0x00000007 => Self::StaticLib,
_ => Self::Unknown,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum VSFileSubtypeDrv {
Unknown = 0x00000000,
Printer = 0x00000001,
Keyboard = 0x00000002,
Language = 0x00000003,
Display = 0x00000004,
Mouse = 0x00000005,
Network = 0x00000006,
System = 0x00000007,
Installable = 0x00000008,
Sound = 0x00000009,
Comm = 0x0000000A,
VersionedPrinter = 0x0000000C,
}
impl VSFileSubtypeDrv {
pub fn from_u32(u: u32) -> Self {
match u {
0x00000001 => Self::Printer,
0x00000002 => Self::Keyboard,
0x00000003 => Self::Language,
0x00000004 => Self::Display,
0x00000005 => Self::Mouse,
0x00000006 => Self::Network,
0x00000007 => Self::System,
0x00000008 => Self::Installable,
0x00000009 => Self::Sound,
0x0000000A => Self::Comm,
0x0000000C => Self::VersionedPrinter,
_ => Self::Unknown,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum VSFileSubtypeFont {
Unknown = 0x00000000,
Raster = 0x00000001,
Vector = 0x00000002,
TrueType = 0x00000003,
}
impl VSFileSubtypeFont {
pub fn from_u32(u: u32) -> Self {
match u {
0x00000001 => Self::Raster,
0x00000002 => Self::Vector,
0x00000003 => Self::TrueType,
_ => Self::Unknown,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct VSFixedFileInfo {
pub signature: u32,
pub struct_version: u32,
pub file_version_ms: u32,
pub file_version_ls: u32,
pub product_version_ms: u32,
pub product_version_ls: u32,
pub file_flags_mask: u32,
pub file_flags: VSFileFlags,
pub file_os: u32,
pub file_type: u32,
pub file_subtype: u32,
pub file_date_ms: u32,
pub file_date_ls: u32,
}
pub struct VSHeader {
pub length: u16,
pub value_length: u16,
pub type_: u16,
pub key: Vec<WChar>,
}
impl VSHeader {
pub fn parse<P: PE>(pe: &P, rva: RVA) -> Result<(usize, Self), Error> {
let mut consumed = 0usize;
let mut offset = pe.translate(PETranslation::Memory(rva))?;
let length = pe.read_val::<u16>(offset)?;
consumed += mem::size_of::<u16>();
offset += mem::size_of::<u16>();
if consumed > length as usize { return Err(Error::CorruptDataDirectory); }
let value_length = pe.read_val::<u16>(offset)?;
consumed += mem::size_of::<u16>();
offset += mem::size_of::<u16>();
if consumed > length as usize { return Err(Error::CorruptDataDirectory); }
let type_value = pe.read_val::<u16>(offset)?;
consumed += mem::size_of::<u16>();
offset += mem::size_of::<u16>();
if consumed > length as usize { return Err(Error::CorruptDataDirectory); }
let key = pe.get_widestring(offset, None)?;
let key_size = key.len() * mem::size_of::<WChar>();
consumed += key_size;
offset += key_size;
if consumed > length as usize { return Err(Error::CorruptDataDirectory); }
Ok((offset, VSHeader {
length,
value_length,
type_: type_value,
key: key.to_vec(),
}))
}
}
pub struct VSString {
pub header: VSHeader,
pub value: Vec<WChar>,
}
impl VSString {
pub fn parse<P: PE>(pe: &P, rva: RVA) -> Result<Self, Error> {
let base_offset = pe.translate(PETranslation::Memory(rva))?;
let (mut offset, header) = VSHeader::parse(pe, rva)?;
let mut consumed = offset - base_offset;
offset = align(offset, 4);
let value = pe.get_widestring(offset.into(), None)?;
let value_size = value.len() * mem::size_of::<WChar>();
consumed += value_size;
if consumed > header.length as usize { return Err(Error::CorruptDataDirectory); }
Ok(Self {
header,
value: value.to_vec(),
})
}
}
pub struct VSStringTable {
pub header: VSHeader,
pub children: Vec<VSString>,
}
impl VSStringTable {
pub fn parse<P: PE>(pe: &P, rva: RVA) -> Result<Self, Error> {
let base_offset = pe.translate(PETranslation::Memory(rva))?;
let (mut offset, header) = VSHeader::parse(pe, rva)?;
let mut consumed = offset - base_offset;
offset = align(offset, 4);
let mut children = Vec::<VSString>::new();
while consumed < (header.length as usize) {
let rva = match pe.get_type() {
PEType::Disk => Offset(offset as u32).as_rva(pe)?,
PEType::Memory => RVA(offset as u32),
};
let child = VSString::parse(pe, rva)?;
offset += child.header.length as usize;
offset = align(offset, 4);
consumed = offset - base_offset;
children.push(child);
}
Ok(Self {
header,
children,
})
}
pub fn key_as_u32(&self) -> Result<u32, Error> {
let key_str = self.header.key.as_u16_str()?;
let result = u32::from_str_radix(key_str.as_ustr().to_string_lossy().as_str(), 16);
if result.is_err() { Err(Error::ParseIntError(result.unwrap_err())) }
else { Ok(result.unwrap()) }
}
pub fn get_code_page(&self) -> Result<u16, Error> {
let key_val = match self.key_as_u32() {
Ok(k) => k,
Err(e) => return Err(e),
};
Ok((key_val & 0xFFFF) as u16)
}
pub fn get_lang_id(&self) -> Result<u16, Error> {
let key_val = match self.key_as_u32() {
Ok(k) => k,
Err(e) => return Err(e),
};
Ok((key_val >> 16) as u16)
}
pub fn string_map(&self) -> Result<HashMap<String, String>, Error> {
let mut result = HashMap::<String, String>::new();
for entry in &self.children {
let entry_key = entry.header.key.as_u16_str()?;
let entry_value = entry.value.as_u16_str()?;
result.insert(entry_key.as_ustr().to_string_lossy(), entry_value.as_ustr().to_string_lossy());
}
Ok(result)
}
}
pub struct VSStringFileInfo {
pub header: VSHeader,
pub children: Vec<VSStringTable>,
}
impl VSStringFileInfo {
pub fn parse<P: PE>(pe: &P, rva: RVA) -> Result<Self, Error> {
let base_offset = pe.translate(PETranslation::Memory(rva))?;
let (mut offset, header) = VSHeader::parse(pe, rva)?;
let mut consumed = offset - base_offset;
offset = align(offset, 4);
let mut children = Vec::<VSStringTable>::new();
while consumed < (header.length as usize) {
let rva = match pe.get_type() {
PEType::Disk => Offset(offset as u32).as_rva(pe)?,
PEType::Memory => RVA(offset as u32),
};
let child = VSStringTable::parse(pe, rva)?;
offset += child.header.length as usize;
offset = align(offset, 4);
consumed = offset - base_offset;
children.push(child);
}
Ok(Self {
header,
children,
})
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct VarDword {
lang_id: u16,
codepage: u16,
}
pub struct VSVar {
pub header: VSHeader,
pub children: Vec<VarDword>,
}
impl VSVar {
pub fn parse<P: PE>(pe: &P, rva: RVA) -> Result<Self, Error> {
let base_offset = pe.translate(PETranslation::Memory(rva))?;
let (mut offset, header) = VSHeader::parse(pe, rva)?;
let mut consumed = offset;
offset = align(offset, 4);
let mut children = Vec::<VarDword>::new();
while consumed < (header.length as usize) {
let child = pe.read_val::<VarDword>(offset.into())?;
offset += mem::size_of::<VarDword>();
offset = align(offset, 4);
consumed = offset - base_offset;
children.push(child);
}
Ok(Self {
header,
children,
})
}
}
pub struct VSVarFileInfo {
pub header: VSHeader,
pub children: Vec<VSVar>,
}
impl VSVarFileInfo {
pub fn parse<P: PE>(pe: &P, rva: RVA) -> Result<Self, Error> {
let base_offset = pe.translate(PETranslation::Memory(rva))?;
let (mut offset, header) = VSHeader::parse(pe, rva)?;
let mut consumed = offset;
offset = align(offset, 4);
let mut children = Vec::<VSVar>::new();
while consumed < (header.length as usize) {
let rva = match pe.get_type() {
PEType::Disk => Offset(offset as u32).as_rva(pe)?,
PEType::Memory => RVA(offset as u32),
};
let child = VSVar::parse(pe, rva)?;
offset += child.header.length as usize;
offset = align(offset, 4);
consumed = offset - base_offset;
children.push(child);
}
Ok(Self {
header,
children,
})
}
}
pub struct VSVersionInfo {
pub header: VSHeader,
pub value: Option<VSFixedFileInfo>,
pub string_file_info: Option<VSStringFileInfo>,
pub var_file_info: Option<VSVarFileInfo>,
}
impl VSVersionInfo {
pub fn parse<P: PE>(pe: &P) -> Result<Self, Error> {
let resource_dir = ResourceDirectory::parse(pe)?;
let version_rsrc = resource_dir.filter(Some(ResolvedDirectoryID::ID(ResourceID::Version as u32)), None, None);
if version_rsrc.len() == 0 { return Err(Error::ResourceNotFound); }
let rsrc_node = version_rsrc[0].get_data_entry(pe)?;
let base_offset = pe.translate(PETranslation::Memory(rsrc_node.offset_to_data))?;
let (mut offset, header) = VSHeader::parse(pe, rsrc_node.offset_to_data)?;
let mut consumed = offset;
offset = align(offset, 4);
let value;
if header.value_length == 0 {
value = None;
}
else
{
value = match pe.read_val::<VSFixedFileInfo>(offset) {
Ok(v) => Some(v),
Err(e) => return Err(Error::from(e)),
};
let struct_size = mem::size_of::<VSFixedFileInfo>();
offset += struct_size;
consumed = offset - base_offset;
if consumed > header.length as usize { return Err(Error::CorruptDataDirectory); }
}
offset = align(offset, 4);
let mut string_file_info = None;
let mut var_file_info = None;
if consumed < header.length as usize {
let rva = match pe.get_type() { PEType::Disk => Offset(offset as u32).as_rva(pe)?,
PEType::Memory => RVA(offset as u32),
};
let (_, header_check) = VSHeader::parse(pe, rva)?;
let header_key = header_check.key.as_u16_str()?;
let header_str = header_key.as_ustr().to_string_lossy();
if header_str == "StringFileInfo" {
let string_file_info_tmp = VSStringFileInfo::parse(pe, rva)?;
offset += string_file_info_tmp.header.length as usize;
consumed = offset - base_offset;
if consumed > header.length as usize { return Err(Error::CorruptDataDirectory); }
string_file_info = Some(string_file_info_tmp);
}
else if header_str == "VarFileInfo" {
let var_file_info_tmp = VSVarFileInfo::parse(pe, rva)?;
offset += var_file_info_tmp.header.length as usize;
consumed = offset - base_offset;
if consumed > header.length as usize { return Err(Error::CorruptDataDirectory); }
var_file_info = Some(var_file_info_tmp);
}
else { panic!("Unknown VS_VERSIONINFO structure header, please report this bug to github.com/frank2/exe-rs with the offending binary."); }
}
offset = align(offset, 4);
if consumed < header.length as usize {
let rva = match pe.get_type() { PEType::Disk => Offset(offset as u32).as_rva(pe)?,
PEType::Memory => RVA(offset as u32),
};
let (_, header_check) = VSHeader::parse(pe, rva)?;
let header_key = header_check.key.as_u16_str()?;
let header_str = header_key.as_ustr().to_string_lossy();
if header_str == "StringFileInfo" {
let string_file_info_tmp = VSStringFileInfo::parse(pe, rva)?;
offset += string_file_info_tmp.header.length as usize;
consumed = offset - base_offset;
if consumed > header.length as usize { return Err(Error::CorruptDataDirectory); }
string_file_info = Some(string_file_info_tmp);
}
else if header_str == "VarFileInfo" {
let var_file_info_tmp = VSVarFileInfo::parse(pe, rva)?;
offset += var_file_info_tmp.header.length as usize;
consumed = offset - base_offset;
if consumed > header.length as usize { return Err(Error::CorruptDataDirectory); }
var_file_info = Some(var_file_info_tmp);
}
else { panic!("Unknown VS_VERSIONINFO structure header, please report this bug to github.com/frank2/exe-rs with the offending binary."); }
}
Ok(Self {
header,
value,
string_file_info,
var_file_info,
})
}
}