When performing threat hunting and live system analysis, I will often look at the processes running on the system. Often, a compromised system will run one or more processes that look suspicious, which gives us an opportunity to identify the threat.
Let's look at how we can use PowerShell to evaluate a running system. We'll focus on two primary PowerShell commands: Get-Process and Get-CimInstance using the Win32_Process class. This first article will focus on using the PowerShell commands and collecting the data. In the second article, we'll look at applying these commands to investigate malicious code running on a Windows host.
TIP: If you are reading this article from Windows, you can follow along with each of the examples to practice! If you use a browser other than Chrome, substitute that browser name where I use chrome below.
Listing Processes
PowerShell makes it easy to list processes using Get-Process:
PS C:\Users\Sec504> Get-Process Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 317 32 42576 107816 2.77 1520 1 chrome 310 33 103376 81224 2.98 1708 1 chrome 151 9 2008 6980 0.02 2660 1 chrome 209 13 6788 16640 0.05 4844 1 chrome 988 42 51576 114540 3.28 6368 1 chrome ...
We can investigate a specific process by supplying the process name as an argument:
PS C:\Users\Sec504> Get-Process explorer Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 2332 93 82448 134420 14.31 4808 1 explorer
NOTE: Don't specify the .exe part of the process executable name when using Get-Process.
Wildcards work too:
PS C:\Users\Sec504> Get-Process vm* Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 92 7 1328 920 0.03 6520 1 vm3dservice 445 23 8672 10872 3200 0 vmtoolsd 541 36 23796 28980 13.64 6356 1 vmtoolsd
In the Get-Process output, we get several columns of detail for each process:
- Handles: The number of handles (threads, open files, registry objects, etc.) accessible
- NPM(K): Non-paged memory in use
- PM(K): Pageable memory in use
- WS(K): Memory working set (memory that is actively in use)
- VM(M): Virtual memory in use (real and paged memory on disk)
- CPU(s): Cumulative processor time used, in seconds.
- ID: Process ID (PID)
- ProcessName: Process name
The defaults are useful, but perhaps more to system administrators than security analysts. Fortunately, Get-Process offers more process details as well.
Process Details
We can get a list of the process properties details available with Get-Process using Get-Member:
PS C:\Users\Sec504> Get-Process | Get-Member -MemberType Properties TypeName: System.Diagnostics.Process Name MemberType Definition ---- ---------- ---------- Handles AliasProperty Handles = Handlecount Name AliasProperty Name = ProcessName NPM AliasProperty NPM = NonpagedSystemMemorySize64 PM AliasProperty PM = PagedMemorySize64 SI AliasProperty SI = SessionId VM AliasProperty VM = VirtualMemorySize64 WS AliasProperty WS = WorkingSet64 __NounName NoteProperty string __NounName=Process BasePriority Property int BasePriority {get;} Container Property System.ComponentModel.IContainer Container {get;} EnableRaisingEvents Property bool EnableRaisingEvents {get;set;} ...
For incident response investigations, I like to examine the following parameters for processes:
- Name
- Handle count
- Id
- Path
- Command line
- Bytes read and written to the disk
- Memory used (real and virtual)
- Parent Id
- Process creation date and time
Here's where I get disappointed with Get-Process: it offers a lot of useful information, but it also omits lots of useful details about processes. We can modify the columns retrieved to get specific properties using Select-Object:
PS C:\Users\Sec504> Get-Process chrome | Select-Object -Property Name, Id, Path, WorkingSet64 Name Id Path WorkingSet64 ---- -- ---- ------------ chrome 1520 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe 110432256 chrome 1708 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe 82894848 chrome 2660 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe 7159808 chrome 4844 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe 16973824 chrome 6368 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe 117047296 chrome 7496 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe 26345472 chrome 7528 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe 26374144
Unfortunately, Get-Process doesn't offer all of the detail we want. Fortunately, Microsoft also makes Get-CimInstance available.
Detailed Process ... Details
Get-Process is good for simple interrogation of processes, but if you want detailed information, you'll want to use Get-CimInstance instead with the Win32_Process class.
PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process ProcessId Name HandleCount WorkingSetSize VirtualSize --------- ---- ----------- -------------- ----------- 0 System Idle Process 0 8192 8192 4 System 2471 36864 3985408 92 Registry 0 27586560 94515200 316 smss.exe 53 274432 2203359694848 424 csrss.exe 578 2191360 2203413659648 ...
NOTE: Get-CimInstance is powerful, but it is only available for systems that implement the full Common Information Model. Practically, this means that Get-CimInstance is only useful on Windows systems.
At first glance, Get-CimInstance -Class Win32_Process returns information that is similar to that of Get-Process, but we can get a lot more process detail information from Get-CimInstance:
PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Get-Member -MemberType Properties TypeName: Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process Name MemberType Definition ---- ---------- ---------- Handles AliasProperty Handles = Handlecount ProcessName AliasProperty ProcessName = Name VM AliasProperty VM = VirtualSize WS AliasProperty WS = WorkingSetSize Caption Property string Caption {get;} CommandLine Property string CommandLine {get;} CreationClassName Property string CreationClassName {get;} ...
Often I'll use the following set of parameters to collect information about running processes when I'm looking for threats on a Windows system:
PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Select-Object -Property Name, HandleCount, ProcessId, ParentProcessId, Path, CommandLine, WriteTransferCount, ReadTransferCount, WorkingSetSize ... Name : chrome.exe HandleCount : 317 ProcessId : 1520 ParentProcessId : 6368 Path : C:\Program Files (x86)\Google\Chrome\Application\chrome.exe CommandLine : "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --type=renderer --display-capture-permissions-policy-allowed --lang=en-US --device-scale-factor=2 --num-raster-threads=1 --renderer-client-id=5 --launch-time-ticks=3385309652 --mojo-platform-channel-handle=2832 --field-trial-handle=1804,i,7538668890417119548,1058345838094988730,131072 /prefetch:1 WriteTransferCount : 22843548 ReadTransferCount : 2816643 WorkingSetSize : 110432256 ...
Get-CimInstance does not allow us to specify a process name as an argument like Get-Process does. If you want to use Get-CimInstance to return information about a specific process by name, you can add a Where-Object command to the pipeline:
PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Select-Object -Property Name, HandleCount, ProcessId, ParentProcessId, Path, CommandLine, WriteTransferCount, ReadTransferCount, WorkingSetSize | Where-Object -Property Name -E "explorer.exe" Name : explorer.exe HandleCount : 2244 ProcessId : 4808 ParentProcessId : 4760 Path : C:\WINDOWS\Explorer.EXE CommandLine : C:\WINDOWS\Explorer.EXE WriteTransferCount : 89089 ReadTransferCount : 5787331 WorkingSetSize : 131350528
TIP: Using Where-Object is this style is very handy, since we can filter the results using any of the properties returned by Get-CimInstance. We'll look at this again in the next article where we apply these techniques to investigate malware.
PID Relationships
Let's look at an example of applying these PowerShell process-interrogation techniques: parent and child relationships. Using Get-CimInstance, we can identify the parent process ID (ParentProcessId) for a given process. The parent process ID tells us the process that launched the process. Let's get the PID of the Chrome process first:
PS C:\Users\Sec504> Get-Process chrome |Select-Object -Property Name, Id Name Id ---- -- chrome 1520 chrome 1708 chrome 2660 chrome 4844 chrome 6368 chrome 7496 chrome 7528
Here we see Get-Process has identified 7 Chrome processes with different process ID values. Unfortunately, Get-Process can't identify the parent process ID, so we turn to Get-CimInstance for that:
PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Where-Object -Property Name -EQ chrome.exe | Select-Object Name, ProcessId, ParentProcessId Name ProcessId ParentProcessId ---- --------- --------------- chrome.exe 6368 4808 chrome.exe 2660 6368 chrome.exe 1708 6368 chrome.exe 7496 6368 chrome.exe 4844 6368 chrome.exe 7528 6368 chrome.exe 1520 6368
In this output we see that the first Chrome process has a process ID of 6368; all other Chrome processes are children of this first Chrome process since they all have process ID 6368 as their parent process ID.
NOTE: One the things that is confusing to PowerShell beginners (and annoying to everyone else) is the inconsistency in how properties are named. Get-Process uses ID for the process ID property; Get-CimInstance uses ProcessId for the same property. Get-Process does not include the .exe extension in the process name; Get-CimInstance does include the .exe extension in the same property. These are little things that trip people up on a regular basis. It's better to accept that PowerShell is confusing and inconsistent, instead of expecting it to be clear and consistent.
That's great, but how do we identify the parent of process ID 6368? Just a slight modification to the Get-CimInstance command gives us the answer:
PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Where-Object -Property ProcessId -EQ 4808 | Select-Object Name, ProcessId, ParentProcessId Name ProcessId ParentProcessId ---- --------- --------------- explorer.exe 4808 4760
After identifying process ID 4808 as the parent for the first Chrome process, we can identify the process name by changing the Where-Object clause to filter on the ProcessId field, revealing the parent process as explorer.exe. We can repeat this process for Explorer as well:
PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Where-Object -Property ProcessId -EQ 4760 | Select-Object Name, ProcessId, ParentProcessId PS C:\Users\Sec504>
Notably here, Explorer has no parent. It's a special process launched by Userinit when a user logs in, and then Userinit exits. This makes Explorer an orphan process, revealing no parent information when we investigate the parent process ID.
Summary
We looked at the Get-Process command as a tool to retrieve information about running processes. Get-Process accepts a process name or wildcard, allowing us to quickly filter the results. This is convenient, but Get-Process is also limited: it doesn't allow us to inspect several properties that are useful for incident response analysis.
We also looked at Get-CimInstance using the Win32_Process class. Get-CimInstance can reveal valuable process properties for incident response analysts: path, command line, parent process ID, and more. Filtering is a little more complicated with Get-CimInstance, but the Where-Object command helps out here.
Finally we looked at a practical investigation opportunity using Get-CimInstance, investigating the relationship between process ID and parent process ID. We'll continue to leverage this in the next article, where we use these process threat hunting skills on a compromised system to hunt our malware.
-Joshua Wright
Return to Getting Started With PowerShell
Joshua Wright is the author of SANS SEC504: Hacker Tools, Techniques, and Incident Handling, a faculty fellow for the SANS Institute, and a senior technical director at Counter Hack.