mirror of
https://codeberg.org/unspeaker/vestal.git
synced 2025-12-06 08:36:41 +01:00
more refactor for the refactor gods
This commit is contained in:
parent
380d64c892
commit
6660e28287
7 changed files with 476 additions and 472 deletions
436
crates/.scratch.rs
Normal file
436
crates/.scratch.rs
Normal file
|
|
@ -0,0 +1,436 @@
|
||||||
|
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],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(link) = self.decode_link(&mut decoder, verbose)? {
|
||||||
|
links += 1;
|
||||||
|
self.call_sites.insert(link.source, link.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_link (self: Arc<Self>, decoder: &mut Decoder, verbose: bool) -> Usually<Option<Arc<CallSite>>> {
|
||||||
|
let position = decoder.position();
|
||||||
|
let instruction = decoder.decode();
|
||||||
|
let opcodes = &self.code[position..position+instruction.len()];
|
||||||
|
if CallSite::matches(&instruction) && !CallSite::skip(opcodes) {
|
||||||
|
let offset = (position + self.code_start) as u32;
|
||||||
|
let source = self.pe.offset_to_rva(Offset(offset))?.0;
|
||||||
|
if let Some(target) = Self::target(opcodes, source) {
|
||||||
|
let module: Arc<Module> = Module::default().into();
|
||||||
|
let method: Arc<str> = "TODO".into();
|
||||||
|
if verbose {
|
||||||
|
let offset = format!("0x{offset:08x}");
|
||||||
|
let source = format!("0x{source:08x}");
|
||||||
|
let instruct = format!("{BOLD}{instruction:30}{RESET}");
|
||||||
|
let target = format!("0x{target:08x}");
|
||||||
|
let external = format!("{module}::{method}");
|
||||||
|
println!("({BOLD}{offset}{RESET} {source} {instruct} {target} {external}");
|
||||||
|
}
|
||||||
|
return Ok(Some(Arc::new(CallSite {
|
||||||
|
caller: self.clone(),
|
||||||
|
offset,
|
||||||
|
source,
|
||||||
|
length: opcodes.len(),
|
||||||
|
target,
|
||||||
|
module,
|
||||||
|
method,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dep_name (&self, target: u32) -> (Option<Arc<str>>, Option<Arc<str>>, Arc<str>) {
|
||||||
|
(None, None, "".into())
|
||||||
|
let deps = Some(&self.deps_by_address);
|
||||||
|
let module = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.0.clone());
|
||||||
|
let method = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.1.clone());
|
||||||
|
let external = format!("{}::{}",
|
||||||
|
module.as_ref().map(|x|x.as_ref()).unwrap_or("???"),
|
||||||
|
method.as_ref().map(|x|x.as_ref()).unwrap_or("???"));
|
||||||
|
(module, method, external.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn links_collect (&mut self, verbose: bool) -> Usually<usize> {
|
||||||
|
let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0);
|
||||||
|
let mut links = 0;
|
||||||
|
while decoder.can_decode() {
|
||||||
|
if let Some(link) = self.decode_link(&mut decoder, verbose)? {
|
||||||
|
links += 1;
|
||||||
|
//self.links_by_source.insert(link.source, link.clone());
|
||||||
|
//if !self.links_by_target.contains_key(&link.target) {
|
||||||
|
//self.links_by_target.insert(link.target, Default::default());
|
||||||
|
//}
|
||||||
|
//self.links_by_target.get_mut(&link.target).unwrap().push(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
println!(" (link-sites {links})");
|
||||||
|
for (target, sites) in self.links_by_target.iter() {
|
||||||
|
let (_, _, external) = self.dep_name(*target);
|
||||||
|
println!(" ({:>5}x link 0x{target:08x} {external})", sites.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(links)
|
||||||
|
}
|
||||||
|
/ Collect all imported dependencies.
|
||||||
|
fn deps_collect (&mut self, verbose: bool) -> Usually<(usize, usize)> {
|
||||||
|
let pe = self.pe.clone();
|
||||||
|
let directory = ImportDirectory::parse(pe.as_ref())?;
|
||||||
|
let mut modules = 0;
|
||||||
|
let mut methods = 0;
|
||||||
|
for descriptor in directory.descriptors.iter() {
|
||||||
|
let (module_name, buffer) = import_collect(pe.as_ref(), descriptor)?;
|
||||||
|
for (index, import_name) in buffer {
|
||||||
|
let link_via = descriptor.first_thunk.0 + index as u32 * 8;
|
||||||
|
let (new_modules, new_methods) = self.dep_collect(
|
||||||
|
link_via, &module_name, &import_name, verbose
|
||||||
|
)?;
|
||||||
|
modules += new_modules;
|
||||||
|
methods += new_methods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
println!(" (deps-modules {modules})");
|
||||||
|
println!(" (deps-methods {methods})");
|
||||||
|
self.show_deps_by_library();
|
||||||
|
}
|
||||||
|
Ok((modules, methods))
|
||||||
|
}
|
||||||
|
/ Relink a VST.
|
||||||
|
fn run (&mut self, path: impl AsRef<Path>) -> Usually<()> {
|
||||||
|
let path = path.as_ref().to_str().expect("path must be unicode");
|
||||||
|
let path = self.find(path)?.unwrap_or_else(||panic!("# not found: {path:?}"));
|
||||||
|
let main = self.load(&path)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/ Load all dependencies recursively, starting from the given path.
|
||||||
|
fn load_all (&mut self, path: &impl AsRef<Path>) -> Usually<Arc<Module>> {
|
||||||
|
let name = to_dll_name(path);
|
||||||
|
let path: Arc<PathBuf> = Arc::from(PathBuf::from(path.as_ref()));
|
||||||
|
if self.modules.contains_key(&name) {
|
||||||
|
let module = self.modules.get(&name).unwrap().clone();
|
||||||
|
return Ok(module)
|
||||||
|
}
|
||||||
|
Log::load(self.verbose, &path);
|
||||||
|
let module = Arc::new(Module::new(&path, self.verbose)?.load(self.verbose)?);
|
||||||
|
self.modules.insert(module.name.clone(), module.clone());
|
||||||
|
Ok(module)
|
||||||
|
}
|
||||||
|
fn resolve (&self, dll: &Module, addr: u32, link: &Arc<CallSite>) -> Usually<()> {
|
||||||
|
let addr = (addr - dll.code_base) as usize;
|
||||||
|
dll.show_call_site(addr, link.length, 1);
|
||||||
|
link.show();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn resolve_recursively (&self, dll: &Module, addr: u32, link: &Arc<CallSite>) -> Usually<()> {
|
||||||
|
self.resolve(dll, addr, link)?;
|
||||||
|
let module = &link.target.name;
|
||||||
|
let export = &link.method;
|
||||||
|
if let Some((module, export)) = match self.modules
|
||||||
|
.get(module)
|
||||||
|
.map(|module|module.exports.get(export))
|
||||||
|
{
|
||||||
|
Some(Some(ThunkData::Function(rva))) => Some((module.clone(), export.clone())),
|
||||||
|
Some(Some(ThunkData::ForwarderString(rva))) => dll.resolve_forward(&rva)?,
|
||||||
|
Some(Some(thunk)) => panic!("# unsupported {thunk:?}"),
|
||||||
|
Some(None) => panic!("# export not resolved: {export}"),
|
||||||
|
None => panic!("# module not resolved: {module}"),
|
||||||
|
} {
|
||||||
|
let module = self.modules.get(&module).unwrap_or_else(||panic!("# no module {module}"));
|
||||||
|
let method = module.exports.get(&export).unwrap_or_else(||panic!("# no method {export}"));
|
||||||
|
println!("{module:?} {export} {method:?}");
|
||||||
|
} else {
|
||||||
|
panic!("# unresolved link at {:?} [{addr}]", &dll.name)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -1,388 +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)
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
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],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//if let Some(link) = self.decode_link(&mut decoder, verbose)? {
|
|
||||||
//links += 1;
|
|
||||||
//self.call_sites.insert(link.source, link.clone());
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//fn decode_link (self: Arc<Self>, decoder: &mut Decoder, verbose: bool) -> Usually<Option<Arc<CallSite>>> {
|
|
||||||
//let position = decoder.position();
|
|
||||||
//let instruction = decoder.decode();
|
|
||||||
//let opcodes = &self.code[position..position+instruction.len()];
|
|
||||||
//if CallSite::matches(&instruction) && !CallSite::skip(opcodes) {
|
|
||||||
//let offset = (position + self.code_start) as u32;
|
|
||||||
//let source = self.pe.offset_to_rva(Offset(offset))?.0;
|
|
||||||
//if let Some(target) = Self::target(opcodes, source) {
|
|
||||||
//let module: Arc<Module> = Module::default().into();
|
|
||||||
//let method: Arc<str> = "TODO".into();
|
|
||||||
//if verbose {
|
|
||||||
//let offset = format!("0x{offset:08x}");
|
|
||||||
//let source = format!("0x{source:08x}");
|
|
||||||
//let instruct = format!("{BOLD}{instruction:30}{RESET}");
|
|
||||||
//let target = format!("0x{target:08x}");
|
|
||||||
//let external = format!("{module}::{method}");
|
|
||||||
//println!("({BOLD}{offset}{RESET} {source} {instruct} {target} {external}");
|
|
||||||
//}
|
|
||||||
//return Ok(Some(Arc::new(CallSite {
|
|
||||||
//caller: self.clone(),
|
|
||||||
//offset,
|
|
||||||
//source,
|
|
||||||
//length: opcodes.len(),
|
|
||||||
//target,
|
|
||||||
//module,
|
|
||||||
//method,
|
|
||||||
//})))
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//Ok(None)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//fn dep_name (&self, target: u32) -> (Option<Arc<str>>, Option<Arc<str>>, Arc<str>) {
|
|
||||||
//(None, None, "".into())
|
|
||||||
//let deps = Some(&self.deps_by_address);
|
|
||||||
//let module = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.0.clone());
|
|
||||||
//let method = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.1.clone());
|
|
||||||
//let external = format!("{}::{}",
|
|
||||||
//module.as_ref().map(|x|x.as_ref()).unwrap_or("???"),
|
|
||||||
//method.as_ref().map(|x|x.as_ref()).unwrap_or("???"));
|
|
||||||
//(module, method, external.into())
|
|
||||||
//}
|
|
||||||
|
|
||||||
//fn links_collect (&mut self, verbose: bool) -> Usually<usize> {
|
|
||||||
//let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0);
|
|
||||||
//let mut links = 0;
|
|
||||||
//while decoder.can_decode() {
|
|
||||||
//if let Some(link) = self.decode_link(&mut decoder, verbose)? {
|
|
||||||
//links += 1;
|
|
||||||
////self.links_by_source.insert(link.source, link.clone());
|
|
||||||
////if !self.links_by_target.contains_key(&link.target) {
|
|
||||||
////self.links_by_target.insert(link.target, Default::default());
|
|
||||||
////}
|
|
||||||
////self.links_by_target.get_mut(&link.target).unwrap().push(link);
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//if verbose {
|
|
||||||
//println!(" (link-sites {links})");
|
|
||||||
//for (target, sites) in self.links_by_target.iter() {
|
|
||||||
//let (_, _, external) = self.dep_name(*target);
|
|
||||||
//println!(" ({:>5}x link 0x{target:08x} {external})", sites.len());
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//Ok(links)
|
|
||||||
//}
|
|
||||||
/// Collect all imported dependencies.
|
|
||||||
//fn deps_collect (&mut self, verbose: bool) -> Usually<(usize, usize)> {
|
|
||||||
//let pe = self.pe.clone();
|
|
||||||
//let directory = ImportDirectory::parse(pe.as_ref())?;
|
|
||||||
//let mut modules = 0;
|
|
||||||
//let mut methods = 0;
|
|
||||||
//for descriptor in directory.descriptors.iter() {
|
|
||||||
//let (module_name, buffer) = import_collect(pe.as_ref(), descriptor)?;
|
|
||||||
//for (index, import_name) in buffer {
|
|
||||||
//let link_via = descriptor.first_thunk.0 + index as u32 * 8;
|
|
||||||
//let (new_modules, new_methods) = self.dep_collect(
|
|
||||||
//link_via, &module_name, &import_name, verbose
|
|
||||||
//)?;
|
|
||||||
//modules += new_modules;
|
|
||||||
//methods += new_methods;
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//if verbose {
|
|
||||||
//println!(" (deps-modules {modules})");
|
|
||||||
//println!(" (deps-methods {methods})");
|
|
||||||
//self.show_deps_by_library();
|
|
||||||
//}
|
|
||||||
//Ok((modules, methods))
|
|
||||||
//}
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Collect all calls that point to imports.
|
/// Collect all calls that point to imports.
|
||||||
pub fn load_call_sites (&mut self) -> Usually<usize> {
|
pub fn load_call_sites (self: Arc<Self>) -> Usually<Arc<Self>> {
|
||||||
let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0);
|
let mut decoder = Decoder::with_ip(64, self.code.as_ref(), 0, 0);
|
||||||
let mut links = 0;
|
|
||||||
while decoder.can_decode() {
|
while decoder.can_decode() {
|
||||||
let position = decoder.position();
|
let position = decoder.position();
|
||||||
let instruction = decoder.decode();
|
let instruction = decoder.decode();
|
||||||
|
|
@ -13,7 +12,7 @@ impl Module {
|
||||||
let source = self.pe.offset_to_rva(Offset(offset))?.0;
|
let source = self.pe.offset_to_rva(Offset(offset))?.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(links)
|
Ok(self)
|
||||||
}
|
}
|
||||||
fn target (opcodes: &[u8], offset_rva: u32) -> Option<u32> {
|
fn target (opcodes: &[u8], offset_rva: u32) -> Option<u32> {
|
||||||
let rip = offset_rva + opcodes.len() as u32;
|
let rip = offset_rva + opcodes.len() as u32;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Collect all exported methods.
|
/// Collect all exported methods.
|
||||||
pub fn load_exports (&mut self) -> Usually<usize> {
|
pub fn load_exports (self: Arc<Self>) -> Usually<Arc<Self>> {
|
||||||
let directory = ExportDirectory::parse(self.pe.as_ref())?;
|
let directory = ExportDirectory::parse(self.pe.as_ref())?;
|
||||||
let export_map = directory.get_export_map(self.pe.as_ref())?;
|
let export_map = directory.get_export_map(self.pe.as_ref())?;
|
||||||
self.exports = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect();
|
*self.exports.write().unwrap() = export_map.into_iter().map(|(k, v)|(k.into(), v)).collect();
|
||||||
Ok(self.exports.len())
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,29 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Collect all imported modules and methods.
|
/// Collect all imported modules and methods.
|
||||||
pub fn load_imports (&mut self) -> Usually<()> {
|
pub fn load_imports (self: Arc<Self>, recurse: bool) -> Usually<Arc<Self>> {
|
||||||
let pe = self.pe.clone();
|
let pe = self.pe.clone();
|
||||||
let directory = ImportDirectory::parse(pe.as_ref())?;
|
let directory = ImportDirectory::parse(pe.as_ref())?;
|
||||||
for descriptor in directory.descriptors.iter() {
|
for descriptor in directory.descriptors.iter() {
|
||||||
let (module, imports) = Self::parse_import(pe.as_ref(), descriptor)?;
|
let (name, imports) = Self::parse_import(pe.as_ref(), descriptor)?;
|
||||||
Log::import(self.verbose, &module, &imports);
|
Log::dep(self.verbose, &name, &imports);
|
||||||
for (index, method) in imports {
|
let exists = self.namespace.read().unwrap().contains_key(&name);
|
||||||
let call_via = descriptor.first_thunk.0 + index as u32 * 8;
|
println!("{exists} {name} {:?}", self.namespace.read().unwrap().keys().collect::<Vec<_>>());
|
||||||
self.load_dependency(call_via, &module, &method);
|
if !exists {
|
||||||
self.load_import(call_via, &module, &method);
|
let path = self.find(&name)?.expect("not found");
|
||||||
|
let module = Arc::new(Self {
|
||||||
|
namespace: self.namespace.clone(),
|
||||||
|
search_paths: self.search_paths.clone(),
|
||||||
|
..Self::from_path(&path, self.verbose)?
|
||||||
|
});
|
||||||
|
self.namespace.write().unwrap().insert(module.name.clone(), module.clone());
|
||||||
|
module.load(recurse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.verbose {
|
if self.verbose {
|
||||||
self.show_dependencies();
|
self.show_dependencies();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect an imported dependency's descriptor thunks into a temporary [Vec].
|
/// Collect an imported dependency's descriptor thunks into a temporary [Vec].
|
||||||
|
|
@ -24,14 +31,16 @@ impl Module {
|
||||||
-> Usually<(Arc<str>, Vec<(usize, Arc<str>)>)>
|
-> Usually<(Arc<str>, Vec<(usize, Arc<str>)>)>
|
||||||
{
|
{
|
||||||
let mut imports = vec![];
|
let mut imports = vec![];
|
||||||
let module = descriptor.get_name(pe)?.as_str()?.to_lowercase();
|
|
||||||
for (index, import) in descriptor.get_imports(pe)?.iter().enumerate() {
|
for (index, import) in descriptor.get_imports(pe)?.iter().enumerate() {
|
||||||
imports.push((index, match import {
|
imports.push((index, match import {
|
||||||
ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"),
|
ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"),
|
||||||
ImportData::ImportByName(name) => format!("{name}"),
|
ImportData::ImportByName(name) => format!("{name}"),
|
||||||
}.into()));
|
}.into()));
|
||||||
}
|
}
|
||||||
Ok((module.into(), imports))
|
Ok((
|
||||||
|
descriptor.get_name(pe)?.as_str()?.to_lowercase().into(),
|
||||||
|
imports
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_import (
|
fn load_import (
|
||||||
|
|
@ -45,9 +54,9 @@ impl Module {
|
||||||
|
|
||||||
fn load_dependency (
|
fn load_dependency (
|
||||||
&mut self,
|
&mut self,
|
||||||
call_via: u32,
|
|
||||||
module_name: &Arc<str>,
|
module_name: &Arc<str>,
|
||||||
method_name: &Arc<str>,
|
method_name: &Arc<str>,
|
||||||
|
call_via: u32,
|
||||||
) -> (usize, usize) {
|
) -> (usize, usize) {
|
||||||
let mut modules = 0;
|
let mut modules = 0;
|
||||||
let mut methods = 0;
|
let mut methods = 0;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ pub struct Module {
|
||||||
/// Call targets to methods from imported modules.
|
/// Call targets to methods from imported modules.
|
||||||
pub imports: BTreeMap<u32, (Arc<str>, Arc<str>)>,
|
pub imports: BTreeMap<u32, (Arc<str>, Arc<str>)>,
|
||||||
/// Addresses of exported methods by name
|
/// Addresses of exported methods by name
|
||||||
pub exports: BTreeMap<Arc<str>, ThunkData>,
|
pub exports: RwLock<BTreeMap<Arc<str>, ThunkData>>,
|
||||||
/// Locations in `.text` section that need to be patched
|
/// Locations in `.text` section that need to be patched
|
||||||
pub call_sites: BTreeMap<u32, Arc<CallSite>>,
|
pub call_sites: BTreeMap<u32, Arc<CallSite>>,
|
||||||
/// More detailed output.
|
/// More detailed output.
|
||||||
|
|
@ -73,11 +73,11 @@ fn main () -> Usually<()> {
|
||||||
// Parse command line arguments.
|
// Parse command line arguments.
|
||||||
let args = cli().get_matches();
|
let args = cli().get_matches();
|
||||||
if let Some(path) = args.get_one::<PathBuf>("path") {
|
if let Some(path) = args.get_one::<PathBuf>("path") {
|
||||||
Module::from_path(path, *(args.get_one::<bool>("verbose").unwrap_or(&false)))?
|
Arc::new(Module::from_path(path, *(args.get_one::<bool>("verbose").unwrap_or(&false)))?)
|
||||||
.search(std::env::current_dir()?)
|
.search(std::env::current_dir()?)
|
||||||
.search(canonicalize(path.clone().parent().expect("invalid parent path"))?)
|
.search(canonicalize(path.clone().parent().expect("invalid parent path"))?)
|
||||||
.search("/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32")
|
.search("/home/user/Lab/Cosmo/wineprefix/drive_c/windows/system32")
|
||||||
.load()?;
|
.load(true)?;
|
||||||
} else {
|
} else {
|
||||||
println!("Pass a path to a VST DLL");
|
println!("Pass a path to a VST DLL");
|
||||||
}
|
}
|
||||||
|
|
@ -112,18 +112,17 @@ impl Module {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Add a search path
|
/// Add a search path
|
||||||
fn search (mut self, path: impl AsRef<Path>) -> Self {
|
fn search (self: Arc<Self>, path: impl AsRef<Path>) -> Arc<Self> {
|
||||||
Log::add(self.verbose, &path);
|
Log::add(self.verbose, &path);
|
||||||
self.search_paths.write().unwrap().insert(path.as_ref().to_path_buf().into());
|
self.search_paths.write().unwrap().insert(path.as_ref().to_path_buf().into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Load the dependency tree, starting from this module.
|
/// Load the dependency tree, starting from this module.
|
||||||
fn load (mut self) -> Usually<Self> {
|
fn load (self: Arc<Self>, recurse: bool) -> Usually<Arc<Self>> {
|
||||||
let _ = self.load_imports()?;
|
Ok(self
|
||||||
let _ = self.load_exports()?;
|
.load_imports(recurse)?
|
||||||
let _ = self.load_call_sites()?;
|
.load_exports()?
|
||||||
println!("{self:?}");
|
.load_call_sites()?)
|
||||||
Ok(self)
|
|
||||||
}
|
}
|
||||||
/// Search for DLL by name in search paths.
|
/// Search for DLL by name in search paths.
|
||||||
fn find (&self, name: &impl AsRef<str>) -> Usually<Option<PathBuf>> {
|
fn find (&self, name: &impl AsRef<str>) -> Usually<Option<PathBuf>> {
|
||||||
|
|
@ -132,68 +131,17 @@ impl Module {
|
||||||
let mut path = base.as_ref().clone();
|
let mut path = base.as_ref().clone();
|
||||||
path.push(name.to_lowercase());
|
path.push(name.to_lowercase());
|
||||||
if self.verbose {
|
if self.verbose {
|
||||||
println!("# looking for {name} at {path:?}");
|
println!("(find? {name} {path:?})");
|
||||||
}
|
}
|
||||||
if std::fs::exists(&path)? {
|
if std::fs::exists(&path)? {
|
||||||
if self.verbose {
|
if self.verbose {
|
||||||
println!("# found {name} at {path:?}");
|
println!("(found {name} {path:?})");
|
||||||
|
|
||||||
}
|
}
|
||||||
return Ok(Some(canonicalize(&path)?))
|
return Ok(Some(canonicalize(&path)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
/// Relink a VST.
|
|
||||||
//fn run (&mut self, path: impl AsRef<Path>) -> Usually<()> {
|
|
||||||
//let path = path.as_ref().to_str().expect("path must be unicode");
|
|
||||||
//let path = self.find(path)?.unwrap_or_else(||panic!("# not found: {path:?}"));
|
|
||||||
//let main = self.load(&path)?;
|
|
||||||
//Ok(())
|
|
||||||
//}
|
|
||||||
/// Load all dependencies recursively, starting from the given path.
|
|
||||||
//fn load_all (&mut self, path: &impl AsRef<Path>) -> Usually<Arc<Module>> {
|
|
||||||
//let name = to_dll_name(path);
|
|
||||||
//let path: Arc<PathBuf> = Arc::from(PathBuf::from(path.as_ref()));
|
|
||||||
//if self.modules.contains_key(&name) {
|
|
||||||
//let module = self.modules.get(&name).unwrap().clone();
|
|
||||||
//return Ok(module)
|
|
||||||
//}
|
|
||||||
//Log::load(self.verbose, &path);
|
|
||||||
//let module = Arc::new(Module::new(&path, self.verbose)?.load(self.verbose)?);
|
|
||||||
//self.modules.insert(module.name.clone(), module.clone());
|
|
||||||
//Ok(module)
|
|
||||||
//}
|
|
||||||
fn resolve (&self, dll: &Module, addr: u32, link: &Arc<CallSite>) -> Usually<()> {
|
|
||||||
let addr = (addr - dll.code_base) as usize;
|
|
||||||
dll.show_call_site(addr, link.length, 1);
|
|
||||||
link.show();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn resolve_recursively (&self, dll: &Module, addr: u32, link: &Arc<CallSite>) -> Usually<()> {
|
|
||||||
self.resolve(dll, addr, link)?;
|
|
||||||
let module = &link.target.name;
|
|
||||||
let export = &link.method;
|
|
||||||
//if let Some((module, export)) = match self.modules
|
|
||||||
//.get(module)
|
|
||||||
//.map(|module|module.exports.get(export))
|
|
||||||
//{
|
|
||||||
//Some(Some(ThunkData::Function(rva))) => Some((module.clone(), export.clone())),
|
|
||||||
//Some(Some(ThunkData::ForwarderString(rva))) => dll.resolve_forward(&rva)?,
|
|
||||||
//Some(Some(thunk)) => panic!("# unsupported {thunk:?}"),
|
|
||||||
//Some(None) => panic!("# export not resolved: {export}"),
|
|
||||||
//None => panic!("# module not resolved: {module}"),
|
|
||||||
//} {
|
|
||||||
//let module = self.modules.get(&module).unwrap_or_else(||panic!("# no module {module}"));
|
|
||||||
//let method = module.exports.get(&export).unwrap_or_else(||panic!("# no method {export}"));
|
|
||||||
//println!("{module:?} {export} {method:?}");
|
|
||||||
//} else {
|
|
||||||
//panic!("# unresolved link at {:?} [{addr}]", &dll.name)
|
|
||||||
//}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Module {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_dll_name (path: &impl AsRef<Path>) -> Arc<str> {
|
fn to_dll_name (path: &impl AsRef<Path>) -> Arc<str> {
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ impl Log {
|
||||||
println!("(load {:?})", path.as_ref());
|
println!("(load {:?})", path.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn import (show: bool, module: &Arc<str>, exports: &[(usize, Arc<str>)]) {
|
pub fn dep (show: bool, name: &impl AsRef<str>, exports: &[(usize, Arc<str>)]) {
|
||||||
if show {
|
if show {
|
||||||
println!("(import {:?} {})", module, exports.len());
|
println!("(dep {:?} {})", name.as_ref(), exports.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ impl std::fmt::Debug for Module {
|
||||||
//self.links_by_source.len(),
|
//self.links_by_source.len(),
|
||||||
//self.links_by_target.len());
|
//self.links_by_target.len());
|
||||||
let exports = format!("(exp {:>5})",
|
let exports = format!("(exp {:>5})",
|
||||||
self.exports.len());
|
self.exports.read().unwrap().len());
|
||||||
write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] (img 0x{:>08x} -> mem 0x{:>08x}) {deps} {links} {exports})",
|
write!(f, "(dll {BOLD}{UNDERLINE}{:15}{RESET} [0x{:>08x}] (img 0x{:>08x} -> mem 0x{:>08x}) {deps} {links} {exports})",
|
||||||
&self.name,
|
&self.name,
|
||||||
self.code_size,
|
self.code_size,
|
||||||
|
|
@ -72,7 +72,7 @@ impl Module {
|
||||||
pub fn show_dependencies (&self) {
|
pub fn show_dependencies (&self) {
|
||||||
for (name, module) in self.dependencies.iter() {
|
for (name, module) in self.dependencies.iter() {
|
||||||
print!(" ({module:?}");
|
print!(" ({module:?}");
|
||||||
for (method, addr) in module.exports.iter() {
|
for (method, addr) in module.exports.read().unwrap().iter() {
|
||||||
//print!("\n (0x{addr:08x} {method})")
|
//print!("\n (0x{addr:08x} {method})")
|
||||||
}
|
}
|
||||||
println!(")");
|
println!(")");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue