csharp/Akaion/Jupiter/Jupiter/Memory/MemoryManager.cs

MemoryManager.cs
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Jupiter.Shared.Handlers;
using Microsoft.Win32.SafeHandles;
using static Jupiter.Native.Enumerations;
using static Jupiter.Native.PInvoke;
using static Jupiter.Native.Structures;

namespace Jupiter.Memory
{
    internal clast MemoryManager: IDisposable
    {
        private readonly SafeProcessHandle _processHandle;
        
        internal MemoryManager(int processId)
        {
            _processHandle = OpenProcessHandle(processId);
        }
        
        internal MemoryManager(string processName)
        {
            _processHandle = OpenProcessHandle(processName);
        }
        
        public void Dispose()
        {
            _processHandle.Dispose();
        }
        
        internal IntPtr AllocateVirtualMemory(IntPtr baseAddress, int allocationSize, MemoryProtection protectionType)
        {
            const AllocationType allocationType = AllocationType.Commit | AllocationType.Reserve;

            var regionAddress = VirtualAllocEx(_processHandle, baseAddress, allocationSize, allocationType, protectionType);

            if (regionAddress == IntPtr.Zero)
            {
                ExceptionHandler.ThrowWin32Exception("Failed to allocate a region of virtual memory in the remote process");
            }
            
            return regionAddress;
        }
        
        internal void FreeVirtualMemory(IntPtr baseAddress)
        {
            if (!VirtualFreeEx(_processHandle, baseAddress, 0, FreeType.Release))
            {
                ExceptionHandler.ThrowWin32Exception("Failed to free a region of virtual memory in the remote process");
            }
        }

        internal MemoryProtection ProtectVirtualMemory(IntPtr baseAddress, int protectionSize, MemoryProtection protectionType)
        {
            if (!VirtualProtectEx(_processHandle, baseAddress, protectionSize, protectionType, out var oldProtectionType))
            {
                ExceptionHandler.ThrowWin32Exception("Failed to protect a region of virtual memory in the remote process");
            }

            return oldProtectionType;
        }

        internal MemoryBasicInformation QueryVirtualMemory(IntPtr baseAddress)
        {
            if (!VirtualQueryEx(_processHandle, baseAddress, out var memoryBasicInformation, Marshal.SizeOf()))
            {
                ExceptionHandler.ThrowWin32Exception("Failed to query a region of virtual memory in the remote process");
            }
            
            return memoryBasicInformation;
        }
        
        internal byte[] ReadVirtualMemory(IntPtr baseAddress, int bytesToRead)
        {
            var bytesBuffer = Marshal.AllocHGlobal(bytesToRead);
            
            if (!ReadProcessMemory(_processHandle, baseAddress, bytesBuffer, bytesToRead, IntPtr.Zero))
            {
                ExceptionHandler.ThrowWin32Exception("Failed to read from a region of virtual memory in the remote process");
            }
            
            var bytesRead = new byte[bytesToRead];
            
            Marshal.Copy(bytesBuffer, bytesRead, 0, bytesToRead);
            
            Marshal.FreeHGlobal(bytesBuffer);

            return bytesRead;
        }

        internal TStructure ReadVirtualMemory(IntPtr baseAddress) where TStructure : struct
        {
            var structureSize = Marshal.SizeOf();
            
            var structureBuffer = Marshal.AllocHGlobal(structureSize);

            if (!ReadProcessMemory(_processHandle, baseAddress, structureBuffer, structureSize, IntPtr.Zero))
            {
                ExceptionHandler.ThrowWin32Exception("Failed to read from a region of virtual memory in the remote process");
            }

            try
            {
                return Marshal.PtrToStructure(structureBuffer);
            }

            finally
            {
                Marshal.FreeHGlobal(structureBuffer);
            }
        }

        internal void WriteVirtualMemory(IntPtr baseAddress, byte[] bytesToWrite)
        {
            // Adjust the protection of the virtual memory region to ensure it has write privileges
            
            var originalProtectionType = ProtectVirtualMemory(baseAddress, bytesToWrite.Length, MemoryProtection.ReadWrite);

            var bytesToWriteBufferHandle = GCHandle.Alloc(bytesToWrite, GCHandleType.Pinned);
            
            if (!WriteProcessMemory(_processHandle, baseAddress, bytesToWriteBufferHandle.AddrOfPinnedObject(), bytesToWrite.Length, IntPtr.Zero))
            {
                ExceptionHandler.ThrowWin32Exception("Failed to write into a region of virtual memory in the remote process");
            }
            
            // Restore the original protection of the virtual memory region
            
            ProtectVirtualMemory(baseAddress, bytesToWrite.Length, originalProtectionType);

            bytesToWriteBufferHandle.Free();
        }

        internal void WriteVirtualMemory(IntPtr baseAddress, TStructure structureToWrite) where TStructure : struct
        {
            var structureSize = Marshal.SizeOf();
            
            // Adjust the protection of the virtual memory region to ensure it has write privileges
            
            var originalProtectionType = ProtectVirtualMemory(baseAddress, structureSize, MemoryProtection.ReadWrite);

            var structureToWriteBufferHandle = GCHandle.Alloc(structureToWrite, GCHandleType.Pinned);
            
            if (!WriteProcessMemory(_processHandle, baseAddress, structureToWriteBufferHandle.AddrOfPinnedObject(), structureSize, IntPtr.Zero))
            {
                ExceptionHandler.ThrowWin32Exception("Failed to write into a region of virtual memory in the remote process");
            }
            
            // Restore the original protection of the virtual memory region
            
            ProtectVirtualMemory(baseAddress, structureSize, originalProtectionType);

            structureToWriteBufferHandle.Free();
        }
        
        private SafeProcessHandle OpenProcessHandle(int processId)
        {
            try
            {
                return Process.GetProcessById(processId).SafeHandle;
            }

            catch (ArgumentException)
            {
                throw new ArgumentException($"No process with the id {processId} is currently running");
            }
        }
        
        private SafeProcessHandle OpenProcessHandle(string processName)
        {
            try
            {
                return Process.GetProcessesByName(processName)[0].SafeHandle;
            }

            catch (IndexOutOfRangeException)
            {
                throw new ArgumentException($"No process with the name {processName} is currently running");
            }
        }
    }
}