Skip to content

PE: add overlay detection #344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 10, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/read/pe/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ where
pub(super) nt_headers: &'data Pe,
pub(super) data_directories: &'data [pe::ImageDataDirectory],
pub(super) common: CoffCommon<'data, R>,
pub(super) overlay_offset: Option<u64>,
pub(super) data: R,
}

Expand All @@ -48,6 +49,16 @@ where
let symbols = nt_headers.symbols(data)?;
let image_base = nt_headers.optional_header().image_base();

let file_size = data
.len()
.map_err(|()| read::Error("Unable to get file length"))?;
let final_offset = Self::compute_final_offset(offset, file_size, &sections);
let overlay_offset = if final_offset < file_size {
Some(final_offset + 1)
} else {
None
};

Ok(PeFile {
dos_header,
nt_headers,
Expand All @@ -57,6 +68,7 @@ where
symbols,
image_base,
},
overlay_offset,
data,
})
}
Expand Down Expand Up @@ -95,6 +107,32 @@ where
pub fn data(&self) -> R {
self.data
}

/// Returns the offset where the overlay (if any) starts.
///
/// A [data overlay](https://security.stackexchange.com/questions/77336/how-is-the-file-overlay-read-by-an-exe-virus)
/// is the part of the PE file after all headers and sections. Some malware use it to conceal data
pub fn overlay_offset(&self) -> Option<u64> {
self.overlay_offset
}

/// Compute the end of file, according to the offset and sizes declared in the headers
fn compute_final_offset(end_of_headers: u64, file_size: u64, sections: &SectionTable) -> u64 {
let mut final_offset: u64 = end_of_headers;
for section in sections.iter() {
let new_offset = section.pointer_to_raw_data.get(LE);
let new_size = section.size_of_raw_data.get(LE);
let new_final = new_offset as u64 + new_size as u64;
if new_final <= file_size && new_final > final_offset {
final_offset = new_final
}
}

// We'll assume the PE "data directories" are all contained within the sections
// Note that some other parsing libraries (e.g. the `pefile` Python library) does not make such an assumption

final_offset
}
}

impl<'data, Pe, R> read::private::Sealed for PeFile<'data, Pe, R>
Expand Down