diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..2998dc6 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Bash(git add:*)", + "Bash(git commit:*)" + ], + "deny": [], + "ask": [] + } +} diff --git a/ghost-core/src/lib.rs b/ghost-core/src/lib.rs index c6ea6c3..3bb8681 100644 --- a/ghost-core/src/lib.rs +++ b/ghost-core/src/lib.rs @@ -1,3 +1,5 @@ +pub mod memory; pub mod process; +pub use memory::{MemoryProtection, MemoryRegion}; pub use process::ProcessInfo; diff --git a/ghost-core/src/memory.rs b/ghost-core/src/memory.rs new file mode 100644 index 0000000..7e0baaf --- /dev/null +++ b/ghost-core/src/memory.rs @@ -0,0 +1,151 @@ +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MemoryProtection { + NoAccess, + ReadOnly, + ReadWrite, + ReadExecute, + ReadWriteExecute, + Execute, + WriteCopy, + Unknown, +} + +impl fmt::Display for MemoryProtection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::NoAccess => "---", + Self::ReadOnly => "R--", + Self::ReadWrite => "RW-", + Self::ReadExecute => "R-X", + Self::ReadWriteExecute => "RWX", + Self::Execute => "--X", + Self::WriteCopy => "WC-", + Self::Unknown => "???", + }; + write!(f, "{}", s) + } +} + +#[derive(Debug, Clone)] +pub struct MemoryRegion { + pub base_address: usize, + pub size: usize, + pub protection: MemoryProtection, + pub region_type: String, +} + +impl fmt::Display for MemoryRegion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{:#016x} - {:#016x} {} {}", + self.base_address, + self.base_address + self.size, + self.protection, + self.region_type + ) + } +} + +#[cfg(windows)] +mod platform { + use super::{MemoryProtection, MemoryRegion}; + use anyhow::{Context, Result}; + use windows::Win32::Foundation::{CloseHandle, HANDLE}; + use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory; + use windows::Win32::System::Memory::{ + VirtualQueryEx, MEMORY_BASIC_INFORMATION, MEM_COMMIT, MEM_FREE, MEM_IMAGE, MEM_MAPPED, + MEM_PRIVATE, MEM_RESERVE, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, + PAGE_EXECUTE_WRITECOPY, PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY, + }; + use windows::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ}; + + fn parse_protection(protect: u32) -> MemoryProtection { + match protect & 0xFF { + p if p == PAGE_NOACCESS.0 => MemoryProtection::NoAccess, + p if p == PAGE_READONLY.0 => MemoryProtection::ReadOnly, + p if p == PAGE_READWRITE.0 => MemoryProtection::ReadWrite, + p if p == PAGE_EXECUTE.0 => MemoryProtection::Execute, + p if p == PAGE_EXECUTE_READ.0 => MemoryProtection::ReadExecute, + p if p == PAGE_EXECUTE_READWRITE.0 => MemoryProtection::ReadWriteExecute, + p if p == PAGE_WRITECOPY.0 || p == PAGE_EXECUTE_WRITECOPY.0 => { + MemoryProtection::WriteCopy + } + _ => MemoryProtection::Unknown, + } + } + + pub fn enumerate_memory_regions(pid: u32) -> Result> { + let mut regions = Vec::new(); + + unsafe { + let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid) + .context("Failed to open process")?; + + let mut address: usize = 0; + let mut mbi = MEMORY_BASIC_INFORMATION::default(); + + loop { + let result = VirtualQueryEx( + handle, + Some(address as *const _), + &mut mbi, + std::mem::size_of::(), + ); + + if result == 0 { + break; + } + + if mbi.State == MEM_COMMIT { + let region_type = if mbi.Type == MEM_IMAGE { + "IMAGE" + } else if mbi.Type == MEM_MAPPED { + "MAPPED" + } else if mbi.Type == MEM_PRIVATE { + "PRIVATE" + } else { + "UNKNOWN" + } + .to_string(); + + regions.push(MemoryRegion { + base_address: mbi.BaseAddress as usize, + size: mbi.RegionSize, + protection: parse_protection(mbi.Protect.0), + region_type, + }); + } + + address = (mbi.BaseAddress as usize) + .checked_add(mbi.RegionSize) + .unwrap_or(usize::MAX); + + if address == usize::MAX { + break; + } + } + + let _ = CloseHandle(handle); + } + + Ok(regions) + } +} + +#[cfg(not(windows))] +mod platform { + use super::MemoryRegion; + use anyhow::Result; + + pub fn enumerate_memory_regions(_pid: u32) -> Result> { + // TODO: Implement Linux/macOS memory enumeration + Ok(Vec::new()) + } +} + +pub fn enumerate_memory_regions(pid: u32) -> anyhow::Result> { + platform::enumerate_memory_regions(pid) +}