1

I'm trying to read the IMAGE_DOS_HEADER of a module using the definition of that structure from the winapi crate.

Here is my working code:

let mut IDH: IMAGE_DOS_HEADER = uninitialized();
copy_nonoverlapping(library, (&mut IDH as *mut IMAGE_DOS_HEADER) as *mut c_void, size_of::<IMAGE_DOS_HEADER>());

I realised that winapi was too huge and I wanted my binary file to be less than a megabyte, so I declared the IMAGE_DOS_HEADER structure by myself:

#[derive(Copy, Clone)]
pub struct _IMAGE_DOS_HEADER{
    pub e_magic: u16,
    pub e_cblp: u16,
    pub e_cp: u16,
    pub e_crlc: u16,
    pub e_cparhdr: u16,
    pub e_minalloc: u16,
    pub e_maxalloc: u16,
    pub e_ss: u16,
    pub e_sp: u16,
    pub e_csum: u16,
    pub e_ip: u16,
    pub e_cs: u16,
    pub e_lfarlc: u16,
    pub e_ovno: u16,
    pub e_res: [u16; 4],
    pub e_oemid: u16,
    pub e_oeminfo: u16,
    pub e_res2: [u16; 10],
    pub e_lfanew: i32,
}

When I did that, all of the fields of the structure get weird values (ex: e_magic = 0x3).

I decided to convert the structure to an array in both cases, even if they are programmatically exactly the same.

println!("{:?}", std::mem::transmute::<IMAGE_DOS_HEADER, [u8;0x40]>(IDH).to_vec());

For both of them it printed:

[77, 90, 144, 0, 3, 0, 0, 0, 4, 0, 0, 0, 255, 255, 0, 0, 184, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 0, 0, 0]

That's really weird. There shouldn't be any differences between the values of their fields. Then I copied the printed array and converted it to my structure and see if all the fields are ok.

let dos: [u8;0x40] = [77, 90, 144, 0, 3, 0, 0, 0, 4, 0, 0, 0, 255, 255, 0, 0, 184, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 0, 0, 0];
println!("{}", std::mem::transmute::<[u8;0x40], _IMAGE_DOS_HEADER>(dos).e_magic);

The first field is equal to 3. I have two structures, with the same fields and the exact same size (I verified), I take an array, I convert it to those structures, and I get different fields.

As you can see there's a 3 in the array dump, but it shouldn't be associated with the first field of the structure (e_magic), it makes no sense, why would it work with the other one?

I guess I'm missing something when I'm initializing the structure, but what?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366

1 Answers1

4

You didn't copy the same definition, you copied the part that is shown in the documentation. This is the (truncated) actual definition:

STRUCT!{struct IMAGE_DOS_HEADER {
    e_magic: WORD,
    e_cblp: WORD,
    // ...
    e_res2: [WORD; 10],
    e_lfanew: LONG,
}}

Notably, this uses the STRUCT macro, which defines a few traits, but more importantly marks the type as repr(C):

#[repr(C)] #[derive(Copy)] $(#[$attrs])*
pub struct $name {
    $(pub $field: $ftype,)+
}

The layout of Rust structs is not specified, so your structure has some other ordering and padding compared to the C structure, and will not work.


I realised that winapi was too huge and I wanted my binary file to be less than a megabyte, so I declared the IMAGE_DOS_HEADER structure by myself

This is incorrect reasoning. Code from libraries that isn't used is removed from the final binary.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366