Windows 10 Windows 10 Powershell based virus detector

Neemobeer

Windows Forum Team
Staff member
#1
Here's a neat script I put together today. If added as a scheduled task it will loop and monitor running processes. The only thing needed is to sign up for an account on virustotal.com to get an API key then replace the F's with your api key
Function Get-ProcessReport([string]$FilePath, [string]$Hash)
{
Get-VirusTotalReport -Hash $hash -Apikey FFFFFFFFFFFFFFFFFFF
}

The script scans through all running processes, detects and ignores any trusted signed binaries. It then generates a hash of any unsigned process and pulls a report from virustotal.com and if there are more than 4 indicators it saves the hash in a blacklist file. Any process that passes virustotal has it's hash saved in the whitelist file.

When a process is blacklisted or fails the virus total check the script will kill the process.

Enjoy.

Code:
# Process_Bouncer.ps1
$main_conf_dir="$env:ProgramData\Malwark\Process_Bouncer"
$log_file="$main_conf_dir\action.log"
$white_file="$main_conf_dir\whitelist"
$black_file="$main_conf_dir\blacklist"

Function New-Configuration
{
    If (-Not (Test-Path $main_conf_dir))
    {
        New-Item -Path $main_conf_dir -ItemType Directory
    }
    If (-Not (Test-Path $log_file))
    {
        New-Item -Path $log_file -ItemType File
    }
    If (-Not (Test-Path $white_file))
    {
        New-Item -Path $white_file -ItemType File
    }
    If (-Not (Test-Path $black_file))
    {
        New-Item -Path $black_file -ItemType File
    }
}

Function Create-NotificationBallon([string]$Message, [string]$Title)
{
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

    $objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon

    $objNotifyIcon.Icon = "C:\Windows\ProgressError.ico"
    $objNotifyIcon.BalloonTipIcon = "Error"
    $objNotifyIcon.BalloonTipText = "$Message"
    $objNotifyIcon.BalloonTipTitle = "$Title"
    $objNotifyIcon.Visible = $True
    $objNotifyIcon.ShowBalloonTip(5000)
}


Function Search-BWList([string]$Hash, [string]$SearchList)
{
    $list = Get-Content $SearchList

    ForEach ($entry In $list)
    {
        If ( $Hash -eq $entry) { return $true }
    }
    return $false
}

Function Get-UnsignedProcesses()
{
    # Get current list of unique running processes based on execution path
    $procs = gwmi -Query 'select ExecutablePath,ProcessID from win32_process'
   
    $unsigned = New-Object System.Collections.ArrayList

    ForEach($proc In $procs)
    {
        $proc_exe_path = $proc.ExecutablePath
        If ($proc_exe_path -eq $null) { continue }
        If ($proc_exe_path -eq "") { continue }
        # Create a list of unsigned binaries for scanning
        $auth_code = Get-AuthenticodeSignature $proc_exe_path
        If ($auth_code.Status -ne "Valid")
        {
            $hash = (Get-FileHash -Path $proc_exe_path -Algorithm SHA1).Hash
            If ( -Not (Search-BWList -SearchList $white_file -Hash $hash))
            {
                New-LogAction -Message "A new unsigned process was detected: $proc_exe_path"
                $proc_obj = New-Object -TypeName psobject
                $proc_obj | Add-Member -MemberType NoteProperty -Name ExecutablePath -Value $proc.ExecutablePath
                $proc_obj | Add-Member -MemberType NoteProperty -Name ProcessID -Value $proc.ProcessID
                $proc_obj | Add-Member -MemberType NoteProperty -Name Hash -Value $hash
                $unsigned.Add($proc_obj) > null
            }           
        }
    }
    return $unsigned
}

Function Get-ProcessReport([string]$FilePath, [string]$Hash)
{
    Get-VirusTotalReport -Hash $hash -Apikey FFFFFFFFFFFFFFFFFFF
}

Function New-LogAction([string]$Message)
{
    # Add in code to check log size and rotate log files (keep 5 log files)
    $main_log_size = $(Get-ChildItem -Path $log_file | Measure-Object -Property Length -Sum).Sum

    If (($main_log_size / 1MB) -gt 1)
    {
        $log_count = @(Get-ChildItem -Path $main_conf_dir -Filter "*action*").Count
        $log_count--

        If ($log_count -ge 4)
        {
            Remove-Item -Path "$log_file.$($log_count)"
            $log_count--
        }
        While($log_count -ge 0)
        {
            If ($log_count -eq 0)
            {
                Rename-Item -Path "$log_file" -NewName "$log_file.$($log_count+1)"
            }
            else
            {
                Rename-Item -Path "$log_file.$log_count" -NewName "$log_file.$($log_count+1)"
            }
            $log_count--
        }
        New-Item -Path $log_file -ItemType File
    }

    # Add an action event to the log
    Add-Content -Path $log_file -Value "$((Get-Date).ToString())    -----    $Message"
}

Function Get-VirusTotalReport
{
    param([string]$Hash, [string]$ApiKey)
   
    $body = @{ resource = $hash; apikey = $ApiKey }
    $Report = Invoke-RestMethod -Method 'POST' -Uri 'https://www.virustotal.com/vtapi/v2/file/report' -Body $body

    return $Report
}

Function Main
{
    New-Configuration

    $suspect_procs = Get-UnsignedProcesses
    ForEach ($proc In $suspect_procs)
    {
        $proc_exe_path = $proc.ExecutablePath
        If ($proc_exe_path -eq $null) { continue }
        If ($proc_exe_path -eq "") { continue }
        $hash = $proc.Hash

        If ($(Search-BWList -SearchList $black_file -Hash $hash) -eq $true)
        {
            "Black Listed $proc_exe_path"
            New-LogAction -Message "[BLACKLIST DETECTION]  $proc_exe_path HASH: $hash was found in the blacklist."

            #"Kill the process"
            "Stopping $proc_exe_path"
            Stop-Process -Id $proc.ProcessId 2> $kill_error
            If ($?)
            {
                Create-NotificationBallon -Message "Blacklisted process detected and killed. Process: $proc_exe_path" -Title "Process Killed"
                New-LogAction -Message "[PROCESS KILL](Success)  $proc_exe_path HASH: $hash"
            } else
            {
                New-LogAction -Message "[PROCESS KILL](Failed)  $proc_exe_path HASH: $hash  ERROR: $kill_error"
            }
           
            continue
        } ElseIf ( $(Search-BWList -SearchList $white_file -Hash $hash) -eq $true )
        {
            "White Listed $proc_exe_path"
            New-LogAction -Message "[WHITELIST DETECTION]  $proc_exe_path HASH: $hash was found in the whitelist."
            continue
        }

        $report = Get-ProcessReport -FilePath $proc_exe_path -Hash $hash

        If ($report.positives -eq $null)
        {
            New-LogAction -Message "[PROBLEM]  Process: $proc_exe_path HASH: $hash.  Problem getting Virus Total Report"
            continue
        }
        If ($report.positives -gt 2)
        {
            Add-Content -Path $black_file -Value $hash
            New-LogAction -Message "[BLACKLIST ADD]  Process: $proc_exe_path HASH: $hash was blacklisted.  Hit Count: $($report.positives)/$($report.total)"

            #"Kill the process"
            "Stopping $proc_exe_path"
            Stop-Process -Id $proc.ProcessId 2> $kill_error
            If ($?)
            {
                Create-NotificationBallon -Message "Blacklisted process detected and killed. Process: $proc_exe_path" -Title "Process Killed"
                New-LogAction -Message "[PROCESS KILL](Success)  $proc_exe_path HASH: $hash"
            } else
            {
                New-LogAction -Message "[PROCESS KILL](Failed)  $proc_exe_path HASH: $hash  ERROR: $kill_error"
            }
        }
        else
        {
            Add-Content -Path $white_file -Value $hash
            New-LogAction -Message "[WHITELIST ADD]  Process: $proc_exe_path HASH: $hash was whitelisted.  Hit Count: $($report.positives)/$($report.total)"
        }
    }
}


While($true)
{
    Main
    Sleep -Seconds 1
}
 


Josephur

Windows Forum Admin
Staff member
Premium Supporter
#2
Here's something you may like Neemobeer, I've used it in the past :)

GitHub - darkoperator/Posh-VirusTotal: PowerShell Module to interact with VirusTotal

I've also written a simple "msconfig" like application that checks startup items against Virus Total, however the new Autoruns and Process Explorer includes this information now, so it's kind of a moot point :)
 


AceInfinity

Senior Member
Microsoft MVP
#3
This is more of a file integrity check. Most viruses now don't run in their own executable, they are mapped into the memory of running processes, and this won't detect that. This is okay as a file scanner however.
 


Neemobeer

Windows Forum Team
Staff member
#4
It checks the locally stored exe against virus total. It doesn't dump the process memory and check that to look for code injection.
 


AceInfinity

Senior Member
Microsoft MVP
#5
It checks the locally stored exe against virus total. It doesn't dump the process memory and check that to look for code injection.
I know, that's what I'm saying, so it's not a *full* virus detector, but more of a file scanner.
 


This website is not affiliated, owned, or endorsed by Microsoft Corporation. It is a member of the Microsoft Partner Program.