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]
|
[dependencies]
|
||||||
ctor = "0.5.0"
|
ctor = "0.5.0"
|
||||||
libc = "0.2.175"
|
libc = "0.2.175"
|
||||||
|
pretty_env_logger = "0.5.0"
|
||||||
# syscalls = "0.6.18"
|
# syscalls = "0.6.18"
|
||||||
syscall_lib = { path = "syscall_lib" }
|
syscall_lib = { path = "syscall_lib" }
|
||||||
syscaller = "0.2.2"
|
syscaller = "0.2.2"
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
TODO:
|
TODO:
|
||||||
[ ] - Copy memory over network
|
- [ ] - Copy memory over network
|
||||||
|
- [ ] - Stream std buffers over network
|
||||||
|
|
||||||
Server:
|
Server:
|
||||||
[x] - Intercept syscalls
|
- [x] - Intercept syscalls
|
||||||
[x] - Transmit syscalls over network
|
- [x] - Transmit syscalls over network
|
||||||
[x] - Read memory from buffers.
|
- [x] - Read memory from buffers.
|
||||||
[ ] - Mutate syscalls into Rust
|
- [ ] - Mutate all syscalls into Rust
|
||||||
[ ] - Figure out which system calls should be forwarded and not
|
- [ ] - Figure out which system calls should be forwarded and not
|
||||||
|
|
||||||
Mimic:
|
Mimic:
|
||||||
[x] - Execute syscalls
|
- [x] - Execute syscalls
|
||||||
[ ] - Mutate syscalls back into C
|
- [ ] - Mutate all syscalls back into C
|
||||||
[ ] - Create subprocess and mimic system calls in it.
|
- [ ] - Create subprocess and mimic system calls in it.
|
||||||
|
|||||||
@@ -4,4 +4,10 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[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" }
|
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::{
|
use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
net::{TcpListener, TcpStream},
|
net::TcpStream,
|
||||||
thread,
|
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 syscall_lib::Syscall;
|
||||||
|
use syscalls::Sysno;
|
||||||
|
|
||||||
fn main() {
|
use crate::proc::UserProcess;
|
||||||
println!("This program has PID: {}", std::process::id());
|
|
||||||
|
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();
|
let listener = TcpListener::bind("127.0.0.1:1234").unwrap();
|
||||||
|
|
||||||
@@ -16,16 +36,20 @@ fn main() {
|
|||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let _ = handle_connection(&mut stream);
|
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> {
|
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 mut memory = ProgramMemory::default();
|
||||||
|
|
||||||
|
let puppet = create_new_proc();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut size_buf = [0u8; 4];
|
let mut size_buf = [0u8; 4];
|
||||||
stream.read_exact(&mut size_buf)?;
|
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)?;
|
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 {
|
// let result = match decoded {
|
||||||
// Syscall::Write(..) => 0,
|
// Syscall::Write(..) => 0,
|
||||||
// _ => syscall_exec::execute_syscall(decoded),
|
// _ => 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;
|
// let result = 0;
|
||||||
|
|
||||||
println!("{:?}", result);
|
trace!("{:?} -> {:?}", syscall, result);
|
||||||
|
|
||||||
let bytes: [u8; 8] = result.to_be_bytes();
|
let bytes: [u8; 8] = result.to_be_bytes();
|
||||||
stream.write_all(&bytes)?;
|
stream.write_all(&bytes)?;
|
||||||
@@ -85,3 +128,63 @@ fn handle_connection(stream: &mut TcpStream) -> Result<(), std::io::Error> {
|
|||||||
// println!("{:?}", decoded);
|
// 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];
|
let mut response = [0u8; 8];
|
||||||
self.reader.read_exact(&mut response).unwrap();
|
self.reader.read_exact(&mut response).unwrap();
|
||||||
|
|
||||||
let response = isize::from_be_bytes(response);
|
let response = u64::from_be_bytes(response);
|
||||||
|
|
||||||
println!("{:?}", response);
|
println!("{:?}", response);
|
||||||
|
|
||||||
response
|
response as isize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -19,17 +19,17 @@ static mut HOST: Option<Mutex<Host>> = None;
|
|||||||
|
|
||||||
#[ctor::ctor]
|
#[ctor::ctor]
|
||||||
fn start() {
|
fn start() {
|
||||||
// unsafe {
|
unsafe {
|
||||||
// HOST = Some({
|
HOST = Some({
|
||||||
// match Host::new() {
|
match Host::new() {
|
||||||
// Ok(host) => Mutex::new(host),
|
Ok(host) => Mutex::new(host),
|
||||||
// Err(e) => {
|
Err(e) => {
|
||||||
// eprintln!("Failed to connect to server: {}", e);
|
eprintln!("Failed to connect to server: {}", e);
|
||||||
// exit(1);
|
exit(1);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
|
|
||||||
unsafe { set_hook_fn(hook) };
|
unsafe { set_hook_fn(hook) };
|
||||||
}
|
}
|
||||||
@@ -102,17 +102,17 @@ extern "C" fn hook(
|
|||||||
return InterceptResult::Forward;
|
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);
|
println!("{:?} -> {}", syscall, result);
|
||||||
|
|
||||||
// unsafe {
|
|
||||||
// #[allow(static_mut_refs)]
|
|
||||||
// if let Some(host) = HOST.as_ref() {
|
|
||||||
// *result = host.lock().unwrap().execute(&syscall);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
set_hook_fn(hook);
|
set_hook_fn(hook);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -923,9 +923,9 @@ impl Syscall {
|
|||||||
// rqtp: args[2] as ConstPtr<libc::timespec>,
|
// rqtp: args[2] as ConstPtr<libc::timespec>,
|
||||||
// rmtp: args[3] as Ptr<libc::timespec>,
|
// rmtp: args[3] as Ptr<libc::timespec>,
|
||||||
// },
|
// },
|
||||||
libc::SYS_exit_group => Syscall::ExitGroup {
|
// libc::SYS_exit_group => Syscall::ExitGroup {
|
||||||
error_code: args[0] as libc::c_int,
|
// error_code: args[0] as libc::c_int,
|
||||||
},
|
// },
|
||||||
// libc::SYS_epoll_wait => Syscall::EpollWait {
|
// libc::SYS_epoll_wait => Syscall::EpollWait {
|
||||||
// epfd: args[0] as libc::c_int,
|
// epfd: args[0] as libc::c_int,
|
||||||
// events: args[1] as Ptr<libc::epoll_event>,
|
// events: args[1] as Ptr<libc::epoll_event>,
|
||||||
|
|||||||
+104
-64
@@ -1,30 +1,30 @@
|
|||||||
use crate::{AsPtr, Syscall};
|
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 {
|
// 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 {
|
// 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 {
|
// 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 {
|
// 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 {
|
// fn syscall4(number: isize, arg0: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
|
||||||
// unsafe {
|
// unsafe {
|
||||||
// syscaller::syscall4(
|
// syscaller::syscall4(
|
||||||
// number as usize,
|
// number as libc::c_ulong,
|
||||||
// arg0 as usize,
|
// arg0 as libc::c_ulong,
|
||||||
// arg1 as usize,
|
// arg1 as libc::c_ulong,
|
||||||
// arg2 as usize,
|
// arg2 as libc::c_ulong,
|
||||||
// arg3 as usize,
|
// arg3 as libc::c_ulong,
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
@@ -39,12 +39,12 @@ use syscaller::{syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, sysc
|
|||||||
// ) -> isize {
|
// ) -> isize {
|
||||||
// unsafe {
|
// unsafe {
|
||||||
// syscaller::syscall5(
|
// syscaller::syscall5(
|
||||||
// number as usize,
|
// number as libc::c_ulong,
|
||||||
// arg0 as usize,
|
// arg0 as libc::c_ulong,
|
||||||
// arg1 as usize,
|
// arg1 as libc::c_ulong,
|
||||||
// arg2 as usize,
|
// arg2 as libc::c_ulong,
|
||||||
// arg3 as usize,
|
// arg3 as libc::c_ulong,
|
||||||
// arg4 as usize,
|
// arg4 as libc::c_ulong,
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
@@ -60,47 +60,79 @@ use syscaller::{syscall0, syscall1, syscall2, syscall3, syscall4, syscall5, sysc
|
|||||||
// ) -> isize {
|
// ) -> isize {
|
||||||
// unsafe {
|
// unsafe {
|
||||||
// syscaller::syscall6(
|
// syscaller::syscall6(
|
||||||
// number as usize,
|
// number as libc::c_ulong,
|
||||||
// arg0 as usize,
|
// arg0 as libc::c_ulong,
|
||||||
// arg1 as usize,
|
// arg1 as libc::c_ulong,
|
||||||
// arg2 as usize,
|
// arg2 as libc::c_ulong,
|
||||||
// arg3 as usize,
|
// arg3 as libc::c_ulong,
|
||||||
// arg4 as usize,
|
// arg4 as libc::c_ulong,
|
||||||
// arg5 as usize,
|
// 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.
|
// 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 {
|
impl Syscall {
|
||||||
pub unsafe fn execute_syscall(&self) -> isize {
|
pub fn to_syscall_args(&self) -> Args {
|
||||||
match self {
|
match self {
|
||||||
Syscall::Read { fd, buf, len } => syscall3(
|
Syscall::Read { fd, buf, len } => [
|
||||||
libc::SYS_read as usize,
|
libc::SYS_read as libc::c_ulong,
|
||||||
*fd as usize,
|
*fd as libc::c_ulong,
|
||||||
buf.as_ptr(),
|
buf.as_ptr(),
|
||||||
*len as usize,
|
*len as libc::c_ulong,
|
||||||
),
|
0,
|
||||||
Syscall::Write { fd, buf, len } => syscall3(
|
0,
|
||||||
libc::SYS_write as usize,
|
0,
|
||||||
*fd as usize,
|
],
|
||||||
|
Syscall::Write { fd, buf, len } => [
|
||||||
|
libc::SYS_write as libc::c_ulong,
|
||||||
|
*fd as libc::c_ulong,
|
||||||
buf.as_ptr(),
|
buf.as_ptr(),
|
||||||
*len as usize,
|
*len as libc::c_ulong,
|
||||||
),
|
0,
|
||||||
Syscall::Open { path, flags, mode } => syscall3(
|
0,
|
||||||
libc::SYS_read as usize,
|
0,
|
||||||
|
],
|
||||||
|
Syscall::Open { path, flags, mode } => [
|
||||||
|
libc::SYS_read as libc::c_ulong,
|
||||||
path.as_ptr(),
|
path.as_ptr(),
|
||||||
*flags as usize,
|
*flags as libc::c_ulong,
|
||||||
*mode as usize,
|
*mode as libc::c_ulong,
|
||||||
),
|
0,
|
||||||
Syscall::Close { fd } => syscall1(libc::SYS_read as usize, *fd as usize),
|
0,
|
||||||
Syscall::Stat { path, statbuf } => {
|
0,
|
||||||
syscall2(libc::SYS_stat as usize, path.as_ptr(), statbuf.as_ptr())
|
],
|
||||||
}
|
Syscall::Close { fd } => [
|
||||||
Syscall::Fstat { fd, statbuf } => {
|
libc::SYS_read as libc::c_ulong,
|
||||||
syscall2(libc::SYS_stat as usize, *fd as usize, statbuf.as_ptr())
|
*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::Fstat(arg0, arg1) => unsafe { syscall2(5, arg0, arg1) },
|
||||||
// Syscall::Lstat(arg0, arg1) => unsafe { syscall2(6, arg0, arg1) },
|
// Syscall::Lstat(arg0, arg1) => unsafe { syscall2(6, arg0, arg1) },
|
||||||
// Syscall::Poll(arg0, arg1, arg2) => unsafe { syscall3(7, arg0, arg1, arg2) },
|
// Syscall::Poll(arg0, arg1, arg2) => unsafe { syscall3(7, arg0, arg1, arg2) },
|
||||||
@@ -112,15 +144,15 @@ impl Syscall {
|
|||||||
flags,
|
flags,
|
||||||
fd,
|
fd,
|
||||||
offset,
|
offset,
|
||||||
} => syscall6(
|
} => [
|
||||||
libc::SYS_mmap as usize,
|
libc::SYS_mmap as libc::c_ulong,
|
||||||
addr.as_ptr(),
|
addr.as_ptr(),
|
||||||
*len as usize,
|
*len as libc::c_ulong,
|
||||||
*prot as usize,
|
*prot as libc::c_ulong,
|
||||||
*flags as usize,
|
*flags as libc::c_ulong,
|
||||||
*fd as usize,
|
*fd as libc::c_ulong,
|
||||||
*offset as usize,
|
*offset as libc::c_ulong,
|
||||||
),
|
],
|
||||||
// Syscall::Mmap(arg0, arg1, arg2, arg3, arg4, arg5) => unsafe {
|
// Syscall::Mmap(arg0, arg1, arg2, arg3, arg4, arg5) => unsafe {
|
||||||
// syscall6(9, arg0, arg1, arg2, arg3, arg4, arg5)
|
// syscall6(9, arg0, arg1, arg2, arg3, arg4, arg5)
|
||||||
// },
|
// },
|
||||||
@@ -409,9 +441,15 @@ impl Syscall {
|
|||||||
// Syscall::ClockNanosleep(arg0, arg1, arg2, arg3) => unsafe {
|
// Syscall::ClockNanosleep(arg0, arg1, arg2, arg3) => unsafe {
|
||||||
// syscall4(230, arg0, arg1, arg2, arg3)
|
// syscall4(230, arg0, arg1, arg2, arg3)
|
||||||
// },
|
// },
|
||||||
Syscall::ExitGroup { error_code } => unsafe {
|
Syscall::ExitGroup { error_code } => [
|
||||||
syscall1(libc::SYS_exit_group as usize, *error_code as usize)
|
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 {
|
// Syscall::EpollWait(arg0, arg1, arg2, arg3) => unsafe {
|
||||||
// syscall4(232, arg0, arg1, arg2, arg3)
|
// syscall4(232, arg0, arg1, arg2, arg3)
|
||||||
// },
|
// },
|
||||||
@@ -462,13 +500,15 @@ impl Syscall {
|
|||||||
filename,
|
filename,
|
||||||
flags,
|
flags,
|
||||||
mode,
|
mode,
|
||||||
} => syscall4(
|
} => [
|
||||||
libc::SYS_openat as usize,
|
libc::SYS_openat as libc::c_ulong,
|
||||||
*dfd as usize,
|
*dfd as libc::c_ulong,
|
||||||
filename.as_ptr(),
|
filename.as_ptr(),
|
||||||
*flags as usize,
|
*flags as libc::c_ulong,
|
||||||
*mode as usize,
|
*mode as libc::c_ulong,
|
||||||
),
|
0,
|
||||||
|
0,
|
||||||
|
],
|
||||||
// Syscall::Openat(arg0, arg1, arg2, arg3) => unsafe { syscall4(257, arg0, arg1, arg2, arg3) },
|
// 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::Mkdirat(arg0, arg1, arg2) => unsafe { syscall3(258, arg0, arg1, arg2) },
|
||||||
// Syscall::Mknodat(arg0, arg1, arg2, arg3) => unsafe {
|
// 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> {
|
impl<T: Debug> AsPtr for Buf<T> {
|
||||||
fn as_ptr(&self) -> usize {
|
fn as_ptr(&self) -> libc::c_ulong {
|
||||||
self.0 as usize
|
self.0 as libc::c_ulong
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ pub use x86_64::*;
|
|||||||
const MAX_STR_LEN: usize = 30;
|
const MAX_STR_LEN: usize = 30;
|
||||||
|
|
||||||
pub trait AsPtr {
|
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> {
|
impl<T: Debug> AsPtr for Ptr<T> {
|
||||||
fn as_ptr(&self) -> usize {
|
fn as_ptr(&self) -> libc::c_ulong {
|
||||||
self.0 as usize
|
self.0 as libc::c_ulong
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ impl Debug for PtrVoid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsPtr for PtrVoid {
|
impl AsPtr for PtrVoid {
|
||||||
fn as_ptr(&self) -> usize {
|
fn as_ptr(&self) -> libc::c_ulong {
|
||||||
self.0 as usize
|
self.0 as libc::c_ulong
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ impl Debug for StrRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsPtr for StrRef {
|
impl AsPtr for StrRef {
|
||||||
fn as_ptr(&self) -> usize {
|
fn as_ptr(&self) -> libc::c_ulong {
|
||||||
self.0 as usize
|
self.0 as libc::c_ulong
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user