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
*.dll

7
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

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

View file

@ -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<Path>,
cb: impl Fn(&[u8], &PE, Option<&Export>, HashMap<String, Vec<&Import>>)
) -> 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<String, Vec<&Import>>)
) -> 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())

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())
}
}
}