#[derive(Debug)] /// An analyzed PE32/PE32+ binary pubstruct PE<'a> { /// Underlying bytes
bytes: &'a [u8],
authenticode_excluded_sections: Option<authenticode::ExcludedSections>, /// The PE header pub header: header::Header, /// A list of the sections in this PE binary pub sections: Vec<section_table::SectionTable>, /// The size of the binary pub size: usize, /// The name of this `dll`, if it has one pub name: Option<&'a str>, /// Whether this is a `dll` or not pub is_lib: bool, /// Whether the binary is 64-bit (PE32+) pub is_64: bool, /// the entry point of the binary pub entry: usize, /// The binary's RVA, or image base - useful for computing virtual addreses pub image_base: usize, /// Data about any exported symbols in this binary (e.g., if it's a `dll`) pub export_data: Option<export::ExportData<'a>>, /// Data for any imported symbols, and from which `dll`, etc., in this binary pub import_data: Option<import::ImportData<'a>>, /// The list of exported symbols in this binary, contains synthetic information for easier analysis pub exports: Vec<export::Export<'a>>, /// The list symbols imported by this binary from other `dll`s pub imports: Vec<import::Import<'a>>, /// The list of libraries which this binary imports symbols from pub libraries: Vec<&'a str>, /// Debug information, if any, contained in the PE header pub debug_data: Option<debug::DebugData<'a>>, /// Exception handling and stack unwind information, if any, contained in the PE header pub exception_data: Option<exception::ExceptionData<'a>>, /// Certificates present, if any, described by the Certificate Table pub certificates: certificate_table::CertificateDirectoryTable<'a>,
}
impl<'a> PE<'a> { /// Reads a PE binary from the underlying `bytes` pubfn parse(bytes: &'a [u8]) -> error::Result<Self> { Self::parse_with_opts(bytes, &options::ParseOptions::default())
}
/// Reads a PE binary from the underlying `bytes` pubfn parse_with_opts(bytes: &'a [u8], opts: &options::ParseOptions) -> error::Result<Self> { let header = header::Header::parse(bytes)?; letmut authenticode_excluded_sections = None;
debug!("{:#?}", header); let optional_header_offset = header.dos_header.pe_pointer as usize
+ header::SIZEOF_PE_MAGIC
+ header::SIZEOF_COFF_HEADER; let offset =
&mut (optional_header_offset + header.coff_header.size_of_optional_header as usize);
let sections = header.coff_header.sections(bytes, offset)?; let is_lib = characteristic::is_dll(header.coff_header.characteristics); letmut entry = 0; letmut image_base = 0; letmut exports = vec![]; letmut export_data = None; letmut name = None; letmut imports = vec![]; letmut import_data = None; letmut libraries = vec![]; letmut debug_data = None; letmut exception_data = None; letmut certificates = Default::default(); letmut is_64 = false; iflet Some(optional_header) = header.optional_header { // Sections we are assembling through the parsing, eventually, it will be passed // to the authenticode_sections attribute of `PE`. let (checksum, datadir_entry_certtable) = match optional_header.standard_fields.magic {
optional_header::MAGIC_32 => { let standard_field_offset =
optional_header_offset + optional_header::SIZEOF_STANDARD_FIELDS_32; let checksum_field_offset =
standard_field_offset + optional_header::OFFSET_WINDOWS_FIELDS_32_CHECKSUM;
(
checksum_field_offset..checksum_field_offset + 4,
optional_header_offset + 128..optional_header_offset + 136,
)
}
optional_header::MAGIC_64 => { let standard_field_offset =
optional_header_offset + optional_header::SIZEOF_STANDARD_FIELDS_64; let checksum_field_offset =
standard_field_offset + optional_header::OFFSET_WINDOWS_FIELDS_64_CHECKSUM;
pubfn write_sections(
&self,
bytes: &mut [u8],
offset: &mut usize,
file_alignment: Option<usize>,
ctx: scroll::Endian,
) -> Result<usize, error::Error> { // sections table and data
debug_assert!( self.sections
.iter()
.flat_map(|section_a| { self.sections
.iter()
.map(move |section_b| (section_a, section_b))
}) // given sections = (s_1, …, s_n) // for all (s_i, s_j), i != j, verify that s_i does not overlap with s_j and vice versa.
.all(|(section_i, section_j)| section_i == section_j
|| !section_i.overlaps_with(section_j)), "Overlapping sections were found, this is not supported."
);
for section in &self.sections { let section_data = section.data(&self.bytes)?.ok_or_else(|| {
error::Error::Malformed(format!( "Section data `{}` is malformed",
section.name().unwrap_or("unknown name")
))
})?; let file_section_offset =
usize::try_from(section.pointer_to_raw_data).map_err(|_| {
error::Error::Malformed(format!( "Section `{}`'s pointer to raw data does not fit in platform `usize`",
section.name().unwrap_or("unknown name")
))
})?; let vsize: usize = section.virtual_size.try_into()?; let ondisk_size: usize = section.size_of_raw_data.try_into()?; let section_name = String::from(section.name().unwrap_or("unknown name"));
letmut file_offset = file_section_offset; // `file_section_offset` is a on-disk offset which can be anywhere in the file. // Write section data first to avoid the final consumption. match section_data {
Cow::Borrowed(borrowed) => bytes.gwrite(borrowed, &mut file_offset)?,
Cow::Owned(owned) => bytes.gwrite(owned.as_slice(), &mut file_offset)?,
};
// Section tables follows the header.
bytes.gwrite_with(section, offset, ctx)?;
// for size size_of_raw_data // if < virtual_size, pad with 0 // Pad with zeros if necessary if file_offset < vsize {
bytes.gwrite(vec![0u8; vsize - file_offset].as_slice(), &mut file_offset)?;
}
// Align on a boundary as per file alignement field. iflet Some(pad) = pad(file_offset - file_section_offset, file_alignment) {
debug!( "aligning `{}` {:#x} -> {:#x} bytes'",
section_name,
file_offset - file_section_offset,
file_offset - file_section_offset + pad.len()
);
bytes.gwrite(pad.as_slice(), &mut file_offset)?;
}
let written_data_size = file_offset - file_section_offset; if ondisk_size != written_data_size {
warn!("Original PE is inefficient or bug (on-disk data size in PE: {:#x}), we wrote {:#x} bytes",
ondisk_size,
written_data_size);
}
}
Ok(*offset)
}
pubfn write_certificates(
&self,
bytes: &mut [u8],
ctx: scroll::Endian,
) -> Result<usize, error::Error> { let opt_header = self
.header
.optional_header
.ok_or(error::Error::Malformed(format!( "This PE binary has no optional header; it is required to write certificates"
)))?; letmut max_offset = 0;
impl<'a> ctx::TryIntoCtx<scroll::Endian> for PE<'a> { type Error = error::Error;
fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> { letmut offset = 0; // We need to maintain a `max_offset` because // we could be writing sections in the wrong order (i.e. not an increasing order for the // pointer on raw disk) // and there could be holes between sections. // If we don't re-layout sections, we cannot fix that ourselves. // Same can be said about the certificate table, there could be a hole between sections // and the certificate data. // To avoid those troubles, we maintain the max over all offsets we see so far. letmut max_offset = 0; let file_alignment: Option<usize> = matchself.header.optional_header {
Some(opt_header) => {
debug_assert!(
opt_header.windows_fields.file_alignment.count_ones() == 1, "file alignment should be a power of 2"
);
Some(opt_header.windows_fields.file_alignment.try_into()?)
}
_ => None,
};
bytes.gwrite_with(self.header, &mut offset, ctx)?;
max_offset = max(offset, max_offset); self.write_sections(bytes, &mut offset, file_alignment, ctx)?; // We want the section offset for which we have the highest pointer on disk. // The next offset is reserved for debug tables (outside of sections) and/or certificate // tables.
max_offset = max( self.sections
.iter()
.max_by_key(|section| section.pointer_to_raw_data as usize)
.map(|section| (section.pointer_to_raw_data + section.size_of_raw_data) as usize)
.unwrap_or(offset),
max_offset,
);
// COFF Symbol Table // Auxiliary Symbol Records // COFF String Table
assert!( self.header.coff_header.pointer_to_symbol_table == 0, "Symbol tables in PE are deprecated and not supported to write"
);
// The following data directories are // taken care inside a section: // - export table (.edata) // - import table (.idata) // - bound import table // - import address table // - delay import tables // - resource table (.rsrc) // - exception table (.pdata) // - base relocation table (.reloc) // - debug table (.debug) <- this one is special, it can be outside of a // section. // - load config table // - tls table (.tls) // - architecture (reserved, 0 for now) // - global ptr is a "empty" data directory (header-only) // - clr runtime header (.cormeta is object-only) // // Nonetheless, we need to write the attribute certificate table one.
max_offset = max(max_offset, self.write_certificates(bytes, ctx)?);
// TODO: we would like to support debug table outside of a section. // i.e. debug tables that are never mapped in memory // See https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#debug-directory-image-only // > The debug directory can be in a discardable .debug section (if one exists), or it can be included in any other section in the image file, or not be in a section at all. // In case it's not in a section at all, we need to find a way // to rewrite it again. // and we need to respect the ordering between attribute certificates // and debug table.
Ok(max_offset)
}
}
/// An analyzed COFF object #[derive(Debug)] pubstruct Coff<'a> { /// The COFF header pub header: header::CoffHeader, /// A list of the sections in this COFF binary pub sections: Vec<section_table::SectionTable>, /// The COFF symbol table, they are not guaranteed to exist. /// For an image, this is expected to be None as COFF debugging information /// has been deprecated. pub symbols: Option<symbol::SymbolTable<'a>>, /// The string table, they don't exist if COFF symbol table does not exist. pub strings: Option<strtab::Strtab<'a>>,
}
impl<'a> Coff<'a> { /// Reads a COFF object from the underlying `bytes` pubfn parse(bytes: &'a [u8]) -> error::Result<Self> { let offset = &mut0; let header = header::CoffHeader::parse(bytes, offset)?;
debug!("{:#?}", header); // TODO: maybe parse optional header, but it isn't present for Windows.
*offset += header.size_of_optional_header as usize; let sections = header.sections(bytes, offset)?; let symbols = header.symbols(bytes)?; let strings = header.strings(bytes)?;
Ok(Coff {
header,
sections,
symbols,
strings,
})
}
}
#[cfg(test)] mod tests { usesuper::Coff; usesuper::PE;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.