diff --git a/.gitignore b/.gitignore index ea8c4bf..96ef6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 12e43fa..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "haxor" -version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 65e1d76..ba81461 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,14 @@ version = "0.1.0" edition = "2021" [dependencies] +derive_more = { version = "1.0.0", features = ["display"] } +thiserror = "2.0.9" +windows-core = "0.58.0" + +[dependencies.windows] +version = "0.58.0" +features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_System_Diagnostics_ToolHelp", +] diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..fb1a96e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,13 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("couldn't get handle to process")] + HandleError(String), + #[error("couldn't create instance of process")] + ProcessError(String), + #[error("couldn't create snapshot")] + SnapshotError(String), + #[error("failed to convert integer")] + IntegerConversionError(#[from] std::num::TryFromIntError), +} diff --git a/src/handle.rs b/src/handle.rs new file mode 100644 index 0000000..9ad23b4 --- /dev/null +++ b/src/handle.rs @@ -0,0 +1,49 @@ +use std::ops::Deref; + +use windows::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE}; +use windows::Win32::System::Threading::{ + OpenProcess, PROCESS_ALL_ACCESS, PROCESS_VM_READ, PROCESS_VM_WRITE, +}; + +#[derive(Debug, Default)] +pub struct Handle(pub HANDLE); + +impl Deref for Handle { + type Target = HANDLE; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Drop for Handle { + fn drop(&mut self) { + if self.0 != INVALID_HANDLE_VALUE { + unsafe { + if let Err(why) = CloseHandle(**self) { + eprintln!("couldn't close handle: {why}"); + }; + } + } + } +} + +impl Handle { + pub fn from_pid(pid: u32) -> Result { + let handle = unsafe { + OpenProcess(PROCESS_ALL_ACCESS, false, pid) + .or_else(|_| OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE, false, pid)) + .map_err(|why| { + crate::Error::HandleError(format!( + "couldn't open process with needed access: {why}", + )) + })? + }; + if handle == INVALID_HANDLE_VALUE { + return Err(crate::Error::HandleError( + "couldn't get a valid handle".to_string(), + )); + } + Ok(Self(handle)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8b13789..513bf83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,7 @@ +mod error; +mod handle; +mod process; +mod snapshot; +pub use error::Error; +pub use process::{Identifier, Process}; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3f0d57b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,6 @@ +use haxor::Process; + +fn main() { + dbg!(Process::with_pid(3440).unwrap()); + dbg!(Process::with_name("notepad.exe").unwrap()); +} diff --git a/src/process.rs b/src/process.rs new file mode 100644 index 0000000..de0c19d --- /dev/null +++ b/src/process.rs @@ -0,0 +1,50 @@ +use crate::{handle::Handle, snapshot}; + +use derive_more::derive::Display; + +#[derive(Debug, Default)] +pub struct Process { + name: String, + id: u32, + base_address: usize, + handle: Handle, +} + +#[derive(Display)] +pub enum Identifier { + Id(u32), + Name(String), +} + +impl Process { + pub fn from(identifier: &Identifier) -> Result { + let snapshot = match identifier { + Identifier::Id(pid) => snapshot::Process::get_processes()? + .into_iter() + .find(|snapshot| snapshot.id == *pid), + Identifier::Name(ref name) => snapshot::Process::get_processes()? + .into_iter() + .find(|snapshot| snapshot.name == *name), + }; + let Some(snapshot) = snapshot else { + return Err(crate::Error::ProcessError(format!( + "couldn't find a process with identifier `{identifier}`", + ))); + }; + let process = Self { + name: snapshot.name, + id: snapshot.id, + base_address: 0, + handle: Handle::from_pid(snapshot.id)?, + }; + Ok(process) + } + + pub fn with_pid(pid: u32) -> Result { + Self::from(&Identifier::Id(pid)) + } + + pub fn with_name(name: &str) -> Result { + Self::from(&Identifier::Name(name.to_string())) + } +} diff --git a/src/snapshot.rs b/src/snapshot.rs new file mode 100644 index 0000000..7e499ab --- /dev/null +++ b/src/snapshot.rs @@ -0,0 +1,55 @@ +use windows::Win32::System::Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS, +}; + +#[derive(Debug, Default)] +pub struct Process { + pub id: u32, + pub name: String, +} + +impl Process { + pub fn get_processes() -> Result, crate::Error> { + let snapshot = unsafe { + match CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) { + Ok(snapshot) => snapshot, + Err(why) => { + return Err(crate::Error::SnapshotError(format!( + "couldn't create snapshot: {why}" + ))) + } + } + }; + let mut process_entry_32_w = PROCESSENTRY32W { + dwSize: u32::try_from(size_of::())?, + ..Default::default() + }; + unsafe { + match Process32FirstW(snapshot, &mut process_entry_32_w) { + Ok(()) => {} + Err(why) => { + return Err(crate::Error::SnapshotError(format!( + "couldn't get first process from snapshot: {why}" + ))) + } + }; + } + let mut processes = Vec::new(); + loop { + let name = String::from_utf16_lossy(&process_entry_32_w.szExeFile) + .trim_end_matches('\0') + .to_string(); + let process = Self { + id: process_entry_32_w.th32ProcessID, + name, + }; + processes.push(process); + unsafe { + if Process32NextW(snapshot, &mut process_entry_32_w).is_err() { + break; + }; + } + } + Ok(processes) + } +}