From 1ef04774580842f2450f89ac7ad13659d078e8c9 Mon Sep 17 00:00:00 2001 From: Piotrekol <4990365+Piotrekol@users.noreply.github.com> Date: Fri, 31 Mar 2023 18:33:05 +0200 Subject: [PATCH] Add: Linux support --- ...uxStructuredOsuMemoryProviderTester.csproj | 18 +++ .../Program.cs | 115 +++++++++++++++++ .../OsuMemoryDataProvider.csproj | 2 +- .../OsuMemoryDataProviderTester.csproj | 2 +- ProcessMemoryDataFinder.sln | 19 ++- ProcessMemoryDataFinder/API/Internals.cs | 69 ---------- .../API/Memory/LinuxMemoryReader.cs | 98 ++++++++++++++ .../API/Memory/MEMORY_BASIC_INFORMATION.cs | 17 +++ .../API/Memory/Math/IIntPtrMath.cs | 10 ++ .../API/Memory/Math/X64IntPtrMath.cs | 15 +++ .../API/Memory/Math/X86IntPtrMath.cs | 15 +++ .../Math/X86ProcessX64RuntimeIntPtrMath.cs | 15 +++ .../API/Memory/MemoryReader.cs | 47 +++++++ .../API/Memory/WindowsMemoryReader.cs | 68 ++++++++++ ...MemoryReader.cs => MemoryReaderManager.cs} | 116 +++++------------ .../API/ProcessMemoryReader.cs | 120 ------------------ ProcessMemoryDataFinder/API/Sig.cs | 8 +- .../API/SigMemoryReader.cs | 2 +- ProcessMemoryDataFinder/API/SigScan.cs | 82 +++--------- .../API/WindowsMemoryProtectionOptions.cs | 21 +++ ProcessMemoryDataFinder/ObjectReader.cs | 10 +- ProcessMemoryDataFinder/ProcessExtensions.cs | 20 ++- .../ProcessMemoryDataFinder.csproj | 5 +- ProcessMemoryDataFinder/SafeProcess.cs | 35 +++++ .../Structured/AddressFinder.cs | 4 +- .../Structured/StructuredMemoryReader.cs | 6 +- .../StructuredOsuMemoryProviderTester.csproj | 2 +- 27 files changed, 580 insertions(+), 361 deletions(-) create mode 100644 LinuxStructuredOsuMemoryProviderTester/LinuxStructuredOsuMemoryProviderTester.csproj create mode 100644 LinuxStructuredOsuMemoryProviderTester/Program.cs delete mode 100644 ProcessMemoryDataFinder/API/Internals.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/LinuxMemoryReader.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/MEMORY_BASIC_INFORMATION.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/Math/IIntPtrMath.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/Math/X64IntPtrMath.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/Math/X86IntPtrMath.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/Math/X86ProcessX64RuntimeIntPtrMath.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/MemoryReader.cs create mode 100644 ProcessMemoryDataFinder/API/Memory/WindowsMemoryReader.cs rename ProcessMemoryDataFinder/API/{MemoryReader.cs => MemoryReaderManager.cs} (54%) delete mode 100644 ProcessMemoryDataFinder/API/ProcessMemoryReader.cs create mode 100644 ProcessMemoryDataFinder/API/WindowsMemoryProtectionOptions.cs create mode 100644 ProcessMemoryDataFinder/SafeProcess.cs 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