shebang loader

This commit is contained in:
🪞👃🪞 2025-02-19 00:05:57 +02:00
parent a22b17d6de
commit b6451308e0
8 changed files with 207 additions and 26 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
target target
*.dll

7
Cargo.lock generated
View file

@ -449,6 +449,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "elf"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
[[package]] [[package]]
name = "encode_unicode" name = "encode_unicode"
version = "1.0.0" version = "1.0.0"
@ -1299,6 +1305,7 @@ name = "vestal"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap 4.5.20", "clap 4.5.20",
"elf",
"exe", "exe",
"goblin 0.9.2", "goblin 0.9.2",
"lancelot", "lancelot",

View file

@ -9,4 +9,5 @@ goblin = "0.9.2"
clap = { version = "4.5.4", features = [ "derive" ] } clap = { version = "4.5.4", features = [ "derive" ] }
lancelot = "0.8.6" lancelot = "0.8.6"
syscalls = "0.6.18" syscalls = "0.6.18"
elf = "0.7.4"
#falcon = "0.5.5" #falcon = "0.5.5"

View file

@ -1,25 +1,68 @@
use crate::*; use crate::*;
use syscalls::{Sysno, syscall}; 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 { impl Vestal {
pub fn execute (&self, path: impl AsRef<Path>) -> Usually<()> { pub fn execute_data (&self, data: &[u8]) -> Usually<()> {
Self::with_pe(&path, |buffer, pe, main, deps|{ Ok(())
let fd = Self::get_fd(); }
// TODO: compose in-memory ELF binary out of PE sections and Wine libraries pub fn execute_path (&self, path: impl AsRef<Path>) -> Usually<()> {
Self::write(fd, buffer); Self::from_path(&path, |buffer, pe, main, deps|{
println!("{fd}"); 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) } { fn make_elf () -> Vec<u8> {
Err(no) => panic!("memfd_create failed: {no}"), let mut buffer = vec![0;1024*1024*1024];
Ok(fd) => fd, // 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,
fn write (fd: usize, buffer: &[u8]) { e_ident: [ // identification data
match unsafe { syscall!(Sysno::write, fd, buffer.as_ptr()) } { 0x7f, b'E', b'L', b'F', // magic bytes
Err(no) => panic!("write failed: {no}"), 0x02, // 64-bit, 0x01 is 32-bit
Ok(_) => (), 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<T: Sized>(p: &T) -> &[u8] {
unsafe {
::core::slice::from_raw_parts(
(p as *const T) as *const u8,
::core::mem::size_of::<T>(),
)
} }
} }

View file

@ -1,8 +1,9 @@
use crate::*; use crate::*;
impl Vestal { impl Vestal {
pub fn inspect (&self, path: impl AsRef<Path>) -> Usually<()> { pub fn inspect_data (&self, path: impl AsRef<Path>) -> Usually<()> { Ok(()) }
Self::with_pe(&path, |buffer, pe, main, deps|{ pub fn inspect_path (&self, path: impl AsRef<Path>) -> Usually<()> {
Self::from_path(&path, |buffer, pe, main, deps|{
if let Some(main) = main { if let Some(main) = main {
println!("VSTPluginMain: {:?} + {:?}", &main.rva, &main.offset); println!("VSTPluginMain: {:?} + {:?}", &main.rva, &main.offset);
} else { } else {

View file

@ -1,5 +1,7 @@
mod execute; mod execute;
mod inspect; mod inspect;
mod memrun;
mod parse;
pub(crate) use std::path::Path; pub(crate) use std::path::Path;
pub(crate) use goblin::{error, Object, pe::{PE, import::Import, export::Export}}; pub(crate) use goblin::{error, Object, pe::{PE, import::Import, export::Export}};
use std::collections::HashMap; use std::collections::HashMap;
@ -18,22 +20,37 @@ pub enum Vestal {
Inspect { path: String }, Inspect { path: String },
/// Load a VST DLL into memory /// Load a VST DLL into memory
Execute { path: String }, Execute { path: String },
/// Load a VST DLL from hashbang
Loader { path: String }
}
pub enum Arch {
ThirtyTwo,
SixtyFour,
} }
impl Vestal { impl Vestal {
pub fn run (&self) -> Usually<()> { pub fn run (&self) -> Usually<()> {
match self { match self {
Self::Inspect { path } => self.inspect(path.as_str()), Self::Inspect { path } => self.inspect_path(path.as_str()),
Self::Execute { path } => self.execute(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<Path>, path: &impl AsRef<Path>,
cb: impl Fn(&[u8], &PE, Option<&Export>, HashMap<String, Vec<&Import>>) cb: impl Fn(&[u8], &PE, Option<&Export>, HashMap<String, Vec<&Import>>)
) -> Usually<()> { ) -> Usually<()> {
println!("PE: {}", path.as_ref().display()); println!("Path: {}", path.as_ref().display());
let buffer = std::fs::read(path.as_ref())?; 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<String, Vec<&Import>>)
) -> Usually<()> {
println!("PE: {}b", buffer.len());
if let Object::PE(ref pe) = Object::parse(buffer)? {
let mut main = None; let mut main = None;
let mut imports: HashMap<_, _> = Default::default(); let mut imports: HashMap<_, _> = Default::default();
for import in pe.imports.iter() { for import in pe.imports.iter() {
@ -50,7 +67,7 @@ impl Vestal {
break break
} }
} }
cb(&buffer, pe, main, imports); callback(&buffer, pe, main, imports);
Ok(()) Ok(())
} else { } else {
Err("not a PE".into()) Err("not a PE".into())

View file

@ -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<Self, MemRunError> {
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(_) => (),
}
}

View file

@ -0,0 +1,54 @@
use crate::*;
impl Vestal {
pub fn from_path (
path: &impl AsRef<Path>,
cb: impl Fn(&[u8], &PE, Option<&Export>, HashMap<String, Vec<&Import>>)
) -> 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<String, Vec<&Import>>)
) -> 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())
}
}
}