Setting a breakpoint (and reformat file)
This commit is contained in:
parent
e929354975
commit
beb3a75cf2
132
src/main.rs
132
src/main.rs
|
|
@ -1,52 +1,104 @@
|
||||||
mod child;
|
mod child;
|
||||||
|
|
||||||
|
use nix::libc::user_regs_struct;
|
||||||
use nix::sys::ptrace::*;
|
use nix::sys::ptrace::*;
|
||||||
use nix::sys::wait::{waitpid, WaitStatus};
|
use nix::sys::signal::Signal::*;
|
||||||
|
use nix::sys::wait::{waitid, waitpid, Id, WaitPidFlag, WaitStatus};
|
||||||
use nix::unistd::{fork, ForkResult, Pid};
|
use nix::unistd::{fork, ForkResult, Pid};
|
||||||
use std::ffi::CString;
|
use std::ffi::{c_long, c_void, CString};
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn single_step_all(child_pid: Pid) -> ExitCode {
|
fn single_step_all(child_pid: Pid) -> ExitCode {
|
||||||
let mut instruction_number = 0;
|
let mut instruction_number = 0;
|
||||||
loop {
|
loop {
|
||||||
let wait_status = waitpid(child_pid, None);
|
let wait_status = waitpid(child_pid, None);
|
||||||
match wait_status {
|
match wait_status {
|
||||||
Ok(WaitStatus::Stopped(_, _)) => {
|
Ok(WaitStatus::Stopped(_, _)) => {
|
||||||
instruction_number += 1;
|
instruction_number += 1;
|
||||||
let regs = getregs(child_pid).unwrap();
|
let regs = getregs(child_pid).unwrap();
|
||||||
println!(
|
println!("🔎 [==> {}] rip= 0x{:016X}, rax = 0x{rax:X} ({rax})", instruction_number, regs.rip, rax = regs.rax);
|
||||||
"🔎 [==> {}] rip= 0x{:016X}, rax = 0x{rax:X} ({rax})",
|
step(child_pid, None).unwrap();
|
||||||
instruction_number,
|
}
|
||||||
regs.rip,
|
Ok(WaitStatus::Exited(_, exit_code)) => {
|
||||||
rax = regs.rax
|
println!("👋 Child exited with code {exit_code}");
|
||||||
);
|
return ExitCode::SUCCESS;
|
||||||
step(child_pid, None).unwrap();
|
}
|
||||||
}
|
other => {
|
||||||
Ok(WaitStatus::Exited(_, exit_code)) => {
|
println!("⚠️ Other (unexpected) wait status: {other:?}");
|
||||||
println!("👋 Child exited with code {exit_code}");
|
return ExitCode::FAILURE;
|
||||||
return ExitCode::SUCCESS;
|
}
|
||||||
}
|
}
|
||||||
other => {
|
}
|
||||||
println!("⚠️ Other (unexpected) wait status: {other:?}");
|
}
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
#[allow(dead_code)]
|
||||||
}
|
fn breakpoint_fun(child_pid: Pid) -> ExitCode {
|
||||||
}
|
println!("⏳️ Waiting for child to be ready");
|
||||||
|
waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED).unwrap();
|
||||||
|
|
||||||
|
let address: u64 = 0x0000000000401019;
|
||||||
|
println!("🚧 Setting breakpoint at location 0x{address:x}");
|
||||||
|
let orig_bytes: [u8; 8] = read(child_pid, address as *mut c_void).expect("Breakpoint memory read").to_le_bytes();
|
||||||
|
println!("\t🔎 Original content is: {}", orig_bytes.map(|b| format!("{:#04x}", b)).join(" "));
|
||||||
|
let mut new_bytes = orig_bytes.clone();
|
||||||
|
new_bytes[0] = 0xCC;
|
||||||
|
println!(
|
||||||
|
"\t🌟 New content will be: {}",
|
||||||
|
new_bytes.map(|b| format!("{:#04x}", b)).join(" ")
|
||||||
|
);
|
||||||
|
write(child_pid, address as *mut c_void, c_long::from_le_bytes(new_bytes)).expect("Breakpoint memory write");
|
||||||
|
println!("\t🖍️ Breakpoint set");
|
||||||
|
|
||||||
|
println!("⚙️ Continuing execution waiting for breakpoint");
|
||||||
|
cont(child_pid, None).unwrap();
|
||||||
|
match waitpid(child_pid, None).unwrap() {
|
||||||
|
WaitStatus::Stopped(_, SIGTRAP) => {
|
||||||
|
let registers = getregs(child_pid).unwrap();
|
||||||
|
let breakpoint_addr = registers.rip - 1;
|
||||||
|
println!("🛑 Stopped at breakpoint ({:#018x})!", breakpoint_addr);
|
||||||
|
println!("\t🔎 Registers content: {:?}", registers);
|
||||||
|
println!("\t🖍️ Restoring instructions to original");
|
||||||
|
write(child_pid, address as *mut c_void, c_long::from_le_bytes(orig_bytes)).expect("breakpoint restore memory");
|
||||||
|
println!("\t↪️ Rolling back instruction pointer");
|
||||||
|
setregs(child_pid, user_regs_struct { rip: breakpoint_addr, ..registers }).unwrap();
|
||||||
|
println!("\t⚙️ One more instruction");
|
||||||
|
step(child_pid, None).unwrap();
|
||||||
|
waitid(Id::Pid(child_pid), WaitPidFlag::WSTOPPED).unwrap();
|
||||||
|
println!("\t⚙️ Continuing execution");
|
||||||
|
cont(child_pid, None).unwrap();
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
println!("⚠️ Other (unexpected) wait status: {other:?}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match waitpid(child_pid, None) {
|
||||||
|
Ok(WaitStatus::Exited(_, exit_code)) => {
|
||||||
|
println!("👋 Child exited with code {exit_code}");
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
println!("⚠️ Other (unexpected) wait status: {other:?}");
|
||||||
|
ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
let child_exec_path = CString::new(env!("TEST_PROG_PATH")).unwrap();
|
let child_exec_path = CString::new(env!("TEST_PROG_PATH")).unwrap();
|
||||||
|
|
||||||
match unsafe { fork() } {
|
match unsafe { fork() } {
|
||||||
Ok(ForkResult::Child) => child::starti(child_exec_path),
|
Ok(ForkResult::Child) => child::starti(child_exec_path),
|
||||||
Ok(ForkResult::Parent { child: child_pid }) => {
|
Ok(ForkResult::Parent { child: child_pid }) => {
|
||||||
println!("✔️ Created child {child_pid}");
|
println!("✔️ Created child {child_pid}");
|
||||||
return single_step_all(child_pid);
|
// return single_step_all(child_pid);
|
||||||
// todo!("Something interesting");
|
return breakpoint_fun(child_pid);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("❌ Fork failed: {e}");
|
println!("❌ Fork failed: {e}");
|
||||||
ExitCode::FAILURE
|
ExitCode::FAILURE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue