diff --git a/Dalamud/Game/SigScanner.cs b/Dalamud/Game/SigScanner.cs
index 9cd9b7694..cb57f3789 100644
--- a/Dalamud/Game/SigScanner.cs
+++ b/Dalamud/Game/SigScanner.cs
@@ -26,7 +26,6 @@ namespace Dalamud.Game {
if (IsCopy)
SetupCopiedSegments();
-
Log.Verbose("Module base: {Address}", TextSectionBase);
Log.Verbose("Module size: {Size}", TextSectionSize);
}
@@ -34,7 +33,7 @@ namespace Dalamud.Game {
///
/// If the search on this module is performed on a copy.
///
- public bool IsCopy { get; private set; }
+ public bool IsCopy { get; }
///
/// If the ProcessModule is 32-bit.
@@ -50,10 +49,12 @@ namespace Dalamud.Game {
/// The base address of the .text section search area.
///
public IntPtr TextSectionBase => new IntPtr(SearchBase.ToInt64() + TextSectionOffset);
+
///
/// The offset of the .text section from the base of the module.
///
public long TextSectionOffset { get; private set; }
+
///
/// The size of the text section.
///
@@ -63,10 +64,12 @@ namespace Dalamud.Game {
/// The base address of the .data section search area.
///
public IntPtr DataSectionBase => new IntPtr(SearchBase.ToInt64() + DataSectionOffset);
+
///
/// The offset of the .data section from the base of the module.
///
public long DataSectionOffset { get; private set; }
+
///
/// The size of the .data section.
///
@@ -128,11 +131,8 @@ namespace Dalamud.Game {
// .text
this.moduleCopyPtr = Marshal.AllocHGlobal(Module.ModuleMemorySize);
Log.Verbose($"Alloc: {this.moduleCopyPtr.ToInt64():x}");
- Buffer.MemoryCopy(Module.BaseAddress.ToPointer(), this.moduleCopyPtr.ToPointer(), Module.ModuleMemorySize,
- Module.ModuleMemorySize);
-
+ Buffer.MemoryCopy(Module.BaseAddress.ToPointer(), this.moduleCopyPtr.ToPointer(), Module.ModuleMemorySize, Module.ModuleMemorySize);
this.moduleCopyOffset = this.moduleCopyPtr.ToInt64() - Module.BaseAddress.ToInt64();
-
Log.Verbose("copy OK!");
}
@@ -143,6 +143,11 @@ namespace Dalamud.Game {
Marshal.FreeHGlobal(this.moduleCopyPtr);
}
+ public IntPtr ResolveRelativeAddress(IntPtr nextInstAddr, int relOffset) {
+ if (Is32BitProcess) throw new NotSupportedException("32 bit is not supported.");
+ return nextInstAddr + relOffset;
+ }
+
///
/// Scan for a byte signature in the .text section.
///
@@ -150,28 +155,23 @@ namespace Dalamud.Game {
/// The real offset of the found signature.
public IntPtr ScanText(string signature) {
var mBase = IsCopy ? this.moduleCopyPtr : TextSectionBase;
-
var scanRet = Scan(mBase, TextSectionSize, signature);
-
if (IsCopy)
scanRet = new IntPtr(scanRet.ToInt64() - this.moduleCopyOffset);
-
- if (Marshal.ReadByte(scanRet) == 0xE8)
+ var insnByte = Marshal.ReadByte(scanRet);
+ if (insnByte == 0xE8 || insnByte == 0xE9)
return ReadCallSig(scanRet);
-
return scanRet;
}
///
- /// Helper for ScanText to get the correct address for
- /// IDA sigs that mark the first CALL location.
+ /// Helper for ScanText to get the correct address for IDA sigs that mark the first CALL location.
///
- /// The address the CALL sig resolved to.
+ /// The address the CALL sig resolved to.
/// The real offset of the signature.
- private IntPtr ReadCallSig(IntPtr SigLocation)
- {
- int jumpOffset = Marshal.ReadInt32(IntPtr.Add(SigLocation, 1));
- return IntPtr.Add(SigLocation, 5 + jumpOffset);
+ private static IntPtr ReadCallSig(IntPtr sigLocation) {
+ var jumpOffset = Marshal.ReadInt32(IntPtr.Add(sigLocation, 1));
+ return IntPtr.Add(sigLocation, 5 + jumpOffset);
}
///
@@ -182,18 +182,16 @@ namespace Dalamud.Game {
/// The signature of the function using the data.
/// The offset from function start of the instruction using the data.
/// An IntPtr to the static memory location.
- public IntPtr GetStaticAddressFromSig(string signature, int offset = 0)
- {
- IntPtr instrAddr = ScanText(signature);
+ public IntPtr GetStaticAddressFromSig(string signature, int offset = 0) {
+ var instrAddr = ScanText(signature);
instrAddr = IntPtr.Add(instrAddr, offset);
- long bAddr = (long)Module.BaseAddress;
+ var bAddr = (long)Module.BaseAddress;
long num;
- do
- {
+ do {
instrAddr = IntPtr.Add(instrAddr, 1);
num = Marshal.ReadInt32(instrAddr) + (long)instrAddr + 4 - bAddr;
- }
- while (!(num >= DataSectionOffset && num <= DataSectionOffset + DataSectionSize));
+ } while (!(num >= DataSectionOffset && num <= DataSectionOffset + DataSectionSize));
+
return IntPtr.Add(instrAddr, Marshal.ReadInt32(instrAddr) + 4);
}
@@ -204,10 +202,8 @@ namespace Dalamud.Game {
/// The real offset of the found signature.
public IntPtr ScanData(string signature) {
var scanRet = Scan(DataSectionBase, DataSectionSize, signature);
-
if (IsCopy)
scanRet = new IntPtr(scanRet.ToInt64() - this.moduleCopyOffset);
-
return scanRet;
}
@@ -218,72 +214,75 @@ namespace Dalamud.Game {
/// The real offset of the found signature.
public IntPtr ScanModule(string signature) {
var scanRet = Scan(SearchBase, Module.ModuleMemorySize, signature);
-
if (IsCopy)
scanRet = new IntPtr(scanRet.ToInt64() - this.moduleCopyOffset);
-
return scanRet;
}
public IntPtr Scan(IntPtr baseAddress, int size, string signature) {
- var needle = SigToNeedle(signature);
-
- unsafe {
- var pCursor = (byte*) baseAddress.ToPointer();
- var pTop = (byte*) (baseAddress + size - needle.Length);
- while (pCursor < pTop) {
- if (IsMatch(pCursor, needle)) return (IntPtr) pCursor;
-
- // Advance an offset
- pCursor += 1;
- }
- }
-
- throw new KeyNotFoundException($"Can't find a signature of {signature}");
+ var (needle, mask) = ParseSignature(signature);
+ var index = IndexOf(baseAddress, size, needle, mask);
+ if (index < 0)
+ throw new KeyNotFoundException($"Can't find a signature of {signature}");
+ return baseAddress + index;
}
- public IntPtr ResolveRelativeAddress(IntPtr nextInstAddr, int relOffset) {
- if (Is32BitProcess) throw new NotSupportedException("32 bit is not supported.");
-
- return nextInstAddr + relOffset;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private unsafe bool IsMatch(byte* pCursor, byte?[] needle) {
- for (var i = 0; i < needle.Length; i++) {
- var expected = needle[i];
- if (expected == null) continue;
-
- var actual = *(pCursor + i);
- if (expected != actual) return false;
- }
-
- return true;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private byte?[] SigToNeedle(string signature) {
- // Strip all whitespaces
+ private static (byte[] needle, bool[] mask) ParseSignature(string signature) {
signature = signature.Replace(" ", "");
-
if (signature.Length % 2 != 0)
- throw new ArgumentException("Signature without whitespaces must be divisible by two.",
- nameof(signature));
-
+ throw new ArgumentException("Signature without whitespaces must be divisible by two.", nameof(signature));
var needleLength = signature.Length / 2;
- var needle = new byte?[needleLength];
-
+ var needle = new byte[needleLength];
+ var mask = new bool[needleLength];
for (var i = 0; i < needleLength; i++) {
var hexString = signature.Substring(i * 2, 2);
if (hexString == "??" || hexString == "**") {
- needle[i] = null;
+ needle[i] = 0;
+ mask[i] = true;
continue;
}
needle[i] = byte.Parse(hexString, NumberStyles.AllowHexSpecifier);
+ mask[i] = false;
}
- return needle;
+ return (needle, mask);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static unsafe int IndexOf(IntPtr bufferPtr, int bufferLength, byte[] needle, bool[] mask) {
+ if (needle.Length > bufferLength) return -1;
+ var badShift = BuildBadCharTable(needle, mask);
+ var last = needle.Length - 1;
+ var offset = 0;
+ var maxoffset = bufferLength - needle.Length;
+ var buffer = (byte*)bufferPtr;
+ while (offset <= maxoffset) {
+ int position;
+ for (position = last; needle[position] == *(buffer + position + offset) || mask[position]; position--)
+ if (position == 0)
+ return offset;
+ offset += badShift[*(buffer + offset + last)];
+ }
+
+ return -1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int[] BuildBadCharTable(byte[] needle, bool[] mask) {
+ int idx;
+ var last = needle.Length - 1;
+ var badShift = new int[256];
+ for (idx = last; idx > 0 && !mask[idx]; --idx) { }
+
+ var diff = last - idx;
+ if (diff == 0) diff = 1;
+
+ for (idx = 0; idx <= 255; ++idx)
+ badShift[idx] = diff;
+ for (idx = last - diff; idx < last; ++idx)
+ badShift[needle[idx]] = last - idx;
+ return badShift;
}
}
}