Analysis/Get-MemoryAnalysis.ps1
|
function Get-MemoryAnalysis { <# .SYNOPSIS Analyzes process memory for suspicious indicators .DESCRIPTION Examines running processes for memory-based threats including code injection, hollowing, and suspicious memory patterns .PARAMETER ProcessId Specific process ID to analyze (optional) .PARAMETER CheckAllProcesses Analyze all running processes .PARAMETER IncludeStrings Extract suspicious strings from process memory .EXAMPLE Get-MemoryAnalysis -CheckAllProcesses Get-MemoryAnalysis -ProcessId 1234 -IncludeStrings .OUTPUTS PSCustomObject with memory analysis results #> [CmdletBinding()] param( [Parameter()] [int]$ProcessId, [Parameter()] [switch]$CheckAllProcesses, [Parameter()] [switch]$IncludeStrings ) begin { Write-Host "Analyzing process memory..." -ForegroundColor Cyan # Check for admin privileges $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Warning "Administrator privileges recommended for complete memory analysis" } $results = [PSCustomObject]@{ AnalysisDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" ProcessAnalysis = @() InjectionIndicators = @() HollowingIndicators = @() SuspiciousModules = @() Statistics = @{ ProcessesAnalyzed = 0 InjectionIndicators = 0 HollowingIndicators = 0 SuspiciousModules = 0 } } # Suspicious module names $suspiciousModulePatterns = @( 'inject', 'hook', 'reflective', 'payload', 'shellcode', 'mimikatz', 'meterpreter', 'cobalt', 'beacon' ) } process { try { $processesToAnalyze = @() if ($ProcessId) { $proc = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue if ($proc) { $processesToAnalyze += $proc } else { Write-Error "Process with ID $ProcessId not found" return } } elseif ($CheckAllProcesses) { $processesToAnalyze = Get-Process | Where-Object { $_.Id -ne 0 -and $_.Id -ne 4 } } else { Write-Warning "No process specified. Use -ProcessId or -CheckAllProcesses" return } foreach ($process in $processesToAnalyze) { try { $results.Statistics.ProcessesAnalyzed++ Write-Verbose "Analyzing process: $($process.ProcessName) (PID: $($process.Id))" $processAnalysis = [PSCustomObject]@{ ProcessName = $process.ProcessName ProcessId = $process.Id Path = $process.Path StartTime = $process.StartTime Threads = $process.Threads.Count Modules = @() MemorySize = [math]::Round($process.WorkingSet64 / 1MB, 2) VirtualMemorySize = [math]::Round($process.VirtualMemorySize64 / 1MB, 2) PrivateMemorySize = [math]::Round($process.PrivateMemorySize64 / 1MB, 2) Findings = @() } # Get loaded modules try { $modules = $process.Modules foreach ($module in $modules) { $moduleName = $module.ModuleName.ToLower() $modulePath = $module.FileName # Check for suspicious modules $isSuspicious = $false $reason = "" foreach ($pattern in $suspiciousModulePatterns) { if ($moduleName -match $pattern) { $isSuspicious = $true $reason = "Module name matches suspicious pattern: $pattern" break } } # Check for modules loaded from suspicious locations if ($modulePath -match '(temp|appdata\\local\\temp|users\\public)') { $isSuspicious = $true $reason = "Module loaded from suspicious location" } if ($isSuspicious) { $results.SuspiciousModules += [PSCustomObject]@{ ProcessName = $process.ProcessName ProcessId = $process.Id ModuleName = $moduleName ModulePath = $modulePath Reason = $reason } $results.Statistics.SuspiciousModules++ } $processAnalysis.Modules += [PSCustomObject]@{ Name = $moduleName Path = $modulePath Size = [math]::Round($module.ModuleMemorySize / 1KB, 2) Suspicious = $isSuspicious } } } catch { Write-Verbose "Could not enumerate modules for $($process.ProcessName): $_" } # Check for process hollowing indicators # Look for mismatches between process image and memory if ($process.Path) { try { $mainModule = $process.MainModule # Compare main module with process path if ($mainModule.FileName -ne $process.Path) { $results.HollowingIndicators += [PSCustomObject]@{ ProcessName = $process.ProcessName ProcessId = $process.Id ExpectedPath = $process.Path ActualPath = $mainModule.FileName Reason = "Main module path mismatch - possible process hollowing" } $results.Statistics.HollowingIndicators++ $processAnalysis.Findings += "Possible process hollowing detected" } } catch { Write-Verbose "Could not check main module for $($process.ProcessName)" } } # Check for code injection indicators # Unusual memory characteristics $memoryRatio = if ($process.VirtualMemorySize64 -gt 0) { $process.WorkingSet64 / $process.VirtualMemorySize64 } else { 0 } if ($memoryRatio -gt 0.9) { $results.InjectionIndicators += [PSCustomObject]@{ ProcessName = $process.ProcessName ProcessId = $process.Id Indicator = "High memory ratio" Value = [math]::Round($memoryRatio, 2) Reason = "Unusually high working set to virtual memory ratio - possible injection" } $results.Statistics.InjectionIndicators++ $processAnalysis.Findings += "Unusual memory characteristics" } # Check for suspicious thread count $threadCount = $process.Threads.Count if ($threadCount -gt 100) { $processAnalysis.Findings += "Unusually high thread count: $threadCount" } # Check for remote threads try { foreach ($thread in $process.Threads) { # Remote threads often have start addresses outside main module if ($thread.StartAddress -and $process.MainModule) { $moduleBase = $process.MainModule.BaseAddress.ToInt64() $moduleEnd = $moduleBase + $process.MainModule.ModuleMemorySize $startAddr = $thread.StartAddress.ToInt64() if ($startAddr -lt $moduleBase -or $startAddr -gt $moduleEnd) { $results.InjectionIndicators += [PSCustomObject]@{ ProcessName = $process.ProcessName ProcessId = $process.Id Indicator = "Remote thread detected" ThreadId = $thread.Id Reason = "Thread start address outside main module - possible remote injection" } $results.Statistics.InjectionIndicators++ break # Only report once per process } } } } catch { Write-Verbose "Could not analyze threads for $($process.ProcessName)" } # Extract strings from memory if requested (basic implementation) if ($IncludeStrings) { try { # This is a simplified version - full string extraction requires more complex memory reading $processAnalysis.Findings += "String extraction requires external tools like Sysinternals Strings" } catch { Write-Verbose "Could not extract strings from $($process.ProcessName)" } } $results.ProcessAnalysis += $processAnalysis } catch { Write-Verbose "Error analyzing process $($process.ProcessName): $_" } } } catch { Write-Error "Error during memory analysis: $_" throw } } end { Write-Host "`nMemory Analysis Complete!" -ForegroundColor Green Write-Host "`n=== Statistics ===" -ForegroundColor Cyan Write-Host "Processes analyzed: $($results.Statistics.ProcessesAnalyzed)" -ForegroundColor Yellow Write-Host "Injection indicators: $($results.Statistics.InjectionIndicators)" -ForegroundColor $(if ($results.Statistics.InjectionIndicators -gt 0) { 'Red' } else { 'Green' }) Write-Host "Hollowing indicators: $($results.Statistics.HollowingIndicators)" -ForegroundColor $(if ($results.Statistics.HollowingIndicators -gt 0) { 'Red' } else { 'Green' }) Write-Host "Suspicious modules: $($results.Statistics.SuspiciousModules)" -ForegroundColor $(if ($results.Statistics.SuspiciousModules -gt 0) { 'Red' } else { 'Green' }) return $results } } |