mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 21:24:16 +01:00
refactor: new code style in SigScanner.cs
This commit is contained in:
parent
6b02e19181
commit
e8cd4e750a
1 changed files with 249 additions and 193 deletions
|
|
@ -4,85 +4,288 @@ using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud.Game {
|
namespace Dalamud.Game
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A SigScanner facilitates searching for memory signatures in a given ProcessModule.
|
/// A SigScanner facilitates searching for memory signatures in a given ProcessModule.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SigScanner : IDisposable {
|
public sealed class SigScanner : IDisposable
|
||||||
|
{
|
||||||
|
private IntPtr moduleCopyPtr;
|
||||||
|
private long moduleCopyOffset;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set up the SigScanner.
|
/// Initializes a new instance of the <see cref="SigScanner"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="module">The ProcessModule to be used for scanning</param>
|
/// <param name="module">The ProcessModule to be used for scanning.</param>
|
||||||
/// <param name="doCopy">Whether or not to copy the module upon initialization for search operations to use, as to not get disturbed by possible hooks.</param>
|
/// <param name="doCopy">Whether or not to copy the module upon initialization for search operations to use, as to not get disturbed by possible hooks.</param>
|
||||||
public SigScanner(ProcessModule module, bool doCopy = false) {
|
public SigScanner(ProcessModule module, bool doCopy = false)
|
||||||
Module = module;
|
{
|
||||||
Is32BitProcess = !Environment.Is64BitProcess;
|
this.Module = module;
|
||||||
IsCopy = doCopy;
|
this.Is32BitProcess = !Environment.Is64BitProcess;
|
||||||
|
this.IsCopy = doCopy;
|
||||||
|
|
||||||
// Limit the search space to .text section.
|
// Limit the search space to .text section.
|
||||||
SetupSearchSpace(module);
|
this.SetupSearchSpace(module);
|
||||||
|
|
||||||
if (IsCopy)
|
if (this.IsCopy)
|
||||||
SetupCopiedSegments();
|
this.SetupCopiedSegments();
|
||||||
Log.Verbose("Module base: {Address}", TextSectionBase);
|
|
||||||
Log.Verbose("Module size: {Size}", TextSectionSize);
|
Log.Verbose("Module base: {Address}", this.TextSectionBase);
|
||||||
|
Log.Verbose("Module size: {Size}", this.TextSectionSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the search on this module is performed on a copy.
|
/// Gets a value indicating whether or not the search on this module is performed on a copy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsCopy { get; }
|
public bool IsCopy { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the ProcessModule is 32-bit.
|
/// Gets a value indicating whether or not the ProcessModule is 32-bit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Is32BitProcess { get; }
|
public bool Is32BitProcess { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base address of the search area. When copied, this will be the address of the copy.
|
/// Gets the base address of the search area. When copied, this will be the address of the copy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IntPtr SearchBase => IsCopy ? this.moduleCopyPtr : Module.BaseAddress;
|
public IntPtr SearchBase => this.IsCopy ? this.moduleCopyPtr : this.Module.BaseAddress;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base address of the .text section search area.
|
/// Gets the base address of the .text section search area.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IntPtr TextSectionBase => new IntPtr(SearchBase.ToInt64() + TextSectionOffset);
|
public IntPtr TextSectionBase => new IntPtr(this.SearchBase.ToInt64() + this.TextSectionOffset);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The offset of the .text section from the base of the module.
|
/// Gets the offset of the .text section from the base of the module.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long TextSectionOffset { get; private set; }
|
public long TextSectionOffset { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The size of the text section.
|
/// Gets the size of the text section.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int TextSectionSize { get; private set; }
|
public int TextSectionSize { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The base address of the .data section search area.
|
/// Gets the base address of the .data section search area.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IntPtr DataSectionBase => new IntPtr(SearchBase.ToInt64() + DataSectionOffset);
|
public IntPtr DataSectionBase => new IntPtr(this.SearchBase.ToInt64() + this.DataSectionOffset);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The offset of the .data section from the base of the module.
|
/// Gets the offset of the .data section from the base of the module.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long DataSectionOffset { get; private set; }
|
public long DataSectionOffset { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The size of the .data section.
|
/// Gets the size of the .data section.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int DataSectionSize { get; private set; }
|
public int DataSectionSize { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ProcessModule on which the search is performed.
|
/// Gets the ProcessModule on which the search is performed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ProcessModule Module { get; }
|
public ProcessModule Module { get; }
|
||||||
|
|
||||||
private IntPtr TextSectionTop => TextSectionBase + TextSectionSize;
|
private IntPtr TextSectionTop => this.TextSectionBase + this.TextSectionSize;
|
||||||
|
|
||||||
private void SetupSearchSpace(ProcessModule module) {
|
/// <summary>
|
||||||
|
/// Scan memory for a signature.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseAddress">The base address to scan from.</param>
|
||||||
|
/// <param name="size">The amount of bytes to scan.</param>
|
||||||
|
/// <param name="signature">The signature to search for.</param>
|
||||||
|
/// <returns>The found offset.</returns>
|
||||||
|
public static IntPtr Scan(IntPtr baseAddress, int size, string 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scan for a .data address using a .text function.
|
||||||
|
/// This is intended to be used with IDA sigs.
|
||||||
|
/// Place your cursor on the line calling a static address, and create and IDA sig.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="signature">The signature of the function using the data.</param>
|
||||||
|
/// <param name="offset">The offset from function start of the instruction using the data.</param>
|
||||||
|
/// <returns>An IntPtr to the static memory location.</returns>
|
||||||
|
public IntPtr GetStaticAddressFromSig(string signature, int offset = 0)
|
||||||
|
{
|
||||||
|
var instrAddr = this.ScanText(signature);
|
||||||
|
instrAddr = IntPtr.Add(instrAddr, offset);
|
||||||
|
var bAddr = (long)this.Module.BaseAddress;
|
||||||
|
long num;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
instrAddr = IntPtr.Add(instrAddr, 1);
|
||||||
|
num = Marshal.ReadInt32(instrAddr) + (long)instrAddr + 4 - bAddr;
|
||||||
|
}
|
||||||
|
while (!(num >= this.DataSectionOffset && num <= this.DataSectionOffset + this.DataSectionSize));
|
||||||
|
|
||||||
|
return IntPtr.Add(instrAddr, Marshal.ReadInt32(instrAddr) + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scan for a byte signature in the .data section.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="signature">The signature.</param>
|
||||||
|
/// <returns>The real offset of the found signature.</returns>
|
||||||
|
public IntPtr ScanData(string signature)
|
||||||
|
{
|
||||||
|
var scanRet = Scan(this.DataSectionBase, this.DataSectionSize, signature);
|
||||||
|
|
||||||
|
if (this.IsCopy)
|
||||||
|
scanRet = new IntPtr(scanRet.ToInt64() - this.moduleCopyOffset);
|
||||||
|
|
||||||
|
return scanRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scan for a byte signature in the whole module search area.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="signature">The signature.</param>
|
||||||
|
/// <returns>The real offset of the found signature.</returns>
|
||||||
|
public IntPtr ScanModule(string signature)
|
||||||
|
{
|
||||||
|
var scanRet = Scan(this.SearchBase, this.Module.ModuleMemorySize, signature);
|
||||||
|
|
||||||
|
if (this.IsCopy)
|
||||||
|
scanRet = new IntPtr(scanRet.ToInt64() - this.moduleCopyOffset);
|
||||||
|
|
||||||
|
return scanRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolve a RVA address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nextInstAddr">The address of the next instruction.</param>
|
||||||
|
/// <param name="relOffset">The relative offset.</param>
|
||||||
|
/// <returns>The calculated offset.</returns>
|
||||||
|
public IntPtr ResolveRelativeAddress(IntPtr nextInstAddr, int relOffset)
|
||||||
|
{
|
||||||
|
if (this.Is32BitProcess) throw new NotSupportedException("32 bit is not supported.");
|
||||||
|
return nextInstAddr + relOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scan for a byte signature in the .text section.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="signature">The signature.</param>
|
||||||
|
/// <returns>The real offset of the found signature.</returns>
|
||||||
|
public IntPtr ScanText(string signature)
|
||||||
|
{
|
||||||
|
var mBase = this.IsCopy ? this.moduleCopyPtr : this.TextSectionBase;
|
||||||
|
|
||||||
|
var scanRet = Scan(mBase, this.TextSectionSize, signature);
|
||||||
|
|
||||||
|
if (this.IsCopy)
|
||||||
|
scanRet = new IntPtr(scanRet.ToInt64() - this.moduleCopyOffset);
|
||||||
|
|
||||||
|
var insnByte = Marshal.ReadByte(scanRet);
|
||||||
|
|
||||||
|
if (insnByte == 0xE8 || insnByte == 0xE9)
|
||||||
|
return ReadCallSig(scanRet);
|
||||||
|
|
||||||
|
return scanRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Free the memory of the copied module search area on object disposal, if applicable.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(this.moduleCopyPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper for ScanText to get the correct address for IDA sigs that mark the first CALL location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sigLocation">The address the CALL sig resolved to.</param>
|
||||||
|
/// <returns>The real offset of the signature.</returns>
|
||||||
|
private static IntPtr ReadCallSig(IntPtr sigLocation)
|
||||||
|
{
|
||||||
|
var jumpOffset = Marshal.ReadInt32(IntPtr.Add(sigLocation, 1));
|
||||||
|
return IntPtr.Add(sigLocation, 5 + jumpOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (byte[] needle, bool[] mask) ParseSignature(string signature)
|
||||||
|
{
|
||||||
|
signature = signature.Replace(" ", string.Empty);
|
||||||
|
if (signature.Length % 2 != 0)
|
||||||
|
throw new ArgumentException("Signature without whitespaces must be divisible by two.", nameof(signature));
|
||||||
|
var needleLength = signature.Length / 2;
|
||||||
|
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] = 0;
|
||||||
|
mask[i] = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
needle[i] = byte.Parse(hexString, NumberStyles.AllowHexSpecifier);
|
||||||
|
mask[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupSearchSpace(ProcessModule module)
|
||||||
|
{
|
||||||
var baseAddress = module.BaseAddress;
|
var baseAddress = module.BaseAddress;
|
||||||
|
|
||||||
// We don't want to read all of IMAGE_DOS_HEADER or IMAGE_NT_HEADER stuff so we cheat here.
|
// We don't want to read all of IMAGE_DOS_HEADER or IMAGE_NT_HEADER stuff so we cheat here.
|
||||||
|
|
@ -97,25 +300,27 @@ namespace Dalamud.Game {
|
||||||
var optionalHeader = fileHeader + 20;
|
var optionalHeader = fileHeader + 20;
|
||||||
|
|
||||||
IntPtr sectionHeader;
|
IntPtr sectionHeader;
|
||||||
if (Is32BitProcess) // IMAGE_OPTIONAL_HEADER32
|
if (this.Is32BitProcess) // IMAGE_OPTIONAL_HEADER32
|
||||||
sectionHeader = optionalHeader + 224;
|
sectionHeader = optionalHeader + 224;
|
||||||
else // IMAGE_OPTIONAL_HEADER64
|
else // IMAGE_OPTIONAL_HEADER64
|
||||||
sectionHeader = optionalHeader + 240;
|
sectionHeader = optionalHeader + 240;
|
||||||
|
|
||||||
// IMAGE_SECTION_HEADER
|
// IMAGE_SECTION_HEADER
|
||||||
var sectionCursor = sectionHeader;
|
var sectionCursor = sectionHeader;
|
||||||
for (var i = 0; i < numSections; i++) {
|
for (var i = 0; i < numSections; i++)
|
||||||
|
{
|
||||||
var sectionName = Marshal.ReadInt64(sectionCursor);
|
var sectionName = Marshal.ReadInt64(sectionCursor);
|
||||||
|
|
||||||
// .text
|
// .text
|
||||||
switch (sectionName) {
|
switch (sectionName)
|
||||||
|
{
|
||||||
case 0x747865742E: // .text
|
case 0x747865742E: // .text
|
||||||
TextSectionOffset = Marshal.ReadInt32(sectionCursor, 12);
|
this.TextSectionOffset = Marshal.ReadInt32(sectionCursor, 12);
|
||||||
TextSectionSize = Marshal.ReadInt32(sectionCursor, 8);
|
this.TextSectionSize = Marshal.ReadInt32(sectionCursor, 8);
|
||||||
break;
|
break;
|
||||||
case 0x617461642E: // .data
|
case 0x617461642E: // .data
|
||||||
DataSectionOffset = Marshal.ReadInt32(sectionCursor, 12);
|
this.DataSectionOffset = Marshal.ReadInt32(sectionCursor, 12);
|
||||||
DataSectionSize = Marshal.ReadInt32(sectionCursor, 8);
|
this.DataSectionSize = Marshal.ReadInt32(sectionCursor, 8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,166 +328,17 @@ namespace Dalamud.Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr moduleCopyPtr;
|
private unsafe void SetupCopiedSegments()
|
||||||
private long moduleCopyOffset;
|
{
|
||||||
|
|
||||||
private unsafe void SetupCopiedSegments() {
|
|
||||||
Log.Verbose("module copy START");
|
|
||||||
// .text
|
// .text
|
||||||
this.moduleCopyPtr = Marshal.AllocHGlobal(Module.ModuleMemorySize);
|
this.moduleCopyPtr = Marshal.AllocHGlobal(this.Module.ModuleMemorySize);
|
||||||
Log.Verbose($"Alloc: {this.moduleCopyPtr.ToInt64():x}");
|
Buffer.MemoryCopy(
|
||||||
Buffer.MemoryCopy(Module.BaseAddress.ToPointer(), this.moduleCopyPtr.ToPointer(), Module.ModuleMemorySize, Module.ModuleMemorySize);
|
this.Module.BaseAddress.ToPointer(),
|
||||||
this.moduleCopyOffset = this.moduleCopyPtr.ToInt64() - Module.BaseAddress.ToInt64();
|
this.moduleCopyPtr.ToPointer(),
|
||||||
Log.Verbose("copy OK!");
|
this.Module.ModuleMemorySize,
|
||||||
}
|
this.Module.ModuleMemorySize);
|
||||||
|
|
||||||
/// <summary>
|
this.moduleCopyOffset = this.moduleCopyPtr.ToInt64() - this.Module.BaseAddress.ToInt64();
|
||||||
/// Free the memory of the copied module search area on object disposal, if applicable.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose() {
|
|
||||||
Marshal.FreeHGlobal(this.moduleCopyPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr ResolveRelativeAddress(IntPtr nextInstAddr, int relOffset) {
|
|
||||||
if (Is32BitProcess) throw new NotSupportedException("32 bit is not supported.");
|
|
||||||
return nextInstAddr + relOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Scan for a byte signature in the .text section.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="signature">The signature.</param>
|
|
||||||
/// <returns>The real offset of the found signature.</returns>
|
|
||||||
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);
|
|
||||||
var insnByte = Marshal.ReadByte(scanRet);
|
|
||||||
if (insnByte == 0xE8 || insnByte == 0xE9)
|
|
||||||
return ReadCallSig(scanRet);
|
|
||||||
return scanRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper for ScanText to get the correct address for IDA sigs that mark the first CALL location.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sigLocation">The address the CALL sig resolved to.</param>
|
|
||||||
/// <returns>The real offset of the signature.</returns>
|
|
||||||
private static IntPtr ReadCallSig(IntPtr sigLocation) {
|
|
||||||
var jumpOffset = Marshal.ReadInt32(IntPtr.Add(sigLocation, 1));
|
|
||||||
return IntPtr.Add(sigLocation, 5 + jumpOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Scan for a .data address using a .text function.
|
|
||||||
/// This is intended to be used with IDA sigs.
|
|
||||||
/// Place your cursor on the line calling a static address, and create and IDA sig.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="signature">The signature of the function using the data.</param>
|
|
||||||
/// <param name="offset">The offset from function start of the instruction using the data.</param>
|
|
||||||
/// <returns>An IntPtr to the static memory location.</returns>
|
|
||||||
public IntPtr GetStaticAddressFromSig(string signature, int offset = 0) {
|
|
||||||
var instrAddr = ScanText(signature);
|
|
||||||
instrAddr = IntPtr.Add(instrAddr, offset);
|
|
||||||
var bAddr = (long)Module.BaseAddress;
|
|
||||||
long num;
|
|
||||||
do {
|
|
||||||
instrAddr = IntPtr.Add(instrAddr, 1);
|
|
||||||
num = Marshal.ReadInt32(instrAddr) + (long)instrAddr + 4 - bAddr;
|
|
||||||
} while (!(num >= DataSectionOffset && num <= DataSectionOffset + DataSectionSize));
|
|
||||||
|
|
||||||
return IntPtr.Add(instrAddr, Marshal.ReadInt32(instrAddr) + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Scan for a byte signature in the .data section.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="signature">The signature.</param>
|
|
||||||
/// <returns>The real offset of the found signature.</returns>
|
|
||||||
public IntPtr ScanData(string signature) {
|
|
||||||
var scanRet = Scan(DataSectionBase, DataSectionSize, signature);
|
|
||||||
if (IsCopy)
|
|
||||||
scanRet = new IntPtr(scanRet.ToInt64() - this.moduleCopyOffset);
|
|
||||||
return scanRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Scan for a byte signature in the whole module search area.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="signature">The signature.</param>
|
|
||||||
/// <returns>The real offset of the found signature.</returns>
|
|
||||||
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, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
var needleLength = signature.Length / 2;
|
|
||||||
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] = 0;
|
|
||||||
mask[i] = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
needle[i] = byte.Parse(hexString, NumberStyles.AllowHexSpecifier);
|
|
||||||
mask[i] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue