Skip to content

Instantly share code, notes, and snippets.

@SveinErik
Last active August 6, 2022 23:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SveinErik/f34033ceaeecb419e4c2c34296077b9d to your computer and use it in GitHub Desktop.
Save SveinErik/f34033ceaeecb419e4c2c34296077b9d to your computer and use it in GitHub Desktop.
Powershell script to scan for encrypted files, and restore them from "previous version" in Windows. Uses QuickIO.NET to avoid PathTooLong
<#
A bit info first:
This script uses QuickIO.NET (https://quickio.net/)
The function Get-FilesQuickIO is made by "Frekac" (http://psfredrik.chiloma.com/2016/12/15/using-quickio-net-with-powershell/)
And the script itself is downloaded from here: https://gist.github.com/frekac/337b8d1b2abddffb2803f36d34d56b27#file-get-filesquickio-ps1
I downloaded "nuget Windows x86 Commandline" from here: https://dist.nuget.org/index.html
Then i used cmd to browse to my folder where nuget.exe is located, and i typed: nuget.exe install QuickIO.net
It's then downloaded in a subdir in the same dir as nuget.exe is located.
Then, i adjusted the location of the .dll in the function Get-FilesQuickIO, this line:
Add-Type -Path C:\Scripts\_Div\QuickIO.NET.2.6.2.0\lib\net40\SchwabenCode.QuickIO.dll
About this script:
Fill info on the top-folder where you want to search. It will search this folder and all sub-folders for files with 6 chars file-endings
You also need to find the backup-folder to use for restoring the infected files. To do this:
Browse to the infected folder, right-click and choose a previous version that is "clean" of infection. Click open, then mark a file or folder, and
you will see the path in the properties-window. Copy that and paste it in the $backupGMTpath in this script.
Make sure you dont have "\" in the ending of the paths.
The $restoreFilesInfo variable holds the text of the files that Cryptolocker leaves behind. Enter your infection text, and it will delete all of those as well.
#>
<#
.Synopsis
Search file structure using QuickIO.Net library
.DESCRIPTION
Search the file structure without the 260+ character path length limit.
.EXAMPLE
Get-FilesQuickIO -FilePath <path> -Filter "*.*" -Recursive
#>
function Get-FilesQuickIO
{
[CmdletBinding()]
Param
(
# Path to start from
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
[string]$FilePath,
# To search recursively
[Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)]
[switch]$Recursive,
# To filter
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
[string]$Filter
)
# Load QuickIO Assmebly
Add-Type -Path C:\Scripts\_Div\QuickIO.NET.2.6.2.0\lib\net40\SchwabenCode.QuickIO.dll
# Initiate the file list
$fileList = @()
# Set search option based on recursive or not
if(($Recursive -eq $true) -and (($Filter -eq "*") -or ($Filter -eq "*.*")))
{
# Set the search option value
$searchOption = "AllDirectories"
# Get the list of files
$fileList = [SchwabenCode.QuickIO.QuickIODirectory]::EnumerateFiles([system.string]$FilePath,[system.string]$Filter,[System.IO.SearchOption]$searchOption)
}
elseif ($Recursive -eq $true)
{
# Set the search option value
$searchOption = "AllDirectories"
# Set the directory pattern
$directoryPattern = "*"
# Get all the directories as the recursive option doesn't work when using a filter.
$directoryList = [SchwabenCode.QuickIO.QuickIODirectory]::EnumerateDirectories([system.string]$FilePath,[system.string]$directoryPattern,[System.IO.SearchOption]$searchOption) | select -ExpandProperty FullName
# Loop through the directories with the file pattern
foreach($d in $directoryList)
{
$fileList += try{[SchwabenCode.QuickIO.QuickIODirectory]::EnumerateFiles([system.string]$d,[system.string]$Filter,[System.IO.SearchOption]$searchOption)}catch{$null}
}
}
else{
# Set the search option value
$searchOption = "TopDirectoryOnly"
# Get the list of files
$fileList = try{[SchwabenCode.QuickIO.QuickIODirectory]::EnumerateFiles([system.string]$FilePath,[system.string]$Filter,[System.IO.SearchOption]$searchOption)}catch{$null}
}
return $fileList
}
#--- THE SCRIPT LOGIC ---#
cls
#Start the stopwatch
$stopWatch = [system.diagnostics.stopwatch]::StartNew()
#Works with both \\server\share.. and <drive>:\folder
$scanThisFolderAndSub = "\\11fil05\felles\IKT\VirusTestMappe"
$backupGMTpath = "F:\@GMT-2017.03.29-05.00.11\IKT\VirusTestMappe"
$restoreFilesInfo = "*GJENOPPRETTING_AV_FILER*"
Write-Host ("Working path: " + $scanThisFolderAndSub) -ForegroundColor Yellow
Write-Host ("Scanning, this may take a while...coffee?") -ForegroundColor Yellow
$fileList = Get-FilesQuickIO -FilePath $scanThisFolderAndSub -Recursive -Filter "*.*"
Write-Host ("Finished with scanning, time used:") -ForegroundColor Yellow
$stopWatch.Elapsed
Write-Host ("Starting with the restore process...") -ForegroundColor Yellow
$result = @()
foreach($f in $fileList)
{
if($f.Name.Contains("."))
{
#Run regex to match a-z A-Z and 0-9. After this, add "known" file-extensions that you want to exclude from the list
if( ($f.Name -match '\.[a-zA-Z0-9]{6}$') -and `
($f.Name -notmatch 'config') -and `
($f.Name -notmatch 'arabic') -and `
($f.Name -notmatch 'sqlite') -and `
($f.Name -notmatch 'binary') -and `
($f.Name -notmatch 'sample') -and `
($f.Name -notmatch 'osiris') -and `
($f.Name -notmatch 'wixpdb') -and `
($f.Name -notmatch 'onepkg') -and `
($f.Name -notmatch 'csproj') )
{
$result += $f
#$f.FullName
}
}
}
if($result.Count -gt 0)
{
Write-Host ("Found: " + $result.Count + " files. Starting recovery") -ForegroundColor Yellow
}
else
{
Write-Host ("Found: " + $result.Count + " files :)") -ForegroundColor Yellow
}
#Create counters
$counter = 0
$restoreFilesCounter = 0
foreach($file in $result)
{
#Build the path to the backup-file
$backupPath = ( $file.ParentFullName.Replace($scanThisFolderAndSub, $backupGMTpath) )
#Get the list of files in the backup-path (should only be the one we're looking for, because the filter is set to this filename)
$filter = $file.Name.Substring(0,$file.Name.Length-7)
$fileList = Get-FilesQuickIO -FilePath $backupPath -Filter $filter
#Get the backup-file (the only file it will match on)
ForEach($f in $fileList)
{
$backupFile = $f
}
#Create the path to the file that is about to be restored, to check if it already exists - meaning that it's not infected
$fileToBeRestored = ($backupFile.FullName.Replace($backupGMTpath, $scanThisFolderAndSub))
#Print info (for debug/checks):
#$file
#write-host "-------"
#$backupFile
#write-host "-------"
#$fileToBeRestored
#Copy the file from the backupGMTpath back to it's original place if it does not already exist. And delete the infected file.
if($backupFile -and !( [SchwabenCode.QuickIO.QuickIO]::FileExists($fileToBeRestored)) )
{
Write-Host ("Copying back the file from backup: " + $backupFile.FullName) -ForegroundColor Green
#Copy the backup-file back to it's original folder
[SchwabenCode.QuickIO.QuickIOFile]::Copy($backupFile.FullNameUnc, ($file.ParentFullName + "\" + $backupFile.Name), $false)
#Remove the virus-infected-file
Write-Host ("Deleting the infected file: " + $file.FullName) -ForegroundColor DarkGreen
[SchwabenCode.QuickIO.QuickIOFile]::Delete($file.FullName)
$counter++
#Also, delete the "restore info files" from the infected directory
$virusLeftBehinds = Get-FilesQuickIO -FilePath $file.ParentFullName -Filter $restoreFilesInfo
foreach($f in $virusLeftBehinds)
{
[SchwabenCode.QuickIO.QuickIOFile]::Delete($f.FullName)
$restoreFilesCounter++
}
}
else
{
Write-Host ("Cannot find the backup-file for: " + $file.FullName + " or it already exists with the original name") -ForegroundColor Yellow
}
}
$stopWatch.Stop()
Write-Host ("---------------") -ForegroundColor White
Write-Host ("Script finished. Replaced: $counter files, and deleted $restoreFilesCounter files containing $restoreFilesInfo pattern") -ForegroundColor White
Write-Host ("Time used:") -ForegroundColor White
$stopWatch.Elapsed
Write-Host ("---------------") -ForegroundColor White
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment