[feat] inline hook relocate

This commit is contained in:
rootacite
2025-10-29 15:13:14 +08:00
parent c4a977a0b0
commit 6412478668
5 changed files with 65 additions and 103 deletions

View File

@@ -20,36 +20,6 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "capstone"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a"
dependencies = [
"capstone-sys",
"libc",
]
[[package]]
name = "capstone-sys"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "cc"
version = "1.2.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -62,43 +32,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "ctor"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59c9b8bdf64ee849747c1b12eb861d21aa47fa161564f48332f1afe2373bf899"
dependencies = [
"ctor-proc-macro",
"dtor",
]
[[package]]
name = "ctor-proc-macro"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1"
[[package]]
name = "dtor"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e58a0764cddb55ab28955347b45be00ade43d4d6f3ba4bf3dc354e4ec9432934"
dependencies = [
"dtor-proc-macro",
]
[[package]]
name = "dtor-proc-macro"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
[[package]]
name = "find-msvc-tools"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "goblin"
version = "0.10.3"
@@ -221,8 +154,6 @@ name = "project-hbj-hook"
version = "0.1.0"
dependencies = [
"anyhow",
"capstone",
"ctor",
"goblin",
"iced-x86",
"libc",
@@ -260,12 +191,6 @@ dependencies = [
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "static_assertions"
version = "1.1.0"

View File

@@ -7,9 +7,7 @@ edition = "2024"
iced-x86 = { version = "1.21.0", features = ["code_asm"] }
libc = "0.2.177"
nix = { version = "0.30.1", features = ["ptrace", "uio", "signal"] }
ctor = "0.6.0"
goblin = "0.10.3"
memmap2 = "0.9.9"
capstone = "0.13.0"
anyhow = "1.0.100"
ouroboros = "0.18.5"

View File

@@ -1,13 +1,38 @@
// asm.rs
use iced_x86::{code_asm::*};
use std::error::Error;
use iced_x86::{code_asm::*, Formatter, Instruction, NasmFormatter};
pub fn assemble<F>(addr: u64, op: F) -> Result<Vec<u8>, Box<dyn std::error::Error>>
pub fn assemble<F>(addr: u64, op: F) -> Result<Vec<u8>, Box<dyn Error>>
where
F: Fn(&mut CodeAssembler) -> Result<(), Box<dyn std::error::Error>>,
F: Fn(&mut CodeAssembler) -> Result<(), Box<dyn Error>>,
{
let mut asm = CodeAssembler::new(64)?;
_ = op(&mut asm);
Ok(asm.assemble(addr)?)
}
pub trait InstructionFormat {
fn fmt_line(&self, formatter: &mut dyn Formatter) -> Result<String, Box<dyn Error>>;
fn fmt_line_default(&self) -> Result<String, Box<dyn Error>>;
}
impl InstructionFormat for Instruction {
fn fmt_line(&self, formatter: &mut dyn Formatter) -> Result<String, Box<dyn Error>> {
let mut asm_str = String::new();
formatter.format(self, &mut asm_str);
Ok(format!(
"{:#016x}[{:02}] {}",
self.ip(),
self.len(),
asm_str
))
}
fn fmt_line_default(&self) -> Result<String, Box<dyn Error>> {
let mut fmt = NasmFormatter::new();
self.fmt_line(&mut fmt)
}
}

View File

@@ -7,7 +7,7 @@ use crate::map::MemoryMap;
use crate::processes::Process;
use anyhow::Context;
use crate::asm::assemble;
use crate::asm::{assemble, InstructionFormat};
const GREEN: &str = "\x1b[32m";
const YELLOW: &str = "\x1b[33m";
@@ -99,9 +99,9 @@ where
let mut sum_inst_size = 0u32;
for i in inst.iter() {
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string());
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.fmt_line_default()?);
if (sum_inst_size as usize) < jmp_inst.len() {
sum_inst_size += i.bytes().len() as u32;
sum_inst_size += i.len() as u32;
} else {
break;
}
@@ -117,8 +117,8 @@ where
// 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)?;
// because the first few instructions are usually endbr64, push rbp or something like that.
let mut gateway = proc.instruction_relocate(remote_addr, sum_inst_size as u64, gateway_addr)?;
println!("{GREEN}[inline hook]{RESET} instruction boundary located at {:#016x}", boundary);
@@ -134,7 +134,7 @@ where
proc.disassemble(gateway_addr, gateway_sz as u64, |inst| {
for i in inst.iter() {
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string());
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.fmt_line_default()?);
}
Ok(())
})?;
@@ -146,14 +146,14 @@ where
proc.disassemble(remote_addr, gap_sz as u64, |inst| {
for i in inst.iter() {
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.to_string());
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.fmt_line_default()?);
}
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());
println!("{GREEN}[disassemble]{RESET} {YELLOW}{}{RESET}", i.fmt_line_default()?);
break;
}
Ok(())

View File

@@ -10,10 +10,10 @@ use nix::unistd::Pid;
use std::error::Error;
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::{BlockEncoder, BlockEncoderOptions, Decoder, DecoderOptions, Instruction, InstructionBlock};
use nix::sys::ptrace;
use libc::{user_regs_struct};
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
@@ -340,23 +340,37 @@ impl Process
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>>
pub fn disassemble<F, T>(
&self,
addr: u64,
size: u64,
callback: F,
) -> Result<T, Box<dyn Error>>
where
F: Fn(&[Instruction]) -> Result<T, Box<dyn Error>>,
{
let instruction = self.read_memory_vm(addr as usize, size as usize)?;
let code_bytes = self.read_memory_vm(addr as usize, size as usize)?;
let decoder = Decoder::with_ip(64, &code_bytes, addr, DecoderOptions::NONE);
let instructions: Vec<Instruction> = decoder.into_iter().collect();
let result = callback(&instructions)?;
Ok(result)
}
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)?;
pub fn instruction_relocate(&self, addr: u64, size: u64, new_addr: u64)
-> Result<Vec<u8>, Box<dyn Error>>
{
let origin = self.read_memory_vm(addr as usize, size as usize)?;
let r = callback(&inst)?;
let decoder = Decoder::with_ip(64, &origin, addr, DecoderOptions::NONE);
let instructions: Vec<_> = decoder.into_iter().collect();
Ok(r)
let block = InstructionBlock::new(&instructions, new_addr);
let options = BlockEncoderOptions::RETURN_RELOC_INFOS;
let result = BlockEncoder::encode(64, block, options)
.map_err(|e| format!("BlockEncoder failed: {}", e))?;
Ok(result.code_buffer.clone())
}
}