wip: resolve in breadth

This commit is contained in:
🪞👃🪞 2025-02-23 01:07:07 +02:00
parent 88ee260cde
commit 8a5452f6a6
3 changed files with 312 additions and 244 deletions

View file

@ -1,98 +0,0 @@
use crate::*;
impl Dll {
pub fn collect_deps (&mut self, verbose: bool) -> Usually<(usize, usize)> {
let pe = self.pe.clone();
let directory = ImportDirectory::parse(pe.as_ref())?;
let mut modules = 0;
let mut methods = 0;
for descriptor in directory.descriptors.iter() {
let module_name = descriptor.get_name(pe.as_ref())?.as_str()?.to_lowercase();
let imp = descriptor.get_imports(pe.as_ref())?;
let iat = descriptor.get_first_thunk(pe.as_ref())?;
let ilt = descriptor.get_original_first_thunk(pe.as_ref())?;
let lut = descriptor.get_lookup_thunks(pe.as_ref())?;
let unwrap_thunk = |thunk: &Thunk, name|match thunk {
Thunk::Thunk32(t) => panic!("32 bit {name}"),
Thunk::Thunk64(t) => t.0
};
let mut buffer = vec![];
for (index, (import, thunk, orig, lookup)) in izip!(
imp,
iat.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "IAT thunk"))),
ilt.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "ILT (orig) thunk"))),
lut.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "lookup thunk"))),
).enumerate() {
buffer.push((index, import, thunk, orig, lookup));
}
for (index, import, thunk, orig, lookup) in buffer {
let (new_modules, new_methods) = self.collect_dep(
module_name.as_str(),
descriptor,
index,
import,
thunk,
orig,
lookup,
verbose
)?;
modules += new_modules;
methods += new_methods;
}
}
if verbose {
println!(" (deps-modules {modules})");
println!(" (deps-methods {methods})");
for (module, methods) in self.deps_by_library.iter() {
print!(" ({module}");
for (method, addr) in methods.iter() {
print!("\n (0x{addr:08x} {method})")
}
println!(")");
}
}
Ok((modules, methods))
}
pub fn collect_dep (
&mut self,
module_name: &str,
descriptor: &ImageImportDescriptor,
index: usize,
import: ImportData,
thunk: String,
orig: String,
lookup: String,
verbose: bool,
) -> Usually<(usize, usize)> {
let mut modules = 0;
let mut methods = 0;
let call_via = descriptor.first_thunk.0 + index as u32 * 8;
let method = match import {
ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"),
ImportData::ImportByName(name) => format!("{name}"),
};
let module_name: Arc<str> = module_name.clone().into();
if !self.deps_by_library.contains_key(&module_name) {
self.deps_by_library.insert(module_name.clone(), Default::default());
modules += 1;
}
let module = self.deps_by_library.get_mut(&module_name).unwrap();
let method: Arc<str> = method.clone().into();
if module.contains_key(&method) {
panic!("duplicate method {method} in {module_name}");
}
module.insert(method.clone(), call_via);
if self.deps_by_address.contains_key(&call_via) {
panic!("duplicate address {call_via} from {module_name}");
}
self.deps_by_address.insert(call_via, (module_name.clone(), method.clone()));
methods += 1;
if verbose {
println!(" (import 0x{call_via:08x} {module_name}::{method})");
}
Ok((modules, methods))
}
}

View file

@ -1,87 +1,105 @@
#![feature(slice_split_once)] #![feature(slice_split_once)]
mod util; pub(crate) use self::util::*; mod util; pub(crate) use self::util::*;
mod deps; pub(crate) use self::deps::*;
mod call; pub(crate) use self::call::*; mod call; pub(crate) use self::call::*;
mod show; pub(crate) use self::show::*; mod show; pub(crate) use self::show::*;
mod bang; mod bang;
/// Takes a 64-bit VST2 in PE format and attempts to
/// relink it with Wine and a custom shim to make it
/// into a standalone, self-contained 64-bit ELF JACK device.
fn main () -> Usually<()> { fn main () -> Usually<()> {
use clap::{arg, command, value_parser, ArgAction, Command}; use clap::{arg, command, value_parser, ArgAction, Command};
let matches = command!() // Parse command line arguments.
.arg(arg!([path] "Path to VST DLL").value_parser(value_parser!(PathBuf))) let matches = cli().get_matches();
//.arg(arg!(-s --stub <VALUE> "Provide a stub import").required(false))
.arg(arg!(-i --inspect "Show info, don't run").required(false))
.arg(arg!(-v --verbose "Show a lot of info").required(false))
.get_matches();
let path = matches.get_one::<PathBuf>("path").unwrap_or_else(||panic!("pass path to VST DLL")); let path = matches.get_one::<PathBuf>("path").unwrap_or_else(||panic!("pass path to VST DLL"));
let verbose = *(matches.get_one::<bool>("verbose").unwrap_or(&false)); let verbose = *(matches.get_one::<bool>("verbose").unwrap_or(&false));
let mut rebuilder = Vestal::new(&[ // Construct a rebuilder.
let mut rebuilder = Vestal::new(verbose, &[
// TODO allow user to specify search paths, system path, wineprefix...
std::env::current_dir()?, std::env::current_dir()?,
canonicalize(path.clone().parent().expect("invalid parent path"))?, canonicalize(path.clone().parent().expect("invalid parent path"))?,
"/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32".into(), "/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32".into(),
]); ]);
println!(); // Resolve input path.
for path in rebuilder.paths.iter() { let path = path.to_str().expect("path must be unicode");
println!("(search {path:?})") let path = rebuilder.find(path)?.unwrap_or_else(||panic!("# not found: {path:?}"));
} // Load with dependencies.
let path = rebuilder.find(path.to_str().expect("path must be unicode"), false)? let main = rebuilder.load_recurse(&path)?;
.unwrap_or_else(||panic!("Could not find: {path:?}")); rebuilder.resolve_recurse(&main)?;
let name = rebuilder.load(&path, true, verbose)?;
let main = rebuilder.dlls.get(&name).unwrap();
rebuilder.resolve_calls(&main, true, verbose)?;
Ok(()) Ok(())
} }
/// Define command line arguments.
fn cli () -> clap::Command {
use clap::{arg, command, value_parser, ArgAction, Command};
command!()
.arg(arg!([path] "Path to VST DLL").value_parser(value_parser!(PathBuf)))
//.arg(arg!(-s --stub <VALUE> "Provide a stub import").required(false))
.arg(arg!(-i --inspect "Show info, don't run").required(false))
.arg(arg!(-v --verbose "Show a lot of info").required(false))
}
/// Application state
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct Vestal { struct Vestal {
/// More detailed output.
verbose: bool,
/// Search paths /// Search paths
paths: BTreeSet<Arc<PathBuf>>, paths: BTreeSet<Arc<PathBuf>>,
/// All DLLs in scope
dlls: BTreeMap<Arc<str>, Arc<Dll>>,
/// Paths visited /// Paths visited
visited: BTreeSet<Arc<PathBuf>>, visited: BTreeSet<Arc<PathBuf>>,
/// All DLLs in scope
dlls: BTreeMap<Arc<str>, Arc<Dll>>,
} }
impl Vestal { impl Vestal {
fn new (paths: &[impl AsRef<Path>]) -> Self { /// Construct a rebuilder
Self { fn new (verbose: bool, paths: &[impl AsRef<Path>]) -> Self {
paths: paths.iter().map(|x|Arc::new(x.as_ref().into())).collect(), let paths: BTreeSet<_> = paths.iter().map(|x|Arc::new(x.as_ref().into())).collect();
..Default::default() if verbose {
// Report search paths
for path in paths.iter() {
println!("(search {path:?})")
} }
} }
fn load (&mut self, path: &impl AsRef<Path>, recurse: bool, verbose: bool) -> Usually<Arc<str>> { Self { verbose, paths, ..Default::default() }
}
/// Load all dependencies recursively, starting from the given path.
fn load_recurse (&mut self, path: &impl AsRef<Path>) -> Usually<Arc<Dll>> {
let dll = self.load(path)?;
for dep in dll.deps_by_library.keys() {
let dep_path = self.find(dep)?.unwrap_or_else(||panic!("not found: {dep:?}"));
if !self.visited.contains(&dep_path) {
self.load_recurse(&dep_path)?;
}
}
Ok(dll)
}
fn load (&mut self, path: &impl AsRef<Path>) -> Usually<Arc<Dll>> {
let path: Arc<PathBuf> = Arc::from(PathBuf::from(path.as_ref())); let path: Arc<PathBuf> = Arc::from(PathBuf::from(path.as_ref()));
if self.visited.contains(&path) { if self.visited.contains(&path) {
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");
return Ok(name) return Ok(self.dlls.get(&name).unwrap().clone())
} }
if verbose { if self.verbose {
println!("(load {path:?})"); println!("(load {path:?})");
} }
self.visited.insert(path.clone()); self.visited.insert(path.clone());
let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), verbose)?); let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), self.verbose)?);
self.dlls.insert(dll.name.clone(), dll.clone()); self.dlls.insert(dll.name.clone(), dll.clone());
if recurse { Ok(dll)
for dep in dll.deps_by_library.keys() {
if let Some(dep) = self.find(dep, verbose)? {
self.load(&dep, recurse, verbose)?;
} else {
panic!("not found: {dep:?}");
} }
}
} fn find (&self, name: &str) -> Usually<Option<PathBuf>> {
Ok(dll.name.clone())
}
fn find (&self, name: &str, verbose: bool) -> Usually<Option<PathBuf>> {
for base in self.paths.iter() { for base in self.paths.iter() {
let mut path = base.as_ref().clone(); let mut path = base.as_ref().clone();
path.push(name.to_lowercase()); path.push(name.to_lowercase());
if verbose { if self.verbose {
println!("# looking for {name} at {path:?}"); println!("# looking for {name} at {path:?}");
} }
if std::fs::exists(&path)? { if std::fs::exists(&path)? {
if verbose { if self.verbose {
println!("# found {name} at {path:?}"); println!("# found {name} at {path:?}");
} }
@ -90,67 +108,110 @@ impl Vestal {
} }
Ok(None) Ok(None)
} }
fn resolve_calls (&self, dll: &Dll, recurse: bool, verbose: bool) -> Usually<()> {
fn resolve (&self, dll: &Dll) -> Usually<()> {
println!("{:11}{BOLD}{}{RESET}", "", dll.name); println!("{:11}{BOLD}{}{RESET}", "", dll.name);
for (addr, call) in dll.calls_by_source.iter() { for (addr, call) in dll.calls_by_source.iter() {
self.resolve_call(dll, *addr, call, recurse, verbose)?; self.resolve_call(dll, *addr, call)?;
} }
Ok(()) Ok(())
} }
fn resolve_call (
&self, fn resolve_recurse (&self, dll: &Dll) -> Usually<()> {
dll: &Dll, println!("{:11}{BOLD}{}{RESET}", "", dll.name);
addr: u32, for (addr, call) in dll.calls_by_source.iter() {
call: &Arc<Call>, self.resolve_call_recurse(dll, *addr, call)?;
recurse: bool, }
verbose: bool, Ok(())
) -> Usually<()> { }
fn resolve_call (&self, dll: &Dll, addr: u32, call: &Arc<Call>) -> Usually<()> {
let addr = (addr - dll.code_base) as usize; let addr = (addr - dll.code_base) as usize;
dll.show_call_site(addr, call.length, 1); dll.show_call_site(addr, call.length, 1);
Show::call_module_method(Some(call)); call.show_call(&dll.name);
if let Some(method) = dll.parse_call(call) { if let Some(method) = dll.parse_call(call) {
let module_name = call.module.as_ref().unwrap(); let module_name = call.module.as_ref().unwrap();
let method_name = call.method.as_ref().unwrap(); let method_name = call.method.as_ref().unwrap();
let path = self.find(module_name, false)? let path = self.find(module_name)?.unwrap_or_else(||panic!("# not found: {module_name}"));
.unwrap_or_else(||panic!("# not found: {module_name}")); let name = path.file_name().expect("no file name");
let name = path.file_name() let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
.expect("no file name"); let dll = self.dlls.get(&name).unwrap_or_else(||panic!("# not found: {name}"));
let name: Arc<str> = name.to_str().map(Arc::from)
.expect("non-unicode filename");
let dll = self.dlls.get(&name)
.unwrap_or_else(||panic!("# not found: {name}"));
match dll.exports.get(method_name) {
Some(ThunkData::Function(rva)) => {
let addr = (rva.0 - dll.code_base) as usize;
if recurse {
self.resolve_call_recurse(dll, addr, module_name, method_name)?;
} }
}, Ok(())
None => panic!("# not found: {method_name}"), }
thunk => panic!("# unsupported {thunk:?}"),
fn resolve_call_recurse (&self, dll: &Dll, addr: u32, call: &Arc<Call>) -> Usually<()> {
let addr = (addr - dll.code_base) as usize;
dll.show_call_site(addr, call.length, 1);
call.show_call(&dll.name);
if let Some(method) = dll.parse_call(call) {
let module_name = call.module.as_ref().unwrap();
let method_name = call.method.as_ref().unwrap();
let path = self.find(module_name)?.unwrap_or_else(||panic!("# not found: {module_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 dll = self.dlls.get(&name).unwrap_or_else(||panic!("# not found: {name}"));
let export = dll.exports.get(method_name);
if let Some(ThunkData::Function(rva)) = export {
self.resolve_call_recurse_into(
//dll,
//(rva.0 - dll.code_base) as usize,
module_name,
method_name,
)?;
} else if let Some(ThunkData::ForwarderString(rva)) = export {
let mut addr = (rva.0 - dll.code_base) as usize;
let mut forward = vec![];
while let Some(c) = dll.pe.as_slice().get(addr) {
if *c == 0x00 {
break
}
forward.push(*c);
addr += 1;
}
let forward = String::from_utf8(forward)?;
if let Some((module, method)) = forward.as_str().split_once(".") {
self.resolve_call_recurse_into(
//dll,
//addr,
&format!("{}.dll", module.to_lowercase()).into(),
&method.into(),
)?;
} else {
panic!("# invalid forward {}", &forward);
}
} else if let Some(thunk) = export {
panic!("# unsupported {thunk:?}");
} else {
panic!("# not found: {method_name}");
} }
} }
Ok(()) Ok(())
} }
fn resolve_call_recurse (
fn resolve_call_recurse_into (
&self, &self,
dll: &Dll, //dll: &Dll,
address: usize, //address: usize,
module_name: &Arc<str>, module_name: &Arc<str>,
method_name: &Arc<str>, method_name: &Arc<str>,
) -> Usually<()> { ) -> Usually<()> {
let mut decoder = Decoder::with_ip(64, &dll.text_section[address..], 0, DecoderOptions::NONE); let module = self.dlls.get(module_name).unwrap_or_else(||panic!("# no dll {module_name}"));
while decoder.can_decode() { let method = module.exports.get(method_name).unwrap_or_else(||panic!("# no export {method_name}"));
let position = decoder.position(); //println!("{} {} {:?}", module_name, method_name, method);
if let Some(call) = dll.collect_call(&mut decoder, true)? { //let mut decoder = Decoder::with_ip(64, &dll.text_section[address..], 0, DecoderOptions::NONE);
println!("(call {call:#?}"); //while decoder.can_decode() {
} //let position = decoder.position();
if dll.text_section[address + position] == 0xc3 { //if let Some(call) = dll.collect_call(&mut decoder, true)? {
break //println!("(call {call:#?}");
} //}
} //if dll.text_section[address + position] == 0xc3 {
//break
//}
//}
Ok(()) Ok(())
} }
} }
struct Dll { struct Dll {
@ -182,15 +243,166 @@ struct Dll {
exports: BTreeMap<Arc<str>, ThunkData>, exports: BTreeMap<Arc<str>, ThunkData>,
} }
impl Dll {
fn new (path: &Arc<PathBuf>, verbose: bool) -> Usually<Self> {
if verbose {
println!("\n(load {BOLD}{path:?}{RESET})");
}
let (name, pe, data, bang) = Self::read_pe(path)?;
let (text_section, text_section_start, text_section_size) = Self::read_text_section(&pe)?;
let mut dll = Self {
bang,
name: name.clone(),
path: path.clone(),
text_section,
text_section_start,
text_section_size,
code_base: match pe.get_valid_nt_headers()? {
NTHeaders::NTHeaders32(h32) => panic!("32 bit headers"),
NTHeaders::NTHeaders64(h64) => h64.optional_header.base_of_code.0,
},
pe,
exports: Default::default(),
deps_by_library: Default::default(),
deps_by_address: Default::default(),
calls_by_source: Default::default(),
calls_by_target: Default::default(),
};
let (_modules_count, _methods_count) = dll.collect_deps(verbose)?;
let _calls = dll.collect_calls(verbose)?;
let _exports = dll.collect_exports(verbose)?;
println!("{dll:?}");
Ok(dll)
}
fn read_pe (path: &Arc<PathBuf>) -> Usually<(Arc<str>, Arc<VecPE>, Arc<[u8]>, Arc<[u8]>)> {
let name = path.as_ref().file_name().expect("no file name");
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 pe = Arc::new(VecPE::from_disk_data(data.clone()));
Ok((name, pe, data, bang))
}
fn read_text_section (pe: &VecPE) -> Usually<(Arc<[u8]>, usize, usize)> {
let code = pe.get_section_by_name(".text")?;
let start = code.pointer_to_raw_data.0 as usize;
let size = code.size_of_raw_data as usize;
let text = &pe.as_slice()[start..start+size];
Ok((text.into(), start, size))
}
fn collect_exports (&mut self, _verbose: bool) -> Usually<usize> {
let directory = ExportDirectory::parse(self.pe.as_ref())?;
let export_map = directory.get_export_map(self.pe.as_ref())?;
self.exports = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect();
Ok(self.exports.len())
}
pub fn collect_deps (&mut self, verbose: bool) -> Usually<(usize, usize)> {
let pe = self.pe.clone();
let directory = ImportDirectory::parse(pe.as_ref())?;
let mut modules = 0;
let mut methods = 0;
let unwrap_thunk = |thunk: &Thunk, name|match thunk {
Thunk::Thunk32(t) => panic!("32 bit {name}"),
Thunk::Thunk64(t) => t.0
};
for descriptor in directory.descriptors.iter() {
let name = descriptor.get_name(pe.as_ref())?.as_str()?.to_lowercase();
let imp = descriptor.get_imports(pe.as_ref())?;
let iat = descriptor.get_first_thunk(pe.as_ref())?;
let ilt = descriptor.get_original_first_thunk(pe.as_ref())?;
let lut = descriptor.get_lookup_thunks(pe.as_ref())?;
let mut buffer = vec![];
for (index, (import, thunk, orig, lookup)) in izip!(
imp,
iat.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "IAT thunk"))),
ilt.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "ILT (orig) thunk"))),
lut.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "lookup thunk"))),
).enumerate() {
buffer.push((index, import, thunk, orig, lookup));
}
for (index, import, thunk, orig, lookup) in buffer {
let (new_modules, new_methods) = self.collect_dep(
name.as_str(),
descriptor,
index,
import,
thunk,
orig,
lookup,
verbose
)?;
modules += new_modules;
methods += new_methods;
}
}
if verbose {
println!(" (deps-modules {modules})");
println!(" (deps-methods {methods})");
for (module, methods) in self.deps_by_library.iter() {
print!(" ({module}");
for (method, addr) in methods.iter() {
print!("\n (0x{addr:08x} {method})")
}
println!(")");
}
}
Ok((modules, methods))
}
pub fn collect_dep (
&mut self,
module_name: &str,
descriptor: &ImageImportDescriptor,
index: usize,
import: ImportData,
thunk: String,
orig: String,
lookup: String,
verbose: bool,
) -> Usually<(usize, usize)> {
let mut modules = 0;
let mut methods = 0;
let call_via = descriptor.first_thunk.0 + index as u32 * 8;
let method = match import {
ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"),
ImportData::ImportByName(name) => format!("{name}"),
};
let module_name: Arc<str> = module_name.clone().into();
if !self.deps_by_library.contains_key(&module_name) {
self.deps_by_library.insert(module_name.clone(), Default::default());
modules += 1;
}
let module = self.deps_by_library.get_mut(&module_name).unwrap();
let method: Arc<str> = method.clone().into();
if module.contains_key(&method) {
panic!("duplicate method {method} in {module_name}");
}
module.insert(method.clone(), call_via);
if self.deps_by_address.contains_key(&call_via) {
panic!("duplicate address {call_via} from {module_name}");
}
self.deps_by_address.insert(call_via, (module_name.clone(), method.clone()));
methods += 1;
if verbose {
println!(" (import 0x{call_via:08x} {module_name}::{method})");
}
Ok((modules, methods))
}
}
impl std::fmt::Debug for Dll { impl std::fmt::Debug for Dll {
fn fmt (&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { fn fmt (&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
let deps = format!("deps (lib {}) (addr {:3})", let deps = format!("(deps lib {:>4} addr {:>4})",
self.deps_by_library.len(), self.deps_by_library.len(),
self.deps_by_address.len()); self.deps_by_address.len());
let calls = format!("calls (src {:4}) (tgt {:4})", let calls = format!("(calls src {:>4} tgt {:>4})",
self.calls_by_source.len(), self.calls_by_source.len(),
self.calls_by_target.len()); self.calls_by_target.len());
let exports = format!("exp {}", let exports = format!("(exp {:>5})",
self.exports.len()); self.exports.len());
write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] (img 0x{:>08x} -> mem 0x{:>08x}) {deps} {calls} {exports})", write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] (img 0x{:>08x} -> mem 0x{:>08x}) {deps} {calls} {exports})",
&self.name, &self.name,
@ -199,52 +411,3 @@ impl std::fmt::Debug for Dll {
self.code_base) self.code_base)
} }
} }
impl Dll {
fn new (path: &Arc<PathBuf>, verbose: bool) -> Usually<Self> {
if verbose {
println!("\n(load {BOLD}{path:?}{RESET})");
}
let (name, pe, data, bang) = Self::read_pe(path)?;
let code = pe.get_section_by_name(".text")?;
let start = code.pointer_to_raw_data.0 as usize;
let size = code.size_of_raw_data as usize;
let text = &data[start..start+size];
let mut dll = Self {
bang,
name: name.clone(),
path: path.clone(),
exports: Default::default(),
deps_by_library: Default::default(),
deps_by_address: Default::default(),
calls_by_source: Default::default(),
calls_by_target: Default::default(),
text_section: Arc::from(text),
text_section_start: start,
text_section_size: size,
code_base: match pe.get_valid_nt_headers()? {
NTHeaders::NTHeaders32(h32) => panic!("32 bit headers"),
NTHeaders::NTHeaders64(h64) => h64.optional_header.base_of_code.0,
},
pe,
};
let (_modules_count, _methods_count) = dll.collect_deps(verbose)?;
let _calls = dll.collect_calls(verbose)?;
println!("{dll:?}");
let _exports = dll.collect_exports(verbose)?;
Ok(dll)
}
fn read_pe (path: &Arc<PathBuf>) -> Usually<(Arc<str>, Arc<VecPE>, Arc<[u8]>, Arc<[u8]>)> {
let name = path.as_ref().file_name().expect("no file name");
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 pe = Arc::new(VecPE::from_disk_data(data.clone()));
Ok((name, pe, data, bang))
}
fn collect_exports (&mut self, _verbose: bool) -> Usually<usize> {
let directory = ImageExportDirectory::parse(self.pe.as_ref())?;
let export_map = directory.get_export_map(self.pe.as_ref())?;
self.exports = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect();
Ok(self.exports.len())
}
}

View file

@ -61,7 +61,22 @@ impl Dll {
write!(&mut output, " \n"); write!(&mut output, " \n");
} }
} }
print!("{output}"); print!("\n{output}");
}
}
impl Call {
pub fn show_call (&self, name: &Arc<str>) {
let module = self.module.as_ref();
let method = self.method.as_ref();
if module.is_some() || method.is_some() {
println!("╰--------> {name} -> {GREEN}{}{}{}{RESET}",
module.map(|x|x.as_ref()).unwrap_or(&""),
if module.is_some() && method.is_some() { "::" } else { "" },
method.map(|x|x.as_ref()).unwrap_or(&""));
} else {
println!("╰--------> {name} -> {RED}(unresolved){RESET} {self:?}");
}
} }
} }
@ -78,18 +93,6 @@ impl Show {
call.map(|call|format!(" (target {})", Show::num(call.target as usize))) call.map(|call|format!(" (target {})", Show::num(call.target as usize)))
.unwrap_or(String::new())); .unwrap_or(String::new()));
} }
pub fn call_module_method (call: Option<&Arc<Call>>) {
let module = call.map(|call|call.module.as_ref()).flatten();
let method = call.map(|call|call.method.as_ref()).flatten();
if module.is_some() || method.is_some() {
println!("╰--------> {GREEN}{}{}{}{RESET}",
module.map(|x|x.as_ref()).unwrap_or(&""),
if module.is_some() && method.is_some() { "::" } else { "" },
method.map(|x|x.as_ref()).unwrap_or(&""));
} else {
println!("╰--------> {RED}(unresolved){RESET} {call:?}");
}
}
pub fn call_dasm (bytes: &[u8], rip: usize) -> Arc<str> { pub fn call_dasm (bytes: &[u8], rip: usize) -> Arc<str> {
let mut decoder = Decoder::with_ip(64, bytes, 0x1000 + rip as u64, DecoderOptions::NONE); let mut decoder = Decoder::with_ip(64, bytes, 0x1000 + rip as u64, DecoderOptions::NONE);
while decoder.can_decode() { while decoder.can_decode() {