Merge pull request #243 from pohky/master

This commit is contained in:
goaaats 2021-01-14 22:01:47 +01:00 committed by GitHub
commit dc8715a485
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

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