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: import parser is broken for specially crafted binary #428

Closed
Tracked by #434
kkent030315 opened this issue Oct 29, 2024 · 1 comment
Closed
Tracked by #434

PE: import parser is broken for specially crafted binary #428

kkent030315 opened this issue Oct 29, 2024 · 1 comment

Comments

@kkent030315
Copy link
Contributor

kkent030315 commented Oct 29, 2024

Sample: special_import_forwarder_tls.exe.zip

Reproducible steps

The sample is specially crafted and "valid" PE64 binary where it contains so-called "import forwarder TLS callback" where the TLS callbacks address points directly to the FT (First Thunk in IMAGE_THUNK_DATA) of import entry; and it fails to parse:

cargo run --example rdr -- "path/to/special_import_forwarder_tls.exe.bin"
Malformed entity: Cannot find name from rva 0x0 in sections: [SectionTable { name: [46, 116, 101, 120, 116, 0, 0, 0], real_name: None, virtual_size: 23, virtual_address: 4096, size_of_raw_data: 512, pointer_to_raw_data: 1024, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 1610612768 }, SectionTable { name: [46, 114, 100, 97, 116, 97, 0, 0], real_name: None, virtual_size: 470, virtual_address: 8192, size_of_raw_data: 512, pointer_to_raw_data: 1536, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 1073741888 }, SectionTable { name: [46, 100, 97, 116, 97, 0, 0, 0], real_name: None, virtual_size: 4, virtual_address: 12288, size_of_raw_data: 0, pointer_to_raw_data: 0, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 3221225536 }, SectionTable { name: [46, 67, 82, 84, 0, 0, 0, 0], real_name: None, virtual_size: 40, virtual_address: 16384, size_of_raw_data: 512, pointer_to_raw_data: 2048, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 3221225536 }, SectionTable { name: [46, 67, 82, 84, 0, 0, 0, 0], real_name: None, virtual_size: 8, virtual_address: 20480, size_of_raw_data: 512, pointer_to_raw_data: 2560, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 1073741888 }, SectionTable { name: [46, 114, 101, 108, 111, 99, 0, 0], real_name: None, virtual_size: 28, virtual_address: 24576, size_of_raw_data: 512, pointer_to_raw_data: 3072, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 1107296320 }, SectionTable { name: [0, 0, 0, 0, 0, 0, 0, 0], real_name: None, virtual_size: 65, virtual_address: 28672, size_of_raw_data: 512, pointer_to_raw_data: 3584, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 3221225536 }]

Sample contains an one specially crafted import entry:
image

Cause

The cause is that import parser tries to parse 2nd entry (at 0xe28) where it does not exists:

DEBUG - Synthesizing lookup table imports for abcd.dll lib, with import lookup table rva: 0x7014
DEBUG - bitfield 0x800000000000c8c6
DEBUG - importing by ordinal 0xc8c6
DEBUG - imports done
DEBUG - Successfully synthesized import lookup table entry from lookup table: [
    OrdinalNumber(
        51398,
    ),
]
...
DEBUG - Found in section (6), remapped into offset 0xe14
DEBUG - entry SyntheticImportDirectoryEntry {
    import_directory_entry: ImportDirectoryEntry {
        import_lookup_table_rva: 28692,
        time_date_stamp: 0,
        forwarder_chain: 0,
        name_rva: 28728,
        import_address_table_rva: 28692,
    },
    name: "abcd.dll",
    import_lookup_table: Some(
        [
            OrdinalNumber(
                51398,
            ),
        ],
    ),
    import_address_table: [
        9223372036854827206,
    ],
} at 0xe14
DEBUG - ImportDirectoryEntry {
    import_lookup_table_rva: 51398,
    time_date_stamp: 2147483648,
    forwarder_chain: 0,
    name_rva: 0,
    import_address_table_rva: 0,
} at 0xe28
...
<panic>

The exact code is following:

goblin/src/pe/import.rs

Lines 346 to 357 in d096260

if import_directory_entry.is_null() {
break;
} else {
let entry = SyntheticImportDirectoryEntry::parse_with_opts::<T>(
bytes,
import_directory_entry,
sections,
file_alignment,
opts,
)?;
debug!("entry {:#?}", entry);
import_data.push(entry);

And the raw data (LE):

00000E10: 14 70 00 00 C6 C8 00 00 00 00 00 80 00 00 00 00
00000E20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000E30: 00 00 00 00 00 00 00 00 00 00 00 00 61 62 63 64
                                              ^^^^^^^^^^^
00000E40: 2E 64 6C 6C 00 00 00 00 00 00 00 00 00 00 00 00
          ^^^^^^^^^^^^^^
^: "abcd.dll\0", name rva 0x7038

First entry thunk (abcd.dllORDINAL 51398)

00000E10: 14 70 00 00 C6 C8 00 00 00 00 00 80 00 00 00 00
                      ^^^^^^^^^^^^^^^^^^^^^^^
00000E20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000E30: 00 00 00 00 00 00 00 00 00 00 00 00 61 62 63 64
00000E40: 2E 64 6C 6C 00 00 00 00 00 00 00 00 00 00 00 00

Second entry (null-terminator entry)

00000E10: 14 70 00 00 C6 C8 00 00 00 00 00 80 00 00 00 00
                                              ^^^^^^^^^^^
                                              |1
00000E20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
          ^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^
                    | |2
00000E30: 00 00 00 00 00 00 00 00 00 00 00 00 61 62 63 64
          ^^^^^^^^^^^
                    |
00000E40: 2E 64 6C 6C 00 00 00 00 00 00 00 00 00 00 00 00
^1: Null-terminator (zero IMAGE_THUNK_DATA)
^2: Non-existent IMAGE_IMPORT_DESCRIPTOR aka ImportDirectoryEntry
panic reading nonexistent 2nd entry at 0xe28: c6 c8 00 00 00 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00

So, it tried to read OFT of first IMAGE_IMPORT_DESCRIPTOR (aka ImportDirectoryEntry) element is cause―current implementation of import parser assumes that import directory is well-formed, where it may not well-formed like this case right now.

I'm going to fix this in minor changes.

@kkent030315
Copy link
Contributor Author

Fixed in #429

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

1 participant