use super::header::{Parsed, IMAGE_DIRECTORY_ENTRY_IMPORT};
use super::PeError;
use crate::emulator::mmu::Mmu;
use crate::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: &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(())
}
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())
}