diff --git a/crates/vestal/src/main.rs b/crates/vestal/src/main.rs index d51567d..f23f867 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -1,9 +1,9 @@ #![feature(slice_split_once)] mod util; +mod bang; //mod load; //mod show; -mod link; -mod bang; +//mod link; pub(crate) use self::util::*; fn main () -> Usually<()> { @@ -27,19 +27,59 @@ fn main () -> Usually<()> { let path = rebuilder.find(path.to_str().expect("path must be unicode"), false)? .unwrap_or_else(||panic!("Could not find: {path:?}")); let main = rebuilder.load(&path, true)?; - //let main = rebuilder.dlls.get(&name).unwrap(); - for (name, dll) in rebuilder.dlls.iter() { - println!("\n{name}"); - println!("{:?}", dll.code.hex_conf(HexConfig { + println!("\n{main}"); + let main = rebuilder.dlls.get(&main).unwrap(); + //println!("{:?}", main.code[0..1024.min(main.code.len())].hex_conf(HexConfig { + //title: false, + //width: 16, + //group: 4, + //chunk: 1, + //display_offset: main.code_base as usize, + //..HexConfig::default() + //})); + for (addr, call) in main.calls_by_source.iter() { + let start = (addr - main.code_base) as usize; + let end = start + call.length; + let mut decoder = Decoder::with_ip(64, &main.code[start..end], 0, DecoderOptions::NONE); + let instruction = decoder.decode(); + let cfg = HexConfig { title: false, - width: 32, + width: 16, group: 4, - chunk: 4, - display_offset: dll.code_start as usize, + chunk: 1, + display_offset: main.code_base as usize, ..HexConfig::default() - })); + }; + let end = start + 16; + let snap = |x|(x/16)*16; + let a = snap(start - 32); + let b = start; + let c = snap(end); + let d = snap(c + 32); + println!("\n{BOLD}0x{addr:x}{RESET} {} {:?} {:?}\n{:?}\n{BOLD}{:?}{RESET}\n{:?}", + instruction, + call.module, + call.method, + &main.code[a..b].hex_conf(HexConfig { display_offset: main.code_base as usize + a, ..cfg }), + &main.code[b..c].hex_conf(HexConfig { display_offset: main.code_base as usize + b, ..cfg }), + &main.code[c..d].hex_conf(HexConfig { display_offset: main.code_base as usize + c, ..cfg })); + if let (Some(module_name), Some(method_name)) = (&call.module, &call.method) { + if let Some(module) = main.deps_by_library.get(module_name) { + if let Some(method) = module.get(method_name) { + println!("0x{method:>08x} {module_name} {method_name}"); + if let Some(dll) = rebuilder.dlls.get(module_name) { + if let Some(address) = dll.exports.get(method_name) { + println!("# found"); + } + } + } + } + } } - //let mut decoder = iced_x86::Decoder::with_ip(64, &main.code, 0x1000, 0); + //println!("{:#?}", &main.calls_by_source); + for (name, dll) in rebuilder.dlls.iter() { + } + //let mut decoder = Decoder::with_ip(64, &main.code, 0x1000, 0); //while decoder.can_decode() { //let instruction = decoder.decode(); //if Dll::matches(&instruction) { @@ -61,18 +101,18 @@ struct Rebuilder { #[derive(Debug)] struct Dll { - /// Canonical name like `xxx.dll` (always lower case!) - name: Arc, + /// Canonical name like `xxx.dll` (always lowercase) + name: Arc, /// Path to DLL on host filesystem. - path: Arc, + path: Arc, /// Bytes of `#!`-instruction - bang: Arc<[u8]>, + bang: Arc<[u8]>, /// Parsed portable executable - pe: Arc, + pe: Arc, /// Bytes of `.text` section - code: Arc<[u8]>, + code: Arc<[u8]>, /// Start of `.text` section - code_start: u32, + code_base: u32, /// Addresses of imported methods by library deps_by_library: BTreeMap, BTreeMap, u32>>, /// Imported methods by address @@ -81,6 +121,8 @@ struct Dll { calls_by_source: BTreeMap>, /// Calls to dependencies by target address calls_by_target: BTreeMap>>, + /// Addresses of exported methods by name + exports: BTreeMap, u32>, } #[derive(Debug)] @@ -146,11 +188,10 @@ impl Rebuilder { } impl Dll { - fn new ( - path: &Arc, - verbose: bool - ) -> Usually { - println!("\n(load {BOLD}{path:?}{RESET})"); + fn new (path: &Arc, verbose: bool) -> Usually { + if verbose { + println!("\n(load {BOLD}{path:?}{RESET})"); + } 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()); @@ -161,8 +202,8 @@ impl Dll { 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 mut deps_by_library = Default::default(); + let mut deps_by_address = Default::default(); let (modules_count, methods_count) = Self::deps( &pe, &mut deps_by_library, @@ -177,19 +218,23 @@ impl Dll { Some(&deps_by_address), &mut calls_by_source, &mut calls_by_target, - true + false )?; Ok(Self { name: name.clone(), path: path.clone(), bang, - pe, code: Arc::from(text), - code_start: start as u32, + 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: Default::default(), + pe, }) } fn deps ( @@ -243,9 +288,9 @@ impl Dll { } } } - println!(" (deps-modules {modules})"); - println!(" (deps-methods {methods})"); 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() { @@ -256,6 +301,16 @@ impl Dll { } Ok((modules, methods)) } + 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()) + } fn calls ( name: &Arc, pe: &VecPE, @@ -266,7 +321,7 @@ impl Dll { calls_by_target: &mut BTreeMap>>, verbose: bool, ) -> Usually { - let mut decoder = iced_x86::Decoder::with_ip(64, data, 0x1000, 0); + let mut decoder = Decoder::with_ip(64, data, 0x1000, 0); let mut calls = 0; while decoder.can_decode() { if let Some(call) = Self::call(name, pe, start, data, deps, &mut decoder, false)? { @@ -278,8 +333,8 @@ impl Dll { calls_by_target.get_mut(&call.target).unwrap().push(call); } } - println!(" (call-sites {calls})"); if verbose { + println!(" (call-sites {calls})"); for (target, sites) in calls_by_target.iter() { let (_, _, external) = Self::dep_name(deps, *target); println!(" ({:>5}x call 0x{target:08x} {external})", sites.len()); @@ -292,32 +347,22 @@ impl Dll { } Ok(calls) } - 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()) - } fn call ( name: &Arc, pe: &VecPE, start: usize, data: &[u8], deps: Option<&BTreeMap, Arc)>>, - decoder: &mut iced_x86::Decoder, + decoder: &mut Decoder, verbose: bool, ) -> Usually>> { let position = decoder.position(); let instruction = decoder.decode(); let opcodes = &data[position..position+instruction.len()]; - if Self::matches(&instruction) && !Self::skip(opcodes) { + 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) = Self::target(opcodes, offset_rva) { + if let Some(target) = Call::target(opcodes, offset_rva) { let (module, method, external) = Self::dep_name(deps, target); if verbose { let external = format!("{}::{}", @@ -338,10 +383,13 @@ impl Dll { } Ok(None) } - fn matches (instruction: &iced_x86::Instruction) -> bool { - instruction.op0_kind() == iced_x86::OpKind::Memory && ( - instruction.flow_control() == iced_x86::FlowControl::IndirectBranch || - instruction.flow_control() == iced_x86::FlowControl::IndirectCall +} + +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 { diff --git a/crates/vestal/src/util.rs b/crates/vestal/src/util.rs index c9a8dca..ca76faa 100644 --- a/crates/vestal/src/util.rs +++ b/crates/vestal/src/util.rs @@ -15,6 +15,13 @@ pub(crate) use ::object::endian::LittleEndian; pub(crate) use ::object::write::elf::Writer as ElfWriter; pub(crate) use ::pretty_hex::*; pub(crate) use ::exe::{Buffer, PE, VecPE, PtrPE, types::*, headers::*}; +pub(crate) use ::iced_x86::{Encoder, Decoder, DecoderOptions, Instruction, OpKind, FlowControl}; pub(crate) type Usually = Result>; pub const RESET: &str = "\u{001b}[0m"; pub const BOLD: &str = "\u{001b}[1m"; +pub enum Verbosity { + Silent, + Terse, + Brief, + Verbose, +}