mirror of
https://codeberg.org/unspeaker/vestal.git
synced 2025-12-06 10:46:42 +01:00
wip: bake 2nd pass into 1st pass
This commit is contained in:
parent
c02df5d47f
commit
a546f2d45e
9 changed files with 666 additions and 636 deletions
4
Justfile
4
Justfile
|
|
@ -1,2 +1,4 @@
|
||||||
hello:
|
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
|
||||||
|
|
|
||||||
292
crates/vestal/src/.scratch.rs
Normal file
292
crates/vestal/src/.scratch.rs
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//#[derive(Default, Debug)]
|
||||||
|
//struct Vestal {
|
||||||
|
//search_paths: Vec<PathBuf>,
|
||||||
|
//paths_visited: BTreeSet<Arc<PathBuf>>,
|
||||||
|
//path_to_bang: BTreeMap<Arc<PathBuf>, Arc<[u8]>>,
|
||||||
|
//path_to_data: BTreeMap<Arc<PathBuf>, Arc<[u8]>>,
|
||||||
|
//path_to_pe: BTreeMap<Arc<PathBuf>, Arc<VecPE>>,
|
||||||
|
//path_to_rpe: BTreeMap<Arc<PathBuf>, Arc<VecPE>>,
|
||||||
|
//addr_to_import: BTreeMap<u32, (String, String)>,
|
||||||
|
////path_to_exports: BTreeMap<Arc<PathBuf>, Vec<ImageExportDescriptor>>,
|
||||||
|
////path_to_imports: BTreeMap<Arc<PathBuf>, Vec<ImageImportDescriptor>>,
|
||||||
|
//}
|
||||||
|
//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<Option<PathBuf>> {
|
||||||
|
//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<PathBuf>, data: &Arc<[u8]>) -> Usually<Arc<VecPE>> {
|
||||||
|
//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<PathBuf>, data: &VecPE, base: u64) -> Usually<Arc<VecPE>> {
|
||||||
|
//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<String, Dll>,
|
||||||
|
}
|
||||||
|
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<u8>,
|
||||||
|
pe: PE,
|
||||||
|
}
|
||||||
|
impl Dll {
|
||||||
|
fn new (name: &str, path: &PathBuf) -> Usually<Self> {
|
||||||
|
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<goblin::pe::PE> {
|
||||||
|
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],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl Dll {
|
||||||
|
|
||||||
|
pub fn parse_call (&self, call: &Arc<Call>) -> Option<u32> {
|
||||||
|
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<usize> {
|
||||||
|
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::<Vec<_>>());
|
||||||
|
//println!(" (call 0x{target:08x} {external}\n {:?})", sites.iter()
|
||||||
|
//.map(|site|format!("0x{:08x}", site.offset))
|
||||||
|
//.collect::<Vec<_>>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_call (&self, decoder: &mut Decoder, verbose: bool) -> Usually<Option<Arc<Call>>> {
|
||||||
|
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<Arc<str>>, Option<Arc<str>>, Arc<str>) {
|
||||||
|
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<Arc<str>>,
|
||||||
|
/// Method being called
|
||||||
|
pub method: Option<Arc<str>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<u32> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
98
crates/vestal/src/deps.rs
Normal file
98
crates/vestal/src/deps.rs
Normal file
|
|
@ -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<str> = 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<str> = 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use exe::pe::{VecPE, PtrPE};
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PEContext {
|
|
||||||
dlls: HashMap<String, Dll>,
|
|
||||||
}
|
|
||||||
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<u8>,
|
|
||||||
pe: PE,
|
|
||||||
}
|
|
||||||
impl Dll {
|
|
||||||
fn new (name: &str, path: &PathBuf) -> Usually<Self> {
|
|
||||||
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<goblin::pe::PE> {
|
|
||||||
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],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
struct Vestal {
|
|
||||||
search_paths: Vec<PathBuf>,
|
|
||||||
paths_visited: BTreeSet<Arc<PathBuf>>,
|
|
||||||
path_to_bang: BTreeMap<Arc<PathBuf>, Arc<[u8]>>,
|
|
||||||
path_to_data: BTreeMap<Arc<PathBuf>, Arc<[u8]>>,
|
|
||||||
path_to_pe: BTreeMap<Arc<PathBuf>, Arc<VecPE>>,
|
|
||||||
path_to_rpe: BTreeMap<Arc<PathBuf>, Arc<VecPE>>,
|
|
||||||
addr_to_import: BTreeMap<u32, (String, String)>,
|
|
||||||
//path_to_exports: BTreeMap<Arc<PathBuf>, Vec<ImageExportDescriptor>>,
|
|
||||||
//path_to_imports: BTreeMap<Arc<PathBuf>, Vec<ImageImportDescriptor>>,
|
|
||||||
}
|
|
||||||
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<Option<PathBuf>> {
|
|
||||||
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<PathBuf>, data: &Arc<[u8]>) -> Usually<Arc<VecPE>> {
|
|
||||||
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<PathBuf>, data: &VecPE, base: u64) -> Usually<Arc<VecPE>> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#![feature(slice_split_once)]
|
#![feature(slice_split_once)]
|
||||||
mod util; pub(crate) use self::util::*;
|
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 show; pub(crate) use self::show::*;
|
||||||
mod bang;
|
mod bang;
|
||||||
|
|
||||||
|
|
@ -13,7 +15,7 @@ fn main () -> Usually<()> {
|
||||||
.get_matches();
|
.get_matches();
|
||||||
let path = matches.get_one::<PathBuf>("path").unwrap_or_else(||panic!("pass path to VST DLL"));
|
let path = matches.get_one::<PathBuf>("path").unwrap_or_else(||panic!("pass path to VST DLL"));
|
||||||
let verbose = *(matches.get_one::<bool>("verbose").unwrap_or(&false));
|
let verbose = *(matches.get_one::<bool>("verbose").unwrap_or(&false));
|
||||||
let mut rebuilder = Rebuilder::new(&[
|
let mut rebuilder = Vestal::new(&[
|
||||||
std::env::current_dir()?,
|
std::env::current_dir()?,
|
||||||
canonicalize(path.clone().parent().expect("invalid parent path"))?,
|
canonicalize(path.clone().parent().expect("invalid parent path"))?,
|
||||||
"/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32".into(),
|
"/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32".into(),
|
||||||
|
|
@ -31,7 +33,7 @@ fn main () -> Usually<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct Rebuilder {
|
struct Vestal {
|
||||||
/// Search paths
|
/// Search paths
|
||||||
paths: BTreeSet<Arc<PathBuf>>,
|
paths: BTreeSet<Arc<PathBuf>>,
|
||||||
/// All DLLs in scope
|
/// All DLLs in scope
|
||||||
|
|
@ -40,7 +42,7 @@ struct Rebuilder {
|
||||||
visited: BTreeSet<Arc<PathBuf>>,
|
visited: BTreeSet<Arc<PathBuf>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rebuilder {
|
impl Vestal {
|
||||||
fn new (paths: &[impl AsRef<Path>]) -> Self {
|
fn new (paths: &[impl AsRef<Path>]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
paths: paths.iter().map(|x|Arc::new(x.as_ref().into())).collect(),
|
paths: paths.iter().map(|x|Arc::new(x.as_ref().into())).collect(),
|
||||||
|
|
@ -54,6 +56,9 @@ impl Rebuilder {
|
||||||
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
|
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
|
||||||
return Ok(name)
|
return Ok(name)
|
||||||
}
|
}
|
||||||
|
if verbose {
|
||||||
|
println!("(load {path:?})");
|
||||||
|
}
|
||||||
self.visited.insert(path.clone());
|
self.visited.insert(path.clone());
|
||||||
let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), verbose)?);
|
let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), verbose)?);
|
||||||
self.dlls.insert(dll.name.clone(), dll.clone());
|
self.dlls.insert(dll.name.clone(), dll.clone());
|
||||||
|
|
@ -103,61 +108,76 @@ impl Rebuilder {
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
let addr = (addr - dll.code_base) as usize;
|
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_site(&dll, addr, call.length, 1);
|
||||||
Show::call_dasm(&dll.code[addr..], addr);
|
|
||||||
Show::call_module_method(Some(call));
|
Show::call_module_method(Some(call));
|
||||||
if let Some(method) = dll.parse_call(call) {
|
if let Some(method) = dll.parse_call(call) {
|
||||||
let module_name = call.module.as_ref().unwrap();
|
let module_name = call.module.as_ref().unwrap();
|
||||||
let method_name = call.method.as_ref().unwrap();
|
let method_name = call.method.as_ref().unwrap();
|
||||||
//println!("{BOLD}0x{method:>08x}{RESET} {module_name:20} {method_name}");
|
let path = self.find(module_name, false)?
|
||||||
if let Some(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 = path.file_name()
|
||||||
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
|
.expect("no file name");
|
||||||
if let Some(dll) = self.dlls.get(&name) {
|
let name: Arc<str> = name.to_str().map(Arc::from)
|
||||||
if let Some(thunk) = dll.exports.get(method_name) {
|
.expect("non-unicode filename");
|
||||||
if let ThunkData::Function(rva) = thunk {
|
let dll = self.dlls.get(&name)
|
||||||
let addr = (rva.0 - dll.code_base) as usize;
|
.unwrap_or_else(||panic!("# not found: {name}"));
|
||||||
if recurse {
|
match dll.exports.get(method_name) {
|
||||||
println!("\n {RESET} {BOLD}{} ({method_name}){RESET}", &dll.name);
|
Some(ThunkData::Function(rva)) => {
|
||||||
let mut decoder = Decoder::with_ip(64, &dll.code[addr..], 0, DecoderOptions::NONE);
|
let addr = (rva.0 - dll.code_base) as usize;
|
||||||
while decoder.can_decode() {
|
if recurse {
|
||||||
let position = decoder.position();
|
self.resolve_call_recurse(dll, addr, module_name, method_name)?;
|
||||||
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:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
} else {
|
None => panic!("# not found: {method_name}"),
|
||||||
println!("# not found {call:?}");
|
thunk => panic!("# unsupported {thunk:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn resolve_call_recurse (
|
||||||
|
&self,
|
||||||
|
dll: &Dll,
|
||||||
|
addr: usize,
|
||||||
|
module_name: &Arc<str>,
|
||||||
|
method_name: &Arc<str>,
|
||||||
|
) -> 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Dll {
|
struct Dll {
|
||||||
/// Canonical name like `xxx.dll` (always lowercase)
|
/// Canonical name like `xxx.dll` (always lowercase)
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
|
|
@ -168,8 +188,10 @@ struct Dll {
|
||||||
/// Parsed portable executable
|
/// Parsed portable executable
|
||||||
pe: Arc<VecPE>,
|
pe: Arc<VecPE>,
|
||||||
/// Bytes of `.text` section
|
/// Bytes of `.text` section
|
||||||
code: Arc<[u8]>,
|
text_section: Arc<[u8]>,
|
||||||
/// Start of `.text` section
|
/// Start of `.text` section
|
||||||
|
text_section_start: usize,
|
||||||
|
/// Assumed address in memory
|
||||||
code_base: u32,
|
code_base: u32,
|
||||||
/// Addresses of imported methods by library
|
/// Addresses of imported methods by library
|
||||||
deps_by_library: BTreeMap<Arc<str>, BTreeMap<Arc<str>, u32>>,
|
deps_by_library: BTreeMap<Arc<str>, BTreeMap<Arc<str>, u32>>,
|
||||||
|
|
@ -183,6 +205,24 @@ struct Dll {
|
||||||
exports: BTreeMap<Arc<str>, ThunkData>,
|
exports: BTreeMap<Arc<str>, 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 {
|
impl Dll {
|
||||||
fn new (path: &Arc<PathBuf>, verbose: bool) -> Usually<Self> {
|
fn new (path: &Arc<PathBuf>, verbose: bool) -> Usually<Self> {
|
||||||
if verbose {
|
if verbose {
|
||||||
|
|
@ -190,287 +230,41 @@ impl Dll {
|
||||||
}
|
}
|
||||||
let name = path.file_name().expect("no file name");
|
let name = path.file_name().expect("no file name");
|
||||||
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
|
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
|
||||||
let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice());
|
let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice());
|
||||||
let pe = Arc::new(VecPE::from_disk_data(data.clone()));
|
let pe = Arc::new(VecPE::from_disk_data(data.clone()));
|
||||||
let code = pe.get_section_by_name(".text")?;
|
let code = pe.get_section_by_name(".text")?;
|
||||||
let start = code.pointer_to_raw_data.0 as usize;
|
let start = code.pointer_to_raw_data.0 as usize;
|
||||||
let size = code.size_of_raw_data as usize;
|
let size = code.size_of_raw_data as usize;
|
||||||
let text = &data[start..start+size];
|
let text = &data[start..start+size];
|
||||||
let mut calls_by_source = Default::default();
|
let mut dll = Self {
|
||||||
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 {
|
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
bang,
|
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()? {
|
code_base: match pe.get_valid_nt_headers()? {
|
||||||
NTHeaders::NTHeaders32(h32) => panic!("32 bit headers"),
|
NTHeaders::NTHeaders32(h32) => panic!("32 bit headers"),
|
||||||
NTHeaders::NTHeaders64(h64) => h64.optional_header.base_of_code.0,
|
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,
|
pe,
|
||||||
})
|
|
||||||
}
|
|
||||||
fn parse_call (&self, call: &Arc<Call>) -> Option<u32> {
|
|
||||||
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<Arc<str>, BTreeMap<Arc<str>, u32>>,
|
|
||||||
deps_by_address: &mut BTreeMap<u32, (Arc<str>, Arc<str>)>,
|
|
||||||
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!(
|
//let _exports = dll.collect_exports(verbose)?;
|
||||||
imp,
|
let (_modules_count, _methods_count) = dll.collect_deps(verbose)?;
|
||||||
iat.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "IAT thunk"))),
|
let _calls = dll.collect_calls(verbose)?;
|
||||||
ilt.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "ILT (orig) thunk"))),
|
println!("{dll:?}");
|
||||||
lut.iter().map(|thunk|format!("0x{:08x}", unwrap_thunk(thunk, "lookup thunk"))),
|
Ok(dll)
|
||||||
).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<str> = 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<str> = 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})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if verbose {
|
fn collect_exports (&mut self, _verbose: bool) -> Usually<usize> {
|
||||||
println!(" (deps-modules {modules})");
|
self.exports = ImageExportDirectory::parse(self.pe.as_ref())?
|
||||||
println!(" (deps-methods {methods})");
|
.get_export_map(self.pe.as_ref())?
|
||||||
for (module, methods) in deps_by_library.iter() {
|
.into_iter()
|
||||||
print!(" ({module}");
|
.map(|(k, v)|(k.into(), v))
|
||||||
for (method, addr) in methods.iter() {
|
.collect();
|
||||||
print!("\n (0x{addr:08x} {method})")
|
Ok(self.exports.len())
|
||||||
}
|
|
||||||
println!(")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok((modules, methods))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_exports (
|
|
||||||
pe: &VecPE,
|
|
||||||
_verbose: bool,
|
|
||||||
) -> Usually<BTreeMap<Arc<str>, 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<u32, Arc<Call>>,
|
|
||||||
calls_by_target: &mut BTreeMap<u32, Vec<Arc<Call>>>,
|
|
||||||
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
|
|
||||||
name: &Arc<str>,
|
|
||||||
pe: &VecPE,
|
|
||||||
start: usize,
|
|
||||||
data: &[u8],
|
|
||||||
verbose: bool,
|
|
||||||
) -> Usually<usize> {
|
|
||||||
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::<Vec<_>>());
|
|
||||||
//println!(" (call 0x{target:08x} {external}\n {:?})", sites.iter()
|
|
||||||
//.map(|site|format!("0x{:08x}", site.offset))
|
|
||||||
//.collect::<Vec<_>>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(calls)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_call (
|
|
||||||
name: &Arc<str>,
|
|
||||||
pe: &VecPE,
|
|
||||||
start: usize,
|
|
||||||
data: &[u8],
|
|
||||||
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
|
|
||||||
decoder: &mut Decoder,
|
|
||||||
verbose: bool,
|
|
||||||
) -> Usually<Option<Arc<Call>>> {
|
|
||||||
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<u32, (Arc<str>, Arc<str>)>>, target: u32
|
|
||||||
) -> (Option<Arc<str>>, Option<Arc<str>>, Arc<str>) {
|
|
||||||
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<Arc<str>>,
|
|
||||||
/// Method being called
|
|
||||||
method: Option<Arc<str>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<u32> {
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
pub fn fmt_bytes (bytes: &[u8]) -> Arc<str> {
|
||||||
|
bytes.iter().map(|x|format!("{x:02x}")).join(" ").into()
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Show;
|
pub struct Show;
|
||||||
|
|
||||||
impl Show {
|
impl Show {
|
||||||
pub fn num (n: usize) -> Arc<str> {
|
pub fn num (n: usize) -> Arc<str> {
|
||||||
format!("0x{n:>08x}").into()
|
format!("0x{n:>08x}").into()
|
||||||
}
|
}
|
||||||
pub fn call (
|
pub fn call (dll: &Dll, addr: usize, call: Option<&Arc<Call>>) {
|
||||||
dll: &Dll,
|
let mut decoder = Decoder::with_ip(64, &dll.text_section[addr..], 0x1000, DecoderOptions::NONE);
|
||||||
addr: usize,
|
|
||||||
call: Option<&Arc<Call>>,
|
|
||||||
) {
|
|
||||||
let mut decoder = Decoder::with_ip(64, &dll.code[addr..], 0x1000, DecoderOptions::NONE);
|
|
||||||
let instruction = decoder.decode();
|
let instruction = decoder.decode();
|
||||||
let is_stack = instruction.is_stack_instruction();
|
let is_stack = instruction.is_stack_instruction();
|
||||||
let is_link = Call::matches(&instruction);
|
let is_link = Call::matches(&instruction);
|
||||||
let style = if is_stack || is_link { BOLD } else { DIM };
|
let style = if is_stack || is_link { BOLD } else { DIM };
|
||||||
print!("{style}{} {:20}{RESET}", Show::num(addr), &dll.name);
|
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}");
|
println!(" {style}{instruction}{RESET}");
|
||||||
}
|
}
|
||||||
pub fn call_target_addrs (
|
pub fn call_target_addrs (
|
||||||
|
|
@ -38,15 +38,14 @@ impl Show {
|
||||||
let module = call.map(|call|call.module.as_ref()).flatten();
|
let module = call.map(|call|call.module.as_ref()).flatten();
|
||||||
let method = call.map(|call|call.method.as_ref()).flatten();
|
let method = call.map(|call|call.method.as_ref()).flatten();
|
||||||
if module.is_some() || method.is_some() {
|
if module.is_some() || method.is_some() {
|
||||||
println!(" {GREEN}{}{}{}{RESET}",
|
println!(" ╰-------> {GREEN}{}{}{}{RESET}",
|
||||||
module.map(|x|x.as_ref()).unwrap_or(&""),
|
module.map(|x|x.as_ref()).unwrap_or(&""),
|
||||||
if module.is_some() && method.is_some() { "::" } else { "" },
|
if module.is_some() && method.is_some() { "::" } else { "" },
|
||||||
method.map(|x|x.as_ref()).unwrap_or(&""));
|
method.map(|x|x.as_ref()).unwrap_or(&""));
|
||||||
} else {
|
} else {
|
||||||
println!(" {RED}(unresolved){RESET}");
|
println!(" ╰-------> {RED}(unresolved){RESET} {call:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_site (
|
pub fn call_site (
|
||||||
dll: &Dll,
|
dll: &Dll,
|
||||||
addr: usize,
|
addr: usize,
|
||||||
|
|
@ -63,13 +62,12 @@ impl Show {
|
||||||
for (index, byte) in (byte_start..byte_end).enumerate() {
|
for (index, byte) in (byte_start..byte_end).enumerate() {
|
||||||
write!(&mut output, "{DIM}");
|
write!(&mut output, "{DIM}");
|
||||||
if byte % line == 0 {
|
if byte % line == 0 {
|
||||||
write!(&mut output, " ");
|
|
||||||
if (byte >= snap(addr)) && (byte < snap(addr) + line) {
|
if (byte >= snap(addr)) && (byte < snap(addr) + line) {
|
||||||
write!(&mut output, "{RESET}╭");
|
write!(&mut output, "{RESET} ╭");
|
||||||
} else if byte >= snap(addr) + line {
|
} else if byte >= snap(addr) + line {
|
||||||
write!(&mut output, "┊");
|
write!(&mut output, " ┊");
|
||||||
} else {
|
} else {
|
||||||
write!(&mut output, " ");
|
write!(&mut output, " ");
|
||||||
}
|
}
|
||||||
write!(&mut output, "{:08x}", byte + base);
|
write!(&mut output, "{:08x}", byte + base);
|
||||||
}
|
}
|
||||||
|
|
@ -86,11 +84,11 @@ impl Show {
|
||||||
write!(&mut output, "{}", " ");
|
write!(&mut output, "{}", " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(&mut output, "{:02x}", dll.code[byte]);
|
write!(&mut output, "{:02x}", dll.text_section[byte]);
|
||||||
write!(&mut output, "{RESET}");
|
write!(&mut output, "{RESET}");
|
||||||
if byte % line == line - 1 {
|
if byte % line == line - 1 {
|
||||||
if snap(byte) == snap(addr) {
|
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, " -> {dasm}");
|
||||||
}
|
}
|
||||||
write!(&mut output, " \n");
|
write!(&mut output, " \n");
|
||||||
|
|
@ -98,7 +96,6 @@ impl Show {
|
||||||
}
|
}
|
||||||
print!("{output}");
|
print!("{output}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_dasm (bytes: &[u8], rip: usize) -> Arc<str> {
|
pub fn call_dasm (bytes: &[u8], rip: usize) -> Arc<str> {
|
||||||
let mut decoder = Decoder::with_ip(64, bytes, 0x1000 + rip as u64, DecoderOptions::NONE);
|
let mut decoder = Decoder::with_ip(64, bytes, 0x1000 + rip as u64, DecoderOptions::NONE);
|
||||||
while decoder.can_decode() {
|
while decoder.can_decode() {
|
||||||
|
|
@ -111,14 +108,6 @@ impl Show {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_hex_line (addr: usize, data: &[u8]) -> Arc<str> {
|
|
||||||
format!("{:>08x} {}", addr, fmt_bytes(data)).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fmt_bytes (bytes: &[u8]) -> Arc<str> {
|
|
||||||
bytes.iter().map(|x|format!("{x:02x}")).join(" ").into()
|
|
||||||
}
|
|
||||||
|
|
||||||
//impl Vestal {
|
//impl Vestal {
|
||||||
//pub fn show_addr_to_import (&self) {
|
//pub fn show_addr_to_import (&self) {
|
||||||
//for (addr, (dll, export)) in self.addr_to_import.iter() {
|
//for (addr, (dll, export)) in self.addr_to_import.iter() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue