/// <summary> /// Resolve host DLL for API Set DLL. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <returns>Dictionary, a combination of Key:APISetDLL and Val:HostDLL.</returns> public static Dictionary <string, string> GetApiSetMapping() { Execute.Native.PROCESS_BASIC_INFORMATION pbi = Native.NtQueryInformationProcessBasicInformation((IntPtr)(-1)); UInt32 ApiSetMapOffset = IntPtr.Size == 4 ? (UInt32)0x38 : 0x68; // Create mapping dictionary Dictionary <string, string> ApiSetDict = new Dictionary <string, string>(); IntPtr pApiSetNamespace = Marshal.ReadIntPtr((IntPtr)((UInt64)pbi.PebBaseAddress + ApiSetMapOffset)); PE.ApiSetNamespace Namespace = (PE.ApiSetNamespace)Marshal.PtrToStructure(pApiSetNamespace, typeof(PE.ApiSetNamespace)); for (var i = 0; i < Namespace.Count; i++) { PE.ApiSetNamespaceEntry SetEntry = new PE.ApiSetNamespaceEntry(); SetEntry = (PE.ApiSetNamespaceEntry)Marshal.PtrToStructure((IntPtr)((UInt64)pApiSetNamespace + (UInt64)Namespace.EntryOffset + (UInt64)(i * Marshal.SizeOf(SetEntry))), typeof(PE.ApiSetNamespaceEntry)); string ApiSetEntryName = Marshal.PtrToStringUni((IntPtr)((UInt64)pApiSetNamespace + (UInt64)SetEntry.NameOffset), SetEntry.NameLength / 2) + ".dll"; PE.ApiSetValueEntry SetValue = new PE.ApiSetValueEntry(); SetValue = (PE.ApiSetValueEntry)Marshal.PtrToStructure((IntPtr)((UInt64)pApiSetNamespace + (UInt64)SetEntry.ValueOffset), typeof(PE.ApiSetValueEntry)); string ApiSetValue = string.Empty; if (SetValue.ValueCount != 0) { ApiSetValue = Marshal.PtrToStringUni((IntPtr)((UInt64)pApiSetNamespace + (UInt64)SetValue.ValueOffset), SetValue.ValueCount / 2); } // Add pair to dict ApiSetDict.Add(ApiSetEntryName, ApiSetValue); } // Return dict return(ApiSetDict); }
/// <summary> /// Given a module base address, resolve the address of a function by calling LdrGetProcedureAddress. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="ModuleBase">A pointer to the base address where the module is loaded in the current process.</param> /// <param name="Ordinal">The ordinal number to search for (e.g. 0x136 -> ntdll!NtCreateThreadEx).</param> /// <returns>IntPtr for the desired function.</returns> public static IntPtr GetNativeExportAddress(IntPtr ModuleBase, short Ordinal) { IntPtr pFuncAddr = IntPtr.Zero; IntPtr pOrd = (IntPtr)Ordinal; Native.LdrGetProcedureAddress(ModuleBase, IntPtr.Zero, pOrd, ref pFuncAddr); return(pFuncAddr); }
/// <summary> /// Resolves LdrLoadDll and uses that function to load a DLL from disk. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="DLLPath">The path to the DLL on disk. Uses the LoadLibrary convention.</param> /// <returns>IntPtr base address of the loaded module or IntPtr.Zero if the module was not loaded successfully.</returns> public static IntPtr LoadModuleFromDisk(string DLLPath) { Execute.Native.UNICODE_STRING uModuleName = new Execute.Native.UNICODE_STRING(); Native.RtlInitUnicodeString(ref uModuleName, DLLPath); IntPtr hModule = IntPtr.Zero; Execute.Native.NTSTATUS CallResult = Native.LdrLoadDll(IntPtr.Zero, 0, ref uModuleName, ref hModule); if (CallResult != Execute.Native.NTSTATUS.Success || hModule == IntPtr.Zero) { return(IntPtr.Zero); } return(hModule); }
/// <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(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, Execute.Win32.WinNT.ACCESS_MASK.STANDARD_RIGHTS_ALL, IntPtr.Zero, (IntPtr)(-1), lpStartAddress, IntPtr.Zero, false, 0, 0, 0, IntPtr.Zero ); }
/// <summary> /// 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. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="DLLName">The name of the DLL (e.g. "ntdll.dll").</param> /// <returns>IntPtr base address of the loaded module or IntPtr.Zero if the module is not found.</returns> public static IntPtr GetPebLdrModuleEntry(string DLLName) { // Get _PEB pointer Execute.Native.PROCESS_BASIC_INFORMATION pbi = Native.NtQueryInformationProcessBasicInformation((IntPtr)(-1)); // Set function variables bool Is32Bit = false; UInt32 LdrDataOffset = 0; UInt32 InLoadOrderModuleListOffset = 0; if (IntPtr.Size == 4) { Is32Bit = true; LdrDataOffset = 0xc; InLoadOrderModuleListOffset = 0xC; } else { LdrDataOffset = 0x18; InLoadOrderModuleListOffset = 0x10; } // Get module InLoadOrderModuleList -> _LIST_ENTRY IntPtr PEB_LDR_DATA = Marshal.ReadIntPtr((IntPtr)((UInt64)pbi.PebBaseAddress + LdrDataOffset)); IntPtr pInLoadOrderModuleList = (IntPtr)((UInt64)PEB_LDR_DATA + InLoadOrderModuleListOffset); Execute.Native.LIST_ENTRY le = (Execute.Native.LIST_ENTRY)Marshal.PtrToStructure(pInLoadOrderModuleList, typeof(Execute.Native.LIST_ENTRY)); // Loop entries IntPtr flink = le.Flink; IntPtr hModule = IntPtr.Zero; Execute.PE.LDR_DATA_TABLE_ENTRY dte = (Execute.PE.LDR_DATA_TABLE_ENTRY)Marshal.PtrToStructure(flink, typeof(Execute.PE.LDR_DATA_TABLE_ENTRY)); while (dte.InLoadOrderLinks.Flink != le.Blink) { // Match module name if (Marshal.PtrToStringUni(dte.FullDllName.Buffer).EndsWith(DLLName, StringComparison.OrdinalIgnoreCase)) { hModule = dte.DllBase; } // Move Ptr flink = dte.InLoadOrderLinks.Flink; dte = (Execute.PE.LDR_DATA_TABLE_ENTRY)Marshal.PtrToStructure(flink, typeof(Execute.PE.LDR_DATA_TABLE_ENTRY)); } return(hModule); }
/// <summary> /// Given a module base address, resolve the address of a function by calling LdrGetProcedureAddress. /// </summary> /// <author>Ruben Boonen (@FuzzySec)</author> /// <param name="ModuleBase">A pointer to the base address where the module is loaded in the current process.</param> /// <param name="ExportName">The name of the export to search for (e.g. "NtAlertResumeThread").</param> /// <returns>IntPtr for the desired function.</returns> public static IntPtr GetNativeExportAddress(IntPtr ModuleBase, string ExportName) { Execute.Native.ANSI_STRING aFunc = new Execute.Native.ANSI_STRING { Length = (ushort)ExportName.Length, MaximumLength = (ushort)(ExportName.Length + 2), Buffer = Marshal.StringToCoTaskMemAnsi(ExportName) }; IntPtr pAFunc = Marshal.AllocHGlobal(Marshal.SizeOf(aFunc)); Marshal.StructureToPtr(aFunc, pAFunc, true); IntPtr pFuncAddr = IntPtr.Zero; Native.LdrGetProcedureAddress(ModuleBase, pAFunc, IntPtr.Zero, ref pFuncAddr); Marshal.FreeHGlobal(pAFunc); return(pFuncAddr); }
/// <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 = Execute.ManualMap.Map.AllocateFileToMemory(NtdllPath); // Fetch PE meta 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, Execute.Win32.Kernel32.MEM_COMMIT | Execute.Win32.Kernel32.MEM_RESERVE, Execute.Win32.WinNT.PAGE_READWRITE ); // Write PE header to memory UInt32 BytesWritten = Native.NtWriteVirtualMemory((IntPtr)(-1), pImage, pModule, SizeOfHeaders); // Write sections to memory foreach (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, Execute.Win32.Kernel32.MEM_COMMIT | Execute.Win32.Kernel32.MEM_RESERVE, Execute.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, Execute.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, Execute.Win32.Kernel32.MEM_RELEASE); return(pCallStub); }