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