diff --git a/.gitignore b/.gitignore index eb5a316..4c2add3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +*.dll diff --git a/Cargo.lock b/Cargo.lock index cbf2884..7b1d6e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,6 +449,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + [[package]] name = "encode_unicode" version = "1.0.0" @@ -1299,6 +1305,7 @@ name = "vestal" version = "0.1.0" dependencies = [ "clap 4.5.20", + "elf", "exe", "goblin 0.9.2", "lancelot", diff --git a/crates/vestal/Cargo.toml b/crates/vestal/Cargo.toml index 3ac8100..db889e0 100644 --- a/crates/vestal/Cargo.toml +++ b/crates/vestal/Cargo.toml @@ -9,4 +9,5 @@ goblin = "0.9.2" clap = { version = "4.5.4", features = [ "derive" ] } lancelot = "0.8.6" syscalls = "0.6.18" +elf = "0.7.4" #falcon = "0.5.5" diff --git a/crates/vestal/src/execute.rs b/crates/vestal/src/execute.rs index 8b4a458..c14a8c9 100644 --- a/crates/vestal/src/execute.rs +++ b/crates/vestal/src/execute.rs @@ -1,25 +1,68 @@ use crate::*; use syscalls::{Sysno, syscall}; -static NAME: &'static [char] = &['\0']; +use elf::file::Elf64_Ehdr; +use std::ffi::CString; +use std::str::FromStr; +static NAME: &'static [u8] = &[b'\0']; impl Vestal { - pub fn execute (&self, path: impl AsRef) -> Usually<()> { - Self::with_pe(&path, |buffer, pe, main, deps|{ - let fd = Self::get_fd(); - // TODO: compose in-memory ELF binary out of PE sections and Wine libraries - Self::write(fd, buffer); - println!("{fd}"); + pub fn execute_data (&self, data: &[u8]) -> Usually<()> { + Ok(()) + } + pub fn execute_path (&self, path: impl AsRef) -> Usually<()> { + Self::from_path(&path, |buffer, pe, main, deps|{ + if let Some(main) = main { + println!("VSTPluginMain: {:?} + {:?}", &main.rva, &main.offset); + } else { + panic!("VSTPluginMain not found. This is not a valid VST plugin."); + } + println!("Imports: {:#?}", &pe.imports.len()); + println!("Dependencies: {:#?}", &deps.len()); + for (dll, imports) in deps.iter() { + println!("- {dll}"); + for import in imports.iter() { + println!(" {:8} + {:8} {:32}", import.rva, import.offset, import.name); + } + } }) } - fn get_fd () -> usize { - match unsafe { syscall!(Sysno::memfd_create, NAME.as_ptr(), 0x0001, 0) } { - Err(no) => panic!("memfd_create failed: {no}"), - Ok(fd) => fd, - } - } - fn write (fd: usize, buffer: &[u8]) { - match unsafe { syscall!(Sysno::write, fd, buffer.as_ptr()) } { - Err(no) => panic!("write failed: {no}"), - Ok(_) => (), - } +} +fn make_elf () -> Vec { + let mut buffer = vec![0;1024*1024*1024]; + // https://wiki.osdev.org/ELF#ELF_Header + buffer[0x00..0x40].copy_from_slice(any_as_u8_slice(&Elf64_Ehdr { + e_ehsize: 0x40, // elf header size, + e_ident: [ // identification data + 0x7f, b'E', b'L', b'F', // magic bytes + 0x02, // 64-bit, 0x01 is 32-bit + 0x01, // little-endian + 0x01, // ELF header version + 0x00, // SysV ABI + 0x00, 0x00, 0x00, 0x00, // unused + 0x00, 0x00, 0x00, 0x00, // unused + ], + e_version: 0x01, // ELF version + e_type: 0x02, // executable + e_machine: 0x3e, // x86_64, 0x03 = x86 + e_flags: 0x00, // TODO machine flags, + e_entry: 0x00, // TODO entry point (from wrapper?) + + e_phnum: 0x00, // TODO program headers + e_phentsize: 0x00, // TODO why is there phent in my elf? + e_phoff: 0x00, // TODO program header table offset + + e_shnum: 0x00, // TODO section headers + e_shentsize: 0x00, // TODO section header entry size + e_shoff: 0x00, // TODO section header table offset + e_shstrndx: 0x00, // TODO section header table index something something + })); + buffer +} +/// From https://stackoverflow.com/a/42186553 +fn any_as_u8_slice(p: &T) -> &[u8] { + unsafe { + ::core::slice::from_raw_parts( + (p as *const T) as *const u8, + ::core::mem::size_of::(), + ) } } diff --git a/crates/vestal/src/inspect.rs b/crates/vestal/src/inspect.rs index 2cf5e26..d7e35d9 100644 --- a/crates/vestal/src/inspect.rs +++ b/crates/vestal/src/inspect.rs @@ -1,8 +1,9 @@ use crate::*; impl Vestal { - pub fn inspect (&self, path: impl AsRef) -> Usually<()> { - Self::with_pe(&path, |buffer, pe, main, deps|{ + pub fn inspect_data (&self, path: impl AsRef) -> Usually<()> { Ok(()) } + pub fn inspect_path (&self, path: impl AsRef) -> Usually<()> { + Self::from_path(&path, |buffer, pe, main, deps|{ if let Some(main) = main { println!("VSTPluginMain: {:?} + {:?}", &main.rva, &main.offset); } else { diff --git a/crates/vestal/src/main.rs b/crates/vestal/src/main.rs index d4e5ab9..128779f 100644 --- a/crates/vestal/src/main.rs +++ b/crates/vestal/src/main.rs @@ -1,5 +1,7 @@ mod execute; mod inspect; +mod memrun; +mod parse; pub(crate) use std::path::Path; pub(crate) use goblin::{error, Object, pe::{PE, import::Import, export::Export}}; use std::collections::HashMap; @@ -18,22 +20,37 @@ pub enum Vestal { Inspect { path: String }, /// Load a VST DLL into memory Execute { path: String }, + /// Load a VST DLL from hashbang + Loader { path: String } +} + +pub enum Arch { + ThirtyTwo, + SixtyFour, } impl Vestal { pub fn run (&self) -> Usually<()> { match self { - Self::Inspect { path } => self.inspect(path.as_str()), - Self::Execute { path } => self.execute(path.as_str()), + Self::Inspect { path } => self.inspect_path(path.as_str()), + Self::Execute { path } => self.execute_path(path.as_str()), + Self::Loader { path } => self.execute_path(path.as_str()), } } - pub fn with_pe ( + pub fn with_path ( path: &impl AsRef, cb: impl Fn(&[u8], &PE, Option<&Export>, HashMap>) ) -> Usually<()> { - println!("PE: {}", path.as_ref().display()); + println!("Path: {}", path.as_ref().display()); let buffer = std::fs::read(path.as_ref())?; - if let Object::PE(ref pe) = Object::parse(&buffer)? { + Self::with_data(buffer.as_slice(), cb) + } + pub fn with_data ( + buffer: &[u8], + callback: impl Fn(&[u8], &PE, Option<&Export>, HashMap>) + ) -> Usually<()> { + println!("PE: {}b", buffer.len()); + if let Object::PE(ref pe) = Object::parse(buffer)? { let mut main = None; let mut imports: HashMap<_, _> = Default::default(); for import in pe.imports.iter() { @@ -50,7 +67,7 @@ impl Vestal { break } } - cb(&buffer, pe, main, imports); + callback(&buffer, pe, main, imports); Ok(()) } else { Err("not a PE".into()) diff --git a/crates/vestal/src/memrun.rs b/crates/vestal/src/memrun.rs new file mode 100644 index 0000000..f7df2f7 --- /dev/null +++ b/crates/vestal/src/memrun.rs @@ -0,0 +1,57 @@ +use std::fmt::{Debug, Display}; +use syscalls::{Sysno, Errno, syscall}; + +static EMPTY: &'static [u8] = &[b'\0']; +static CMD: &'static [u8] = &[b'/',b'p',b'r',b'o',b'c',b'/',b's',b'e',b'l',b'f',b'/',b'f',b'd',b'/',b'3',b'\0']; +static ARGS: &'static [u8] = &[b'\0',b'\0']; +static ENVS: &'static [u8] = &[b'\0']; + +pub struct MemRun(usize); + +#[derive(Debug)] +pub enum MemRunError { + MemfdCreateFailed(Errno), + ExecveFailed(Errno), +} + +impl MemRun { + pub fn new () -> Result { + unsafe { syscall!(Sysno::memfd_create, EMPTY.as_ptr(), 0x0001, 0) } + .map_err(MemRunError::MemfdCreateFailed) + .map(Self) + } + pub fn run (code: &[u8]) -> Result<(), MemRunError> { + let cmd = b"/proc/self/fd/3\0"; + let arg = [b"custom process name\0"]; + let env = [b"\0"]; + unsafe { syscall!(Sysno::execve, CMD.as_ptr(), ARGS.as_ptr(), ENVS.as_ptr()) } + .map_err(MemRunError::ExecveFailed) + .map(|_|()) + } +} + +// From https://github.com/guitmz/memrun/ +fn get_fd () -> usize { + match unsafe { syscall!(Sysno::memfd_create, EMPTY.as_ptr(), 0x0001, 0) } { + Err(no) => panic!("memfd_create failed: {no}"), + Ok(fd) => fd, + } +} +// From https://github.com/guitmz/memrun/ +fn write (fd: usize, buffer: &[u8]) { + match unsafe { syscall!(Sysno::write, fd, buffer.as_ptr()) } { + Err(no) => panic!("write failed: {no}"), + Ok(_) => (), + } +} +// From https://github.com/guitmz/memrun/ +fn run (fd: usize) { + println!("fd = {fd}"); + let cmd = b"/proc/self/fd/3\0"; + let arg = [b"it is i, leclerc\0"]; + let env = [b"\0"]; + match unsafe { syscall!(Sysno::execve, cmd.as_ptr(), arg.as_ptr(), env.as_ptr()) } { + Err(no) => panic!("write failed: {no}"), + Ok(_) => (), + } +} diff --git a/crates/vestal/src/parse.rs b/crates/vestal/src/parse.rs new file mode 100644 index 0000000..7345702 --- /dev/null +++ b/crates/vestal/src/parse.rs @@ -0,0 +1,54 @@ +use crate::*; +impl Vestal { + + pub fn from_path ( + path: &impl AsRef, + cb: impl Fn(&[u8], &PE, Option<&Export>, HashMap>) + ) -> Usually<()> { + println!("Path: {}", path.as_ref().display()); + let buffer = std::fs::read(path.as_ref())?; + Self::from_data(buffer.as_slice(), cb) + } + + pub fn from_data ( + mut buffer: &[u8], + callback: impl Fn(&[u8], &PE, Option<&Export>, HashMap>) + ) -> Usually<()> { + println!("PE: {}b", buffer.len()); + let mut index = 2; + let mut slice = false; + if buffer.get(0) == Some(&b'#') && buffer.get(1) == Some(&b'!') { + while let Some(c) = buffer.get(index) { + if *c == 0x0a { + slice = true; + break + } + index += 1; + } + } + println!("Slice: {slice} {index}"); + let buffer = if slice { &buffer[index+1..] } else { buffer }; + if let Object::PE(ref pe) = Object::parse(buffer)? { + let mut main = None; + let mut imports: HashMap<_, _> = Default::default(); + for import in pe.imports.iter() { + let dll = import.dll.clone(); + if !imports.contains_key(dll) { + imports.insert(dll.to_string(), vec![]); + } + imports.get_mut(dll).unwrap().push(import); + } + for export in pe.exports.iter() { + if let Some("VSTPluginMain") = export.name { + main = Some(export); + break + } + } + callback(&buffer, pe, main, imports); + Ok(()) + } else { + Err("not a PE".into()) + } + } + +}