diff --git a/LinuxStructuredOsuMemoryProviderTester/LinuxStructuredOsuMemoryProviderTester.csproj b/LinuxStructuredOsuMemoryProviderTester/LinuxStructuredOsuMemoryProviderTester.csproj
new file mode 100644
index 0000000..e60f34b
--- /dev/null
+++ b/LinuxStructuredOsuMemoryProviderTester/LinuxStructuredOsuMemoryProviderTester.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LinuxStructuredOsuMemoryProviderTester/Program.cs b/LinuxStructuredOsuMemoryProviderTester/Program.cs
new file mode 100644
index 0000000..d35f96f
--- /dev/null
+++ b/LinuxStructuredOsuMemoryProviderTester/Program.cs
@@ -0,0 +1,115 @@
+using CommandLine;
+using OsuMemoryDataProvider;
+using System.Diagnostics;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+var parsedArgs = Parser.Default.ParseArguments(args).MapResult((o) => o, o => null);
+if (parsedArgs == null)
+ return;
+
+Console.WriteLine(JsonSerializer.Serialize(parsedArgs));
+
+var reader = StructuredOsuMemoryReader.Instance;
+reader.InvalidRead += readerOnInvalidRead;
+var baseAddresses = StructuredOsuMemoryReader.Instance.OsuMemoryAddresses;
+
+JsonSerializerOptions jsonSerializerOptions = new()
+{
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ WriteIndented = parsedArgs.Indented,
+};
+var stopwatch = Stopwatch.StartNew();
+double readTimeMs, readTimeMsMin, readTimeMsMax;
+double _memoryReadTimeMin = double.PositiveInfinity;
+double _memoryReadTimeMax = double.NegativeInfinity;
+while (true)
+{
+ while (!reader.CanRead)
+ {
+ Console.WriteLine("Waiting for osu! process...");
+ await Task.Delay(200);
+ };
+
+ stopwatch = Stopwatch.StartNew();
+
+ reader.TryRead(baseAddresses.Beatmap);
+ reader.TryRead(baseAddresses.Skin);
+ reader.TryRead(baseAddresses.GeneralData);
+ reader.TryRead(baseAddresses.BanchoUser);
+
+ if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.SongSelect)
+ reader.TryRead(baseAddresses.SongSelectionScores);
+ else
+ baseAddresses.SongSelectionScores.Scores.Clear();
+
+ if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.ResultsScreen)
+ reader.TryRead(baseAddresses.ResultsScreen);
+
+ if (baseAddresses.GeneralData.OsuStatus == OsuMemoryStatus.Playing)
+ {
+ reader.TryRead(baseAddresses.Player);
+ reader.TryRead(baseAddresses.LeaderBoard);
+ reader.TryRead(baseAddresses.KeyOverlay);
+ }
+ else
+ {
+ baseAddresses.LeaderBoard.Players.Clear();
+ }
+
+ var hitErrors = baseAddresses.Player?.HitErrors;
+ if (hitErrors != null)
+ {
+ var hitErrorsCount = hitErrors.Count;
+ hitErrors.Clear();
+ hitErrors.Add(hitErrorsCount);
+ }
+
+ stopwatch.Stop();
+ readTimeMs = stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerMillisecond;
+ if (readTimeMs < _memoryReadTimeMin) _memoryReadTimeMin = readTimeMs;
+ if (readTimeMs > _memoryReadTimeMax) _memoryReadTimeMax = readTimeMs;
+
+ readTimeMsMin = _memoryReadTimeMin;
+ readTimeMsMax = _memoryReadTimeMax;
+
+ if (parsedArgs.Output)
+ Console.WriteLine(JsonSerializer.Serialize(baseAddresses, jsonSerializerOptions));
+
+ Console.WriteLine($"ReadTimeMS: {readTimeMs}{Environment.NewLine}" +
+ $"Min ReadTimeMS: {readTimeMsMin}{Environment.NewLine}" +
+ $"Max ReadTimeMS: {readTimeMsMax}{Environment.NewLine}" +
+ $"Press any key to reset min/max values{Environment.NewLine}");
+ if (parsedArgs.ExitAfter)
+ break;
+
+ if (Console.KeyAvailable)
+ {
+ Console.ReadKey(true);
+ while (Console.KeyAvailable)
+ Console.ReadKey(true);
+
+ _memoryReadTimeMin = double.PositiveInfinity;
+ _memoryReadTimeMax = double.NegativeInfinity;
+
+ }
+
+ await Task.Delay(parsedArgs.Delay);
+}
+
+void readerOnInvalidRead(object sender, (object readObject, string propPath) e)
+{
+ Console.WriteLine($"{DateTime.Now:T} Error reading {e.propPath}{Environment.NewLine}");
+}
+
+class CommandLineOptions
+{
+ [Option('o', "output", Required = false, Default = false, HelpText = "Send read memory values to stdout")]
+ public bool Output { get; set; }
+ [Option('i', "indentedOutput", Required = false, Default = false, HelpText = "Format memory values sent to stdout")]
+ public bool Indented { get; set; }
+ [Option('e', "exitAfter", Required = false, Default = false, HelpText = "exit after reading memory values once")]
+ public bool ExitAfter { get; set; }
+ [Option('d', "delay", Required = false, Default = 200, HelpText = "delay between reads when continuous is enabled")]
+ public int Delay { get; set; }
+}
diff --git a/OsuMemoryDataProvider/OsuMemoryDataProvider.csproj b/OsuMemoryDataProvider/OsuMemoryDataProvider.csproj
index 75aa2ba..9eb9383 100644
--- a/OsuMemoryDataProvider/OsuMemoryDataProvider.csproj
+++ b/OsuMemoryDataProvider/OsuMemoryDataProvider.csproj
@@ -1,6 +1,6 @@
- netcoreapp3.1;net472;net471;net5.0
+ net5.0;net6.0
x86;x64;AnyCPU
Library
false
diff --git a/OsuMemoryDataProviderTester/OsuMemoryDataProviderTester.csproj b/OsuMemoryDataProviderTester/OsuMemoryDataProviderTester.csproj
index ab850a7..2afc3dc 100644
--- a/OsuMemoryDataProviderTester/OsuMemoryDataProviderTester.csproj
+++ b/OsuMemoryDataProviderTester/OsuMemoryDataProviderTester.csproj
@@ -1,6 +1,6 @@
- netcoreapp3.1;net472;net471;net5.0-windows
+ net5.0-windows
x86;x64;AnyCPU
WinExe
false
diff --git a/ProcessMemoryDataFinder.sln b/ProcessMemoryDataFinder.sln
index 9a61bb9..9633054 100644
--- a/ProcessMemoryDataFinder.sln
+++ b/ProcessMemoryDataFinder.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29009.5
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33402.96
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessMemoryDataFinder", "ProcessMemoryDataFinder\ProcessMemoryDataFinder.csproj", "{9960D641-300A-432C-AA04-C351A99B9ADB}"
EndProject
@@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "osu!", "osu!", "{FD3B44E6-6
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StructuredOsuMemoryProviderTester", "StructuredOsuMemoryProviderTester\StructuredOsuMemoryProviderTester.csproj", "{2965C7BF-B5DD-4345-9DFD-4E6B7D93DC0A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinuxStructuredOsuMemoryProviderTester", "LinuxStructuredOsuMemoryProviderTester\LinuxStructuredOsuMemoryProviderTester.csproj", "{55FA063E-A4AF-4CB1-9F04-E6D553113289}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,18 @@ Global
{2965C7BF-B5DD-4345-9DFD-4E6B7D93DC0A}.Release|x64.Build.0 = Release|x64
{2965C7BF-B5DD-4345-9DFD-4E6B7D93DC0A}.Release|x86.ActiveCfg = Release|x86
{2965C7BF-B5DD-4345-9DFD-4E6B7D93DC0A}.Release|x86.Build.0 = Release|x86
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Debug|x64.Build.0 = Debug|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Debug|x86.Build.0 = Debug|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Release|x64.ActiveCfg = Release|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Release|x64.Build.0 = Release|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Release|x86.ActiveCfg = Release|Any CPU
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -76,6 +90,7 @@ Global
{D117800F-072D-4AE4-9679-3E2A129A1A3C} = {FD3B44E6-66B3-4C58-B590-FB11B9CF6FF7}
{2450525D-627D-4ABA-ACF0-FA1AA76CE4A0} = {FD3B44E6-66B3-4C58-B590-FB11B9CF6FF7}
{2965C7BF-B5DD-4345-9DFD-4E6B7D93DC0A} = {FD3B44E6-66B3-4C58-B590-FB11B9CF6FF7}
+ {55FA063E-A4AF-4CB1-9F04-E6D553113289} = {FD3B44E6-66B3-4C58-B590-FB11B9CF6FF7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2FBAFFCF-2E42-43DD-B5D2-EFB906701CBF}
diff --git a/ProcessMemoryDataFinder/API/Internals.cs b/ProcessMemoryDataFinder/API/Internals.cs
deleted file mode 100644
index 3cc36dc..0000000
--- a/ProcessMemoryDataFinder/API/Internals.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-namespace ProcessMemoryDataFinder.API
-{
- internal class X64MemoryProcessAddressFinder : MemoryProcessAddressFinder
- {
- protected override IntPtr SumIntPtrs(IntPtr first, IntPtr second) =>
- new IntPtr(first.ToInt64() + second.ToInt64());
- }
- internal class X86ProcessX86RuntimeAddressFinder : MemoryProcessAddressFinder
- {
- protected override IntPtr SumIntPtrs(IntPtr first, IntPtr second)
- => IntPtr.Add(first, second.ToInt32());
- }
-
- internal class X86ProcessX64RuntimeAddressFinder : MemoryProcessAddressFinder
- {
- protected override IntPtr SumIntPtrs(IntPtr first, IntPtr second)
- => new IntPtr(first.ToInt64() + second.ToInt64());
- }
-
- internal abstract class MemoryProcessAddressFinder
- {
- public int IntPtrSize { get; set; } = IntPtr.Size;
-
- [StructLayout(LayoutKind.Sequential)]
- // ReSharper disable once InconsistentNaming
- public struct MEMORY_BASIC_INFORMATION
- {
- public IntPtr BaseAddress;
- public IntPtr AllocationBase;
- public uint AllocationProtect;
- public IntPtr RegionSize;
- public uint State;
- public uint Protect;
- public uint Type;
- }
-
- [DllImport("kernel32.dll", SetLastError = true)]
- protected static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
-
- ///
- /// Finds process fragmented memory information
- ///
- /// process handle
- public List MemInfo(IntPtr pHandle)
- {
- IntPtr addy = new IntPtr();
- var result = new List();
-
- while (true)
- {
- MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION();
- int memDump = VirtualQueryEx(pHandle, addy, out memInfo, Marshal.SizeOf(memInfo));
- if (memDump == 0) break;
- if ((memInfo.State & 0x1000) != 0 && (memInfo.Protect & 0x100) == 0)
- result.Add(memInfo);
-
- addy = SumIntPtrs(memInfo.BaseAddress, memInfo.RegionSize);
- }
-
- return result;
- }
-
- protected abstract IntPtr SumIntPtrs(IntPtr first, IntPtr second);
- }
-}
\ No newline at end of file
diff --git a/ProcessMemoryDataFinder/API/Memory/LinuxMemoryReader.cs b/ProcessMemoryDataFinder/API/Memory/LinuxMemoryReader.cs
new file mode 100644
index 0000000..99d9048
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/LinuxMemoryReader.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace ProcessMemoryDataFinder.API.Memory
+{
+ internal class LinuxMemoryReader : MemoryReader
+ {
+ public LinuxMemoryReader(int intPtrSize) : base(intPtrSize)
+ {
+ }
+
+ public override List ReadProcessMaps(IntPtr processHandle, int processPID)
+ {
+ var result = new List();
+ var rawProcessMaps = File.ReadAllLines($"/proc/{processPID}/maps");
+ for (int i = 0; i < rawProcessMaps.Length; i++)
+ {
+ var line = rawProcessMaps[i].AsSpan();
+ var addressStrLength = line.IndexOf('-');
+ var startAddressSpan = line[0..addressStrLength];
+ var endAddressSpan = line[(addressStrLength + 1)..(addressStrLength * 2 + 1)];
+ var flagsSpan = line[(addressStrLength * 2 + 2)..(addressStrLength * 2 + 6)];
+
+ var memInfo = new MEMORY_BASIC_INFORMATION();
+ memInfo.BaseAddress = new IntPtr(long.Parse(startAddressSpan, System.Globalization.NumberStyles.HexNumber));
+ var endRegionAddress = new IntPtr(long.Parse(endAddressSpan, System.Globalization.NumberStyles.HexNumber));
+ memInfo.RegionSize = IntPtrMath.SubstractIntPtrs(endRegionAddress, memInfo.BaseAddress);
+ //flags: rwxp
+ if (flagsSpan[1] != '-')
+ result.Add(memInfo);
+ }
+
+ return result;
+ }
+
+ public override bool ReadProcessMemory(IntPtr processHandle, int processPID, IntPtr address, uint size, byte[] targetArray, out int bytesRead)
+ => ReadProcessMemoryUnsafe(address, processPID, size, targetArray, out bytesRead);
+
+ protected static unsafe bool ReadProcessMemoryUnsafe(IntPtr address, int processId, uint size, byte[] dumpArray, out int bytesRead)
+ {
+ //TODO: create block of memory upfront and recreate if size is less than requested.
+ var dumpPointer = Marshal.AllocCoTaskMem(dumpArray.Length);
+ try
+ {
+ var localIo = new iovec
+ {
+ iov_base = dumpPointer.ToPointer(),
+ iov_len = (int)size
+ };
+ var remoteIo = new iovec
+ {
+ iov_base = address.ToPointer(),
+ iov_len = (int)size
+ };
+
+ bytesRead = process_vm_readv(processId, &localIo, 1, &remoteIo, 1, 0);
+ if (bytesRead < 0)
+ return false;
+
+ //TODO: use spans
+ Marshal.Copy(dumpPointer, dumpArray, 0, bytesRead);
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(dumpPointer);
+ }
+
+ return true;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ unsafe struct iovec
+ {
+ public void* iov_base;
+ public int iov_len;
+ }
+
+ ///
+ /// https://linux.die.net/man/2/process_vm_readv
+ ///
+ /// Process Id
+ /// return struct buffer(s) to write the read data to.
+ /// Amount of return structs provided. Unused here, always 1
+ /// struct(s) containing address to read data from.
+ /// Amount of address structs provided. Unused here, always 1
+ /// Unused, must be 0
+ /// Amount of bytes read or -1 on error
+ [DllImport("libc", SetLastError = true)]
+ private static extern unsafe int process_vm_readv(int pid,
+ iovec* local_iov,
+ ulong liovcnt,
+ iovec* remote_iov,
+ ulong riovcnt,
+ ulong flags);
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/Memory/MEMORY_BASIC_INFORMATION.cs b/ProcessMemoryDataFinder/API/Memory/MEMORY_BASIC_INFORMATION.cs
new file mode 100644
index 0000000..92b090e
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/MEMORY_BASIC_INFORMATION.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ProcessMemoryDataFinder.API.Memory
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MEMORY_BASIC_INFORMATION
+ {
+ public IntPtr BaseAddress;
+ public IntPtr AllocationBase;
+ public uint AllocationProtect;
+ public IntPtr RegionSize;
+ public uint State;
+ public uint Protect;
+ public uint Type;
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/Memory/Math/IIntPtrMath.cs b/ProcessMemoryDataFinder/API/Memory/Math/IIntPtrMath.cs
new file mode 100644
index 0000000..09dadec
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/Math/IIntPtrMath.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace ProcessMemoryDataFinder.API.Memory.Math
+{
+ public interface IIntPtrMath
+ {
+ IntPtr SumIntPtrs(IntPtr first, IntPtr second);
+ IntPtr SubstractIntPtrs(IntPtr first, IntPtr second);
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/Memory/Math/X64IntPtrMath.cs b/ProcessMemoryDataFinder/API/Memory/Math/X64IntPtrMath.cs
new file mode 100644
index 0000000..01c59d1
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/Math/X64IntPtrMath.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace ProcessMemoryDataFinder.API.Memory.Math
+{
+ ///
+ /// Both target and executing processes are x64
+ ///
+ internal class X64IntPtrMath : IIntPtrMath
+ {
+ public IntPtr SumIntPtrs(IntPtr first, IntPtr second) =>
+ new IntPtr(first.ToInt64() + second.ToInt64());
+ public IntPtr SubstractIntPtrs(IntPtr first, IntPtr second) =>
+ new IntPtr(first.ToInt64() - second.ToInt64());
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/Memory/Math/X86IntPtrMath.cs b/ProcessMemoryDataFinder/API/Memory/Math/X86IntPtrMath.cs
new file mode 100644
index 0000000..ec5d244
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/Math/X86IntPtrMath.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace ProcessMemoryDataFinder.API.Memory.Math
+{
+ ///
+ /// Both target and executing processes are x86
+ ///
+ internal class X86IntPtrMath : IIntPtrMath
+ {
+ public IntPtr SumIntPtrs(IntPtr first, IntPtr second)
+ => IntPtr.Add(first, second.ToInt32());
+ public IntPtr SubstractIntPtrs(IntPtr first, IntPtr second)
+ => IntPtr.Subtract(first, second.ToInt32());
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/Memory/Math/X86ProcessX64RuntimeIntPtrMath.cs b/ProcessMemoryDataFinder/API/Memory/Math/X86ProcessX64RuntimeIntPtrMath.cs
new file mode 100644
index 0000000..3f69004
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/Math/X86ProcessX64RuntimeIntPtrMath.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace ProcessMemoryDataFinder.API.Memory.Math
+{
+ ///
+ /// Target process is x86 while executing process is x64
+ ///
+ internal class X86ProcessX64RuntimeIntPtrMath : IIntPtrMath
+ {
+ public IntPtr SumIntPtrs(IntPtr first, IntPtr second)
+ => new IntPtr(first.ToInt64() + second.ToInt64());
+ public IntPtr SubstractIntPtrs(IntPtr first, IntPtr second)
+ => new IntPtr(first.ToInt64() - second.ToInt64());
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/Memory/MemoryReader.cs b/ProcessMemoryDataFinder/API/Memory/MemoryReader.cs
new file mode 100644
index 0000000..9321938
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/MemoryReader.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using ProcessMemoryDataFinder.API.Memory.Math;
+
+namespace ProcessMemoryDataFinder.API.Memory
+{
+ internal abstract class MemoryReader
+ {
+ public IIntPtrMath IntPtrMath { get; private set; }
+ private int _intPtrSize;
+ public int IntPtrSize
+ {
+ get => _intPtrSize;
+ set
+ {
+ _intPtrSize = value;
+ if (value == 4)
+ {
+ if (Environment.Is64BitProcess)
+ IntPtrMath = new X86ProcessX64RuntimeIntPtrMath();
+ else
+ IntPtrMath = new X86IntPtrMath();
+ }
+ else
+ IntPtrMath = new X64IntPtrMath();
+ }
+ }
+
+ public MemoryReader(int intPtrSize)
+ {
+ IntPtrSize = intPtrSize;
+ }
+
+ ///
+ /// Reads process memory on specific operating system
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// true if read call was successful
+ public abstract bool ReadProcessMemory(IntPtr processHandle, int processPID, IntPtr address, uint size, byte[] targetArray, out int bytesRead);
+ public abstract List ReadProcessMaps(IntPtr processHandle, int processPID);
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/Memory/WindowsMemoryReader.cs b/ProcessMemoryDataFinder/API/Memory/WindowsMemoryReader.cs
new file mode 100644
index 0000000..143f650
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/Memory/WindowsMemoryReader.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace ProcessMemoryDataFinder.API.Memory
+{
+ internal class WindowsMemoryReader : MemoryReader
+ {
+ public WindowsMemoryReader(int intPtrSize) : base(intPtrSize)
+ {
+ }
+
+ public override bool ReadProcessMemory(IntPtr processHandle, int processPID, IntPtr address, uint size, byte[] targetArray, out int bytesRead)
+ {
+ try
+ {
+ ReadProcessMemory(processHandle, address, targetArray, (int)size, out bytesRead);
+ return size == bytesRead;
+ }
+ catch
+ {
+ bytesRead = 0;
+ return false;
+ }
+ }
+
+ public override List ReadProcessMaps(IntPtr processHandle, int processPID)
+ {
+ var result = new List();
+ var lastRegionEndAddress = new IntPtr();
+ while (true)
+ {
+ MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION();
+ int memDump = VirtualQueryEx(processHandle, lastRegionEndAddress, out memInfo, Marshal.SizeOf(memInfo));
+ if (memDump == 0) break;
+ if ((memInfo.State & 0x1000) != 0 && (memInfo.Protect & 0x100) == 0)
+ result.Add(memInfo);
+
+ lastRegionEndAddress = IntPtrMath.SumIntPtrs(memInfo.BaseAddress, memInfo.RegionSize);
+ }
+
+ return result;
+ }
+
+ ///
+ /// ReadProcessMemory
+ ///
+ /// API import definition for ReadProcessMemory.
+ ///
+ /// Handle to the process we want to read from.
+ /// The base address to start reading from.
+ /// The return buffer to write the read data to.
+ /// The size of data we wish to read.
+ /// The number of bytes successfully read.
+ ///
+ [DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory", SetLastError = true)]
+ protected static extern bool ReadProcessMemory(
+ IntPtr hProcess,
+ IntPtr lpBaseAddress,
+ [Out] byte[] lpBuffer,
+ int dwSize,
+ out int lpNumberOfBytesRead
+ );
+
+ [DllImport("kernel32.dll", EntryPoint = "VirtualQueryEx", SetLastError = true)]
+ protected static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
+ }
+}
diff --git a/ProcessMemoryDataFinder/API/MemoryReader.cs b/ProcessMemoryDataFinder/API/MemoryReaderManager.cs
similarity index 54%
rename from ProcessMemoryDataFinder/API/MemoryReader.cs
rename to ProcessMemoryDataFinder/API/MemoryReaderManager.cs
index 0f7030f..b936801 100644
--- a/ProcessMemoryDataFinder/API/MemoryReader.cs
+++ b/ProcessMemoryDataFinder/API/MemoryReaderManager.cs
@@ -2,79 +2,60 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using ProcessMemoryDataFinder.Misc;
+using ProcessMemoryDataFinder.API.Memory;
namespace ProcessMemoryDataFinder.API
{
- public class MemoryReader : IDisposable
+ public class MemoryReaderManager : IDisposable
{
public delegate IntPtr FindPatternF(byte[] btPattern, string strMask, int nOffset, bool useMask);
public delegate byte[] ReadDataF(IntPtr adress, uint size);
- private MemoryProcessAddressFinder _internals;
-
private readonly string _processName;
private readonly string _mainWindowTitleHint;
- private readonly ProcessMemoryReader _reader = new ProcessMemoryReader();
- private readonly SigScan _sigScan = new SigScan();
- private Process _currentProcess;
+ private readonly SigScan _sigScan;
+ private readonly MemoryReader _memoryReader;
+ private SafeProcess _currentProcess;
private Task ProcessWatcher;
public int ProcessWatcherDelayMs { get; set; } = 1000;
private CancellationTokenSource cts = new CancellationTokenSource();
- private int _intPtrSize = IntPtr.Size;
public event EventHandler ProcessChanged;
- protected virtual IntPtr CurrentProcessHandle { get; set; } = IntPtr.Zero;
- public virtual Process CurrentProcess
+ public virtual SafeProcess CurrentProcess
{
get => _currentProcess;
private set
{
_currentProcess = value;
_sigScan.Process = value;
- _reader.ReadProcess = value;
ProcessChanged?.Invoke(null, EventArgs.Empty);
- _reader.OpenProcess();
- try
- {
- CurrentProcessHandle = value?.Handle ?? IntPtr.Zero;
- }
- catch (InvalidOperationException)
- {
- CurrentProcessHandle = IntPtr.Zero;
- }
}
}
public int IntPtrSize
{
- get => _intPtrSize;
+ get => _memoryReader.IntPtrSize;
set
{
- _intPtrSize = value;
- if (value == 4)
- {
- if(Environment.Is64BitProcess)
- _internals = new X86ProcessX64RuntimeAddressFinder();
- else
- _internals = new X86ProcessX86RuntimeAddressFinder();
- }
- else
- _internals = new X64MemoryProcessAddressFinder();
+ _memoryReader.IntPtrSize = value;
}
}
- public MemoryReader(string processName, string mainWindowTitleHint)
+ public MemoryReaderManager(string processName, string mainWindowTitleHint)
{
- //Initialize process address finder
- IntPtrSize = IntPtrSize;
-
_processName = processName;
_mainWindowTitleHint = mainWindowTitleHint;
ProcessWatcher = Task.Run(MonitorProcess, cts.Token);
+ if (OperatingSystem.IsWindows())
+ _memoryReader = new WindowsMemoryReader(IntPtr.Size);
+ else if (OperatingSystem.IsLinux())
+ _memoryReader = new LinuxMemoryReader(IntPtr.Size);
+ else
+ throw new NotImplementedException("Current operating system is not supported.");
+
+ _sigScan = new SigScan(_memoryReader);
}
protected async Task MonitorProcess()
@@ -84,13 +65,13 @@ protected async Task MonitorProcess()
if (cts.IsCancellationRequested)
return;
- if (CurrentProcess == null || CurrentProcess.SafeHasExited())
+ if (CurrentProcess == null || CurrentProcess.HasExited())
{
OpenProcess();
}
if (CurrentProcess != null)
{
- while (!CurrentProcess.WaitForExit(1000))
+ while (!CurrentProcess.Process.WaitForExit(1000))
{
if (cts.IsCancellationRequested)
return;
@@ -109,18 +90,18 @@ public IntPtr FindPattern(byte[] btPattern, string strMask, int nOffset, bool us
if (CurrentProcess == null)
return IntPtr.Zero;
- var pageExecuteRead = (uint)MemoryProtectionOptions.PAGE_EXECUTE_READ;
+ var pageExecuteRead = (uint)WindowsMemoryProtectionOptions.PAGE_EXECUTE_READ;
IntPtr result;
- foreach (var memoryAdress in GetMemoryAddresses())
+ foreach (var memoryAddress in _memoryReader.ReadProcessMaps(CurrentProcess.Handle, CurrentProcess.PID))
{
- if ((memoryAdress.Protect & pageExecuteRead) != 0)
+ if ((memoryAddress.Protect & pageExecuteRead) != 0)
{
continue;
}
_sigScan.ResetRegion();
- _sigScan.Address = memoryAdress.BaseAddress;
- _sigScan.Size = (int)memoryAdress.RegionSize;
+ _sigScan.Address = memoryAddress.BaseAddress;
+ _sigScan.Size = (int)memoryAddress.RegionSize;
if (useMask)
{
result = _sigScan.FindPattern(btPattern, strMask, nOffset);
@@ -132,6 +113,7 @@ public IntPtr FindPattern(byte[] btPattern, string strMask, int nOffset, bool us
if (result != IntPtr.Zero)
{
+ Console.WriteLine("found: "+ BitConverter.ToString(btPattern).Replace("-", ""));
return result;
}
}
@@ -146,13 +128,9 @@ public byte[] ReadData(IntPtr address, uint size)
return null;
}
- var bytesRead =
- _reader.ReadProcessMemory(address, size,
- out var bytesNumber);
- if (bytesNumber == size)
- {
- return bytesRead;
- }
+ var bytes = new byte[size];
+ if (_memoryReader.ReadProcessMemory(CurrentProcess.Handle, CurrentProcess.PID, address, size, bytes, out _))
+ return bytes;
return null;
}
@@ -161,16 +139,18 @@ private void OpenProcess()
{
try
{
+ Process[] p2 = Process.GetProcesses();
IEnumerable p = Process.GetProcessesByName(_processName);
+ if (!p.Any())
+ p = Process.GetProcessesByName(_processName + ".exe");
+
if (!string.IsNullOrEmpty(_mainWindowTitleHint))
- {
p = p.Where(x => x.MainWindowTitle.IndexOf(_mainWindowTitleHint, StringComparison.Ordinal) >= 0);
- }
+
var resolvedProcess = p.FirstOrDefault();
if (resolvedProcess != null || CurrentProcess != null)
- {
- CurrentProcess = resolvedProcess;
- }
+ CurrentProcess = resolvedProcess.ToSafeProcess();
+
}
catch (Win32Exception)
{
@@ -178,32 +158,6 @@ private void OpenProcess()
}
}
- private IEnumerable GetMemoryAddresses()
- {
- var memInfoList = _internals.MemInfo(CurrentProcessHandle);
-
- foreach (var memoryInfo in memInfoList)
- {
- yield return memoryInfo;
- }
- }
-
- [SuppressMessage("ReSharper", "InconsistentNaming")]
- [Flags]
- private enum MemoryProtectionOptions
- {
- PAGE_EXECUTE = 0x10,
- PAGE_EXECUTE_READ = 0x20,
- PAGE_EXECUTE_READWRITE = 0x40,
- PAGE_EXECUTE_WRITECOPY = 0x80,
- PAGE_NOACCESS = 0x01,
- PAGE_READONLY = 0x02,
- PAGE_READWRITE = 0x04,
- PAGE_WRITECOPY = 0x08,
- PAGE_TARGETS_INVALID = 0x40000000,
- PAGE_TARGETS_NO_UPDATE = 0x40000000
- }
-
protected virtual void Dispose(bool disposing)
{
if (disposing)
diff --git a/ProcessMemoryDataFinder/API/ProcessMemoryReader.cs b/ProcessMemoryDataFinder/API/ProcessMemoryReader.cs
deleted file mode 100644
index 601f600..0000000
--- a/ProcessMemoryDataFinder/API/ProcessMemoryReader.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using ProcessMemoryDataFinder.Misc;
-
-namespace ProcessMemoryDataFinder.API
-{
- internal class ProcessMemoryReader
- {
- private Process _mReadProcess;
- public IntPtr m_hProcess = IntPtr.Zero;
-
- private Process m_ReadProcess
- {
- get => _mReadProcess;
- set
- {
- _mReadProcess = value;
- if (m_hProcess != IntPtr.Zero)
- CloseHandle();
- m_hProcess = IntPtr.Zero;
- }
- }
-
- public Process ReadProcess
- {
- get => m_ReadProcess;
- set => m_ReadProcess = value;
- }
-
- ///
- /// Closes the process handle if it was open.
- ///
- /// CloseHandle failed
- public void CloseHandle()
- {
- if (ProcessMemoryReaderApi.CloseHandle(m_hProcess) == 0) throw new Exception("CloseHandle failed");
- }
-
- ///
- ///
- /// IntPtr of the opened process or IntPtr. Zero if open failed.
- public IntPtr OpenProcess()
- {
- if (m_ReadProcess != null)
- if (!_mReadProcess.SafeHasExited() && m_hProcess == IntPtr.Zero)
- try
- {
- m_hProcess = ProcessMemoryReaderApi.OpenProcess(0x38, 1, (uint) m_ReadProcess.Id);
- }
- catch (Win32Exception)
- {
- // Most likely "Access denied"
- //TODO: stop swallowing this exception and throw it like a man(and handle it elsewhere as necessary)
- return IntPtr.Zero;
- }
-
- return m_hProcess;
- }
-
- ///
- /// Reads the process memory.
- ///
- /// The memory address.
- /// The bytes to read.
- /// The bytes read.
- ///
- ///
- public byte[] ReadProcessMemory(IntPtr memoryAddress, uint bytesToRead, out uint bytesRead)
- {
- try
- {
- if (m_hProcess == IntPtr.Zero)
- {
- bytesRead = 0;
- return null;
- }
-
- var buffer = new byte[bytesToRead];
- if (
- ProcessMemoryReaderApi.ReadProcessMemory(m_hProcess, memoryAddress, buffer, bytesToRead,
- out var lpNumberOfBytesRead) == 0)
- {
- bytesRead = 0;
- return null;
- }
-
- // lpNumberOfBytesRead is an IntPtr here so technically if we are 32bit platform we should be calling ToInt32()
- // but that doesnt matter here since we just want a uint value, which on a 32bit platform will work just fine (just tiny bit of overhead of first going to a int64)
- // and on a 64bit platform we dont have to worry about the value being larger then uint max, since we gave it a uint bytesToRead
- // so this cast should never fail
- bytesRead = (uint)lpNumberOfBytesRead.ToInt64();
- return buffer;
- }
- catch
- {
- bytesRead = 0;
- return null;
- }
- }
-
- private class ProcessMemoryReaderApi
- {
- public const uint PROCESS_VM_OPERATION = 8;
- public const uint PROCESS_VM_READ = 0x10;
- public const uint PROCESS_VM_WRITE = 0x20;
-
- [DllImport("kernel32.dll")]
- public static extern int CloseHandle(IntPtr hObject);
-
- [DllImport("kernel32.dll")]
- public static extern IntPtr OpenProcess(uint dwDesiredAccess, int bInheritHandle, uint dwProcessId);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern int ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In] [Out] byte[] buffer,
- uint size, out IntPtr lpNumberOfBytesRead);
- }
- }
-}
\ No newline at end of file
diff --git a/ProcessMemoryDataFinder/API/Sig.cs b/ProcessMemoryDataFinder/API/Sig.cs
index 1c28e6e..bec0bf9 100644
--- a/ProcessMemoryDataFinder/API/Sig.cs
+++ b/ProcessMemoryDataFinder/API/Sig.cs
@@ -19,8 +19,8 @@ public class Sig
public class SigEx : Sig
{
- private MemoryReader.FindPatternF _findPatternFunc;
- private MemoryReader.ReadDataF _readDataFunc;
+ private MemoryReaderManager.FindPatternF _findPatternFunc;
+ private MemoryReaderManager.ReadDataF _readDataFunc;
private IObjectReader _objectReader;
public string Name { get; set; }
@@ -46,13 +46,13 @@ public class SigEx : Sig
///
public List PointerOffsets { get; set; } = new List();
- public void SetFindPatternF(MemoryReader.FindPatternF f)
+ public void SetFindPatternF(MemoryReaderManager.FindPatternF f)
{
_findPatternFunc = f;
ParentSig?.SetFindPatternF(f);
}
- public void SetReadDataF(MemoryReader.ReadDataF f)
+ public void SetReadDataF(MemoryReaderManager.ReadDataF f)
{
_readDataFunc = f;
ParentSig?.SetReadDataF(f);
diff --git a/ProcessMemoryDataFinder/API/SigMemoryReader.cs b/ProcessMemoryDataFinder/API/SigMemoryReader.cs
index 55dc7fc..39bcde7 100644
--- a/ProcessMemoryDataFinder/API/SigMemoryReader.cs
+++ b/ProcessMemoryDataFinder/API/SigMemoryReader.cs
@@ -3,7 +3,7 @@
namespace ProcessMemoryDataFinder.API
{
- public abstract class SigMemoryReader : MemoryReader
+ public abstract class SigMemoryReader : MemoryReaderManager
{
protected Dictionary Signatures = new Dictionary();
protected IObjectReader ObjectReader;
diff --git a/ProcessMemoryDataFinder/API/SigScan.cs b/ProcessMemoryDataFinder/API/SigScan.cs
index 385cb23..5c8bf14 100644
--- a/ProcessMemoryDataFinder/API/SigScan.cs
+++ b/ProcessMemoryDataFinder/API/SigScan.cs
@@ -1,86 +1,42 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using ProcessMemoryDataFinder.Misc;
+using ProcessMemoryDataFinder.API.Memory;
namespace ProcessMemoryDataFinder.API
{
- //Slightly modified (by Piotrekol) signature scanner (added ability of scanning memory without need of mask usage)
- //
- //Original changelog and credits follow:
- //
- // sigScan C# Implementation - Written by atom0s [aka Wiccaan]
- // Class Version: 2.0.0
- //
- // [ CHANGE LOG ] -------------------------------------------------------------------------
- //
- // 2.0.0
- // - Updated to no longer require unsafe or fixed code.
- // - Removed unneeded methods and code.
- //
- // 1.0.0
- // - First version written and release.
- //
- // [ CREDITS ] ----------------------------------------------------------------------------
- //
- // sigScan is based on the FindPattern code written by
- // dom1n1k and Patrick at GameDeception.net
- //
- // Full credit to them for the purpose of this code. I, atom0s, simply
- // take credit for converting it to C#.
internal class SigScan
{
- ///
- /// ReadProcessMemory
- ///
- /// API import definition for ReadProcessMemory.
- ///
- /// Handle to the process we want to read from.
- /// The base address to start reading from.
- /// The return buffer to write the read data to.
- /// The size of data we wish to read.
- /// The number of bytes successfully read.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool ReadProcessMemory(
- IntPtr hProcess,
- IntPtr lpBaseAddress,
- [Out] byte[] lpBuffer,
- int dwSize,
- out int lpNumberOfBytesRead
- );
-
///
/// m_vDumpedRegion
///
/// The memory dumped from the external process.
///
- private byte[] m_vDumpedRegion;
+ protected byte[] m_vDumpedRegion;
///
/// m_vProcess
///
/// The process we want to read the memory of.
///
- private Process m_vProcess;
+ protected SafeProcess m_vProcess;
///
/// m_vAddress
///
/// The starting address we want to begin reading at.
///
- private IntPtr m_vAddress;
+ protected IntPtr m_vAddress;
///
/// m_vSize
///
/// The number of bytes we wish to read from the process.
///
- private Int32 m_vSize;
+ protected Int32 m_vSize;
private List m_SigQueue;
+ private readonly MemoryReader m_OSMemoryReader;
#region "sigScan Class Construction"
///
@@ -90,13 +46,14 @@ out int lpNumberOfBytesRead
/// Simply initializes the class properties and
/// expects the user to set them later.
///
- public SigScan()
+ public SigScan(MemoryReader osMemoryReader)
{
m_vProcess = null;
m_vAddress = IntPtr.Zero;
m_vSize = 0;
m_vDumpedRegion = null;
m_SigQueue = new List();
+ m_OSMemoryReader = osMemoryReader;
}
///
/// SigScan
@@ -107,7 +64,7 @@ public SigScan()
/// The process to dump the memory from.
/// The started address to begin the dump.
/// The size of the dump.
- public SigScan(Process proc, IntPtr addr, int size)
+ public SigScan(SafeProcess proc, IntPtr addr, int size)
{
m_vProcess = proc;
m_vAddress = addr;
@@ -117,7 +74,7 @@ public SigScan(Process proc, IntPtr addr, int size)
#endregion
#region "sigScan Class Private Methods"
-
+
///
/// DumpMemory
///
@@ -129,11 +86,10 @@ private bool DumpMemory()
{
try
{
+
// Checks to ensure we have valid data.
if (m_vProcess == null)
return false;
- if (m_vProcess.SafeHasExited())
- return false;
if (m_vAddress == IntPtr.Zero)
return false;
if (m_vSize == 0)
@@ -146,9 +102,7 @@ private bool DumpMemory()
int nBytesRead = 0;
// Dump the memory.
- bReturn = ReadProcessMemory(
- m_vProcess.Handle, m_vAddress, m_vDumpedRegion, m_vSize, out nBytesRead
- );
+ bReturn = m_OSMemoryReader.ReadProcessMemory(m_vProcess.Handle, m_vProcess.PID, m_vAddress, (uint)m_vSize, m_vDumpedRegion, out nBytesRead);
// Validation checks.
if (bReturn == false || nBytesRead != m_vSize)
@@ -259,10 +213,10 @@ public void FindPattern()
// Ensure the mask and pattern lengths match.
foreach (var sig in SigQueue)
{
- if(sig.Mask.Length != sig.Pattern.Length)
+ if (sig.Mask.Length != sig.Pattern.Length)
return;
}
-
+
// Loop the region and look for the patterns.
for (int x = 0; x < m_vDumpedRegion.Length; x++)
@@ -296,11 +250,12 @@ public void ResetRegion()
#endregion
#region "sigScan Class Properties"
- public Process Process
+ public SafeProcess Process
{
get { return m_vProcess; }
set { m_vProcess = value; }
}
+
public IntPtr Address
{
get { return m_vAddress; }
@@ -321,6 +276,7 @@ public List SigQueue
/// Faster FindPattern implementation that doesn't use mask
/// Instead of 4~5s scans with mask this can do same byte length scan in about half a second
///
+ //TODO: replace with readOnlySpans and test
public IntPtr FindPattern(byte[] patternBytes, int nOffset)
{
// Dump the memory region if we have not dumped it yet.
@@ -335,7 +291,7 @@ public IntPtr FindPattern(byte[] patternBytes, int nOffset)
return m_vAddress + (nOffset + result);
}
-
+ //TODO: replace with readOnlySpans and test
private static int Scan(byte[] sIn, byte[] sFor)
{
if (sIn == null)
@@ -371,6 +327,4 @@ private static int Scan(byte[] sIn, byte[] sFor)
return -1;
}
}
-
-
}
\ No newline at end of file
diff --git a/ProcessMemoryDataFinder/API/WindowsMemoryProtectionOptions.cs b/ProcessMemoryDataFinder/API/WindowsMemoryProtectionOptions.cs
new file mode 100644
index 0000000..33f15c6
--- /dev/null
+++ b/ProcessMemoryDataFinder/API/WindowsMemoryProtectionOptions.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+
+namespace ProcessMemoryDataFinder.API
+{
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ [Flags]
+ internal enum WindowsMemoryProtectionOptions
+ {
+ PAGE_EXECUTE = 0x10,
+ PAGE_EXECUTE_READ = 0x20,
+ PAGE_EXECUTE_READWRITE = 0x40,
+ PAGE_EXECUTE_WRITECOPY = 0x80,
+ PAGE_NOACCESS = 0x01,
+ PAGE_READONLY = 0x02,
+ PAGE_READWRITE = 0x04,
+ PAGE_WRITECOPY = 0x08,
+ PAGE_TARGETS_INVALID = 0x40000000,
+ PAGE_TARGETS_NO_UPDATE = 0x40000000
+ }
+}
\ No newline at end of file
diff --git a/ProcessMemoryDataFinder/ObjectReader.cs b/ProcessMemoryDataFinder/ObjectReader.cs
index 9ec37c2..3da68e2 100644
--- a/ProcessMemoryDataFinder/ObjectReader.cs
+++ b/ProcessMemoryDataFinder/ObjectReader.cs
@@ -18,7 +18,7 @@ public interface IObjectReader
public class ObjectReader : IObjectReader
{
- private readonly MemoryReader _memoryReader;
+ private readonly MemoryReaderManager _memoryReader;
///
/// Size of pointers in searched process
@@ -26,10 +26,12 @@ public class ObjectReader : IObjectReader
public int IntPtrSize { get; set; } = IntPtr.Size;
private bool IsX64 => IntPtrSize == 8;
+ private int ListArrayFirstElementOffset = 0;
- public ObjectReader(MemoryReader memoryReader)
+ public ObjectReader(MemoryReaderManager memoryReader)
{
_memoryReader = memoryReader;
+ ListArrayFirstElementOffset = OperatingSystem.IsLinux() ? 4 : 0;
}
public List ReadUIntList(IntPtr baseAddress)
@@ -156,7 +158,7 @@ public byte[] ReadStringBytes(IntPtr baseAddress, int bytesPerCharacter = 2)
// 2. resolve pinter to internal array
// 3. skip VTable and internal number of elements (both 4 or 8 bytes depending on platform)
var internalArray = ReadPointer(address + IntPtrSize);
- firstElementPtr = internalArray + 2 * IntPtrSize;// IsX64 ? internalArray +16 : internalArray + 8
+ firstElementPtr = internalArray + 2 * IntPtrSize + ListArrayFirstElementOffset;// IsX64 ? internalArray +16 : internalArray + 8
}
else
{
@@ -189,7 +191,7 @@ public byte[] ReadStringBytes(IntPtr baseAddress, int bytesPerCharacter = 2)
return (-1, IntPtr.Zero);
}
numberOfElements = (int)numberOfElementsLong;
- firstElementPtr = numberOfElementsAddr + 8;
+ firstElementPtr = numberOfElementsAddr + 8 + ListArrayFirstElementOffset;
}
}
else
diff --git a/ProcessMemoryDataFinder/ProcessExtensions.cs b/ProcessMemoryDataFinder/ProcessExtensions.cs
index c50baad..6c1088d 100644
--- a/ProcessMemoryDataFinder/ProcessExtensions.cs
+++ b/ProcessMemoryDataFinder/ProcessExtensions.cs
@@ -1,20 +1,28 @@
-using System.Diagnostics;
+using System;
+using System.Diagnostics;
-namespace ProcessMemoryDataFinder.Misc
+namespace ProcessMemoryDataFinder
{
internal static class ProcessExtensions
{
- internal static bool SafeHasExited(this Process process)
+ internal static SafeProcess ToSafeProcess(this Process process)
{
+ if (process == null)
+ return null;
+ IntPtr handle;
+ int pid;
try
{
- return process.HasExited;
+ handle = process?.Handle ?? IntPtr.Zero;
+ pid = process?.Id ?? -1;
}
- catch
+ catch (InvalidOperationException)
{
- return true;
+ return null;
}
+
+ return new SafeProcess(process, handle, pid);
}
}
}
\ No newline at end of file
diff --git a/ProcessMemoryDataFinder/ProcessMemoryDataFinder.csproj b/ProcessMemoryDataFinder/ProcessMemoryDataFinder.csproj
index 0f4b7c5..c6db826 100644
--- a/ProcessMemoryDataFinder/ProcessMemoryDataFinder.csproj
+++ b/ProcessMemoryDataFinder/ProcessMemoryDataFinder.csproj
@@ -1,6 +1,6 @@
-
+
- netcoreapp3.1;net472;net471;net5.0
+ net5.0;net6.0
x64;x86;AnyCPU
ProcessMemoryDataFinder
ProcessMemoryDataFinder
@@ -23,6 +23,7 @@
true
GPL-3.0-or-later
+ true
bin\x64\Debug\
diff --git a/ProcessMemoryDataFinder/SafeProcess.cs b/ProcessMemoryDataFinder/SafeProcess.cs
new file mode 100644
index 0000000..3a464a3
--- /dev/null
+++ b/ProcessMemoryDataFinder/SafeProcess.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Diagnostics;
+
+namespace ProcessMemoryDataFinder
+{
+ public class SafeProcess : IDisposable
+ {
+ public Process Process { get; }
+ public IntPtr Handle { get; }
+ public int PID { get; }
+ internal SafeProcess(Process process, IntPtr handle, int pID)
+ {
+ Process = process;
+ Handle = handle;
+ PID = pID;
+ }
+
+ public bool HasExited()
+ {
+ try
+ {
+ return Process.HasExited;
+ }
+ catch
+ {
+ return true;
+ }
+ }
+
+ public void Dispose()
+ {
+ Process.Dispose();
+ }
+ }
+}
diff --git a/ProcessMemoryDataFinder/Structured/AddressFinder.cs b/ProcessMemoryDataFinder/Structured/AddressFinder.cs
index 2920dec..bd09364 100644
--- a/ProcessMemoryDataFinder/Structured/AddressFinder.cs
+++ b/ProcessMemoryDataFinder/Structured/AddressFinder.cs
@@ -7,7 +7,7 @@ namespace ProcessMemoryDataFinder.Structured
{
public class AddressFinder
{
- private readonly MemoryReader _memoryReader;
+ private readonly MemoryReaderManager _memoryReader;
private readonly IObjectReader _objectReader;
private readonly Dictionary _constantAddresses;
private readonly Dictionary _constantAddressesCache;
@@ -19,7 +19,7 @@ public class AddressFinder
///
public int IntPtrSize { get; set; } = IntPtr.Size;
- public AddressFinder(MemoryReader memoryReader, IObjectReader objectReader, Dictionary constantAddresses)
+ public AddressFinder(MemoryReaderManager memoryReader, IObjectReader objectReader, Dictionary constantAddresses)
{
_memoryReader = memoryReader;
_memoryReader.ProcessChanged += MemoryReaderOnProcessChanged;
diff --git a/ProcessMemoryDataFinder/Structured/StructuredMemoryReader.cs b/ProcessMemoryDataFinder/Structured/StructuredMemoryReader.cs
index b4185ff..b2b6dd2 100644
--- a/ProcessMemoryDataFinder/Structured/StructuredMemoryReader.cs
+++ b/ProcessMemoryDataFinder/Structured/StructuredMemoryReader.cs
@@ -11,7 +11,7 @@ namespace ProcessMemoryDataFinder.Structured
{
public class StructuredMemoryReader : IDisposable, IStructuredMemoryReader
{
- protected MemoryReader _memoryReader;
+ protected MemoryReaderManager _memoryReader;
protected AddressFinder _addressFinder;
private AddressTokenizer _addressTokenizer = new AddressTokenizer();
protected IObjectReader ObjectReader;
@@ -88,9 +88,9 @@ public TypeCacheEntry(Type type, string classPath, List props)
};
protected Dictionary ReadHandlers;
- public StructuredMemoryReader(string processName, Dictionary baseAdresses, string mainWindowTitleHint = null, MemoryReader memoryReader = null, IObjectReader objectReader = null)
+ public StructuredMemoryReader(string processName, Dictionary baseAdresses, string mainWindowTitleHint = null, MemoryReaderManager memoryReader = null, IObjectReader objectReader = null)
{
- _memoryReader = memoryReader ?? new MemoryReader(processName, mainWindowTitleHint);
+ _memoryReader = memoryReader ?? new MemoryReaderManager(processName, mainWindowTitleHint);
ObjectReader = objectReader ?? new ObjectReader(_memoryReader);
_addressFinder = new AddressFinder(_memoryReader, ObjectReader, baseAdresses);
diff --git a/StructuredOsuMemoryProviderTester/StructuredOsuMemoryProviderTester.csproj b/StructuredOsuMemoryProviderTester/StructuredOsuMemoryProviderTester.csproj
index ed68f4a..30a3421 100644
--- a/StructuredOsuMemoryProviderTester/StructuredOsuMemoryProviderTester.csproj
+++ b/StructuredOsuMemoryProviderTester/StructuredOsuMemoryProviderTester.csproj
@@ -2,7 +2,7 @@
WinExe
- netcoreapp3.1;net472;net471;net5.0-windows
+ net5.0-windows
x86;x64;AnyCPU
WinExe
true