rsmemoryapi 1.0.1

Allows interract with process memory
Documentation
#![allow(dead_code)]

#[link(name="psapi")]
extern "system"
{
    fn EnumProcesses(id_processes: *mut u32, cb: u32, cb_needed: *mut u32) -> i32;
    fn GetProcessImageFileNameA(handle: usize, image_filename: *mut u8, size: u32) -> u32;
}

#[link(name="kernel32")]
extern "system"
{
    fn IsWow64Process(handle: usize, bool_result: *mut i32) -> i32;
    fn OpenProcess(desired_access: u32, inherit_handle: i32, process_id: u32) -> usize;
    fn GetModuleBaseNameA(handle: usize, module_handle: usize, name: *mut u8, size: u32) -> u32;
    fn GetModuleInformation(handle: usize, module_handle: usize, module_pointer: *mut Module, cb: u32) -> i32;
    fn EnumProcessModulesEx(handle: usize, modules_array: *mut usize, cb: u32, cb_needed: *mut u32, filter_flag: u32) -> i32;
    fn ReadProcessMemory(handle: usize, base_address: *const usize, buffer: *mut u8, size: usize, bytes_read: *mut usize) -> i32;
    fn WriteProcessMemory(handle: usize, base_address: *const usize, buffer: *const u8, size: usize, bytes_written: *mut usize) -> i32;
}

const PATH_SIZE: usize = 260;
const PROCESS_ALL_ACCESS: u32 = 0x001F0FFF;

// Стркуктуры
#[repr(C)]
#[derive(Debug)]
#[derive(Clone, Copy)]
#[derive(PartialEq, PartialOrd)]
pub struct Module
{
    pub base_address: usize,
    pub size_of_image: usize,
    pub entry_point: usize
}

#[repr(C)]
#[derive(Debug)]
pub struct Process
{
    pub process_name: String,
    pub process_handle: usize,
    pub process_id: u32,
}

impl Process
{
    pub fn new_from_id(process_id: u32) -> Option<Process>
    {
        let active_process_ids: Vec<u32> = list_processes().unwrap().into_iter().map(|(x, _)| x).collect();

        match active_process_ids.iter().position(|&x| x == process_id)
        {
            None => panic!("Process with \"{}\" id not found", process_id),
            Some(_) => 
            {
                let handle: usize = unsafe {OpenProcess(PROCESS_ALL_ACCESS, 0, process_id)};
                let mut image_file_name: [u8; PATH_SIZE] = [0; PATH_SIZE];

                
                unsafe {GetProcessImageFileNameA(handle, &mut image_file_name as *mut u8, 260)};
                let file_name_decoded: String = String::from_utf8(image_file_name.to_vec()).unwrap().trim_end_matches("\0").to_string();

                match file_name_decoded.split("\\").last()
                {
                    Some(name) => {Some(Process{process_name: name.to_string(), process_handle: handle, process_id: process_id})}
                    None => {Some(Process{process_name: "".to_string(), process_handle: handle, process_id: process_id})}
                }
            }    
        }
    }

    pub fn new_from_name(process_name: String) -> Option<Process>
    {
        let active_process_ids: Vec<u32> = list_processes().unwrap().into_iter().map(|(x, _)| x).collect();
        let active_process_names: Vec<String> = list_processes().unwrap().into_iter().map(|(_, x)| x).collect();

        match active_process_names.into_iter().position(|x| x.to_lowercase() == process_name.to_lowercase())
        {
            None => panic!("Process with \"{}\" name not found", process_name),
            Some(index) =>
            {
                let handle: usize = unsafe {OpenProcess(PROCESS_ALL_ACCESS, 0, active_process_ids[index])};
                let mut image_file_name: [u8; PATH_SIZE] = [0; PATH_SIZE];

                
                unsafe {GetProcessImageFileNameA(handle, &mut image_file_name as *mut u8, 260)};
                let file_name_decoded: String = String::from_utf8(image_file_name.to_vec()).unwrap().trim_end_matches("\0").to_string();

                match file_name_decoded.split("\\").last()
                {
                    Some(name) => {Some(Process{process_name: name.to_string(), process_handle: handle, process_id: active_process_ids[index]})}
                    None => {Some(Process{process_name: "".to_string(), process_handle: handle, process_id: active_process_ids[index]})}
                }
            }
        }
    }

    pub fn is_64x(&self) -> bool
    {
        let mut is_64: i32 = 0;
        if unsafe {IsWow64Process(self.process_handle, &mut is_64 as *mut i32)} != 0 {true} else {false}
    }

    pub fn read_memory<T>(&self, address: usize) -> Option<T>
    {
        let mut buffer: T = unsafe {std::mem::zeroed::<T>()};
        let mut bytes_read: usize = 0;

        let success: i32 = unsafe {ReadProcessMemory(self.process_handle, address as *const usize, &mut buffer as *mut T as *mut u8, std::mem::size_of::<T>(), &mut bytes_read)};
        if success != 0 {Some(buffer)} else {None}
    }

    pub fn write_memory<T>(&self, address: usize, value: T) -> bool
    {
        let mut bytes_written: usize = 0;

        let success: i32 = unsafe {WriteProcessMemory(self.process_handle, address as *const usize, &value as *const T as *const u8, std::mem::size_of::<T>(), &mut bytes_written)};
        if success != 0 {true} else {false}
    }

    // TODO
    pub fn get_module_info(&self, module_name: String) -> Option<Module>
    {
        let modules = self.list_modules().unwrap();

        for (name, module_object) in modules
        {
            if module_name.to_lowercase() == name.to_lowercase()
            {
                return Some(module_object);
            }
        }
        None
    }

    pub fn list_modules(&self) -> Option<Vec<(String, Module)>>
    {
        let mut current_module_objects: Vec<(String, Module)> = Vec::new();
        let mut module_handles: [usize; 2048] = [0; 2048];
        let mut cb_needed: u32 = 0;

        match unsafe {EnumProcessModulesEx(self.process_handle, &mut module_handles as *mut usize, std::mem::size_of_val(&module_handles) as u32, &mut cb_needed as *mut u32, 0x003)}
        {
            0 => {}
            _ =>
            {
                let active_modules_count: usize = (cb_needed / std::mem::size_of::<u32>() as u32) as usize;
                for module_handle in 0..active_modules_count
                {
                    let mut module_info: Module = unsafe{std::mem::zeroed::<Module>()};
                    unsafe {GetModuleInformation(self.process_handle, module_handles[module_handle], &mut module_info as *mut Module, std::mem::size_of_val(&mut module_handles) as u32)};
                    
                    if !current_module_objects.contains(&(get_module_name(self.process_handle, module_handles[module_handle]), module_info))
                    {
                        current_module_objects.push((get_module_name(self.process_handle, module_handles[module_handle]), module_info));
                    }
                }
            }
        }
        if !current_module_objects.is_empty() {Some(current_module_objects)} else {None}
    }
}

// Функции
pub fn get_module_name(process_handle: usize, module_hande: usize) -> String
{
    let mut buffer: [u8; PATH_SIZE] = [0; PATH_SIZE];
    unsafe {GetModuleBaseNameA(process_handle, module_hande, &mut buffer as *mut u8, std::mem::size_of_val(&mut buffer) as u32)};

    let decoded_buffer: String = String::from_utf8(buffer.to_vec()).unwrap().trim_end_matches("\0").to_string();
    decoded_buffer
    
}

pub fn list_processes() -> Option<Vec<(u32, String)>>
{
    let mut current_process_names: Vec<(u32, String)> = Vec::new();
    let mut current_process_ids: [u32; 2048] = [0; 2048];
    let mut cb_needed: u32 = 0;

    match unsafe {EnumProcesses(current_process_ids.as_mut_ptr(), std::mem::size_of_val(&current_process_ids) as u32, &mut cb_needed)}
    {
        0 => {},
        _ => 
        {
            let active_processes_count: usize = (cb_needed / std::mem::size_of::<u32>() as u32) as usize;
            for process_id in 0..active_processes_count
            {
                let handle: usize = unsafe {OpenProcess(PROCESS_ALL_ACCESS, 0, current_process_ids[process_id] as u32)};

                match handle
                {
                    0 => {}
                    _ =>
                    {
                        let mut image_file_name: [u8; PATH_SIZE] = [0; PATH_SIZE];
                        unsafe {GetProcessImageFileNameA(handle, &mut image_file_name as *mut u8, 260)};
                        
                        let file_name_decoded: String = String::from_utf8(image_file_name.to_vec()).unwrap().trim_end_matches("\0").to_string();
                        
                        match file_name_decoded.split("\\").last()
                        {
                            None => {}
                            Some(name) => {current_process_names.push((current_process_ids[process_id], name.to_string()))}
                        }
                    }
                }
            }
        }
    }
    if !current_process_names.is_empty() {Some(current_process_names)} else {None}
}