mirror of
https://github.com/luanslimadev/Wand-Enhancer.git
synced 2026-08-28 23:01:14 +00:00
178 lines
5.6 KiB
C#
178 lines
5.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using WeModPatcher.Models;
|
|
using WeModPatcher.Utils.Win32;
|
|
|
|
namespace WeModPatcher.Utils
|
|
{
|
|
public class MemoryUtils
|
|
{
|
|
public static int ScanMemoryBlock(byte[] buffer, int bufferLength, byte[] pattern, byte[] mask)
|
|
{
|
|
var patternLength = pattern.Length;
|
|
if (bufferLength < patternLength)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Make a length of length outside the first cycle for optimization
|
|
var searchEnd = bufferLength - patternLength;
|
|
|
|
// first pass - use the first non-empty byte of the mask for a quick check
|
|
var firstValidIndex = -1;
|
|
for (var i = 0; i < patternLength; i++)
|
|
{
|
|
if (mask[i] == 1)
|
|
{
|
|
firstValidIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (firstValidIndex == -1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
var firstByte = pattern[firstValidIndex];
|
|
|
|
for (var i = 0; i <= searchEnd; i++)
|
|
{
|
|
// quick check by the first byte before full comparison
|
|
if (buffer[i + firstValidIndex] != firstByte)
|
|
continue;
|
|
|
|
var found = true;
|
|
|
|
// check only those positions where mask = 1
|
|
for (var j = 0; j < patternLength; j++)
|
|
{
|
|
if (mask[j] == 0 || buffer[i + j] == pattern[j])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
found = false;
|
|
break;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public static void ParseSignature(string signatureStr, out byte[] pattern, out byte[] mask)
|
|
{
|
|
var parts = signatureStr.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
|
var length = parts.Length;
|
|
|
|
pattern = new byte[length];
|
|
mask = new byte[length];
|
|
|
|
for (var i = 0; i < length; i++)
|
|
{
|
|
if (parts[i] == "??" || parts[i] == "?")
|
|
{
|
|
pattern[i] = 0;
|
|
// wildcard byte
|
|
mask[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
pattern[i] = Convert.ToByte(parts[i], 16);
|
|
mask[i] = 1;
|
|
}
|
|
}
|
|
|
|
public static bool SafeWriteVirtualMemory(IntPtr hProcess, IntPtr address, byte[] bytes)
|
|
{
|
|
if (!Imports.VirtualProtectEx(hProcess, address, (IntPtr)1, 0x40, out uint oldProtect))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool result = Imports.WriteProcessMemory(hProcess, address, bytes, bytes.Length, out _);
|
|
|
|
// Restore the previous access rights
|
|
Imports.VirtualProtectEx(hProcess, address, (IntPtr)1, oldProtect, out _);
|
|
return result;
|
|
}
|
|
|
|
public static IntPtr ScanVirtualMemory(IntPtr hProcess, IntPtr startAddress, int searchSize, byte[] signature, byte[] mask)
|
|
{
|
|
const int BUFFER_SIZE = 4096;
|
|
byte[] buffer = new byte[BUFFER_SIZE];
|
|
|
|
// We can't copy all the crap of the process into a byte array at once. Don't try this
|
|
for (long currentAddress = startAddress.ToInt64();
|
|
currentAddress < startAddress.ToInt64() + searchSize;
|
|
currentAddress += BUFFER_SIZE - signature.Length)
|
|
{
|
|
if (!Imports.ReadProcessMemory(hProcess, new IntPtr(currentAddress), buffer, BUFFER_SIZE, out int bytesRead) || bytesRead == 0)
|
|
{
|
|
// Read error or end of memory, throw mb?
|
|
continue;
|
|
}
|
|
|
|
var i = ScanMemoryBlock(buffer, bytesRead, signature, mask);
|
|
if (i != -1)
|
|
{
|
|
return new IntPtr(currentAddress + i);
|
|
}
|
|
}
|
|
|
|
return IntPtr.Zero;
|
|
}
|
|
|
|
public static int PatchFile(string filePath, Signature signature, byte[] patchBytes)
|
|
{
|
|
const int bufferSize = 8192;
|
|
var buffer = new byte[bufferSize + signature.Length - 1];
|
|
|
|
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
|
|
{
|
|
int filePosition = 0;
|
|
while (true)
|
|
{
|
|
int bytesRead = fileStream.Read(buffer, 0, bufferSize);
|
|
if (bytesRead == 0) break;
|
|
|
|
int matchIndex = ScanMemoryBlock(buffer, bytesRead, signature, signature.Mask);
|
|
if (matchIndex != -1)
|
|
{
|
|
int functionStartPosition = filePosition + matchIndex;
|
|
|
|
var checkBuffer = new byte[patchBytes.Length];
|
|
fileStream.Seek(functionStartPosition + signature.Offset, SeekOrigin.Begin);
|
|
fileStream.Read(checkBuffer, 0, patchBytes.Length);
|
|
|
|
if (checkBuffer.SequenceEqual(patchBytes))
|
|
{
|
|
return 0; // Memory already patched
|
|
}
|
|
|
|
// Go to patch position
|
|
fileStream.Seek(functionStartPosition + signature.Offset, SeekOrigin.Begin);
|
|
fileStream.Write(patchBytes, 0, patchBytes.Length);
|
|
|
|
return functionStartPosition; // Return the address of the function start by signature
|
|
}
|
|
|
|
filePosition += bytesRead;
|
|
Array.Copy(buffer, bufferSize, buffer, 0, signature.Length - 1);
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
} |