Bootcamp: Powershell Olympics
Last updated
Last updated
1) Enter the command Write-Host 'Hello, PowerShell!'
, then press Enter.
2) PowerShell commands and parameters options are not case-sensitive. Press the up arrow to return to the previous command, then change Write-Host to read write-host (lowercase). Press Enter to run the command.
3) PowerShell supports many different commands. Let's try another. Type Clear-Host then press Enter. You can also clear your screen by pressing CTRL+L.
4) Next, let's try a different PowerShell command: Get-ChildItem
. This command lists files and objects.
5) Use tab completion to run the Get-NetAdapterAdvancedProperty
by typing just a few characters, then press Enter.
6) Use Get-ChildItem
to list the contents of the C:\Program Files (x86) directory by typing just a few characters
7) Run Get-Verb
to see a list of approved PowerShell verbs.
8) Next, let's look at some popular Verb-Noun PowerShell functionality. Run the Get-Date
command.
9) Next, type Get-Volume
, then press Enter.
10) Next, let's try a different verb. Type the Start-Process Notepad
, then press Enter.
11) Next, let's look at the Get-Process
command. Type Get-Process Notepad
, then press Enter.
12) Next, type Stop-Process Notepad
, then press Enter.
13) Let's investigate the Start-Process
command further. We can use Get-Command
to collect additional information about PowerShell commands. Type Get-Command -Name Start-Process
, then press Enter.
14) Let's take a look at another command using Get-Command
. Type Get-Command -Name Get-Volume
, then press Enter.
15) Type Get-Command -Noun Volume, then press Enter.
16) Aliases are shortcuts to run PowerShell commands. Instead of Get-ChildItem, you can run the dir command.
17) You can use the Get-Alias command to look up the command associated with an alias. Type Get-Alias Dir, then press Enter.
18) Type Get-Alias -Definition Get-ChildItem, then press Enter.
19) Find an alias for Get-Timezone.
20) Next, find out which command uses the alias ps.
21) Type Get-Help, then press Enter to display PowerShell's help system information.
22) Get-Help also provides instructions for specific commands. Run Get-Help Get-Process to get help information for the Get-Process cmdlet.
23) When you run Get-Help, the help information displays all at once. Most of the time, we want to read the help documentation one screen at a time. This is the behavior of the Help function.
Type Help Get-Process, then press Enter. Press the spacebar several times to get to the end of the help information, then run next to continue.
24) But there's more! Type Get-Help -Examples Get-Process, then press Enter. Scroll up to scan through the help information.
25) PowerShell also provides high-level help information. Type Get-Help about_Core_Commands, then press Enter.
26) There are many other PowerShell about help subjects. Type Get-Help about*, then press Enter.
27) Type ping 127.0.0.1, then press Enter to use the ping.exe program.
28) Type sc query, then press Enter. PowerShell will prompt you for an option; press Enter when prompted.
1) Let's start with a straightforward command. Type Get-Service
, then press Enter.
2) The Get-Service cmdlet allow us to interrogate the services running on the Windows system. There are a lot of services though, so it can be difficult to catch all of the output.
Press the up arrow to recall the Get-Service
command. At the end of the line add a space followed by | more, then press Enter. Scroll through the output by pressing the spacebar multiple times until you return to the prompt.
3) Use Sort-Object as a pipeline command to change the sort order of the Get-Service output to alphabetically descending. Type Get-Service | Sort-Object -Descending
, then press Enter.
4) Create a pipeline to sort the output of Get-Process by name. Type Get-Process | Sort-Object -Property Name
, then press Enter.
5) In this output we see that Sort-Object
has sorted the output by the Name property. You can also sort by process ID using the Id property.
6) Sort-Object can also make the output unique. This can be useful to identifying the name of each item, eliminating duplicates.
Retrieve the process list again, sorting by name in alphabetically descending order. Add the -Unique
argument to make the list of processes unique.
7) Like other shells, PowerShell supports redirection operators such as > to send the output of the pipeline to a file.
Run the PowerShell Get-Process, redirecting the output to a file in the default user profile directory called processes1.txt
8) The redirection operator > allows us to create a file with the output of the pipeline, but we have little flexibility in defining the format of the file. This is where the cmdlet Out-File
is useful.
Press the up arrow to recall the previous command. Instead of using the redirection operator, create a pipeline after Get-Process adding Out-File -FilePath processes2.txt
8) When you use the redirection operator > or Out-File, PowerShell takes the pipeline output and creates a little-endian UTF-16 unicode text file with a Byte Order Mark (BOM) at the beginning of the file. That is, these look like text files, but they aren't plain ASCII files.
To create a plain ASCII text file, use Out-File
with the parameter -Encoding ASCII
. Run the previous command again, this time creating an ASCII file called processes3.txt
9) Run Get-ChildItem processes*
.Notice how the third file (the ASCII file) is approximately half the size of the other two. This is because UTF-16 format requires two bytes per ASCII character.
10) Examine the first several lines of the processes3.txt file. Type Get-Content -Path processes3.txt -First 5
, then press Enter.
11) No surprise here, the content of processes3.txt is just like what we saw when we ran Get-Process output in the terminal. Adding Out-File
just redirects the output to a file.
What if you want the output to go to the screen and go to a file? This is the job of the Tee-Object cmdlet and the PowerShell pipeline. Type Get-Process | Tee-Object -FilePath processes4.txt
, then press Enter.
Tee-Object
duplicated the output of Get-Process
, showing it on the screen and saving it to processes4.txt
12) PowerShell also supports other output mechanisms, such as Export-Csv
. Create a list of processes, sending the output to the pipeline with Export-Csv -Path processes.csv
.
Export-Csv
took the output of Get-Process
and converted it into a CSV file, allowing us to process the data with other tools.
13) Take a look at the first several lines of processes.csv using Get-Content. Limit the output to the first 5 lines.
This is a lot more information than what we saw when we used Get-Process | Out-File
. Earlier we saw 8 columns of information but the CSV file has over 60 columns!
This highlights an important concept about the PowerShell pipeline: PowerShell passes objects in the pipeline, not just text. An object is a collection of code (methods) and data properties that represents the item. Type next then press Enter to continue.
14) This is an important concept. When you run a PowerShell command and send the output in a pipeline, you are sending an object or a collection of objects to the next step in the pipeline. These objects can include more information than what you see by default.
Using PowerShell, we can retrieve the information that is important to us using different commands. Let's try it now. Type Get-Process -Name lsass | Select-Object -Property *
, then press Enter.
15) Use Get-Process
to collect the default properties for the rexplorer process.
Get-Process shows some information, but let's gather more data. Let's examine summary information about the object returned by Get-Process using the PowerShell pipeline.
16) Run Get-Process -Name rexplorer | Measure-Object
Measure-Object returns information about the numeric properties of the objects received in the pipeline. In this example, Measure-Object
tells us that there are 2 objects in the pipeline, corresponding to the two instances of the rexplorer process. We'll return to Measure-Object
when we look at file processing.
17) Next, let's look at the object members. Run Get-Process -Name rexplorer | Get-Member
Get-Member returns information about the object properties and methods received in the pipeline. For Get-Process
, we see lots of interesting properties about the processes, but no values.
18) To see the values and the property names, run Get-Process -Name rexplorer | Select-Object -First 1 -Property *
In the previous command we used Select-Object
to get a list of the properties available, limiting it to the first object returned with -First 1
to avoid repeating the output for the two rexplorer processes. In this output we see the same property names that we saw with Get-Member
, but we can also see the values for the first object.
This type of analysis is called introspection: the ability to examine properties of an object at runtime. We'll use Get-Member and Select-Object -Property * often to see which properties are available when working with PowerShell objects.
19) Let's retrieve specific properties about the rexplorer process using Select-Object
. Instead of using -Property *
, we can specify a comma-separated list of properties that we want to see.
Run Get-Process -Name rexplorer | Select-Object -Property Name, Id, Path
to get the process name, process ID, and process path information.
20) Repeat the last command by pressing the up arrow, then add two additional properties: CPU and WorkingSet64
21) Press the up arrow to recall the previous command and extend the pipeline, adding Format-Table
as the last element.
22) Many of the associated PowerShell nouns work together in a pipeline. You can use a PowerShell command such as Get-Process
, then stop the target process by adding Stop-Process
to the pipeline with no parameters.
Type Get-Process -Name rexplorer | Stop-Process
then press Enter to terminate the rexplorer processes.
23) Since PowerShell is designed around the concept of using a pipeline, there are lots of cmdlets that allow us to leverage this functionality. We can take the output of Get-Process
and export the results to an HTML report using ConvertTo-HTML, for example.
Run Get-Process | Select-Object -Property Name, Id, Path, CPU, WorkingSet64 | ConvertTo-Html | Out-File processes.html
to create an HTML report of all running processes, saving the output as processes.html
24) Once you understand the concepts around PowerShell pipelines, it can be applied to lots of different functionality. For example, we can get basic information about the Event Log service by running Get-Service -Name eventlog
25) Press the up arrow to recall the previous command. Use Select-Object
in a pipeline to retrieve the following fields about the Event Log service: Status, Name, DisplayName, and StartType
26) Press the up arrow to recall that command, then display the results in a table format.
27) So far we've started out pipeline examples with Get-Process
and Get-Service
, but that doesn't have to be the start of the pipeline. Examine the status of the WinRM service: type 'winrm' | Get-Service
then press Enter.
In this command we created a string object 'winrm' as the input to the Get-Servic
e cmdlet.
27) Type 'winrm' | Get-Member
to see all of the string properties.
28) Run Get-Content -Path services.txt
29) This file has several services listed. We can quickly interrogate all of the services by leveraging PowerShell parameter binding in the pipeline.
Press the up arrow to recall the previous Get-Content
command, then add Get-Service
to the pipeline.
30) Run 'rexplorer' | Stop-Process
Stop-Process returns an error here, not because it doesn't support parameter binding (it does), but because it can't accept a string as the default parameter. However, you can build a pipeline starting with Get-Process -Name rexplorer
, followed by Stop-Process
. Do that now to stop the rexplorer process.
31) Get a list of running services using Where-Object
by typing Get-Service | Where-Object -Property Status -EQ Running
, then press Enter.
32) Type Get-Service | Where-Object -Property DisplayName -Like 'Application*'
then press Enter.
33) Use Get-Process
to identify all processes running from a path that includes the string temp.
Here we identifed all processes running from a directory with temp anywhere in the name. This is useful for incident response, since non-malicious processes rarely launch from temporary directories.
34) Press the up arrow to recall the previous command. Use the pipeline to retrieve multiple details about these processes including the name, ID, path, and start time.
35) Now, extend the pipeline to sort the results by StartTime.
36) Export this analysis into a CSV file named malicious-processes.csv and an HTML report named malicious-processes.html
37) Next, terminate all of the processes running from temporary directories.
39) Remove the files starting with services in the file name.
1) Navigating the file system is an essential skill for using PowerShell well. Start by examining your current directory.
Run Get-Location
to examine your current directory.
2) You can also use the alias pwd for Get-Location
. Try running the alias for Get-Location
now.
3) In PowerShell you can use Set-Location
to change to a different directory. Use Set-Location
to change to the C:\temp\bobsled directory, then run Get-Location
to display the current directory.
4) In the previous command you used an absolute directory reference, but PowerShell also supports a relative directory reference.
Use Set-Location
to go back one directory by running Set-Location ..
then run Get-Location
to display the current directory.
5) Next, return to the bobsled directory, this time using the relative path name with Set-Location
, then run Get-Location
to display the current directory.
6) Next, let's create a new directory. Creating directories in PowerShell is the job of the New-Item
cmdlet. To create a directory, specify -ItemType
Directory and the name of the directory using -Name DirectoryName.
Use New-Item
to create the directory sled (you need to already be in the C:\temp\bobsled directory).
7) PowerShell will let you specify multiple directories in one command, creating each needed sub-directory. Run the New-Item
command again, this time creating the following nested directories for a 4-person bobsled team: Brakeman\Pusher\Pusher\Pilot.
8) The New-Item
syntax to create directories can be simplified using the mkdir
function: mkdir DirName
. Create the directory structure using mkdir
for a 2-person bobsled team: Brakeman\Pilot.
9) When you want to remove a directory, you can use Remove-Item
followed by the directory name. Remove the sled directory using the Remove-Item
cmdlet.
10) PowerShell will prompt you if you try to remove a directory that has sub-directories or files in it. You can add the -Recurse
argument to remove a directory and everything in it without a prompt. Remove the brakeman directory and sub-directories using Remove-Item
. Add -Recurse
to remove the directories without a prompt.
11) Next we'll look at the powerful cmdlet Get-ChildItem
. This cmdlet allows us to list items and child items in one or more directories.Run the Get-ChildItem
cmdlet with no arguments. Make sure you are in the C:\temp\bobsled directory. Note that we've added some files for your analysis.
12) Get the content of the data folder. Use -Name also.
13) Run the previous command again, this time adding -Recurse
to display file and directory names in all subdirectories.
14) Let's focus on the files in the data/1952 directory. Change to this directory, then run Get-Location
. Then list the files in this directory.
15) By default, Get-ChildItem
doesn't list files that have the hidden attribute set. To display hidden files, add the -Force
argument. Run the previous command again, this time adding -Force
.
16) Using Get-ChildItem
you can search for file or directory names by specifying a name as an argument. When you add -Recurse
, you can search all subdirectories as well.
Search for the file named Hans_He-GER-Bronze.txt.
17) You can also use the wildcard * in the file name to perform partial name matches. Use Get-ChildItem
with a wildcard to identify all Gold medal winners.
18) PowerShell also allows you to exclude named with the -Exclude
argument. PowerShell also allows you to exclude named with the -Exclude
argument.
19) The Get-ChildItem
cmdlet is especially powerful, not just because you can list files and directories, but also because it allows you to examine other PowerShell Drives as well.
We normally think of drives as a drive letter (C:, E:, etc.), but PowerShell also supports other drive prefixes. Run the Get-PsDrive
cmdlet to see a list of PPowerShell drives on the system.
The output of Get-PSDrive shows us other PowerShell drive prefixes that we can also interrogate with Get-ChildItem.
20) Examine the contents of the Env drive prefix using Get-ChildItem
.
21) Repeat the prior command, this time examining the contents of the HKCU PowerShell Drive.
HKCU is an alias for the Windows registry hive HKEY_CURRENT_USER
. Since PowerShell treats this as a drive, we can use Get-ChildItem
to interrogate the registry.
22) Repeat the prior command, this time specifying the registry location HKCU:\Software.
23) Here we see the Software registry key. Within this key we see several subkeys, including Microsoft. Repeat the prior command, this time examining the contents of the HKCU:\Software\Microsoft key.
24) As you can see, we can navigate the contents of the registry hive very much like we navigate a conventional file system. Examine the registry key values in the HKCU:\Software\Monobob2022 key.
25) Repeat the last command, enumerating the contents of each of these registry keys by adding the -Recurse
option.
26) Repeat the following command, this time adding the pipeline Where-Object -Property Name -Match "Qing"
to find the key matching the Olymplic athlete name.
27) Next, let's investigate one more PowerShell Drive provider: system certificates. Use Get-ChildItem
to examine the Cert: drive.
28) Examine the trusted root certificate authorities on the local system using Get-ChildItem Cert:\LocalMachine\Root
PowerShell has support for variables: named objects that hold data and code. First we'll look at PowerShell's automatic variables.
1) PowerShell has several automatic variables available to every PowerShell session that describes the environment. Let's examine several of them.
2) Examine the available members by typing $Host | Get-Member
, then press Enter.
The $Host variable is an object, just like everything else in PowerShell. Here we see the names of the properties and the methods. We can access these object members using dot notation.
3) Access the Version property of the $Host
variable: type $Host.Version
, then press Enter.
Like $Host, $Host.Version is also a PowerShell object. Accessing this member shows the table format for the major, minor, build, and revision members. We can see all of the members in this object like we did for $Host using dot notation.
4) Run the previous command with Get-Member
.
5) Here we see the list of properties for the $Host.Version property. If you want to access just the Major value you can reference this member directly using more dot notation.
6) You can also invoke the methods associated with the $Host.Version
object by specifying the method name followed by () (sometimes with a value inside the parenthesis).
Invoke the ToString()
method by typing $Host.Version.ToString()
, then press Enter.
7) Let's take a look at a few more useful variables. Type $Home
then press Enter.
9) We've created a file C:\TEMP\report.html; copy it to your user profile directory by typing Copy-Item C:\TEMP\report.html $Home
10) The $Error
automatic variable keeps track of all the times your commands generated an error for the current session. This is valuable since your custom PowerShell programs can reference error messages to provide help to users.
11) We'll look at one more automatic variable: $?
. First, run Get-ChildItem -Name report.html
Notice how $?
returns True. True is a Boolean data type; that is, it is either True or False. The $?
variable indicates if the last command ran successfully. The output True here indicates that the Get-ChildItem
command ran successfully.
12) Let's look at another case: Type Get-ChildItem -Name report.txt, then press Enter. Then type $?
.
Notice how the value of $?
is now set to False. This tells us the previous command did not run successfully. This is valuable in PowerShell scripts to test if the previous command returned an error. PowerShell has other automatic variables as well, you can read about them by running help about_Automatic_Variables
.
13) You may remember this from the PowerShell Olympics Using the Pipeline event. Get-Process
retrieves a list of process details.
Run the command again, this time creating a variable name $processes
to store the process information by typing $processes = Get-Process
, then press Enter.
Type $processes
and press Enter.
14) Declare a new process information variable: type $pnow = Get-Process
, then press Enter.
We can compare the $processes
and $pnow
variables using Compare-Object
. Type Compare-Object $processes $pnow
, then press Enter.
Notice how Compare-Object
examines the two variables and shows us the differences? This is a powerful feature of PowerShell variables: they allow us to save a point-in-time representation of any object output, then compare it to another similar variable later.
15) In the output of Compare-Object
we see the rexplorer process is back. Type $targetproc = Get-Process -Name rexplorer
then press Enter.
Notice how this time the variable holds just the process information for rexplorer, corresponding to the -Name
argument of the Get-Process
command. We can continue to leverage the features of different PowerShell commands as needed to refine what is represented in the variable.
16) Use the PowerShell pipeline to examine the members of the $targetproc
variable with Get-Member
.
Scroll up a bit and examine some of the methods and properties associated with the object. Notice the Kill()
method that can be invoked to kill the processes identified in the $targetproc
variable. Methods for an object can be invoked using dot notation ObjectName.Method()
.
17) Invoke the Kill()
method on the $targetproc
variable to terminate the two processes using dot notation.
By calling the Kill()
method on the $targetproc
variable you terminated both the rexplorer processes. You could have used the pipeline, sending the variable to Stop-Process
as well; which one you choose is a matter of preference.
18) Let's start by declaring a variable that represents a number. Type $limit = 10
, then press Enter.
19) Like everything in PowerShell, $limit
is an object. Take a look at the members of this object by running $limit | Get-Member
At the top of this output look at the line starting with TypeName. When you declared the $limit variable with the value 10, PowerShell decided to use a type of Int32 (a 32-bit integer) to represent this data. The methods associated with this object are mostly for converting this to other types. Let's invoke one of those methods now.
20) Type $limit.GetType()
then press Enter.
21) PowerShell is a dynamically typed scripting language, which means that every variable has a type, but that variable type can change (it's dynamic). Let's experiment with that now: type $limit = '100'
(with the quotation marks), then press Enter.
Press the up arrow a few times, recalling the $limit | Get-Member
command, then press Enter.
If you scroll up again you'll see the type has changed. This time, $limit
is a String type with different methods and properties. When you specify quotation marks around a value, PowerShell will treat it as a string. Numbers assigned to a variable without quotations will be assigned a type of Int32 or other numeric types depending on the number selected.
22) Next, let's look at something cool and useful. Type $limit = 100MB
, then press Enter.
Instead of $limit
having a value of 100, it has a value of 104857600, the number of bytes in 100 MB. PowerShell is designed to be "administrator first", with handy integer literals to interpret values including kilobytes, megabytes, gigabytes, terabytes, and petabytes.
23) PowerShell can also work with converting hexadecimal and binary values using the prefix 0x or 0b. Change $limit
to 0x31337 now.
24) Next, let's look at another type: arrays. PowerShell arrays hold zero or more elements denoted by @()
notation. Create a PowerShell array: type $alpinecourses = @('Rock', 'Ice River', 'Nile')
then press Enter.
Access the last element of the array. Type $alpinecourses[2]
, then press Enter.
Access the first element of the array.
25) Retrieve a list of the Windows services using Get-Service
, saving it in a variable called $services
.
Call the GetType()
method on the $services
variable.
The $services
variable has a type of Object[]
, but notice how the base type indicates that it is an array. We can use the array subscript notation to access different elements in this variable as well.
26) We'll look at one more variable type: DateTime. Type Get-Date | Get-Membe
r, then press Enter.
The PowerShell Get-Date
command returns a DateTime type which represents the date and time when you invoke the command. We can use this command to record a start and ending time for an operation (such as a long-running job). Let's do that now.
27) Create two variables, $start
and $stop
using Get-Date
, with a delay of 3 seconds between using Start-Sleep 3. We'll use the command separator ; to run all three commands on one line. Type $start = Get-Date ; Start-Sleep 3 ; $stop = Get-Date
then press Enter.
Subtract $stop
from $start
to see the time difference: $stop - $start
The TotalSeconds property will show you the time delay between the two Get-Date commands, which will be just over 3 seconds. Get-Date
will return the current date, but we can also specify an arbitrary date.
28) Create a $future
variable string of Feb. 8, 2030 at 9:00 AM (use MM/DD/YYYY or DD/MM/YYYY based on your regional convention; add a space after the year then 9:00 AM for the time).
Try to subtract the $start
object from $future
.
We know that $future
is a string object, since we declared it using quotes. We received an error because we can't perform arithmetic using a string and a DateTime object. We need to convert the string to a DateTime object using the cast operator.
29) In PowerShell, you can specify a type in square brackets before the variable name to convert it to the specified type. Let's try that now.
Type [DateTime]$future
, then press Enter.
PowerShell converted the string in $future to a DateTime object.
30) Press the up arrow several times to return to the subtraction operation and add a typecast to calculate the time span difference between $future
and $start
.
31) PowerShell can easily embed variables inside of strings. To practice this, create a variable called $favcolor
with a value of 'gold'.
Send a message to the console, using string substitution to expand the $favcolor variable. Type Write-Host "My favorite color is $favcolor."
Press the up arrow to recall the last command and change the double quotes to single quotes, then press Enter.
32) Along with string substitution, we also have escape sequences in strings. The backtick character is used to indicate an escape sequence, followed by an escape sequence character. For example, `n is used to indicate a newline.
We can create a prompt to accept user input with Read-Host
. Type $age = Read-Host "Please enter your age.`nBe honest please"
then press Enter. Enter a value when prompted, then press Enter.
Here we used ` to indicate that a newline character should be inserted in the string. The Read-Host
command prompts the user for input and saves it in the specified variable. Examine the contents of the $age
variable to see your answer.