89 lines
3.3 KiB
C#
89 lines
3.3 KiB
C#
using System.Runtime.InteropServices;
|
|
|
|
namespace BSO
|
|
{
|
|
/// <summary>
|
|
/// Speicher des Spiels lesen. Vorher immer mit VirtualQuery pruefen: eine Zugriffsverletzung laesst sich in .NET nicht
|
|
/// abfangen und beendet das Spiel. Die Bereiche werden pro Bild zwischengespeichert (NewFrame leert den Cache).
|
|
/// </summary>
|
|
public static unsafe class Mem
|
|
{
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct MemoryBasicInformation
|
|
{
|
|
public nint BaseAddress, AllocationBase;
|
|
public uint AllocationProtect;
|
|
public ushort PartitionId;
|
|
public nint RegionSize;
|
|
public uint State, Protect, Type;
|
|
}
|
|
|
|
[DllImport("kernel32.dll")]
|
|
static extern nint VirtualQuery(nint address, out MemoryBasicInformation info, nint length);
|
|
|
|
const uint MemCommit = 0x1000, MemImage = 0x1000000;
|
|
const uint Readable = 0x02 | 0x04 | 0x08 | 0x20 | 0x40 | 0x80; // R, RW, WC, XR, XRW, XWC
|
|
const uint Guard = 0x100;
|
|
|
|
struct Region { public nint Start, End; public bool Readable, Image; }
|
|
static readonly Region[] cache = new Region[16];
|
|
static int cacheCount, cacheNext;
|
|
|
|
public static void NewFrame() { cacheCount = 0; cacheNext = 0; }
|
|
|
|
static bool Query(nint address, out Region region)
|
|
{
|
|
for (int i = 0; i < cacheCount; i++)
|
|
if (address >= cache[i].Start && address < cache[i].End) { region = cache[i]; return true; }
|
|
|
|
region = default;
|
|
if (address < 0x10000) return false;
|
|
if (VirtualQuery(address, out MemoryBasicInformation info, sizeof(MemoryBasicInformation)) == 0) return false;
|
|
region.Start = info.BaseAddress;
|
|
region.End = info.BaseAddress + info.RegionSize;
|
|
region.Readable = info.State == MemCommit && (info.Protect & Readable) != 0 && (info.Protect & Guard) == 0;
|
|
region.Image = info.Type == MemImage;
|
|
cache[cacheNext] = region;
|
|
cacheNext = (cacheNext + 1) % cache.Length;
|
|
if (cacheCount < cache.Length) cacheCount++;
|
|
return true;
|
|
}
|
|
|
|
public static bool IsReadable(nint address, int size)
|
|
{
|
|
nint end = address + size;
|
|
while (address < end)
|
|
{
|
|
if (!Query(address, out Region r) || !r.Readable) return false;
|
|
address = r.End;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Liegt die Adresse in einer geladenen DLL/EXE (z.B. vtables, Typinfos)?</summary>
|
|
public static bool IsImage(nint address) => Query(address, out Region r) && r.Readable && r.Image;
|
|
|
|
public static bool TryReadPtr(nint address, out nint value)
|
|
{
|
|
value = 0;
|
|
if (!IsReadable(address, 8)) return false;
|
|
value = *(nint*)address;
|
|
return true;
|
|
}
|
|
|
|
public static string ReadAscii(nint address, int max)
|
|
{
|
|
var chars = new char[max];
|
|
int n = 0;
|
|
for (; n < max; n++)
|
|
{
|
|
if (!IsReadable(address + n, 1)) break;
|
|
byte b = *(byte*)(address + n);
|
|
if (b == 0) break;
|
|
chars[n] = (char)b;
|
|
}
|
|
return new string(chars, 0, n);
|
|
}
|
|
}
|
|
}
|