PowerShell Remoting Project Home

Tuesday, February 14, 2006

Peek into process token: Add-ProcessOwner cmdlet

Take a look at process tab in windws task manager. You will find all process followed by a user name who start the process. Although Monad’s buildin cmdlet get-process also return information of processes running on current windows box, due to the limitation of System.Diagnostics.Process object, results does not contain process owner information.

In order to get process token, I have to call Win32API – OpenProcessToken, which return a System.IntPtr type token. This token can be used to construct a System.Security. Principal.WindowsIdentity object. (It is first time I use P/Invoke. Please tell me if I did anything wrong.)

Using add-member cmdlet, I add this WindowsIdentity object to System.Diagnostics.Process object as NoteProperty. To use it:
>$ps= Get-process –ProcessName exporler | Add-ProcessOwner.msh
>$ps | get-member

WaitForExit       Method        System.Boolean WaitForExit(Int...
WaitForInputIdle      Method       System.Boolean WaitForInputIdl...
__NounName         NoteProperty   System.String __NounName=Process
ProcessOwner      NoteProperty   System.Security.Principal.Wind...

>$ps.ProcessOwner

AuthenticationType : NTLM
ImpersonationLevel : None
IsAuthenticated    :
IsGuest            : False
IsSystem           : False
IsAnonymous        : False
Name               : Domain\tony
Owner              : S-1-5-21-854245398-XXXXXXXXXX-XXXXXX-XXX
User               : S-1-5-21-854245398--XXXXXXXXXX-XXXXXX-XXX
Groups             : {S-1-5-21-854245398--XXXXXXXXXX, , , , , }
Token              : XXXX

It is pretty sad that due to local security policy, I can not get owner of processes runing as System or Network service (which are those most intersting process). I might have to mount myself as a system service first than call OpenProcessToken. Well, that is to complicated for me.

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Collections;
using System.Diagnostics;

namespace Monad.Security
{
    public class OpenProcessTokenHelper
    {
        [Flags]
        enum TOKEN_ACCESS : uint
        {
            TOKEN_ASSIGN_PRIMARY = 0x0001,
            TOKEN_DUPLICATE = 0x0002,
            TOKEN_IMPERSONATE = 0x0004,
            TOKEN_QUERY = 0x0008,
            TOKEN_QUERY_SOURCE = 0x0010,
            TOKEN_ADJUST_PRIVILEGES = 0x0020,
            TOKEN_ADJUST_GROUPS = 0x0040,
            TOKEN_ADJUST_DEFAULT = 0x0080,
            TOKEN_ADJUST_SESSIONID = 0x0100,
            TOKEN_READ = 0x00020000 | TOKEN_QUERY,
            TOKEN_WRITE = 0x00020000 | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT,
            TOKEN_EXECUTE = 0x00020000,
        };
        
        [DllImport("Advapi32.dll", SetLastError = true)]
        extern static int OpenProcessToken(IntPtr processHandle, TOKEN_ACCESS desiredAccess, out IntPtr tokenHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        extern static bool CloseHandle(IntPtr handle);

        public static WindowsIdentity GetProcessesIdentities(Process process)
        {
            try
            {
                IntPtr token = IntPtr.Zero;
                if (OpenProcessToken(process.Handle, TOKEN_ACCESS.TOKEN_QUERY, out token) == 0)
                {
                    throw new ApplicationException("Can't open process token for: " + process.ProcessName);
                }
                WindowsIdentity id = new WindowsIdentity(token);
                CloseHandle(token);
                return (id);
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Open process token error", ex);
            }

        }
    }
}

#################################################
## Add-ProcessOwner.msh
## Add System.Security.Principal.WindowsIdentity object
## to System.Diagnostics.Process object as NoteProperty
## using P/invoke OpenProcessToken Win32API
##
## from http://mshforfun.blogspot.com/
################################################
begin
{
    [void][Reflection.Assembly]::LoadFile("D:\msh\OpenProcessTokenHelper.dll")
}
process
{
    if ($_)
    {
       # Only working with process object
       if ($_ -isnot [System.Diagnostics.Process]) {continue}  
       else
       {
           # Get WindowsIdentityObject
        $id=[Monad.Security.OpenProcessTokenHelper]::GetProcessesIdentities($_)
           add-member -InputObject $_ -Type NoteProperty -Name "ProcessOwner" -Value $id -ErrorAction "SilentlyContinue"
       }
     }
}

Reference: http://www.sellsbrothers.com/askthewonk/secure/default.aspx?content=howcanigetthesecurityprin.htm

Tags:    


Comments:
That's cool, Tony. You're 98% of the way towards making this a C# cmdlet -- why did you decide to make this a script that calls a DLL instead?
 
To lee,

Well, I was in a hurry last night. I will try to make it a cmdlet (dll) some time later.

Thnaks for your suggestion.
 

Post a Comment





<< Home