PowerShell provides many ways to work with files and with other sorts of structured data it treats as files. Typically as shown before we can use the same commands as in cmd.exe but they parameters change also we can call many using he names of commands found in Unix type systems, these are aliases for PowerShell cmdlets so as to make the transition to PowerShell easier for administrators. Let have a look at the common commands used to manage files and their aliases. Do not worry to much on the manipulation commands used since I will cover those later in other blog posts but do take a look at what those aliases map to:
PS C:\> Get-Alias | where {$_.definition -match "path|item|content|location"} | Group-Object definition Count Name Group ----- ---- ----- 1 Add-Content {ac} 3 Get-Content {cat, gc, type} 3 Set-Location {cd, chdir, sl} 1 Clear-Content {clc} 1 Clear-Item {cli} 1 Clear-ItemProperty {clp} 3 Copy-Item {copy, cp, cpi} 1 Copy-ItemProperty {cpp} 1 Convert-Path {cvpa} 6 Remove-Item {del, erase, rd, ri...} 3 Get-ChildItem {dir, gci, ls} 1 Get-Item {gi} 2 Get-Location {gl, pwd} 1 Get-ItemProperty {gp} 1 Invoke-Item {ii} 3 Move-Item {mi, move, mv} 1 Move-ItemProperty {mp} 1 New-Item {ni} 1 Pop-Location {popd} 1 Push-Location {pushd} 2 Rename-Item {ren, rni} 1 Rename-ItemProperty {rnp} 1 Remove-ItemProperty {rp} 1 Resolve-Path {rvpa} 1 Set-Content {sc} 1 Set-Item {si} 1 Set-ItemProperty {sp}
As we can see in addition to the commands that we know from Unix type systems and those we use from cmd.exe we can find that PowerShell provides even more aliases for those cmdlets and for other actions we will discuss we will see that it has itβs own aliases and cmdlets.
Lets start with the concept that PowerShell treats files and folders as Items, the reason for this is that PowerShell treats other structure data as a file systems and calls the mappings to them PSDrives. To list the PSDrives on our current system we use the cmdlet Get-PSDive:
PS C:\> Get-PSDrive | ft -AutoSize Name Used (GB) Free (GB) Provider Root CurrentLocation ---- --------- --------- -------- ---- --------------- Alias Alias C 60.13 535.94 FileSystem C:\ cert Certificate \ D 764.70 166.81 FileSystem D:\ E 617.89 313.62 FileSystem E:\ Env Environment F FileSystem F:\ Function Function G FileSystem G:\ H FileSystem H:\ HKCU Registry HKEY_CURRENT_USER HKLM Registry HKEY_LOCAL_MACHINE I FileSystem I:\ J FileSystem J:\ Variable Variable WSMan WSMan
As we can see in addition to the normal drives we have on the system we have others drives we can navigate to:
Each of these PowerShell Drives are dependent on what is called PowerShell Providers that allow the access to the structured information. These can be listed with the Get-PSProvider cmndlet:
PS C:\> Get-PSProvider | ft -AutoSize Name Capabilities Drives ---- ------------ ------ WSMan Credentials {WSMan} Alias ShouldProcess {Alias} Environment ShouldProcess {Env} FileSystem Filter, ShouldProcess {C, D, E, F...} Function ShouldProcess {Function} Registry ShouldProcess, Transactions {HKLM, HKCU} Variable ShouldProcess {Variable} Certificate ShouldProcess {cert}
As we can see there are provider for other types other than FileSystem, this can me extended depending on PowerShell modules loaded and installed on a system for example on Windows 7 systems with the Remote Administration Tools or Windows 2008 R2 Domain Controller the can have access to an Active Directory provider, machines with the VMware PowerCLI installed will have access to providers for VMware Datastore and Virtual Infrastructures:
PowerCLI C:\> Get-PSProvider Name Capabilities Drives ---- ------------ ------ WSMan Credentials {WSMan} Alias ShouldProcess {Alias} Environment ShouldProcess {Env} FileSystem Filter, ShouldProcess {C, A, D} Function ShouldProcess {Function} Registry ShouldProcess, Transactions {HKLM, HKCU} Variable ShouldProcess {Variable} Certificate ShouldProcess {cert} VimDatastore ShouldProcess {vmstores, vmstore} VimInventory Filter {vis, vi}
Using one of this providers is quite simple, for it we use the New-PSDrive cmdlet, options for the cmdlet may change depending on the provider used so if using any external provide do look at the documentation provided by the company that made the provider. Each provider has different capabilities and this capabilities dictate what can be done on the data that is accessed, for example:
Lets map a drive:
PS C:\Users\carlos> New-PSDrive -Name isostore -Root \\192.168.1.2\isostore -PSProvider filesystem Name Used (GB) Free (GB) Provider Root CurrentLocation ---- --------- --------- -------- ---- --------------- isostore FileSystem \\192.168.1.2\isostore PS C:\Users\carlos> ls isostore: Directory: \\192.168.1.2\isostore Mode LastWriteTime Length Name ---- ------------- ------ ---- da--- 1/26/2012 12:49 PM Oracle da--- 3/27/2012 1:11 PM Microsoft da--- 3/15/2012 7:34 PM Linux da--- 12/30/2011 3:49 PM FreeBSD da--- 3/15/2012 7:33 PM Solaris d---- 12/2/2011 11:29 AM unlock-all-v102 da--- 3/15/2012 7:34 PM VMWare da--- 2/27/2012 8:04 AM Apple -a--- 2/24/2012 9:51 PM 3589316608 8250.0.WINMAIN_WIN8BETA.120217-1520_X64FRE_SERVER_EN-US-HB1_SSS_X64FRE_EN- US_DV5.ISO -a--- 1/4/2012 2:06 PM 403 shutdown_vms.rb -a--- 4/13/2011 3:17 AM 531705856 openfileresa-2.99.1-x86_64-disc1.iso -a--- 10/8/2007 4:06 PM 661127168 win2k3entsp2.iso -a--- 12/30/2011 7:32 PM 115838976 pfSense.iso -a--- 1/2/2012 11:16 PM 533204992 XenServer-6.0.0-install-cd.iso -a--- 1/4/2012 1:50 PM 177 shtdown.sh -a--- 5/4/2011 5:42 PM 369717248 VMware-VMvisor-Installer-4.0.0.Update01-208167.x86_64.iso
One thing that we need to keep in mind is that the drives we create are only present in the current PowerShell Session only and only can be accessed by the session so Windows Explorer and other tools on windows will not have access to the drive. Also as we can see in the example we can use a longer name for the drive than the letters we are used to use on Windows when mapping drives.
Lets look first at listing the contents of the current working folder for this we will use the Get-ChildItem cmdlet:
PS C:\> Get-ChildItem Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 7/13/2009 11:20 PM PerfLogs d-r-- 4/5/2012 10:27 PM Program Files d-r-- 4/8/2012 6:39 PM Program Files (x86) d---- 4/5/2012 7:42 PM Python27 d---- 4/5/2012 7:41 PM Python32 d---- 4/5/2012 7:38 PM Ruby193 d---- 4/6/2012 12:27 PM SysinternalsSuite d-r-- 4/5/2012 10:54 PM Users d---- 4/8/2012 11:14 AM Windows -a--- 4/5/2012 10:32 PM 1024 .rnd
As we can see we get a listing of the files and folders and basic information about them. Each item is in fact a .Net object of System.IO.FileInfo type that we can manipulate. Lets try searching in a given path for a file that matches a wild card, as we saw before when talink about PSProviders the FileSystem provider allows for filtering. Lets search for any file that starts with telnet in my install of Ruby 1.9.3:
PS C:\> Get-ChildItem -Path .\Ruby193 -Recurse -Filter telnet* Directory: C:\Ruby193\lib\ruby\1.9.1\net Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 5/18/2011 9:07 PM 32598 telnet.rb
Lets crate a directory and file for us to use to keep exploring the cmdlets, lets start by using the New-Item cmdlet to create a folder called testfolder:
PS C:\> New-Item -Path . -Name testfolder -ItemType "directory" Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 4/9/2012 11:45 AM testfolder
As with all cmdlets I mention on the blog posts I do recommend that you look at full help of the command and look at the members of the objects returned as covered in the initial blogposts.
Now lets create a file, for this we will use the ItemType of "file" to indicate we want a file.
PS C:\> New-Item -Path .\testfolder -Name testfile -ItemType "file" Directory: C:\testfolder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 4/9/2012 11:53 AM 0 testfile
Now that we have a file we can work with lets look at the properties and methods available with the Get-Item cmdlet:
PS C:\> Get-Item -Path .\testfolder\testfile | Get-Member TypeName: System.IO.FileInfo Name MemberType Definition ---- ---------- ---------- Mode CodeProperty System.String Mode{get=Mode;} AppendText Method System.IO.StreamWriter AppendText() CopyTo Method System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo CopyTo(s... Create Method System.IO.FileStream Create() CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType) CreateText Method System.IO.StreamWriter CreateText() Decrypt Method System.Void Decrypt() Delete Method System.Void Delete() Encrypt Method System.Void Encrypt() Equals Method bool Equals(System.Object obj) GetAccessControl Method System.Security.AccessControl.FileSecurity GetAccessControl(), System.Secur... GetHashCode Method int GetHashCode() GetLifetimeService Method System.Object GetLifetimeService() GetObjectData Method System.Void GetObjectData(System.Runtime.Serialization.SerializationInfo in... GetType Method type GetType() InitializeLifetimeService Method System.Object InitializeLifetimeService() MoveTo Method System.Void MoveTo(string destFileName) Open Method System.IO.FileStream Open(System.IO.FileMode mode), System.IO.FileStream Op... OpenRead Method System.IO.FileStream OpenRead() OpenText Method System.IO.StreamReader OpenText() OpenWrite Method System.IO.FileStream OpenWrite() Refresh Method System.Void Refresh() Replace Method System.IO.FileInfo Replace(string destinationFileName, string destinationBa... SetAccessControl Method System.Void SetAccessControl(System.Security.AccessControl.FileSecurity fil... ToString Method string ToString() PSChildName NoteProperty System.String PSChildName=testfile PSDrive NoteProperty System.Management.Automation.PSDriveInfo PSDrive=C PSIsContainer NoteProperty System.Boolean PSIsContainer=False PSParentPath NoteProperty System.String PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\testfolder PSPath NoteProperty System.String PSPath=Microsoft.PowerShell.Core\FileSystem::C:\testfolder\te... PSProvider NoteProperty System.Management.Automation.ProviderInfo PSProvider=Microsoft.PowerShell.C... Attributes Property System.IO.FileAttributes Attributes {get;set;} CreationTime Property System.DateTime CreationTime {get;set;} CreationTimeUtc Property System.DateTime CreationTimeUtc {get;set;} Directory Property System.IO.DirectoryInfo Directory {get;} DirectoryName Property System.String DirectoryName {get;} Exists Property System.Boolean Exists {get;} Extension Property System.String Extension {get;} FullName Property System.String FullName {get;} IsReadOnly Property System.Boolean IsReadOnly {get;set;} LastAccessTime Property System.DateTime LastAccessTime {get;set;} LastAccessTimeUtc Property System.DateTime LastAccessTimeUtc {get;set;} LastWriteTime Property System.DateTime LastWriteTime {get;set;} LastWriteTimeUtc Property System.DateTime LastWriteTimeUtc {get;set;} Length Property System.Int64 Length {get;} Name Property System.String Name {get;} BaseName ScriptProperty System.Object BaseName {get=if ($this.Extension.Length -gt 0){$this.Name.Re... VersionInfo ScriptProperty System.Object VersionInfo {get=[System.Diagnostics.FileVersionInfo]::GetVer...
For getting properties for the file object we have several ways to achive this first one is using the Get-ItemProperty cmdlet by given as the name the object property:
PS C:\> Get-ItemProperty -Path .\testfolder\testfile -Name LastAccessTime PSPath : Microsoft.PowerShell.Core\FileSystem::C:\testfolder\testfile PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\testfolder PSChildName : testfile PSDrive : C PSProvider : Microsoft.PowerShell.Core\FileSystem LastAccessTime : 4/9/2012 11:53:25 AM
Another Method we can use is to get the object and just request it, lets look at some properties that security professionals will find quite interesting:
PS C:\> (Get-Item -Path .\testfolder\testfile).LastWriteTime Monday, April 09, 2012 11:53:25 AM PS C:\> (Get-Item -Path .\testfolder\testfile).LastAccessTime Monday, April 09, 2012 11:53:25 AM PS C:\> (Get-Item -Path C:\Windows\System32\aaclient.dll).VersionInfo ProductVersion FileVersion FileName -------------- ----------- -------- 6.1.7600.16385 6.1.7600.1638... C:\Windows\System32\aaclient.dll
Just like other shell we can redirect output of commands as text to files using > and >> symbols:
Lets look also at the Add-Content cmdlet:
PS C:\> Add-Content -Path C:\testfolder\testfile -Value (get-date) PS C:\> Get-Content -Path C:\testfolder\testfile 4/9/2012 3:39:29 PM
Lets work with the object method to modify the file, in this case we will use EFS to encrypt the file on NTFS, lets start with checking if the file is encrypted:
PS C:\> (Get-Item -Path .\testfolder\testfile).attributes Archive
Now lets encrypt the file and see if its encrypted:
PS C:\> (Get-Item -Path .\testfolder\testfile).encrypt() PS C:\> (Get-Item -Path .\testfolder\testfile).attributes Archive, Encrypted
We can even confirm using the cipher.exe command:
PS C:\> cipher.exe /c .\testfolder\testfile Listing C:\testfolder\ New files added to this directory will not be encrypted. E testfile Compatibility Level: Windows XP/Server 2003 Users who can decrypt: infidel01\Carlos [Carlos(Carlos@infidel01)] Certificate thumbprint: 45F5 3D35 94B0 3C47 B727 AB63 0198 F19A 2793 1283 No recovery certificate found. Key Information: Algorithm: AES Key Length: 256 Key Entropy: 256
Lets Rename an item with the Rename-Item cmdlet:
PS C:\> Rename-Item -Path C:\testfolder -NewName test_folder PS C:\> ls Directory: C:\ Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 7/13/2009 11:20 PM PerfLogs d-r-- 4/5/2012 10:27 PM Program Files d-r-- 4/8/2012 6:39 PM Program Files (x86) d---- 4/5/2012 7:42 PM Python27 d---- 4/5/2012 7:41 PM Python32 d---- 4/5/2012 7:38 PM Ruby193 d---- 4/6/2012 12:27 PM SysinternalsSuite d---- 4/9/2012 4:44 PM test_folder d-r-- 4/5/2012 10:54 PM Users d---- 4/8/2012 11:14 AM Windows -a--- 4/5/2012 10:32 PM 1024 .rnd
Lets delete the file we have been using for the examples:
PS C:\> Remove-Item -Path C:\test_folder\testfil PS C:\> ls .\test_folder Directory: C:\test_folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 4/9/2012 3:39 PM 21 testfile PS C:\> Remove-Item -Path C:\test_folder\testfile PS C:\> ls .\test_folder
Lets look at working with paths, we will firs start with defining the difference of Path and LiteralPath in the parameters of several commands. This is a source of confusion for many people learning PowerShell on their own by exploring the shell cmdlets. When working with a file system on a drive or share Powershell Windows restricts the characters that can be used for a file name, like *, ?, /, $ and others since they are use for variable expansion and wildcard search but since PowerShell lets us work with Active Directory, Certificate Store, Registry and others that do not have the same restrictions as the file system. This is why we use -Path when we want the special characters treated as wildcards and -LiteralPath for those cases where those special characters are part of the item names. An example of expansion:
PS C:\> Set-Location -Path Perf* PS C:\PerfLogs>
We can see as wildcards where used to match the path. To get the current location of where we are in a provider we use the Get-Location cmdlet:
PS C:\PerfLogs> Get-Location Path ---- C:\PerfLogs
To Change locations we use the Set-Location cmdlet:
PS C:\PerfLogs> Set-Location C:\testfolder PS C:\testfolder> Get-Location Path ---- C:\testfolder
We can take a path and add a child item to the path with Join-Path cmdlet:
PS C:\> Join-Path -Path C:\Windows -ChildPath system C:\Windows\system
We can also have it join a path using wildcards:
PS C:\> Join-Path -Path C:\Win* -ChildPath tem* -Resolve C:\Windows\Temp
We can also give it a list of path to append a child object to:
PS C:\> join-path -path c:\windows,c:\python,c:\ruby -ChildPath temp c:\windows\temp c:\python\temp c:\ruby\temp
Some time we will find our self with path that we obtained from a property of an object and we may need to extract parts of the path, for this we will use the Split-Path cmdlet and we can get different pats of the paths depending of what we want:
PS C:\> split-path c:\windows\secret.txt c:\windows PS C:\> split-path c:\windows\secret.txt -Qualifier c: PS C:\> split-path c:\windows\secret.txt -NoQualifier \windows\secret.txt PS C:\> split-path c:\windows\secret.txt -Parent c:\windows PS C:\> split-path c:\windows\secret.txt -Leaf secret.txt
It also supports extracting parts from other types of paths:
PS C:\> Split-Path -Path /var/log/tftp.log -Leaf tftp.log PS C:\> Split-Path -Path /var/log/tftp.log -Parent \var\log PS C:\> split-path -Path http://www.darkoperator.com/index.html -Qualifier http: PS C:\> split-path -Path http://www.darkoperator.com/index.html -NoQualifier //www.darkoperator.com/index.html
We can test if a path exists:
PS C:\> test-path -path HKLM:\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell True PS C:\> test-path -path C:\Windows True PS C:\> test-path -path C:\Windows\system32\aaclient.dll True
As we can see this works with both files, folders and even other paths in other providers. Lets say we want to test is the path is for a File or a Folder, for this we will use Container for Folder and Leaf for File:
PS C:\> test-path -path C:\Windows\system32\aaclient.dll -PathType leaf True PS C:\> test-path -path C:\Windows\system32\aaclient.dll -PathType container False
I invite you to keep exploring in the registry, variables and other psdrives available and learning what is possible and not and the differences in the parameters we can use with this providers. As always I hope this blog post is useful and informative.