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(); return (IntPtr)DynamicAsmInvoke( stub, typeof(ReadGs), ref parameters); } /// /// 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 parses the _PEB_LDR_DATA structure. /// /// 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 GetPebLdrModuleEntry(string dllName) { // Set function variables uint ldrDataOffset; uint inLoadOrderModuleListOffset; if (IntPtr.Size == 4) { ldrDataOffset = 0xc; inLoadOrderModuleListOffset = 0xC; } else { ldrDataOffset = 0x18; inLoadOrderModuleListOffset = 0x10; } // Get _PEB pointer var pPeb = GetPebAddress(); // Get module InLoadOrderModuleList -> _LIST_ENTRY var pebLdrData = Marshal.ReadIntPtr((IntPtr)((ulong)pPeb + ldrDataOffset)); var pInLoadOrderModuleList = (IntPtr)((ulong)pebLdrData + inLoadOrderModuleListOffset); var le = (Structs.LIST_ENTRY)Marshal.PtrToStructure(pInLoadOrderModuleList, typeof(Structs.LIST_ENTRY)); // Loop entries var flink = le.Flink; var hModule = IntPtr.Zero; var dte = (PE.LDR_DATA_TABLE_ENTRY)Marshal.PtrToStructure(flink, typeof(PE.LDR_DATA_TABLE_ENTRY)); while (dte.InLoadOrderLinks.Flink != le.Blink) { // Match module name var moduleName = Marshal.PtrToStringUni(dte.BaseDllName.Buffer); if (!string.IsNullOrWhiteSpace(moduleName) && moduleName.Equals(dllName, StringComparison.OrdinalIgnoreCase)) { hModule = dte.DllBase; break; } // Move Ptr flink = dte.InLoadOrderLinks.Flink; dte = (PE.LDR_DATA_TABLE_ENTRY)Marshal.PtrToStructure(flink, typeof(PE.LDR_DATA_TABLE_ENTRY)); } return hModule; } /// /// Get a syscall stub for the given Nt* API by walking the PEB to find the correct SSN. /// The stub can be executed using DynamicAsmInvoke. /// x64 only. /// /// A pointer to the PEB. /// The function name to get the stub for (e.g. NtOpenProcess). /// A byte[] containing the stub. public static byte[] GetSyscallStub(IntPtr pPeb, string functionName) { // x64 only if (IntPtr.Size == 4) return Array.Empty(); const uint ldrDataOffset = 0x18; const uint inLoadOrderModuleListOffset = 0x10; var pebLdrData = Marshal.ReadIntPtr((IntPtr)((ulong)pPeb + ldrDataOffset)); var pInLoadOrderModuleList = (IntPtr)((ulong)pebLdrData + inLoadOrderModuleListOffset); var le = Marshal.PtrToStructure(pInLoadOrderModuleList); // loop modules var flink = le.Flink; var dte = Marshal.PtrToStructure(flink); while (dte.InLoadOrderLinks.Flink != le.Blink) { // match module name var moduleName = Marshal.PtrToStringUni(dte.BaseDllName.Buffer); if (!string.IsNullOrWhiteSpace(moduleName) && moduleName.Equals("ntdll.dll", StringComparison.OrdinalIgnoreCase)) { var export = GetExportAddress(dte.DllBase, functionName); var ssn = Marshal.ReadByte(export + 4); var stub = _x64SyscallStub; stub[4] = ssn; return stub; } // increment ptr flink = dte.InLoadOrderLinks.Flink; dte = Marshal.PtrToStructure(flink); } return Array.Empty(); } /// /// Get a syscall stub for the given Nt* API by walking the PEB to find the correct SSN. /// The stub can be executed using DynamicAsmInvoke. /// x64 only. /// /// A pointer to the PEB. /// The hashed function name to get the stub for. /// 64-bit integer to initialize the keyed hash object (e.g. 0xabc or 0x1122334455667788). /// A byte[] containing the stub. public static byte[] GetSyscallStub(IntPtr pPeb, string hashedFunctionName, long key) { // x64 only if (IntPtr.Size == 4) return Array.Empty(); const uint ldrDataOffset = 0x18; const uint inLoadOrderModuleListOffset = 0x10; var pebLdrData = Marshal.ReadIntPtr((IntPtr)((ulong)pPeb + ldrDataOffset)); var pInLoadOrderModuleList = (IntPtr)((ulong)pebLdrData + inLoadOrderModuleListOffset); var le = Marshal.PtrToStructure(pInLoadOrderModuleList); // loop modules var flink = le.Flink; var dte = Marshal.PtrToStructure(flink); while (dte.InLoadOrderLinks.Flink != le.Blink) { // match module name var moduleName = Marshal.PtrToStringUni(dte.BaseDllName.Buffer); if (!string.IsNullOrWhiteSpace(moduleName) && moduleName.Equals("ntdll.dll", StringComparison.OrdinalIgnoreCase)) { var export = GetExportAddress(dte.DllBase, hashedFunctionName, key); var ssn = Marshal.ReadByte(export + 4); var stub = _x64SyscallStub; stub[4] = ssn; return stub; } // increment ptr flink = dte.InLoadOrderLinks.Flink; dte = Marshal.PtrToStructure(flink); } return Array.Empty(); } /// /// 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"). /// Whether or not to resolve export forwards. Default is true. /// IntPtr for the desired function. public static IntPtr GetExportAddress(IntPtr moduleBase, string exportName, bool resolveForwards = true) { var functionPtr = IntPtr.Zero; try { // Traverse the PE header in memory var peHeader = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + 0x3C)); var optHeader = moduleBase.ToInt64() + peHeader + 0x18; var magic = Marshal.ReadInt16((IntPtr)optHeader); long pExport; if (magic == 0x010b) pExport = optHeader + 0x60; else pExport = optHeader + 0x70; var exportRva = Marshal.ReadInt32((IntPtr)pExport); var ordinalBase = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x10)); var numberOfNames = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x18)); var functionsRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x1C)); var namesRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x20)); var ordinalsRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x24)); for (var i = 0; i < numberOfNames; i++) { var functionName = Marshal.PtrToStringAnsi((IntPtr)(moduleBase.ToInt64() + Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + namesRva + i * 4)))); if (string.IsNullOrWhiteSpace(functionName)) continue; if (!functionName.Equals(exportName, StringComparison.OrdinalIgnoreCase)) continue; var functionOrdinal = Marshal.ReadInt16((IntPtr)(moduleBase.ToInt64() + ordinalsRva + i * 2)) + ordinalBase; var functionRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + functionsRva + 4 * (functionOrdinal - ordinalBase))); functionPtr = (IntPtr)((long)moduleBase + functionRva); if (resolveForwards) functionPtr = GetForwardAddress(functionPtr); break; } } catch { throw new InvalidOperationException("Failed to parse module exports."); } if (functionPtr == IntPtr.Zero) throw new MissingMethodException(exportName + ", export not found."); return functionPtr; } /// /// 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 ordinal number to search for (e.g. 0x136 -> ntdll!NtCreateThreadEx). /// Whether or not to resolve export forwards. Default is true. /// IntPtr for the desired function. public static IntPtr GetExportAddress(IntPtr moduleBase, short ordinal, bool resolveForwards = true) { var functionPtr = IntPtr.Zero; try { var peHeader = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + 0x3C)); var optHeader = moduleBase.ToInt64() + peHeader + 0x18; var magic = Marshal.ReadInt16((IntPtr)optHeader); long pExport; if (magic == 0x010b) pExport = optHeader + 0x60; else pExport = optHeader + 0x70; var exportRva = Marshal.ReadInt32((IntPtr)pExport); var ordinalBase = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x10)); var numberOfNames = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x18)); var functionsRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x1C)); var ordinalsRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x24)); for (var i = 0; i < numberOfNames; i++) { var functionOrdinal = Marshal.ReadInt16((IntPtr)(moduleBase.ToInt64() + ordinalsRva + i * 2)) + ordinalBase; if (functionOrdinal != ordinal) continue; var functionRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + functionsRva + 4 * (functionOrdinal - ordinalBase))); functionPtr = (IntPtr)((long)moduleBase + functionRva); if (resolveForwards) functionPtr = GetForwardAddress(functionPtr); break; } } catch { throw new InvalidOperationException("Failed to parse module exports."); } if (functionPtr == IntPtr.Zero) throw new MissingMethodException(ordinal + ", ordinal not found."); return functionPtr; } /// /// 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. /// Hash of the exported procedure. /// 64-bit integer to initialize the keyed hash object (e.g. 0xabc or 0x1122334455667788). /// Whether or not to resolve export forwards. Default is true. /// IntPtr for the desired function. public static IntPtr GetExportAddress(IntPtr moduleBase, string functionHash, long key, bool resolveForwards = true) { var functionPtr = IntPtr.Zero; try { var peHeader = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + 0x3C)); var optHeader = moduleBase.ToInt64() + peHeader + 0x18; var magic = Marshal.ReadInt16((IntPtr)optHeader); long pExport; if (magic == 0x010b) pExport = optHeader + 0x60; else pExport = optHeader + 0x70; var exportRva = Marshal.ReadInt32((IntPtr)pExport); var ordinalBase = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x10)); var numberOfNames = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x18)); var functionsRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x1C)); var namesRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x20)); var ordinalsRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + exportRva + 0x24)); for (var i = 0; i < numberOfNames; i++) { var functionName = Marshal.PtrToStringAnsi((IntPtr)(moduleBase.ToInt64() + Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + namesRva + i * 4)))); if (string.IsNullOrWhiteSpace(functionName)) continue; if (!GetApiHash(functionName, key).Equals(functionHash, StringComparison.OrdinalIgnoreCase)) continue; var functionOrdinal = Marshal.ReadInt16((IntPtr)(moduleBase.ToInt64() + ordinalsRva + i * 2)) + ordinalBase; var functionRva = Marshal.ReadInt32((IntPtr)(moduleBase.ToInt64() + functionsRva + 4 * (functionOrdinal - ordinalBase))); functionPtr = (IntPtr)((long)moduleBase + functionRva); if (resolveForwards) functionPtr = GetForwardAddress(functionPtr); break; } } catch { throw new InvalidOperationException("Failed to parse module exports."); } if (functionPtr == IntPtr.Zero) throw new MissingMethodException(functionHash + ", export hash not found."); return functionPtr; } /// /// Check if an address to an exported function should be resolved to a forward. If so, return the address of the forward. /// /// The Wover (@TheRealWover) /// Function of an exported address, found by parsing a PE file's export table. /// 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 forward. If the function is not forwarded, return the original pointer. public static IntPtr GetForwardAddress(IntPtr exportAddress, bool canLoadFromDisk = false) { var functionPtr = exportAddress; try { var forwardNames = Marshal.PtrToStringAnsi(functionPtr); if (string.IsNullOrWhiteSpace(forwardNames)) return functionPtr; var values = forwardNames.Split('.'); if (values.Length > 1) { var forwardModuleName = values[0]; var forwardExportName = values[1]; var apiSet = GetApiSetMapping(); var lookupKey = forwardModuleName.Substring(0, forwardModuleName.Length - 2) + ".dll"; if (apiSet.ContainsKey(lookupKey)) forwardModuleName = apiSet[lookupKey]; else forwardModuleName = forwardModuleName + ".dll"; var hModule = GetPebLdrModuleEntry(forwardModuleName); if (hModule == IntPtr.Zero && canLoadFromDisk) hModule = LoadModuleFromDisk(forwardModuleName); if (hModule != IntPtr.Zero) functionPtr = GetExportAddress(hModule, forwardExportName); } } catch { // Do nothing, it was not a forward } return functionPtr; } /// /// Given a module base address, resolve the address of a function by calling LdrGetProcedureAddress. /// /// 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 GetNativeExportAddress(IntPtr moduleBase, string exportName) { var aFunc = new Structs.ANSI_STRING { Length = (ushort)exportName.Length, MaximumLength = (ushort)(exportName.Length + 2), Buffer = Marshal.StringToCoTaskMemAnsi(exportName) }; var pAFunc = Marshal.AllocHGlobal(Marshal.SizeOf(aFunc)); Marshal.StructureToPtr(aFunc, pAFunc, true); var pFuncAddr = IntPtr.Zero; Native.LdrGetProcedureAddress(moduleBase, pAFunc, IntPtr.Zero, ref pFuncAddr); Marshal.FreeHGlobal(pAFunc); return pFuncAddr; } /// /// Given a module base address, resolve the address of a function by calling LdrGetProcedureAddress. /// /// Ruben Boonen (@FuzzySec) /// A pointer to the base address where the module is loaded in the current process. /// The ordinal number to search for (e.g. 0x136 -> ntdll!NtCreateThreadEx). /// IntPtr for the desired function. public static IntPtr GetNativeExportAddress(IntPtr moduleBase, short ordinal) { var pFuncAddr = IntPtr.Zero; var pOrd = (IntPtr)ordinal; Native.LdrGetProcedureAddress(moduleBase, IntPtr.Zero, pOrd, ref pFuncAddr); return pFuncAddr; } /// /// Retrieve PE header information from the module base pointer. /// /// Ruben Boonen (@FuzzySec) /// Pointer to the module base. /// PE.PE_META_DATA public static PE.PE_META_DATA GetPeMetaData(IntPtr pModule) { var peMetaData = new PE.PE_META_DATA(); try { var e_lfanew = (uint)Marshal.ReadInt32((IntPtr)((ulong)pModule + 0x3c)); peMetaData.Pe = (uint)Marshal.ReadInt32((IntPtr)((ulong)pModule + e_lfanew)); if (peMetaData.Pe != 0x4550) throw new InvalidOperationException("Invalid PE signature."); peMetaData.ImageFileHeader = (PE.IMAGE_FILE_HEADER)Marshal.PtrToStructure((IntPtr)((ulong)pModule + e_lfanew + 0x4), typeof(PE.IMAGE_FILE_HEADER)); var optHeader = (IntPtr)((ulong)pModule + e_lfanew + 0x18); var peArch = (ushort)Marshal.ReadInt16(optHeader); switch (peArch) { case 0x010b: peMetaData.Is32Bit = true; peMetaData.OptHeader32 = (PE.IMAGE_OPTIONAL_HEADER32)Marshal.PtrToStructure(optHeader, typeof(PE.IMAGE_OPTIONAL_HEADER32)); break; case 0x020b: peMetaData.Is32Bit = false; peMetaData.OptHeader64 = (PE.IMAGE_OPTIONAL_HEADER64)Marshal.PtrToStructure(optHeader, typeof(PE.IMAGE_OPTIONAL_HEADER64)); break; default: throw new InvalidOperationException("Invalid magic value (PE32/PE32+)."); } var sectionArray = new PE.IMAGE_SECTION_HEADER[peMetaData.ImageFileHeader.NumberOfSections]; for (var i = 0; i < peMetaData.ImageFileHeader.NumberOfSections; i++) { var sectionPtr = (IntPtr)((ulong)optHeader + peMetaData.ImageFileHeader.SizeOfOptionalHeader + (uint)(i * 0x28)); sectionArray[i] = Marshal.PtrToStructure(sectionPtr); } peMetaData.Sections = sectionArray; } catch { throw new InvalidOperationException("Invalid module base specified."); } return peMetaData; } /// /// Resolve host DLL for API Set DLL. /// /// Ruben Boonen (@FuzzySec), The Wover (@TheRealWover) /// Dictionary, a combination of Key:APISetDLL and Val:HostDLL. public static Dictionary GetApiSetMapping() { var pbi = Native.NtQueryInformationProcessBasicInformation((IntPtr)(-1)); var apiSetMapOffset = IntPtr.Size == 4 ? (uint)0x38 : 0x68; var apiSetDict = new Dictionary(); var pApiSetNamespace = Marshal.ReadIntPtr((IntPtr)((ulong)pbi.PebBaseAddress + apiSetMapOffset)); var apiSetNamespace = (PE.ApiSetNamespace)Marshal.PtrToStructure(pApiSetNamespace, typeof(PE.ApiSetNamespace)); for (var i = 0; i < apiSetNamespace.Count; i++) { var setEntry = new PE.ApiSetNamespaceEntry(); var pSetEntry = (IntPtr)((ulong)pApiSetNamespace + (ulong)apiSetNamespace.EntryOffset + (ulong)(i * Marshal.SizeOf(setEntry))); setEntry = (PE.ApiSetNamespaceEntry)Marshal.PtrToStructure(pSetEntry, typeof(PE.ApiSetNamespaceEntry)); var apiSetEntryName = Marshal.PtrToStringUni((IntPtr)((ulong)pApiSetNamespace + (ulong)setEntry.NameOffset), setEntry.NameLength / 2); var apiSetEntryKey = apiSetEntryName.Substring(0, apiSetEntryName.Length - 2) + ".dll"; // Remove the patch number and add .dll var valueEntry = new PE.ApiSetValueEntry(); var pSetValue = IntPtr.Zero; switch (setEntry.ValueLength) { case 1: pSetValue = (IntPtr)((ulong)pApiSetNamespace + (ulong)setEntry.ValueOffset); break; default: { for (var j = 0; j < setEntry.ValueLength; j++) { var host = (IntPtr)((ulong)pApiSetNamespace + (ulong)setEntry.ValueOffset + (ulong)Marshal.SizeOf(valueEntry) * (ulong)j); if (Marshal.PtrToStringUni(host) != apiSetEntryName) pSetValue = (IntPtr)((ulong)pApiSetNamespace + (ulong)setEntry.ValueOffset + (ulong)Marshal.SizeOf(valueEntry) * (ulong)j); } if (pSetValue == IntPtr.Zero) pSetValue = (IntPtr)((ulong)pApiSetNamespace + (ulong)setEntry.ValueOffset); break; } } valueEntry = (PE.ApiSetValueEntry)Marshal.PtrToStructure(pSetValue, typeof(PE.ApiSetValueEntry)); var apiSetValue = string.Empty; if (valueEntry.ValueCount != 0) { var pValue = (IntPtr)((ulong)pApiSetNamespace + (ulong)valueEntry.ValueOffset); apiSetValue = Marshal.PtrToStringUni(pValue, valueEntry.ValueCount / 2); } apiSetDict.Add(apiSetEntryKey, apiSetValue); } return apiSetDict; } /// /// Call a manually mapped PE by its EntryPoint. /// /// Ruben Boonen (@FuzzySec) /// Module meta data struct (PE.PE_META_DATA). /// Base address of the module in memory. /// void public static void CallMappedPEModule(PE.PE_META_DATA peInfo, IntPtr moduleMemoryBase) { var hRemoteThread = IntPtr.Zero; var lpStartAddress = peInfo.Is32Bit ? (IntPtr)((ulong)moduleMemoryBase + peInfo.OptHeader32.AddressOfEntryPoint) : (IntPtr)((ulong)moduleMemoryBase + peInfo.OptHeader64.AddressOfEntryPoint); Native.NtCreateThreadEx( ref hRemoteThread, ACCESS_MASK.STANDARD_RIGHTS_ALL, IntPtr.Zero, (IntPtr)(-1), lpStartAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero ); } /// /// Call a manually mapped DLL by DllMain -> DLL_PROCESS_ATTACH. /// /// Ruben Boonen (@FuzzySec), TheWover (@TheRealWover) /// Module meta data struct (PE.PE_META_DATA). /// Base address of the module in memory. /// void public static void CallMappedDLLModule(PE.PE_META_DATA peInfo, IntPtr moduleMemoryBase) { var lpEntryPoint = peInfo.Is32Bit ? (IntPtr)((ulong)moduleMemoryBase + peInfo.OptHeader32.AddressOfEntryPoint) : (IntPtr)((ulong)moduleMemoryBase + peInfo.OptHeader64.AddressOfEntryPoint); if (lpEntryPoint == moduleMemoryBase) return; var fDllMain = (PE.DllMain)Marshal.GetDelegateForFunctionPointer(lpEntryPoint, typeof(PE.DllMain)); try { var result = fDllMain(moduleMemoryBase, PE.DLL_PROCESS_ATTACH, IntPtr.Zero); if (!result) throw new InvalidOperationException("Call to entry point failed -> DLL_PROCESS_ATTACH"); } catch { throw new InvalidOperationException("Invalid entry point -> DLL_PROCESS_ATTACH"); } } /// /// Call a manually mapped DLL by Export. /// /// Ruben Boonen (@FuzzySec) /// Module meta data struct (PE.PE_META_DATA). /// Base address of the module in memory. /// The name of the export to search for (e.g. "NtAlertResumeThread"). /// 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. /// Specify whether to invoke the module's entry point. /// void public static object CallMappedDLLModuleExport(PE.PE_META_DATA peInfo, IntPtr moduleMemoryBase, string exportName, Type functionDelegateType, object[] parameters, bool callEntry = true) { if (callEntry) CallMappedDLLModule(peInfo, moduleMemoryBase); var pFunc = GetExportAddress(moduleMemoryBase, exportName); return DynamicFunctionInvoke(pFunc, functionDelegateType, ref parameters); } /// /// Call a manually mapped DLL by Export. /// /// The Wover (@TheRealWover), Ruben Boonen (@FuzzySec) /// Module meta data struct (PE.PE_META_DATA). /// Base address of the module in memory. /// The number of the ordinal to search for (e.g. 0x07). /// 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. /// Specify whether to invoke the module's entry point. /// void public static object CallMappedDLLModuleExport(PE.PE_META_DATA peInfo, IntPtr moduleMemoryBase, short ordinal, Type functionDelegateType, object[] parameters, bool callEntry = true) { if (callEntry) CallMappedDLLModule(peInfo, moduleMemoryBase); var pFunc = GetExportAddress(moduleMemoryBase, ordinal); return DynamicFunctionInvoke(pFunc, functionDelegateType, ref parameters); } /// /// Call a manually mapped DLL by Export. /// /// The Wover (@TheRealWover), Ruben Boonen (@FuzzySec) /// Module meta data struct (PE.PE_META_DATA). /// Base address of the module in memory. /// Hash of the exported procedure. /// 64-bit integer to initialize the keyed hash object (e.g. 0xabc or 0x1122334455667788). /// 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. /// Specify whether to invoke the module's entry point. /// void public static object CallMappedDLLModuleExport(PE.PE_META_DATA peInfo, IntPtr moduleMemoryBase, string functionHash, long key, Type functionDelegateType, object[] parameters, bool callEntry = true) { if (callEntry) CallMappedDLLModule(peInfo, moduleMemoryBase); var pFunc = GetExportAddress(moduleMemoryBase, functionHash, key); return DynamicFunctionInvoke(pFunc, functionDelegateType, ref parameters); } } }