Skip to content

Commit

Permalink
Fixed pe pointer issue, added DOS explanation, add Borland tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kkent030315 committed Oct 10, 2024
1 parent 7bcc51b commit 7dc98ad
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 9 deletions.
114 changes: 106 additions & 8 deletions src/pe/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,12 +333,12 @@ impl DosHeader {
offset
);

let pe_pointer = bytes
let pe_pointer: u32 = bytes
.pread_with(PE_POINTER_OFFSET as usize, scroll::LE)
.map_err(|_| {
error::Error::Malformed(format!(
"cannot parse PE header pointer (offset {:#x})",
PE_POINTER_OFFSET
offset
))
})?;

Expand Down Expand Up @@ -396,18 +396,53 @@ pub struct DosStub {
pub data: Vec<u8>,
}
impl Default for DosStub {
/// This is the very basic DOS program bytecode representation embedded in MSVC linker.
///
/// An equivalent (Not a equal) DOS program can be follows:
///
/// ```asm
/// push cs ; 0E Push the code segment onto the stack
/// pop ds ; 1F Pop the top of the stack into the data segment register
///
/// _start:
/// mov dx, aMessage ; BA 0E 00 Load the address of the message to the DX register
/// mov ah, 09h ; B4 09 DOS function 09h (display string) to print the message at DS:DX
/// int 21h ; CD 21 Call DOS interrupt 21h for displaying the message
///
/// mov ax, 4C01h ; B8 01 4C DOS function 4Ch (terminate program) with return code 1
/// int 21h ; CD 21 Call DOS interrupt 21h for program termination
///
/// aMessage db 'This program cannot be run in DOS mode.', 0x0D, 0x0D, 0x0A, '$'
/// ```
#[rustfmt::skip]
fn default() -> Self {
Self {
data: vec![
0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21,
0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63,
0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69,
0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x0D, 0x0D, 0x0A,
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0E, 0x1F, // push cs; pop ds: Setup segment registers
0xBA, 0x0E, 0x00, // mov dx, 0x000E: Load the message address into the DX register
0xB4, 0x09, // mov ah, 0x09: DOS function to print a string
0xCD, 0x21, // int 0x21: Trigger DOS interrupt 21h to print the message
0xB8, 0x01, 0x4C, // mov ax, 0x4C01: Prepare to terminate the program (DOS function 4Ch)
0xCD, 0x21, // int 0x21: Trigger DOS interrupt 21h to terminate the program
0x54, 0x68, 0x69, 0x73, // "This" ASCII string "This program can be run in DOS mode."
0x20, 0x70, 0x72, 0x6F, // " pro" Continuation of the ASCII string,
0x67, 0x72, 0x61, 0x6D, // "gram" Continuation of the ASCII string,
0x20, 0x63, 0x61, 0x6E, // " can" Continuation of the ASCII string,
0x6E, 0x6F, 0x74, 0x20, // "not " Continuation of the ASCII string,
0x62, 0x65, 0x20, 0x72, // "be r" Continuation of the ASCII string,
0x75, 0x6E, 0x20, 0x69, // "un i" Continuation of the ASCII string,
0x6E, 0x20, 0x44, 0x4F, // "n DO" Continuation of the ASCII string,
0x53, 0x20, 0x6D, 0x6F, // "S mo" Continuation of the ASCII string,
0x64, 0x65, 0x2E, // "DE." Continuation of the ASCII string, ending with a period.
0x0D, 0x0D, 0x0A, // Carriage return (CR `0x0D, 0x0D`) and line feed (LF `0x0A`)
0x24, // '$' (End of string marker for DOS function 09h)
0x00, 0x00, 0x00, 0x00, // Null terminators or padding bytes for alignment
0x00, 0x00, 0x00, // Padding bytes
],
}
}
}

impl DosStub {
/// Parse the DOS stub.
///
Expand All @@ -429,6 +464,15 @@ impl DosStub {
})
}
}
impl ctx::TryIntoCtx<scroll::Endian> for DosStub {
type Error = error::Error;

fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let offset = &mut 0;
bytes.gwrite_with(&*self.data, offset, ())?;
Ok(*offset)
}
}

/// In `winnt.h`, it's `IMAGE_FILE_HEADER`. COFF Header.
///
Expand Down Expand Up @@ -849,7 +893,7 @@ impl ctx::TryIntoCtx<scroll::Endian> for Header {
fn try_into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) -> Result<usize, Self::Error> {
let offset = &mut 0;
bytes.gwrite_with(self.dos_header, offset, ctx)?;
bytes.gwrite_with(&*self.dos_stub.data, offset, ())?;
bytes.gwrite_with(self.dos_stub, offset, ctx)?;
bytes.gwrite_with(self.signature, offset, scroll::LE)?;
bytes.gwrite_with(self.coff_header, offset, ctx)?;
if let Some(opt_header) = self.optional_header {
Expand Down Expand Up @@ -1175,6 +1219,8 @@ pub fn machine_to_str(machine: u16) -> &'static str {

#[cfg(test)]
mod tests {
use crate::pe::header::DosStub;

use super::{
machine_to_str, Header, RichHeader, RichMetadata, COFF_MACHINE_X86, DOS_MAGIC, PE_MAGIC,
};
Expand Down Expand Up @@ -1291,6 +1337,46 @@ mod tests {
0x00,
];

const BORLAND_PE32_VALID_NO_RICH_HEADER: [u8; 528] = [
0x4D, 0x5A, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1A, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0xBA, 0x10, 0x00, 0x0E, 0x1F, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01,
0x4C, 0xCD, 0x21, 0x90, 0x90, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72,
0x61, 0x6D, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20,
0x75, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x57, 0x69, 0x6E, 0x33, 0x32, 0x0D, 0x0A, 0x24, 0x37,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, // PE
0x50, 0x45, 0x00, 0x00, 0x4C, 0x01, 0x08, 0x00, 0xC0, 0x9C, 0x07, 0x67, 0x00, 0x00, 0x00,
0x00,
];

#[test]
fn crss_header() {
let header = Header::parse(&&CRSS_HEADER[..]).unwrap();
Expand All @@ -1301,6 +1387,18 @@ mod tests {
println!("header: {:?}", &header);
}

#[test]
fn parse_borland_weird_dos_stub() {
let dos_stub = DosStub::new(&BORLAND_PE32_VALID_NO_RICH_HEADER, 0x200).unwrap();
assert_ne!(dos_stub.data, BORLAND_PE32_VALID_NO_RICH_HEADER.to_vec());
}

#[test]
fn parse_borland_no_rich_header() {
let header = RichHeader::parse(&BORLAND_PE32_VALID_NO_RICH_HEADER).unwrap();
assert_eq!(header, None);
}

#[test]
fn parse_no_rich_header() {
let header = RichHeader::parse(&NO_RICH_HEADER).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/pe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ impl<'a> ctx::TryIntoCtx<scroll::Endian> for PE<'a> {
"Symbol tables in PE are deprecated and not supported to write"
);

bytes.gwrite_with(self.header, &mut offset, ctx)?;
bytes.gwrite_with(self.header.clone(), &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.
Expand Down

0 comments on commit 7dc98ad

Please sign in to comment.