Last active
August 6, 2022 23:51
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
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