Created
January 22, 2019 18:18
-
-
Save mattifestation/3c2e8f80ca1fe1a7e276ee2607da8d18 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Get-ProcessStartKey { | |
<# | |
.SYNOPSIS | |
Derives the process start key for one or more processes. | |
.DESCRIPTION | |
Get-ProcessStartKey derives the process start key for one or more processes. Process start keys were introduced in Win 10 1507 and are intended to serve as a locally unique identifier for a process. A process ID cannot be considered a unique identifier since process IDs are repeatable. | |
Author: Matthew Graeber (@mattifestation) | |
.PARAMETER Id | |
Specifies one or more processes by process ID (PID). To specify multiple IDs, use commas to separate the IDs. | |
.EXAMPLE | |
Get-ProcessStartKey -Id $PID | |
Retrieves the process start key for the current PowerShell process. | |
.EXAMPLE | |
Get-Process notepad | Get-ProcessStartKey | |
Retrieves the process start keys for notepad.exe processes. | |
.INPUTS | |
System.Diagnostics.Process | |
Accepts one or more Process objects over the pipeline. | |
#> | |
param ( | |
[Parameter(ValueFromPipelineByPropertyName)] | |
[Int32[]] | |
[Alias('PID')] | |
$Id | |
) | |
BEGIN { | |
$WindowsReleaseId = [UInt32] (Get-ItemPropertyValue -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name 'ReleaseId') | |
# Process start key is supported in versions >= 1507 according to this doc: | |
# https://docs.microsoft.com/en-us/windows/desktop/ETW/enable-trace-parameters | |
if ($WindowsReleaseId -ge 1507) { | |
$ProcessSequenceNumber = 92 | |
$UInt64Length = 8 | |
if (-not ('Win32.NativeMethods' -as [Type])) { | |
$Sig = @' | |
[DllImport("ntdll.dll")] | |
public static extern int NtQueryInformationProcess(IntPtr processHandle, int ProcessInformationClass, out ulong ProcessSequenceNumber, uint ProcessInformationLength, out uint ReturnLength); | |
'@ | |
Add-Type -MemberDefinition $Sig -Name 'NativeMethods' -Namespace 'Win32' -ErrorAction Stop | |
} | |
# Get _KUSER_SHARED_DATA.BootId (offset 0x02C4 for both x86 and x64) | |
[UInt64] $BootId = [System.Runtime.InteropServices.Marshal]::ReadInt32(0x7ffe02C4) | |
} else { | |
Write-Error 'Process start key is not supported in this version of Windows. Win 10 1507+ is required.' | |
} | |
} | |
PROCESS { | |
foreach ($ProcessID in $ID) { | |
$ProcessInfo = Get-Process -Id $ProcessID | |
if ($ProcessInfo.Handle) { | |
[UInt64] $SequenceNumber = 0 | |
[UInt32] $ReturnLength = 0 | |
# This won't work in 32-bit PowerShell on a 64-bit OS. | |
$Result = [Win32.NativeMethods]::NtQueryInformationProcess($ProcessInfo.Handle, $ProcessSequenceNumber, [Ref] $SequenceNumber, $UInt64Length, [Ref] $ReturnLength) | |
if (($Result -eq 0) -and ($ReturnLength -eq $UInt64Length)) { | |
[PSCustomObject] @{ | |
PID = $ProcessID | |
ProcessStartKey = (($BootId -shl 0x30) -bor $SequenceNumber) | |
BootID = $BootId | |
ProcessSequenceNumber = $SequenceNumber | |
} | |
} else { | |
$NTSTATUSMessage = Get-NTStatusException -ErrorCode $Result | |
Write-Error "Unable to obtain process sequence number for process ID $ProcessID. NtQueryInformationProcess result: $NTSTATUSMessage (0x$($Result.ToString('X8')))" | |
} | |
} else { | |
Write-Error "Unable to obtain process handle for process ID $ProcessID." | |
} | |
} | |
} | |
} | |
function Get-NTStatusException | |
{ | |
<# | |
.SYNOPSIS | |
Resolves an NTSTATUS error code. | |
.DESCRIPTION | |
Get-NTStatusException returns a friendly error message based on the NTSTATUS code passed in. This function is useful when interacting with Windows Native API functions with NTSTATUS return codes. | |
Author: Matthew Graeber (@mattifestation) | |
.PARAMETER ErrorCode | |
An NTSTATUS code returned by a native API function (Nt or Rtl prefixed functions) | |
.EXAMPLE | |
C:\PS> Get-NTStatusException -ErrorCode 0xC0000005 | |
Invalid access to memory location. | |
.EXAMPLE | |
C:\PS> 0xC0000005, 0xC0000017, 0x00000000 | Get-NTStatusException | |
Invalid access to memory location. | |
Not enough storage is available to process this command. | |
The operation completed successfully. | |
#> | |
[CmdletBinding()] Param ( | |
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)] | |
[Int32[]] | |
$ErrorCode | |
) | |
BEGIN | |
{ | |
Set-StrictMode -Version 2 | |
$Win32Native = [AppDomain]::CurrentDomain.GetAssemblies() | %{ $_.GetTypes() } | ? { $_.FullName -eq 'Microsoft.Win32.Win32Native' } | |
if ($Win32Native -eq $null) | |
{ | |
throw "Unable to get a reference to type: Microsoft.Win32.Win32Native" | |
} | |
$LsaNtStatusToWinError = $Win32Native.GetMethod('LsaNtStatusToWinError', [Reflection.BindingFlags] 'NonPublic, Static') | |
$GetMessage = $Win32Native.GetMethod('GetMessage', [Reflection.BindingFlags] 'NonPublic, Static') | |
} | |
PROCESS | |
{ | |
foreach ($Error in $ErrorCode) | |
{ | |
$WinErrorCode = $LsaNtStatusToWinError.Invoke($null, @($ErrorCode)) | |
$GetMessage.Invoke($null, @($WinErrorCode)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment