diff --git a/Reloaded.Memory.SigScan.Tests/ScannerTests.cs b/Reloaded.Memory.SigScan.Tests/ScannerTests.cs
index 28966f0..fbb5bed 100644
--- a/Reloaded.Memory.SigScan.Tests/ScannerTests.cs
+++ b/Reloaded.Memory.SigScan.Tests/ScannerTests.cs
@@ -39,9 +39,6 @@ public void InstantiateFromCurrentProcess()
// Test fails if function throws.
var thisProcess = Process.GetCurrentProcess();
var scanner = new Scanner(thisProcess, thisProcess.MainModule);
-
- Assert.NotEmpty(scanner.Data);
- Assert.NotNull (scanner.Data);
}
[Fact]
diff --git a/Reloaded.Memory.Sigscan.Benchmark/Benchmarks/Parsing/ParsePattern.cs b/Reloaded.Memory.Sigscan.Benchmark/Benchmarks/Parsing/ParsePattern.cs
index f6b4aca..97248a1 100644
--- a/Reloaded.Memory.Sigscan.Benchmark/Benchmarks/Parsing/ParsePattern.cs
+++ b/Reloaded.Memory.Sigscan.Benchmark/Benchmarks/Parsing/ParsePattern.cs
@@ -12,7 +12,7 @@ public class ParsePattern
[Benchmark]
public PatternScanInstructionSet MakeInstructionSetWithMask()
{
- return PatternScanInstructionSet.FromStringPattern("DA 69 ?? ?? FE B9");
+ return new PatternScanInstructionSet("DA 69 ?? ?? FE B9");
}
[Benchmark]
@@ -25,7 +25,7 @@ public SimplePatternScanData MakeScanDataWithMask()
[Benchmark]
public PatternScanInstructionSet MakeInstructionSetWithoutMask()
{
- return PatternScanInstructionSet.FromStringPattern("DA 69 DD AA FE B9");
+ return new PatternScanInstructionSet("DA 69 DD AA FE B9");
}
[Benchmark]
@@ -38,7 +38,7 @@ public SimplePatternScanData MakeScanDataWithoutMask()
[Benchmark]
public PatternScanInstructionSet MakeInstructionSetWithoutMaskLong()
{
- return PatternScanInstructionSet.FromStringPattern("DA 69 DD AA FE B9 BB CC DD EE FF");
+ return new PatternScanInstructionSet("DA 69 DD AA FE B9 BB CC DD EE FF");
}
[Benchmark]
diff --git a/Reloaded.Memory.Sigscan/Scanner.cs b/Reloaded.Memory.Sigscan/Scanner.cs
index e153b6f..cb8d27b 100644
--- a/Reloaded.Memory.Sigscan/Scanner.cs
+++ b/Reloaded.Memory.Sigscan/Scanner.cs
@@ -16,16 +16,12 @@ namespace Reloaded.Memory.Sigscan
///
/// Provides an implementation of a simple signature scanner sitting ontop of Reloaded.Memory.
///
- public unsafe class Scanner
+ public unsafe class Scanner : IDisposable
{
- ///
- /// The region of data to be scanned for signatures.
- ///
- public byte[] Data => _data.ToArray();
- private Memory _data;
-
- private GCHandle _gcHandle;
+ private bool _disposedValue;
+ private GCHandle? _gcHandle;
private byte* _dataPtr;
+ private int _dataLength;
///
/// Creates a signature scanner given the data in which patterns are to be found.
@@ -33,9 +29,9 @@ public unsafe class Scanner
/// The data to look for signatures inside.
public Scanner(byte[] data)
{
- _data = data;
_gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
- _dataPtr = (byte*) _gcHandle.AddrOfPinnedObject();
+ _dataPtr = (byte*)_gcHandle.Value.AddrOfPinnedObject();
+ _dataLength = data.Length;
}
///
@@ -49,9 +45,25 @@ public Scanner(Process process, ProcessModule module)
var externalProcess = new ExternalMemory(process);
externalProcess.ReadRaw(module.BaseAddress, out var data, module.ModuleMemorySize);
- _data = data;
_gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
- _dataPtr = (byte*)_gcHandle.AddrOfPinnedObject();
+ _dataPtr = (byte*)_gcHandle.Value.AddrOfPinnedObject();
+ _dataLength = data.Length;
+ }
+
+ ///
+ /// Creates a signature scanner given the data in which patterns are to be found.
+ ///
+ /// The data to look for signatures inside.
+ /// The length of the data.
+ public Scanner(byte* data, int length)
+ {
+ _dataPtr = data;
+ _dataLength = length;
+ }
+
+ ~Scanner()
+ {
+ Dispose(false);
}
///
@@ -67,16 +79,27 @@ public Scanner(Process process, ProcessModule module)
/// A result indicating an offset (if found) of the pattern.
public PatternScanResult CompiledFindPattern(string pattern)
{
- var instructionSet = PatternScanInstructionSet.FromStringPattern(pattern);
- int numberOfInstructions = instructionSet.NumberOfInstructions;
- int dataLength = _data.Length;
+ var instructionSet = new CompiledScanPattern(pattern);
+ return CompiledFindPattern(instructionSet);
+ }
+ ///
+ /// Attempts to find a given pattern inside the memory region this class was created with.
+ /// This method generally works better when the expected offset is bigger than 4096.
+ ///
+ ///
+ /// The compiled pattern to look for inside the given region.
+ ///
+ /// A result indicating an offset (if found) of the pattern.
+ public PatternScanResult CompiledFindPattern(CompiledScanPattern pattern)
+ {
+ int numberOfInstructions = pattern.NumberOfInstructions;
byte* dataBasePointer = _dataPtr;
byte* currentDataPointer;
- int lastIndex = dataLength - Math.Max(instructionSet.Length, sizeof(long)) + 1;
+ int lastIndex = _dataLength - Math.Max(pattern.Length, sizeof(long)) + 1;
// Note: All of this has to be manually inlined otherwise performance suffers, this is a bit ugly though :/
- fixed (GenericInstruction* instructions = instructionSet.Instructions)
+ fixed (GenericInstruction* instructions = pattern.Instructions)
{
var firstInstruction = instructions[0];
@@ -125,7 +148,7 @@ as opposing to having to dereference a pointer and then take an offset from the
// Check last few bytes in cases pattern was not found and long overflows into possibly unallocated memory.
- return SimpleFindPattern(pattern, lastIndex);
+ return SimpleFindPattern(pattern.Pattern, lastIndex);
// PS. This function is a prime example why the `goto` statement is frowned upon.
// I have to use it here for performance though.
@@ -150,7 +173,7 @@ public PatternScanResult SimpleFindPattern(string pattern, int startingIndex = 0
var patternData = target.Bytes;
var patternMask = target.Mask;
- int lastIndex = (_data.Span.Length - patternMask.Length) + 1;
+ int lastIndex = (_dataLength - patternMask.Length) + 1;
fixed (byte* patternDataPtr = patternData)
{
@@ -187,5 +210,25 @@ public PatternScanResult SimpleFindPattern(string pattern, int startingIndex = 0
return new PatternScanResult(-1);
}
}
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ _gcHandle?.Free();
+ }
+
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
}
diff --git a/Reloaded.Memory.Sigscan/Structs/PatternScanInstructionSet.cs b/Reloaded.Memory.Sigscan/Structs/CompiledScanPattern.cs
similarity index 90%
rename from Reloaded.Memory.Sigscan/Structs/PatternScanInstructionSet.cs
rename to Reloaded.Memory.Sigscan/Structs/CompiledScanPattern.cs
index 8c4f247..6732af7 100644
--- a/Reloaded.Memory.Sigscan/Structs/PatternScanInstructionSet.cs
+++ b/Reloaded.Memory.Sigscan/Structs/CompiledScanPattern.cs
@@ -12,17 +12,21 @@
namespace Reloaded.Memory.Sigscan.Structs
{
///
- /// [Internal & Test Use]
/// Represents the pattern to be searched by the scanner.
///
- public ref struct PatternScanInstructionSet
+ public ref struct CompiledScanPattern
{
private const string MaskIgnore = "??";
+ ///
+ /// The pattern the instruction set was created from.
+ ///
+ public readonly string Pattern;
+
///
/// The length of the original given pattern.
///
- public int Length;
+ public readonly int Length;
///
/// Contains the functions that will be executed in order to validate a given block of memory to equal
@@ -43,15 +47,9 @@ public ref struct PatternScanInstructionSet
/// Example: "11 22 33 ?? 55".
/// Key: ?? represents a byte that should be ignored, anything else if a hex byte. i.e. 11 represents 0x11, 1F represents 0x1F.
///
- public static PatternScanInstructionSet FromStringPattern(string stringPattern)
- {
- var instructionSet = new PatternScanInstructionSet();
- instructionSet.Initialize(stringPattern);
- return instructionSet;
- }
-
- private unsafe void Initialize(string stringPattern)
+ public CompiledScanPattern(string stringPattern)
{
+ Pattern = stringPattern;
string[] entries = stringPattern.Split(' ');
Length = entries.Length;
@@ -70,6 +68,7 @@ private unsafe void Initialize(string stringPattern)
// Get bytes to make instructions with.
Instructions = new GenericInstruction[Length];
+ NumberOfInstructions = 0;
// Optimization for short-medium patterns with masks.
// Check if our pattern is 1-8 bytes and contains any skips.