[feat] inline hook inject

This commit is contained in:
rootacite
2025-10-29 02:04:20 +08:00
parent 58a2308c10
commit c4a977a0b0
11 changed files with 338 additions and 135 deletions

View File

@@ -5,6 +5,7 @@ use std::fs::File;
use std::ops::Deref;
use ouroboros::self_referencing;
fn open_mem_map(path: &str) -> Result<Mmap, Box<dyn std::error::Error>> {
let file = File::open(path)?;
unsafe { Ok(Mmap::map(&file)?) }
@@ -108,4 +109,9 @@ impl ExecuteLinkFile {
Ok(str.to_owned())
}
}
pub fn get_e_type(&self) -> u16
{
self.borrow_elf().header.e_type
}
}

View File

@@ -98,45 +98,11 @@ pub fn inject2(proc: &Process, seg_rw: (u64, u64)) -> Result<(), Box<dyn std::er
pub fn inject3(proc: &Process, seg_rw: (u64, u64)) -> Result<i32, Box<dyn std::error::Error>> // thread inject
{
let regs = ptrace::getregs(proc.get_pid())?;
// Alloc rwx memory
let injected_inst = assemble(regs.rip as u64, |asm| {
asm.mov(rax, 9u64)?; // Syscall 9 (mmap)
let page_addr
= proc.alloc_pages(1, (libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as u64)? as usize;
asm.mov(rdi, 0u64)?; // Addr
asm.mov(rsi, 4096u64)?; // Length, we alloc a page (4K)
asm.mov(
rdx,
(libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC) as u64,
)?; // Set protect to rwx
asm.mov(r10, (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64)?; // Private and anonymous
asm.mov(r8, 01i64)?; // Fd (-1 because we want anonymous)
asm.mov(r9, 0u64)?; // Offset
asm.syscall()?; // Syscall interrupt
asm.int3()?; // (Important!!!) Use int3 interrupt to retrieve control flow
Ok(())
})?;
proc.write_memory_ptrace(regs.rip as usize, &injected_inst)?;
println!(
"{GREEN}[trace]{RESET} write instructions to {:#016x}",
regs.rip
);
// Continue target
ptrace::cont(proc.get_pid(), None)?;
println!("{GREEN}[trace]{RESET} continue from {:#016x}", regs.rip);
proc.wait();
println!(
"{GREEN}[trace]{RESET} int3 at {:#016x}",
ptrace::getregs(proc.get_pid())?.rip
);
let regs_after_map = ptrace::getregs(proc.get_pid())?;
let page_addr = regs_after_map.rax as usize;
println!(
"{GREEN}[trace]{RESET} allocated page is at {:#016x}",
page_addr
@@ -180,8 +146,7 @@ pub fn inject3(proc: &Process, seg_rw: (u64, u64)) -> Result<i32, Box<dyn std::e
println!("{GREEN}[trace]{RESET} write payload to {:#016x}", page_addr);
// Start Trigger
// let regs = ptrace::getregs(proc.get_pid())?;
ptrace::setregs(proc.get_pid(), regs)?;
let regs = ptrace::getregs(proc.get_pid())?;
let injected_trigger = assemble(regs.rip as u64, |asm| {
asm.mov(rax, 56u64)?; // Syscall 56 (clone)

View File

@@ -6,17 +6,17 @@ use nix::unistd::Pid;
use std::error::Error;
use std::ffi::CString;
use std::io::{IoSliceMut, IoSlice};
use anyhow::Context;
use goblin::elf64::{header, section_header};
use nix::sys::ptrace;
use iced_x86::code_asm::{r10, r8, r9, rax, rdi, rdx, rsi};
use libc::{dlsym, RTLD_NEXT};
use libc::{dlsym, user_regs_struct, RTLD_NEXT};
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
use crate::helper::ExecuteLinkFile;
use crate::helper::{assemble, ExecuteLinkFile};
use crate::helper::map::MemoryMap;
const GREEN: &str = "\x1b[32m";
const RESET: &str = "\x1b[0m";
fn list_processes() -> Result<HashMap<String, i32>, std::io::Error>
{
let mut processes = HashMap::<String, i32>::new();
@@ -73,7 +73,7 @@ impl Process
let copy_len = usize::min(word_size - head_offset, data.len());
bytes[head_offset..head_offset + copy_len].copy_from_slice(&data[..copy_len]);
let new_word = libc::c_long::from_ne_bytes(bytes);
let new_word = libc::c_long::from_le_bytes(bytes);
ptrace::write(pid, aligned_addr as *mut libc::c_void, new_word)?;
Ok(copy_len)
@@ -87,7 +87,7 @@ impl Process
{
let mut arr = [0u8; size_of::<libc::c_long>()];
arr.copy_from_slice(data);
let val = libc::c_long::from_ne_bytes(arr);
let val = libc::c_long::from_le_bytes(arr);
ptrace::write(pid, addr as *mut libc::c_void, val)?;
Ok(size_of::<libc::c_long>())
}
@@ -102,7 +102,7 @@ impl Process
let orig_word = ptrace::read(pid, addr as *mut libc::c_void)?;
let mut bytes = orig_word.to_ne_bytes();
bytes[..data.len()].copy_from_slice(data);
let new_word = libc::c_long::from_ne_bytes(bytes);
let new_word = libc::c_long::from_le_bytes(bytes);
ptrace::write(pid, addr as *mut libc::c_void, new_word)?;
Ok(data.len())
@@ -239,15 +239,30 @@ impl Process
Ok(written)
}
pub fn find_remote_proc(&self, module: &str, symbol: &str) -> Option<u64>
pub fn find_remote_proc(
&self,
module: &str, // Full path of module, like '/usr/lib/libc.so.6'
symbol: &str // Symbol name, like 'printf'
) -> Option<u64>
{
let target_maps = fs::read_to_string(format!("/proc/{}/maps", self.pid)).ok()?;
let base = MemoryMap::new(&target_maps.lines().collect::<Vec<&str>>()).module_base_address(module)?;
let elf = ExecuteLinkFile::prase(module).ok()?;
let sym = elf.prase_dyn_sym(symbol).ok()?;
Some(sym.st_value + base)
let target_maps = fs::read_to_string(format!("/proc/{}/maps", self.pid)).ok()?;
let base = MemoryMap::new(&target_maps.lines().collect::<Vec<&str>>()).module_base_address(module)?;
let is_undefined = sym.st_shndx == section_header::SHN_UNDEF as usize;
if !is_undefined && sym.st_value != 0 {
return if elf.get_e_type() == header::ET_DYN {
Some(base + sym.st_value)
} else {
// ET_EXEC or others: assume st_value is absolute
Some(sym.st_value)
}
}
None
}
pub fn find_got_pointer_plt(&self, symbol: &str) -> Option<u64>
@@ -258,4 +273,63 @@ impl Process
let r_sym = elf.get_rela_sym(symbol).ok()?;
Some(r_sym.r_offset + self.map.module_base_address(&exe)?)
}
pub fn execute_once_inplace<F>(
&self,
payload_builder: F
)
-> Result<user_regs_struct, Box<dyn Error>>
where F: Fn(u64) -> Option<Vec<u8>>
{
const GREEN: &str = "\x1b[32m";
const RESET: &str = "\x1b[0m";
// Save context
let regs = ptrace::getregs(self.pid)?;
let payload = payload_builder(regs.rip).context("payload build failed")?;
let buffer = self.read_memory_vm(regs.rip as usize, payload.len() + 1)?;
let instruction = [&payload as &[u8], &[0xccu8]].concat();
self.write_memory_ptrace(regs.rip as usize, &instruction)?;
println!("{GREEN}[trace]{RESET} write instructions to {:#016x}", regs.rip);
// Continue target
ptrace::cont(self.pid, None)?;
println!("{GREEN}[trace]{RESET} continue from {:#016x}", regs.rip);
self.wait();
let r = ptrace::getregs(self.pid)?;
println!("{GREEN}[trace]{RESET} int3 at {:#016x}", r.rip);
self.write_memory_ptrace(regs.rip as usize, &buffer)?;
ptrace::setregs(self.pid, regs)?;
Ok(r)
}
pub fn alloc_pages(&self, count: u64, permissions: u64)
-> Result<u64, Box<dyn Error>>
{
// Alloc r-x private memory
let r = self.execute_once_inplace(|addr| {
let r = assemble(addr, |asm| {
asm.mov(rax, 9u64)?; // Syscall 9 (mmap)
asm.mov(rdi, 0u64)?; // Addr
asm.mov(rsi, 0x1000u64 * count)?; // Length, we alloc a page (4K)
asm.mov(rdx, permissions, )?;
asm.mov(r10, (libc::MAP_PRIVATE | libc::MAP_ANONYMOUS) as u64)?; // Private and anonymous
asm.mov(r8, -1i64)?; // Fd (-1 because we want anonymous)
asm.mov(r9, 0u64)?; // Offset
asm.syscall()?; // Syscall interrupt
Ok(())
}).ok()?;
Some(r)
})?;
Ok(r.rax as u64)
}
}