Forensics/Get-ArtifactCollection.ps1

function Get-ArtifactCollection {
    <#
    .SYNOPSIS
        Collects comprehensive forensic artifacts from Windows systems
    .DESCRIPTION
        KAPE-style artifact collection tool that gathers system information,
        event logs, registry hives, browser history, execution artifacts,
        and other forensic evidence for incident response and investigation.
    .PARAMETER OutputPath
        Root path for artifact collection (default: C:\ForensicCollection)
    .PARAMETER CollectionProfile
        Predefined collection profile: Quick, Standard, Full, or Custom
    .PARAMETER IncludeEventLogs
        Collect Windows event logs
    .PARAMETER IncludeRegistry
        Collect registry hives
    .PARAMETER IncludeBrowserHistory
        Collect browser artifacts
    .PARAMETER IncludeExecutionArtifacts
        Collect prefetch, UserAssist, etc.
    .PARAMETER IncludeNetworkInfo
        Collect network configuration and connections
    .PARAMETER CompressOutput
        Create ZIP archive of collected artifacts
    .PARAMETER RemoveSource
        Remove original files after compression
    .EXAMPLE
        Get-ArtifactCollection -CollectionProfile Standard
        Get-ArtifactCollection -OutputPath "E:\Evidence" -CollectionProfile Full -CompressOutput
    .OUTPUTS
        PSCustomObject with collection results
    #>

    
    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$OutputPath = "C:\ForensicCollection",
        
        [Parameter()]
        [ValidateSet('Quick', 'Standard', 'Full', 'Custom')]
        [string]$CollectionProfile = 'Standard',
        
        [Parameter()]
        [switch]$IncludeEventLogs,
        
        [Parameter()]
        [switch]$IncludeRegistry,
        
        [Parameter()]
        [switch]$IncludeBrowserHistory,
        
        [Parameter()]
        [switch]$IncludeExecutionArtifacts,
        
        [Parameter()]
        [switch]$IncludeNetworkInfo,
        
        [Parameter()]
        [switch]$CompressOutput,
        
        [Parameter()]
        [switch]$RemoveSource
    )
    
    begin {
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host " FORENSIC ARTIFACT COLLECTION TOOL" -ForegroundColor Cyan
        Write-Host " KAPE-Style Evidence Gathering" -ForegroundColor Cyan
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host "Collection Profile: $CollectionProfile" -ForegroundColor Yellow
        Write-Host "Output Path: $OutputPath" -ForegroundColor Yellow
        Write-Host "Start Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Yellow
        
        # Check 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 collection!"
        }

        $acquisitionStartUtc = [DateTime]::UtcNow

        $moduleVersion = 'Unknown'
        if ($null -ne $MyInvocation.MyCommand.Module -and $null -ne $MyInvocation.MyCommand.Module.Version) {
            $moduleVersion = $MyInvocation.MyCommand.Module.Version.ToString()
        }

        # Set collection flags based on profile
        switch ($CollectionProfile) {
            'Quick' {
                $IncludeEventLogs = $false
                $IncludeRegistry = $false
                $IncludeBrowserHistory = $false
                $IncludeExecutionArtifacts = $true
                $IncludeNetworkInfo = $true
            }
            'Standard' {
                $IncludeEventLogs = $true
                $IncludeRegistry = $false
                $IncludeBrowserHistory = $true
                $IncludeExecutionArtifacts = $true
                $IncludeNetworkInfo = $true
            }
            'Full' {
                $IncludeEventLogs = $true
                $IncludeRegistry = $true
                $IncludeBrowserHistory = $true
                $IncludeExecutionArtifacts = $true
                $IncludeNetworkInfo = $true
            }
        }
        
        # Create collection structure
        $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
        $collectionRoot = Join-Path $OutputPath "Collection_$timestamp"
        
        $paths = @{
            Root = $collectionRoot
            SystemInfo = Join-Path $collectionRoot "1_SystemInfo"
            EventLogs = Join-Path $collectionRoot "2_EventLogs"
            Registry = Join-Path $collectionRoot "3_Registry"
            FileSystem = Join-Path $collectionRoot "4_FileSystem"
            Browser = Join-Path $collectionRoot "5_Browser"
            Network = Join-Path $collectionRoot "6_Network"
            Execution = Join-Path $collectionRoot "7_Execution"
            Users = Join-Path $collectionRoot "8_Users"
        }
        
        foreach ($path in $paths.Values) {
            if (-not (Test-Path $path)) {
                New-Item -Path $path -ItemType Directory -Force | Out-Null
            }
        }
        
        Write-Host "Collection directory created: $collectionRoot" -ForegroundColor Green
        
        $results = [PSCustomObject]@{
            CollectionDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            ComputerName = $env:COMPUTERNAME
            Profile = $CollectionProfile
            OutputPath = $collectionRoot
            ArtifactsCollected = @()
            Summary = @{
                TotalFiles = 0
                TotalSize = 0
                Errors = 0
            }
            CompressedArchive = $null
        }
    }
    
    process {
        try {
            # 1. COLLECT SYSTEM INFORMATION
            Write-Host "`n[1/8] Collecting System Information..." -ForegroundColor Cyan
            
            try {
                # Computer info
                $computerInfo = Get-ComputerInfo -ErrorAction SilentlyContinue
                $computerInfo | ConvertTo-Json -Depth 5 | Out-File (Join-Path $paths.SystemInfo "ComputerInfo.json") -Encoding UTF8
                
                # OS Info
                Get-CimInstance Win32_OperatingSystem | 
                    Select-Object Caption, Version, BuildNumber, OSArchitecture, InstallDate, LastBootUpTime |
                    ConvertTo-Json | Out-File (Join-Path $paths.SystemInfo "OSInfo.json") -Encoding UTF8
                
                # Hardware Info
                Get-CimInstance Win32_ComputerSystem |
                    Select-Object Manufacturer, Model, TotalPhysicalMemory, NumberOfProcessors |
                    ConvertTo-Json | Out-File (Join-Path $paths.SystemInfo "Hardware.json") -Encoding UTF8
                
                # Disk Info
                Get-PSDrive -PSProvider FileSystem |
                    Select-Object Name, Used, Free, @{N='TotalGB';E={[Math]::Round($_.Used/1GB + $_.Free/1GB,2)}} |
                    ConvertTo-Json | Out-File (Join-Path $paths.SystemInfo "Disks.json") -Encoding UTF8
                
                # Installed Software
                Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* -ErrorAction SilentlyContinue |
                    Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
                    Where-Object { $_.DisplayName } |
                    ConvertTo-Json | Out-File (Join-Path $paths.SystemInfo "InstalledSoftware.json") -Encoding UTF8
                
                # Environment Variables
                Get-ChildItem Env: | ConvertTo-Json | Out-File (Join-Path $paths.SystemInfo "Environment.json") -Encoding UTF8
                
                Write-Host " System information collected" -ForegroundColor Green
                $results.ArtifactsCollected += "System Information"
            }
            catch {
                Write-Warning "Error collecting system info: $_"
                $results.Summary.Errors++
            }
            
            # 2. COLLECT EVENT LOGS
            if ($IncludeEventLogs) {
                Write-Host "`n[2/8] Collecting Event Logs..." -ForegroundColor Cyan
                
                $logNames = @('Security', 'System', 'Application', 'Microsoft-Windows-PowerShell/Operational')
                
                foreach ($logName in $logNames) {
                    try {
                        Write-Host " Exporting: $logName..." -ForegroundColor Gray
                        
                        $safeName = $logName -replace '/', '-'
                        $logPath = Join-Path $paths.EventLogs "$safeName.evtx"
                        
                        # Export using wevtutil (quote the destination: output path may contain spaces)
                        wevtutil epl $logName "$logPath" /ow:true 2>&1 | Out-Null
                        
                        if (Test-Path $logPath) {
                            Write-Host " Exported successfully" -ForegroundColor Green
                        }
                    }
                    catch {
                        Write-Warning "Could not export log $logName"
                    }
                }
                
                $results.ArtifactsCollected += "Event Logs"
            }
            
            # 3. COLLECT REGISTRY ARTIFACTS
            if ($IncludeRegistry) {
                Write-Host "`n[3/8] Collecting Registry Artifacts..." -ForegroundColor Cyan

                try {
                    # Save raw registry hives (reg save preserves the binary hive
                    # format, unlike reg export which produces lossy text).
                    # reg save requires elevation (SeBackupPrivilege).
                    $registryHives = @(
                        @{RegPath='HKLM\SOFTWARE'; Name='HKLM_SOFTWARE.hiv'},
                        @{RegPath='HKLM\SYSTEM'; Name='HKLM_SYSTEM.hiv'},
                        @{RegPath='HKLM\SAM'; Name='HKLM_SAM.hiv'},
                        @{RegPath='HKLM\SECURITY'; Name='HKLM_SECURITY.hiv'},
                        @{RegPath='HKCU'; Name='HKCU_NTUSER.hiv'}
                    )

                    foreach ($hive in $registryHives) {
                        try {
                            Write-Host " Saving hive: $($hive.RegPath)..." -ForegroundColor Gray

                            $savePath = Join-Path $paths.Registry $hive.Name

                            $regOutput = reg save $hive.RegPath "$savePath" /y 2>&1

                            if (Test-Path $savePath) {
                                Write-Host " Saved successfully" -ForegroundColor Green
                            }
                            else {
                                Write-Warning "Could not save registry hive $($hive.RegPath) (elevation required) - $regOutput"
                            }
                        }
                        catch {
                            Write-Warning "Could not save registry hive: $($hive.RegPath) - $_"
                        }
                    }

                    $results.ArtifactsCollected += "Registry Hives"
                }
                catch {
                    Write-Warning "Error collecting registry: $_"
                    $results.Summary.Errors++
                }
            }
            
            # 4. COLLECT FILE SYSTEM ARTIFACTS
            Write-Host "`n[4/8] Collecting File System Artifacts..." -ForegroundColor Cyan
            
            try {
                # Prefetch files
                $prefetchPath = "$env:SystemRoot\Prefetch"
                if (Test-Path $prefetchPath) {
                    $prefetchDest = Join-Path $paths.FileSystem "Prefetch"
                    New-Item -Path $prefetchDest -ItemType Directory -Force | Out-Null
                    Copy-Item "$prefetchPath\*.pf" -Destination $prefetchDest -Force -ErrorAction SilentlyContinue
                    Write-Host " Prefetch files collected" -ForegroundColor Green
                }
                
                # Recent files
                $recentPath = "$env:APPDATA\Microsoft\Windows\Recent"
                if (Test-Path $recentPath) {
                    $recentDest = Join-Path $paths.FileSystem "Recent"
                    New-Item -Path $recentDest -ItemType Directory -Force | Out-Null
                    Copy-Item "$recentPath\*" -Destination $recentDest -Force -Recurse -ErrorAction SilentlyContinue
                    Write-Host " Recent files collected" -ForegroundColor Green
                }
                
                $results.ArtifactsCollected += "File System Artifacts"
            }
            catch {
                Write-Warning "Error collecting file system artifacts: $_"
                $results.Summary.Errors++
            }
            
            # 5. COLLECT BROWSER ARTIFACTS
            if ($IncludeBrowserHistory) {
                Write-Host "`n[5/8] Collecting Browser Artifacts..." -ForegroundColor Cyan
                
                try {
                    # Chrome
                    $chromePath = "$env:LOCALAPPDATA\Google\Chrome\User Data\Default"
                    if (Test-Path $chromePath) {
                        $chromeDest = Join-Path $paths.Browser "Chrome"
                        New-Item -Path $chromeDest -ItemType Directory -Force | Out-Null
                        
                        # Copy History, Cookies, etc.
                        $chromeFiles = @('History', 'Cookies', 'Web Data', 'Login Data')
                        foreach ($file in $chromeFiles) {
                            $srcFile = Join-Path $chromePath $file
                            if (Test-Path $srcFile) {
                                Copy-Item $srcFile -Destination $chromeDest -Force -ErrorAction SilentlyContinue
                            }
                        }
                        Write-Host " Chrome artifacts collected" -ForegroundColor Green
                    }
                    
                    # Edge
                    $edgePath = "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default"
                    if (Test-Path $edgePath) {
                        $edgeDest = Join-Path $paths.Browser "Edge"
                        New-Item -Path $edgeDest -ItemType Directory -Force | Out-Null
                        
                        $edgeFiles = @('History', 'Cookies', 'Web Data')
                        foreach ($file in $edgeFiles) {
                            $srcFile = Join-Path $edgePath $file
                            if (Test-Path $srcFile) {
                                Copy-Item $srcFile -Destination $edgeDest -Force -ErrorAction SilentlyContinue
                            }
                        }
                        Write-Host " Edge artifacts collected" -ForegroundColor Green
                    }
                    
                    $results.ArtifactsCollected += "Browser Artifacts"
                }
                catch {
                    Write-Warning "Error collecting browser artifacts: $_"
                    $results.Summary.Errors++
                }
            }
            
            # 6. COLLECT NETWORK INFORMATION
            if ($IncludeNetworkInfo) {
                Write-Host "`n[6/8] Collecting Network Information..." -ForegroundColor Cyan
                
                try {
                    # Network adapters
                    Get-NetAdapter | ConvertTo-Json | Out-File (Join-Path $paths.Network "Adapters.json") -Encoding UTF8
                    
                    # IP configuration
                    ipconfig /all | Out-File (Join-Path $paths.Network "ipconfig.txt")
                    
                    # Routing table
                    route print | Out-File (Join-Path $paths.Network "routes.txt")
                    
                    # ARP cache
                    arp -a | Out-File (Join-Path $paths.Network "arp.txt")
                    
                    # DNS cache
                    ipconfig /displaydns | Out-File (Join-Path $paths.Network "dns_cache.txt")
                    
                    # Active connections
                    Get-NetTCPConnection | ConvertTo-Json | Out-File (Join-Path $paths.Network "tcp_connections.json") -Encoding UTF8
                    
                    # Listening ports
                    netstat -ano | Out-File (Join-Path $paths.Network "netstat.txt")
                    
                    # Network shares
                    net share | Out-File (Join-Path $paths.Network "shares.txt")
                    
                    Write-Host " Network information collected" -ForegroundColor Green
                    $results.ArtifactsCollected += "Network Information"
                }
                catch {
                    Write-Warning "Error collecting network info: $_"
                    $results.Summary.Errors++
                }
            }
            
            # 7. COLLECT EXECUTION ARTIFACTS
            if ($IncludeExecutionArtifacts) {
                Write-Host "`n[7/8] Collecting Execution Artifacts..." -ForegroundColor Cyan
                
                try {
                    # Running processes
                    Get-Process | Select-Object Name, Id, Path, StartTime, @{N='WorkingSetMB';E={[Math]::Round($_.WorkingSet64/1MB,2)}} |
                        ConvertTo-Json | Out-File (Join-Path $paths.Execution "Processes.json") -Encoding UTF8
                    
                    # Services
                    Get-Service | Select-Object Name, DisplayName, Status, StartType |
                        ConvertTo-Json | Out-File (Join-Path $paths.Execution "Services.json") -Encoding UTF8
                    
                    # Scheduled tasks
                    Get-ScheduledTask | Select-Object TaskName, TaskPath, State, @{N='LastRunTime';E={($_ | Get-ScheduledTaskInfo).LastRunTime}} |
                        ConvertTo-Json | Out-File (Join-Path $paths.Execution "ScheduledTasks.json") -Encoding UTF8
                    
                    # Startup programs
                    Get-CimInstance Win32_StartupCommand |
                        Select-Object Name, Command, Location, User |
                        ConvertTo-Json | Out-File (Join-Path $paths.Execution "StartupPrograms.json") -Encoding UTF8
                    
                    Write-Host " Execution artifacts collected" -ForegroundColor Green
                    $results.ArtifactsCollected += "Execution Artifacts"
                }
                catch {
                    Write-Warning "Error collecting execution artifacts: $_"
                    $results.Summary.Errors++
                }
            }
            
            # 8. COLLECT USER INFORMATION
            Write-Host "`n[8/8] Collecting User Information..." -ForegroundColor Cyan
            
            try {
                # Local users
                Get-LocalUser | Select-Object Name, Enabled, LastLogon, PasswordLastSet |
                    ConvertTo-Json | Out-File (Join-Path $paths.Users "LocalUsers.json") -Encoding UTF8
                
                # Local groups
                Get-LocalGroup | ConvertTo-Json | Out-File (Join-Path $paths.Users "LocalGroups.json") -Encoding UTF8
                
                # Logged on users
                quser 2>&1 | Out-File (Join-Path $paths.Users "LoggedOnUsers.txt")
                
                # User profiles
                Get-ChildItem "C:\Users" -Directory -ErrorAction SilentlyContinue |
                    Select-Object Name, CreationTime, LastWriteTime |
                    ConvertTo-Json | Out-File (Join-Path $paths.Users "UserProfiles.json") -Encoding UTF8
                
                Write-Host " User information collected" -ForegroundColor Green
                $results.ArtifactsCollected += "User Information"
            }
            catch {
                Write-Warning "Error collecting user info: $_"
                $results.Summary.Errors++
            }
            
            # CALCULATE COLLECTION SIZE
            Write-Host "`n[*] Calculating collection statistics..." -ForegroundColor Cyan

            $collectedFiles = @(Get-ChildItem -Path $collectionRoot -Recurse -File -ErrorAction SilentlyContinue)
            $results.Summary.TotalFiles = $collectedFiles.Count
            $results.Summary.TotalSize = ($collectedFiles | Measure-Object -Property Length -Sum).Sum

            # EVIDENCE INTEGRITY: SHA256 hash for every collected file
            Write-Host "`n[*] Hashing collected files (SHA256)..." -ForegroundColor Cyan

            $fileManifest = @()
            foreach ($collectedFile in $collectedFiles) {
                $sha256Hash = $null
                try {
                    $sha256Hash = (Get-FileHash -Path $collectedFile.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
                }
                catch {
                    Write-Verbose "Could not hash file: $($collectedFile.FullName) - $_"
                }

                $fileManifest += [PSCustomObject]@{
                    Path = $collectedFile.FullName.Substring($collectionRoot.Length).TrimStart('\', '/')
                    SizeBytes = $collectedFile.Length
                    SHA256 = $sha256Hash
                }
            }

            Write-Host " Hashed $(@($fileManifest | Where-Object { $null -ne $_.SHA256 }).Count) of $($collectedFiles.Count) files" -ForegroundColor Green
            
            $totalSizeMB = [Math]::Round($results.Summary.TotalSize / 1MB, 2)
            Write-Host " Total Files: $($results.Summary.TotalFiles)" -ForegroundColor Green
            Write-Host " Total Size: $totalSizeMB MB" -ForegroundColor Green
            
            # CREATE COLLECTION MANIFEST
            $manifest = @{
                CollectionDate = $results.CollectionDate
                ComputerName = $results.ComputerName
                Profile = $results.Profile
                Collector = $env:USERNAME
                ArtifactsCollected = $results.ArtifactsCollected
                TotalFiles = $results.Summary.TotalFiles
                TotalSizeMB = $totalSizeMB
                Errors = $results.Summary.Errors
                AcquisitionLog = @{
                    Tool = 'WindowsSecurityAudit'
                    ToolVersion = $moduleVersion
                    Operator = $env:USERNAME
                    Hostname = $env:COMPUTERNAME
                    StartTimeUtc = $acquisitionStartUtc.ToString('o')
                    EndTimeUtc = ([DateTime]::UtcNow).ToString('o')
                }
                Files = $fileManifest
            }
            
            $manifest | ConvertTo-Json -Depth 5 | Out-File (Join-Path $collectionRoot "MANIFEST.json") -Encoding UTF8
            Write-Host "`nCollection manifest created" -ForegroundColor Green
            
            # COMPRESS OUTPUT
            if ($CompressOutput) {
                Write-Host "`n[*] Compressing collection..." -ForegroundColor Cyan
                
                try {
                    $zipPath = "$collectionRoot.zip"
                    
                    Compress-Archive -Path $collectionRoot -DestinationPath $zipPath -CompressionLevel Optimal
                    
                    if (Test-Path $zipPath) {
                        $zipFile = Get-Item $zipPath
                        $zipSizeMB = [Math]::Round($zipFile.Length / 1MB, 2)
                        # TotalSize can be 0/$null when every collector produced no files
                        $compressionRatio = if ($results.Summary.TotalSize -gt 0) {
                            [Math]::Round((1 - ($zipFile.Length / $results.Summary.TotalSize)) * 100, 1)
                        } else { 0 }
                        
                        Write-Host " Archive created: $zipPath" -ForegroundColor Green
                        Write-Host " Compressed Size: $zipSizeMB MB" -ForegroundColor Cyan
                        Write-Host " Compression Ratio: $compressionRatio%" -ForegroundColor Cyan
                        
                        $results.CompressedArchive = $zipPath
                        
                        # Remove source if requested
                        if ($RemoveSource) {
                            Remove-Item $collectionRoot -Recurse -Force
                            Write-Host " Original collection removed" -ForegroundColor Gray
                        }
                    }
                }
                catch {
                    Write-Warning "Compression failed: $_"
                }
            }
            
        }
        catch {
            Write-Error "Error during artifact collection: $_"
            throw
        }
    }
    
    end {
        Write-Host "`n========================================" -ForegroundColor Green
        Write-Host " FORENSIC COLLECTION COMPLETE" -ForegroundColor Green
        Write-Host "========================================" -ForegroundColor Green
        
        Write-Host "`nCollection Summary:" -ForegroundColor Cyan
        Write-Host " Computer: $($results.ComputerName)" -ForegroundColor White
        Write-Host " Profile: $($results.Profile)" -ForegroundColor White
        Write-Host " Output Path: $($results.OutputPath)" -ForegroundColor White
        Write-Host " Total Files: $($results.Summary.TotalFiles)" -ForegroundColor White
        Write-Host " Total Size: $([Math]::Round($results.Summary.TotalSize/1MB,2)) MB" -ForegroundColor White
        Write-Host " Errors: $($results.Summary.Errors)" -ForegroundColor $(if($results.Summary.Errors -gt 0){'Yellow'}else{'White'})
        
        if ($results.CompressedArchive) {
            Write-Host " Archive: $($results.CompressedArchive)" -ForegroundColor Green
        }
        
        Write-Host "`nArtifacts Collected:" -ForegroundColor Cyan
        foreach ($artifact in $results.ArtifactsCollected) {
            Write-Host " - $artifact" -ForegroundColor Green
        }
        
        Write-Host "`nNext Steps:" -ForegroundColor Yellow
        Write-Host " 1. Review MANIFEST.json for collection details" -ForegroundColor Gray
        Write-Host " 2. Analyze collected artifacts with forensic tools" -ForegroundColor Gray
        Write-Host " 3. Store evidence securely with chain of custody" -ForegroundColor Gray
        Write-Host " 4. Verify evidence integrity with the SHA256 hashes in MANIFEST.json" -ForegroundColor Gray
        
        return $results
    }
}