m4b / goblin

An impish, cross-platform binary parsing crate, written in Rust
MIT License
1.19k stars 160 forks source link

Failed to parse PE of loaded module #267

Closed 2vg closed 3 years ago

2vg commented 3 years ago

I thought goblin was the only library that could parse PE correctly, but unfortunately after getting the list of modules loaded from PEB, trying to parse from the DllBase address seems to fail halfway. (fyi, pelite succeeded in parsing this, but failed to parse the system DLL. Goblin is perfect for parsing from disc dll.) Here is the procedure for reproducing the error.

put this in Cargo.toml

[dependencies]
anyhow = "1.0.40"
goblin = "0.3.4"
ntapi = { version = "0.3.6", features = ["nightly"] }
winapi = "0.3.9"

then, code like a this:

use std::fs::File;
use std::io::prelude::*;

use anyhow::*;
use ntapi::{
    ntldr::LDR_DATA_TABLE_ENTRY,
    ntpebteb::PEB,
    winapi_local::um::winnt::__readgsqword
};
use winapi::ctypes::c_void;

pub fn get_binary_from_file(file_name: impl Into<String>) -> Result<Vec<u8>> {
    let file_name = file_name.into();
    let mut f = File::open(&file_name)
        .with_context(|| format!("could not opening the file: {}", &file_name))?;
    let mut buffer = Vec::new();
    f.read_to_end(&mut buffer)
        .with_context(|| format!("could not reading from the file: {}", &file_name))?;
    Ok(buffer)
}

pub fn ptr_to_u8slice(p: *mut c_void) -> &'static [u8] {
    unsafe { std::mem::transmute_copy::<*mut c_void, &[u8]>(&p) }
}

fn main() -> Result<()>{
    unsafe {
        let ntdll_buffer = get_binary_from_file("c:\\windows\\system32\\ntdll.dll")?;
        // this works :3
        let _ = goblin::pe::PE::parse(ptr_to_u8slice(&ntdll_buffer[0] as *const _ as _))?;
        println!("parsed!");

        let ppeb = __readgsqword(0x60) as *mut PEB;

        let p_peb_ldr_data = (*ppeb).Ldr;
        let mut module_list = (*p_peb_ldr_data).InLoadOrderModuleList.Flink as *mut LDR_DATA_TABLE_ENTRY;

        while !(*module_list).DllBase.is_null() {
            let dll_base = (*module_list).DllBase;

            module_list = (*module_list).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;

            // this not works :<
            let _ = goblin::pe::PE::parse(ptr_to_u8slice(dll_base))?;
            println!("parsed!");
        }

        Ok(())
    }
}

example output (got this error on my env):

parsed!
Error: Malformed entity: Cannot find name from rva 0x40 in sections: [SectionTable { name: [46, 116, 101, 120, 116, 0, 0, 0], real_name: None, virtual_size: 485194, virtual_address: 4096, size_of_raw_data: 485376, 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: 100872, virtual_address: 491520, size_of_raw_data: 101376, pointer_to_raw_data: 486400, 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: 904, virtual_address: 593920, size_of_raw_data: 512, pointer_to_raw_data: 587776, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 3221225536 }, SectionTable { name: 
[46, 112, 100, 97, 116, 97, 0, 0], real_name: None, virtual_size: 37020, virtual_address: 598016, size_of_raw_data: 37376, pointer_to_raw_data: 588288, 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: 2076, virtual_address: 638976, size_of_raw_data: 2560, pointer_to_raw_data: 625664, pointer_to_relocations: 0, pointer_to_linenumbers: 0, number_of_relocations: 0, number_of_linenumbers: 0, characteristics: 1107296320 }]

I don't know if this only happens in my environment. Or is this not a problem and my code is wrong?

thanks in advance!

2vg commented 3 years ago

hi I didn't know that the resolve_rva option could be set to false. I used it, but I get another error.

code changed to this:

while !(*module_list).DllBase.is_null() {
    let dll_base = (*module_list).DllBase;
    let size = (*module_list).SizeOfImage as usize;

    module_list = (*module_list).InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;

    let buffer = std::slice::from_raw_parts::<u8>(dll_base as _, size);

    let opts = goblin::pe::options::ParseOptions{ resolve_rva: false };
    let res = goblin::pe::PE::parse_with_opts(buffer, &opts);

    if res.is_err() { continue }

    let parsed = res?;
    println!("{}", parsed.name.unwrap());
    println!("parsed!");
}
Error: bad input invalid utf8 (56906)

Caused by:
    bad input invalid utf8 (56906)

Only some loaded images fail, but others parsed correctly. (For example, ntdll, kernel32, etc)

2vg commented 3 years ago

By ignoring the error, I was able to achieve what I wanted to do, but I'm not sure if this is a problem to be resolved in the future, so I'll leave an issue. If you're willing to close it, you can close it!