mirror of
https://codeberg.org/unspeaker/vestal.git
synced 2025-12-06 21:36:44 +01:00
shebang loader
This commit is contained in:
parent
a22b17d6de
commit
b6451308e0
8 changed files with 207 additions and 26 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
target
|
target
|
||||||
|
*.dll
|
||||||
|
|
|
||||||
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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>(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
57
crates/vestal/src/memrun.rs
Normal file
57
crates/vestal/src/memrun.rs
Normal 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(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
54
crates/vestal/src/parse.rs
Normal file
54
crates/vestal/src/parse.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue