[feat] inline hook inject
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
// asm.rs
|
||||
|
||||
use iced_x86::{Instruction, code_asm::*};
|
||||
use iced_x86::{code_asm::*};
|
||||
|
||||
pub fn assemble<F>(addr: u64, op: F) -> Result<Vec<u8>, Box<dyn std::error::Error>>
|
||||
where
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
/*
|
||||
let cs = Capstone::new()
|
||||
.x86()
|
||||
.mode(arch::x86::ArchMode::Mode64)
|
||||
.syntax(arch::x86::ArchSyntax::Att)
|
||||
.detail(true)
|
||||
.build()?;
|
||||
|
||||
let code = vec![0x55, 0x48, 0x8b, 0x05, 0xb8, 0x13, 0x00, 0x00];
|
||||
let insns = cs.disasm_all(&code, 0x1000)?;
|
||||
|
||||
for insn in insns.iter() {
|
||||
println!("{}", insn);
|
||||
}
|
||||
*/
|
||||
@@ -48,6 +48,7 @@ impl ExecuteLinkFile {
|
||||
Ok(loads)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_dynamic(&self) -> Result<ProgramHeader, Box<dyn std::error::Error>> {
|
||||
let dynamic = self.borrow_elf()
|
||||
.program_headers
|
||||
@@ -94,6 +95,7 @@ impl ExecuteLinkFile {
|
||||
Ok(dyn_sym.clone())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn prase_dyn_sym(&self, name: &str) -> Result<Sym, Box<dyn std::error::Error>> {
|
||||
let dyn_sym = self.borrow_elf()
|
||||
.dynsyms.iter()
|
||||
@@ -112,6 +114,7 @@ impl ExecuteLinkFile {
|
||||
Ok(str.to_owned())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_e_type(&self) -> u16
|
||||
{
|
||||
self.borrow_elf().header.e_type
|
||||
|
||||
163
01/project-hbj-hook/src/hooks.rs
Normal file
163
01/project-hbj-hook/src/hooks.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
// hooks.rs
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use crate::map::MemoryMap;
|
||||
use crate::processes::Process;
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::asm::assemble;
|
||||
|
||||
const GREEN: &str = "\x1b[32m";
|
||||
const YELLOW: &str = "\x1b[33m";
|
||||
const RESET: &str = "\x1b[0m";
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn plt_hook<F>(
|
||||
proc: &Process,
|
||||
map: &MemoryMap,
|
||||
symbol: &str,
|
||||
payload: F,
|
||||
) -> Result<(), Box<dyn Error>>
|
||||
where
|
||||
F: Fn(&Process, u64, u64) -> Result<Vec<u8>, Box<dyn Error>>,
|
||||
{
|
||||
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);
|
||||
|
||||
let page_addr = proc.alloc_pages(1, (libc::PROT_READ | libc::PROT_EXEC) as u64)?;
|
||||
println!(
|
||||
"{GREEN}[plt hook]{RESET} allocated page is at {:#016x}",
|
||||
page_addr
|
||||
);
|
||||
|
||||
let payload = payload(&proc, page_addr, got_item)?;
|
||||
if payload.len() > 0x1000 {
|
||||
return Err(Box::<dyn Error>::from("payload exceeds 0x1000 bytes"));
|
||||
}
|
||||
|
||||
proc.write_memory_ptrace(page_addr as usize, &payload)?;
|
||||
println!(
|
||||
"{GREEN}[plt hook]{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}[plt hook]{RESET} rewrote got item at {:#016x} to {:#016x}, old value is {:#016x}",
|
||||
got_item_ptr, page_addr, got_item
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn inline_hook<F>(
|
||||
proc: &Process,
|
||||
module: &str,
|
||||
symbol: &str,
|
||||
payload: F, // With stdcall, the payload has to clean the stack. But that's uncommon.
|
||||
) -> Result<(), Box<dyn Error>>
|
||||
where
|
||||
F: Fn(&Process, u64, u64) -> Result<Vec<u8>, Box<dyn Error>>,
|
||||
{
|
||||
let remote_addr = proc.find_remote_proc(module, symbol).context("Unable to find proc")?;
|
||||
|
||||
println!("{GREEN}[memory map]{RESET} remote proc addr = {:#016x}", remote_addr);
|
||||
|
||||
let page_addr = proc.alloc_pages(2, (libc::PROT_READ | libc::PROT_EXEC) as u64)?;
|
||||
println!(
|
||||
"{GREEN}[inline hook]{RESET} allocated page is at {:#016x}",
|
||||
page_addr
|
||||
);
|
||||
|
||||
let payload_addr = page_addr + 32;
|
||||
let gateway_addr = page_addr;
|
||||
|
||||
let payload = payload(&proc, payload_addr, gateway_addr)?;
|
||||
if payload.len() > 0x2000 - 32 { return Err(Box::<dyn Error>::from("payload exceeds limit bytes")); }
|
||||
proc.write_memory_ptrace(payload_addr as usize, &payload)?;
|
||||
println!("{GREEN}[inline hook]{RESET} wrote payload to {:#016x}", payload_addr);
|
||||
|
||||
let jmp_inst = assemble(remote_addr, |asm| {
|
||||
asm.jmp(payload_addr)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let (gateway_sz, gap_sz) = proc.disassemble(remote_addr, 128, |inst| {
|
||||
let mut sum_inst_size = 0u32;
|
||||
|
||||
for i in inst.iter() {
|
||||
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string());
|
||||
if (sum_inst_size as usize) < jmp_inst.len() {
|
||||
sum_inst_size += i.bytes().len() as u32;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum_inst_size as usize) < jmp_inst.len() {
|
||||
return Err(Box::<dyn Error>::from(
|
||||
"Unable to find a suitable instruction boundary.",
|
||||
));
|
||||
}
|
||||
|
||||
let boundary = remote_addr + sum_inst_size as u64;
|
||||
|
||||
// Moving instructions to other locations for execution is !NOT! always safe,
|
||||
// but for library functions, this usually works,
|
||||
// because the first few instructions are usually endbr64, push ebp or something like that.
|
||||
let mut gateway = proc.read_memory_vm(remote_addr as usize, sum_inst_size as usize)?;
|
||||
|
||||
println!("{GREEN}[inline hook]{RESET} instruction boundary located at {:#016x}", boundary);
|
||||
|
||||
gateway.append(&mut assemble(gateway_addr + gateway.len() as u64, |asm|{
|
||||
asm.jmp(boundary)?;
|
||||
Ok(())
|
||||
})?);
|
||||
|
||||
proc.write_memory_ptrace(gateway_addr as usize, &gateway)?;
|
||||
println!("{GREEN}[inline hook]{RESET} {} bytes of gateway code write to {:#016x}",gateway.len(), gateway_addr);
|
||||
Ok((gateway.len(), sum_inst_size))
|
||||
})?;
|
||||
|
||||
proc.disassemble(gateway_addr, gateway_sz as u64, |inst| {
|
||||
for i in inst.iter() {
|
||||
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string());
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
proc.write_memory_ptrace(remote_addr as usize, &vec![0x90u8; gap_sz as usize])?;
|
||||
proc.write_memory_ptrace(remote_addr as usize, &jmp_inst)?;
|
||||
|
||||
println!("{GREEN}[inline hook]{RESET} wrote springboard to {:#016x}", remote_addr);
|
||||
|
||||
proc.disassemble(remote_addr, gap_sz as u64, |inst| {
|
||||
for i in inst.iter() {
|
||||
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string());
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
proc.disassemble(remote_addr + gap_sz as u64, 32, |inst| {
|
||||
for i in inst.iter() {
|
||||
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string());
|
||||
break;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,76 +1,24 @@
|
||||
|
||||
// main.rs
|
||||
|
||||
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<F>(proc: &Process, map: &MemoryMap, symbol: &str, payload: F)
|
||||
-> Result<(), Box<dyn Error>>
|
||||
where
|
||||
F: Fn(&Process, u64, u64) -> Vec<u8>
|
||||
{
|
||||
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::<dyn Error>::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(())
|
||||
}
|
||||
mod hooks;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Find our target program
|
||||
@@ -107,8 +55,9 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
// Do inject here
|
||||
|
||||
plt_hook(&process, &map, "write", |proc, addr, old| {
|
||||
hooks::inline_hook(&process, "/usr/lib/libc.so.6", "write", |_proc, addr, old| {
|
||||
let inst = assemble(addr, |asm| {
|
||||
asm.endbr64()?;
|
||||
asm.push(rdi)?;
|
||||
asm.push(rsi)?;
|
||||
asm.push(rdx)?;
|
||||
@@ -136,7 +85,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
payload.splice(0x800..(0x800 + 7), Vec::from("[hook] ".as_bytes()));
|
||||
payload.splice(0x810..(0x810 + 2), Vec::from("\r\n".as_bytes()));
|
||||
|
||||
return payload;
|
||||
Ok(payload)
|
||||
})?;
|
||||
|
||||
// End inject logics
|
||||
|
||||
@@ -70,6 +70,7 @@ impl MemoryMap {
|
||||
Self { regions }
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn first_rw_segment(&self, module: &str) -> Option<(u64, u64)> {
|
||||
self.regions
|
||||
.iter()
|
||||
@@ -77,6 +78,7 @@ impl MemoryMap {
|
||||
.map(|r| (r.start_addr, r.end_addr))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn first_exec_segment(&self, module: &str) -> Option<(u64, u64)> {
|
||||
self.regions
|
||||
.iter()
|
||||
@@ -84,7 +86,7 @@ impl MemoryMap {
|
||||
.map(|r| (r.start_addr, r.end_addr))
|
||||
}
|
||||
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn module_base_address(
|
||||
&self,
|
||||
module: &str // Full path of module, like '/usr/lib/libc.so.6'
|
||||
@@ -106,6 +108,7 @@ impl MemoryMap {
|
||||
Some(map_item.start_addr - first_load.p_vaddr)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn collect_module(&self, module: &str) -> Vec<MemoryRegion>
|
||||
{
|
||||
let r = self.regions.iter()
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
use anyhow::Context;
|
||||
use nix::sys::uio::{process_vm_readv, RemoteIoVec, process_vm_writev};
|
||||
use nix::unistd::Pid;
|
||||
use std::error::Error;
|
||||
use std::ffi::CString;
|
||||
|
||||
use std::io::{IoSliceMut, IoSlice};
|
||||
use capstone::{arch, Capstone, Instructions};
|
||||
use capstone::arch::{BuildsCapstone, BuildsCapstoneSyntax};
|
||||
use goblin::elf64::{header, section_header};
|
||||
use iced_x86::code_asm::{r10, r8, r9, rax, rdi, rdx, rsi};
|
||||
use iced_x86::Instruction;
|
||||
use nix::sys::ptrace;
|
||||
|
||||
use libc::{dlsym, user_regs_struct, RTLD_NEXT};
|
||||
use libc::{user_regs_struct};
|
||||
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
|
||||
use crate::asm::assemble;
|
||||
use crate::elf::ExecuteLinkFile;
|
||||
@@ -149,6 +150,7 @@ impl Process
|
||||
Some(f)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_pid(&self) -> Pid { self.pid.clone() }
|
||||
|
||||
pub fn get_exe(&self) -> Result<String, Box<dyn Error>>
|
||||
@@ -188,6 +190,7 @@ impl Process
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn write_memory_vm(&self, mut start_addr: usize, mut data: &[u8]) -> Result<usize, Box<dyn Error>>
|
||||
{
|
||||
let mut total_written = 0usize;
|
||||
@@ -245,6 +248,7 @@ impl Process
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn find_remote_proc(
|
||||
&self,
|
||||
module: &str, // Full path of module, like '/usr/lib/libc.so.6'
|
||||
@@ -280,12 +284,19 @@ impl Process
|
||||
Some(r_sym.r_offset + self.map.module_base_address(&exe)?)
|
||||
}
|
||||
|
||||
pub fn execute_once_inplace(&self, payload: &[u8]) -> Result<user_regs_struct, Box<dyn Error>>
|
||||
pub fn execute_once_inplace<F>(
|
||||
&self,
|
||||
payload_builder: F
|
||||
)
|
||||
-> Result<user_regs_struct, Box<dyn Error>>
|
||||
where F: Fn(u64) -> Option<Vec<u8>>
|
||||
{
|
||||
// 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, &[0xccu8]].concat();
|
||||
let instruction = [&payload as &[u8], &[0xccu8]].concat();
|
||||
|
||||
|
||||
self.write_memory_ptrace(regs.rip as usize, &instruction)?;
|
||||
@@ -303,4 +314,49 @@ impl Process
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn disassemble<F, T>(&self, addr: u64, size: u64, callback: F)
|
||||
-> Result<T, Box<dyn Error>>
|
||||
where F: Fn(&Instructions) -> Result<T, Box<dyn Error>>
|
||||
{
|
||||
let instruction = self.read_memory_vm(addr as usize, size as usize)?;
|
||||
|
||||
let cs = Capstone::new()
|
||||
.x86()
|
||||
.mode(arch::x86::ArchMode::Mode64)
|
||||
.syntax(arch::x86::ArchSyntax::Att)
|
||||
.detail(true)
|
||||
.build()?;
|
||||
|
||||
let inst = cs.disasm_all(&instruction, addr)?;
|
||||
|
||||
let r = callback(&inst)?;
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user