diff --git a/Justfile b/Justfile index c58ef57..5a643ad 100644 --- a/Justfile +++ b/Justfile @@ -1,2 +1,4 @@ hello: - clear; tmux clear-history || true; cargo build && target/debug/vestal bin/hello-msg.exe | less + clear; tmux clear-history || true; cargo build && target/debug/vestal bin/hello-msg.exe 2>&1 +hello-v: + clear; tmux clear-history || true; cargo build && target/debug/vestal -v bin/hello-msg.exe 2>&1 diff --git a/crates/vestal/src/.scratch.rs b/crates/vestal/src/.scratch.rs new file mode 100644 index 0000000..397cc98 --- /dev/null +++ b/crates/vestal/src/.scratch.rs @@ -0,0 +1,292 @@ +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], + } + } +} diff --git a/crates/vestal/src/call.rs b/crates/vestal/src/call.rs index e69de29..69355a4 100644 --- a/crates/vestal/src/call.rs +++ b/crates/vestal/src/call.rs @@ -0,0 +1,147 @@ +use crate::*; + +impl Dll { + + pub fn parse_call (&self, call: &Arc) -> Option { + self.deps_by_library.get(call.module.as_ref()?)?.get(call.method.as_ref()?).map(|x|*x) + } + + pub fn collect_calls (&mut self, verbose: bool) -> Usually { + let mut decoder = Decoder::with_ip(64, self.text_section.as_ref(), 0, 0); + let mut calls = 0; + while decoder.can_decode() { + if let Some(call) = self.collect_call(&mut decoder, verbose)? { + calls += 1; + self.calls_by_source.insert(call.source, call.clone()); + if !self.calls_by_target.contains_key(&call.target) { + self.calls_by_target.insert(call.target, Default::default()); + } + self.calls_by_target.get_mut(&call.target).unwrap().push(call); + } + } + if verbose { + println!(" (call-sites {calls})"); + for (target, sites) in self.calls_by_target.iter() { + let (_, _, external) = self.dep_name(*target); + println!(" ({:>5}x call 0x{target:08x} {external})", sites.len()); + //.map(|site|format!("0x{:08x}", site.offset)) + //.collect::>()); + //println!(" (call 0x{target:08x} {external}\n {:?})", sites.iter() + //.map(|site|format!("0x{:08x}", site.offset)) + //.collect::>()); + } + } + Ok(calls) + } + + pub fn collect_call (&self, decoder: &mut Decoder, verbose: bool) -> Usually>> { + let position = decoder.position(); + let instruction = decoder.decode(); + let opcodes = &self.text_section[position..position+instruction.len()]; + if Call::matches(&instruction) && !Call::skip(opcodes) { + let offset = (position + self.text_section_start) as u32; + let offset_rva = self.pe.offset_to_rva(Offset(offset))?.0; + if let Some(target) = Call::target(opcodes, offset_rva) { + let (module, method, external) = self.dep_name(target); + if verbose { + let external = format!("{}::{}", + module.as_ref().map(|x|x.as_ref()).unwrap_or("???"), + method.as_ref().map(|x|x.as_ref()).unwrap_or("???")); + println!(" ({BOLD}0x{:08x}{RESET} 0x{:08x} {BOLD}{:30}{RESET} 0x{:x} {}", + offset, offset_rva, instruction, target, external); + } + return Ok(Some(Arc::new(Call { + offset: offset, + source: offset_rva, + length: opcodes.len(), + target, + module, + method, + }))) + } + } + Ok(None) + } + + fn dep_name (&self, target: u32) -> (Option>, Option>, Arc) { + 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()) + } +} + +#[derive(Debug)] +pub struct Call { + /// Address on disk + pub offset: u32, + /// Address in memory + pub source: u32, + /// Length of call in opcodes + pub length: usize, + /// Call trampoline address + pub target: u32, + /// Library being called + pub module: Option>, + /// Method being called + pub method: Option>, +} + +impl Call { + 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 { + match opcodes[0] { + 0xff => match opcodes[1] { + 0x15 | 0x25 => return Some(offset_rva + opcodes.len() as u32 + u32::from_le_bytes([ + opcodes[2], + opcodes[3], + opcodes[4], + opcodes[5] + ])), + _ => {} + }, + 0x48 => match opcodes[1] { + 0xff => match opcodes[2] { + 0x15 | 0x25 => return Some(offset_rva + opcodes.len() as u32 + u32::from_le_bytes([ + opcodes[3], + opcodes[4], + opcodes[5], + opcodes[6] + ])), + _ => {} + }, + _ => {} + } + _ => {} + } + None + } +} diff --git a/crates/vestal/src/deps.rs b/crates/vestal/src/deps.rs new file mode 100644 index 0000000..324beeb --- /dev/null +++ b/crates/vestal/src/deps.rs @@ -0,0 +1,98 @@ +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 = 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 = 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)) + } + +} diff --git a/crates/vestal/src/dll.rs b/crates/vestal/src/dll.rs index 2b18b3a..e69de29 100644 --- a/crates/vestal/src/dll.rs +++ b/crates/vestal/src/dll.rs @@ -1,138 +0,0 @@ -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], - } - } -} diff --git a/crates/vestal/src/link.rs b/crates/vestal/src/elf.rs similarity index 100% rename from crates/vestal/src/link.rs rename to crates/vestal/src/elf.rs diff --git a/crates/vestal/src/load.rs b/crates/vestal/src/load.rs deleted file mode 100644 index 64e23f9..0000000 --- a/crates/vestal/src/load.rs +++ /dev/null @@ -1,154 +0,0 @@ -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) - } -} diff --git a/crates/vestal/src/main.rs b/crates/vestal/src/main.rs index 85c7574..2dbbeb1 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -1,5 +1,7 @@ #![feature(slice_split_once)] mod util; pub(crate) use self::util::*; +mod deps; pub(crate) use self::deps::*; +mod call; pub(crate) use self::call::*; mod show; pub(crate) use self::show::*; mod bang; @@ -13,7 +15,7 @@ fn main () -> Usually<()> { .get_matches(); let path = matches.get_one::("path").unwrap_or_else(||panic!("pass path to VST DLL")); let verbose = *(matches.get_one::("verbose").unwrap_or(&false)); - let mut rebuilder = Rebuilder::new(&[ + let mut rebuilder = Vestal::new(&[ std::env::current_dir()?, canonicalize(path.clone().parent().expect("invalid parent path"))?, "/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32".into(), @@ -31,7 +33,7 @@ fn main () -> Usually<()> { } #[derive(Debug, Default)] -struct Rebuilder { +struct Vestal { /// Search paths paths: BTreeSet>, /// All DLLs in scope @@ -40,7 +42,7 @@ struct Rebuilder { visited: BTreeSet>, } -impl Rebuilder { +impl Vestal { fn new (paths: &[impl AsRef]) -> Self { Self { paths: paths.iter().map(|x|Arc::new(x.as_ref().into())).collect(), @@ -54,6 +56,9 @@ impl Rebuilder { let name: Arc = name.to_str().map(Arc::from).expect("non-unicode filename"); return Ok(name) } + if verbose { + println!("(load {path:?})"); + } self.visited.insert(path.clone()); let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), verbose)?); self.dlls.insert(dll.name.clone(), dll.clone()); @@ -103,61 +108,76 @@ impl Rebuilder { verbose: bool, ) -> Usually<()> { let addr = (addr - dll.code_base) as usize; - //Show::call(&dll, addr, Some(call)); - //Show::call_target_addrs(Some(call)); Show::call_site(&dll, addr, call.length, 1); - Show::call_dasm(&dll.code[addr..], addr); Show::call_module_method(Some(call)); if let Some(method) = dll.parse_call(call) { let module_name = call.module.as_ref().unwrap(); let method_name = call.method.as_ref().unwrap(); - //println!("{BOLD}0x{method:>08x}{RESET} {module_name:20} {method_name}"); - if let Some(path) = self.find(module_name, false)? { - let name = path.file_name().expect("no file name"); - let name: Arc = name.to_str().map(Arc::from).expect("non-unicode filename"); - if let Some(dll) = self.dlls.get(&name) { - if let Some(thunk) = dll.exports.get(method_name) { - if let ThunkData::Function(rva) = thunk { - let addr = (rva.0 - dll.code_base) as usize; - if recurse { - println!("\n {RESET} {BOLD}{} ({method_name}){RESET}", &dll.name); - let mut decoder = Decoder::with_ip(64, &dll.code[addr..], 0, DecoderOptions::NONE); - while decoder.can_decode() { - let position = decoder.position(); - let instruction = decoder.decode(); - let opcodes = &dll.code[position..position+instruction.len()]; - //Show::call_site(&dll, addr, instruction.len(), 1); - if Call::matches(&instruction) && !Call::skip(opcodes) { - let addr = addr + position; - let start = 0x0; - let offset = (position + start) as u32; - let offset_rva = dll.pe.offset_to_rva(Offset(offset))?.0 as u32; - let target = Call::target(opcodes, 0); - Show::call_site(&dll, addr, instruction.len(), 1); - //println!(" ╰-------> (target {:?}) (offset {offset:?}) (rva {offset_rva:?})", target); - Show::call_dasm(&dll.code[addr..], addr); - Show::call_module_method(dll.calls_by_source.get(&(addr as u32))); - //Show::call(&dll, addr + position, None); - } - if dll.code[addr + position] == 0xc3 { - break - } - } - } - } else { - panic!("# unsupported {thunk:?}"); - } + let path = self.find(module_name, false)? + .unwrap_or_else(||panic!("# not found: {module_name}")); + let name = path.file_name() + .expect("no file name"); + let name: Arc = 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)?; } - } - } else { - println!("# not found {call:?}"); + }, + None => panic!("# not found: {method_name}"), + thunk => panic!("# unsupported {thunk:?}"), + } + } + Ok(()) + } + fn resolve_call_recurse ( + &self, + dll: &Dll, + addr: usize, + module_name: &Arc, + method_name: &Arc, + ) -> Usually<()> { + println!("\n {RESET} {BOLD}{} ({method_name}){RESET}", &dll.name); + //dll.collect_call( + //&dll.name, + //&dll.pe, + //&dll.text_section_start, + //&dll.text_section, + //); + let mut decoder = Decoder::with_ip(64, &dll.text_section[addr..], 0, DecoderOptions::NONE); + while decoder.can_decode() { + let position = decoder.position(); + let instruction = decoder.decode(); + let opcodes = &dll.text_section[position..position+instruction.len()]; + if Call::matches(&instruction) && !Call::skip(opcodes) { + let addr = addr + position; + let start = dll.text_section_start; + let offset = (position + start) as u32; + let offset_rva = dll.pe.offset_to_rva(Offset(offset))?.0 as u32; + println!("{opcodes:?} {addr:x} {position:x} {start:x} {offset:x} {offset_rva:x}"); + let target = Call::target(opcodes, offset_rva); + Show::call_site(&dll, addr, instruction.len(), 1); + println!("{target:?}"); + if let Some(target) = target { + println!("{target:x} {:?}", dll.deps_by_address.get(&(target as u32))); + println!("{target:x} {:?}", dll.calls_by_source.get(&(target as u32))) + } else { + println!("???"); + }; + Show::call_module_method(dll.calls_by_source.get(&(addr as u32))); + } + if dll.text_section[addr + position] == 0xc3 { + break } } Ok(()) } } -#[derive(Debug)] struct Dll { /// Canonical name like `xxx.dll` (always lowercase) name: Arc, @@ -168,8 +188,10 @@ struct Dll { /// Parsed portable executable pe: Arc, /// Bytes of `.text` section - code: Arc<[u8]>, + text_section: Arc<[u8]>, /// Start of `.text` section + text_section_start: usize, + /// Assumed address in memory code_base: u32, /// Addresses of imported methods by library deps_by_library: BTreeMap, BTreeMap, u32>>, @@ -183,6 +205,24 @@ struct Dll { exports: BTreeMap, ThunkData>, } +impl std::fmt::Debug for Dll { + fn fmt (&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { + let deps = format!("(deps :by-lib {} :by-addr {})", + self.deps_by_library.len(), + self.deps_by_address.len()); + let calls = format!("(calls :by-src {} :by-tgt {})", + self.calls_by_source.len(), + self.calls_by_target.len()); + let exports = format!("(exports {})", + self.exports.len()); + write!(f, "(dll {} {:?}\n (img 0x{:>08x} -> mem 0x{:>08x})\n {deps}\n {calls}\n {exports})", + &self.name, + &self.path, + self.text_section_start, + self.code_base) + } +} + impl Dll { fn new (path: &Arc, verbose: bool) -> Usually { if verbose { @@ -190,287 +230,41 @@ impl Dll { } let name = path.file_name().expect("no file name"); let name: Arc = 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())); - let code = pe.get_section_by_name(".text")?; + let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice()); + let pe = Arc::new(VecPE::from_disk_data(data.clone())); + 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 calls_by_source = Default::default(); - let mut calls_by_target = Default::default(); - let mut deps_by_library = Default::default(); - let mut deps_by_address = Default::default(); - let exports = collect_exports( - &pe, - verbose - ).unwrap_or_default(); - let (modules_count, methods_count) = collect_deps( - &mut deps_by_library, - &mut deps_by_address, - &pe, - verbose, - )?; - let calls = collect_calls( - &mut calls_by_source, - &mut calls_by_target, - Some(&deps_by_address), - &name, - &pe, - start, - text, - verbose - )?; - Ok(Self { + let size = code.size_of_raw_data as usize; + let text = &data[start..start+size]; + let mut dll = Self { name: name.clone(), path: path.clone(), bang, - code: Arc::from(text), + 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, 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, }, - deps_by_library: deps_by_library.clone(), - deps_by_address: deps_by_address.clone(), - calls_by_source, - calls_by_target, - exports, pe, - }) - } - fn parse_call (&self, call: &Arc) -> Option { - self.deps_by_library.get(call.module.as_ref()?)?.get(call.method.as_ref()?).map(|x|*x) - } -} - -fn collect_deps ( - deps_by_library: &mut BTreeMap, BTreeMap, u32>>, - deps_by_address: &mut BTreeMap, Arc)>, - pe: &VecPE, - verbose: bool, -) -> Usually<(usize, usize)> { - let directory = ImportDirectory::parse(pe)?; - let mut modules = 0; - let mut methods = 0; - for descriptor in directory.descriptors { - let module_name = descriptor.get_name(pe)?.as_str()?.to_lowercase(); - 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)?; - let unwrap_thunk = |thunk: &Thunk, name|match thunk { - Thunk::Thunk32(t) => panic!("32 bit {name}"), - Thunk::Thunk64(t) => t.0 }; - 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() { - 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 = module_name.clone().into(); - if !deps_by_library.contains_key(&module_name) { - deps_by_library.insert(module_name.clone(), Default::default()); - modules += 1; - } - let module = deps_by_library.get_mut(&module_name).unwrap(); - let method: Arc = method.clone().into(); - if module.contains_key(&method) { - panic!("duplicate method {method} in {module_name}"); - } - module.insert(method.clone(), call_via); - if deps_by_address.contains_key(&call_via) { - panic!("duplicate address {call_via} from {module_name}"); - } - deps_by_address.insert(call_via, (module_name.clone(), method.clone())); - methods += 1; - if verbose { - println!(" (import 0x{call_via:08x} {module_name}::{method})"); - } - } + //let _exports = dll.collect_exports(verbose)?; + let (_modules_count, _methods_count) = dll.collect_deps(verbose)?; + let _calls = dll.collect_calls(verbose)?; + println!("{dll:?}"); + Ok(dll) } - if verbose { - println!(" (deps-modules {modules})"); - println!(" (deps-methods {methods})"); - for (module, methods) in deps_by_library.iter() { - print!(" ({module}"); - for (method, addr) in methods.iter() { - print!("\n (0x{addr:08x} {method})") - } - println!(")"); - } - } - Ok((modules, methods)) -} - -fn collect_exports ( - pe: &VecPE, - _verbose: bool, -) -> Usually, ThunkData>> { - Ok(ImageExportDirectory::parse(pe)? - .get_export_map(pe)? - .into_iter() - .map(|(k, v)|(k.into(), v)) - .collect()) -} - -fn collect_calls ( - calls_by_source: &mut BTreeMap>, - calls_by_target: &mut BTreeMap>>, - deps: Option<&BTreeMap, Arc)>>, - name: &Arc, - pe: &VecPE, - start: usize, - data: &[u8], - verbose: bool, -) -> Usually { - let mut decoder = Decoder::with_ip(64, data, 0, 0); - let mut calls = 0; - while decoder.can_decode() { - if let Some(call) = collect_call(name, pe, start, data, deps, &mut decoder, false)? { - calls += 1; - calls_by_source.insert(call.source, call.clone()); - if !calls_by_target.contains_key(&call.target) { - calls_by_target.insert(call.target, Default::default()); - } - calls_by_target.get_mut(&call.target).unwrap().push(call); - } - } - if verbose { - println!(" (call-sites {calls})"); - for (target, sites) in calls_by_target.iter() { - let (_, _, external) = dep_name(deps, *target); - println!(" ({:>5}x call 0x{target:08x} {external})", sites.len()); - //.map(|site|format!("0x{:08x}", site.offset)) - //.collect::>()); - //println!(" (call 0x{target:08x} {external}\n {:?})", sites.iter() - //.map(|site|format!("0x{:08x}", site.offset)) - //.collect::>()); - } - } - Ok(calls) -} - -fn collect_call ( - name: &Arc, - pe: &VecPE, - start: usize, - data: &[u8], - deps: Option<&BTreeMap, Arc)>>, - decoder: &mut Decoder, - verbose: bool, -) -> Usually>> { - let position = decoder.position(); - let instruction = decoder.decode(); - let opcodes = &data[position..position+instruction.len()]; - if Call::matches(&instruction) && !Call::skip(opcodes) { - let offset = (position + start) as u32; - let offset_rva = pe.offset_to_rva(Offset(offset))?.0; - if let Some(target) = Call::target(opcodes, offset_rva) { - let (module, method, external) = dep_name(deps, target); - if verbose { - let external = format!("{}::{}", - module.as_ref().map(|x|x.as_ref()).unwrap_or("???"), - method.as_ref().map(|x|x.as_ref()).unwrap_or("???")); - println!(" ({BOLD}0x{:08x}{RESET} 0x{:08x} {BOLD}{:30}{RESET} 0x{:x} {}", - offset, offset_rva, instruction, target, external); - } - return Ok(Some(Arc::new(Call { - offset: offset, - source: offset_rva, - length: opcodes.len(), - target, - module, - method, - }))) - } - } - Ok(None) -} - -fn dep_name ( - deps: Option<&BTreeMap, Arc)>>, target: u32 -) -> (Option>, Option>, Arc) { - 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()) -} - -#[derive(Debug)] -struct Call { - /// Address on disk - offset: u32, - /// Address in memory - source: u32, - /// Length of call in opcodes - length: usize, - /// Call trampoline address - target: u32, - /// Library being called - module: Option>, - /// Method being called - method: Option>, -} - -impl Call { - 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 - } - fn target (opcodes: &[u8], offset_rva: u32) -> Option { - match opcodes[0] { - 0xff => match opcodes[1] { - 0x15 | 0x25 => return Some(offset_rva + opcodes.len() as u32 + u32::from_le_bytes([ - opcodes[2], - opcodes[3], - opcodes[4], - opcodes[5] - ])), - _ => {} - }, - 0x48 => match opcodes[1] { - 0xff => match opcodes[2] { - 0x15 | 0x25 => return Some(offset_rva + opcodes.len() as u32 + u32::from_le_bytes([ - opcodes[3], - opcodes[4], - opcodes[5], - opcodes[6] - ])), - _ => {} - }, - _ => {} - } - _ => {} - } - None + fn collect_exports (&mut self, _verbose: bool) -> Usually { + self.exports = ImageExportDirectory::parse(self.pe.as_ref())? + .get_export_map(self.pe.as_ref())? + .into_iter() + .map(|(k, v)|(k.into(), v)) + .collect(); + Ok(self.exports.len()) } } diff --git a/crates/vestal/src/show.rs b/crates/vestal/src/show.rs index 629b108..f1f9738 100644 --- a/crates/vestal/src/show.rs +++ b/crates/vestal/src/show.rs @@ -1,24 +1,24 @@ use crate::*; use std::fmt::Write; +pub fn fmt_bytes (bytes: &[u8]) -> Arc { + bytes.iter().map(|x|format!("{x:02x}")).join(" ").into() +} + pub struct Show; impl Show { pub fn num (n: usize) -> Arc { format!("0x{n:>08x}").into() } - pub fn call ( - dll: &Dll, - addr: usize, - call: Option<&Arc>, - ) { - let mut decoder = Decoder::with_ip(64, &dll.code[addr..], 0x1000, DecoderOptions::NONE); + pub fn call (dll: &Dll, addr: usize, call: Option<&Arc>) { + let mut decoder = Decoder::with_ip(64, &dll.text_section[addr..], 0x1000, DecoderOptions::NONE); let instruction = decoder.decode(); let is_stack = instruction.is_stack_instruction(); let is_link = Call::matches(&instruction); let style = if is_stack || is_link { BOLD } else { DIM }; print!("{style}{} {:20}{RESET}", Show::num(addr), &dll.name); - print!(" {style}{:26}{RESET}", fmt_bytes(&dll.code[addr..addr+instruction.len()])); + print!(" {style}{:26}{RESET}", fmt_bytes(&dll.text_section[addr..addr+instruction.len()])); println!(" {style}{instruction}{RESET}"); } pub fn call_target_addrs ( @@ -38,15 +38,14 @@ impl Show { 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}", + 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}"); + println!(" ╰-------> {RED}(unresolved){RESET} {call:?}"); } } - pub fn call_site ( dll: &Dll, addr: usize, @@ -63,13 +62,12 @@ impl Show { for (index, byte) in (byte_start..byte_end).enumerate() { write!(&mut output, "{DIM}"); if byte % line == 0 { - write!(&mut output, " "); if (byte >= snap(addr)) && (byte < snap(addr) + line) { - write!(&mut output, "{RESET}╭"); + write!(&mut output, "{RESET} ╭"); } else if byte >= snap(addr) + line { - write!(&mut output, "┊"); + write!(&mut output, " ┊"); } else { - write!(&mut output, " "); + write!(&mut output, " "); } write!(&mut output, "{:08x}", byte + base); } @@ -86,11 +84,11 @@ impl Show { write!(&mut output, "{}", " "); } } - write!(&mut output, "{:02x}", dll.code[byte]); + write!(&mut output, "{:02x}", dll.text_section[byte]); write!(&mut output, "{RESET}"); if byte % line == line - 1 { if snap(byte) == snap(addr) { - let dasm = Self::call_dasm(&dll.code[addr..addr+length], addr); + let dasm = Self::call_dasm(&dll.text_section[addr..], addr); write!(&mut output, " -> {dasm}"); } write!(&mut output, " \n"); @@ -98,7 +96,6 @@ impl Show { } print!("{output}"); } - pub fn call_dasm (bytes: &[u8], rip: usize) -> Arc { let mut decoder = Decoder::with_ip(64, bytes, 0x1000 + rip as u64, DecoderOptions::NONE); while decoder.can_decode() { @@ -111,14 +108,6 @@ impl Show { } } -pub fn fmt_hex_line (addr: usize, data: &[u8]) -> Arc { - format!("{:>08x} {}", addr, fmt_bytes(data)).into() -} - -pub fn fmt_bytes (bytes: &[u8]) -> Arc { - bytes.iter().map(|x|format!("{x:02x}")).join(" ").into() -} - //impl Vestal { //pub fn show_addr_to_import (&self) { //for (addr, (dll, export)) in self.addr_to_import.iter() {