0

I have the following powershell code to dump the process memory from a given process. In this case, cmd.exe.

It opens the given process with PROCESS_ALL_ACCESS and uses VirtualQueryEx to list all the memory regions. For each memory region it finds, it calls ReadProcessMemory and writes the bytes read to a file.

$typeDefinition = @"
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

public static partial class MemApi
{
    [StructLayout(LayoutKind.Sequential)]
    public struct MEMORY_BASIC_INFORMATION64
    {
        public ulong BaseAddress;
        public ulong AllocationBase;
        public int AllocationProtect;
        public int __alignment1;
        public ulong RegionSize;
        public int State;
        public int Protect;
        public int Type;
        public int __alignment2;
    }

    [Flags]
    public enum ProcessAccessFlags : uint
    {
        VM_READ = 0x00000010,
        PROCESS_ALL_ACCESS = 0x001F0FFF
    }
    [DllImport("kernel32.dll")]
    public static extern uint GetLastError();

    [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
    public static extern IntPtr OpenProcess(
        ProcessAccessFlags dwAccess,
        [MarshalAs(UnmanagedType.Bool)]
        bool bInheritHandle,
        int dwProcId
    );

    [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
    public static extern int VirtualQueryEx(
        IntPtr hProc,
        IntPtr lpAddr,
        out MEMORY_BASIC_INFORMATION64 lpBuf,
        int dwLen
    );

    public static List<MEMORY_BASIC_INFORMATION64> ListMemoryRegions(
        IntPtr hProc
    )
    {
        long maxAddr = 0x00007FFFFFFFFFFF;
        long addr = 0;
        List<MEMORY_BASIC_INFORMATION64> regions = new List<MEMORY_BASIC_INFORMATION64>();

        do
        {
            MEMORY_BASIC_INFORMATION64 m;

            int result = VirtualQueryEx(hProc, (IntPtr)addr, out m, (int)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION64)));

            if (addr == (long)m.BaseAddress + (long)m.RegionSize)
                break;

            addr = (long)m.BaseAddress + (long)m.RegionSize;

            regions.Add(m);
        } while (addr <= maxAddr);

        return regions;
    }

    [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
    public static extern Boolean ReadProcessMemory(
        IntPtr hProc,
        IntPtr lpBaseAddr,
        byte[] lpBuf,
        UInt32 nSize,
        ref UInt32 lpNBytes
    );
}
"@

Add-Type -TypeDefinition $typeDefinition -Language CSharp

$cmd = Get-Process -Name cmd

$handle = [MemApi]::OpenProcess(0x1F0FFF, $false, $cmd.Id)
$regions = [MemApi]::ListMemoryRegions($handle)
$fileStream = [System.IO.File]::OpenWrite("$env:TEMP\cmd.DMP")


Write-Host "$($regions.Count) memory segments have been found"

for ($i = 0; $i -lt $regions.Count; $i++)
{
    Write-Host "Exporting memory region $("{0:d4}/{1:d4}" -f $i, ($regions.Count + 1)) ($("0x{0:X16} - 0x{1:X16}" -f $regions[$i].BaseAddress, ($regions[$i].BaseAddress + $regions[$i].RegionSize)))..."
    
    try
    {
        $read = 0
        $buff = New-Object byte[] $regions[$i].RegionSize

        if ([MemApi]::ReadProcessMemory($handle, [System.IntPtr]::new($regions[$i].BaseAddress), $buff, $buff.Length, [ref]$read) -eq $true)
        {
            $fileStream.Write($buff, 0, $buff.Length)
        }
    } catch { } finally { }
}
    
$fileStream.Close()

I would like to avoid using dbghelp.dll to do this, but is there a way I can create a MiniDump file out of this raw memory? Would I need to change the code at all or is there an external tool that can convert this?

I've tried to take a look into the MiniDumpWriteDump function to see if I can replicate it at all but I've had no luck.

I've also tried volatility's raw2dmp function, but this has not worked unfortunately.

Apologies if this is completely on the wrong tracks!

Any help would be much appreciated - Merry Christmas!

meep
  • 334
  • 3
  • 10
  • If you want to avoid dbghelp.dll then you would have to replace its code with your own. But why bother? – Dialecticus Dec 16 '22 at 10:13
  • I'm sure you can turn the memory regions into a valid minidump file, so long as you follow the (undocumented) file format. It's a *lot* of work, and you're best off using the interfaces provided by the system (i.e. `dbghelp.dll`). Incidentally, why do you want to avoid using `dbghelp.dll`? – IInspectable Dec 16 '22 at 10:13
  • @Dialecticus @IInspectable I'm mainly looking at these type of functions from a security point of view. I've found that the functions from `dbghelp.dll` get flagged by AV often when I started to use the `MiniDumpWriteDump` function. The function would work, however it would still be flagged. The method above does not flag my AV, however I am unable to parse/debug it if that makes sense. Most debuggers/software accept minidumps mainly as I'm sure you know. Thanks for the swift replies! – meep Dec 16 '22 at 10:21
  • Dbghelp.dll is signed by Microsoft. You can verify this executing the following line in Powershell: `Get-AuthenticodeSignature -FilePath C:\Windows\System32\dbghelp.dll`. If Antivirus software complains about libraries signed by Microsoft then that's the problem with AV software. – Dialecticus Dec 16 '22 at 10:41
  • Check that your dbghelp.dll is not in fact replaced. If it's not you should not waste your time circumventing faulty AV software. Just add the library to the AV's white list, and walk away happy. – Dialecticus Dec 16 '22 at 10:43
  • @Dialecticus The AV software isn't flagging the DLL file, it's flagging the script for using the functions from the DLL file in what looks to be a malicious way since I am dumping memory from random processes. Ultimately, I would like to see if there is a way to create a minidump from raw memory dumps as the above script has not flagged AV. – meep Dec 16 '22 at 10:45
  • I would recommend to whitelist your script. Dbghelp.dll is not easily replaceable. You may spend a week or two trying, and then place the script in the white list, or you can do it right now. – Dialecticus Dec 16 '22 at 10:49
  • @Dialecticus the AV side isn't really what my question is about, I just want to know if there's a way I can convert the output above to a MiniDump file. If not, that's fine or if there's an alternative way to `MiniDumpWriteDump`, then even better. Whitelisting scripts and files on AV is out of scope for what this is. Thanks for the suggestions though. – meep Dec 16 '22 at 11:02
  • 1
    As others have pointed out, attempting to re-implement DbgHelp in PowerShell is an interesting intellectual exercise, but commercially speaking a waste of time, especially given that even if you succeed, you have no guarantee that a future release won't simply break things by allowing/requiring more information in the minidump format. I would in fact not even bother calling DbgHelp directly but use dedicated tools like [ProcDump](https://learn.microsoft.com/sysinternals/downloads/procdump). With some luck the AV might even be cool with that rather than your script doing it directly. – Jeroen Mostert Dec 16 '22 at 11:14
  • @JeroenMostert I've had a look at tools such as ProcDump and was thinking the same thing to be honest, it's a shame as I would have loved to create my own implementation but I think you are right in saying it most likely is a waste of time. Thank you for your reply! – meep Dec 16 '22 at 11:18

0 Answers0