mirror of
https://github.com/Astatin3/syscall-stream-rs.git
synced 2026-06-08 16:08:01 -06:00
Implement puppeting
This commit is contained in:
@@ -6,6 +6,7 @@ edition = "2024"
|
||||
[dependencies]
|
||||
ctor = "0.5.0"
|
||||
libc = "0.2.175"
|
||||
pretty_env_logger = "0.5.0"
|
||||
# syscalls = "0.6.18"
|
||||
syscall_lib = { path = "syscall_lib" }
|
||||
syscaller = "0.2.2"
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
TODO:
|
||||
[ ] - Copy memory over network
|
||||
- [ ] - Copy memory over network
|
||||
- [ ] - Stream std buffers over network
|
||||
|
||||
Server:
|
||||
[x] - Intercept syscalls
|
||||
[x] - Transmit syscalls over network
|
||||
[x] - Read memory from buffers.
|
||||
[ ] - Mutate syscalls into Rust
|
||||
[ ] - Figure out which system calls should be forwarded and not
|
||||
- [x] - Intercept syscalls
|
||||
- [x] - Transmit syscalls over network
|
||||
- [x] - Read memory from buffers.
|
||||
- [ ] - Mutate all syscalls into Rust
|
||||
- [ ] - Figure out which system calls should be forwarded and not
|
||||
|
||||
Mimic:
|
||||
[x] - Execute syscalls
|
||||
[ ] - Mutate syscalls back into C
|
||||
[ ] - Create subprocess and mimic system calls in it.
|
||||
- [x] - Execute syscalls
|
||||
- [ ] - Mutate all syscalls back into C
|
||||
- [ ] - Create subprocess and mimic system calls in it.
|
||||
|
||||
@@ -4,4 +4,10 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.27"
|
||||
nix = { version = "0.30.1", features = ["ptrace"] }
|
||||
pretty_env_logger = "0.5.0"
|
||||
subprocess = "0.2.9"
|
||||
syscall_lib = { path = "../syscall_lib" }
|
||||
syscalls = "0.6.18"
|
||||
thiserror = "2.0.16"
|
||||
|
||||
+112
-9
@@ -1,13 +1,33 @@
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::net::TcpListener;
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
net::{TcpListener, TcpStream},
|
||||
net::TcpStream,
|
||||
thread,
|
||||
};
|
||||
|
||||
use log::{error, info, trace};
|
||||
use nix::libc::{self, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE, user_regs_struct};
|
||||
use nix::unistd::Pid;
|
||||
use subprocess::{Popen, PopenConfig, Redirection};
|
||||
use syscall_lib::Syscall;
|
||||
use syscalls::Sysno;
|
||||
|
||||
fn main() {
|
||||
println!("This program has PID: {}", std::process::id());
|
||||
use crate::proc::UserProcess;
|
||||
|
||||
mod proc;
|
||||
mod sleeper;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
pretty_env_logger::formatted_builder()
|
||||
.filter_level(log::LevelFilter::Trace)
|
||||
.init();
|
||||
|
||||
log::info!("This program has PID: {}", std::process::id());
|
||||
|
||||
// run()?;
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:1234").unwrap();
|
||||
|
||||
@@ -16,16 +36,20 @@ fn main() {
|
||||
|
||||
thread::spawn(move || {
|
||||
let _ = handle_connection(&mut stream);
|
||||
println!("Connection from {} closed", stream.peer_addr().unwrap());
|
||||
info!("Connection from {} closed", stream.peer_addr().unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> {
|
||||
println!("Got connection from {}", stream.peer_addr()?);
|
||||
info!("Got connection from {}", stream.peer_addr()?);
|
||||
|
||||
// let mut memory = ProgramMemory::default();
|
||||
|
||||
let puppet = create_new_proc();
|
||||
|
||||
loop {
|
||||
let mut size_buf = [0u8; 4];
|
||||
stream.read_exact(&mut size_buf)?;
|
||||
@@ -35,19 +59,38 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> {
|
||||
|
||||
stream.read_exact(&mut buf)?;
|
||||
|
||||
let decoded = Syscall::decode(&buf).unwrap();
|
||||
let syscall = Syscall::decode(&buf).unwrap();
|
||||
|
||||
println!("{:?} -> ", decoded);
|
||||
trace!("{:?}", syscall);
|
||||
|
||||
// let result = match decoded {
|
||||
// Syscall::Write(..) => 0,
|
||||
// _ => syscall_exec::execute_syscall(decoded),
|
||||
// };
|
||||
let result = unsafe { decoded.execute_syscall() };
|
||||
|
||||
let args = syscall.to_syscall_args();
|
||||
|
||||
let result = puppet.sys_call(
|
||||
args[0], args[1], args[2], args[3], args[4], args[5], args[6],
|
||||
);
|
||||
|
||||
let result = match result {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
error!("Error: {}", e.to_string());
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let result = result.rax;
|
||||
|
||||
// let result: u64 = result.unwrap().rax;
|
||||
|
||||
// result.r
|
||||
|
||||
// let result = 0;
|
||||
|
||||
println!("{:?}", result);
|
||||
trace!("{:?} -> {:?}", syscall, result);
|
||||
|
||||
let bytes: [u8; 8] = result.to_be_bytes();
|
||||
stream.write_all(&bytes)?;
|
||||
@@ -85,3 +128,63 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> {
|
||||
// println!("{:?}", decoded);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_new_proc() -> UserProcess {
|
||||
//TODO: Improve
|
||||
let mut p = Popen::create(
|
||||
&["sleep", "9999999999s"],
|
||||
PopenConfig {
|
||||
// stdout: Redirection::Pipe,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let pid = Pid::from_raw(p.pid().unwrap() as libc::pid_t);
|
||||
|
||||
// Attach to the process
|
||||
UserProcess::attach(pid).unwrap()
|
||||
}
|
||||
|
||||
// fn run() -> io::Result<()> {
|
||||
// // Refactor out the expect later, but the input should never fail because we know the input does not contain an internal 0 byte.
|
||||
// let output_message = CString::new("/tmp").expect("CString::new failed");
|
||||
|
||||
// // We want the bytes of the Cstring.
|
||||
// let output_message = output_message.as_bytes();
|
||||
|
||||
// // Allocate 8 bytes of data, i64 is 8 bytes
|
||||
// let mut user_memory = user_process
|
||||
// .allocate_memory(
|
||||
// 0,
|
||||
// output_message.len() as u64,
|
||||
// (PROT_READ | PROT_WRITE) as u64,
|
||||
// (MAP_PRIVATE | MAP_ANONYMOUS) as u64,
|
||||
// u64::MAX,
|
||||
// 0,
|
||||
// )
|
||||
// .unwrap();
|
||||
|
||||
// log::info!("UserMemory Result Address: {:#X}", user_memory.address());
|
||||
|
||||
// // Read the memory and demonstrate it is zero'd out
|
||||
// let read = user_process
|
||||
// .read_user_memory(&user_memory, user_memory.len() as usize)
|
||||
// .unwrap();
|
||||
// log::info!("Allocated Memory: {:?}", read);
|
||||
|
||||
// // Write to the memory out cstring
|
||||
// user_process
|
||||
// .write_user_memory(&mut user_memory, 0, output_message)
|
||||
// .unwrap();
|
||||
|
||||
// // We can check if the call succeeded by the resultant rax value.
|
||||
// let result = user_process
|
||||
// .sys_call(Sysno::chdir, user_memory.address(), 0, 0, 0, 0, 0)
|
||||
// .unwrap()
|
||||
// .rax;
|
||||
|
||||
// log::info!("Result {result:?}");
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
// Full credit to https://github.com/ohchase/ptrace_syscalls/tree/master
|
||||
|
||||
use std::vec;
|
||||
|
||||
use nix::{
|
||||
libc::user_regs_struct,
|
||||
sys::{
|
||||
ptrace,
|
||||
signal::Signal::SIGTRAP,
|
||||
wait::{WaitStatus, waitpid},
|
||||
},
|
||||
unistd::Pid,
|
||||
};
|
||||
use syscalls::Sysno;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum HostError {
|
||||
#[error("Process not found `{0}`")]
|
||||
ProcessNotFound(String),
|
||||
#[error("Nix Error `{0}`")]
|
||||
NixError(#[from] nix::errno::Errno),
|
||||
#[error("Unexpected Wait Status `{0:#?}`")]
|
||||
UnexpectedWaitStatus(WaitStatus),
|
||||
#[error("StdIo Error `{0}`")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("Mmap Error `{0:#?}`")]
|
||||
MmapBadAddress(u64),
|
||||
#[error("Munmap Error `{0:#?}`")]
|
||||
MunmapFailed(u64),
|
||||
}
|
||||
|
||||
pub type HostResult<T> = Result<T, HostError>;
|
||||
|
||||
pub struct UserProcessMemory<'a> {
|
||||
address: u64,
|
||||
owner: &'a UserProcess,
|
||||
len: u64,
|
||||
}
|
||||
|
||||
impl<'a> UserProcessMemory<'a> {
|
||||
/// Getter for UserProcessMemory's start address
|
||||
pub fn address(&self) -> u64 {
|
||||
self.address
|
||||
}
|
||||
|
||||
/// Getter for UserProcessMemory's length
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
pub fn len(&self) -> u64 {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for UserProcessMemory<'a> {
|
||||
/// Munmap's the memory on drop
|
||||
fn drop(&mut self) {
|
||||
match self.owner.deallocate_memory(self.address, self.len) {
|
||||
Ok(()) => log::trace!(
|
||||
"Successfully deallocated memory {:#X} with length {}",
|
||||
self.address,
|
||||
self.len
|
||||
),
|
||||
Err(err) => log::error!(
|
||||
"Failed to deallocate memory {:#X} with length {}, Error: {:#?}",
|
||||
self.address,
|
||||
self.len,
|
||||
err
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserProcess {
|
||||
pid: Pid,
|
||||
}
|
||||
|
||||
impl UserProcess {
|
||||
/// Initializes the UserProcess
|
||||
/// Given you have a UserProcess instance it means the attach must of succeeded.
|
||||
/// Attach should be the only way to acquire a UserProcess.
|
||||
pub fn attach(pid: Pid) -> HostResult<Self> {
|
||||
ptrace::attach(pid)?;
|
||||
|
||||
log::info!("New UserProcess successfully attached to pid: {}", pid);
|
||||
Ok(Self { pid })
|
||||
}
|
||||
|
||||
/// Getter for UserProcess actively connected pid
|
||||
pub fn pid(&self) -> Pid {
|
||||
self.pid
|
||||
}
|
||||
|
||||
/// Uses mmap syscall with ptrace to allocate user process memory
|
||||
pub fn allocate_memory(
|
||||
&self,
|
||||
address: u64,
|
||||
len: u64,
|
||||
prot: u64,
|
||||
flags: u64,
|
||||
fd: u64,
|
||||
offset: u64,
|
||||
) -> HostResult<UserProcessMemory> {
|
||||
let mmap_result =
|
||||
self.sys_call(Sysno::mmap as u64, address, len, prot, flags, fd, offset)?;
|
||||
let mmap_result = mmap_result.rax;
|
||||
|
||||
// invalid address
|
||||
// TODO not quite right...
|
||||
if mmap_result == 0 {
|
||||
return Err(HostError::MmapBadAddress(address));
|
||||
}
|
||||
|
||||
Ok(UserProcessMemory {
|
||||
address: mmap_result,
|
||||
owner: self,
|
||||
len,
|
||||
})
|
||||
}
|
||||
|
||||
/// Uses munmap syscall with ptrace to deallocate user process memory
|
||||
fn deallocate_memory(&self, address: u64, len: u64) -> HostResult<()> {
|
||||
let munmap_result = self.sys_call(Sysno::munmap as u64, address, len, 0, 0, 0, 0)?;
|
||||
let munmap_result = munmap_result.rax;
|
||||
|
||||
// not a zero return value means a failure
|
||||
if munmap_result != 0 {
|
||||
return Err(HostError::MunmapFailed(address));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write to the user process memory
|
||||
pub fn write_user_memory(
|
||||
&self,
|
||||
user_memory: &mut UserProcessMemory,
|
||||
offset: u64,
|
||||
bytes: &[u8],
|
||||
) -> HostResult<usize> {
|
||||
self.write_memory(user_memory.address + offset, bytes)
|
||||
}
|
||||
|
||||
/// Read from user process memory
|
||||
pub fn read_user_memory(
|
||||
&self,
|
||||
user_memory: &UserProcessMemory,
|
||||
len: usize,
|
||||
) -> HostResult<Vec<u8>> {
|
||||
self.read_memory(user_memory.address, len)
|
||||
}
|
||||
|
||||
/// String to the proc's memory file
|
||||
/// Reference: https://crates.io/crates/pete
|
||||
fn proc_mem_path(&self) -> String {
|
||||
format!("/proc/{}/mem", self.pid.as_raw() as u32)
|
||||
}
|
||||
|
||||
/// Common wrapper around writing memory
|
||||
/// Returns the amount of bytes written
|
||||
/// Reference: https://crates.io/crates/pete
|
||||
fn write_memory(&self, addr: u64, data: &[u8]) -> HostResult<usize> {
|
||||
use std::os::unix::fs::FileExt;
|
||||
let mem = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(self.proc_mem_path())?;
|
||||
let len = mem.write_at(data, addr)?;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
/// Common wrapper around reading memory
|
||||
/// Returns a vector to the memory bytes
|
||||
/// Reference: https://crates.io/crates/pete
|
||||
fn read_memory(&self, addr: u64, len: usize) -> HostResult<Vec<u8>> {
|
||||
use std::os::unix::fs::FileExt;
|
||||
|
||||
let mut data = vec![0u8; len];
|
||||
let mem = std::fs::File::open(self.proc_mem_path())?;
|
||||
let len_read = mem.read_at(&mut data, addr)?;
|
||||
|
||||
data.truncate(len_read);
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Invokes a syscall in the userprocess
|
||||
/// Accepts up to six arguments
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn sys_call(
|
||||
&self,
|
||||
sys_call: u64,
|
||||
rdi: u64,
|
||||
rsi: u64,
|
||||
rdx: u64,
|
||||
r10: u64,
|
||||
r8: u64,
|
||||
r9: u64,
|
||||
) -> HostResult<user_regs_struct> {
|
||||
log::trace!("UserProcess {} Syscall: {:#?}", self.pid, sys_call);
|
||||
let syscall_instruction = [0x0Fu8, 0x05u8];
|
||||
|
||||
// Cache original registers, original instruction pointer (rip), and the original instructions
|
||||
let original_registers = ptrace::getregs(self.pid)?;
|
||||
let original_ip = original_registers.rip;
|
||||
let original_instructions = self.read_memory(original_ip, syscall_instruction.len())?;
|
||||
|
||||
// Write over our shell code 0x0F05 for sys call
|
||||
self.write_memory(original_ip, &syscall_instruction)?;
|
||||
|
||||
// Create a copy of the original registers
|
||||
// Set the sys_call index and args[0..5] (six arguments)
|
||||
let mut new_registers = original_registers;
|
||||
new_registers.rax = sys_call;
|
||||
new_registers.rdi = rdi;
|
||||
new_registers.rsi = rsi;
|
||||
new_registers.rdx = rdx;
|
||||
new_registers.r10 = r10;
|
||||
new_registers.r8 = r8;
|
||||
new_registers.r9 = r9;
|
||||
|
||||
// Apply the new registers, and new instructions then single step waiting for SIG_TRAP
|
||||
ptrace::setregs(self.pid, new_registers)?;
|
||||
|
||||
// Single step the process and wait for a SIGTRAP signal
|
||||
self.single_step()?;
|
||||
|
||||
// Cache the resultant registers
|
||||
let result = ptrace::getregs(self.pid)?;
|
||||
|
||||
// Restore original instructions, and original registers to continue normal program control flow
|
||||
self.write_memory(original_ip, &original_instructions)?;
|
||||
ptrace::setregs(self.pid, original_registers)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Single steps the process
|
||||
/// Fails if the next signal is not a SIGTRAP
|
||||
fn single_step(&self) -> HostResult<()> {
|
||||
ptrace::step(self.pid, None)?;
|
||||
self.wait_trap()
|
||||
}
|
||||
|
||||
/// Waits for the next signal
|
||||
/// Returns an error if that signal is not a SIGTRAP
|
||||
fn wait_trap(&self) -> HostResult<()> {
|
||||
match self.wait()? {
|
||||
WaitStatus::Stopped(_, SIGTRAP) => Ok(()),
|
||||
status => Err(HostError::UnexpectedWaitStatus(status)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits on the current process
|
||||
fn wait(&self) -> HostResult<WaitStatus> {
|
||||
waitpid(self.pid, None).map_err(HostError::NixError)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UserProcess {
|
||||
/// Drop implementation for the UserProcess
|
||||
/// Attempts to detach from the process, does not panic on error instead only logs
|
||||
fn drop(&mut self) {
|
||||
if let Err(err) = ptrace::detach(self.pid, None) {
|
||||
log::error!(
|
||||
"UserProcess failed to detach from: {}, with Err: {:#?}",
|
||||
self.pid,
|
||||
err
|
||||
);
|
||||
} else {
|
||||
log::trace!("UserProcess successfully detached from: {}", self.pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, Write},
|
||||
};
|
||||
|
||||
const FILENAME: &'static str = "/tmp/sleeper";
|
||||
|
||||
pub fn create_sleeper() -> io::Result<()> {
|
||||
// ELF header for x86_64 Linux executable
|
||||
let mut binary_bytes = Vec::new();
|
||||
|
||||
// ELF Magic number and identification
|
||||
binary_bytes.extend_from_slice(&[
|
||||
0x7f, 0x45, 0x4c, 0x46, // ELF magic
|
||||
0x02, // 64-bit
|
||||
0x01, // Little endian
|
||||
0x01, // ELF version
|
||||
0x00, // System V ABI
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Padding
|
||||
]);
|
||||
|
||||
// ELF header continuation
|
||||
binary_bytes.extend_from_slice(&[
|
||||
0x02, 0x00, // Executable file
|
||||
0x3e, 0x00, // x86_64
|
||||
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||
]);
|
||||
|
||||
// Entry point (0x401000)
|
||||
binary_bytes.extend_from_slice(&[0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||
|
||||
// Program header offset (64 bytes)
|
||||
binary_bytes.extend_from_slice(&[0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||
|
||||
// Section header offset (0 - no sections)
|
||||
binary_bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||
|
||||
// Flags, header size, program header size, program header count
|
||||
binary_bytes.extend_from_slice(&[
|
||||
0x00, 0x00, 0x00, 0x00, // Flags
|
||||
0x40, 0x00, // ELF header size (64 bytes)
|
||||
0x38, 0x00, // Program header size (56 bytes)
|
||||
0x01, 0x00, // Program header count (1)
|
||||
0x00, 0x00, // Section header size
|
||||
0x00, 0x00, // Section header count
|
||||
0x00, 0x00, // String table index
|
||||
]);
|
||||
|
||||
// Program header (LOAD segment)
|
||||
binary_bytes.extend_from_slice(&[
|
||||
0x01, 0x00, 0x00, 0x00, // PT_LOAD
|
||||
0x05, 0x00, 0x00, 0x00, // PF_X | PF_R (executable + readable)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Offset in file
|
||||
0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // Virtual address
|
||||
0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // Physical address
|
||||
0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Size in file
|
||||
0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Size in memory
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Alignment
|
||||
]);
|
||||
|
||||
// Pad to entry point (0x401000 - current position)
|
||||
while binary_bytes.len() < 0x1000 {
|
||||
binary_bytes.push(0x00);
|
||||
}
|
||||
|
||||
// Assembly code that loops and sleeps
|
||||
// This is x86_64 assembly for:
|
||||
// loop:
|
||||
// mov rax, 35 ; sys_nanosleep
|
||||
// mov rdi, timespec ; pointer to timespec struct
|
||||
// mov rsi, 0 ; remaining time (NULL)
|
||||
// syscall
|
||||
// jmp loop
|
||||
//
|
||||
// timespec struct (1 second sleep):
|
||||
// .quad 1 ; seconds
|
||||
// .quad 0 ; nanoseconds
|
||||
|
||||
binary_bytes.extend_from_slice(&[
|
||||
// Main loop
|
||||
0x48, 0xc7, 0xc0, 0x23, 0x00, 0x00, 0x00, // mov rax, 35 (sys_nanosleep)
|
||||
0x48, 0xc7, 0xc7, 0x18, 0x10, 0x40, 0x00, // mov rdi, 0x401018 (timespec address)
|
||||
0x48, 0x31, 0xf6, // xor rsi, rsi (NULL)
|
||||
0x0f, 0x05, // syscall
|
||||
0xeb, 0xf1, // jmp -15 (back to mov rax, 35)
|
||||
// timespec struct at 0x401018
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 second
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 nanoseconds
|
||||
]);
|
||||
|
||||
// Write the binary to /tmp
|
||||
let mut file = File::create(FILENAME)?;
|
||||
file.write_all(&binary_bytes)?;
|
||||
|
||||
// Make it executable
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = file.metadata()?.permissions();
|
||||
perms.set_mode(0o755);
|
||||
std::fs::set_permissions(FILENAME, perms)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+2
-2
@@ -43,10 +43,10 @@ impl Host {
|
||||
let mut response = [0u8; 8];
|
||||
self.reader.read_exact(&mut response).unwrap();
|
||||
|
||||
let response = isize::from_be_bytes(response);
|
||||
let response = u64::from_be_bytes(response);
|
||||
|
||||
println!("{:?}", response);
|
||||
|
||||
response
|
||||
response as isize
|
||||
}
|
||||
}
|
||||
|
||||
+19
-19
@@ -19,17 +19,17 @@ static mut HOST: Option<Mutex<Host>> = None;
|
||||
|
||||
#[ctor::ctor]
|
||||
fn start() {
|
||||
// unsafe {
|
||||
// HOST = Some({
|
||||
// match Host::new() {
|
||||
// Ok(host) => Mutex::new(host),
|
||||
// Err(e) => {
|
||||
// eprintln!("Failed to connect to server: {}", e);
|
||||
// exit(1);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
unsafe {
|
||||
HOST = Some({
|
||||
match Host::new() {
|
||||
Ok(host) => Mutex::new(host),
|
||||
Err(e) => {
|
||||
eprintln!("Failed to connect to server: {}", e);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unsafe { set_hook_fn(hook) };
|
||||
}
|
||||
@@ -102,17 +102,17 @@ extern "C" fn hook(
|
||||
return InterceptResult::Forward;
|
||||
}
|
||||
|
||||
*result = unsafe { syscall.execute_syscall() };
|
||||
// *result = unsafe { syscall.execute_syscall() };
|
||||
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
if let Some(host) = HOST.as_ref() {
|
||||
*result = host.lock().unwrap().execute(&syscall);
|
||||
}
|
||||
}
|
||||
|
||||
println!("{:?} -> {}", syscall, result);
|
||||
|
||||
// unsafe {
|
||||
// #[allow(static_mut_refs)]
|
||||
// if let Some(host) = HOST.as_ref() {
|
||||
// *result = host.lock().unwrap().execute(&syscall);
|
||||
// }
|
||||
// }
|
||||
|
||||
unsafe {
|
||||
set_hook_fn(hook);
|
||||
}
|
||||
|
||||
@@ -923,9 +923,9 @@ impl Syscall {
|
||||
// rqtp: args[2] as ConstPtr<libc::timespec>,
|
||||
// rmtp: args[3] as Ptr<libc::timespec>,
|
||||
// },
|
||||
libc::SYS_exit_group => Syscall::ExitGroup {
|
||||
error_code: args[0] as libc::c_int,
|
||||
},
|
||||
// libc::SYS_exit_group => Syscall::ExitGroup {
|
||||
// error_code: args[0] as libc::c_int,
|
||||
// },
|
||||
// libc::SYS_epoll_wait => Syscall::EpollWait {
|
||||
// epfd: args[0] as libc::c_int,
|
||||
// events: args[1] as Ptr<libc::epoll_event>,
|
||||
|
||||
+104
-64
@@ -1,30 +1,30 @@
|
||||
use crate::{AsPtr, Syscall};
|
||||
use syscaller::{syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, syscall6};
|
||||
// use syscaller::{syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, syscall6};
|
||||
|
||||
// fn syscall0(number: isize) -> isize {
|
||||
// unsafe { syscaller::syscall0(number as usize) }
|
||||
// unsafe { syscaller::syscall0(number as libc::c_ulong) }
|
||||
// }
|
||||
|
||||
// fn syscall1(number: isize, arg0: isize) -> isize {
|
||||
// unsafe { syscaller::syscall1(number as usize, arg0 as usize) }
|
||||
// unsafe { syscaller::syscall1(number as libc::c_ulong, arg0 as libc::c_ulong) }
|
||||
// }
|
||||
|
||||
// fn syscall2(number: isize, arg0: isize, arg1: isize) -> isize {
|
||||
// unsafe { syscaller::syscall2(number as usize, arg0 as usize, arg1 as usize) }
|
||||
// unsafe { syscaller::syscall2(number as libc::c_ulong, arg0 as libc::c_ulong, arg1 as libc::c_ulong) }
|
||||
// }
|
||||
|
||||
// fn syscall3(number: isize, arg0: isize, arg1: isize, arg2: isize) -> isize {
|
||||
// unsafe { syscaller::syscall3(number as usize, arg0 as usize, arg1 as usize, arg2 as usize) }
|
||||
// unsafe { syscaller::syscall3(number as libc::c_ulong, arg0 as libc::c_ulong, arg1 as libc::c_ulong, arg2 as libc::c_ulong) }
|
||||
// }
|
||||
|
||||
// fn syscall4(number: isize, arg0: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
|
||||
// unsafe {
|
||||
// syscaller::syscall4(
|
||||
// number as usize,
|
||||
// arg0 as usize,
|
||||
// arg1 as usize,
|
||||
// arg2 as usize,
|
||||
// arg3 as usize,
|
||||
// number as libc::c_ulong,
|
||||
// arg0 as libc::c_ulong,
|
||||
// arg1 as libc::c_ulong,
|
||||
// arg2 as libc::c_ulong,
|
||||
// arg3 as libc::c_ulong,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
@@ -39,12 +39,12 @@ use syscaller::{syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, sysc
|
||||
// ) -> isize {
|
||||
// unsafe {
|
||||
// syscaller::syscall5(
|
||||
// number as usize,
|
||||
// arg0 as usize,
|
||||
// arg1 as usize,
|
||||
// arg2 as usize,
|
||||
// arg3 as usize,
|
||||
// arg4 as usize,
|
||||
// number as libc::c_ulong,
|
||||
// arg0 as libc::c_ulong,
|
||||
// arg1 as libc::c_ulong,
|
||||
// arg2 as libc::c_ulong,
|
||||
// arg3 as libc::c_ulong,
|
||||
// arg4 as libc::c_ulong,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
@@ -60,47 +60,79 @@ use syscaller::{syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, sysc
|
||||
// ) -> isize {
|
||||
// unsafe {
|
||||
// syscaller::syscall6(
|
||||
// number as usize,
|
||||
// arg0 as usize,
|
||||
// arg1 as usize,
|
||||
// arg2 as usize,
|
||||
// arg3 as usize,
|
||||
// arg4 as usize,
|
||||
// arg5 as usize,
|
||||
// number as libc::c_ulong,
|
||||
// arg0 as libc::c_ulong,
|
||||
// arg1 as libc::c_ulong,
|
||||
// arg2 as libc::c_ulong,
|
||||
// arg3 as libc::c_ulong,
|
||||
// arg4 as libc::c_ulong,
|
||||
// arg5 as libc::c_ulong,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
pub type Args = [libc::c_ulong; 7];
|
||||
|
||||
// The Syscall enum and type aliases from the previous response are assumed to be present here.
|
||||
#[allow(unused_unsafe, unsafe_op_in_unsafe_fn)]
|
||||
// #[allow(unused_unsafe, unsafe_op_in_unsafe_fn)]
|
||||
impl Syscall {
|
||||
pub unsafe fn execute_syscall(&self) -> isize {
|
||||
pub fn to_syscall_args(&self) -> Args {
|
||||
match self {
|
||||
Syscall::Read { fd, buf, len } => syscall3(
|
||||
libc::SYS_read as usize,
|
||||
*fd as usize,
|
||||
Syscall::Read { fd, buf, len } => [
|
||||
libc::SYS_read as libc::c_ulong,
|
||||
*fd as libc::c_ulong,
|
||||
buf.as_ptr(),
|
||||
*len as usize,
|
||||
),
|
||||
Syscall::Write { fd, buf, len } => syscall3(
|
||||
libc::SYS_write as usize,
|
||||
*fd as usize,
|
||||
*len as libc::c_ulong,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
Syscall::Write { fd, buf, len } => [
|
||||
libc::SYS_write as libc::c_ulong,
|
||||
*fd as libc::c_ulong,
|
||||
buf.as_ptr(),
|
||||
*len as usize,
|
||||
),
|
||||
Syscall::Open { path, flags, mode } => syscall3(
|
||||
libc::SYS_read as usize,
|
||||
*len as libc::c_ulong,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
Syscall::Open { path, flags, mode } => [
|
||||
libc::SYS_read as libc::c_ulong,
|
||||
path.as_ptr(),
|
||||
*flags as usize,
|
||||
*mode as usize,
|
||||
),
|
||||
Syscall::Close { fd } => syscall1(libc::SYS_read as usize, *fd as usize),
|
||||
Syscall::Stat { path, statbuf } => {
|
||||
syscall2(libc::SYS_stat as usize, path.as_ptr(), statbuf.as_ptr())
|
||||
}
|
||||
Syscall::Fstat { fd, statbuf } => {
|
||||
syscall2(libc::SYS_stat as usize, *fd as usize, statbuf.as_ptr())
|
||||
}
|
||||
*flags as libc::c_ulong,
|
||||
*mode as libc::c_ulong,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
Syscall::Close { fd } => [
|
||||
libc::SYS_read as libc::c_ulong,
|
||||
*fd as libc::c_ulong,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
|
||||
Syscall::Stat { path, statbuf } => [
|
||||
libc::SYS_stat as libc::c_ulong,
|
||||
path.as_ptr(),
|
||||
statbuf.as_ptr(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
Syscall::Fstat { fd, statbuf } => [
|
||||
libc::SYS_stat as libc::c_ulong,
|
||||
*fd as libc::c_ulong,
|
||||
statbuf.as_ptr(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
// Syscall::Fstat(arg0, arg1) => unsafe { syscall2(5, arg0, arg1) },
|
||||
// Syscall::Lstat(arg0, arg1) => unsafe { syscall2(6, arg0, arg1) },
|
||||
// Syscall::Poll(arg0, arg1, arg2) => unsafe { syscall3(7, arg0, arg1, arg2) },
|
||||
@@ -112,15 +144,15 @@ impl Syscall {
|
||||
flags,
|
||||
fd,
|
||||
offset,
|
||||
} => syscall6(
|
||||
libc::SYS_mmap as usize,
|
||||
} => [
|
||||
libc::SYS_mmap as libc::c_ulong,
|
||||
addr.as_ptr(),
|
||||
*len as usize,
|
||||
*prot as usize,
|
||||
*flags as usize,
|
||||
*fd as usize,
|
||||
*offset as usize,
|
||||
),
|
||||
*len as libc::c_ulong,
|
||||
*prot as libc::c_ulong,
|
||||
*flags as libc::c_ulong,
|
||||
*fd as libc::c_ulong,
|
||||
*offset as libc::c_ulong,
|
||||
],
|
||||
// Syscall::Mmap(arg0, arg1, arg2, arg3, arg4, arg5) => unsafe {
|
||||
// syscall6(9, arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
// },
|
||||
@@ -409,9 +441,15 @@ impl Syscall {
|
||||
// Syscall::ClockNanosleep(arg0, arg1, arg2, arg3) => unsafe {
|
||||
// syscall4(230, arg0, arg1, arg2, arg3)
|
||||
// },
|
||||
Syscall::ExitGroup { error_code } => unsafe {
|
||||
syscall1(libc::SYS_exit_group as usize, *error_code as usize)
|
||||
},
|
||||
Syscall::ExitGroup { error_code } => [
|
||||
libc::SYS_exit_group as libc::c_ulong,
|
||||
*error_code as libc::c_ulong,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
// Syscall::EpollWait(arg0, arg1, arg2, arg3) => unsafe {
|
||||
// syscall4(232, arg0, arg1, arg2, arg3)
|
||||
// },
|
||||
@@ -462,13 +500,15 @@ impl Syscall {
|
||||
filename,
|
||||
flags,
|
||||
mode,
|
||||
} => syscall4(
|
||||
libc::SYS_openat as usize,
|
||||
*dfd as usize,
|
||||
} => [
|
||||
libc::SYS_openat as libc::c_ulong,
|
||||
*dfd as libc::c_ulong,
|
||||
filename.as_ptr(),
|
||||
*flags as usize,
|
||||
*mode as usize,
|
||||
),
|
||||
*flags as libc::c_ulong,
|
||||
*mode as libc::c_ulong,
|
||||
0,
|
||||
0,
|
||||
],
|
||||
// Syscall::Openat(arg0, arg1, arg2, arg3) => unsafe { syscall4(257, arg0, arg1, arg2, arg3) },
|
||||
// Syscall::Mkdirat(arg0, arg1, arg2) => unsafe { syscall3(258, arg0, arg1, arg2) },
|
||||
// Syscall::Mknodat(arg0, arg1, arg2, arg3) => unsafe {
|
||||
|
||||
@@ -33,7 +33,7 @@ impl<T: Debug> Debug for Buf<T> {
|
||||
}
|
||||
|
||||
impl<T: Debug> AsPtr for Buf<T> {
|
||||
fn as_ptr(&self) -> usize {
|
||||
self.0 as usize
|
||||
fn as_ptr(&self) -> libc::c_ulong {
|
||||
self.0 as libc::c_ulong
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,5 @@ pub use x86_64::*;
|
||||
const MAX_STR_LEN: usize = 30;
|
||||
|
||||
pub trait AsPtr {
|
||||
fn as_ptr(&self) -> usize;
|
||||
fn as_ptr(&self) -> libc::c_ulong;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ impl<T: Debug> Debug for Ptr<T> {
|
||||
}
|
||||
|
||||
impl<T: Debug> AsPtr for Ptr<T> {
|
||||
fn as_ptr(&self) -> usize {
|
||||
self.0 as usize
|
||||
fn as_ptr(&self) -> libc::c_ulong {
|
||||
self.0 as libc::c_ulong
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ impl Debug for PtrVoid {
|
||||
}
|
||||
|
||||
impl AsPtr for PtrVoid {
|
||||
fn as_ptr(&self) -> usize {
|
||||
self.0 as usize
|
||||
fn as_ptr(&self) -> libc::c_ulong {
|
||||
self.0 as libc::c_ulong
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl Debug for StrRef {
|
||||
}
|
||||
|
||||
impl AsPtr for StrRef {
|
||||
fn as_ptr(&self) -> usize {
|
||||
self.0 as usize
|
||||
fn as_ptr(&self) -> libc::c_ulong {
|
||||
self.0 as libc::c_ulong
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user