question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature Request: add parameter -CurrentUser for Get-Process

See original GitHub issue

Hi

Sometimes I need to check if a process is running for the current user (non-admin on terminal server). Now I use get-process -Name "ProcessName" | ? { $_.SI -eq $([System.Diagnostics.Process]::GetCurrentProcess().SessionId) }. But it would be much easier if I could use get-process -Name "ProcessName" -CurrentUser.

Kind regards Frederik.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:2
  • Comments:9 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
jborean93commented, Mar 25, 2021

What you propose sounds like sane behaviour to me.

1reaction
jborean93commented, Mar 25, 2021

requires elevation on Windows.

That’s just what Get-Process states but it’s not the whole truth. Yes you need elevation to get the username of processes run by other users (or elevated ones of the current user) but it is still possible to get the token information for the majority of the processes run by the current user.

Add-Type -TypeDefinition @'
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

namespace Win32
{
    public class NativeHelpers
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct TOKEN_USER
        {
            public SID_AND_ATTRIBUTES User;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SID_AND_ATTRIBUTES
        {
            public IntPtr Sid;
            public int Attributes;
        }
    }
    
    public class NativeMethods
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool CloseHandle(
            IntPtr hObject);

        [DllImport("Advapi32.dll", SetLastError = true)]
        public static extern bool GetTokenInformation(
            SafeHandle TokenHandle,
            int TokenInformationClass,
            IntPtr TokenInformation,
            int TokenInformationLength,
            out int ReturnLength);

        [DllImport("Advapi32.dll", SetLastError = true)]
        public static extern bool OpenProcessToken(
            SafeHandle ProcessHandle,
            int DesiredAccess,
            out IntPtr TokenHandle);
    }

    public class SafeNativeHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public SafeNativeHandle() : base(true) { }
        public SafeNativeHandle(IntPtr handle) : base(true) { this.handle = handle; }

        protected override bool ReleaseHandle()
        {
            return NativeMethods.CloseHandle(handle);
        }
    }
}
'@

$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent().User

Get-Process | Where-Object {
    if (-not $_.SafeHandle -or $_.SafeHandle.IsInvalid -or $_.SafeHandle.IsClosed) {
        return
    }

    $rawAccessToken = [IntPtr]::Zero
    $res = [Win32.NativeMethods]::OpenProcessToken(
        $_.SafeHandle,
        0x00000008,  # TOKEN_QUERY
        [ref]$rawAccessToken
    ); $err = [Runtime.InteropServices.Marshal]::GetLastWin32Error()

    if (-not $res) {
        $exp = [ComponentModel.Win32Exception]$err
        Write-Warning -Message "Failed to open access token for $($_.Id): $($exp.Message)"
        return
    }

    $accessToken = [Win32.SafeNativeHandle]::new($rawAccessToken)
    try {
        $userLength = 0
        $tokenUserId = 1  # TOKEN_USER

        $null = [Win32.NativeMethods]::GetTokenInformation(
            $accessToken,
            $tokenUserId,
            [IntPtr]::Zero,
            0,
            [ref]$userLength
        )
        $tokenInfo = [Runtime.InteropServices.Marshal]::AllocHGlobal($userLength)
        try {
            $res = [Win32.NativeMethods]::GetTokenInformation(
                $accessToken,
                $tokenUserId,
                $tokenInfo,
                $userLength,
                [ref]$userLength
            ); $err = [Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if (-not $res) {
                $exp = [ComponentModel.Win32Exception]$err
                Write-Warning -Message "Failed to get TOKEN_USER for $($_.Id): $($exp.Message)"
                return
            }

            $tokenUser = [Runtime.InteropServices.Marshal]::PtrToStructure(
                $tokenInfo, ([type][Win32.NativeHelpers+TOKEN_USER]))

            $processUser = [Security.Principal.SecurityIdentifier]::new($tokenUser.User.Sid)

            $processUser.Equals($currentUser)
        }
        finally {
            [Runtime.InteropServices.Marshal]::FreeHGlobal($tokenInfo)
        }
    }
    finally {
        $accessToken.Dispose()
    }
}

The DACL of a standard/limited access token gives query/read access to the following accounts

image

and the following for an admin one

image

There are other complications when it comes to getting a copy of the process token (not the same as the access token) which stops you from getting the access token of elevated processes.

it includes additional processes that are not owned by the current user.

In addition to this, filtering by the session id means you will miss out on processes for that user that are run in a separate session (task scheduler, network logon/winrm, services).

Read more comments on GitHub >

github_iconTop Results From Across the Web

List process for current user - powershell
But as a non-administrator I can't use -IncludeUserName becuase "The 'IncludeUserName' parameter requires elevated user rights". So if I'm ...
Read more >
about Functions - PowerShell
Function parameters can be read from the command line or from the pipeline. Functions can return values that can be displayed, assigned to ......
Read more >
Start-Job - Module: Microsoft.PowerShell.Core
Specifies the definition name of the job that this cmdlet starts. Use this parameter to start custom job types that have a definition...
Read more >
Designing Professional Parameters - powershell.one
Parameters are a way for the caller to submit information to code. They are a fundamental feature of PowerShell scriptblocks: a param() ...
Read more >
Get process request
Table 1. URL parameters. Parameter, Type, Required, Description. requestId, string, true, Id of process request. Table 2. Header parameters ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found