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