use std::error::Error; use std::ffi::CString; use anyhow::Context; use iced_x86::code_asm::*; use crate::map::MemoryMap; use crate::processes::{get_pid_by_name, Process}; use nix::unistd::Pid; use libc::user_regs_struct; use nix::sys::ptrace; use crate::asm::assemble; const GREEN: &str = "\x1b[32m"; const RESET: &str = "\x1b[0m"; mod disassembly; mod elf; mod map; mod processes; mod asm; pub fn plt_hook(proc: &Process, map: &MemoryMap, symbol: &str, payload: F) -> Result<(), Box> where F: Fn(&Process, u64, u64) -> Vec { let bias = map.module_base_address(&proc.get_exe()?).unwrap_or(0); let got_item_ptr = proc.find_got_pointer_plt(symbol).context("Unable to find symbol")?; println!("{GREEN}[memory map]{RESET} Bias is {:#016x}", bias); let got_item_byte: [u8; 8] = proc.read_memory_vm(got_item_ptr as usize, 8)? .try_into() .map_err(|_| "Failed to convert Vec to array")?; let got_item = u64::from_le_bytes(got_item_byte); println!( "{GREEN}[memory map]{RESET} got_item = {:#016x}", got_item ); // Alloc r-x private memory let regs = ptrace::getregs(proc.get_pid())?; let r = proc.execute_once_inplace(&assemble(regs.rip as u64, |asm| { asm.mov(rax, 9u64)?; // Syscall 9 (mmap) asm.mov(rdi, 0u64)?; // Addr asm.mov(rsi, 0x1000u64)?; // Length, we alloc a page (4K) asm.mov( rdx, (libc::PROT_READ | 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 Ok(()) })?)?; let page_addr = r.rax as u64; println!("{GREEN}[trace]{RESET} allocated page is at {:#016x}", page_addr); let payload = payload(&proc, page_addr, got_item); if payload.len() > 0x1000 { return Err(Box::::from("payload exceeds 0x1000 bytes")); } proc.write_memory_ptrace(page_addr as usize, &payload)?; println!("{GREEN}[trace]{RESET} wrote {} bytes of instructions/data to {:#016x}", payload.len(), page_addr); proc.write_memory_ptrace(got_item_ptr as usize, &page_addr.to_le_bytes())?; println!("{GREEN}[trace]{RESET} rewrote got item at {:#016x} to {:#016x}, old value is {:#016x}", got_item_ptr, page_addr, got_item); Ok(()) } fn main() -> Result<(), Box> { // Find our target program let pid = Pid::from_raw(get_pid_by_name("target")?); let process = Process::new(pid)?; let exe = process.get_exe()?; let maps = process.get_map_str()?; let lines: Vec<&str> = maps.lines().filter(|&line| !line.is_empty()).collect(); for line in &lines { println!("{GREEN}[memory map]{RESET} {}", line); } let map = MemoryMap::new(&lines); let seg_x = map.first_exec_segment(&exe).context("Can't find first exec segment")?; ptrace::attach(pid)?; process.wait(); ptrace::step(pid, None)?; process.wait(); // Save context let regs = ptrace::getregs(pid)?; // Save current registers ptrace::setregs( pid, user_regs_struct { rip: seg_x.0, ..regs }, )?; // Do inject here plt_hook(&process, &map, "write", |proc, addr, old| { let inst = assemble(addr, |asm| { asm.push(rdi)?; asm.push(rsi)?; asm.push(rdx)?; asm.mov(rdi, 1u64)?; asm.mov(rsi, addr + 0x800)?; asm.mov(rdx, 7u64)?; asm.call(old)?; asm.pop(rdx)?; asm.pop(rsi)?; asm.pop(rdi)?; asm.call(old)?; asm.mov(rdi, 1u64)?; asm.mov(rsi, addr + 0x810)?; asm.mov(rdx, 2u64)?; asm.call(old)?; asm.ret()?; Ok(()) }).unwrap(); let mut payload = Vec::from([0u8; 0x1000]); payload.splice(0..inst.len(), inst); payload.splice(0x800..(0x800 + 7), Vec::from("[hook] ".as_bytes())); payload.splice(0x810..(0x810 + 2), Vec::from("\r\n".as_bytes())); return payload; })?; // End inject logics // Restore context ptrace::setregs(pid, regs)?; ptrace::detach(pid, None)?; Ok(()) }