//
// Author: B4rtik (@b4rtik)
// Project: RedPeanut (https://github.com/b4rtik/RedPeanut)
// License: BSD 3-Clause
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
namespace SharpMiniDump
{
public class CustomLoadLibrary
{
///
/// 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)
{
Natives.UNICODE_STRING uModuleName = new Natives.UNICODE_STRING();
Natives.RtlInitUnicodeString(ref uModuleName, DLLPath);
IntPtr hModule = IntPtr.Zero;
Natives.NTSTATUS CallResult = (Natives.NTSTATUS)Natives.LdrLoadDll(IntPtr.Zero, 0, ref uModuleName, ref hModule);
if (CallResult != Natives.NTSTATUS.Success || hModule == IntPtr.Zero)
{
return IntPtr.Zero;
}
return hModule;
}
public static IntPtr GetDllAddress(string DLLName, bool CanLoadFromDisk = false)
{
IntPtr 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 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.
/// IntPtr for the desired function.
public static IntPtr GetLibraryAddress(string DLLName, string FunctionName, bool CanLoadFromDisk = false)
{
IntPtr 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);
}
///
/// 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.
///
/// 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)
{
ProcessModuleCollection ProcModules = Process.GetCurrentProcess().Modules;
foreach (ProcessModule Mod in ProcModules)
{
if (Mod.FileName.ToLower().EndsWith(DLLName.ToLower()))
{
return Mod.BaseAddress;
}
}
return IntPtr.Zero;
}
///
/// Given a module base address, resolve the address of a function by manually walking the module export table.
///
/// Ruben Boonen (@FuzzySec)
/// A pointer to the base address where the module is loaded in the current process.
/// The name of the export to search for (e.g. "NtAlertResumeThread").
/// IntPtr for the desired function.
public static IntPtr GetExportAddress(IntPtr ModuleBase, string ExportName)
{
IntPtr FunctionPtr = IntPtr.Zero;
try
{
// Traverse the PE header in memory
Int32 PeHeader = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + 0x3C));
Int16 OptHeaderSize = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + PeHeader + 0x14));
Int64 OptHeader = ModuleBase.ToInt64() + PeHeader + 0x18;
Int16 Magic = Marshal.ReadInt16((IntPtr)OptHeader);
Int64 pExport = 0;
if (Magic == 0x010b)
{
pExport = OptHeader + 0x60;
}
else
{
pExport = OptHeader + 0x70;
}
// Read -> IMAGE_EXPORT_DIRECTORY
Int32 ExportRVA = Marshal.ReadInt32((IntPtr)pExport);
Int32 OrdinalBase = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x10));
Int32 NumberOfFunctions = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x14));
Int32 NumberOfNames = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x18));
Int32 FunctionsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x1C));
Int32 NamesRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x20));
Int32 OrdinalsRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + ExportRVA + 0x24));
// Loop the array of export name RVA's
for (int i = 0; i < NumberOfNames; i++)
{
String FunctionName = Marshal.PtrToStringAnsi((IntPtr)(ModuleBase.ToInt64() + Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + NamesRVA + i * 4))));
if (FunctionName.ToLower() == ExportName.ToLower())
{
Int32 FunctionOrdinal = Marshal.ReadInt16((IntPtr)(ModuleBase.ToInt64() + OrdinalsRVA + i * 2)) + OrdinalBase;
Int32 FunctionRVA = Marshal.ReadInt32((IntPtr)(ModuleBase.ToInt64() + FunctionsRVA + (4 * (FunctionOrdinal - OrdinalBase))));
FunctionPtr = (IntPtr)((Int64)ModuleBase + FunctionRVA);
break;
}
}
}
catch
{
// Catch parser failure
throw new InvalidOperationException("Failed to parse module exports.");
}
if (FunctionPtr == IntPtr.Zero)
{
// Export not found
throw new MissingMethodException(ExportName + ", export not found.");
}
return FunctionPtr;
}
}
}