diff --git a/DeployCryptoBlocker.ps1 b/DeployCryptoBlocker.ps1 index d7e17ef..611e7a8 100644 --- a/DeployCryptoBlocker.ps1 +++ b/DeployCryptoBlocker.ps1 @@ -2,59 +2,87 @@ # ################################ Functions ################################ -function ConvertFrom-Json20([Object] $obj) +Function ConvertFrom-Json20 { + # Deserializes JSON input into PowerShell object output + Param ( + [Object] $obj + ) Add-Type -AssemblyName System.Web.Extensions $serializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer return ,$serializer.DeserializeObject($obj) } -Function New-CBArraySplit { - +Function New-CBArraySplit +{ + <# + Takes an array of file extensions and checks if they would make a string >4Kb, + if so, turns it into several arrays + #> param( - $extArr, - $depth = 1 + $Extensions ) - $extArr = $extArr | Sort-Object -Unique - - # Concatenate the input array - $conStr = $extArr -join ',' - $outArr = @() - - # If the input string breaks the 4Kb limit - If ($conStr.Length -gt 4096) { - # Pull the first 4096 characters and split on comma - $conArr = $conStr.SubString(0,4096).Split(',') - # Find index of the last guaranteed complete item of the split array in the input array - $endIndex = [array]::IndexOf($extArr,$conArr[-2]) - # Build shorter array up to that indexNumber and add to output array - $shortArr = $extArr[0..$endIndex] - $outArr += [psobject] @{ - index = $depth - array = $shortArr - } + $Extensions = $Extensions | Sort-Object -Unique + + $workingArray = @() + $WorkingArrayIndex = 1 + $LengthOfStringsInWorkingArray = 0 + + # TODO - is the FSRM limit for bytes or characters? + # maybe [System.Text.Encoding]::UTF8.GetBytes($_).Count instead? + # -> in case extensions have Unicode characters in them + # and the character Length is <4Kb but the byte count is >4Kb + + # Take the items from the input array and build up a + # temporary workingarray, tracking the length of the items in it and future commas + $Extensions | ForEach-Object { + + if (($LengthOfStringsInWorkingArray + 1 + $_.Length) -gt 4096) + { + # Adding this item to the working array (with +1 for a comma) + # pushes the contents past the 4Kb limit + # so output the workingArray + [PSCustomObject]@{ + index = $WorkingArrayIndex + FileGroupName = "$Script:FileGroupName$WorkingArrayIndex" + array = $workingArray + } + + # and reset the workingArray and counters + $workingArray = @($_) # new workingArray with current Extension in it + $LengthOfStringsInWorkingArray = $_.Length + $WorkingArrayIndex++ - # Then call this function again to split further - $newArr = $extArr[($endindex + 1)..($extArr.Count -1)] - $outArr += New-CBArraySplit $newArr -depth ($depth + 1) - - return $outArr + } + else #adding this item to the workingArray is fine + { + $workingArray += $_ + $LengthOfStringsInWorkingArray += (1 + $_.Length) #1 for imaginary joining comma + } } - # If the concat string is less than 4096 characters already, just return the input array - Else { - return [psobject] @{ - index = $depth - array = $extArr - } + + # The last / only workingArray won't have anything to push it past 4Kb + # and trigger outputting it, so output that one as well + [PSCustomObject]@{ + index = ($WorkingArrayIndex) + FileGroupName = "$Script:FileGroupName$WorkingArrayIndex" + array = $workingArray } } ################################ Functions ################################ -# Add to all drives -$drivesContainingShares = Get-WmiObject Win32_Share | Select Name,Path,Type | Where-Object { $_.Type -eq 0 } | Select -ExpandProperty Path | % { "$((Get-Item -ErrorAction SilentlyContinue $_).Root)" } | Select -Unique -if ($drivesContainingShares -eq $null -or $drivesContainingShares.Length -eq 0) +# Get all drives with shared folders, these drives will get FRSRM protection +$DrivesContainingShares = @(Get-WmiObject Win32_Share | # all shares on this computer, filter: + Where-Object { $_.Type -eq 0 } | # 0 = disk drives (not printers, IPC$, C$ Admin shares) + Selec-Object -ExpandProperty Path | # Shared folder path, e.g. "D:\UserFolders\" + ForEach-Object { + ([System.IO.DirectoryInfo]$_).Root.Name # Extract the driveletter, as a string + } | Sort-Object -Unique) # remove duplicates + + +if ($drivesContainingShares.Count -eq 0) { Write-Host "No drives containing shares were found. Exiting.." exit @@ -62,6 +90,8 @@ if ($drivesContainingShares -eq $null -or $drivesContainingShares.Length -eq 0) Write-Host "The following shares needing to be protected: $($drivesContainingShares -Join ",")" + +#### Identify Windows Server version, and install FSRM role $majorVer = [System.Environment]::OSVersion.Version.Major $minorVer = [System.Environment]::OSVersion.Version.Minor @@ -95,32 +125,29 @@ if ($majorVer -ge 6) else { # Assume Server 2003 - Write-Host "Other version of Windows detected! Quitting.." + Write-Host "Unsupported version of Windows detected! Quitting.." return } + $fileGroupName = "CryptoBlockerGroup" $fileTemplateName = "CryptoBlockerTemplate" $fileScreenName = "CryptoBlockerScreen" +# Download list of CryptoLocker file extensions $webClient = New-Object System.Net.WebClient $jsonStr = $webClient.DownloadString("https://fsrm.experiant.ca/api/v1/get") -$monitoredExtensions = @(ConvertFrom-Json20($jsonStr) | % { $_.filters }) - +$monitoredExtensions = @(ConvertFrom-Json20 $jsonStr | ForEach-Object { $_.filters }) -If (TestPath .\SkipList.txt) { - $exclusions = Get-Content .\SkipList.txt - ForEach ($exclusion in $exclusions) { - if ($exclusion) { - $monitoredExtensions = $monitoredExtensions.replace($exclusion.trim(),$null) - } - } +If (TestPath .\SkipList.txt) +{ + $Exclusions = Get-Content .\SkipList.txt | ForEach-Object { $_.Trim() } + $monitoredExtensions = $monitoredExtensions | Where-Object { $Exclusions -notcontains $_ } } Else { - $emptyFile = ` -` -"# + $emptyFile = @' +# # Add one filescreen per line that you want to ignore # # For example, if *.doc files are being blocked by the list but you want @@ -132,18 +159,13 @@ Else # The script will check this file every time it runs and remove these # entries before applying the list to your FSRM implementation. # - -" - Add-Content .\SkipList.txt $emptyFile - +'@ + Set-Content -Path .\SkipList.txt -Value $emptyFile } # Split the $monitoredExtensions array into fileGroups of less than 4kb to allow processing by filescrn.exe -$fileGroups = New-CBArraySplit $monitoredExtensions -ForEach ($group in $fileGroups) { - $group | Add-Member -MemberType NoteProperty -Name fileGroupName -Value "$FileGroupName$($group.index)" -} +$fileGroups = @(New-CBArraySplit $monitoredExtensions) # Perform these steps for each of the 4KB limit split fileGroups ForEach ($group in $fileGroups) { @@ -155,7 +177,7 @@ ForEach ($group in $fileGroups) { Write-Host "Adding/replacing File Screen Template [$fileTemplateName] with Event Notification [$eventConfFilename] and Command Notification [$cmdConfFilename].." &filescrn.exe Template Delete /Template:$fileTemplateName /Quiet # Build the argument list with all required fileGroups -$screenArgs = 'Template','Add',"/Template:$fileTemplateName" +$screenArgs = 'Template', 'Add', "/Template:$fileTemplateName" ForEach ($group in $fileGroups) { $screenArgs += "/Add-Filegroup:$($group.fileGroupName)" } @@ -163,7 +185,7 @@ ForEach ($group in $fileGroups) { &filescrn.exe $screenArgs Write-Host "Adding/replacing File Screens.." -$drivesContainingShares | % { +$drivesContainingShares | ForEach-Object { Write-Host "`tAdding/replacing File Screen for [$_] with Source Template [$fileTemplateName].." &filescrn.exe Screen Delete "/Path:$_" /Quiet &filescrn.exe Screen Add "/Path:$_" "/SourceTemplate:$fileTemplateName"