See More

using System; using System.Collections.Concurrent; using System.Text; using ProcessNET.Extensions; using ProcessNET.Native; using ProcessNET.Native.Types; namespace ProcessNET.Memory { ///

/// Class for memory editing a process. /// /// public abstract class ProcessMemory : IMemory { /// /// The open handle to the process which contains the memory of interest, /// protected readonly SafeMemoryHandle Handle; /// /// Runtime types names based on their addresses. /// protected ConcurrentDictionary rttiCache = null; protected readonly bool is32Bit = false; /// /// Returns the type of the processor architecture of the process. /// public bool Is32Bit => is32Bit; /// /// Initializes a new instance of the class. /// /// The open handle to the process which contains the memory of interest. protected ProcessMemory(SafeMemoryHandle handle) { Handle = handle; is32Bit = Kernel32.Is32BitProcess(handle.DangerousGetHandle()); rttiCache = new ConcurrentDictionary(); } /// /// Writes a set of bytes to memory. /// /// The address where the bytes start in memory. /// The length of the byte chunk to read from the memory address. /// /// The byte array section read from memory. /// public abstract byte[] Read(IntPtr intPtr, int length); /// /// Writes a set of bytes to memory. /// /// The address where the bytes start in memory. /// The buffer to read data onto, it's size determines the count of bytes to read. public abstract void Read(IntPtr intPtr, byte[] buffer); /// /// Reads a string with a specified encoding from memory. /// /// The address where the string is read. /// The encoding used. /// /// The number of maximum bytes to read. The string is automatically cropped at this end ('\0' /// char). /// /// The string. public string ReadString(IntPtr intPtr, Encoding encoding, int maxLength = 512) { var buffer = Read(intPtr, maxLength); var ret = encoding.GetString(buffer); if (ret.IndexOf('\0') != -1) ret = ret.Remove(ret.IndexOf('\0')); return ret; } /// /// Reads the value of a specified type from memory. /// /// The type of the value. /// The address where the value is read. /// A value. public abstract T Read(IntPtr intPtr); /// /// Reads an array of a specified type from memory. /// /// The type of the values. /// The address where the values is read. /// The number of cells in the array. /// An array. public T[] Read(IntPtr intPtr, int length) { var buffer = new T[length]; for (var i = 0; i < buffer.Length; i++) buffer[i] = Read(intPtr); return buffer; } /// /// Write an array of bytes in the remote process. /// /// The address where the array is written. /// The array of bytes to write. public abstract int Write(IntPtr intPtr, byte[] bytesToWrite); /// /// Writes a string with a specified encoding to memory. /// /// The address where the string is written. /// The text to write. /// The encoding used. public virtual void WriteString(IntPtr intPtr, string stringToWrite, Encoding encoding) { if (stringToWrite[stringToWrite.Length - 1] != '\0') stringToWrite += '\0'; var bytes = encoding.GetBytes(stringToWrite); Write(intPtr, bytes); } /// /// Writes an array of a specified type to memory, /// /// The type of the values. /// The address where the values is written. /// The array to write. public void Write(IntPtr intPtr, T[] values) { foreach (var value in values) Write(intPtr, value); } /// /// Writes the values of a specified type to memory. /// /// The type of the value. /// The address where the value is written. /// The value to write. public abstract void Write(IntPtr intPtr, T value); /// /// Reads the Runtime Type Information (RTTI) at certain address. /// /// Address to read the type from. /// Runtime Type Information of the object at the passed address or null if it's not an object. public string ReadRemoteRuntimeTypeInformation(IntPtr address) { if (address.MayBeValid()) { if(!rttiCache.TryGetValue(address, out string rtti)) { var objectLocatorPtr = Read(address - (Is32Bit ? 4 : 8)); if (objectLocatorPtr.MayBeValid()) { if (is32Bit) rtti = ReadRemoteRuntimeTypeInformation32(objectLocatorPtr); else rtti = ReadRemoteRuntimeTypeInformation64(objectLocatorPtr); rttiCache.AddOrUpdate(address, rtti, (oldK, oldV) => rtti); } } return rtti; } return null; } private string ReadRemoteRuntimeTypeInformation32(IntPtr address) { var classHierarchyDescriptorPtr = Read(address + 0x10); if (classHierarchyDescriptorPtr.MayBeValid()) { var baseClassCount = Read(classHierarchyDescriptorPtr + 8); if (baseClassCount > 0 && baseClassCount < 25) { var baseClassArrayPtr = Read(classHierarchyDescriptorPtr + 0xC); if (baseClassArrayPtr.MayBeValid()) { var sb = new StringBuilder(); for (var i = 0; i < baseClassCount; ++i) { var baseClassDescriptorPtr = Read(baseClassArrayPtr + (4 * i)); if (baseClassDescriptorPtr.MayBeValid()) { var typeDescriptorPtr = Read(baseClassDescriptorPtr); if (typeDescriptorPtr.MayBeValid()) { var name = ReadString(typeDescriptorPtr + 0x0C, Encoding.UTF8, 60); if (name.EndsWith("@@")) { name = DbgHelp.UnDecorateSymbolName($"?{name}", UnDecorateFlags.UNDNAME_NAME_ONLY); } sb.Append(name); sb.Append(" : "); continue; } } break; } if (sb.Length != 0) { sb.Length -= 3; return sb.ToString(); } } } } return null; } private string ReadRemoteRuntimeTypeInformation64(IntPtr address) { int baseOffset = Read(address + 0x14); if (baseOffset != 0) { var baseAddress = address - baseOffset; var classHierarchyDescriptorOffset = Read(address + 0x10); if (classHierarchyDescriptorOffset != 0) { var classHierarchyDescriptorPtr = baseAddress + classHierarchyDescriptorOffset; var baseClassCount = Read(classHierarchyDescriptorPtr + 0x08); if (baseClassCount > 0 && baseClassCount < 25) { var baseClassArrayOffset = Read(classHierarchyDescriptorPtr + 0x0C); if (baseClassArrayOffset != 0) { var baseClassArrayPtr = baseAddress + baseClassArrayOffset; var sb = new StringBuilder(); for (var i = 0; i < baseClassCount; ++i) { var baseClassDescriptorOffset = Read(baseClassArrayPtr + (4 * i)); if (baseClassDescriptorOffset != 0) { var baseClassDescriptorPtr = baseAddress + baseClassDescriptorOffset; var typeDescriptorOffset = Read(baseClassDescriptorPtr); if (typeDescriptorOffset != 0) { var typeDescriptorPtr = baseAddress + typeDescriptorOffset; var name = ReadString(typeDescriptorPtr + 0x14, Encoding.UTF8, 60); if (string.IsNullOrEmpty(name)) { break; } if (name.EndsWith("@@")) { name = DbgHelp.UnDecorateSymbolName($"?{name}", UnDecorateFlags.UNDNAME_NAME_ONLY); } sb.Append(name); sb.Append(" : "); continue; } } break; } if (sb.Length != 0) { sb.Length -= 3; return sb.ToString(); } } } } } return null; } } }