use super::PeError;
use super::header::{IMAGE_DIRECTORY_ENTRY_IMPORT, Parsed};
use crate::emulator::emulator::mmu::Mmu;
use crate::emulator::win32::Registry;
const IMAGE_ORDINAL_FLAG32: u32 = 0x8000_0000;
pub fn resolve(
mmu: &mut Mmu,
parsed: &Parsed,
image_base: u32,
registry: &mut Registry,
) -> Result<(), PeError> {
resolve_with(mmu, parsed, image_base, registry, ResolveMode::Strict, None)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResolveMode {
Strict,
FailSoft,
}
pub fn resolve_with(
mmu: &mut Mmu,
parsed: &Parsed,
image_base: u32,
registry: &mut Registry,
mode: ResolveMode,
mut unresolved: Option<&mut Vec<(String, String)>>,
) -> Result<(), PeError> {
let dir = parsed.optional.data_directories[IMAGE_DIRECTORY_ENTRY_IMPORT];
if dir.virtual_address == 0 || dir.size == 0 {
return Ok(());
}
let mut desc = image_base.wrapping_add(dir.virtual_address);
loop {
let original_first_thunk = mmu.load32(desc)?;
let _time_date_stamp = mmu.load32(desc.wrapping_add(4))?;
let _forwarder_chain = mmu.load32(desc.wrapping_add(8))?;
let name_rva = mmu.load32(desc.wrapping_add(12))?;
let first_thunk = mmu.load32(desc.wrapping_add(16))?;
if original_first_thunk == 0 && first_thunk == 0 && name_rva == 0 {
break; }
let dll_name = read_cstr(mmu, image_base.wrapping_add(name_rva))?;
let dll_lower = dll_name.to_ascii_lowercase();
let ilt = if original_first_thunk != 0 {
image_base.wrapping_add(original_first_thunk)
} else {
image_base.wrapping_add(first_thunk)
};
let iat = image_base.wrapping_add(first_thunk);
let mut i: u32 = 0;
loop {
let entry = mmu.load32(ilt.wrapping_add(4 * i))?;
if entry == 0 {
break;
}
let (import_name, resolved) = if (entry & IMAGE_ORDINAL_FLAG32) != 0 {
let ord = entry & 0xFFFF;
let name = format!("@{ord}");
let r = registry.resolve(&dll_lower, &name);
(name, r)
} else {
let by_name = image_base.wrapping_add(entry & 0x7FFF_FFFF);
let name = read_cstr(mmu, by_name.wrapping_add(2))?;
let r = registry.resolve(&dll_lower, &name);
(name, r)
};
let thunk = match (resolved, mode) {
(Some(addr), _) => addr,
(None, ResolveMode::Strict) => {
return Err(PeError::UnknownImportFunction {
dll: dll_lower.clone(),
name: import_name,
});
}
(None, ResolveMode::FailSoft) => {
if let Some(sink) = unresolved.as_mut() {
sink.push((dll_lower.clone(), import_name.clone()));
}
registry.register_unknown_fallback(&dll_lower, &import_name)
}
};
mmu.store32(iat.wrapping_add(4 * i), thunk)?;
i = i.wrapping_add(1);
}
desc = desc.wrapping_add(20);
}
Ok(())
}
pub fn resolve_strict(
mmu: &mut Mmu,
parsed: &Parsed,
image_base: u32,
registry: &mut Registry,
) -> Result<(), PeError> {
let dir = parsed.optional.data_directories[IMAGE_DIRECTORY_ENTRY_IMPORT];
if dir.virtual_address == 0 || dir.size == 0 {
return Ok(());
}
let mut desc = image_base.wrapping_add(dir.virtual_address);
loop {
let original_first_thunk = mmu.load32(desc)?;
let name_rva = mmu.load32(desc.wrapping_add(12))?;
let first_thunk = mmu.load32(desc.wrapping_add(16))?;
if original_first_thunk == 0 && first_thunk == 0 && name_rva == 0 {
break;
}
let dll_name = read_cstr(mmu, image_base.wrapping_add(name_rva))?;
let dll_lower = dll_name.to_ascii_lowercase();
let ilt = if original_first_thunk != 0 {
image_base.wrapping_add(original_first_thunk)
} else {
image_base.wrapping_add(first_thunk)
};
let iat = image_base.wrapping_add(first_thunk);
let mut i: u32 = 0;
loop {
let entry = mmu.load32(ilt.wrapping_add(4 * i))?;
if entry == 0 {
break;
}
let (import_name, resolved) = if (entry & IMAGE_ORDINAL_FLAG32) != 0 {
let ord = entry & 0xFFFF;
let name = format!("@{ord}");
let r = registry.resolve(&dll_lower, &name);
(name, r)
} else {
let by_name = image_base.wrapping_add(entry & 0x7FFF_FFFF);
let name = read_cstr(mmu, by_name.wrapping_add(2))?;
let r = registry.resolve(&dll_lower, &name);
(name, r)
};
let Some(thunk) = resolved else {
return Err(PeError::UnknownImportFunction {
dll: dll_lower.clone(),
name: import_name,
});
};
mmu.store32(iat.wrapping_add(4 * i), thunk)?;
i = i.wrapping_add(1);
}
desc = desc.wrapping_add(20);
}
Ok(())
}
pub fn list_imported_dlls(parsed: &Parsed, bytes: &[u8]) -> Result<Vec<String>, PeError> {
let dir = parsed.optional.data_directories[IMAGE_DIRECTORY_ENTRY_IMPORT];
if dir.virtual_address == 0 || dir.size == 0 {
return Ok(Vec::new());
}
let rva_to_off = |rva: u32| -> Option<u32> {
for s in &parsed.sections {
let start = s.virtual_address;
let span = s.virtual_size.max(s.size_of_raw_data);
let end = start.checked_add(span)?;
if rva >= start && rva < end {
let delta = rva - start;
if delta < s.size_of_raw_data {
return Some(s.pointer_to_raw_data + delta);
}
return None;
}
}
None
};
let mut off = rva_to_off(dir.virtual_address).ok_or(PeError::DirectoryOutOfRange {
name: "IMPORT",
rva: dir.virtual_address,
size: dir.size,
})? as usize;
let mut out = Vec::new();
let mut seen: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
loop {
if off + 20 > bytes.len() {
break;
}
let orig = super::header::read_u32(bytes, off)?;
let name_rva = super::header::read_u32(bytes, off + 12)?;
let first = super::header::read_u32(bytes, off + 16)?;
if orig == 0 && first == 0 && name_rva == 0 {
break;
}
if let Some(name_off) = rva_to_off(name_rva) {
let no = name_off as usize;
let mut end = no;
while end < bytes.len() && bytes[end] != 0 {
end += 1;
}
let name = String::from_utf8_lossy(&bytes[no..end]).into_owned();
let lc = name.to_ascii_lowercase();
if seen.insert(lc) {
out.push(name);
}
}
off += 20;
}
Ok(out)
}
fn read_cstr(mmu: &Mmu, mut addr: u32) -> Result<String, PeError> {
let mut bytes = Vec::new();
for _ in 0..1024 {
let b = mmu.load8(addr)?;
if b == 0 {
break;
}
bytes.push(b);
addr = addr.wrapping_add(1);
}
Ok(String::from_utf8_lossy(&bytes).into_owned())
}