diff --git a/.gitignore b/.gitignore index 4c2add3..e7e783a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target *.dll +*.exe diff --git a/crates/vestal/src/load.rs b/crates/vestal/src/load.rs new file mode 100644 index 0000000..9a32271 --- /dev/null +++ b/crates/vestal/src/load.rs @@ -0,0 +1,103 @@ +use crate::*; +const RESET: &str = "\u{001b}[0m"; +const BOLD: &str = "\u{001b}[1m"; + +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 = HashMap::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 lookups = descriptor.get_lookup_thunks(dll)?; + let resolved = self.resolve(&dep)?.expect("no path for {name}"); + print!("\n (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 + })), + lookups.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; + match import { + ImportData::Ordinal(x) => + print!("\n (import-ordinal {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} 0x{:>04x})", + call_via, thunk, orig, lookup, x), + ImportData::ImportByName(name) => + print!("\n (import-by-name {BOLD}0x{:>08x}{RESET} IAT={} ILT={} LU={} {:?})", + call_via, thunk, orig, lookup, name), + } + imports.push((thunk, orig, import)); + } + 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_bang_data (&mut self, path: &Arc) -> Usually> { + let (bang, data) = slice_shebang(read(path.as_path())?.as_slice()); + self.path_to_bang.insert(path.clone(), bang.clone()); + if bang.len() > 0 { + println!(" (bang {path:?} {:x})", bang.len()) + } + self.path_to_data.insert(path.clone(), data.clone()); + println!(" (buffer {:p} 0x{:08x} {path:?})", data.as_ptr(), data.len()); + Ok(data) + } + 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 f0b5da6..eec820e 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -1,8 +1,7 @@ #![feature(slice_split_once)] mod util; +mod load; pub(crate) use self::util::*; -//mod dll; -//mod exec; use clap::{arg, command, value_parser, ArgAction, Command}; fn main () -> Usually<()> { let matches = command!() @@ -18,8 +17,7 @@ fn main () -> Usually<()> { println!("(search {path:?})") } if let Some(path) = vestal.resolve(path.to_str().expect("path must be unicode"))? { - vestal.load(&path)?; - vestal.run_main(&path)?; + vestal.enter(&path)?; } else { panic!("Could not find: {path:?}") } @@ -35,11 +33,13 @@ struct Vestal { path_to_bang: HashMap, Arc<[u8]>>, path_to_data: HashMap, Arc<[u8]>>, path_to_pe: HashMap, Arc>, + path_to_rpe: HashMap, Arc>, //path_to_exports: HashMap, Vec>, //path_to_imports: HashMap, Vec>, } impl Vestal { - fn run_main (&self, path: &PathBuf) -> Usually<()> { + fn enter (&mut self, path: &PathBuf) -> Usually<()> { + self.load(&path)?; let data = self.path_to_data.get(path); let len = data.as_ref().unwrap().len(); let data = data.as_ref().unwrap(); @@ -62,9 +62,9 @@ impl Vestal { let section_data = &buf[section_ptr..section_ptr+section_len]; let mut decoder = iced_x86::Decoder::with_ip(64, section_data, 0x1000, 0); while decoder.can_decode() { - let position = decoder.position(); + let position = decoder.position(); let instruction = decoder.decode(); - let opcodes = §ion_data[position..position+instruction.len()]; + let opcodes = §ion_data[position..position+instruction.len()]; //println!("0x{position:08x} {opcodes:32} {instruction}"); if (instruction.flow_control() == iced_x86::FlowControl::IndirectBranch || instruction.flow_control() == iced_x86::FlowControl::IndirectCall) @@ -91,7 +91,6 @@ impl Vestal { dll.offset_to_rva(Offset(offset))?.0, opcodes.iter().map(|x|format!("{x:>02x}")).collect::>().join(" "), instruction); - break //println!("0x{:08x} {}", decoder.position(), instruction); //return Ok(()) } @@ -112,88 +111,4 @@ impl Vestal { //panic!("no main"); Ok(()) } - 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) - } - 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 imports = self.load_imports(&path, &dll); - let exports = self.load_exports(&path, &dll); - } - Ok(()) - } - fn load_imports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { - let mut import_map = HashMap::new(); - let directory = ImportDirectory::parse(dll)?; - for descriptor in directory.descriptors { - let dep = descriptor.get_name(dll)?.as_str()?; - let resolved = self.resolve(&dep)?.expect("no path for {name}"); - print!("\n (module {dep:?} 0x{:>08x} 0x{:>08x}\n {resolved:?}", - &descriptor.first_thunk.0, - &descriptor.original_first_thunk.0); - let mut imports = Vec::new(); - let thunks = descriptor.get_first_thunk(dll)?; - let origs = descriptor.get_original_first_thunk(dll)?; - let lookups = descriptor.get_lookup_thunks(dll)?; - for (thunk, orig, lookup, import) in izip!( - thunks.iter().map(|thunk|format!("0x{:08x}", match thunk { - Thunk::Thunk32(t) => panic!("32 bit thunk"), - Thunk::Thunk64(t) => t.0 - })), - origs.iter().map(|thunk|format!("0x{:08x}", match thunk { - Thunk::Thunk32(t) => panic!("32 bit original thunk"), - Thunk::Thunk64(t) => t.0 - })), - lookups.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)? - ) { - match import { - ImportData::Ordinal(x) => - print!("\n (import-ordinal {thunk} 0x{x:>04x})"), - ImportData::ImportByName(n) => - print!("\n (import-by-name {thunk} {n:?})"), - } - imports.push((thunk, orig, import)); - } - import_map.insert(dep, (resolved, imports)); - println!(")") - } - for (name, (path, imports)) in import_map.iter() { - self.load(path)?; - } - Ok(()) - } - fn load_exports (&mut self, path: &PathBuf, dll: &VecPE) -> Usually<()> { - Ok(()) - } - fn load_bang_data (&mut self, path: &Arc) -> Usually> { - let (bang, data) = slice_shebang(read(path.as_path())?.as_slice()); - self.path_to_bang.insert(path.clone(), bang.clone()); - if bang.len() > 0 { - println!(" (bang {path:?} {:x})", bang.len()) - } - self.path_to_data.insert(path.clone(), data.clone()); - println!(" (buffer {:p} 0x{:08x} {path:?})", data.as_ptr(), data.len()); - Ok(data) - } - 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) - } }