process_read_write/
lib.rs

1#![allow(dead_code)]
2
3use nix::{unistd::Pid,sys::{ptrace,uio::{process_vm_readv,process_vm_writev,RemoteIoVec}},Error};
4use std::io::{IoSliceMut,IoSlice};
5use sysinfo::System;
6use std::collections::HashMap;
7use nix::{self,sys::wait::waitpid};
8use serde_json;
9
10const SYSCALL_DATA: &str = include_str!("../data/syscall.json");
11
12/// `get_proc_by_id` will take a process id as `i32` and get the process with that id 
13pub fn get_proc_by_id(id: i32) -> Pid{
14    Pid::from_raw(id)
15}
16
17fn syscalls_list() -> HashMap<u64,String>{
18    let mut map = HashMap::new();
19    let parse: serde_json::Value = serde_json::from_str(&SYSCALL_DATA).unwrap();
20    for sysobj in parse["aaData"].as_array().unwrap() {
21        map.insert(sysobj[0].as_u64().unwrap(),sysobj[1].as_str().unwrap().to_string());
22    }
23    map
24}
25fn printsyscall(syscall_names:&HashMap<u64,String>,regs:nix::libc::user_regs_struct,i:usize) {
26    let syscall_name = syscall_names.get(&regs.orig_rax).unwrap();
27    // use something like this if you wanna trigger an event only on specefic syscall `if syscall_name.as_str() != "process_vm_readv" {return}`
28    eprint!("{} => ",i/2);
29    eprintln!("{} ({},{:x},{:x},..) = {}",
30        syscall_name,
31        regs.rdi,
32        regs.rsi,
33        regs.rdx,
34        regs.rax
35     );
36} 
37
38/// `watch_proc` is used to monitor a process and get a real-time list of all the system calls it makes.
39pub fn watch_proc(pid:i32){
40    let syscall_names = syscalls_list();
41    let childpid = Pid::from_raw(pid);
42    ptrace::attach(childpid).expect("cant attach to pid");
43    let _ = waitpid(childpid,None).expect("timeout waiting for pid");
44    let mut i = 0;
45    loop{
46        ptrace::syscall(childpid,None).unwrap();
47        let _ = waitpid(childpid,None).unwrap();
48        let regs = ptrace::getregs(childpid).expect(&format!("{}","Error: process most likely terminated!"));
49        if i%2 == 0{
50            printsyscall(&syscall_names,regs,i);
51        }
52        i+=1;
53    }
54}
55
56/// `get_proc_by_name` will take a name try to find a process with that name 
57/// 
58/// # Note 
59///
60/// this function will panic with an error message if it 0 or multipe process by that name were found. 
61/// planning to make so it returns Result<T,E>
62/// 
63/// # Examples
64///
65/// ```
66/// use process_read_write;
67/// use nix::unistd::Pid;
68///
69/// fn main(){
70///     let name = "MyGame-x86";
71///     let pid:Pid = process_read_write::get_proc_by_name(name);
72/// }
73/// ```
74pub fn get_proc_by_name(process_name: &str) -> Pid{
75    let s = System::new_all();
76    let pid:usize;
77    let procs:Vec<_> = s.processes_by_exact_name(process_name).collect();
78    match procs.len() {
79        1 => pid = procs[0].pid().into(),
80        0 => panic!("No process found!"),
81        _ => {
82            println!("Multipe processes found!, please try get_proc_by_id(pid) instead");
83            for proc in procs{
84                println!("{} => {}",proc.name(),proc.pid());
85            }
86            println!("Trying to use pidof"); 
87            std::process::exit(1);
88        },
89    }
90    Pid::from_raw(pid.try_into().unwrap())
91}
92
93
94/// `read_addr` is used to read `n` bytes from a process `pid` and starting from `addr`
95/// 
96/// # Note
97///
98/// the function will return Result<T,E>
99///
100/// Error examples:
101/// - `EPERM`: make sure running as sudo
102/// - `ESRCH`: make sure the process exist
103/// - `ESFAULT`: make sure the address exist in the scope of the process 
104///
105/// # Backend
106///
107/// this function invokes the `process_vm_readv` syscall, enabling direct memory reading from a specified address in the target process.
108pub fn read_addr(pid:Pid,addr:usize,length:usize) -> Result<Vec<u8>,Error>
109{
110    let mut data: Vec<u8> = vec![0;length]; 
111    let local_iov = IoSliceMut::new(&mut data);
112
113    let remote_iov = RemoteIoVec {
114        base : addr,
115        len : length,
116    };
117
118    process_vm_readv(pid,&mut [local_iov],&[remote_iov])?;
119    Ok(data[..length].to_vec())
120}
121
122/// `write_addr` is used to write a buffer of `n` bytes into the memory of process `pid` at `addr` 
123/// 
124/// # Backend
125///
126/// this function invokes the `process_vm_writev` syscall, enabling direct memory writing into a specified address in the target process. 
127/// 
128pub fn write_addr(pid:Pid,addr:usize,data:&[u8]){
129    let mut _data:&[u8] = data; 
130    let local_iov = IoSlice::new(&mut _data);
131
132    let remote_iov = RemoteIoVec {
133        base : addr,
134        len : data.len(),
135    };
136
137    process_vm_writev(pid,&mut [local_iov],&[remote_iov]).unwrap();
138}