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