Files
MrMahoandClaude Opus 5.5 35f2d1330d Erste Version
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 17:26:23 +01:00

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);
}
}
}