mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-09 06:47:59 -06:00
Load ELF from memory using memfd_create
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
mod manager;
|
||||
mod module;
|
||||
|
||||
mod proc_load;
|
||||
|
||||
// use std::any::Any;
|
||||
|
||||
// pub use logger::setup_logger;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
use crate::module::proc_load::memfd_create_dlopen;
|
||||
use crate::{ModuleError, logger::SetupLogger, logger::logger};
|
||||
|
||||
use crate::*;
|
||||
@@ -22,26 +23,27 @@ impl Module {
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
// TODO: Implement actual reflective ELF loading (possibly even custom format)
|
||||
// Look at https://github.com/weizhiao/rust-elfloader
|
||||
pub fn new_bytes(bytes: &[u8]) -> Result<Self, ModuleError> {
|
||||
let lib =
|
||||
memfd_create_dlopen(bytes).map_err(|e| ModuleError::Error(e.to_string().into()))?;
|
||||
|
||||
let this = Self { lib };
|
||||
|
||||
if let Ok(setup_logger) = this.get_symbol::<SetupLogger>(b"setup_logger") {
|
||||
setup_logger(logger());
|
||||
} else {
|
||||
warn!("setup_logger not found");
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
pub fn get_symbol<T>(&self, symbol: &[u8]) -> Result<Symbol<'_, T>, ModuleError> {
|
||||
let symbol = unsafe { self.lib.get::<T>(symbol) }
|
||||
.map_err(|e| ModuleError::LinkError(format!("Failed to load symbol: {}", e)))?;
|
||||
|
||||
Ok(symbol)
|
||||
}
|
||||
// pub fn get_id(&self) -> &str {
|
||||
// self.id
|
||||
// }
|
||||
// pub fn get_interface<T>(&self) -> Result<T, ModuleError> {
|
||||
// if let Ok(interface_function) = self.get_symbol::<fn() -> T>(b"interface") {
|
||||
// Ok(interface_function())
|
||||
// } else {
|
||||
// Err(ModuleError::LinkError(format!(
|
||||
// "Interface function not found!"
|
||||
// )))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// extern "C" fn test1234() {
|
||||
// info!("Test1234!");
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Load a shared object by saving bytes to a filesystem in /proc
|
||||
|
||||
use std::{error::Error, ffi::CString, io}; // 0.8
|
||||
|
||||
use libloading::Library;
|
||||
|
||||
use crate::debug;
|
||||
|
||||
// The `memfd_create` syscall flags (MFD_CLOEXEC is common and good practice)
|
||||
const MFD_CLOEXEC: u32 = 0x0001;
|
||||
const MFD_ALLOW_SEALING: u32 = 0x0002;
|
||||
|
||||
pub fn memfd_create_dlopen(payload: &[u8]) -> Result<Library, Box<dyn Error>> {
|
||||
use rand::distr::{Alphanumeric, SampleString};
|
||||
|
||||
let string = Alphanumeric.sample_string(&mut rand::rng(), 16);
|
||||
|
||||
// 1. Create the anonymous in-memory file descriptor using the raw syscall
|
||||
let c_name = CString::new(string).expect("CString conversion failed");
|
||||
|
||||
let fd = unsafe { libc::memfd_create(c_name.as_ptr(), MFD_CLOEXEC | MFD_ALLOW_SEALING) };
|
||||
|
||||
if fd < 0 {
|
||||
return Err(io::Error::last_os_error().to_string().into());
|
||||
}
|
||||
|
||||
// 2. Write the payload bytes to the in-memory file
|
||||
let bytes_written =
|
||||
unsafe { libc::write(fd, payload.as_ptr() as *const libc::c_void, payload.len()) };
|
||||
|
||||
if bytes_written != payload.len() as isize {
|
||||
// If write fails or is incomplete, clean up the file descriptor
|
||||
unsafe {
|
||||
libc::close(fd);
|
||||
}
|
||||
return Err("Failed to write full payload to memfd".into());
|
||||
}
|
||||
|
||||
// Optional: Seal the file to prevent modification, common for security/integrity
|
||||
// Note: The MFD_ALLOW_SEALING flag must be set during creation for this to work.
|
||||
let seals = libc::F_SEAL_GROW | libc::F_SEAL_SHRINK | libc::F_SEAL_WRITE;
|
||||
if unsafe { libc::fcntl(fd, libc::F_ADD_SEALS, seals) } == -1 {
|
||||
// Log a warning but continue if sealing fails (e.g., due to permissions)
|
||||
debug!(
|
||||
"memfd_create_dlopen: Failed to apply seals. Error: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Construct the virtual path to the in-memory file
|
||||
// This path is necessary for dlopen to work, as dlopen expects a filesystem path.
|
||||
let dl_path = format!("/proc/self/fd/{}", fd);
|
||||
|
||||
// 4. Use dlopen (via libloading) on the virtual path
|
||||
Ok(unsafe { Library::new(&dl_path)? })
|
||||
}
|
||||
Reference in New Issue
Block a user