CasualX / pelite

Lightweight, memory-safe, zero-allocation library for reading and navigating PE binaries.
MIT License
280 stars 42 forks source link

Unable to read PE version on Linux #253

Closed wangjia184 closed 2 years ago

wangjia184 commented 2 years ago

The following code works well on Windows, but on Linux in our CI environment, it cannot extract version.

We are running cross-compilation in containers of GitLab CI, the PE files are fine. I download them and try to parse on Windows, and it works. But when the code executes on Linux, it fails.

fn parse_pe_version(file_path : &str) -> String {
    match FileMap::open(file_path) {
        Err(e) => format!("FileMap::open failed. {}", e),
        Ok(file_map) => {
            match PeFile::from_bytes(&file_map) {
                Err(e) => format!("PeFile::from_bytes failed. {}", e),
                Ok(file) => {
                    match file.resources() {
                        Err(e) => format!("file.resources() failed. {}", e),
                        Ok(resources) => {
                            match resources.version_info() {
                                Err(e) => format!("resources.version_info() failed. {}", e),
                                Ok(version_info) => {
                                    match version_info.fixed() {
                                        None => format!("No fixed version info"),
                                        Some(fixed_info) => {
                                            let version = fixed_info.dwFileVersion.to_string();
                                            if version.len() > 0 {
                                                version
                                            } else {
                                                fixed_info.dwProductVersion.to_string()
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
    }
}

PeFile::from_bytes failed. try again with correct parser

CasualX commented 2 years ago

It sounds like you're either using the PeFile from the wrong bitness or that you're running this on a big endian arch? big endian is not supported by this library as it does not allow reinterpreting disk bytes directly as rust structures.

Edit: it could also be that my filemapping code is just broken on linux... You can try std::fs::read and hope the allocated byte buffer has the right alignment and see if that helps.

wangjia184 commented 2 years ago

thanks @CasualX

I found the reason and it is not caused by the library. The reason is that -- when dotnet build on Linux targeting Windows, even if the assembly dll will be used in a x64 environment, the PE NT optional header still generating using a PE32 format.

I fixed the issue by try both PE64 and PE32

pub fn parse_pe_version(file_path : &str) -> String {
  match FileMap::open(file_path) {
      Err(_) => "".to_string(),
      Ok(file_map) => {

          let result = match Pe32File::from_bytes(&file_map) {
              Err(_) => {
                  match Pe64File::from_bytes(&file_map) {
                      Ok(f) => f.resources(),
                      Err(e) => Err(e),
                  }
              } 
              Ok(f) => f.resources()
          };

          match result {
              Err(_) => "".to_string(),
              Ok(resources) => {
                  match resources.version_info() {
                      Err(_) => "".to_string(),
                      Ok(version_info) => {
                          match version_info.fixed() {
                              None => "".to_string(),
                              Some(fixed_info) => {
                                  let version = fixed_info.dwFileVersion.to_string();
                                  if version.len() > 0 {
                                      version
                                  } else {
                                      fixed_info.dwProductVersion.to_string()
                                  }
                              }
                          }
                      }
                  }
              }
          }
      },
  }
}
CasualX commented 2 years ago

Ah, surely explicitly using pelite::pe64::PeFile should work?

The method to 'transparently' read from the 32-bit or 64-bit PE files is using the Wrap. It redefines the whole pelite API on top of a generic Wrap<T32, T64> type which dispatches between 32-bit (T32) and 64-bit (T64) variants.

wangjia184 commented 2 years ago

Ah, surely explicitly using pelite::pe64::PeFile should work?

No, that does not work.

CasualX commented 2 years ago

No, that does not work.

OH I see, it's ACTUALLY a PE32 file for C# dot net DLLs on linux, that took me some time to understand.

You can try pelite::PeFile to handle dispatching between PE32 and PE64 for you:

let file_map = pelite::FileMap::open(path)?;
let resources = pelite::PeFile::from_bytes(&file_map)?.resources()?;