BUP-Extractor.psm1
<#
.SYNOPSIS Extracts and decodes files that have been quarantined by McAfee Anti-Virus from their .bup containers. .DESCRIPTION This PowerShell module extracts and decodes files that have been quarantined by McAfee Anti-Virus from their .bup containers. McAfee quarantines files first by encoding them using a bitwise XOR operation and the key "6A" (0x006a as a byte or 106 as a decimal). Details of the qurantined file are stored in a text file, which is encoded with the same key. Both files (the quarantined file + the details file) are then combined in "Compound File Binary Format" file. AKA, "COM Structured Storage" or "OLE file". This PowerShell module uses a 7-zip to extract the details and quarantined files, and a bespoke, compiled C DLL to quickly decode them in-place. .EXAMPLE # Extract a BUP file Sample Files> Expand-BUPFile .\7e49612f3a3030.bup Expanding the BUP archive... Decoding the BUP details file... The BUP archive 7e49612f3a3030 contains "C:\Windows\mssecsvc.exe", which was detected as "Generic.ayx" Decoding the BUP qurantined file... The quarantined file was decoded and extracted to .\Sample Files\7e49612f3a3030\File_0 Sample Files> A simple demonstration of extracting the quarantined file. .EXAMPLE # Extract just the details from a BUP archive Sample Files> Expand-BUPFile .\7e496101d35960.bup -InfoOnly Extracting the BUP details file... Decoding the BUP details file... The BUP archive 7e496101d35960 contains "C:\Windows\mssecsvc.exe", which was detected as "Generic.ayx" The details file was decoded and extracted to .\Sample Files\7e496101d35960\Details Sample Files> A simple demonstration of how to extract the "details" file from the container, without extracting the potentially malicious quarantined file. .INPUTS [System.IO.FileInfo] $InputBUPFile, [String] $OutputDirectory, [Switch] $InfoOnly .OUTPUTS Output (if any) .NOTES Due to the nature of quarantined files, you are quite likely to extract and decode malicious files, which your anti-virus software may have a problem with. Most AV programs will let you exclude a directory from AV scanning/alerting, which should give you somewhere safe to extract the quarantined files to. BUP-Extractor also includes an "-InfoOnly" switch which will only extract and decode the "details" file and may be able to meet your needs without triggering your AV. .LINK # Project repository https://github.com/graememeyer/BUP-Extractor # McAfee Knowledge Center: How to restore a quarantined file not listed in the Quarantine Manager https://kc.mcafee.com/corporate/index?page=content&id=KB72755&actp=null&viewlocale=en_US&showDraft=false&locale=en_US # Wikipedia: Compound File Binary Format https://en.wikipedia.org/wiki/Compound_File_Binary_Format # SANS ISC: Analyzing Quarantine Files https://isc.sans.edu/forums/diary/Analyzing+Quarantine+Files/19867/ #> function Expand-BUPFile { [alias("Expand-BUPContent")] [alias("Expand-BUPContents")] [alias("Expand-BUPArchive")] param( [Parameter(mandatory=$True)] [Alias("Path", "Input")] [ValidateScript({ if( -Not ($_ | Test-Path) ) { throw "File of folder does not exist" } return $True })] [System.IO.FileInfo] $InputBUPFile, [Parameter(mandatory=$False)] [Alias("Output", "OutputPath")] [ValidateScript({ if( -not ($_ | Test-Path -PathType "container") ){ throw "Not a valid output directory" } return $True })] [String] $OutputDirectory = (Get-Item $InputBUPFile).Directory.FullName, [Switch] $InfoOnly ) process { $ModuleDirectory = (Get-Module "BUP-Extractor").Path | Split-Path -Parent $7zPath = Get-ChildItem "$ModuleDirectory\bin\7z.exe" | Select-Object -ExpandProperty "FullName" $FileName = [System.IO.Path]::GetFileNameWithoutExtension($InputBUPFile) $BUPOutputDirectory = Join-Path -Path $OutputDirectory -ChildPath $FileName if ($InfoOnly) { Write-Host "Extracting the BUP details file..." $ArgumentList = "e `"$InputBUPFile`" -o`"$BUPOutputDirectory`" -aoa -r `"Details`"" } else { Write-Host "Expanding the BUP archive..." $ArgumentList = "e `"$InputBUPFile`" -o`"$BUPOutputDirectory`" -aoa" } Start-Process -FilePath $7zPath -ArgumentList $ArgumentList -Wait $DetailsPath = Join-Path -Path $BUPOutputDirectory -ChildPath "Details" $File_0Path = Join-Path -Path $BUPOutputDirectory -ChildPath "File_0" Write-Host "Decoding the BUP details file..." Unprotect-BxorEncodedFile $DetailsPath $BUPDetails = $DetailsPath | Get-BUPFileDetails Write-Host "The BUP archive $FileName contains `"$($BUPDetails.File_0.OriginalName)`", which was detected as `"$($BUPDetails.Details.DetectionName)`"" Write-Host "The details file was decoded and extracted to $DetailsPath" if ($Info) { Return $BUPDetails } if (-not $InfoOnly) { Write-Host "Decoding the BUP qurantined file..." Unprotect-BxorEncodedFile $File_0Path Write-Host "The quarantined file was decoded and extracted to $File_0Path" } } } function Protect-BupFile { [alias("Unprotect-BupFile")] [alias("Encode-BupFile")] [alias("Decode-BupFile")] [alias("Protect-BxorEncodedFile")] [alias("Unprotect-BxorEncodedFile")] [alias("Bxor-File")] [alias("BxorEncode-File")] [alias("BxorDecode-File")] [CmdletBinding()] param( [Parameter(mandatory=$True, ValueFromPipeline=$True)] [ValidateScript({ if( -Not ($_ | Test-Path) ) { throw "Invalid input file - it may not exist" } return $True })] [System.IO.FileInfo] $InputFile, [Parameter(mandatory=$False)] [String] $OutputFile, [Parameter(mandatory=$False)] [Char] $Key = [Char]0x006A ) process { $FileNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($InputFile) $OutputDirectory = (Get-Item $InputFile).Directory.FullName if(-Not $OutputFile) { $OutputFile = (Join-Path -Path $OutputDirectory -ChildPath $FileNameWithoutExtension) } $OutputResult = Get-Content $InputFile -ReadCount 0 -Encoding Byte | Unprotect-Bytes -Key "0x006A" Set-Content -Path $OutputFile -Value $OutputResult -Encoding Byte -Force } } function Protect-Bytes { [alias("Unprotect-Bytes")] [alias("BxorEncode-Bytes")] [alias("BxorDecode-Bytes")] [alias("Encode-BytesWithBxor")] [alias("Decode-BytesWithBxor")] [CmdletBinding()] param( [Parameter(Mandatory=$True, ValueFromPipeline=$True)] [Byte[]] $InputByteArray, [Parameter(mandatory=$False)] [Byte] $Key = [Byte]0x006A ) process { if("BXORClass" -as [Type]) { } else { $SourceCode = @" using System; using System.Runtime.InteropServices; public class BXORClass { [DllImport("BXOR-DLL.dll")] public static extern void BXORBytes( [Out] byte[] byteArray, int len, byte key ); } "@ $ModuleDirectory = (Get-Module "BUP-Extractor").Path | Split-Path -Parent $DLLPath = Join-Path $ModuleDirectory -ChildPath '\bin\BXOR-DLL.dll' $DLLPath = $DLLPath -replace '\\', '\\' $Sourcecode = $Sourcecode -replace "BXOR-DLL.dll", $DLLPath Add-Type -TypeDefinition $SourceCode } $OutputByteArray = $InputByteArray [BXORClass]::BXORBytes($OutputByteArray, $OutputByteArray.Length, $Key) return $OutputByteArray } } function Get-BUPFileInfo { [alias("Get-QuarantineFileInfo")] [alias("Get-QuarantinedFileInfo")] [alias("Get-BUPFileDetails")] [alias("Get-QuarantineFileDetails")] [CmdletBinding()] param( [Parameter(mandatory=$True, ValueFromPipeline=$True)] [ValidateScript({ if( -Not ($_ | Test-Path) ) { throw "Invalid input file - it may not exist" } return $True })] [System.IO.FileInfo] $InputFilePath ) begin { if ($InputFilePath.Extension -match ".bup") { Expand-BUPFile -InputBUPFile $InputFilePath -InfoOnly } } process { $BUPDetailsObject = New-Object PSObject $DetailsFile = Get-Content $InputFilePath -ReadCount 0 for ($i=0; $i -lt $DetailsFile.Length; $i++) { if (($DetailsFile[$i] -match "\[(?<CurrentStanza>\w+)\]") -or $i -eq $DetailsFile.Length -1) { if ($CurrentStanzaObject) { $BUPDetailsObject | Add-Member -MemberType NoteProperty ` -Name $CurrentStanza ` -Value $CurrentStanzaObject } $CurrentStanzaObject = New-Object PSObject $CurrentStanza = $Matches.CurrentStanza } elseif($DetailsFile[$i] -match "^(?<Key>\w+)=(?<keyvalue>.*$)") { $CurrentStanzaObject | Add-Member -MemberType NoteProperty ` -Name $Matches.key ` -Value $Matches.keyvalue } } return $BUPDetailsObject } } |