using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using DllUtils.Attributes; using DllUtils.Exceptions; using DllUtils.Memory; using DllUtils.Process; namespace DllUtils.Serializers { public class RemoteParamSerializer { public RemoteParamSerializer(ProcessHandle process) { Process = process; } public ProcessHandle Process { get; } public AllocatedMemory Serialize(T param) { AllocatedMemory memory; if (param is int) { return new AllocatedMemory(Process, (IntPtr)Convert.ToInt32(param), null, sizeof(int)); } if (param is Array array) { memory = SerializeArray(array); } else if (param.GetType().GetCustomAttribute() is CustomFunctionParamsAttribute attribute) { memory = SerializeCustom(param, attribute); } else { memory = SerializeUsingMarshal(param); } if (!memory.Alloc()) { throw new MemoryAllocationException("Failed to allocate memory."); } if (!memory.Write()) { throw new MemoryWriteException("Failed to write to allocated memory."); } return memory; } public AllocatedMemory SerializeArray(Array array) { byte[] bytes = SerializeArrayToBytes(array, out int size); return new AllocatedMemory(Process, IntPtr.Zero, bytes, size); } public AllocatedMemory SerializeCustom(T param, CustomFunctionParamsAttribute customOptions) { System.Type type = param.GetType(); IEnumerable fields = type.GetFields(); List dataList = new List(); foreach (FieldInfo field in fields) { ParamPositionAttribute positionAttribute = field.GetCustomAttribute(); if (positionAttribute == null) { continue; } ParamData currentData = new ParamData { Position = positionAttribute.Index, ParamType = positionAttribute.ParamType, Value = field.GetValue(param) }; int size; if (currentData.ParamType == ParamType.NotSpecified) { currentData.ParamType = type.IsValueType ? ParamType.Reference : ParamType.Value; } if (currentData.Value is string stringVal) { switch (customOptions.StringEncoding) { case EncodingType.ASCII: currentData.Value = Encoding.ASCII.GetBytes(stringVal); break; case EncodingType.UTF8: currentData.Value = Encoding.UTF8.GetBytes(stringVal); break; case EncodingType.UTF32: currentData.Value = Encoding.UTF32.GetBytes(stringVal); break; case EncodingType.Unicode: currentData.Value = Encoding.Unicode.GetBytes(stringVal); break; } } if (currentData.ParamType == ParamType.Reference) { currentData.Memory = Serialize(currentData.Value); currentData.Size = Marshal.SizeOf(typeof(IntPtr)); currentData.Data = SerializeToBytesUsingMarshal(currentData.Memory.Address, out _); } else { if (currentData.Value is Array array) { currentData.Data = SerializeArrayToBytes(array, out size); currentData.Size = size; } else { currentData.Data = SerializeToBytesUsingMarshal(currentData.Value, out size); currentData.Size = size; } } dataList.Add(currentData); } int totalSize = dataList.Sum(x => x.Size); int currentPosition = 0; byte[] bytes = new byte[totalSize]; foreach (ParamData data in dataList.OrderBy(x => x.Position)) { Array.Copy(data.Data, 0, bytes, currentPosition, data.Size); currentPosition += data.Size; } return new AllocatedMemory(Process, IntPtr.Zero, bytes, totalSize); } public AllocatedMemory SerializeUsingMarshal(T param) { byte[] bytes = SerializeToBytesUsingMarshal(param, out int size); AllocatedMemory memory = new AllocatedMemory(Process, IntPtr.Zero, bytes, size); return memory; } protected byte[] SerializeArrayToBytes(Array array, out int size) { int elementSize; try { elementSize = Marshal.SizeOf(array.GetType().GetElementType()); } catch (Exception exception) { throw new SerializationException("Only arrays of simple structures allowed.", exception); } size = elementSize * array.Length; IntPtr ptr = Marshal.AllocHGlobal(size); IntPtr current = ptr; foreach (object value in array) { Marshal.StructureToPtr(value, current, false); current += elementSize; } byte[] bytes = new byte[size]; Marshal.Copy(ptr, bytes, 0, size); Marshal.FreeHGlobal(ptr); return bytes; } protected byte[] SerializeToBytesUsingMarshal(T param, out int size) { size = Marshal.SizeOf(param); IntPtr ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(param, ptr, false); byte[] bytes = new byte[size]; Marshal.Copy(ptr, bytes, 0, size); Marshal.FreeHGlobal(ptr); return bytes; } private class ParamData { public int Position { get; set; } public ParamType ParamType { get; set; } public int Size { get; set; } public object Value { get; set; } public AllocatedMemory Memory { get; set; } public byte[] Data { get; set; } } } }