use crate::memory::{read_memory_bytes, write_memory_bytes, MemoryError};
use crate::memory_resolver::MemoryAddress;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use std::thread;
use std::time::Duration;
use windows::Win32::Foundation::HANDLE;
pub trait AsBytes {
fn as_bytes(&self) -> &[u8];
}
impl AsBytes for u8 {
fn as_bytes(&self) -> &[u8] {
std::slice::from_ref(self)
}
}
impl AsBytes for u16 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const u16 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<u16>()) }
}
}
impl AsBytes for u32 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const u32 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<u32>()) }
}
}
impl AsBytes for u64 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const u64 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<u64>()) }
}
}
impl AsBytes for f32 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const f32 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<f32>()) }
}
}
impl AsBytes for f64 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const f64 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<f64>()) }
}
}
impl AsBytes for i8 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const i8 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<i8>()) }
}
}
impl AsBytes for i16 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const i16 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<i16>()) }
}
}
impl AsBytes for i32 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const i32 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<i32>()) }
}
}
impl AsBytes for i64 {
fn as_bytes(&self) -> &[u8] {
let ptr = self as *const i64 as *const u8;
unsafe { std::slice::from_raw_parts(ptr, std::mem::size_of::<i64>()) }
}
}
pub struct MemoryLockBuilder {
handle: Option<HANDLE>,
memory_address: Option<MemoryAddress>,
pid: Option<u32>,
locked_value: Option<Vec<u8>>,
scan_interval: Duration,
}
impl MemoryLockBuilder {
pub fn new() -> Self {
Self {
handle: None,
memory_address: None,
pid: None,
locked_value: None,
scan_interval: Duration::from_millis(100), }
}
pub fn handle(mut self, handle: HANDLE) -> Self {
self.handle = Some(handle);
self
}
pub fn address(mut self, address: usize) -> Self {
let mem_addr = MemoryAddress {
base: crate::memory_resolver::AddressBase::Absolute(address),
operations: vec![],
pointer_size: crate::memory_resolver::PointerSize::default_architecture(),
};
self.memory_address = Some(mem_addr);
self
}
pub fn address_from_resolver(mut self, addr: MemoryAddress) -> Self {
self.memory_address = Some(addr);
self
}
pub fn pid(mut self, pid: u32) -> Self {
self.pid = Some(pid);
self
}
pub fn value<T: AsBytes>(mut self, val: T) -> Self {
self.locked_value = Some(val.as_bytes().to_vec());
self
}
pub fn bytes(mut self, data: Vec<u8>) -> Self {
self.locked_value = Some(data);
self
}
pub fn scan_interval(mut self, interval: Duration) -> Self {
self.scan_interval = interval;
self
}
pub fn build(self) -> Result<MemoryLock, MemoryError> {
let handle = self.handle.ok_or_else(|| {
MemoryError::WriteFailed(
"handle must be set. Call .handle(handle) before build().".to_string(),
)
})?;
let memory_address = self.memory_address.ok_or_else(|| {
MemoryError::WriteFailed(
"address must be set. Call .address(addr) or .address_from_resolver(addr) before build().".to_string()
)
})?;
let locked_value = self.locked_value.ok_or_else(|| {
MemoryError::WriteFailed(
"locked_value must be set. Call .value(val) or .bytes(data) before build()."
.to_string(),
)
})?;
let size = locked_value.len();
if self.pid.is_none() {
let needs_pid = matches!(
memory_address.base,
crate::memory_resolver::AddressBase::Module { .. }
);
if needs_pid {
return Err(MemoryError::WriteFailed(
"PID must be set when using module-relative addresses. Call .pid(pid) before build().".to_string()
));
}
}
Ok(MemoryLock {
handle: Some(handle), pid: self.pid,
memory_address: Some(memory_address),
size,
locked_value,
scan_interval: self.scan_interval,
stop_flag: Arc::new(AtomicBool::new(false)),
worker_thread: None,
})
}
}
pub struct MemoryLock {
handle: Option<HANDLE>, memory_address: Option<MemoryAddress>, pid: Option<u32>, size: usize,
locked_value: Vec<u8>,
scan_interval: Duration,
stop_flag: Arc<AtomicBool>,
worker_thread: Option<thread::JoinHandle<()>>,
}
impl MemoryLock {
pub fn builder() -> MemoryLockBuilder {
MemoryLockBuilder::new()
}
pub fn set_scan_interval(&mut self, interval: Duration) {
self.scan_interval = interval;
}
pub fn lock_value<T: Copy + AsBytes>(&mut self, value: T) -> Result<(), MemoryError> {
let handle = self.handle.ok_or_else(|| {
MemoryError::WriteFailed(
"handle must be set. Call .handle(handle) before lock_value().".to_string(),
)
})?;
let memory_address = self.memory_address.as_ref().ok_or_else(|| {
MemoryError::WriteFailed("address must be set. Call .address(addr), .address_from_resolver(addr), or .address_from_aob(pattern) before lock_value().".to_string())
})?;
let pid = self.pid.unwrap_or(0);
let address = match memory_address.resolve_address(handle, pid) {
Ok(addr) => addr,
Err(_) => {
let bytes = value.as_bytes();
self.size = bytes.len();
self.locked_value = bytes.to_vec();
return self.start_monitoring(); }
};
let bytes = value.as_bytes();
self.size = bytes.len();
self.locked_value = bytes.to_vec();
write_memory_bytes(handle, address, &self.locked_value)?;
self.start_monitoring()?;
Ok(())
}
pub fn lock_bytes(&mut self, bytes: &[u8]) -> Result<(), MemoryError> {
let handle = self.handle.ok_or_else(|| {
MemoryError::WriteFailed(
"handle must be set. Call .handle(handle) before lock_bytes().".to_string(),
)
})?;
let memory_address = self.memory_address.as_ref().ok_or_else(|| {
MemoryError::WriteFailed("address must be set. Call .address(addr), .address_from_resolver(addr), or .address_from_aob(pattern) before lock_bytes().".to_string())
})?;
let pid = self.pid.unwrap_or(0);
let address = match memory_address.resolve_address(handle, pid) {
Ok(addr) => addr,
Err(_) => {
self.size = bytes.len();
self.locked_value = bytes.to_vec();
return self.start_monitoring(); }
};
self.size = bytes.len();
self.locked_value = bytes.to_vec();
write_memory_bytes(handle, address, &self.locked_value)?;
self.start_monitoring()?;
Ok(())
}
fn start_monitoring(&mut self) -> Result<(), MemoryError> {
let handle = self.handle.unwrap();
let memory_address = self.memory_address.clone().unwrap();
let pid = self.pid.unwrap_or(0);
let size = self.size;
let locked_value = self.locked_value.clone();
let scan_interval = self.scan_interval;
let stop_flag = self.stop_flag.clone();
let handle_usize = handle.0 as usize;
self.worker_thread = Some(thread::spawn(move || {
let handle = HANDLE(handle_usize as *mut std::ffi::c_void);
while !stop_flag.load(Ordering::SeqCst) {
thread::sleep(scan_interval);
let address = match memory_address.resolve_address(handle, pid) {
Ok(addr) => addr,
Err(_) => continue,
};
let current_value = match read_memory_bytes(handle, address, size) {
Ok(val) => val,
Err(_) => continue,
};
if current_value != locked_value {
if let Err(_) = write_memory_bytes(handle, address, &locked_value) {
continue;
}
}
}
}));
Ok(())
}
pub fn stop(&mut self) -> Result<(), MemoryError> {
self.stop_flag.store(true, Ordering::SeqCst);
if let Some(handle) = self.worker_thread.take() {
let _ = handle.join();
}
Ok(())
}
pub fn is_running(&self) -> bool {
self.worker_thread.is_some() && !self.stop_flag.load(Ordering::SeqCst)
}
pub fn get_handle(&self) -> Option<HANDLE> {
self.handle
}
pub fn get_pid(&self) -> Option<u32> {
self.pid
}
}
impl Drop for MemoryLock {
fn drop(&mut self) {
let _ = self.stop();
}
}