using ProcessInjection.Native;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static ProcessInjection.Native.Enum;
using static ProcessInjection.Utils.Utils;
using static ProcessInjection.DInvoke.Native;
namespace ProcessInjection.DInvoke
{
public class DynamicInvoke
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr ReadGs();
private static byte[] _x64SyscallStub =
{
0x49, 0x89, 0xCA, // mov r10, rcx
0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, ssn
0x0F, 0x05, // syscall
0xC3 // ret
};
///
/// Dynamically invoke an arbitrary function from a DLL, providing its name, function prototype, and arguments.
///
/// The Wover (@TheRealWover)
/// Name of the DLL.
/// Name of the function.
/// Prototype for the function, represented as a Delegate object.
/// Parameters to pass to the function. Can be modified if function uses call by reference.
/// Whether the DLL may be loaded from disk if it is not already loaded. Default is false.
/// Whether or not to resolve export forwards. Default is true.
/// Object returned by the function. Must be unmarshalled by the caller.
public static object DynamicApiInvoke(string dllName, string functionName, Type functionDelegateType, ref object[] parameters, bool canLoadFromDisk = false, bool resolveForwards = true)
{
var pFunction = GetLibraryAddress(dllName, functionName, canLoadFromDisk, resolveForwards);
return DynamicFunctionInvoke(pFunction, functionDelegateType, ref parameters);
}
///
/// Dynamically invokes an arbitrary function from a pointer. Useful for manually mapped modules or loading/invoking unmanaged code from memory.
///
/// The Wover (@TheRealWover)
/// A pointer to the unmanaged function.
/// Prototype for the function, represented as a Delegate object.
/// Arbitrary set of parameters to pass to the function. Can be modified if function uses call by reference.
/// Object returned by the function. Must be unmarshalled by the caller.
public static object DynamicFunctionInvoke(IntPtr functionPointer, Type functionDelegateType, ref object[] parameters)
{
var funcDelegate = Marshal.GetDelegateForFunctionPointer(functionPointer, functionDelegateType);
return funcDelegate.DynamicInvoke(parameters);
}
public static object DynamicAsmInvoke(byte[] asmStub, Type functionDelegateType, ref object[] parameters)
{
unsafe
{
fixed (byte* buffer = asmStub)
{
var ptr = (IntPtr)buffer;
var size = new IntPtr(asmStub.Length);
var oldProtect = NtProtectVirtualMemory(new IntPtr(-1), ref ptr,
ref size, Constants.PAGE_EXECUTE_READWRITE);
var result = DynamicFunctionInvoke(ptr, functionDelegateType, ref parameters);
NtProtectVirtualMemory(new IntPtr(-1), ref ptr,
ref size, oldProtect);
return result;
}
}
}
///
/// Resolves LdrLoadDll and uses that function to load a DLL from disk.
///
/// Ruben Boonen (@FuzzySec)
/// The path to the DLL on disk. Uses the LoadLibrary convention.
/// IntPtr base address of the loaded module or IntPtr.Zero if the module was not loaded successfully.
public static IntPtr LoadModuleFromDisk(string dllPath)
{
var uModuleName = new Structs.UNICODE_STRING();
Native.RtlInitUnicodeString(ref uModuleName, dllPath);
var hModule = IntPtr.Zero;
var callResult = Native.LdrLoadDll(IntPtr.Zero, 0, ref uModuleName, ref hModule);
if (callResult != NTSTATUS.Success || hModule == IntPtr.Zero)
return IntPtr.Zero;
return hModule;
}
///
/// Helper for getting the pointer to a function from a DLL loaded by the process.
///
/// Ruben Boonen (@FuzzySec)
/// The name of the DLL (e.g. "ntdll.dll" or "C:\Windows\System32\ntdll.dll").
/// Name of the exported procedure.
/// Optional, indicates if the function can try to load the DLL from disk if it is not found in the loaded module list.
/// Whether or not to resolve export forwards. Default is true.
/// IntPtr for the desired function.
public static IntPtr GetLibraryAddress(string dllName, string functionName, bool canLoadFromDisk = false, bool resolveForwards = true)
{
var hModule = GetLoadedModuleAddress(dllName);
if (hModule == IntPtr.Zero && canLoadFromDisk)
{
hModule = LoadModuleFromDisk(dllName);
if (hModule == IntPtr.Zero)
throw new FileNotFoundException(dllName + ", unable to find the specified file.");
}
else if (hModule == IntPtr.Zero)
{
throw new DllNotFoundException(dllName + ", Dll was not found.");
}
return GetExportAddress(hModule, functionName, resolveForwards);
}
///
/// Helper for getting the pointer to a function from a DLL loaded by the process.
///
/// Ruben Boonen (@FuzzySec)
/// The name of the DLL (e.g. "ntdll.dll" or "C:\Windows\System32\ntdll.dll").
/// Ordinal of the exported procedure.
/// Optional, indicates if the function can try to load the DLL from disk if it is not found in the loaded module list.
/// Whether or not to resolve export forwards. Default is true.
/// IntPtr for the desired function.
public static IntPtr GetLibraryAddress(string dllName, short ordinal, bool canLoadFromDisk = false, bool resolveForwards = true)
{
var hModule = GetLoadedModuleAddress(dllName);
if (hModule == IntPtr.Zero && canLoadFromDisk)
{
hModule = LoadModuleFromDisk(dllName);
if (hModule == IntPtr.Zero)
throw new FileNotFoundException(dllName + ", unable to find the specified file.");
}
else if (hModule == IntPtr.Zero)
{
throw new DllNotFoundException(dllName + ", Dll was not found.");
}
return GetExportAddress(hModule, ordinal, resolveForwards);
}
///
/// Helper for getting the pointer to a function from a DLL loaded by the process.
///
/// Ruben Boonen (@FuzzySec)
/// The name of the DLL (e.g. "ntdll.dll" or "C:\Windows\System32\ntdll.dll").
/// Hash of the exported procedure.
/// 64-bit integer to initialize the keyed hash object (e.g. 0xabc or 0x1122334455667788).
/// Optional, indicates if the function can try to load the DLL from disk if it is not found in the loaded module list.
/// Whether or not to resolve export forwards. Default is true.
/// IntPtr for the desired function.
public static IntPtr GetLibraryAddress(string dllName, string functionHash, long key, bool canLoadFromDisk = false, bool resolveForwards = true)
{
var hModule = GetLoadedModuleAddress(dllName);
if (hModule == IntPtr.Zero && canLoadFromDisk)
{
hModule = LoadModuleFromDisk(dllName);
if (hModule == IntPtr.Zero)
throw new FileNotFoundException(dllName + ", unable to find the specified file.");
}
else if (hModule == IntPtr.Zero)
{
throw new DllNotFoundException(dllName + ", Dll was not found.");
}
return GetExportAddress(hModule, functionHash, key, resolveForwards);
}
///
/// Helper for getting the base address of a module loaded by the current process. This base
/// address could be passed to GetProcAddress/LdrGetProcedureAddress or it could be used for
/// manual export parsing. This function uses the .NET System.Diagnostics.Process class.
///
/// Ruben Boonen (@FuzzySec)
/// The name of the DLL (e.g. "ntdll.dll").
/// IntPtr base address of the loaded module or IntPtr.Zero if the module is not found.
public static IntPtr GetLoadedModuleAddress(string dllName)
{
var process = Process.GetCurrentProcess();
foreach (ProcessModule module in process.Modules)
{
if (module.ModuleName.Equals(dllName, StringComparison.OrdinalIgnoreCase))
return module.BaseAddress;
}
return IntPtr.Zero;
}
///
/// Helper for getting the base address of a module loaded by the current process. This base
/// address could be passed to GetProcAddress/LdrGetProcedureAddress or it could be used for
/// manual export parsing. This function uses the .NET System.Diagnostics.Process class.
///
/// Hash of the DLL name.
/// 64-bit integer to initialize the keyed hash object (e.g. 0xabc or 0x1122334455667788).
/// IntPtr base address of the loaded module or IntPtr.Zero if the module is not found.
public static IntPtr GetLoadedModuleAddress(string hashedDllName, long key)
{
var process = Process.GetCurrentProcess();
foreach (ProcessModule module in process.Modules)
{
var hashedName = GetApiHash(module.ModuleName, key);
if (hashedName.Equals(hashedDllName))
return module.BaseAddress;
}
return IntPtr.Zero;
}
///
/// This function uses dynamic assembly invocation to obtain a pointer to the PEB.
/// __readgsqword(0x60) or __readfsdword(0x30)
///
/// Base address of the PEB as an IntPtr.
public static IntPtr GetPebAddress()
{
byte[] stub;
if (IntPtr.Size == 8)
{
stub = new byte[]
{
0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, // mov rax, qword ptr gs:[0x60]
0x00, 0x00, 0x00,
0xc3 // ret
};
}
else
{
stub = new byte[]
{
0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, // mov eax,dword ptr fs:[30]
0xC3 // ret
};
}
var parameters = Array.Empty