[feat] inline hook relocate
This commit is contained in:
75
01/project-hbj-hook/Cargo.lock
generated
75
01/project-hbj-hook/Cargo.lock
generated
@@ -20,36 +20,6 @@ version = "2.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
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]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -62,43 +32,6 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
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]]
|
[[package]]
|
||||||
name = "goblin"
|
name = "goblin"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
@@ -221,8 +154,6 @@ name = "project-hbj-hook"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"capstone",
|
|
||||||
"ctor",
|
|
||||||
"goblin",
|
"goblin",
|
||||||
"iced-x86",
|
"iced-x86",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -260,12 +191,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "shlex"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ edition = "2024"
|
|||||||
iced-x86 = { version = "1.21.0", features = ["code_asm"] }
|
iced-x86 = { version = "1.21.0", features = ["code_asm"] }
|
||||||
libc = "0.2.177"
|
libc = "0.2.177"
|
||||||
nix = { version = "0.30.1", features = ["ptrace", "uio", "signal"] }
|
nix = { version = "0.30.1", features = ["ptrace", "uio", "signal"] }
|
||||||
ctor = "0.6.0"
|
|
||||||
goblin = "0.10.3"
|
goblin = "0.10.3"
|
||||||
memmap2 = "0.9.9"
|
memmap2 = "0.9.9"
|
||||||
capstone = "0.13.0"
|
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
ouroboros = "0.18.5"
|
ouroboros = "0.18.5"
|
||||||
|
|||||||
@@ -1,13 +1,38 @@
|
|||||||
|
|
||||||
// asm.rs
|
// 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
|
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)?;
|
let mut asm = CodeAssembler::new(64)?;
|
||||||
_ = op(&mut asm);
|
_ = op(&mut asm);
|
||||||
Ok(asm.assemble(addr)?)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ use crate::map::MemoryMap;
|
|||||||
use crate::processes::Process;
|
use crate::processes::Process;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
||||||
use crate::asm::assemble;
|
use crate::asm::{assemble, InstructionFormat};
|
||||||
|
|
||||||
const GREEN: &str = "\x1b[32m";
|
const GREEN: &str = "\x1b[32m";
|
||||||
const YELLOW: &str = "\x1b[33m";
|
const YELLOW: &str = "\x1b[33m";
|
||||||
@@ -99,9 +99,9 @@ where
|
|||||||
let mut sum_inst_size = 0u32;
|
let mut sum_inst_size = 0u32;
|
||||||
|
|
||||||
for i in inst.iter() {
|
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() {
|
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 {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -117,8 +117,8 @@ where
|
|||||||
|
|
||||||
// Moving instructions to other locations for execution is !NOT! always safe,
|
// Moving instructions to other locations for execution is !NOT! always safe,
|
||||||
// but for library functions, this usually works,
|
// but for library functions, this usually works,
|
||||||
// because the first few instructions are usually endbr64, push ebp or something like that.
|
// because the first few instructions are usually endbr64, push rbp or something like that.
|
||||||
let mut gateway = proc.read_memory_vm(remote_addr as usize, sum_inst_size as usize)?;
|
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);
|
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| {
|
proc.disassemble(gateway_addr, gateway_sz as u64, |inst| {
|
||||||
for i in inst.iter() {
|
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(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
@@ -146,14 +146,14 @@ where
|
|||||||
|
|
||||||
proc.disassemble(remote_addr, gap_sz as u64, |inst| {
|
proc.disassemble(remote_addr, gap_sz as u64, |inst| {
|
||||||
for i in inst.iter() {
|
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(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
proc.disassemble(remote_addr + gap_sz as u64, 32, |inst| {
|
proc.disassemble(remote_addr + gap_sz as u64, 32, |inst| {
|
||||||
for i in inst.iter() {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ use nix::unistd::Pid;
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use std::io::{IoSliceMut, IoSlice};
|
use std::io::{IoSliceMut, IoSlice};
|
||||||
use capstone::{arch, Capstone, Instructions};
|
|
||||||
use capstone::arch::{BuildsCapstone, BuildsCapstoneSyntax};
|
|
||||||
use goblin::elf64::{header, section_header};
|
use goblin::elf64::{header, section_header};
|
||||||
use iced_x86::code_asm::{r10, r8, r9, rax, rdi, rdx, rsi};
|
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 nix::sys::ptrace;
|
||||||
use libc::{user_regs_struct};
|
use libc::{user_regs_struct};
|
||||||
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
|
use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
|
||||||
@@ -340,23 +340,37 @@ impl Process
|
|||||||
Ok(r.rax as u64)
|
Ok(r.rax as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassemble<F, T>(&self, addr: u64, size: u64, callback: F)
|
pub fn disassemble<F, T>(
|
||||||
-> Result<T, Box<dyn Error>>
|
&self,
|
||||||
where F: Fn(&Instructions) -> Result<T, Box<dyn Error>>
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user