vestal/crates/.scratch.rs

521 lines
22 KiB
Rust

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(())
}
/// 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(())
//}
/// Collect an imported dependency's descriptor thunks into a temporary [Vec].
///
/// Most of these are currently discarded, but may be needed in the future
/// to resolve some of the trickier linkings.
fn import_collect (pe: &VecPE, descriptor: &ImageImportDescriptor)
-> Usually<(Arc<str>, Vec<(usize, Arc<str>)>)>
{
let mut buffer = vec![];
let name = descriptor.get_name(pe)?.as_str()?.to_lowercase();
for (index, import) in descriptor.get_imports(pe)?.iter().enumerate() {
buffer.push((index, match import {
ImportData::Ordinal(x) => format!("___VESTAL_ORDINAL_{x}"),
ImportData::ImportByName(name) => format!("{name}"),
}.into()));
}
Ok((name.into(), buffer))
//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)?;
//for (index, (import, thunk, orig, lookup)) in izip!(
//imp,
//iat.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "IAT thunk"))),
//ilt.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "ILT (orig) thunk"))),
//lut.iter().map(|thunk|format!("0x{:08x}", Self::thunk_unwrap(thunk, "lookup thunk"))),
//).enumerate() {
//buffer.push((index, import));//, thunk, orig, lookup));
//}
}
//fn thunk_unwrap (thunk: &Thunk, name: impl AsRef<str>) -> u64 {
//match thunk {
//Thunk::Thunk32(t) => panic!("32 bit {}", name.as_ref()),
//Thunk::Thunk64(t) => t.0
//}
//}