CleanGuard

 avatar
unknown
powershell
2 months ago
6.3 kB
3
No Index
# CleanGuard.ps1
# Author: Gorstak

$ErrorActionPreference = "SilentlyContinue"

$Quarantine = "C:\ProgramData\CleanGuard\Quarantine"
$Backup     = "C:\ProgramData\CleanGuard\Backup"
$LogFile    = "C:\ProgramData\CleanGuard\log.txt"
$LastFile   = "C:\ProgramData\CleanGuard\Quarantine\.last"

@($Quarantine, $Backup, (Split-Path $LogFile -Parent)) | ForEach-Object {
    if (!(Test-Path $_)) {
        New-Item -ItemType Directory -Path $_ -Force | Out-Null
    }
}

function Log($msg) {
    "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') | $msg" | Out-File -FilePath $LogFile -Append -Encoding UTF8
}

Log "CleanGuard started - monitoring .exe, .dll, .sys and .winmd"

function Get-SHA256($path) {
    (Get-FileHash -Path $path -Algorithm SHA256).Hash.ToLower()
}

function Test-KnownGood($hash) {
    try {
        $json = Invoke-RestMethod -Uri "https://hashlookup.circl.lu/lookup/sha256/$hash" -TimeoutSec 8
        return ($json.'hashlookup:trust' -gt 50)
    } catch {
        return $false
    }
}

function Test-MalwareBazaar($hash) {
    $body = @{ query = "get_info"; hash = $hash } | ConvertTo-Json -Compress
    try {
        $resp = Invoke-RestMethod -Method Post -Uri "https://mb-api.abuse.ch/api/v1/" -Body $body -ContentType "application/json" -TimeoutSec 12
        return ($resp.query_status -eq "hash_found")
    } catch {
        return $false
    }
}

function Test-SignedByMicrosoft($path) {
    try {
        $sig = Get-AuthenticodeSignature -FilePath $path
        if ($sig.Status -eq "Valid") {
            if ($sig.SignerCertificate.Subject -match "O=Microsoft Corporation") { return $true }
            if ($sig.SignerCertificate.Thumbprint -match "109F2DD82E0C9D1E6B2B9A46B2D4B5E4F5B9F5D6|3A2F5E8F4E5D6C8B9A1F2E3D4C5B6A7F8E9D0C1B") { return $true }
        }
    } catch {}
    return $false
}

# UI types (needed before MessageBox / NotifyIcon)
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Move-ToQuarantine($file) {
    $name = [IO.Path]::GetFileName($file)
    $ts   = Get-Date -Format "yyyyMMdd_HHmmss_fff"
    $bak  = Join-Path $Backup ($name + "_" + $ts + ".bak")
    $q    = Join-Path $Quarantine ($name + "_" + $ts)

    Copy-Item $file $bak -Force
    Move-Item $file $q -Force

    "$bak|$file" | Out-File $LastFile -Encoding UTF8

    Log "QUARANTINED -> $q"
    [System.Windows.Forms.MessageBox]::Show("$name`nQuarantined!", "CleanGuard", "OK", "Warning") | Out-Null
}

function Undo-LastQuarantine {
    if (!(Test-Path $LastFile)) { return }

    $line = Get-Content $LastFile -ErrorAction SilentlyContinue
    if (-not $line) { return }

    $bak, $orig = $line.Split('|', 2)
    if (Test-Path $orig) {
        Remove-Item $orig -Force
    }
    if (Test-Path $bak) {
        Move-Item $bak $orig -Force
        $name = [IO.Path]::GetFileName($orig)
        Log "UNDO -> restored $name"
        [System.Windows.Forms.MessageBox]::Show("Last file restored:`n$name", "CleanGuard", "OK", "Information") | Out-Null
    }

    Remove-Item $LastFile -Force
}

# Real-time monitoring
$drives   = Get-CimInstance -ClassName Win32_LogicalDisk | Where-Object { $_.DriveType -in (2, 3, 4) }
$watchers = @()

foreach ($drive in $drives) {
    $root = $drive.DeviceID + "\"
    Log "Setting up FileSystemWatcher for drive: $root"

    try {
        $watcher = New-Object System.IO.FileSystemWatcher
        $watcher.Path = $root
        $watcher.IncludeSubdirectories = $true
        $watcher.NotifyFilter = [System.IO.NotifyFilters]'FileName, LastWrite'
        $watchers += $watcher
    } catch {
        Log "Failed to create watcher for $root : $_"
    }
}

$action = {
    $path = $Event.SourceEventArgs.FullPath
    if ($path -notmatch '\.(exe|dll|sys|winmd)$') { return }

    Start-Sleep -Milliseconds 1200
    if (!(Test-Path $path)) { return }

    $name = [IO.Path]::GetFileName($path)
    $hash = Get-SHA256 $path

    if (Test-KnownGood $hash) {
        Log "Known-good (CIRCL): $name"
        return
    }

    if (Test-SignedByMicrosoft $path) {
        Log "Trusted Microsoft: $name"
        return
    }

    if (Test-MalwareBazaar $hash) {
        Log "MALWARE DETECTED (MalwareBazaar): $name"
        Move-ToQuarantine $path
        return
    }

    $lower = $path.ToLower()
    if ($lower -notmatch 'c:\\windows\\|c:\\program files\\|c:\\program files \(x86\)\\|c:\\windowsapps\\') {
        Log "SUSPICIOUS unsigned file: $name"
        $choice = [System.Windows.Forms.MessageBox]::Show(
            "Suspicious unsigned file detected:`n`n$path`n`nQuarantine it?",
            "CleanGuard",
            "YesNo",
            "Warning"
        )
        if ($choice -eq [System.Windows.Forms.DialogResult]::Yes) {
            Move-ToQuarantine $path
        }
    }
}

foreach ($watcher in $watchers) {
    Register-ObjectEvent -InputObject $watcher -EventName Created -Action $action | Out-Null
    Register-ObjectEvent -InputObject $watcher -EventName Changed -Action $action | Out-Null
    $watcher.EnableRaisingEvents = $true
}

# Tray icon
$notify = New-Object System.Windows.Forms.NotifyIcon
$notify.Icon = [System.Drawing.SystemIcons]::Shield
$notify.Text = "CleanGuard - Real-time protection ON"
$notify.Visible = $true

$menu = New-Object System.Windows.Forms.ContextMenuStrip
$menu.Items.Add("Open Quarantine", $null, { explorer $Quarantine }) | Out-Null
$menu.Items.Add("Undo Last", $null, { Undo-LastQuarantine }) | Out-Null
$menu.Items.Add("Exit", $null, {
    $script:notify.Visible = $false
    if ($script:watchers) {
        $script:watchers | ForEach-Object { $_.Dispose() }
    }
    Log "CleanGuard stopped by user"
    exit
}) | Out-Null
$notify.ContextMenuStrip = $menu

Log "Real-time protection ACTIVE"
[System.Windows.Forms.MessageBox]::Show(
    "CleanGuard is now running!`nMonitoring .exe, .dll, .sys and .winmd files.",
    "CleanGuard",
    "OK",
    "Information"
) | Out-Null

# Keep script alive
try {
    while ($true) {
        Start-Sleep -Seconds 3600
    }
}
finally {
    if ($notify) { $notify.Visible = $false }
    if ($watchers) {
        $watchers | ForEach-Object { $_.Dispose() }
    }
}
Editor is loading...
Leave a Comment