use crate::*; /////////////////////////////////////////////////////////////////////////////////////////////////// #[derive(Default, Debug)] struct Vestal { search_paths: Vec, paths_visited: BTreeSet>, path_to_bang: BTreeMap, Arc<[u8]>>, path_to_data: BTreeMap, Arc<[u8]>>, path_to_pe: BTreeMap, Arc>, path_to_rpe: BTreeMap, Arc>, addr_to_import: BTreeMap, //path_to_exports: BTreeMap, Vec>, //path_to_imports: BTreeMap, Vec>, } impl Vestal { fn enter (&mut self, path: &PathBuf) -> Usually<()> { let mut total = 0usize; self.load(&path)?; for (dll_path, dll) in self.path_to_pe.iter() { self.show_dll(dll_path)?; let dll_data = self.path_to_data.get(dll_path).unwrap(); let len = dll_data.len(); println!(" (bytes {len} 0x{len:x})"); self.show_calls(dll_path, dll_path.as_ref() == path)?; if dll_path.as_ref() == path { println!("{:?}", dll_data.hex_dump()); let text = dll.get_section_by_name(".text")?; println!("\n{:#?}", text); let start = text.pointer_to_raw_data.0 as usize; let size = text.size_of_raw_data as usize; let vsize = text.virtual_size as usize; println!("\n{:?}", &dll_data[start..start+size].hex_dump()); println!("\n{:?}", &dll_data[start..start+vsize].hex_dump()); let elf = crate::link::create_elf(&dll_data[start..start+vsize]); println!("\n{:?}", &elf.hex_dump()); println!("\n{:?}", &elf.len()); let mut options = std::fs::OpenOptions::new(); options.write(true).create(true).mode(0o755); let mut file = options.open("output")?; file.write_all(&elf)?; println!("\nDone."); } total += len; //println!("{:?}", dll_data.hex_dump()); } //self.show_addr_to_import(); println!("(bytes-total {total} (0x{total:x})"); Ok(()) } } impl Vestal { pub fn resolve (&self, name: &str) -> Usually> { for base in self.search_paths.iter() { let mut path = base.clone(); path.push(name.to_lowercase()); if std::fs::exists(&path)? { return Ok(Some(canonicalize(&path)?)) } } Ok(None) } pub fn load (&mut self, path: &PathBuf) -> Usually<()> { if !self.paths_visited.contains(path) { let path = Arc::new(path.clone()); println!("\n(load {path:?})"); self.paths_visited.insert(path.clone()); let data = self.load_bang_data(&path)?; let dll = self.load_pe(&path, &data)?; //let rdll = self.load_rpe(&path, &dll, 0)?; let imports = self.load_imports(&path, &dll); let exports = self.load_exports(&path, &dll); } Ok(()) } pub fn load_imports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { let mut import_map = BTreeMap::new(); let directory = ImportDirectory::parse(dll)?; for descriptor in directory.descriptors { let dep = descriptor.get_name(dll)?.as_str()?; let iat = descriptor.get_first_thunk(dll)?; let ilt = descriptor.get_original_first_thunk(dll)?; let lut = descriptor.get_lookup_thunks(dll)?; let resolved = Arc::new(self.resolve(&dep)?.expect("no path for {name}")); print!(" (module {BOLD}{dep:?}{RESET} N=0x{:>08x} IAT=0x{:>08x} ILT=0x{:>08x}\n {resolved:?}", &descriptor.name.0, &descriptor.first_thunk.0, &descriptor.original_first_thunk.0); let mut imports = Vec::new(); for (index, (thunk, orig, lookup, import)) in izip!( iat.iter().map(|thunk|format!("0x{:08x}", match thunk { Thunk::Thunk32(t) => panic!("32 bit thunk"), Thunk::Thunk64(t) => t.0 })), ilt.iter().map(|thunk|format!("0x{:08x}", match thunk { Thunk::Thunk32(t) => panic!("32 bit original thunk"), Thunk::Thunk64(t) => t.0 })), lut.iter().map(|thunk|format!("0x{:08x}", match thunk { Thunk::Thunk32(t) => panic!("32 bit original thunk"), Thunk::Thunk64(t) => t.0 })), descriptor.get_imports(dll)? ).enumerate() { let call_via = descriptor.first_thunk.0 + index as u32 * 8; let name = match import { ImportData::Ordinal(x) => { //print!("\n (import-ordinal {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} 0x{:>04x})", //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}") }, }; imports.push((thunk, orig, import)); if let Some(existing) = self.addr_to_import.get(&call_via) { panic!("addr space overlap at 0x{call_via:x}: {}::{} vs {}::{}", existing.0, existing.1, dep.to_string(), name); } self.addr_to_import.insert(call_via, (dep.to_string(), name)); } import_map.insert(dep, (resolved, imports)); println!(")") } for (name, (path, imports)) in import_map.iter() { self.load(path)?; } Ok(()) } pub fn load_exports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { Ok(()) } pub fn load_pe (&mut self, path: &Arc, data: &Arc<[u8]>) -> Usually> { let pe = Arc::new(VecPE::from_disk_data(data)); self.path_to_pe.insert(path.clone(), pe.clone()); Ok(pe) } pub fn load_rpe (&mut self, path: &Arc, data: &VecPE, base: u64) -> Usually> { let rdir = RelocationDirectory::parse(data)?; let mut rpe = data.clone(); rdir.relocate(&mut rpe, base)?; let rpe = Arc::new(rpe); self.path_to_rpe.insert(path.clone(), rpe.clone()); Ok(rpe) } } use crate::*; use exe::pe::{VecPE, PtrPE}; #[derive(Default)] pub struct PEContext { dlls: HashMap, } impl PEContext { pub fn load (&mut self, name: &str, path: &PathBuf) -> Usually<()> { let name = name.to_lowercase(); if self.dlls.contains_key(&name) { return Ok(()) } println!("\n(:= \"{}\" {:?})", name, path); let dll = Dll::new(name.as_str(), path)?; println!("(:= \"{}\" {:p}+{})", name, &dll.pe.buf, dll.pe.buf.len()); let mut imports = vec![]; for export in dll.goblin()?.imports.iter() { //println!(" - {:8} + {:?} = {:?}", &export.rva, &export.offset, &export.name); } for import in dll.goblin()?.imports.iter() { let dll = import.dll.to_lowercase(); println!(" (-> {} {})", &dll, &import.name); imports.push(dll); } self.dlls.insert(name.to_string(), dll); for name in imports.iter() { let path = format!("/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32/{}", name); self.load(name, &PathBuf::from(path))?; } Ok(()) } } struct Dll { name: String, path: PathBuf, bang: Vec, pe: PE, } impl Dll { fn new (name: &str, path: &PathBuf) -> Usually { let (bang, data) = slice_shebang(read(path)?.as_slice()); let mut pe = PE::from_bytes(data.as_slice())?; apply_relocations(&mut pe)?; Ok(Self { bang, name: name.to_string(), path: path.into(), pe, }) } fn goblin (&self) -> Usually { Ok(goblin::pe::PE::parse(self.pe.buf.as_slice())?) } } use std::ffi::c_void; //typedef VstIntPtr (VSTCALLBACK *audioMasterCallback) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); #[repr(C)] pub struct AEffect { magic: [u8; 4], /// ```c /// typedef VstIntPtr (VSTCALLBACK *AEffectDispatcherProc) (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); /// ``` dispatcher: fn(*const AEffect, i32, i32, i32, *mut c_void, f32) -> usize, /// ```c /// typedef void (VSTCALLBACK *AEffectProcessProc) (AEffect* effect, float** inputs, float** outputs, VstInt32 sampleFrames); /// typedef void (VSTCALLBACK *AEffectProcessDoubleProc) (AEffect* effect, double** inputs, double** outputs, VstInt32 sampleFrames); /// ``` process: usize, /// ```c /// typedef void (VSTCALLBACK *AEffectSetParameterProc) (AEffect* effect, VstInt32 index, float parameter); /// ``` set_parameter: usize, /// ```c /// typedef float (VSTCALLBACK *AEffectGetParameterProc) (AEffect* effect, VstInt32 index); /// ``` get_parameter: usize, num_programs: i32, num_params: i32, num_inputs: i32, num_outputs: i32, flags: i32, resvd1: usize, resvd2: usize, initial_delay: i32, real_qualities: i32, off_qualities: i32, io_ratio: f32, object: usize, user: usize, unique_id: i32, version: i32, process_replacing: usize, process_double_replacing: usize, _future: [u8;56], } impl AEffect { fn null_dispatcher (_: *const AEffect, _: i32, _: i32, _: i32, _: *mut c_void, _: f32) -> usize { 0 } fn host_callback ( _: *const AEffect, opcode: i32, index: i32, value: i32, ptr: *mut c_void, opt: f32 ) -> usize { 0 } pub fn run (address: usize) -> Self { let effect = Self::default(); // call(address, host_callback) effect } } impl Default for AEffect { fn default () -> Self { Self { magic: [ b'V', b's', b't', b'P'], dispatcher: Self::null_dispatcher, process: 0, set_parameter: 0, get_parameter: 0, num_programs: 0, num_params: 0, num_inputs: 0, num_outputs: 0, flags: 0, resvd1: 0, resvd2: 0, initial_delay: 0, real_qualities: 0, off_qualities: 0, io_ratio: 0.0, object: 0, user: 0, unique_id: 0, version: 0, process_replacing: 0, process_double_replacing: 0, _future: [0;56], } } } if let Some(link) = self.decode_link(&mut decoder, verbose)? { links += 1; self.call_sites.insert(link.source, link.clone()); } } } fn decode_link (self: Arc, decoder: &mut Decoder, verbose: bool) -> Usually>> { let position = decoder.position(); let instruction = decoder.decode(); let opcodes = &self.code[position..position+instruction.len()]; if CallSite::matches(&instruction) && !CallSite::skip(opcodes) { let offset = (position + self.code_start) as u32; let source = self.pe.offset_to_rva(Offset(offset))?.0; if let Some(target) = Self::target(opcodes, source) { let module: Arc = Module::default().into(); let method: Arc = "TODO".into(); if verbose { let offset = format!("0x{offset:08x}"); let source = format!("0x{source:08x}"); let instruct = format!("{BOLD}{instruction:30}{RESET}"); let target = format!("0x{target:08x}"); let external = format!("{module}::{method}"); println!("({BOLD}{offset}{RESET} {source} {instruct} {target} {external}"); } return Ok(Some(Arc::new(CallSite { caller: self.clone(), offset, source, length: opcodes.len(), target, module, method, }))) } } Ok(None) } fn dep_name (&self, target: u32) -> (Option>, Option>, Arc) { (None, None, "".into()) let deps = Some(&self.deps_by_address); let module = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.0.clone()); let method = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.1.clone()); let external = format!("{}::{}", module.as_ref().map(|x|x.as_ref()).unwrap_or("???"), method.as_ref().map(|x|x.as_ref()).unwrap_or("???")); (module, method, external.into()) } fn links_collect (&mut self, verbose: bool) -> Usually { let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0); let mut links = 0; while decoder.can_decode() { if let Some(link) = self.decode_link(&mut decoder, verbose)? { links += 1; //self.links_by_source.insert(link.source, link.clone()); //if !self.links_by_target.contains_key(&link.target) { //self.links_by_target.insert(link.target, Default::default()); //} //self.links_by_target.get_mut(&link.target).unwrap().push(link); } } if verbose { println!(" (link-sites {links})"); for (target, sites) in self.links_by_target.iter() { let (_, _, external) = self.dep_name(*target); println!(" ({:>5}x link 0x{target:08x} {external})", sites.len()); } } Ok(links) } / Collect all imported dependencies. fn deps_collect (&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, buffer) = import_collect(pe.as_ref(), descriptor)?; for (index, import_name) in buffer { let link_via = descriptor.first_thunk.0 + index as u32 * 8; let (new_modules, new_methods) = self.dep_collect( link_via, &module_name, &import_name, verbose )?; modules += new_modules; methods += new_methods; } } if verbose { println!(" (deps-modules {modules})"); println!(" (deps-methods {methods})"); self.show_deps_by_library(); } Ok((modules, methods)) } / 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(()) } /// 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 //} //}