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)
{