diff --git a/Examples/PatternScanner/Program.cs b/Examples/PatternScanner/Program.cs index e3be910..6dac8bc 100644 --- a/Examples/PatternScanner/Program.cs +++ b/Examples/PatternScanner/Program.cs @@ -11,7 +11,7 @@ static class Program .WriteTo.Console() .CreateLogger(); - public static unsafe void Main() + public static void Main() { var processId = Process.GetProcessesByName("CS2").FirstOrDefault()!.Id; @@ -25,8 +25,8 @@ public static unsafe void Main() IntPtr startAddr = IntPtr.Subtract(localPlayerPtr, 20); - var region = mem.Read(startAddr, 20 + sizeof(IntPtr) + 20); - var ptr = mem.Read(localPlayerPtr, sizeof(IntPtr)); + var region = mem.Read(startAddr, 20 + IntPtr.Size + 20); + var ptr = mem.Read(localPlayerPtr, IntPtr.Size); Log.Information("Region: {Region}", BitConverter.ToString(region)); diff --git a/MemNet/Enum/AllocationType.cs b/MemNet/Enum/AllocationType.cs index 2ee87bc..fa7b312 100644 --- a/MemNet/Enum/AllocationType.cs +++ b/MemNet/Enum/AllocationType.cs @@ -25,7 +25,7 @@ public enum AllocationType : uint /// /// Indicates that the pages of the specified region should be protected for access by multiple processes. /// - MEM_SHARED = 0x00001000, // Note: This value overlaps with MEM_COMMIT in some contexts, careful usage is needed. + MEM_SHARED = 0x08000000, /// /// Allocates memory using large page support. diff --git a/MemNet/Library.cs b/MemNet/Library.cs index ddfacd2..cb89a05 100644 --- a/MemNet/Library.cs +++ b/MemNet/Library.cs @@ -71,6 +71,7 @@ public void Open( /// /// An existing valid handle to the target process. /// Thrown if the process is already open. + /// Thrown if the handle is invalid (zero or -1). public void Open(IntPtr existingHandle) { if (_processHandle != IntPtr.Zero) @@ -78,6 +79,11 @@ public void Open(IntPtr existingHandle) throw new InvalidOperationException("Process already open. Close before hijacking another handle."); } + if (existingHandle == IntPtr.Zero || existingHandle == new IntPtr(-1)) + { + throw new ArgumentException("The provided handle is invalid.", nameof(existingHandle)); + } + _processHandle = existingHandle; _logger.Debug("Hijacked handle for process {ProcessId}.", _processId); } @@ -169,7 +175,14 @@ out var returnedBytes Type = (MemoryType)mbi.Type }; - long nextAddress = mbi.BaseAddress.ToInt64() + mbi.RegionSize; + long baseAddr = mbi.BaseAddress.ToInt64(); + long regionSize = mbi.RegionSize.ToInt64(); + + // Check for overflow before addition + if (baseAddr > long.MaxValue - regionSize) + break; + + long nextAddress = baseAddr + regionSize; if (nextAddress < 0) break; address = (IntPtr)nextAddress; } @@ -189,11 +202,18 @@ out var returnedBytes /// The size of each chunk read from the process memory. /// A list of matching addresses. /// Thrown if the process is not open. - /// Thrown if the pattern is null or empty. + /// Thrown if the pattern is null. + /// Thrown if the pattern is empty or contains no valid tokens. /// Thrown if addresses or chunk size are invalid. - public List Search(string pattern, IntPtr startAddress, IntPtr endAddress, int chunkSize = 8196) + public List Search(string pattern, IntPtr startAddress, IntPtr endAddress, int chunkSize = 8192) { - var patternTokens = pattern.Split(' '); + if (pattern is null) + throw new ArgumentNullException(nameof(pattern), "The search pattern cannot be null."); + + var patternTokens = pattern.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (patternTokens.Length == 0) + throw new ArgumentException("The search pattern is empty or contains only whitespace.", nameof(pattern)); + var wildcards = patternTokens.Select(token => new Wildcard(token)).ToArray(); return Search(wildcards, startAddress, endAddress, chunkSize); } @@ -209,7 +229,7 @@ public List Search(string pattern, IntPtr startAddress, IntPtr endAddres /// Thrown if the process is not open. /// Thrown if the pattern is null or empty. /// Thrown if addresses or chunk size are invalid. - public List Search(Wildcard[] pattern, IntPtr startAddress, IntPtr endAddress, int chunkSize = 8196) + public List Search(Wildcard[] pattern, IntPtr startAddress, IntPtr endAddress, int chunkSize = 8192) { if (_processHandle == IntPtr.Zero) throw new InvalidOperationException($"Process with ID {_processId} is not open."); @@ -319,7 +339,7 @@ public byte[] Read(IntPtr address, int size) throw new InvalidOperationException($"Process with ID {_processId} is not open."); if (size <= 0) - throw new ArgumentOutOfRangeException(nameof(size), "Size must be non-negative."); + throw new ArgumentOutOfRangeException(nameof(size), "Size must be positive."); byte[] buffer = new byte[size]; GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); @@ -403,7 +423,7 @@ public void Write(IntPtr address, byte[] buffer) throw new InvalidOperationException($"Process with ID {_processId} is not open."); if (buffer.Length <= 0) - throw new ArgumentOutOfRangeException(nameof(buffer), "Buffer must be non-negative."); + throw new ArgumentOutOfRangeException(nameof(buffer), "Buffer length must be positive."); GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); diff --git a/MemNet/Native/ProcessAccessRights.cs b/MemNet/Native/ProcessAccessRights.cs index 5ba8223..c8c834c 100644 --- a/MemNet/Native/ProcessAccessRights.cs +++ b/MemNet/Native/ProcessAccessRights.cs @@ -23,8 +23,6 @@ public enum ProcessAccessRights : uint PROCESS_SET_INFORMATION = 0x0200, PROCESS_QUERY_INFORMATION = 0x0400, PROCESS_SUSPEND_RESUME = 0x0800, - PROCESS_GET_CONTEXT = 0x1000, - PROCESS_SET_CONTEXT = 0x2000, - PROCESS_QUERY_LIMITED_INFORMATION = 0x2000, // Introduced in Windows Server 2003 + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, // Introduced in Windows Vista PROCESS_ALL_ACCESS = 0x000F0000 | 0x001FFFFF } \ No newline at end of file diff --git a/MemNet/Wildcard.cs b/MemNet/Wildcard.cs index e0db5a4..8ead039 100644 --- a/MemNet/Wildcard.cs +++ b/MemNet/Wildcard.cs @@ -1,9 +1,32 @@ namespace MemNet; -public readonly struct Wildcard(string token) +public readonly struct Wildcard { - private readonly int? _highNibble = token[0] == '?' ? null : Convert.ToInt32(token[0].ToString(), 16); // null represents wildcard - private readonly int? _lowNibble = token[1] == '?' ? null : Convert.ToInt32(token[1].ToString(), 16); // null represents wildcard + private readonly int? _highNibble; // null represents wildcard + private readonly int? _lowNibble; // null represents wildcard + + public Wildcard(string token) + { + if (string.IsNullOrEmpty(token)) + throw new ArgumentException("Token cannot be null or empty.", nameof(token)); + + if (token.Length != 2) + throw new ArgumentException($"Token must be exactly 2 characters, got {token.Length}: '{token}'", nameof(token)); + + _highNibble = ParseNibble(token[0], nameof(token)); + _lowNibble = ParseNibble(token[1], nameof(token)); + } + + private static int? ParseNibble(char c, string paramName) + { + if (c == '?') + return null; + + if (c is (>= '0' and <= '9') or (>= 'A' and <= 'F') or (>= 'a' and <= 'f')) + return Convert.ToInt32(c.ToString(), 16); + + throw new ArgumentException($"Invalid hex character '{c}'. Expected 0-9, A-F, a-f, or '?' for wildcard.", paramName); + } public bool Matches(byte b) {