/// <summary> /// Call a manually mapped DLL by DllMain -> DLL_PROCESS_ATTACH. /// </summary> /// <author>Ruben Boonen (@FuzzySec), TheWover (@TheRealWover)</author> /// <param name="peInfo">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="moduleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void CallMappedDLLModule(Data.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 = (Data.PE.DllMain)Marshal.GetDelegateForFunctionPointer(lpEntryPoint, typeof(Data.PE.DllMain)); try { var result = fDllMain(moduleMemoryBase, Data.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"); } }
/// <summary> /// Relocates a module in memory. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="PEINFO">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="ModuleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void RelocateModule(Data.PE.PE_META_DATA PEINFO, IntPtr ModuleMemoryBase) { Data.PE.IMAGE_DATA_DIRECTORY idd = PEINFO.Is32Bit ? PEINFO.OptHeader32.BaseRelocationTable : PEINFO.OptHeader64.BaseRelocationTable; Int64 ImageDelta = PEINFO.Is32Bit ? (Int64)((UInt64)ModuleMemoryBase - PEINFO.OptHeader32.ImageBase) : (Int64)((UInt64)ModuleMemoryBase - PEINFO.OptHeader64.ImageBase); // Ptr for the base reloc table IntPtr pRelocTable = (IntPtr)((UInt64)ModuleMemoryBase + idd.VirtualAddress); Int32 nextRelocTableBlock = -1; // Loop reloc blocks while (nextRelocTableBlock != 0) { Data.PE.IMAGE_BASE_RELOCATION ibr = new Data.PE.IMAGE_BASE_RELOCATION(); ibr = (Data.PE.IMAGE_BASE_RELOCATION)Marshal.PtrToStructure(pRelocTable, typeof(Data.PE.IMAGE_BASE_RELOCATION)); Int64 RelocCount = ((ibr.SizeOfBlock - Marshal.SizeOf(ibr)) / 2); for (int i = 0; i < RelocCount; i++) { // Calculate reloc entry ptr IntPtr pRelocEntry = (IntPtr)((UInt64)pRelocTable + (UInt64)Marshal.SizeOf(ibr) + (UInt64)(i * 2)); UInt16 RelocValue = (UInt16)Marshal.ReadInt16(pRelocEntry); // Parse reloc value // The type should only ever be 0x0, 0x3, 0xA // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#base-relocation-types UInt16 RelocType = (UInt16)(RelocValue >> 12); UInt16 RelocPatch = (UInt16)(RelocValue & 0xfff); // Perform relocation if (RelocType != 0) // IMAGE_REL_BASED_ABSOLUTE (0 -> skip reloc) { try { IntPtr pPatch = (IntPtr)((UInt64)ModuleMemoryBase + ibr.VirtualAdress + RelocPatch); if (RelocType == 0x3) // IMAGE_REL_BASED_HIGHLOW (x86) { Int32 OriginalPtr = Marshal.ReadInt32(pPatch); Marshal.WriteInt32(pPatch, (OriginalPtr + (Int32)ImageDelta)); } else // IMAGE_REL_BASED_DIR64 (x64) { Int64 OriginalPtr = Marshal.ReadInt64(pPatch); Marshal.WriteInt64(pPatch, (OriginalPtr + ImageDelta)); } } catch { throw new InvalidOperationException("Memory access violation."); } } } // Check for next block pRelocTable = (IntPtr)((UInt64)pRelocTable + ibr.SizeOfBlock); nextRelocTableBlock = Marshal.ReadInt32(pRelocTable); } }
/// <summary> /// Retrieve PE header information from the module base pointer. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="pModule">Pointer to the module base.</param> /// <returns>PE.PE_META_DATA</returns> public static Data.PE.PE_META_DATA GetPeMetaData(IntPtr pModule) { var peMetaData = new Data.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 = (Data.PE.IMAGE_FILE_HEADER)Marshal.PtrToStructure((IntPtr)((ulong)pModule + e_lfanew + 0x4), typeof(Data.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 = (Data.PE.IMAGE_OPTIONAL_HEADER32)Marshal.PtrToStructure(optHeader, typeof(Data.PE.IMAGE_OPTIONAL_HEADER32)); break; case 0x020b: peMetaData.Is32Bit = false; peMetaData.OptHeader64 = (Data.PE.IMAGE_OPTIONAL_HEADER64)Marshal.PtrToStructure(optHeader, typeof(Data.PE.IMAGE_OPTIONAL_HEADER64)); break; default: throw new InvalidOperationException("Invalid magic value (PE32/PE32+)."); } var sectionArray = new Data.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] = (Data.PE.IMAGE_SECTION_HEADER)Marshal.PtrToStructure(sectionPtr, typeof(Data.PE.IMAGE_SECTION_HEADER)); } peMetaData.Sections = sectionArray; } catch { throw new InvalidOperationException("Invalid module base specified."); } return(peMetaData); }
/// <summary> /// Call a manually mapped DLL by Export. /// </summary> /// <author>The Wover (@TheRealWover), Ruben Boonen (@FuzzySec)</author> /// <param name="peInfo">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="moduleMemoryBase">Base address of the module in memory.</param> /// <param name="functionHash">Hash of the exported procedure.</param> /// <param name="key">64-bit integer to initialize the keyed hash object (e.g. 0xabc or 0x1122334455667788).</param> /// <param name="functionDelegateType">Prototype for the function, represented as a Delegate object.</param> /// <param name="parameters">Arbitrary set of parameters to pass to the function. Can be modified if function uses call by reference.</param> /// <param name="callEntry">Specify whether to invoke the module's entry point.</param> /// <returns>void</returns> public static object CallMappedDLLModuleExport(Data.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)); }
/// <summary> /// Relocates a module in memory. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="peMetaData">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="moduleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void RelocateModule(Data.PE.PE_META_DATA peMetaData, IntPtr moduleMemoryBase) { var idd = peMetaData.Is32Bit ? peMetaData.OptHeader32.BaseRelocationTable : peMetaData.OptHeader64.BaseRelocationTable; var imageDelta = peMetaData.Is32Bit ? (long)((ulong)moduleMemoryBase - peMetaData.OptHeader32.ImageBase) : (long)((ulong)moduleMemoryBase - peMetaData.OptHeader64.ImageBase); var pRelocTable = (IntPtr)((ulong)moduleMemoryBase + idd.VirtualAddress); var nextRelocTableBlock = -1; while (nextRelocTableBlock != 0) { var ibr = new Data.PE.IMAGE_BASE_RELOCATION(); ibr = (Data.PE.IMAGE_BASE_RELOCATION)Marshal.PtrToStructure(pRelocTable, typeof(Data.PE.IMAGE_BASE_RELOCATION)); var relocCount = (ibr.SizeOfBlock - Marshal.SizeOf(ibr)) / 2; for (var i = 0; i < relocCount; i++) { var pRelocEntry = (IntPtr)((ulong)pRelocTable + (ulong)Marshal.SizeOf(ibr) + (ulong)(i * 2)); var relocValue = (ushort)Marshal.ReadInt16(pRelocEntry); var relocType = (ushort)(relocValue >> 12); var relocPatch = (ushort)(relocValue & 0xfff); if (relocType == 0) { continue; } try { var pPatch = (IntPtr)((ulong)moduleMemoryBase + ibr.VirtualAdress + relocPatch); if (relocType == 0x3) { var originalPtr = Marshal.ReadInt32(pPatch); Marshal.WriteInt32(pPatch, originalPtr + (int)imageDelta); } else { var originalPtr = Marshal.ReadInt64(pPatch); Marshal.WriteInt64(pPatch, originalPtr + imageDelta); } } catch { throw new InvalidOperationException("Memory access violation."); } } pRelocTable = (IntPtr)((ulong)pRelocTable + ibr.SizeOfBlock); nextRelocTableBlock = Marshal.ReadInt32(pRelocTable); } }
/// <summary> /// Call a manually mapped DLL by DllMain -> DLL_PROCESS_ATTACH. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="PEINFO">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="ModuleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void CallMappedDLLModule(Data.PE.PE_META_DATA PEINFO, IntPtr ModuleMemoryBase) { IntPtr lpEntryPoint = PEINFO.Is32Bit ? (IntPtr)((UInt64)ModuleMemoryBase + PEINFO.OptHeader32.AddressOfEntryPoint) : (IntPtr)((UInt64)ModuleMemoryBase + PEINFO.OptHeader64.AddressOfEntryPoint); Data.PE.DllMain fDllMain = (Data.PE.DllMain)Marshal.GetDelegateForFunctionPointer(lpEntryPoint, typeof(Data.PE.DllMain)); bool CallRes = fDllMain(ModuleMemoryBase, Data.PE.DLL_PROCESS_ATTACH, IntPtr.Zero); if (!CallRes) { throw new InvalidOperationException("Failed to call DllMain -> DLL_PROCESS_ATTACH"); } }
/// <summary> /// Call a manually mapped DLL by Export. /// </summary> /// <author>The Wover (@TheRealWover), Ruben Boonen (@FuzzySec)</author> /// <param name="PEINFO">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="ModuleMemoryBase">Base address of the module in memory.</param> /// <param name="FunctionHash">Hash of the exported procedure.</param> /// <param name="Key">64-bit integer to initialize the keyed hash object (e.g. 0xabc or 0x1122334455667788).</param> /// <param name="FunctionDelegateType">Prototype for the function, represented as a Delegate object.</param> /// <param name="Parameters">Arbitrary set of parameters to pass to the function. Can be modified if function uses call by reference.</param> /// <param name="CallEntry">Specify whether to invoke the module's entry point.</param> /// <returns>void</returns> public static object CallMappedDLLModuleExport(Data.PE.PE_META_DATA PEINFO, IntPtr ModuleMemoryBase, string FunctionHash, long Key, Type FunctionDelegateType, object[] Parameters, bool CallEntry = true) { // Call entry point if user has specified if (CallEntry) { CallMappedDLLModule(PEINFO, ModuleMemoryBase); } // Get export pointer IntPtr pFunc = GetExportAddress(ModuleMemoryBase, FunctionHash, Key); // Call export return(DynamicFunctionInvoke(pFunc, FunctionDelegateType, ref Parameters)); }
/// <summary> /// Call a manually mapped PE by its EntryPoint. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="peInfo">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="moduleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void CallMappedPEModule(Data.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, Data.Win32.WinNT.ACCESS_MASK.STANDARD_RIGHTS_ALL, IntPtr.Zero, (IntPtr)(-1), lpStartAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero ); }
/// <summary> /// Call a manually mapped PE by its EntryPoint. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="PEINFO">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="ModuleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void CallMappedPEModule(Data.PE.PE_META_DATA PEINFO, IntPtr ModuleMemoryBase) { // Call module by EntryPoint (eg Mimikatz.exe) IntPtr hRemoteThread = IntPtr.Zero; IntPtr lpStartAddress = PEINFO.Is32Bit ? (IntPtr)((UInt64)ModuleMemoryBase + PEINFO.OptHeader32.AddressOfEntryPoint) : (IntPtr)((UInt64)ModuleMemoryBase + PEINFO.OptHeader64.AddressOfEntryPoint); Native.NtCreateThreadEx( ref hRemoteThread, Data.Win32.WinNT.ACCESS_MASK.STANDARD_RIGHTS_ALL, IntPtr.Zero, (IntPtr)(-1), lpStartAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero ); }
/// <summary> /// Set correct module section permissions. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="PEINFO">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="ModuleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void SetModuleSectionPermissions(Data.PE.PE_META_DATA PEINFO, IntPtr ModuleMemoryBase) { // Apply RO to the module header IntPtr BaseOfCode = PEINFO.Is32Bit ? (IntPtr)PEINFO.OptHeader32.BaseOfCode : (IntPtr)PEINFO.OptHeader64.BaseOfCode; DynamicInvoke.Native.NtProtectVirtualMemory((IntPtr)(-1), ref ModuleMemoryBase, ref BaseOfCode, Data.Win32.WinNT.PAGE_READONLY); // Apply section permissions foreach (Data.PE.IMAGE_SECTION_HEADER ish in PEINFO.Sections) { bool isRead = (ish.Characteristics & Data.PE.DataSectionFlags.MEM_READ) != 0; bool isWrite = (ish.Characteristics & Data.PE.DataSectionFlags.MEM_WRITE) != 0; bool isExecute = (ish.Characteristics & Data.PE.DataSectionFlags.MEM_EXECUTE) != 0; uint flNewProtect = 0; if (isRead & !isWrite & !isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_READONLY; } else if (isRead & isWrite & !isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_READWRITE; } else if (isRead & isWrite & isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_EXECUTE_READWRITE; } else if (isRead & !isWrite & isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_EXECUTE_READ; } else if (!isRead & !isWrite & isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_EXECUTE; } else { throw new InvalidOperationException("Unknown section flag, " + ish.Characteristics); } // Calculate base IntPtr pVirtualSectionBase = (IntPtr)((UInt64)ModuleMemoryBase + ish.VirtualAddress); IntPtr ProtectSize = (IntPtr)ish.VirtualSize; // Set protection DynamicInvoke.Native.NtProtectVirtualMemory((IntPtr)(-1), ref pVirtualSectionBase, ref ProtectSize, flNewProtect); } }
/// <summary> /// Retrieve PE header information from the module base pointer. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="pModule">Pointer to the module base.</param> /// <returns>PE.PE_META_DATA</returns> public static Data.PE.PE_META_DATA GetPeMetaData(IntPtr pModule) { Data.PE.PE_META_DATA PeMetaData = new Data.PE.PE_META_DATA(); try { UInt32 e_lfanew = (UInt32)Marshal.ReadInt32((IntPtr)((UInt64)pModule + 0x3c)); PeMetaData.Pe = (UInt32)Marshal.ReadInt32((IntPtr)((UInt64)pModule + e_lfanew)); // Validate PE signature if (PeMetaData.Pe != 0x4550) { throw new InvalidOperationException("Invalid PE signature."); } PeMetaData.ImageFileHeader = (Data.PE.IMAGE_FILE_HEADER)Marshal.PtrToStructure((IntPtr)((UInt64)pModule + e_lfanew + 0x4), typeof(Data.PE.IMAGE_FILE_HEADER)); IntPtr OptHeader = (IntPtr)((UInt64)pModule + e_lfanew + 0x18); UInt16 PEArch = (UInt16)Marshal.ReadInt16(OptHeader); // Validate PE arch if (PEArch == 0x010b) // Image is x32 { PeMetaData.Is32Bit = true; PeMetaData.OptHeader32 = (Data.PE.IMAGE_OPTIONAL_HEADER32)Marshal.PtrToStructure(OptHeader, typeof(Data.PE.IMAGE_OPTIONAL_HEADER32)); } else if (PEArch == 0x020b) // Image is x64 { PeMetaData.Is32Bit = false; PeMetaData.OptHeader64 = (Data.PE.IMAGE_OPTIONAL_HEADER64)Marshal.PtrToStructure(OptHeader, typeof(Data.PE.IMAGE_OPTIONAL_HEADER64)); } else { throw new InvalidOperationException("Invalid magic value (PE32/PE32+)."); } // Read sections Data.PE.IMAGE_SECTION_HEADER[] SectionArray = new Data.PE.IMAGE_SECTION_HEADER[PeMetaData.ImageFileHeader.NumberOfSections]; for (int i = 0; i < PeMetaData.ImageFileHeader.NumberOfSections; i++) { IntPtr SectionPtr = (IntPtr)((UInt64)OptHeader + PeMetaData.ImageFileHeader.SizeOfOptionalHeader + (UInt32)(i * 0x28)); SectionArray[i] = (Data.PE.IMAGE_SECTION_HEADER)Marshal.PtrToStructure(SectionPtr, typeof(Data.PE.IMAGE_SECTION_HEADER)); } PeMetaData.Sections = SectionArray; } catch { throw new InvalidOperationException("Invalid module base specified."); } return(PeMetaData); }
/// <summary> /// Set correct module section permissions. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="peMetaData">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="moduleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void SetModuleSectionPermissions(Data.PE.PE_META_DATA peMetaData, IntPtr moduleMemoryBase) { var baseOfCode = peMetaData.Is32Bit ? (IntPtr)peMetaData.OptHeader32.BaseOfCode : (IntPtr)peMetaData.OptHeader64.BaseOfCode; Native.NtProtectVirtualMemory((IntPtr)(-1), ref moduleMemoryBase, ref baseOfCode, Data.Win32.WinNT.PAGE_READONLY); foreach (var ish in peMetaData.Sections) { var isRead = (ish.Characteristics & Data.PE.DataSectionFlags.MEM_READ) != 0; var isWrite = (ish.Characteristics & Data.PE.DataSectionFlags.MEM_WRITE) != 0; var isExecute = (ish.Characteristics & Data.PE.DataSectionFlags.MEM_EXECUTE) != 0; uint flNewProtect = 0; if (isRead & !isWrite & !isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_READONLY; } else if (isRead & isWrite & !isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_READWRITE; } else if (isRead & isWrite & isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_EXECUTE_READWRITE; } else if (isRead & !isWrite & isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_EXECUTE_READ; } else if (!isRead & !isWrite & isExecute) { flNewProtect = Data.Win32.WinNT.PAGE_EXECUTE; } else { throw new InvalidOperationException("Unknown section flag, " + ish.Characteristics); } var pVirtualSectionBase = (IntPtr)((ulong)moduleMemoryBase + ish.VirtualAddress); var protectSize = (IntPtr)ish.VirtualSize; Native.NtProtectVirtualMemory((IntPtr)(-1), ref pVirtualSectionBase, ref protectSize, flNewProtect); } }
/// <summary> /// Free a module that was mapped into the current process. /// </summary> /// <author>The Wover (@TheRealWover)</author> /// <param name="PEMapped">The metadata of the manually mapped module.</param> public static void FreeModule(Data.PE.PE_MANUAL_MAP PEMapped) { // Check if PE was mapped via module overloading if (!string.IsNullOrEmpty(PEMapped.DecoyModule)) { DynamicInvoke.Native.NtUnmapViewOfSection((IntPtr)(-1), PEMapped.ModuleBase); } // If PE not mapped via module overloading, free the memory. else { Data.PE.PE_META_DATA PEINFO = PEMapped.PEINFO; // Get the size of the module in memory IntPtr size = PEINFO.Is32Bit ? (IntPtr)PEINFO.OptHeader32.SizeOfImage : (IntPtr)PEINFO.OptHeader64.SizeOfImage; IntPtr pModule = PEMapped.ModuleBase; DynamicInvoke.Native.NtFreeVirtualMemory((IntPtr)(-1), ref pModule, ref size, Data.Win32.Kernel32.MEM_RELEASE); } }
/// <summary> /// Manually map module into current process. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="pModule">Pointer to the module base.</param> /// <returns>PE_MANUAL_MAP object</returns> public static Data.PE.PE_MANUAL_MAP MapModuleToMemory(IntPtr pModule) { // Fetch PE meta data Data.PE.PE_META_DATA PEINFO = DynamicInvoke.Generic.GetPeMetaData(pModule); // Check module matches the process architecture if ((PEINFO.Is32Bit && IntPtr.Size == 8) || (!PEINFO.Is32Bit && IntPtr.Size == 4)) { Marshal.FreeHGlobal(pModule); throw new InvalidOperationException("The module architecture does not match the process architecture."); } // Alloc PE image memory -> RW IntPtr BaseAddress = IntPtr.Zero; IntPtr RegionSize = PEINFO.Is32Bit ? (IntPtr)PEINFO.OptHeader32.SizeOfImage : (IntPtr)PEINFO.OptHeader64.SizeOfImage; IntPtr pImage = DynamicInvoke.Native.NtAllocateVirtualMemory( (IntPtr)(-1), ref BaseAddress, IntPtr.Zero, ref RegionSize, Data.Win32.Kernel32.MEM_COMMIT | Data.Win32.Kernel32.MEM_RESERVE, Data.Win32.WinNT.PAGE_READWRITE ); return(MapModuleToMemory(pModule, pImage, PEINFO)); }
/// <summary> /// Call a manually mapped DLL by DllMain -> DLL_PROCESS_ATTACH. /// </summary> /// <author>Ruben Boonen (@FuzzySec), TheWover (@TheRealWover)</author> /// <param name="PEINFO">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="ModuleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void CallMappedDLLModule(Data.PE.PE_META_DATA PEINFO, IntPtr ModuleMemoryBase) { IntPtr lpEntryPoint = PEINFO.Is32Bit ? (IntPtr)((UInt64)ModuleMemoryBase + PEINFO.OptHeader32.AddressOfEntryPoint) : (IntPtr)((UInt64)ModuleMemoryBase + PEINFO.OptHeader64.AddressOfEntryPoint); // If there is an entry point, call it if (lpEntryPoint != ModuleMemoryBase) { Data.PE.DllMain fDllMain = (Data.PE.DllMain)Marshal.GetDelegateForFunctionPointer(lpEntryPoint, typeof(Data.PE.DllMain)); try { bool CallRes = fDllMain(ModuleMemoryBase, Data.PE.DLL_PROCESS_ATTACH, IntPtr.Zero); if (!CallRes) { throw new InvalidOperationException("Call to entry point failed -> DLL_PROCESS_ATTACH"); } } catch { throw new InvalidOperationException("Invalid entry point -> DLL_PROCESS_ATTACH"); } } }
/// <summary> /// Read ntdll from disk, find/copy the appropriate syscall stub and free ntdll. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="FunctionName">The name of the function to search for (e.g. "NtAlertResumeThread").</param> /// <returns>IntPtr, Syscall stub</returns> public static IntPtr GetSyscallStub(string FunctionName) { // Verify process & architecture bool isWOW64 = Native.NtQueryInformationProcessWow64Information((IntPtr)(-1)); if (IntPtr.Size == 4 && isWOW64) { throw new InvalidOperationException("Generating Syscall stubs is not supported for WOW64."); } // Find the path for ntdll by looking at the currently loaded module string NtdllPath = string.Empty; ProcessModuleCollection ProcModules = Process.GetCurrentProcess().Modules; foreach (ProcessModule Mod in ProcModules) { if (Mod.FileName.EndsWith("ntdll.dll", StringComparison.OrdinalIgnoreCase)) { NtdllPath = Mod.FileName; } } // Alloc module into memory for parsing IntPtr pModule = ManualMap.Map.AllocateFileToMemory(NtdllPath); // Fetch PE meta data Data.PE.PE_META_DATA PEINFO = GetPeMetaData(pModule); // Alloc PE image memory -> RW IntPtr BaseAddress = IntPtr.Zero; IntPtr RegionSize = PEINFO.Is32Bit ? (IntPtr)PEINFO.OptHeader32.SizeOfImage : (IntPtr)PEINFO.OptHeader64.SizeOfImage; UInt32 SizeOfHeaders = PEINFO.Is32Bit ? PEINFO.OptHeader32.SizeOfHeaders : PEINFO.OptHeader64.SizeOfHeaders; IntPtr pImage = Native.NtAllocateVirtualMemory( (IntPtr)(-1), ref BaseAddress, IntPtr.Zero, ref RegionSize, Data.Win32.Kernel32.MEM_COMMIT | Data.Win32.Kernel32.MEM_RESERVE, Data.Win32.WinNT.PAGE_READWRITE ); // Write PE header to memory UInt32 BytesWritten = Native.NtWriteVirtualMemory((IntPtr)(-1), pImage, pModule, SizeOfHeaders); // Write sections to memory foreach (Data.PE.IMAGE_SECTION_HEADER ish in PEINFO.Sections) { // Calculate offsets IntPtr pVirtualSectionBase = (IntPtr)((UInt64)pImage + ish.VirtualAddress); IntPtr pRawSectionBase = (IntPtr)((UInt64)pModule + ish.PointerToRawData); // Write data BytesWritten = Native.NtWriteVirtualMemory((IntPtr)(-1), pVirtualSectionBase, pRawSectionBase, ish.SizeOfRawData); if (BytesWritten != ish.SizeOfRawData) { throw new InvalidOperationException("Failed to write to memory."); } } // Get Ptr to function IntPtr pFunc = GetExportAddress(pImage, FunctionName); if (pFunc == IntPtr.Zero) { throw new InvalidOperationException("Failed to resolve ntdll export."); } // Alloc memory for call stub BaseAddress = IntPtr.Zero; RegionSize = (IntPtr)0x50; IntPtr pCallStub = Native.NtAllocateVirtualMemory( (IntPtr)(-1), ref BaseAddress, IntPtr.Zero, ref RegionSize, Data.Win32.Kernel32.MEM_COMMIT | Data.Win32.Kernel32.MEM_RESERVE, Data.Win32.WinNT.PAGE_READWRITE ); // Write call stub BytesWritten = Native.NtWriteVirtualMemory((IntPtr)(-1), pCallStub, pFunc, 0x50); if (BytesWritten != 0x50) { throw new InvalidOperationException("Failed to write to memory."); } // Change call stub permissions Native.NtProtectVirtualMemory((IntPtr)(-1), ref pCallStub, ref RegionSize, Data.Win32.WinNT.PAGE_EXECUTE_READ); // Free temporary allocations Marshal.FreeHGlobal(pModule); RegionSize = PEINFO.Is32Bit ? (IntPtr)PEINFO.OptHeader32.SizeOfImage : (IntPtr)PEINFO.OptHeader64.SizeOfImage; Native.NtFreeVirtualMemory((IntPtr)(-1), ref pImage, ref RegionSize, Data.Win32.Kernel32.MEM_RELEASE); return(pCallStub); }
/// <summary> /// Rewrite IAT for manually mapped module. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="PEINFO">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="ModuleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void RewriteModuleIAT(Data.PE.PE_META_DATA PEINFO, IntPtr ModuleMemoryBase) { Data.PE.IMAGE_DATA_DIRECTORY idd = PEINFO.Is32Bit ? PEINFO.OptHeader32.ImportTable : PEINFO.OptHeader64.ImportTable; // Check if there is no import table if (idd.VirtualAddress == 0) { // Return so that the rest of the module mapping process may continue. return; } // Ptr for the base import directory IntPtr pImportTable = (IntPtr)((UInt64)ModuleMemoryBase + idd.VirtualAddress); // Get API Set mapping dictionary if on Win10+ Data.Native.OSVERSIONINFOEX OSVersion = new Data.Native.OSVERSIONINFOEX(); DynamicInvoke.Native.RtlGetVersion(ref OSVersion); Dictionary <string, string> ApiSetDict = new Dictionary <string, string>(); if (OSVersion.MajorVersion >= 10) { ApiSetDict = DynamicInvoke.Generic.GetApiSetMapping(); } // Loop IID's int counter = 0; Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR iid = new Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR(); iid = (Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure( (IntPtr)((UInt64)pImportTable + (uint)(Marshal.SizeOf(iid) * counter)), typeof(Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR) ); while (iid.Name != 0) { // Get DLL string DllName = string.Empty; try { DllName = Marshal.PtrToStringAnsi((IntPtr)((UInt64)ModuleMemoryBase + iid.Name)); } catch { } // Loop imports if (DllName == string.Empty) { throw new InvalidOperationException("Failed to read DLL name."); } else { // API Set DLL? if (OSVersion.MajorVersion >= 10 && (DllName.StartsWith("api-") || DllName.StartsWith("ext-")) && ApiSetDict.ContainsKey(DllName) && ApiSetDict[DllName].Length > 0) { // Not all API set DLL's have a registered host mapping DllName = ApiSetDict[DllName]; } // Check and / or load DLL IntPtr hModule = DynamicInvoke.Generic.GetLoadedModuleAddress(DllName); if (hModule == IntPtr.Zero) { hModule = DynamicInvoke.Generic.LoadModuleFromDisk(DllName); if (hModule == IntPtr.Zero) { throw new FileNotFoundException(DllName + ", unable to find the specified file."); } } // Loop thunks if (PEINFO.Is32Bit) { Data.PE.IMAGE_THUNK_DATA32 oft_itd = new Data.PE.IMAGE_THUNK_DATA32(); for (int i = 0; true; i++) { oft_itd = (Data.PE.IMAGE_THUNK_DATA32)Marshal.PtrToStructure((IntPtr)((UInt64)ModuleMemoryBase + iid.OriginalFirstThunk + (UInt32)(i * (sizeof(UInt32)))), typeof(Data.PE.IMAGE_THUNK_DATA32)); IntPtr ft_itd = (IntPtr)((UInt64)ModuleMemoryBase + iid.FirstThunk + (UInt64)(i * (sizeof(UInt32)))); if (oft_itd.AddressOfData == 0) { break; } if (oft_itd.AddressOfData < 0x80000000) // !IMAGE_ORDINAL_FLAG32 { IntPtr pImpByName = (IntPtr)((UInt64)ModuleMemoryBase + oft_itd.AddressOfData + sizeof(UInt16)); IntPtr pFunc = IntPtr.Zero; pFunc = DynamicInvoke.Generic.GetNativeExportAddress(hModule, Marshal.PtrToStringAnsi(pImpByName)); // Write ProcAddress Marshal.WriteInt32(ft_itd, pFunc.ToInt32()); } else { ulong fOrdinal = oft_itd.AddressOfData & 0xFFFF; IntPtr pFunc = IntPtr.Zero; pFunc = DynamicInvoke.Generic.GetNativeExportAddress(hModule, (short)fOrdinal); // Write ProcAddress Marshal.WriteInt32(ft_itd, pFunc.ToInt32()); } } } else { Data.PE.IMAGE_THUNK_DATA64 oft_itd = new Data.PE.IMAGE_THUNK_DATA64(); for (int i = 0; true; i++) { oft_itd = (Data.PE.IMAGE_THUNK_DATA64)Marshal.PtrToStructure((IntPtr)((UInt64)ModuleMemoryBase + iid.OriginalFirstThunk + (UInt64)(i * (sizeof(UInt64)))), typeof(Data.PE.IMAGE_THUNK_DATA64)); IntPtr ft_itd = (IntPtr)((UInt64)ModuleMemoryBase + iid.FirstThunk + (UInt64)(i * (sizeof(UInt64)))); if (oft_itd.AddressOfData == 0) { break; } if (oft_itd.AddressOfData < 0x8000000000000000) // !IMAGE_ORDINAL_FLAG64 { IntPtr pImpByName = (IntPtr)((UInt64)ModuleMemoryBase + oft_itd.AddressOfData + sizeof(UInt16)); IntPtr pFunc = IntPtr.Zero; pFunc = DynamicInvoke.Generic.GetNativeExportAddress(hModule, Marshal.PtrToStringAnsi(pImpByName)); // Write pointer Marshal.WriteInt64(ft_itd, pFunc.ToInt64()); } else { ulong fOrdinal = oft_itd.AddressOfData & 0xFFFF; IntPtr pFunc = IntPtr.Zero; pFunc = DynamicInvoke.Generic.GetNativeExportAddress(hModule, (short)fOrdinal); // Write pointer Marshal.WriteInt64(ft_itd, pFunc.ToInt64()); } } } counter++; iid = (Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure( (IntPtr)((UInt64)pImportTable + (uint)(Marshal.SizeOf(iid) * counter)), typeof(Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR) ); } } }
/// <summary> /// Rewrite IAT for manually mapped module. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="peMetaData">Module meta data struct (PE.PE_META_DATA).</param> /// <param name="moduleMemoryBase">Base address of the module in memory.</param> /// <returns>void</returns> public static void RewriteModuleIAT(Data.PE.PE_META_DATA peMetaData, IntPtr moduleMemoryBase) { var idd = peMetaData.Is32Bit ? peMetaData.OptHeader32.ImportTable : peMetaData.OptHeader64.ImportTable; if (idd.VirtualAddress == 0) { return; } var pImportTable = (IntPtr)((ulong)moduleMemoryBase + idd.VirtualAddress); var osVersion = new Data.Native.OSVERSIONINFOEX(); Native.RtlGetVersion(ref osVersion); var apiSetDict = new Dictionary <string, string>(); if (osVersion.MajorVersion >= 10) { apiSetDict = Generic.GetApiSetMapping(); } var counter = 0; var iid = new Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR(); iid = (Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure( (IntPtr)((ulong)pImportTable + (uint)(Marshal.SizeOf(iid) * counter)), typeof(Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR) ); while (iid.Name != 0) { var dllName = string.Empty; try { dllName = Marshal.PtrToStringAnsi((IntPtr)((ulong)moduleMemoryBase + iid.Name)); } catch { // ignore } if (dllName == string.Empty) { throw new InvalidOperationException("Failed to read DLL name."); } var lookupKey = dllName !.Substring(0, dllName.Length - 6) + ".dll"; if (osVersion.MajorVersion >= 10 && (dllName.StartsWith("api-") || dllName.StartsWith("ext-")) && apiSetDict.ContainsKey(lookupKey) && apiSetDict[lookupKey].Length > 0) { dllName = apiSetDict[lookupKey]; } var hModule = Generic.GetLoadedModuleAddress(dllName); if (hModule == IntPtr.Zero) { hModule = Generic.LoadModuleFromDisk(dllName); if (hModule == IntPtr.Zero) { throw new FileNotFoundException(dllName + ", unable to find the specified file."); } } if (peMetaData.Is32Bit) { var oft_itd = new Data.PE.IMAGE_THUNK_DATA32(); for (var i = 0;; i++) { oft_itd = (Data.PE.IMAGE_THUNK_DATA32)Marshal.PtrToStructure( (IntPtr)((ulong)moduleMemoryBase + iid.OriginalFirstThunk + (uint)(i * sizeof(uint))), typeof(Data.PE.IMAGE_THUNK_DATA32)); var ft_itd = (IntPtr)((ulong)moduleMemoryBase + iid.FirstThunk + (ulong)(i * sizeof(uint))); if (oft_itd.AddressOfData == 0) { break; } if (oft_itd.AddressOfData < 0x80000000) { var pImpByName = (IntPtr)((ulong)moduleMemoryBase + oft_itd.AddressOfData + sizeof(ushort)); var pFunc = IntPtr.Zero; pFunc = Generic.GetNativeExportAddress(hModule, Marshal.PtrToStringAnsi(pImpByName)); Marshal.WriteInt32(ft_itd, pFunc.ToInt32()); } else { ulong fOrdinal = oft_itd.AddressOfData & 0xFFFF; var pFunc = IntPtr.Zero; pFunc = Generic.GetNativeExportAddress(hModule, (short)fOrdinal); Marshal.WriteInt32(ft_itd, pFunc.ToInt32()); } } } else { var oft_itd = new Data.PE.IMAGE_THUNK_DATA64(); for (var i = 0;; i++) { oft_itd = (Data.PE.IMAGE_THUNK_DATA64)Marshal.PtrToStructure( (IntPtr)((ulong)moduleMemoryBase + iid.OriginalFirstThunk + (ulong)(i * sizeof(ulong))), typeof(Data.PE.IMAGE_THUNK_DATA64)); var ft_itd = (IntPtr)((ulong)moduleMemoryBase + iid.FirstThunk + (ulong)(i * sizeof(ulong))); if (oft_itd.AddressOfData == 0) { break; } if (oft_itd.AddressOfData < 0x8000000000000000) // !IMAGE_ORDINAL_FLAG64 { var pImpByName = (IntPtr)((ulong)moduleMemoryBase + oft_itd.AddressOfData + sizeof(ushort)); var pFunc = IntPtr.Zero; pFunc = Generic.GetNativeExportAddress(hModule, Marshal.PtrToStringAnsi(pImpByName)); Marshal.WriteInt64(ft_itd, pFunc.ToInt64()); } else { var fOrdinal = oft_itd.AddressOfData & 0xFFFF; var pFunc = IntPtr.Zero; pFunc = Generic.GetNativeExportAddress(hModule, (short)fOrdinal); Marshal.WriteInt64(ft_itd, pFunc.ToInt64()); } } } counter++; iid = (Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR)Marshal.PtrToStructure( (IntPtr)((ulong)pImportTable + (uint)(Marshal.SizeOf(iid) * counter)), typeof(Data.Win32.Kernel32.IMAGE_IMPORT_DESCRIPTOR) ); } }
/// <summary> /// Manually map module into current process. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="pModule">Pointer to the module base.</param> /// <param name="pImage">Pointer to the PEINFO image.</param> /// <returns>PE_MANUAL_MAP object</returns> public static Data.PE.PE_MANUAL_MAP MapModuleToMemory(IntPtr pModule, IntPtr pImage) { Data.PE.PE_META_DATA PEINFO = DynamicInvoke.Generic.GetPeMetaData(pModule); return(MapModuleToMemory(pModule, pImage, PEINFO)); }
/// <summary> /// Manually map module into current process. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="pModule">Pointer to the module base.</param> /// <param name="pImage">Pointer to the PEINFO image.</param> /// <param name="peMetaData">PE_META_DATA of the module being mapped.</param> /// <returns>PE_MANUAL_MAP object</returns> public static Data.PE.PE_MANUAL_MAP MapModuleToMemory(IntPtr pModule, IntPtr pImage, Data.PE.PE_META_DATA peMetaData) { if (peMetaData.Is32Bit && IntPtr.Size == 8 || !peMetaData.Is32Bit && IntPtr.Size == 4) { Marshal.FreeHGlobal(pModule); throw new InvalidOperationException("The module architecture does not match the process architecture."); } var sizeOfHeaders = peMetaData.Is32Bit ? peMetaData.OptHeader32.SizeOfHeaders : peMetaData.OptHeader64.SizeOfHeaders; var bytesWritten = Native.NtWriteVirtualMemory((IntPtr)(-1), pImage, pModule, sizeOfHeaders); foreach (var ish in peMetaData.Sections) { var pVirtualSectionBase = (IntPtr)((ulong)pImage + ish.VirtualAddress); var pRawSectionBase = (IntPtr)((ulong)pModule + ish.PointerToRawData); bytesWritten = Native.NtWriteVirtualMemory((IntPtr)(-1), pVirtualSectionBase, pRawSectionBase, ish.SizeOfRawData); if (bytesWritten != ish.SizeOfRawData) { throw new InvalidOperationException("Failed to write to memory."); } } RelocateModule(peMetaData, pImage); RewriteModuleIAT(peMetaData, pImage); SetModuleSectionPermissions(peMetaData, pImage); Marshal.FreeHGlobal(pModule); return(new Data.PE.PE_MANUAL_MAP { ModuleBase = pImage, PEINFO = peMetaData }); }
/// <summary> /// Manually map module into current process. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="pModule">Pointer to the module base.</param> /// <param name="pImage">Pointer to the PEINFO image.</param> /// <param name="PEINFO">PE_META_DATA of the module being mapped.</param> /// <returns>PE_MANUAL_MAP object</returns> public static Data.PE.PE_MANUAL_MAP MapModuleToMemory(IntPtr pModule, IntPtr pImage, Data.PE.PE_META_DATA PEINFO) { // Check module matches the process architecture if ((PEINFO.Is32Bit && IntPtr.Size == 8) || (!PEINFO.Is32Bit && IntPtr.Size == 4)) { Marshal.FreeHGlobal(pModule); throw new InvalidOperationException("The module architecture does not match the process architecture."); } // Write PE header to memory UInt32 SizeOfHeaders = PEINFO.Is32Bit ? PEINFO.OptHeader32.SizeOfHeaders : PEINFO.OptHeader64.SizeOfHeaders; UInt32 BytesWritten = DynamicInvoke.Native.NtWriteVirtualMemory((IntPtr)(-1), pImage, pModule, SizeOfHeaders); // Write sections to memory foreach (Data.PE.IMAGE_SECTION_HEADER ish in PEINFO.Sections) { // Calculate offsets IntPtr pVirtualSectionBase = (IntPtr)((UInt64)pImage + ish.VirtualAddress); IntPtr pRawSectionBase = (IntPtr)((UInt64)pModule + ish.PointerToRawData); // Write data BytesWritten = DynamicInvoke.Native.NtWriteVirtualMemory((IntPtr)(-1), pVirtualSectionBase, pRawSectionBase, ish.SizeOfRawData); if (BytesWritten != ish.SizeOfRawData) { throw new InvalidOperationException("Failed to write to memory."); } } // Perform relocations RelocateModule(PEINFO, pImage); // Rewrite IAT RewriteModuleIAT(PEINFO, pImage); // Set memory protections SetModuleSectionPermissions(PEINFO, pImage); // Free temp HGlobal Marshal.FreeHGlobal(pModule); // Prepare return object Data.PE.PE_MANUAL_MAP ManMapObject = new Data.PE.PE_MANUAL_MAP { ModuleBase = pImage, PEINFO = PEINFO }; return(ManMapObject); }