Forensics/Export-MemoryDump.ps1

function Invoke-WsaExternalDumpCommand {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$FilePath,

        [Parameter()]
        [string[]]$ArgumentList = @(),

        [Parameter()]
        [ValidateRange(1, 86400)]
        [int]$TimeoutSeconds = 300
    )

    $invokeResult = New-Object PSObject -Property ([ordered]@{
        ExitCode = $null
        Output = @()
        TimedOut = $false
        ErrorMessage = $null
    })
    $job = $null

    try {
        $job = Start-Job -ScriptBlock {
            param($CommandPath, $CommandArguments)

            $commandOutput = @(& $CommandPath @CommandArguments 2>&1)
            [PSCustomObject]@{
                ExitCode = $LASTEXITCODE
                Output = $commandOutput
            }
        } -ArgumentList $FilePath, (, $ArgumentList)

        $completedJob = $job | Wait-Job -Timeout $TimeoutSeconds
        if ($null -eq $completedJob) {
            $invokeResult.TimedOut = $true
            $invokeResult.ErrorMessage = "External dump command exceeded $TimeoutSeconds seconds."
            $job | Stop-Job -ErrorAction SilentlyContinue
        }
        else {
            $jobResult = $job | Receive-Job
            if ($null -eq $jobResult) {
                $invokeResult.ErrorMessage = 'External dump command returned no completion result.'
            }
            else {
                $invokeResult.ExitCode = $jobResult.ExitCode
                $invokeResult.Output = @($jobResult.Output)
            }
        }
    }
    catch {
        $invokeResult.ErrorMessage = $_.Exception.Message
    }
    finally {
        if ($null -ne $job) {
            $job | Remove-Job -Force -ErrorAction SilentlyContinue
        }
    }

    return $invokeResult
}

function Get-WsaDumpFileValidation {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $validation = New-Object PSObject -Property ([ordered]@{
        IsValid = $false
        FileSize = [long]0
        ErrorMessage = $null
    })

    try {
        if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
            $validation.ErrorMessage = 'Dump file was not created.'
            return $validation
        }

        $dumpItem = Get-Item -LiteralPath $Path -ErrorAction Stop
        $validation.FileSize = [long]$dumpItem.Length
        if ($dumpItem.Length -le 0) {
            $validation.ErrorMessage = 'Dump file is empty.'
            return $validation
        }

        $stream = $null
        $signature = [byte[]]@(0, 0, 0, 0)
        $bytesRead = 0
        try {
            $stream = [System.IO.File]::Open(
                $Path,
                [System.IO.FileMode]::Open,
                [System.IO.FileAccess]::Read,
                [System.IO.FileShare]::ReadWrite)
            $bytesRead = $stream.Read($signature, 0, $signature.Length)
        }
        finally {
            if ($null -ne $stream) { $stream.Dispose() }
        }

        if ($bytesRead -ne 4 -or
            $signature[0] -ne 0x4D -or $signature[1] -ne 0x44 -or
            $signature[2] -ne 0x4D -or $signature[3] -ne 0x50) {
            $validation.ErrorMessage = 'Dump file does not have a valid MDMP signature.'
            return $validation
        }

        $validation.IsValid = $true
    }
    catch {
        $validation.ErrorMessage = "Dump file validation failed: $($_.Exception.Message)"
    }

    return $validation
}

function Export-MemoryDump {
    <#
    .SYNOPSIS
        Creates memory dumps for forensic analysis
    .DESCRIPTION
        Captures memory dumps of specific processes or full system memory using
        multiple methods. Supports live system memory acquisition, process dumps,
        and LSASS dumps for credential analysis.
    .PARAMETER ProcessName
        Name of specific process to dump (e.g., 'lsass', 'chrome')
    .PARAMETER ProcessId
        Process ID to dump
    .PARAMETER DumpType
        Type of dump: Full, Mini, or LSASS
    .PARAMETER OutputPath
        Path to save memory dump files (default: C:\MemoryDumps)
    .PARAMETER UseProcDump
        Use Sysinternals ProcDump if available (more reliable)
    .PARAMETER Compress
        Compress dump file after creation
    .EXAMPLE
        Export-MemoryDump -ProcessName lsass -OutputPath "C:\Forensics"
        Export-MemoryDump -ProcessId 1234 -DumpType Full
    .OUTPUTS
        PSCustomObject with dump file information
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param(
        [Parameter()]
        [string]$ProcessName,
        
        [Parameter()]
        [int]$ProcessId,
        
        [Parameter()]
        [ValidateSet('Full', 'Mini', 'LSASS')]
        [string]$DumpType = 'Mini',
        
        [Parameter()]
        [string]$OutputPath = 'C:\MemoryDumps',
        
        [Parameter()]
        [switch]$UseProcDump,
        
        [Parameter()]
        [switch]$Compress
    )
    
    begin {
        $effectiveLanguageMode = Get-WsaEffectiveLanguageMode
        $dynamicCompilationExpected = Test-WsaDynamicCompilationExpected

        $result = New-Object PSObject -Property ([ordered]@{
            Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            ComputerName = $env:COMPUTERNAME
            DumpType = $DumpType
            ProcessName = $null
            ProcessId = $null
            DumpFile = $null
            FileSize = 0
            Success = $false
            Method = $null
            ErrorMessage = $null
            EffectiveLanguageMode = $effectiveLanguageMode
            DynamicCompilationExpected = $dynamicCompilationExpected
            UnavailableReason = $null
        })

        $languageModeUnavailable = ($effectiveLanguageMode -ne 'FullLanguage')
        if ($languageModeUnavailable) {
            $result.UnavailableReason = $effectiveLanguageMode
            $result.ErrorMessage = "Memory dump acquisition requires FullLanguage; effective language mode '$effectiveLanguageMode' cannot safely invoke the supported acquisition methods."
            Write-Warning "Export-MemoryDump: memory dump acquisition is unavailable in effective language mode '$effectiveLanguageMode'."
        }
        else {
            Write-Host "=== MEMORY DUMP EXPORT ===" -ForegroundColor Cyan
            Write-Host "Dump Type: $DumpType" -ForegroundColor Yellow
            Write-Host "Output Path: $OutputPath" -ForegroundColor Yellow

            # Check if running as Administrator (Windows-only check; the WindowsPrincipal
            # APIs do not exist on other platforms)
            if ($env:OS -eq 'Windows_NT') {
                $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

                if (-not $isAdmin) {
                    throw "This function requires Administrator privileges!"
                }
            }

            # Create output directory
            if (-not (Test-Path $OutputPath)) {
                if ($PSCmdlet.ShouldProcess($OutputPath, 'Create output directory')) {
                    New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
                    Write-Host "Created output directory: $OutputPath" -ForegroundColor Green
                }
            }

            # Memory dumps (LSASS especially) can contain plaintext credentials, so restrict
            # the dump file to Administrators and SYSTEM instead of the inherited default
            # that grants every local user read access (mirrors the generated-script lockdown
            # in Register-ScheduledSecurityScan)
            function Protect-DumpFile {
                param([Parameter(Mandatory = $true)][string]$Path)
                if ($env:OS -ne 'Windows_NT') { return $true }
                $requiredSids = @('S-1-5-18', 'S-1-5-32-544')
                try {
                    $fileAcl = Get-Acl -Path $Path -ErrorAction Stop
                    $fileAcl.SetAccessRuleProtection($true, $false)
                    foreach ($existingRule in @($fileAcl.Access)) {
                        $null = $fileAcl.RemoveAccessRule($existingRule)
                    }
                    foreach ($sidString in $requiredSids) {
                        $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
                            (New-Object System.Security.Principal.SecurityIdentifier($sidString)),
                            [System.Security.AccessControl.FileSystemRights]::FullControl,
                            [System.Security.AccessControl.AccessControlType]::Allow)
                        $fileAcl.AddAccessRule($accessRule)
                    }
                    Set-Acl -Path $Path -AclObject $fileAcl -ErrorAction Stop
                }
                catch {
                    Write-Warning "Could not restrict permissions on '$Path'; the dump may contain credentials and may be readable by other local users: $_"
                    return $false
                }

                $appliedAcl = Get-Acl -Path $Path -ErrorAction SilentlyContinue
                if ($null -eq $appliedAcl) {
                    Write-Warning "ACL verification FAILED on '$Path': could not re-read the ACL; the dump may contain credentials and may be readable by other local users."
                    return $false
                }
                $missingSids = @($requiredSids | Where-Object {
                    $sid = $_
                    -not @($appliedAcl.Access | Where-Object { Test-IdentityReferenceIsSid -IdentityReference $_.IdentityReference -Sid $sid }).Count
                })
                if ($missingSids.Count -gt 0) {
                    Write-Warning "ACL verification FAILED on '$Path': missing access rules for SID(s) $($missingSids -join ', '); the dump may contain credentials and may be readable by other local users."
                    return $false
                }
                return $true
            }

            # Pre-create the dump target as an empty, ACL-locked file so the dump content
            # is never written to a world-readable file (closes the create -> protect race:
            # writers that open the existing file inherit the locked ACL instead of the
            # permissive directory default)
            function Initialize-LockedDumpFile {
                param([Parameter(Mandatory = $true)][string]$Path)
                if (-not (Test-Path $Path)) {
                    New-Item -Path $Path -ItemType File -Force | Out-Null
                }
                $null = Protect-DumpFile -Path $Path
            }
        }
    }
    
    process {
        if ($languageModeUnavailable) { return }

        try {
            # Determine target process
            $targetProcess = $null
            
            if ($DumpType -eq 'LSASS') {
                Write-Host "`n[*] Targeting LSASS process for credential dump..." -ForegroundColor Cyan
                Write-Warning "LSASS dumps may trigger antivirus alerts!"
                
                $targetProcess = Get-Process -Name lsass -ErrorAction SilentlyContinue
                if (-not $targetProcess) {
                    Write-Error "Could not find LSASS process"
                    return
                }
                $ProcessName = 'lsass'
            }
            elseif ($ProcessId) {
                Write-Host "`n[*] Targeting Process ID: $ProcessId..." -ForegroundColor Cyan
                $targetProcess = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
                if (-not $targetProcess) {
                    Write-Error "Process with ID $ProcessId not found. Use Get-Process to list running processes."
                    return
                }
                $ProcessName = $targetProcess.ProcessName
            }
            elseif ($ProcessName) {
                Write-Host "`n[*] Targeting Process: $ProcessName..." -ForegroundColor Cyan
                $targetProcess = Get-Process -Name $ProcessName -ErrorAction SilentlyContinue | Select-Object -First 1
                if (-not $targetProcess) {
                    Write-Error "Process '$ProcessName' not found. Use Get-Process to list running processes."
                    return
                }
            }
            else {
                Write-Error "No process specified. Use -ProcessName, -ProcessId, or -DumpType LSASS"
                return
            }
            
            $result.ProcessName = $targetProcess.ProcessName
            $result.ProcessId = $targetProcess.Id
            
            Write-Host "Process: $($targetProcess.ProcessName) (PID: $($targetProcess.Id))" -ForegroundColor Green
            
            # Generate dump filename
            $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
            $dumpFileName = "$($targetProcess.ProcessName)_$($targetProcess.Id)_${timestamp}.dmp"
            $dumpFilePath = Join-Path $OutputPath $dumpFileName
            $dumpTarget = "Process '$($targetProcess.ProcessName)' (PID: $($targetProcess.Id))"
            
            # METHOD 1: Try ProcDump if available and requested
            if ($UseProcDump) {
                Write-Host "`n[1/3] Attempting ProcDump method..." -ForegroundColor Cyan
                
                # Only search admin-controlled fixed paths. This function runs elevated
                # (and can target LSASS), so a procdump.exe planted in a user-writable
                # location (TEMP, current directory) would execute attacker-controlled
                # code with elevated privileges
                $procDumpPaths = @(
                    "C:\Tools\procdump.exe",
                    "C:\Tools\procdump64.exe"
                )
                
                $foundProcDump = $null
                foreach ($path in $procDumpPaths) {
                    if (Test-Path $path) {
                        $foundProcDump = $path
                        break
                    }
                }
                
                if ($foundProcDump) {
                    Write-Host " Found ProcDump: $foundProcDump" -ForegroundColor Green
                    
                    $procDumpArgs = @(
                        "-accepteula",
                        "-ma",  # Full dump
                        $targetProcess.Id,
                        "`"$dumpFilePath`""
                    )
                    
                    if ($PSCmdlet.ShouldProcess($dumpTarget, 'Create memory dump')) {
                        try {
                            # Pre-create and ACL-lock the empty target so procdump writes
                            # into an already-locked file
                            Initialize-LockedDumpFile -Path $dumpFilePath

                            $procDumpResult = Invoke-WsaExternalDumpCommand -FilePath $foundProcDump -ArgumentList $procDumpArgs
                            if ($procDumpResult.TimedOut) {
                                throw "ProcDump timed out: $($procDumpResult.ErrorMessage)"
                            }
                            if ($procDumpResult.ExitCode -ne 0) {
                                throw "ProcDump exited with code '$($procDumpResult.ExitCode)'. $($procDumpResult.ErrorMessage)"
                            }

                            $validation = Get-WsaDumpFileValidation -Path $dumpFilePath
                            if (-not $validation.IsValid) {
                                throw "ProcDump created an invalid dump: $($validation.ErrorMessage)"
                            }

                            $result.Success = $true
                            $result.Method = "ProcDump"
                            $result.DumpFile = $dumpFilePath
                            Write-Host " Successfully created dump with ProcDump" -ForegroundColor Green
                        }
                        catch {
                            Write-Warning "ProcDump failed: $_"
                            $result.ErrorMessage = $_.Exception.Message
                            if (Test-Path -LiteralPath $dumpFilePath) {
                                Remove-Item -LiteralPath $dumpFilePath -Force -ErrorAction SilentlyContinue
                            }
                        }
                    }
                }
                else {
                    Write-Host " ProcDump not found, trying alternative methods..." -ForegroundColor Gray
                }
            }

            # METHOD 2: Use MiniDumpWriteDump with proper handle
            if (-not $result.Success -and -not $dynamicCompilationExpected) {
                if ($effectiveLanguageMode -eq 'FullLanguage') {
                    $result.UnavailableReason = 'DynamicCompilationUnavailable'
                }
                else {
                    $result.UnavailableReason = $effectiveLanguageMode
                }
                Write-Warning "Export-MemoryDump: MiniDumpWriteDump is unavailable in effective language mode '$effectiveLanguageMode'; trying the existing external-tool fallback when applicable."
            }
            if (-not $result.Success -and $dynamicCompilationExpected -and $PSCmdlet.ShouldProcess($dumpTarget, 'Create memory dump')) {
                Write-Host "`n[2/3] Attempting MiniDumpWriteDump method..." -ForegroundColor Cyan
                
                $processHandle = [IntPtr]::Zero
                $fileStream = $null
                
                try {
                    # FIXED: Define complete P/Invoke signatures with proper types
                    Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
 
public class ProcessDump {
    [Flags]
    public enum ProcessAccessFlags : uint {
        PROCESS_ALL_ACCESS = 0x001F0FFF,
        PROCESS_VM_READ = 0x00000010,
        PROCESS_QUERY_INFORMATION = 0x00000400
    }
     
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr OpenProcess(
        ProcessAccessFlags processAccess,
        bool bInheritHandle,
        int processId
    );
     
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool CloseHandle(IntPtr hObject);
     
    [DllImport("dbghelp.dll", SetLastError = true)]
    public static extern bool MiniDumpWriteDump(
        IntPtr hProcess,
        uint ProcessId,
        IntPtr hFile,
        uint DumpType,
        IntPtr ExceptionParam,
        IntPtr UserStreamParam,
        IntPtr CallbackParam
    );
}
"@
 -ErrorAction SilentlyContinue
                    
                    # FIXED: Open process handle with OpenProcess API
                    $processHandle = [ProcessDump]::OpenProcess(
                        [ProcessDump+ProcessAccessFlags]::PROCESS_ALL_ACCESS,
                        $false,
                        $targetProcess.Id
                    )
                    
                    if ($processHandle -eq [IntPtr]::Zero) {
                        $lastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
                        throw "Failed to open process handle. Error code: $lastError (May need SeDebugPrivilege or SYSTEM access)"
                    }
                    
                    Write-Host " Process handle obtained successfully" -ForegroundColor Green
                    
                    # Create dump file: pre-create and ACL-lock the empty target first
                    # so the dump content is written into an already-locked file
                    # (File::Create on an existing file preserves its security descriptor)
                    Initialize-LockedDumpFile -Path $dumpFilePath
                    $fileStream = [System.IO.File]::Create($dumpFilePath)
                    
                    # Determine dump type flag
                    $dumpTypeFlag = switch ($DumpType) {
                        'Full' { 2 }    # MiniDumpWithFullMemory
                        'Mini' { 0 }    # MiniDumpNormal
                        'LSASS' { 2 }   # Full dump for LSASS
                    }
                    
                    Write-Host " Creating dump file..." -ForegroundColor Gray
                    
                    # Create the dump
                    $success = [ProcessDump]::MiniDumpWriteDump(
                        $processHandle,
                        [uint32]$targetProcess.Id,
                        $fileStream.SafeFileHandle.DangerousGetHandle(),
                        $dumpTypeFlag,
                        [IntPtr]::Zero,
                        [IntPtr]::Zero,
                        [IntPtr]::Zero
                    )
                    
                    $fileStream.Close()
                    $fileStream = $null
                    [ProcessDump]::CloseHandle($processHandle) | Out-Null
                    $processHandle = [IntPtr]::Zero
                    
                    if ($success) {
                        $validation = Get-WsaDumpFileValidation -Path $dumpFilePath
                        if (-not $validation.IsValid) {
                            throw "MiniDumpWriteDump created an invalid dump: $($validation.ErrorMessage)"
                        }

                        $result.Success = $true
                        $result.Method = "MiniDumpWriteDump"
                        $result.DumpFile = $dumpFilePath
                        Write-Host " Successfully created dump with MiniDumpWriteDump" -ForegroundColor Green
                    }
                    else {
                        $lastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
                        throw "MiniDumpWriteDump failed with error code: $lastError"
                    }
                }
                catch {
                    Write-Warning "MiniDumpWriteDump failed: $_"
                    $result.ErrorMessage = $_.Exception.Message
                    
                    # Release resources if the attempt failed after they were acquired
                    if ($null -ne $fileStream) { $fileStream.Dispose() }
                    if ($processHandle -ne [IntPtr]::Zero) { [ProcessDump]::CloseHandle($processHandle) | Out-Null }
                    
                    # Clean up failed dump file
                    if (Test-Path $dumpFilePath) {
                        Remove-Item $dumpFilePath -Force -ErrorAction SilentlyContinue
                    }
                }
            }
            
            # METHOD 3: Use rundll32.exe comsvcs.dll method (LSASS specific)
            if (-not $result.Success -and $DumpType -eq 'LSASS' -and $PSCmdlet.ShouldProcess($dumpTarget, 'Create memory dump (LSASS)')) {
                Write-Host "`n[3/3] Attempting comsvcs.dll method (LSASS specific)..." -ForegroundColor Cyan

                try {
                    $comsvcsPath = "$env:SystemRoot\System32\comsvcs.dll"
                    if (-not (Test-Path $comsvcsPath)) {
                        throw "comsvcs.dll was not found at '$comsvcsPath'."
                    }

                    Write-Host " Using rundll32.exe with comsvcs.dll..." -ForegroundColor Gray
                    $arguments = @(
                        $comsvcsPath,
                        "MiniDump",
                        $targetProcess.Id,
                        "`"$dumpFilePath`"",
                        "full"
                    )

                    # The function already requires elevation. Pre-create and ACL-lock the
                    # target so rundll32 inherits the locked ACL when it writes the dump.
                    Initialize-LockedDumpFile -Path $dumpFilePath
                    $rundll32Path = "$env:SystemRoot\System32\rundll32.exe"
                    $comsvcsResult = Invoke-WsaExternalDumpCommand -FilePath $rundll32Path -ArgumentList $arguments
                    if ($comsvcsResult.TimedOut) {
                        throw "comsvcs.dll timed out: $($comsvcsResult.ErrorMessage)"
                    }
                    if ($comsvcsResult.ExitCode -ne 0) {
                        throw "comsvcs.dll exited with code '$($comsvcsResult.ExitCode)'. $($comsvcsResult.ErrorMessage)"
                    }

                    $validation = Get-WsaDumpFileValidation -Path $dumpFilePath
                    if (-not $validation.IsValid) {
                        throw "comsvcs.dll created an invalid dump: $($validation.ErrorMessage)"
                    }

                    $result.Success = $true
                    $result.Method = "comsvcs.dll"
                    $result.DumpFile = $dumpFilePath
                    Write-Host " Successfully created LSASS dump with comsvcs.dll" -ForegroundColor Green
                }
                catch {
                    Write-Warning "comsvcs.dll method failed: $_"
                    $result.ErrorMessage = $_.Exception.Message
                    if (Test-Path -LiteralPath $dumpFilePath) {
                        Remove-Item -LiteralPath $dumpFilePath -Force -ErrorAction SilentlyContinue
                    }
                }
            }

            if (-not $result.Success -and -not $dynamicCompilationExpected -and [string]::IsNullOrEmpty($result.ErrorMessage)) {
                $result.ErrorMessage = "MiniDumpWriteDump was skipped because dynamic compilation is not expected in effective language mode '$effectiveLanguageMode'."
            }

            # Check final result
            if ($result.Success -and (Test-Path $dumpFilePath)) {
                $dumpFile = Get-Item $dumpFilePath
                $result.FileSize = $dumpFile.Length
                $null = Protect-DumpFile -Path $dumpFilePath

                $fileSizeMB = [Math]::Round($dumpFile.Length / 1MB, 2)
                Write-Host "`n[SUCCESS] Memory dump created!" -ForegroundColor Green
                Write-Host " File: $dumpFilePath" -ForegroundColor Cyan
                Write-Host " Size: $fileSizeMB MB" -ForegroundColor Cyan
                Write-Host " Method: $($result.Method)" -ForegroundColor Cyan
                
                # Compress if requested
                if ($Compress) {
                    Write-Host "`n[*] Compressing dump file..." -ForegroundColor Cyan
                    
                    try {
                        $zipPath = "$dumpFilePath.zip"
                        Compress-Archive -Path $dumpFilePath -DestinationPath $zipPath -CompressionLevel Optimal
                        
                        if (Test-Path $zipPath) {
                            $null = Protect-DumpFile -Path $zipPath
                            $zipFile = Get-Item $zipPath
                            $zipSizeMB = [Math]::Round($zipFile.Length / 1MB, 2)
                            $compressionRatio = [Math]::Round((1 - ($zipFile.Length / $dumpFile.Length)) * 100, 1)
                            
                            Write-Host " Compressed: $zipPath" -ForegroundColor Green
                            Write-Host " Compressed Size: $zipSizeMB MB" -ForegroundColor Cyan
                            Write-Host " Compression Ratio: $compressionRatio%" -ForegroundColor Cyan
                            
                            # Optionally remove uncompressed file
                            Write-Host "`n Remove original dump file? (Y/N)" -ForegroundColor Yellow -NoNewline
                            $removeOriginal = Read-Host
                            if ($removeOriginal -eq 'Y') {
                                Remove-Item $dumpFilePath -Force
                                Write-Host " Removed original dump file" -ForegroundColor Gray
                                $result.DumpFile = $zipPath
                            }
                        }
                    }
                    catch {
                        Write-Warning "Compression failed: $_"
                    }
                }
                
                # Security recommendations
                Write-Host "`n[!] SECURITY RECOMMENDATIONS:" -ForegroundColor Yellow
                Write-Host " 1. Store dump files securely (contains sensitive data)" -ForegroundColor Yellow
                Write-Host " 2. Encrypt dump files before transferring" -ForegroundColor Yellow
                Write-Host " 3. Delete dump files after analysis" -ForegroundColor Yellow
                if ($DumpType -eq 'LSASS') {
                    Write-Host " 4. LSASS dumps may contain plaintext credentials!" -ForegroundColor Red
                    Write-Host " 5. Use Mimikatz or pypykatz for credential extraction" -ForegroundColor Yellow
                }
            }
            else {
                Write-Host "`n[FAILED] Could not create memory dump" -ForegroundColor Red
                Write-Host "Possible reasons:" -ForegroundColor Yellow
                Write-Host " - Insufficient permissions (need SeDebugPrivilege or SYSTEM)" -ForegroundColor Yellow
                Write-Host " - Process is protected (PPL - Protected Process Light)" -ForegroundColor Yellow
                Write-Host " - Antivirus/EDR blocking the operation" -ForegroundColor Yellow
                Write-Host " - Insufficient disk space" -ForegroundColor Yellow
                
                if ($DumpType -eq 'LSASS') {
                    Write-Host "`nFor LSASS dumps, try:" -ForegroundColor Cyan
                    Write-Host " - Using ProcDump: procdump.exe -accepteula -ma lsass.exe lsass.dmp" -ForegroundColor Gray
                    Write-Host " - Task Manager: Right-click lsass.exe > Create dump file" -ForegroundColor Gray
                    Write-Host " - Run PowerShell as SYSTEM: psexec -s -i powershell.exe" -ForegroundColor Gray
                }
            }
            
        }
        catch {
            Write-Error "Error during memory dump: $_"
            $result.Success = $false
            $result.ErrorMessage = $_.Exception.Message
        }
    }
    
    end {
        return $result
    }
}