From 0a1b74b2ae55ece55b1de4e40f988c639afbefea Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 23 Feb 2025 21:40:41 +0200 Subject: [PATCH] good entrypoint for async rust --- crates/.scratch.rs | 85 +++++++++++++++++++++++++++++++++ crates/vestal/src/call_sites.rs | 74 +++++++++++++++++++--------- crates/vestal/src/exports.rs | 5 +- crates/vestal/src/imports.rs | 11 +++-- crates/vestal/src/main.rs | 84 ++++---------------------------- crates/vestal/src/show.rs | 23 +++++---- 6 files changed, 170 insertions(+), 112 deletions(-) diff --git a/crates/.scratch.rs b/crates/.scratch.rs index ad6c3ff..d94163b 100644 --- a/crates/.scratch.rs +++ b/crates/.scratch.rs @@ -434,3 +434,88 @@ impl Default for AEffect { } Ok(()) } + /// Relink a VST. + //fn run (&mut self, path: impl AsRef) -> Usually<()> { + //let path = path.as_ref().to_str().expect("path must be unicode"); + //let path = self.find(path)?.unwrap_or_else(||panic!("# not found: {path:?}")); + //let main = self.load(&path)?; + //Ok(()) + //} + /// Load all dependencies recursively, starting from the given path. + //fn load_all (&mut self, path: &impl AsRef) -> Usually> { + //let name = to_dll_name(path); + //let path: Arc = Arc::from(PathBuf::from(path.as_ref())); + //if self.modules.contains_key(&name) { + //let module = self.modules.get(&name).unwrap().clone(); + //return Ok(module) + //} + //Log::load(self.verbose, &path); + //let module = Arc::new(Module::new(&path, self.verbose)?.load(self.verbose)?); + //self.modules.insert(module.name.clone(), module.clone()); + //Ok(module) + //} + //fn resolve (&self, dll: &Module, addr: u32, link: &Arc) -> Usually<()> { + //let addr = (addr - dll.code_base) as usize; + //dll.show_call_site(addr, link.length, 1); + //link.show(); + //Ok(()) + //} + //fn resolve_recursively (&self, dll: &Module, addr: u32, link: &Arc) -> Usually<()> { + //self.resolve(dll, addr, link)?; + //let module = &link.target.name; + //let export = &link.method; + //if let Some((module, export)) = match self.modules + //.get(module) + //.map(|module|module.exports.get(export)) + //{ + //Some(Some(ThunkData::Function(rva))) => Some((module.clone(), export.clone())), + //Some(Some(ThunkData::ForwarderString(rva))) => dll.resolve_forward(&rva)?, + //Some(Some(thunk)) => panic!("# unsupported {thunk:?}"), + //Some(None) => panic!("# export not resolved: {export}"), + //None => panic!("# module not resolved: {module}"), + //} { + //let module = self.modules.get(&module).unwrap_or_else(||panic!("# no module {module}")); + //let method = module.exports.get(&export).unwrap_or_else(||panic!("# no method {export}")); + //println!("{module:?} {export} {method:?}"); + //} else { + //panic!("# unresolved link at {:?} [{addr}]", &dll.name) + //} + //Ok(()) + //} + +/// Collect an imported dependency's descriptor thunks into a temporary [Vec]. +/// +/// Most of these are currently discarded, but may be needed in the future +/// to resolve some of the trickier linkings. +fn import_collect (pe: &VecPE, descriptor: &ImageImportDescriptor) + -> Usually<(Arc, Vec<(usize, Arc)>)> +{ + let mut buffer = vec![]; + let name = descriptor.get_name(pe)?.as_str()?.to_lowercase(); + for (index, import) in descriptor.get_imports(pe)?.iter().enumerate() { + buffer.push((index, match import { + ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"), + ImportData::ImportByName(name) => format!("{name}"), + }.into())); + } + Ok((name.into(), buffer)) + //let imp = descriptor.get_imports(pe)?; + //let iat = descriptor.get_first_thunk(pe)?; + //let ilt = descriptor.get_original_first_thunk(pe)?; + //let lut = descriptor.get_lookup_thunks(pe)?; + //for (index, (import, thunk, orig, lookup)) in izip!( + //imp, + //iat.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "IAT thunk"))), + //ilt.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "ILT (orig) thunk"))), + //lut.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "lookup thunk"))), + //).enumerate() { + //buffer.push((index, import));//, thunk, orig, lookup)); + //} +} + +//fn thunk_unwrap (thunk: &Thunk, name: impl AsRef) -> u64 { + //match thunk { + //Thunk::Thunk32(t) => panic!("32 bit {}", name.as_ref()), + //Thunk::Thunk64(t) => t.0 + //} +//} diff --git a/crates/vestal/src/call_sites.rs b/crates/vestal/src/call_sites.rs index c693041..02f08cb 100644 --- a/crates/vestal/src/call_sites.rs +++ b/crates/vestal/src/call_sites.rs @@ -1,4 +1,5 @@ use crate::*; + impl Module { /// Collect all calls that point to imports. pub fn load_call_sites (self: Arc) -> Usually> { @@ -14,28 +15,6 @@ impl Module { } Ok(self) } - fn target (opcodes: &[u8], offset_rva: u32) -> Option { - let rip = offset_rva + opcodes.len() as u32; - match opcodes[0] { - 0xff => match opcodes[1] { - 0x15 | 0x25 => return Some(rip + u32::from_le_bytes([ - opcodes[2], opcodes[3], opcodes[4], opcodes[5] - ])), - _ => {} - }, - 0x48 => match opcodes[1] { - 0xff => match opcodes[2] { - 0x15 | 0x25 => return Some(rip + u32::from_le_bytes([ - opcodes[3], opcodes[4], opcodes[5], opcodes[6] - ])), - _ => {} - }, - _ => {} - } - _ => {} - } - None - } fn parse_link (&self, link: &Arc) -> Option { None @@ -60,3 +39,54 @@ impl Module { .map(|(x, y)|(x.into(), y.into()))) } } + +impl CallSite { + pub fn matches (instruction: &Instruction) -> bool { + instruction.op0_kind() == OpKind::Memory && ( + instruction.flow_control() == FlowControl::IndirectBranch || + instruction.flow_control() == FlowControl::IndirectCall + ) + } + pub fn skip (opcodes: &[u8]) -> bool { + match opcodes[0] { + 0x41 | 0x42 | 0x43 | 0x49 => match opcodes[1] { + 0xff => return true, + _ => {} + }, + 0x48 => match opcodes[2] { + 0x20 | 0x60 | 0x62 | 0xa0 | 0xa2 => return true, + _ => {} + }, + 0xff => match opcodes[1] { + 0x10 | 0x12 | 0x13 | + 0x50 | 0x51 | 0x52 | 0x53 | 0x54 | 0x55 | 0x56 | 0x57 | + 0x60 | 0x90 | 0x92 | 0x93 | 0x94 | 0x97 => return true, + _ => {} + }, + _ => {} + } + false + } + pub fn target (opcodes: &[u8], offset_rva: u32) -> Option { + let rip = offset_rva + opcodes.len() as u32; + match opcodes[0] { + 0xff => match opcodes[1] { + 0x15 | 0x25 => return Some(rip + u32::from_le_bytes([ + opcodes[2], opcodes[3], opcodes[4], opcodes[5] + ])), + _ => {} + }, + 0x48 => match opcodes[1] { + 0xff => match opcodes[2] { + 0x15 | 0x25 => return Some(rip + u32::from_le_bytes([ + opcodes[3], opcodes[4], opcodes[5], opcodes[6] + ])), + _ => {} + }, + _ => {} + } + _ => {} + } + None + } +} diff --git a/crates/vestal/src/exports.rs b/crates/vestal/src/exports.rs index 64afafd..79d268b 100644 --- a/crates/vestal/src/exports.rs +++ b/crates/vestal/src/exports.rs @@ -2,9 +2,10 @@ use crate::*; impl Module { /// Collect all exported methods. pub fn load_exports (self: Arc) -> Usually> { - let directory = ExportDirectory::parse(self.pe.as_ref())?; + let directory = ExportDirectory::parse(self.pe.as_ref())?; let export_map = directory.get_export_map(self.pe.as_ref())?; - *self.exports.write().unwrap() = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect(); + let exports = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect(); + *self.exports.write().unwrap() = exports; Ok(self) } } diff --git a/crates/vestal/src/imports.rs b/crates/vestal/src/imports.rs index aa29893..b3c8fd2 100644 --- a/crates/vestal/src/imports.rs +++ b/crates/vestal/src/imports.rs @@ -4,12 +4,12 @@ impl Module { pub fn load_imports (self: Arc, recurse: bool) -> Usually> { let pe = self.pe.clone(); let directory = ImportDirectory::parse(pe.as_ref())?; + let mut new_modules = vec![]; for descriptor in directory.descriptors.iter() { let (name, imports) = Self::parse_import(pe.as_ref(), descriptor)?; - Log::dep(self.verbose, &name, &imports); let exists = self.namespace.read().unwrap().contains_key(&name); - println!("{exists} {name} {:?}", self.namespace.read().unwrap().keys().collect::>()); if !exists { + Log::dep(false, &name, &imports); let path = self.find(&name)?.expect("not found"); let module = Arc::new(Self { namespace: self.namespace.clone(), @@ -17,7 +17,12 @@ impl Module { ..Self::from_path(&path, self.verbose)? }); self.namespace.write().unwrap().insert(module.name.clone(), module.clone()); - module.load(recurse); + new_modules.push(module); + } + } + if recurse { + for module in new_modules { + module.load(recurse)?; } } if self.verbose { diff --git a/crates/vestal/src/main.rs b/crates/vestal/src/main.rs index 70fdb05..fb0139e 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -119,10 +119,14 @@ impl Module { } /// Load the dependency tree, starting from this module. fn load (self: Arc, recurse: bool) -> Usually> { - Ok(self - .load_imports(recurse)? + let module = self .load_exports()? - .load_call_sites()?) + .load_imports(recurse)? + .load_call_sites()?; + if module.verbose { + println!("{:?}", &module); + } + Ok(module) } /// Search for DLL by name in search paths. fn find (&self, name: &impl AsRef) -> Usually> { @@ -130,13 +134,9 @@ impl Module { for base in self.search_paths.read().unwrap().iter() { let mut path = base.as_ref().clone(); path.push(name.to_lowercase()); - if self.verbose { - println!("(find? {name} {path:?})"); - } + Log::find(false, &name, &path); if std::fs::exists(&path)? { - if self.verbose { - println!("(found {name} {path:?})"); - } + Log::found(false, &name, &path); return Ok(Some(canonicalize(&path)?)) } } @@ -172,69 +172,3 @@ fn base_of_code (pe: &VecPE) -> Usually { NTHeaders::NTHeaders64(h64) => h64.optional_header.base_of_code.0, }) } - -/// Collect an imported dependency's descriptor thunks into a temporary [Vec]. -/// -/// Most of these are currently discarded, but may be needed in the future -/// to resolve some of the trickier linkings. -fn import_collect (pe: &VecPE, descriptor: &ImageImportDescriptor) - -> Usually<(Arc, Vec<(usize, Arc)>)> -{ - let mut buffer = vec![]; - let name = descriptor.get_name(pe)?.as_str()?.to_lowercase(); - for (index, import) in descriptor.get_imports(pe)?.iter().enumerate() { - buffer.push((index, match import { - ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"), - ImportData::ImportByName(name) => format!("{name}"), - }.into())); - } - Ok((name.into(), buffer)) - //let imp = descriptor.get_imports(pe)?; - //let iat = descriptor.get_first_thunk(pe)?; - //let ilt = descriptor.get_original_first_thunk(pe)?; - //let lut = descriptor.get_lookup_thunks(pe)?; - //for (index, (import, thunk, orig, lookup)) in izip!( - //imp, - //iat.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "IAT thunk"))), - //ilt.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "ILT (orig) thunk"))), - //lut.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "lookup thunk"))), - //).enumerate() { - //buffer.push((index, import));//, thunk, orig, lookup)); - //} -} - -//fn thunk_unwrap (thunk: &Thunk, name: impl AsRef) -> u64 { - //match thunk { - //Thunk::Thunk32(t) => panic!("32 bit {}", name.as_ref()), - //Thunk::Thunk64(t) => t.0 - //} -//} - -impl CallSite { - fn matches (instruction: &Instruction) -> bool { - instruction.op0_kind() == OpKind::Memory && ( - instruction.flow_control() == FlowControl::IndirectBranch || - instruction.flow_control() == FlowControl::IndirectCall - ) - } - fn skip (opcodes: &[u8]) -> bool { - match opcodes[0] { - 0x41 | 0x42 | 0x43 | 0x49 => match opcodes[1] { - 0xff => return true, - _ => {} - }, - 0x48 => match opcodes[2] { - 0x20 | 0x60 | 0x62 | 0xa0 | 0xa2 => return true, - _ => {} - }, - 0xff => match opcodes[1] { - 0x10 | 0x12 | 0x13 | - 0x50 | 0x51 | 0x52 | 0x53 | 0x54 | 0x55 | 0x56 | 0x57 | - 0x60 | 0x90 | 0x92 | 0x93 | 0x94 | 0x97 => return true, - _ => {} - }, - _ => {} - } - false - } -} diff --git a/crates/vestal/src/show.rs b/crates/vestal/src/show.rs index 5e62158..ee09ad6 100644 --- a/crates/vestal/src/show.rs +++ b/crates/vestal/src/show.rs @@ -21,7 +21,7 @@ impl Log { } pub fn load (show: bool, path: &impl AsRef) { if show { - println!("(load {:?})", path.as_ref()); + println!("{DIM}(load {:?}){RESET}", path.as_ref()); } } pub fn dep (show: bool, name: &impl AsRef, exports: &[(usize, Arc)]) { @@ -29,6 +29,16 @@ impl Log { println!("(dep {:?} {})", name.as_ref(), exports.len()); } } + pub fn find (show: bool, name: &impl AsRef, path: &impl AsRef) { + if show { + println!("(find? {} {:?})", name.as_ref(), path.as_ref()); + } + } + pub fn found (show: bool, name: &impl AsRef, path: &impl AsRef) { + if show { + println!("(found {} {:?})", name.as_ref(), path.as_ref()); + } + } } impl Show { @@ -52,17 +62,10 @@ impl Show { impl std::fmt::Debug for Module { fn fmt (&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { - let deps = "";// format!("(deps lib {:>4} addr {:>4})", - //self.dependencies.len(), - //self.deps_by_address.len()); - let links = "";// format!("(links src {:>4} tgt {:>4})", - //self.links_by_source.len(), - //self.links_by_target.len()); - let exports = format!("(exp {:>5})", - self.exports.read().unwrap().len()); - write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] (img 0x{:>08x} -> mem 0x{:>08x}) {deps} {links} {exports})", + write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] {} (img 0x{:>08x} -> mem 0x{:>08x}))", &self.name, self.code_size, + format!("({:>5})", self.exports.read().unwrap().len()), self.code_start, self.code_base) }