Skip to content
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

PE images with multiple debug directory entries not fully supported #314

Open
lzybkr opened this issue Jun 14, 2022 · 1 comment
Open

PE images with multiple debug directory entries not fully supported #314

lzybkr opened this issue Jun 14, 2022 · 1 comment

Comments

@lzybkr
Copy link
Contributor

lzybkr commented Jun 14, 2022

Goblin currently parses the first debug directory and ignores any subsequent entries.

A PE debug directory can have multiple entries, for example, a C# program compiled with the option /debug:embedded might have 3 entries like:

Debug Directories

        Time Type        Size      RVA  Pointer
    -------- ------- -------- -------- --------
    DB2C7143 cv           11C 000024C8      6C8    Format: RSDS, {2943D330-EE6C-414F-A997-0E64FA8EB29F}, 1, Program.pdb
    00000000 pdbhash       27 000025E4      7E4    SHA256: 30 D3 43 29 6C EE 4F E1 69 97 0E 64 FA 8E B2 9F 43 71 2C 5B 0F 63 56 9C 29 D7 42 47 3E 86 68 70
    00000000 mpdb         790 0000260B      80B    Embedded Portable PDB: signature = 4244504D, uncompressed size = B30

In the above - the pdb data is in the image, not in a separate file. This isn't the only scenario where there may be multiple debug directory entries.

I'm not sure what a fix should look like - the debug_data field returns a singleton Option<DebugData>, and the image_debug_directory field is also a single instance of ImageDebugDirectory.

I think the debug_data field should ideally be a Option<Vec<DebugData>>, but that would be an api breaking change.

I suppose there could be an additional field name additional_debug_data to hold the entries after the first.

Note that I thought it might be very difficult to read the additional entries, but it's not too bad. One must explicitly add a reference to the scroll crate to use the Pread trait and then you can write something like this:

    let pe = PE::parse(&bytes)?;

    if let Some(optional_header) = pe.header.optional_header {
        if let Some(debug_directory) = optional_header.data_directories.get_debug_table() {
            let entries = debug_directory.size as usize / std::mem::size_of::<debug::ImageDebugDirectory>();
            
            let rva = debug_directory.virtual_address as usize;
            let opts = options::ParseOptions::default();
            let offset = utils::find_offset(rva, &pe.sections, optional_header.windows_fields.file_alignment, &opts).ok_or_else(|| {
                goblin::error::Error::Malformed(format!(
                    "Cannot map ImageDebugDirectory rva {:#x} into offset",
                    rva
                ))
            })?;
            for i in 0..entries {
                let entry = offset + i * std::mem::size_of::<debug::ImageDebugDirectory>();
                let idd: debug::ImageDebugDirectory = bytes.pread_with(entry, scroll::LE)?;
                println!("{:?}", idd);
            }
        }
    }
@JohnScience
Copy link
Contributor

What about Vec<DebugData> instead? Empty Vec should represent the absence of those well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants