~ruther/csharp-dll-injector

a5cb4605d7b28142f2ae1ab9d8e0f7091530e7e5 — František Boháček 4 years ago d0dac92
Add DllUtils
A DllUtils/Attributes/CustomFunctionParamsAttribute.cs => DllUtils/Attributes/CustomFunctionParamsAttribute.cs +31 -0
@@ 0,0 1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Attributes
{
    /// <summary>
    /// Use this attribute to specify custom serialization to remote process.
    /// This is needed for example for arrays that should be given by ref.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
    public class CustomFunctionParamsAttribute : Attribute
    {
        public CustomFunctionParamsAttribute(EncodingType stringEncoding = EncodingType.Unicode)
        {
            StringEncoding = stringEncoding;
        }

        public EncodingType StringEncoding { get; set; }
    }

    public enum EncodingType
    {
        ASCII,
        Unicode,
        UTF8,
        UTF32
    }
}

A DllUtils/Attributes/ParamPositionAttribute.cs => DllUtils/Attributes/ParamPositionAttribute.cs +29 -0
@@ 0,0 1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Attributes
{
    public enum ParamType
    {
        NotSpecified,
        Reference,
        Value
    }

    [AttributeUsage(AttributeTargets.Field)]
    public class ParamPositionAttribute : Attribute
    {
        public ParamPositionAttribute(int index, ParamType paramType = ParamType.NotSpecified)
        {
            Index = index;
            ParamType = paramType;
        }

        public ParamType ParamType { get; set; }

        public int Index { get; set; }
    }
}

A DllUtils/DllUtils.csproj => DllUtils/DllUtils.csproj +109 -0
@@ 0,0 1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{70B2AF2E-BFD2-48C9-9D13-18053F26E710}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>DllUtils</RootNamespace>
    <AssemblyName>DllUtils</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x64\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
    <OutputPath>bin\x64\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x64</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
    <DebugSymbols>true</DebugSymbols>
    <OutputPath>bin\x86\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <DebugType>full</DebugType>
    <PlatformTarget>x86</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
    <OutputPath>bin\x86\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <Optimize>true</Optimize>
    <DebugType>pdbonly</DebugType>
    <PlatformTarget>x86</PlatformTarget>
    <LangVersion>7.3</LangVersion>
    <ErrorReport>prompt</ErrorReport>
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Attributes\CustomFunctionParamsAttribute.cs" />
    <Compile Include="Attributes\ParamPositionAttribute.cs" />
    <Compile Include="Exceptions\MemoryAllocationException.cs" />
    <Compile Include="Exceptions\MemoryWriteException.cs" />
    <Compile Include="Exceptions\SerializeException.cs" />
    <Compile Include="Exceptions\ThreadCreationException.cs" />
    <Compile Include="Exceptions\FunctionException.cs" />
    <Compile Include="Exceptions\ModuleException.cs" />
    <Compile Include="Exceptions\OpenHandleException.cs" />
    <Compile Include="Exceptions\InjectionException.cs" />
    <Compile Include="Injector.cs" />
    <Compile Include="Interop\Kernel32.cs" />
    <Compile Include="Memory\AllocatedMemory.cs" />
    <Compile Include="Memory\FunctionResult.cs" />
    <Compile Include="Modules\IModule.cs" />
    <Compile Include="Modules\InjectedModule.cs" />
    <Compile Include="Modules\RemoteModule.cs" />
    <Compile Include="Process\ProcessHandle.cs" />
    <Compile Include="Process\RemoteProcessHandle.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Serializers\RemoteParamSerializer.cs" />
    <Compile Include="Threads\RemoteThread.cs" />
  </ItemGroup>
  <ItemGroup />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file

A DllUtils/Exceptions/FunctionException.cs => DllUtils/Exceptions/FunctionException.cs +28 -0
@@ 0,0 1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class FunctionException : InjectionException
    {
        public FunctionException()
        {
        }

        public FunctionException(string message) : base(message)
        {
        }

        public FunctionException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected FunctionException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

A DllUtils/Exceptions/InjectionException.cs => DllUtils/Exceptions/InjectionException.cs +31 -0
@@ 0,0 1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class InjectionException : Exception
    {
        public InjectionException()
        {
        }

        public InjectionException(string message) : base(message)
        {
        }

        public InjectionException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected InjectionException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }

        public int LatestError => Marshal.GetLastWin32Error();
    }
}

A DllUtils/Exceptions/MemoryAllocationException.cs => DllUtils/Exceptions/MemoryAllocationException.cs +28 -0
@@ 0,0 1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class MemoryAllocationException : InjectionException
    {
        public MemoryAllocationException()
        {
        }

        public MemoryAllocationException(string message) : base(message)
        {
        }

        public MemoryAllocationException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected MemoryAllocationException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

A DllUtils/Exceptions/MemoryWriteException.cs => DllUtils/Exceptions/MemoryWriteException.cs +28 -0
@@ 0,0 1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class MemoryWriteException : InjectionException
    {
        public MemoryWriteException()
        {
        }

        public MemoryWriteException(string message) : base(message)
        {
        }

        public MemoryWriteException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected MemoryWriteException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

A DllUtils/Exceptions/ModuleException.cs => DllUtils/Exceptions/ModuleException.cs +28 -0
@@ 0,0 1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class ModuleException : InjectionException
    {
        public ModuleException()
        {
        }

        public ModuleException(string message) : base(message)
        {
        }

        public ModuleException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected ModuleException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

A DllUtils/Exceptions/OpenHandleException.cs => DllUtils/Exceptions/OpenHandleException.cs +28 -0
@@ 0,0 1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class OpenHandleException : InjectionException
    {
        public OpenHandleException()
        {
        }

        public OpenHandleException(string message) : base(message)
        {
        }

        public OpenHandleException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected OpenHandleException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

A DllUtils/Exceptions/SerializeException.cs => DllUtils/Exceptions/SerializeException.cs +28 -0
@@ 0,0 1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class SerializeException : InjectionException
    {
        public SerializeException()
        {
        }

        public SerializeException(string message) : base(message)
        {
        }

        public SerializeException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected SerializeException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

A DllUtils/Exceptions/ThreadCreationException.cs => DllUtils/Exceptions/ThreadCreationException.cs +28 -0
@@ 0,0 1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Exceptions
{
    public class ThreadCreationException : InjectionException
    {
        public ThreadCreationException()
        {
        }

        public ThreadCreationException(string message) : base(message)
        {
        }

        public ThreadCreationException(string message, Exception innerException) : base(message, innerException)
        {
        }

        protected ThreadCreationException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }
    }
}

A DllUtils/Injector.cs => DllUtils/Injector.cs +75 -0
@@ 0,0 1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Modules;
using DllUtils.Process;

namespace DllUtils
{
    public static class Injector
    {
        /// <summary>
        /// Returns RemoteProcessHandle that can be used to Inject dll
        /// </summary>
        /// <param name="processName"></param>
        /// <returns></returns>
        public static RemoteProcessHandle GetRemoteProcess(string processName)
        {
            return GetRemoteProcess(System.Diagnostics.Process.GetProcessesByName(processName).FirstOrDefault());
        }

        /// <summary>
        /// Returns RemoteProcessHandle that can be used to Inject dll
        /// </summary>
        /// <param name="processId"></param>
        /// <returns></returns>
        public static RemoteProcessHandle GetRemoteProcess(int processId)
        {
            return GetRemoteProcess(System.Diagnostics.Process.GetProcessById(processId));
        }

        /// <summary>
        /// Returns RemoteProcessHandle that can be used to Inject dll
        /// </summary>
        /// <param name="process"></param>
        /// <returns></returns>
        public static RemoteProcessHandle GetRemoteProcess(System.Diagnostics.Process process)
        {
            if (process == null)
            {
                return null;
            }

            return new RemoteProcessHandle(process);
        }

        /// <summary>
        /// Injects dll into process using its name
        /// </summary>
        /// <param name="processName"></param>
        /// <param name="dllPath"></param>
        /// <returns></returns>
        public static InjectedModule Inject(string processName, string dllPath)
            => GetRemoteProcess(processName).Inject(dllPath);

        /// <summary>
        /// Injects dll into process using its id
        /// </summary>
        /// <param name="processId"></param>
        /// <param name="dllPath"></param>
        /// <returns></returns>
        public static InjectedModule Inject(int processId, string dllPath)
            => GetRemoteProcess(processId).Inject(dllPath);

        /// <summary>
        /// Injects dll into process using System.Diagnostics.Process
        /// </summary>
        /// <param name="process"></param>
        /// <param name="dllPath"></param>
        /// <returns></returns>
        public static InjectedModule Inject(System.Diagnostics.Process process, string dllPath)
            => GetRemoteProcess(process).Inject(dllPath);
    }
}

A DllUtils/Interop/Kernel32.cs => DllUtils/Interop/Kernel32.cs +55 -0
@@ 0,0 1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace DllUtils.Interop
{
    public static class Kernel32
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(uint dwDesiredAccess, int bInheritHandle, uint dwProcessId);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int CloseHandle(IntPtr hObject);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LoadLibraryA(string lpModuleName);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr GetModuleHandleA(string lpModuleName);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, out int lpNumberOfBytesWritten);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, out int lpNumberOfBytesRead);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttribute, IntPtr dwStackSize, IntPtr lpStartAddress,
            IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        public static extern Int32 WaitForSingleObject(IntPtr handle, Int32 milliseconds);


        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        public static extern bool VirtualFreeEx(
            IntPtr hProcess,  IntPtr lpAddress,  UIntPtr dwSize,  uint dwFreeType
        );

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        public static extern bool GetExitCodeThread(IntPtr hThread, out IntPtr lpExitCode);
    }
}

A DllUtils/Memory/AllocatedMemory.cs => DllUtils/Memory/AllocatedMemory.cs +72 -0
@@ 0,0 1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Interop;
using DllUtils.Process;

namespace DllUtils.Memory
{
    public class AllocatedMemory
    {
        public AllocatedMemory(ProcessHandle process, IntPtr address, byte[] data, int length)
        {
            Process = process;
            Address = address;
            Data = data;
            Length = length;
        }

        public ProcessHandle Process { get; }

        public bool IsAllocated { get; private set; }
        public bool IsFreed { get; private set; }

        public byte[] Data { get; }

        public int Length { get; }

        public IntPtr Address { get; private set; }

        public bool Alloc()
        {
            if (!IsAllocated)
            {
                Address = Kernel32.VirtualAllocEx(Process.Handle, (IntPtr) null, (IntPtr) Length, 0x1000, 0x40);

                if (Address != IntPtr.Zero)
                {
                    IsAllocated = true;
                }
            }

            return Address != IntPtr.Zero;
        }

        public bool Write()
        {
            if (!IsAllocated)
            {
                return false;
            }

            if (Kernel32.WriteProcessMemory(Process.Handle, Address, Data, (uint) Length, out int bytesWritten) == 0)
            {
                return false;
            }

            return bytesWritten == Length;
        }

        public void Free()
        {
            if (!IsFreed)
            {
                IsFreed = Kernel32.VirtualFreeEx(Process.Handle, Address, UIntPtr.Zero, 0x8000);
                IsAllocated = !IsFreed;
                Address = IntPtr.Zero;
            }
        }
    }
}

A DllUtils/Memory/FunctionResult.cs => DllUtils/Memory/FunctionResult.cs +48 -0
@@ 0,0 1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Exceptions;
using DllUtils.Interop;
using DllUtils.Process;

namespace DllUtils.Memory
{
    public class FunctionResult
    {
        public FunctionResult(ProcessHandle process, IntPtr address)
        {
            Process = process;
            Address = address;
        }

        public ProcessHandle Process { get; }

        public IntPtr Address { get; }

        public T To<T>(bool reference = true)
        {
            if (default(T) != null || !reference)
            {
                return (T) Convert.ChangeType((int)Address, typeof(T));
            }

            int size = Marshal.SizeOf(typeof(T));
            byte[] bytes = new byte[size];
            Kernel32.ReadProcessMemory(Process.Handle, Address, bytes, (uint)size, out int bytesRead);
            GCHandle gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);

            if (bytesRead != size)
            {
                throw new FunctionException("Whole function result could not be read.");
            }

            T obj = Marshal.PtrToStructure<T>(gcHandle.AddrOfPinnedObject());
            gcHandle.Free();

            return obj;
        }
    }
}

A DllUtils/Modules/IModule.cs => DllUtils/Modules/IModule.cs +34 -0
@@ 0,0 1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Memory;

namespace DllUtils.Modules
{
    public interface IModule
    {
        IntPtr LocalHandle { get; }

        IntPtr RemoteHandle { get; }

        IntPtr FindFunctionHandle(string name);

        FunctionResult ExecuteFunction(IntPtr function);

        FunctionResult ExecuteFunction<T>(IntPtr function, T param);

        FunctionResult ExecuteFunction(string function);

        FunctionResult ExecuteFunction<T>(string function, T param);

        Task<IntPtr> FindFunctionHandleAsync(string name);

        Task<FunctionResult> ExecuteFunctionAsync(IntPtr function);

        Task<FunctionResult> ExecuteFunctionAsync(string function);

        Task<FunctionResult> ExecuteFunctionAsync<T>(string function, T param);
    }
}

A DllUtils/Modules/InjectedModule.cs => DllUtils/Modules/InjectedModule.cs +23 -0
@@ 0,0 1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Interop;
using DllUtils.Process;

namespace DllUtils.Modules
{
    public class InjectedModule : RemoteModule
    {
        public InjectedModule(IntPtr remoteHandle, IntPtr localHandle, RemoteProcessHandle processHandle, string dllPath)
            : base(remoteHandle, localHandle, processHandle)
        {
            DllPath = dllPath;
        }

        public string DllPath { get; }


    }
}

A DllUtils/Modules/RemoteModule.cs => DllUtils/Modules/RemoteModule.cs +134 -0
@@ 0,0 1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Exceptions;
using DllUtils.Interop;
using DllUtils.Memory;
using DllUtils.Process;
using DllUtils.Serializers;
using DllUtils.Threads;

namespace DllUtils.Modules
{
    public class RemoteModule : IModule
    {
        public RemoteModule(IntPtr remoteHandle, IntPtr localHandle, RemoteProcessHandle processHandle)
        {
            Process = processHandle;
            RemoteHandle = remoteHandle;
            LocalHandle = localHandle;
        }

        public IntPtr LocalHandle { get; }

        public IntPtr RemoteHandle { get; }

        public RemoteProcessHandle Process { get; }

        public virtual IntPtr FindFunctionHandle(string name)
        {
            Process.Open();

            IntPtr localAddress = Kernel32.GetProcAddress(LocalHandle, name);
            long functionOffset = ((long)localAddress) - ((long)LocalHandle);
            long remoteAddressLong = ((long)RemoteHandle) + functionOffset;
            IntPtr remoteAddress = new IntPtr(remoteAddressLong);

            if (remoteAddress == IntPtr.Zero)
            { 
                throw new FunctionException($"Function {name} not found.");
            }

            return remoteAddress;
        }

        public virtual FunctionResult ExecuteFunction(IntPtr function)
        {
            return ExecuteFunctionRaw(function, null);
        }

        public virtual FunctionResult ExecuteFunction<T>(IntPtr function, T param)
        {
            return ExecuteFunctionRaw(function, GetParam(param));
        }

        public virtual FunctionResult ExecuteFunction(string function)
        {
            return ExecuteFunction(FindFunctionHandle(function));
        }

        public virtual FunctionResult ExecuteFunction<T>(string function, T param)
        {
            return ExecuteFunction(FindFunctionHandle(function), param);
        }

        public Task<IntPtr> FindFunctionHandleAsync(string name)
            => new Task<IntPtr>(() => FindFunctionHandle(name));

        public Task<FunctionResult> ExecuteFunctionAsync(IntPtr function)
            => new Task<FunctionResult>(() => ExecuteFunction(function));

        public Task<FunctionResult> ExecuteFunctionAsync(string function)
            => new Task<FunctionResult>(() => ExecuteFunction(function));

        public Task<FunctionResult> ExecuteFunctionAsync<T>(string function, T param)
            => new Task<FunctionResult>(() => ExecuteFunction(function, param));

        public virtual ParamData GetParam<T>(T param)
        {
            RemoteParamSerializer serializer = new RemoteParamSerializer(Process);

            if (param.GetType().IsPrimitive)
            {
                return new ParamData
                {
                    IsRemote = false,
                    Value = Marshal.ReadIntPtr(param, 0)
                };
            }

            AllocatedMemory memory = serializer.Serialize(param);
            return new ParamData
            {
                Allocated = memory,
                IsRemote = true,
                Value = memory.Address
            };
        }

        public virtual FunctionResult ExecuteFunctionRaw(IntPtr function, ParamData param)
        {
            Process.Open();
            RemoteThread thread = new RemoteThread(Process, function, param?.Value ?? IntPtr.Zero, IntPtr.Zero);

            if (!thread.Create())
            {
                throw new ThreadCreationException("Could not create remote thread. Check if architecture of the remote process matches local process");
            }

            thread.WaitForResult(10 * 1000);
            thread.CheckResult();

            FunctionResult result = new FunctionResult(Process, thread.GetResult());

            if (param?.IsRemote ?? false)
            {
                param.Allocated.Free();
            }

            return result;
        }

        public class ParamData
        {
            public bool IsRemote { get; set; }

            public IntPtr Value { get; set; }

            public AllocatedMemory Allocated { get; set; }
        }
    }
}

A DllUtils/Process/ProcessHandle.cs => DllUtils/Process/ProcessHandle.cs +54 -0
@@ 0,0 1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Exceptions;
using DllUtils.Interop;

namespace DllUtils.Process
{
    public class ProcessHandle
    {
        public ProcessHandle(System.Diagnostics.Process process)
        {
            Process = process;
        }

        public IntPtr Handle { get; private set; }

        public bool IsOpened { get; private set; }

        public System.Diagnostics.Process Process { get; }

        public string ProcessName => Process.ProcessName;

        public int ProcessId => Process.Id;

        public void Close()
        {
            if (Handle != IntPtr.Zero && IsOpened)
            {
                Kernel32.CloseHandle(Handle);
                Handle = IntPtr.Zero;
            }

            IsOpened = false;
        }

        public void Open()
        {
            if (!IsOpened || Handle == IntPtr.Zero)
            {
                Handle = Kernel32.OpenProcess((0x2 | 0x8 | 0x10 | 0x20 | 0x400), 1, (uint)ProcessId);

                if (Handle == IntPtr.Zero)
                {
                    throw new OpenHandleException($"Failed to open process handle for {ProcessName}");
                }
            }

            IsOpened = true;
        }
    }
}

A DllUtils/Process/RemoteProcessHandle.cs => DllUtils/Process/RemoteProcessHandle.cs +55 -0
@@ 0,0 1,55 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Exceptions;
using DllUtils.Interop;
using DllUtils.Modules;

namespace DllUtils.Process
{
    public class RemoteProcessHandle : ProcessHandle
    {
        public RemoteProcessHandle(System.Diagnostics.Process process) : base(process)
        {
        }

        public InjectedModule Inject(string dllPath)
        {
            Open();

            RemoteModule kernel32 = GetRemoteKernel32();
            string fullPath = Path.GetFullPath(dllPath);
            IntPtr loadedLibrary = kernel32.ExecuteFunction("LoadLibraryA", Encoding.ASCII.GetBytes(fullPath)).Address;

            if (loadedLibrary == IntPtr.Zero)
            {
                int error = Marshal.GetLastWin32Error();
                ;
                throw new ModuleException($"Failed to inject {dllPath} library.");
            }

            IntPtr localHandle = Kernel32.LoadLibraryA(fullPath);
            if (localHandle == IntPtr.Zero)
            {
                throw new ModuleException($"Failed to load {fullPath} to local process.");
            }

            return new InjectedModule(loadedLibrary, localHandle, this, dllPath);
        }

        public RemoteModule GetRemoteKernel32()
        {
            IntPtr kernel32 = Kernel32.GetModuleHandle("kernel32.dll");
            if (kernel32 == IntPtr.Zero)
            {
                throw new ModuleException("Failed to load kernel32.dll.");
            }

            return new RemoteModule(kernel32, kernel32, this);
        }
    }
}

A DllUtils/Properties/AssemblyInfo.cs => DllUtils/Properties/AssemblyInfo.cs +36 -0
@@ 0,0 1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DllUtils")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DllUtils")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a paramType in this assembly from
// COM, set the ComVisible attribute to true on that paramType.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("70b2af2e-bfd2-48c9-9d13-18053f26e710")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

A DllUtils/Serializers/RemoteParamSerializer.cs => DllUtils/Serializers/RemoteParamSerializer.cs +217 -0
@@ 0,0 1,217 @@
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>(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<CustomFunctionParamsAttribute>() 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>(T param, CustomFunctionParamsAttribute customOptions)
        {
            System.Type type = param.GetType();
            IEnumerable<FieldInfo> fields = type.GetFields();
            List<ParamData> dataList = new List<ParamData>();

            foreach (FieldInfo field in fields)
            {
                ParamPositionAttribute positionAttribute = field.GetCustomAttribute<ParamPositionAttribute>();

                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>(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>(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; }
        }
    }
}

A DllUtils/Threads/RemoteThread.cs => DllUtils/Threads/RemoteThread.cs +108 -0
@@ 0,0 1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using DllUtils.Interop;
using DllUtils.Process;

namespace DllUtils.Threads
{
    public class RemoteThread
    {
        private int _waitForSingleObjectResult;
        private bool _waitedForObject;

        public RemoteThread(ProcessHandle process, IntPtr startAddress, IntPtr param, IntPtr handle)
        {
            Handle = handle;
            if (handle != IntPtr.Zero)
            {
                IsOpened = true;
            }

            StartAddress = startAddress;
            Param = param;
            Handle = handle;
            Process = process;
        }

        public bool IsOpened { get; private set; }

        public IntPtr Handle { get; private set; }

        public IntPtr StartAddress { get; }

        public IntPtr Param { get; }

        public ProcessHandle Process { get; }


        public bool Create()
        {
            if (!IsOpened)
            {
                Process.Open();

                Handle = Kernel32.CreateRemoteThread(Process.Handle, (IntPtr) null, IntPtr.Zero, StartAddress, Param, 0,
                    (IntPtr) null);

                int error = Marshal.GetLastWin32Error();

                if (Handle != IntPtr.Zero)
                {
                    IsOpened = true;
                }
            }

            return Handle != IntPtr.Zero;
        }

        public int WaitForResult(int timeout = 100000)
        {
            _waitForSingleObjectResult = Kernel32.WaitForSingleObject(Handle, timeout);
            _waitedForObject = true;

            return _waitForSingleObjectResult;
        }

        public bool CheckResult()
        {
            if (!_waitedForObject)
            {
                WaitForResult();
            }

            int result = _waitForSingleObjectResult;
            if (result == 0x00000080L || result == 0x00000102L || result == 0xFFFFFFF)
            { 
                Close();
                return false;
            }

            return true;
        }

        public IntPtr GetResult()
        {
            bool success = Kernel32.GetExitCodeThread(Handle, out IntPtr result);
            if (!success)
            {
                return IntPtr.Zero;
            }

            return result;
        }

        public void Close()
        {
            if (Handle != IntPtr.Zero)
            {
                Kernel32.CloseHandle(Handle);
                IsOpened = false;
                Handle = IntPtr.Zero;
            }
        }
    }
}

Do not follow this link