From 4cf04b20c8dfd86a6037d5275a450d9685ecf245 Mon Sep 17 00:00:00 2001 From: Jharaxus Date: Sun, 15 Dec 2024 21:59:25 +0100 Subject: [PATCH 01/12] Read memory using ptrace instead of get-tasks --- app/src/main.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ hello-world.c | 2 +- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/app/src/main.rs b/app/src/main.rs index 4798032..90431e8 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -104,6 +104,10 @@ enum Command { Restore { dump: String, }, + PtraceRestore { + pid: u64, + dump: String, + }, } fn main() { @@ -134,9 +138,75 @@ fn main() { Command::Restore { dump } => { restore_from_dump(dump); } + Command::PtraceRestore { pid, dump} => { + let pid = Pid::from_raw(pid as i32); + stop_with_ptrace(pid); + dump_with_ptrace(pid, dump.clone()); + restore_from_dump(dump); + ptrace::cont(pid, None).expect("failed to continue process"); + } } } +fn stop_with_ptrace(pid: Pid) { + ptrace::attach(pid).expect("failed to seize process"); + waitpid(pid, None).unwrap(); + + println!("process attached !"); +} + +fn dump_with_ptrace(pid: Pid, to: String) { + let exe = fs::read_link(format!("/proc/{}/exe", pid)) + .expect("failed to read exe name").into_os_string().into_string() + .expect("failed to convert exe name to string"); + + println!("exe: {}", exe); + + let regs = ptrace::getregs(pid).expect("Error when retrieving child process registers"); + + let mut data = Probe{ + pid: pid.as_raw() as u64, + rax: regs.rax, + rbx: regs.rbx, + rcx: regs.rcx, + rdx: regs.rdx, + r8: regs.r8, + r9: regs.r9, + r10: regs.r10, + r11: regs.r11, + r12: regs.r12, + r13: regs.r13, + r14: regs.r14, + r15: regs.r15, + rip: regs.rip, + rsp: regs.rsp, + rbp: regs.rbp, + ss: regs.ss, + rsi: regs.rsi, + rdi: regs.rdi, + cs: regs.cs, + ds: regs.ds, + es: regs.es, + fs: regs.fs, + gs: regs.gs, + exe, + stack_from: 0, + stack: vec![], + }; + + let stack_from = read_mem_region(pid, "stack", &mut data.stack); + data.stack_from = stack_from; + + File::create(to) + .expect("Failed to create output file") + .write_all( + postcard::to_allocvec(&data) + .expect("Failed to serialize data") + .as_slice(), + ) + .expect("Failed to write to output file"); +} + fn dump_pid(pid: u64, to: String) { let mut data = read_regs_with_get_tasks(pid); diff --git a/hello-world.c b/hello-world.c index c1a5300..2f3adf9 100644 --- a/hello-world.c +++ b/hello-world.c @@ -1,7 +1,7 @@ #include int main() { - for (int i = 0; i < 1000000; i++) { + for (int i = 0; i < 10000000; i++) { printf("%d: Hello world!\n", i); } } From e064e9246965baf3697f73684a918f85b94d16bd Mon Sep 17 00:00:00 2001 From: Jharaxus Date: Sun, 15 Dec 2024 22:19:39 +0100 Subject: [PATCH 02/12] Add force interrupt in the main to ensure that the program will restart in the main --- app/src/main.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index 90431e8..4a08bb2 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -140,19 +140,37 @@ fn main() { } Command::PtraceRestore { pid, dump} => { let pid = Pid::from_raw(pid as i32); - stop_with_ptrace(pid); + let call_instr = stop_with_ptrace(pid); + dump_with_ptrace(pid, dump.clone()); restore_from_dump(dump); + + ptrace::write(pid, 0x401504 as *mut libc::c_void, call_instr).expect("failed to write back call instruction"); ptrace::cont(pid, None).expect("failed to continue process"); } } } -fn stop_with_ptrace(pid: Pid) { +fn stop_with_ptrace(pid: Pid) -> i64 { + // ptrace attache to the process ptrace::attach(pid).expect("failed to seize process"); waitpid(pid, None).unwrap(); println!("process attached !"); + + // replace the call instruction with an interrupt + let call_addr = 0x401504 as *mut libc::c_void; + let call_instr = ptrace::read(pid, call_addr).expect("failed to read process"); + + println!("instruction read !"); + + ptrace::write(pid, call_addr, INT3).expect("failed to write interrupt instruction"); + + // restart process and wait for interrupt + ptrace::cont(pid, None).expect("failed to continue process"); + waitpid(pid, None).unwrap(); + + call_instr as i64 } fn dump_with_ptrace(pid: Pid, to: String) { @@ -193,7 +211,7 @@ fn dump_with_ptrace(pid: Pid, to: String) { stack_from: 0, stack: vec![], }; - + let stack_from = read_mem_region(pid, "stack", &mut data.stack); data.stack_from = stack_from; From 69ee052aae4ddadaacae6419cc5c667fe38a259b Mon Sep 17 00:00:00 2001 From: Jharaxus Date: Sun, 15 Dec 2024 23:14:32 +0100 Subject: [PATCH 03/12] Modularize the use of a fixed stop address --- app/src/main.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index 4a08bb2..3587dd4 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -8,6 +8,7 @@ use object::{Object, ObjectSymbol}; use regex::Regex; use serde::{Deserialize, Serialize}; use std::{env, ffi::CStr, fs::{self, File, OpenOptions}, io::{Read, Seek, Write}, os::fd::AsRawFd}; +use std::ffi::c_void; const INT3: i64 = 0xcc; @@ -140,18 +141,23 @@ fn main() { } Command::PtraceRestore { pid, dump} => { let pid = Pid::from_raw(pid as i32); - let call_instr = stop_with_ptrace(pid); + + let stop_addr = 0x401504 as *mut libc::c_void; // stops in main loop for hello-world + // let stop_addr = 0x401650 as *mut libc::c_void; // stops in function body for hello-world-func.c + // let stop_addr = 0x40150b as *mut libc::c_void; // stops in main loop for hello-world-func.c + + let call_instr = stop_with_ptrace(pid, stop_addr); dump_with_ptrace(pid, dump.clone()); restore_from_dump(dump); - ptrace::write(pid, 0x401504 as *mut libc::c_void, call_instr).expect("failed to write back call instruction"); + ptrace::write(pid, stop_addr, call_instr).expect("failed to write back call instruction"); ptrace::cont(pid, None).expect("failed to continue process"); } } } -fn stop_with_ptrace(pid: Pid) -> i64 { +fn stop_with_ptrace(pid: Pid, stop_addr: *mut c_void) -> i64 { // ptrace attache to the process ptrace::attach(pid).expect("failed to seize process"); waitpid(pid, None).unwrap(); @@ -159,12 +165,11 @@ fn stop_with_ptrace(pid: Pid) -> i64 { println!("process attached !"); // replace the call instruction with an interrupt - let call_addr = 0x401504 as *mut libc::c_void; - let call_instr = ptrace::read(pid, call_addr).expect("failed to read process"); + let call_instr = ptrace::read(pid, stop_addr).expect("failed to read process"); println!("instruction read !"); - ptrace::write(pid, call_addr, INT3).expect("failed to write interrupt instruction"); + ptrace::write(pid, stop_addr, INT3).expect("failed to write interrupt instruction"); // restart process and wait for interrupt ptrace::cont(pid, None).expect("failed to continue process"); From b3907be2138df3c5871b93b10cb7a312d6372d7f Mon Sep 17 00:00:00 2001 From: Jharaxus Date: Sun, 15 Dec 2024 23:14:59 +0100 Subject: [PATCH 04/12] Add another basic usage example --- hello-world-func.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 hello-world-func.c diff --git a/hello-world-func.c b/hello-world-func.c new file mode 100644 index 0000000..15c8535 --- /dev/null +++ b/hello-world-func.c @@ -0,0 +1,13 @@ +#include + +int __attribute__ ((noinline)) time_2(int n) { + asm (""); + return 2 * n; +} + +int main() { + for (int i = 0; i < 10000000; i++) { + int i_mult = time_2(i); + printf("%d: Hello world!\n", i_mult); + } +} From a2084bb71ff27dfd0dbe49459281b93a76aabf8c Mon Sep 17 00:00:00 2001 From: Jharaxus Date: Sun, 15 Dec 2024 23:17:34 +0100 Subject: [PATCH 05/12] Add script to automatically launch a.out and restore it --- app/run.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 app/run.sh diff --git a/app/run.sh b/app/run.sh new file mode 100755 index 0000000..04cbe90 --- /dev/null +++ b/app/run.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +SESSION_NAME="runner" + +# Setup tmux session +tmux new-session -d -s $SESSION_NAME +tmux split-window -h -t $SESSION_NAME + +# Starts a.out in the second pane and retrieves the pid of the process +tmux send-keys -t $SESSION_NAME:0.1 "cd .." C-m +tmux send-keys -t $SESSION_NAME:0.1 "./a.out & echo \$! > /tmp/a_out_pid" C-m +sleep 2 + +A_OUT_PID=$(cat /tmp/a_out_pid) +rm -f /tmp/a_out_pid + +echo "PID of a.out: $A_OUT_PID" + +# starts the restore process using the pid found +tmux send-keys -t $SESSION_NAME:0.0 "./target/debug/app ptrace-restore $A_OUT_PID hello.proc" C-m + +# Attach to tmux and setup kill-session on exit +tmux set-option -t $SESSION_NAME destroy-unattached off +tmux set-option -t $SESSION_NAME remain-on-exit off +tmux set-hook -t $SESSION_NAME pane-exited "kill-session" + +tmux attach-session -t $SESSION_NAME \ No newline at end of file From 097a6fe7974d01b955aee941014414a9f6738e78 Mon Sep 17 00:00:00 2001 From: Jharaxus Date: Sun, 15 Dec 2024 23:26:16 +0100 Subject: [PATCH 06/12] Update readme --- README.md | 18 ++++++++++++++++-- get-tasks/Makefile | 5 ++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4e2de1a..ab495b2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Checkpointing +## Requirements +This system was tested on linux with kernel version >= 6.11.5. ## Setup @@ -6,8 +8,9 @@ in `/get-tasks`: ``` make -make install +make install ``` +(use `make reinstall` to reinstall the kernel extension) in `/app`: @@ -21,8 +24,13 @@ in `/`: gcc hello-world.c -O3 -no-pie -fno-pic -static ``` -## Run +Anywhere: +``` +echo 0 | sudo tee /proc/sys/kernel/randomize_va_spac # disables ASLR +``` +## Run +### GDP & kernel extension in `/`: ``` @@ -40,3 +48,9 @@ sudo ./target/debug/app read sudo ./target/debug/app dump hello.proc sudo ./target/debug/app restore hello.proc ``` + +### ptrace only +in `/app` +``` +sudo ./run.sh +``` \ No newline at end of file diff --git a/get-tasks/Makefile b/get-tasks/Makefile index a9d9cde..9f7eccd 100644 --- a/get-tasks/Makefile +++ b/get-tasks/Makefile @@ -6,6 +6,9 @@ all: clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean -install: all +install: + sudo insmod get-tasks.ko + +reinstall: all sudo rmmod get-tasks.ko sudo insmod get-tasks.ko From 21bab1a237bc73cfc9e3cc30e593f9b4e30ba50e Mon Sep 17 00:00:00 2001 From: Ludovic Mermod Date: Mon, 16 Dec 2024 09:51:59 +0100 Subject: [PATCH 07/12] style: reformat --- app/src/main.rs | 86 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index 3587dd4..adb801d 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -7,8 +7,14 @@ use nix::{ use object::{Object, ObjectSymbol}; use regex::Regex; use serde::{Deserialize, Serialize}; -use std::{env, ffi::CStr, fs::{self, File, OpenOptions}, io::{Read, Seek, Write}, os::fd::AsRawFd}; use std::ffi::c_void; +use std::{ + env, + ffi::CStr, + fs::{self, File, OpenOptions}, + io::{Read, Seek, Write}, + os::fd::AsRawFd, +}; const INT3: i64 = 0xcc; @@ -43,9 +49,32 @@ struct ProbeRaw { } fn new_empty_raw_probe() -> ProbeRaw { - ProbeRaw { pid: 0, rax: 0, rbx: 0, rcx: 0, rdx: 0, r8: 0, r9: 0, r10: 0, r11: 0, r12: 0, r13: 0, - r14: 0, r15: 0, rip: 0, rsp: 0, rbp: 0, ss: 0, rsi: 0, rdi: 0, cs: 0, ds: 0, es: 0, fs: 0, - gs: 0, exe: [0; 4096], + ProbeRaw { + pid: 0, + rax: 0, + rbx: 0, + rcx: 0, + rdx: 0, + r8: 0, + r9: 0, + r10: 0, + r11: 0, + r12: 0, + r13: 0, + r14: 0, + r15: 0, + rip: 0, + rsp: 0, + rbp: 0, + ss: 0, + rsi: 0, + rdi: 0, + cs: 0, + ds: 0, + es: 0, + fs: 0, + gs: 0, + exe: [0; 4096], } } @@ -139,19 +168,20 @@ fn main() { Command::Restore { dump } => { restore_from_dump(dump); } - Command::PtraceRestore { pid, dump} => { + Command::PtraceRestore { pid, dump } => { let pid = Pid::from_raw(pid as i32); let stop_addr = 0x401504 as *mut libc::c_void; // stops in main loop for hello-world - // let stop_addr = 0x401650 as *mut libc::c_void; // stops in function body for hello-world-func.c - // let stop_addr = 0x40150b as *mut libc::c_void; // stops in main loop for hello-world-func.c + // let stop_addr = 0x401650 as *mut libc::c_void; // stops in function body for hello-world-func.c + // let stop_addr = 0x40150b as *mut libc::c_void; // stops in main loop for hello-world-func.c let call_instr = stop_with_ptrace(pid, stop_addr); dump_with_ptrace(pid, dump.clone()); restore_from_dump(dump); - ptrace::write(pid, stop_addr, call_instr).expect("failed to write back call instruction"); + ptrace::write(pid, stop_addr, call_instr) + .expect("failed to write back call instruction"); ptrace::cont(pid, None).expect("failed to continue process"); } } @@ -180,14 +210,16 @@ fn stop_with_ptrace(pid: Pid, stop_addr: *mut c_void) -> i64 { fn dump_with_ptrace(pid: Pid, to: String) { let exe = fs::read_link(format!("/proc/{}/exe", pid)) - .expect("failed to read exe name").into_os_string().into_string() + .expect("failed to read exe name") + .into_os_string() + .into_string() .expect("failed to convert exe name to string"); println!("exe: {}", exe); let regs = ptrace::getregs(pid).expect("Error when retrieving child process registers"); - let mut data = Probe{ + let mut data = Probe { pid: pid.as_raw() as u64, rax: regs.rax, rbx: regs.rbx, @@ -266,7 +298,7 @@ fn restore_from_dump(dump: String) { ptrace::traceme().expect("Failed to ptrace traceme"); println!("child process setup"); - println!("exe: {:#?}", dump.exe.as_ptr()); + println!("exe: {:#?}", dump.exe); // Stop itself for first setup phase raise(SIGSTOP); @@ -300,15 +332,27 @@ fn restore_from_dump(dump: String) { println!("status after child reached main: {:#?}", status); // Restores registers of the child - let mut regs = - ptrace::getregs(pid).expect("Error when retrieving child process registers"); - - regs.rax = dump.rax; regs.rbx = dump.rbx; regs.rcx = dump.rcx; regs.rdx = dump.rdx; - regs.r8 = dump.r8; regs.r9 = dump.r9; regs.r10 = dump.r10; regs.r11 = dump.r11; - regs.r12 = dump.r12; regs.r13 = dump.r13; regs.r14 = dump.r14; regs.r15 = dump.r15; - - regs.rip = dump.rip; regs.rsp = dump.rsp; regs.rbp = dump.rbp; regs.ss = dump.ss; - regs.rsi = dump.rsi; regs.rdi = dump.rdi; + let mut regs = ptrace::getregs(pid).expect("Error when retrieving child process registers"); + + regs.rax = dump.rax; + regs.rbx = dump.rbx; + regs.rcx = dump.rcx; + regs.rdx = dump.rdx; + regs.r8 = dump.r8; + regs.r9 = dump.r9; + regs.r10 = dump.r10; + regs.r11 = dump.r11; + regs.r12 = dump.r12; + regs.r13 = dump.r13; + regs.r14 = dump.r14; + regs.r15 = dump.r15; + + regs.rip = dump.rip; + regs.rsp = dump.rsp; + regs.rbp = dump.rbp; + regs.ss = dump.ss; + regs.rsi = dump.rsi; + regs.rdi = dump.rdi; regs.cs = dump.cs; // TODO: find a way to get the real values in get-tasks.c or in another way @@ -381,7 +425,7 @@ fn read_mem_region(pid: Pid, region_name: &str, data: &mut Vec) -> u64 { region_from as u64 } -fn write_mem_region(pid: Pid, mut from: u64, data: &Vec) { +fn write_mem_region(pid: Pid, mut from: u64, data: &[u8]) { println!( "Write {} bytes in region 0x{:x} to 0x{:x}", data.len(), From 9f62737026083dafdd3419d31dd96fd48364cd9a Mon Sep 17 00:00:00 2001 From: Ludovic Mermod Date: Mon, 16 Dec 2024 09:52:31 +0100 Subject: [PATCH 08/12] docs: fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab495b2..5d62406 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ gcc hello-world.c -O3 -no-pie -fno-pic -static Anywhere: ``` -echo 0 | sudo tee /proc/sys/kernel/randomize_va_spac # disables ASLR +echo 0 | sudo tee /proc/sys/kernel/randomize_va_space # disables ASLR ``` ## Run From e32c7b71364c54bd74c722cbbc10ee4beb8c5c9f Mon Sep 17 00:00:00 2001 From: Ludovic Mermod Date: Mon, 16 Dec 2024 11:12:00 +0100 Subject: [PATCH 09/12] feat: dump all memory regions --- app/src/main.rs | 49 ++++++++++++++++++++++++++++++++++++++++++------- hello-world.c | 2 +- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index adb801d..4290a89 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -7,7 +7,6 @@ use nix::{ use object::{Object, ObjectSymbol}; use regex::Regex; use serde::{Deserialize, Serialize}; -use std::ffi::c_void; use std::{ env, ffi::CStr, @@ -15,9 +14,16 @@ use std::{ io::{Read, Seek, Write}, os::fd::AsRawFd, }; +use std::{ffi::c_void, io::BufRead}; const INT3: i64 = 0xcc; +#[derive(Debug, Serialize, Deserialize)] +struct Region { + from: usize, + data: Vec, +} + #[repr(C)] #[derive(Debug)] struct ProbeRaw { @@ -105,8 +111,7 @@ struct Probe { fs: u64, gs: u64, exe: String, - stack_from: u64, - stack: Vec, + regions: Vec, } ioctl_readwrite!(read_regs, b'a', 1, ProbeRaw); @@ -245,8 +250,7 @@ fn dump_with_ptrace(pid: Pid, to: String) { fs: regs.fs, gs: regs.gs, exe, - stack_from: 0, - stack: vec![], + regions: vec![], }; let stack_from = read_mem_region(pid, "stack", &mut data.stack); @@ -265,8 +269,7 @@ fn dump_with_ptrace(pid: Pid, to: String) { fn dump_pid(pid: u64, to: String) { let mut data = read_regs_with_get_tasks(pid); - let stack_from = read_mem_region(Pid::from_raw(pid as i32), "stack", &mut data.stack); - data.stack_from = stack_from; + data.regions = dump_regions(pid); File::create(to) .expect("Failed to create output file") @@ -503,3 +506,35 @@ fn read_regs_with_get_tasks(pid: u64) -> Probe { stack: vec![], } } + +fn dump_regions(pid: u64) -> Vec { + let map = File::open(format!("/proc/{}/maps", pid)).expect("Failed to open maps file"); + + let mut regions = vec![]; + + for line in std::io::BufReader::new(map).lines() { + let line = line.expect("Failed to read line"); + regions.push(dump_region(&line)); + } + + regions +} + +fn dump_region(line: &str) -> Region { + let regex = Regex::new(r"^([0-9a-f]+)-([0-9a-f]+)").unwrap(); + let captures = regex.captures(line).unwrap(); + + let from = usize::from_str_radix(captures.get(1).unwrap().as_str(), 16).unwrap(); + let to = usize::from_str_radix(captures.get(2).unwrap().as_str(), 16).unwrap(); + + let mut file = File::open(format!("/proc/{}/mem", Pid::from_raw(1).as_raw())) + .expect("Failed to open mem file"); + file.seek(std::io::SeekFrom::Start(from as u64)) + .expect("Failed to seek in mem file"); + + let mut data = vec![0; to - from]; + file.read_exact(data.as_mut_slice()) + .expect("Failed to read from mem file"); + + Region { from, data } +} diff --git a/hello-world.c b/hello-world.c index 2f3adf9..c1a5300 100644 --- a/hello-world.c +++ b/hello-world.c @@ -1,7 +1,7 @@ #include int main() { - for (int i = 0; i < 10000000; i++) { + for (int i = 0; i < 1000000; i++) { printf("%d: Hello world!\n", i); } } From aae8c5294bd391749330acd95d2880b85e28bd6a Mon Sep 17 00:00:00 2001 From: Ludovic Mermod Date: Mon, 16 Dec 2024 11:14:47 +0100 Subject: [PATCH 10/12] feat: restore all mem regions --- app/src/main.rs | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index 4290a89..1e34082 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -20,7 +20,7 @@ const INT3: i64 = 0xcc; #[derive(Debug, Serialize, Deserialize)] struct Region { - from: usize, + from: u64, data: Vec, } @@ -224,7 +224,7 @@ fn dump_with_ptrace(pid: Pid, to: String) { let regs = ptrace::getregs(pid).expect("Error when retrieving child process registers"); - let mut data = Probe { + let data = Probe { pid: pid.as_raw() as u64, rax: regs.rax, rbx: regs.rbx, @@ -253,9 +253,6 @@ fn dump_with_ptrace(pid: Pid, to: String) { regions: vec![], }; - let stack_from = read_mem_region(pid, "stack", &mut data.stack); - data.stack_from = stack_from; - File::create(to) .expect("Failed to create output file") .write_all( @@ -366,8 +363,10 @@ fn restore_from_dump(dump: String) { ptrace::setregs(pid, regs).expect("Error when setting registers of child process."); // Restores stack of the child - println!("Write stack (from 0x{:x})", dump.stack_from); - write_mem_region(pid, dump.stack_from, &dump.stack); + for region in dump.regions { + println!("Write {} bytes at 0x{:x}", region.data.len(), region.from); + write_mem_region(pid, region.from, ®ion.data); + } ptrace::cont(pid, None).unwrap(); @@ -413,21 +412,6 @@ fn get_mem_region_limits(pid: Pid, region_name: &str) -> (usize, usize) { (region_from, region_to) } -fn read_mem_region(pid: Pid, region_name: &str, data: &mut Vec) -> u64 { - let (region_from, region_to) = get_mem_region_limits(pid, region_name); - - println!("Read {} ({:x}-{:x})", region_name, region_from, region_to); - - let mut mem = File::open(format!("/proc/{}/mem", pid)).expect("Failed to open mem file"); - mem.seek(std::io::SeekFrom::Start(region_from as u64)) - .unwrap(); - - data.resize(region_to - region_from, 0); - mem.read_exact(data.as_mut_slice()).unwrap(); - - region_from as u64 -} - fn write_mem_region(pid: Pid, mut from: u64, data: &[u8]) { println!( "Write {} bytes in region 0x{:x} to 0x{:x}", @@ -502,8 +486,7 @@ fn read_regs_with_get_tasks(pid: u64) -> Probe { fs: data.fs, gs: data.gs, exe, - stack_from: 0, - stack: vec![], + regions: vec![], } } @@ -524,15 +507,15 @@ fn dump_region(line: &str) -> Region { let regex = Regex::new(r"^([0-9a-f]+)-([0-9a-f]+)").unwrap(); let captures = regex.captures(line).unwrap(); - let from = usize::from_str_radix(captures.get(1).unwrap().as_str(), 16).unwrap(); - let to = usize::from_str_radix(captures.get(2).unwrap().as_str(), 16).unwrap(); + let from = u64::from_str_radix(captures.get(1).unwrap().as_str(), 16).unwrap(); + let to = u64::from_str_radix(captures.get(2).unwrap().as_str(), 16).unwrap(); let mut file = File::open(format!("/proc/{}/mem", Pid::from_raw(1).as_raw())) .expect("Failed to open mem file"); - file.seek(std::io::SeekFrom::Start(from as u64)) + file.seek(std::io::SeekFrom::Start(from)) .expect("Failed to seek in mem file"); - let mut data = vec![0; to - from]; + let mut data = vec![0; (to - from) as usize]; file.read_exact(data.as_mut_slice()) .expect("Failed to read from mem file"); From 8ba2e9dc4c23206d8215f1dbbdc6668286d972eb Mon Sep 17 00:00:00 2001 From: Ludovic Mermod Date: Mon, 16 Dec 2024 11:27:23 +0100 Subject: [PATCH 11/12] feat: only dump writable memory regions --- app/src/main.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index 1e34082..03ac793 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -20,6 +20,7 @@ const INT3: i64 = 0xcc; #[derive(Debug, Serialize, Deserialize)] struct Region { + name: String, from: u64, data: Vec, } @@ -497,21 +498,30 @@ fn dump_regions(pid: u64) -> Vec { for line in std::io::BufReader::new(map).lines() { let line = line.expect("Failed to read line"); - regions.push(dump_region(&line)); + if let Some(region) = dump_region(&line, pid) { + regions.push(region); + } } regions } -fn dump_region(line: &str) -> Region { - let regex = Regex::new(r"^([0-9a-f]+)-([0-9a-f]+)").unwrap(); +fn dump_region(line: &str, pid: u64) -> Option { + let regex = Regex::new(r"^([0-9a-f]+)-([0-9a-f]+) [r-]([w-])[x-][p-].*\s(.*)").unwrap(); let captures = regex.captures(line).unwrap(); + let is_writable = captures.get(3).unwrap().as_str() == "w"; + let name = captures.get(4).unwrap().as_str().to_owned(); + if ["[vvar]", "[vdso]", "[vsyscall]"].contains(&name.as_str()) || !is_writable { + return None; + } + let from = u64::from_str_radix(captures.get(1).unwrap().as_str(), 16).unwrap(); let to = u64::from_str_radix(captures.get(2).unwrap().as_str(), 16).unwrap(); - let mut file = File::open(format!("/proc/{}/mem", Pid::from_raw(1).as_raw())) - .expect("Failed to open mem file"); + println!("Saving region {} (0x{:x}-0x{:x})", name, from, to); + + let mut file = File::open(format!("/proc/{}/mem", pid)).expect("Failed to open mem file"); file.seek(std::io::SeekFrom::Start(from)) .expect("Failed to seek in mem file"); @@ -519,5 +529,5 @@ fn dump_region(line: &str) -> Region { file.read_exact(data.as_mut_slice()) .expect("Failed to read from mem file"); - Region { from, data } + Some(Region { name, from, data }) } From 18fe9d6f877dbac4fd4708197329ed925ca8aaee Mon Sep 17 00:00:00 2001 From: Jharaxus Date: Mon, 16 Dec 2024 11:27:57 +0100 Subject: [PATCH 12/12] Add missing registers --- app/src/main.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index adb801d..081c080 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -104,6 +104,7 @@ struct Probe { es: u64, fs: u64, gs: u64, + eflags: u64, exe: String, stack_from: u64, stack: Vec, @@ -171,9 +172,9 @@ fn main() { Command::PtraceRestore { pid, dump } => { let pid = Pid::from_raw(pid as i32); - let stop_addr = 0x401504 as *mut libc::c_void; // stops in main loop for hello-world - // let stop_addr = 0x401650 as *mut libc::c_void; // stops in function body for hello-world-func.c - // let stop_addr = 0x40150b as *mut libc::c_void; // stops in main loop for hello-world-func.c + // let stop_addr = 0x401504 as *mut libc::c_void; // stops in main loop for hello-world + // let stop_addr = 0x401650 as *mut libc::c_void; // stops in function body for hello-world-func.c + let stop_addr = 0x40150b as *mut libc::c_void; // stops in main loop for hello-world-func.c let call_instr = stop_with_ptrace(pid, stop_addr); @@ -244,6 +245,7 @@ fn dump_with_ptrace(pid: Pid, to: String) { es: regs.es, fs: regs.fs, gs: regs.gs, + eflags: regs.eflags, exe, stack_from: 0, stack: vec![], @@ -355,8 +357,11 @@ fn restore_from_dump(dump: String) { regs.rdi = dump.rdi; regs.cs = dump.cs; - // TODO: find a way to get the real values in get-tasks.c or in another way - //regs.ds = dump.ds; regs.es = dump.es; regs.fs = dump.fs; regs.gs = dump.gs; regs.eflags = 0x202; + regs.ds = dump.ds; + regs.es = dump.es; + regs.fs = dump.fs; + regs.gs = dump.gs; + regs.eflags = dump.eflags; println!("registers restored: {:x} -> {:#?}", regs.rip, regs); @@ -394,6 +399,8 @@ fn get_mem_region_limits(pid: Pid, region_name: &str) -> (usize, usize) { map.read_to_string(&mut str) .expect("Failed to read maps file"); + println!("maps file: {}", str); + let mut region_from = usize::MAX; let mut region_to = 0; @@ -498,8 +505,9 @@ fn read_regs_with_get_tasks(pid: u64) -> Probe { es: data.es, fs: data.fs, gs: data.gs, + eflags: 0x202, exe, stack_from: 0, stack: vec![], } -} +} \ No newline at end of file