use std::path::Path;
use pelite::PeFile;
use pelite::image::IMAGE_DATA_DIRECTORY;
use polyplug::error::LoaderError;
#[derive(Clone, Copy)]
#[repr(C)]
struct ImageCor20Header {
cb: u32,
major_runtime_version: u16,
minor_runtime_version: u16,
metadata_rva: u32,
metadata_size: u32,
flags: u32,
entry_point_token: u32,
resources_rva: u32,
resources_size: u32,
strong_name_signature: u64,
code_manager_table: u64,
vtable_fixups: u64,
extra_address_jumps: u64,
managed_native_header: u64,
}
unsafe impl pelite::Pod for ImageCor20Header {}
const IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: usize = 14;
const TFM_MARKER: &[u8] = b".NETCoreApp,Version=v";
pub fn read_target_framework(dll_path: &Path) -> Result<String, LoaderError> {
let bytes: Vec<u8> = std::fs::read(dll_path).map_err(|_| LoaderError::InitFailed {
bundle: dll_path.to_string_lossy().into_owned(),
error: "assembly not found or unreadable".to_owned(),
})?;
let label: String = dll_path.to_string_lossy().into_owned();
target_framework_from_bytes(&bytes, &label)
}
pub fn target_framework_from_bytes(bytes: &[u8], label: &str) -> Result<String, LoaderError> {
let pe: PeFile<'_> = PeFile::from_bytes(bytes).map_err(|_| LoaderError::InitFailed {
bundle: label.to_owned(),
error: "invalid PE format".to_owned(),
})?;
let data_dirs: &[IMAGE_DATA_DIRECTORY] = pe.data_directory();
if data_dirs.len() <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR {
return Ok(String::new());
}
let com_dir: &IMAGE_DATA_DIRECTORY = &data_dirs[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
if com_dir.VirtualAddress == 0 {
return Ok(String::new());
}
let cor20: &ImageCor20Header =
pe.derva(com_dir.VirtualAddress)
.map_err(|_| LoaderError::InitFailed {
bundle: label.to_owned(),
error: "COR20 header not found or invalid".to_owned(),
})?;
let metadata_slice: &[u8] = pe
.derva_slice(cor20.metadata_rva, cor20.metadata_size as usize)
.map_err(|_| LoaderError::InitFailed {
bundle: label.to_owned(),
error: "CLI metadata section not found or invalid".to_owned(),
})?;
let start: usize = match metadata_slice
.windows(TFM_MARKER.len())
.position(|w: &[u8]| w == TFM_MARKER)
{
Some(p) => p,
None => return Ok(String::new()),
};
let end: usize = metadata_slice[start..]
.iter()
.position(|&b: &u8| b == 0u8)
.map(|n: usize| start + n)
.unwrap_or(metadata_slice.len());
let tfm_bytes: &[u8] = &metadata_slice[start..end];
let tfm: String = String::from_utf8_lossy(tfm_bytes).into_owned();
Ok(tfm) }