resolve 1 level of imports

This commit is contained in:
🪞👃🪞 2025-02-21 22:51:39 +02:00
parent 5188bc5581
commit dbca25d9cb

View file

@ -26,11 +26,11 @@ struct Dll {
pe: Arc<VecPE>, pe: Arc<VecPE>,
/// Bytes of `.text` section /// Bytes of `.text` section
code: Arc<[u8]>, code: Arc<[u8]>,
/// DLLs that this one depends on /// Addresses of each imported method from each dependency
deps: BTreeMap<Arc<str>, Arc<Self>>, deps: BTreeMap<Arc<str>, BTreeMap<Arc<str>, u32>>,
/// Calls to deps by source address. /// Calls to dependencies by source address
calls_by_source: BTreeMap<u32, Arc<Call>>, calls_by_source: BTreeMap<u32, Arc<Call>>,
/// Calls to deps by target address. /// Calls to dependencies by target address
calls_by_target: BTreeMap<u32, Vec<Arc<Call>>>, calls_by_target: BTreeMap<u32, Vec<Arc<Call>>>,
} }
@ -53,15 +53,20 @@ struct Call {
impl Rebuilder { impl Rebuilder {
fn new (path: &impl AsRef<Path>) -> Usually<Self> { fn new (path: &impl AsRef<Path>) -> Usually<Self> {
let mut build = Self { paths: Default::default(), dlls: Default::default() }; let mut build = Self { paths: Default::default(), dlls: Default::default() };
let dll = Dll::new(&mut build, &Arc::new(PathBuf::from(path.as_ref()))); let path: Arc<PathBuf> = Arc::from(PathBuf::from(path.as_ref()));
build.paths.insert(path.clone());
let dll = Dll::new(&Arc::new(PathBuf::from(path.as_ref())), false)?;
build.dlls.insert(dll.name.clone(), dll);
Ok(build) Ok(build)
} }
} }
impl Dll { impl Dll {
fn new (build: &mut Rebuilder, path: &Arc<PathBuf>) -> Usually<Arc<Self>> { fn new (
path: &Arc<PathBuf>,
verbose: bool
) -> Usually<Arc<Self>> {
println!("\n(load {BOLD}{path:?}{RESET})"); println!("\n(load {BOLD}{path:?}{RESET})");
build.paths.insert(path.clone());
let name = path.file_name().expect("no file name"); let name = path.file_name().expect("no file name");
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename"); let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice()); let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice());
@ -72,6 +77,12 @@ impl Dll {
let text = &data[start..start+size]; let text = &data[start..start+size];
let mut calls_by_source = Default::default(); let mut calls_by_source = Default::default();
let mut calls_by_target = Default::default(); let mut calls_by_target = Default::default();
let mut deps = Default::default();
let (modules_count, methods_count) = Self::deps(
&pe,
&mut deps,
false
)?;
let calls = Self::calls( let calls = Self::calls(
&name, &name,
&pe, &pe,
@ -81,38 +92,43 @@ impl Dll {
&mut calls_by_target, &mut calls_by_target,
false false
)?; )?;
let (modules_count, methods_count) = Self::imports(&pe)?;
let dll = Arc::new(Self { let dll = Arc::new(Self {
name: name.clone(), name: name.clone(),
path: path.clone(), path: path.clone(),
bang, bang,
pe, pe,
code: Arc::from(text), code: Arc::from(text),
deps: Default::default(), deps: deps.clone(),
calls_by_source, calls_by_source,
calls_by_target, calls_by_target,
}); });
println!(" (deps-modules {modules_count})"); println!(" (deps-modules {modules_count})");
println!(" (deps-methods {methods_count})"); println!(" (deps-methods {methods_count})");
println!(" (call-sites {calls})"); println!(" (call-sites {calls})");
for (call, sites) in dll.calls_by_target.iter() { if verbose {
println!(" (0x{call:08x}\n {:?})", sites.iter() for (call, sites) in dll.calls_by_target.iter() {
.map(|call|format!("0x{:08x}", call.offset)) println!(" (0x{call:08x}\n {:?})", sites.iter()
.collect::<Vec<_>>()); .map(|call|format!("0x{:08x}", call.offset))
.collect::<Vec<_>>());
}
println!("{deps:#?}");
} }
build.dlls.insert(name.clone(), dll.clone());
Ok(dll) Ok(dll)
} }
fn imports (pe: &VecPE) -> Usually<(usize, usize)> { fn deps (
pe: &VecPE,
deps: &mut BTreeMap<Arc<str>, BTreeMap<Arc<str>, u32>>,
verbose: bool,
) -> Usually<(usize, usize)> {
let directory = ImportDirectory::parse(pe)?; let directory = ImportDirectory::parse(pe)?;
let mut total = 0; let mut modules = 0;
let mut methods = 0;
for descriptor in directory.descriptors { for descriptor in directory.descriptors {
let dep = descriptor.get_name(pe)?.as_str()?.to_lowercase(); let module_name = descriptor.get_name(pe)?.as_str()?.to_lowercase();
let imp = descriptor.get_imports(pe)?; let imp = descriptor.get_imports(pe)?;
let iat = descriptor.get_first_thunk(pe)?; let iat = descriptor.get_first_thunk(pe)?;
let ilt = descriptor.get_original_first_thunk(pe)?; let ilt = descriptor.get_original_first_thunk(pe)?;
let lut = descriptor.get_lookup_thunks(pe)?; let lut = descriptor.get_lookup_thunks(pe)?;
let mut imports = Vec::new();
let unwrap_thunk = |thunk: &Thunk, name|match thunk { let unwrap_thunk = |thunk: &Thunk, name|match thunk {
Thunk::Thunk32(t) => panic!("32 bit {name}"), Thunk::Thunk32(t) => panic!("32 bit {name}"),
Thunk::Thunk64(t) => t.0 Thunk::Thunk64(t) => t.0
@ -124,32 +140,28 @@ impl Dll {
lut.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "lookup thunk"))), lut.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "lookup thunk"))),
).enumerate() { ).enumerate() {
let call_via = descriptor.first_thunk.0 + index as u32 * 8; let call_via = descriptor.first_thunk.0 + index as u32 * 8;
let name = match import { let method = match import {
ImportData::Ordinal(x) => { ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"),
//print!("\n (import-ordinal {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} 0x{:>04x})", ImportData::ImportByName(name) => format!("{name}"),
//call_via, thunk, orig, lookup, x);
format!("___VESTAL___ORD___{x}___")
},
ImportData::ImportByName(name) => {
//print!("\n (import-by-name {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} {:?})",
//call_via, thunk, orig, lookup, name);
format!("{name}")
},
}; };
println!(" ({index:5} 0x{call_via:08x} {dep:>20} {name}"); let module_name: Arc<str> = module_name.clone().into();
imports.push((thunk, orig, import)); if !deps.contains_key(&module_name) {
total += 1; deps.insert(module_name.clone(), Default::default());
//if let Some(existing) = self.addr_to_import.get(&call_via) { modules += 1;
//panic!("addr space overlap at 0x{call_via:x}: {}::{} vs {}::{}", }
//existing.0, let module = deps.get_mut(&module_name).unwrap();
//existing.1, let method: Arc<str> = method.clone().into();
//dep.to_string(), if module.contains_key(&method) {
//name); panic!("duplicate method {method} in {module_name}");
//} }
//self.addr_to_import.insert(call_via, (dep.to_string(), name)); module.insert(method.clone(), call_via);
methods += 1;
if verbose {
println!(" ({index:5} 0x{call_via:08x} {module_name:>20} {method})");
}
} }
} }
Ok((0, total)) Ok((modules, methods))
} }
fn calls ( fn calls (
name: &Arc<str>, name: &Arc<str>,
@ -193,17 +205,6 @@ impl Dll {
print!(" ({BOLD}0x{offset:08x}{RESET} 0x{offset_rva:08x}"); print!(" ({BOLD}0x{offset:08x}{RESET} 0x{offset_rva:08x}");
println!(" {BOLD}{instruction:30}{RESET} 0x{call_target:x})"); println!(" {BOLD}{instruction:30}{RESET} 0x{call_target:x})");
} }
//let unknown = (String::from("unknown"), String::from("unknown"));
//let target = self.addr_to_import.get(call_target).unwrap_or(&unknown).0;
//let method = self.addr_to_import.get(call_target).unwrap_or(&unknown).1;
//let external = format!("{}::{}", target, method);
//if verbose {
//println!(" ({BOLD}{external}{RESET}\n Offset(0x{:08x}) RVA(R=0x{:08x})\n {:25} {:40} 0x{:08x}",
//offset, offset_rva,
//opcodes.iter().map(|x|format!("{x:>02x}")).collect::<Vec<_>>().join(" "),
//instruction, call_target,
//);
//}
return Ok(Some(Arc::new(Call { return Ok(Some(Arc::new(Call {
offset: offset, offset: offset,
source: offset_rva, source: offset_rva,