Volume shadow copies can help you to:

recover a file, a directory or a volume, or Windows

find additional artifacts when conducting a live incident response.

It may be worth reading VISTA and Windows 7 Shadow Volume Forensics etc.

All the demos, I’ve seen so far were using the built-in DOS mklink command to mount a volume shadow copy and vssadmin to list shadow copies. While playing with vssadmin, I’ve found a use of case of the context parameter of the Select-String cmdlet.

vssadmin list shadows | Select-String -Pattern "shadow copies at creation time" -Context 0,3 | ForEach-Object { [pscustomobject]@{ Path = (($_.Context.PostContext -split "\r

")[2] -split ':')[1].Trim(); InstallDate = ($_.Line -split ':\s',2)[1]; } }



Ugly, I know. Let’s forget about this, there’s a very simple way return the list volume shadow copies on Windows as objects.

Last year, Boe Prox wrote an excellent article where he showed how to Create a Symbolic Link using PowerShell. Kudos to him, I’ll reuse his brilliant code 😀

Unfortunately, he didn’t show how to remove these symbolic links. The following MSDN article tells us how:

To remove a symbolic link, delete the file (using DeleteFile or similar APIs) or remove the directory (using RemoveDirectory or similar APIs) depending on what type of symbolic link is used.

Now let’s see what I propose to mount and dismount volume shadow copies:

Function Mount-VolumeShadowCopy { <# .SYNOPSIS Mount a volume shadow copy. .DESCRIPTION Mount a volume shadow copy. .PARAMETER ShadowPath Path of volume shadow copies submitted as an array of strings .PARAMETER Destination Target folder that will contain mounted volume shadow copies .EXAMPLE Get-CimInstance -ClassName Win32_ShadowCopy | Mount-VolumeShadowCopy -Destination C:\VSS -Verbose #> [CmdletBinding()] Param( [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] [ValidatePattern('\\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy\d{1,}')] [Alias("DeviceObject")] [String[]]$ShadowPath, [Parameter(Mandatory)] [ValidateScript({ Test-Path -Path $_ -PathType Container } )] [String]$Destination ) Begin { Try { $null = [mklink.symlink] } Catch { Add-Type @" using System; using System.Runtime.InteropServices; namespace mklink { public class symlink { [DllImport("kernel32.dll")] public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags); } } "@ } } Process { $ShadowPath | ForEach-Object -Process { if ($($_).EndsWith("\")) { $sPath = $_ } else { $sPath = "$($_)\" } $tPath = Join-Path -Path $Destination -ChildPath ( '{0}-{1}' -f (Split-Path -Path $sPath -Leaf),[GUID]::NewGuid().Guid ) try { if ( [mklink.symlink]::CreateSymbolicLink($tPath,$sPath,1) ) { Write-Verbose -Message "Successfully mounted $sPath to $tPath" } else { Write-Warning -Message "Failed to mount $sPath" } } catch { Write-Warning -Message "Failed to mount $sPath because $($_.Exception.Message)" } } } End {} } Function Dismount-VolumeShadowCopy { <# .SYNOPSIS Dismount a volume shadow copy. .DESCRIPTION Dismount a volume shadow copy. .PARAMETER Path Path of volume shadow copies mount points submitted as an array of strings .EXAMPLE Get-ChildItem -Path C:\VSS | Dismount-VolumeShadowCopy -Verbose #> [CmdletBinding()] Param( [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] [Alias("FullName")] [string[]]$Path ) Begin { } Process { $Path | ForEach-Object -Process { $sPath = $_ if (Test-Path -Path $sPath -PathType Container) { if ((Get-Item -Path $sPath).Attributes -band [System.IO.FileAttributes]::ReparsePoint) { try { [System.IO.Directory]::Delete($sPath,$false) | Out-Null Write-Verbose -Message "Successfully dismounted $sPath" } catch { Write-Warning -Message "Failed to dismount $sPath because $($_.Exception.Message)" } } else { Write-Warning -Message "The path $sPath isn't a reparsepoint" } } else { Write-Warning -Message "The path $sPath isn't a directory" } } } End {} }

Let’s see these two functions in action:

Mount all volume shadow copies

Get-CimInstance -ClassName Win32_ShadowCopy | Mount-VolumeShadowCopy -Destination C:\VSS -Verbose

Dismount all volume shadow copies mount points located in C:\VSS

Get-ChildItem -Path C:\VSS | Dismount-VolumeShadowCopy -Verbose

PowerShell rocks! No doubt 😎