Showing posts with label process. Show all posts
Showing posts with label process. Show all posts

Wednesday, June 11, 2008

CTP2 - Get Process/Service from remote computers

 

One major change to the Get-Process/Get-Service cmdlets in CTP2 is the ability to get objects from remote computers via a new parameter, computerName (or -c for short). You can map process/service objects to their computer by the machineName member.

 

PS > Get-Service s* -c computer1,computer2 | select name,machineName

Name                  MachineName
----                  -----------
sacsvr                computer2
SamSs                 computer1
SCardSvr              computer1
Schedule              computer1
seclogon              computer1
SENS                  computer1
ServeRAIDManagerAgent computer2
(...)

PS > Get-Process s* -c computer1,computer2 | select name,machineName

Name          MachineName
----          -----------
searchindexer computer1
services      computer2
services      computer1
slp_srvreg    computer2
smax4pnp      computer1
smss          computer2
smss          computer1
snmp          computer2
spoolsv       computer2
spoolsv       computer1
(...)

Tuesday, January 1, 2008

Creating and managing processes in PowerShell

 

process

PowerShell's get-process cmdlet gets the processes that are running on the local computer, and is restricted to the local computer only, you can't interact with processes that runs on remote computers nor create ones locally or remotely.


You can use WMI (see below), but I want to show another way of doing it, less documented one - static methods.

Static methods give you the option to break loose of the cmdlet 'wrapper' and work directly on the underlying NET class. To find which static methods you can use on processes we'll use PowerShell Get-Process cmdlet, we'll pipe it to Get-Member to reveal the underlying NET type that get-process is using: 

 

PS:55 >Get-Process | Get-Member

   TypeName: System.Diagnostics.Process

Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
Name                       AliasProperty  Name = ProcessName
(...)

 

 

Managing processes with static methods

As you can see, below the surface, Get-Process is using the [System.Diagnostics.Process] NET type to retrieve a process object for each process. From now on, you can work with the static methods of this type to interact with process objects. To find the static methods (surround the type name with square brackets), type:

PS > [System.Diagnostics.Process] | Get-Member -Static -MemberType method



TypeName: System.Diagnostics.Process
Name               MemberType Definition
----               ---------- ----------(...)
EnterDebugMode     Method     static System.Void EnterDebugMode()...
Equals             Method     static System.Boolean Equals(Object...
GetCurrentProcess  Method     static System.Diagnostics.Process G...
GetProcessById     Method     static System.Diagnostics.Process G...
GetProcesses       Method     static System.Diagnostics.Process[]...
GetProcessesByName Method     static System.Diagnostics.Process[]...
LeaveDebugMode     Method     static System.Void LeaveDebugMode()...
ReferenceEquals    Method     static System.Boolean ReferenceEqua...
Start              Method     static System.Diagnostics.Process S...
(...)

 

The Definition column shows all the available overloads for each method. The notation for calling a static method is:
PS > [System.Diagnostics.Process]::StaticMethodName(<args>)

Lets find the overloads for the GetProcesses method, (each overload is delimited with a comma):

PS > ([System.Diagnostics.Process] | gm -s GetProcesses).Definition

static System.Diagnostics.Process[] GetProcesses(),
static System.Diagnostics.Process[] GetProcesses(String machineName)

The output shows two ways to call the method. You can call it with no arguments and get all running processes on the local computer or use the later to get processes from a remote computer. We are interested in the second one since it accept a string argument for a computer name. To save typing space we'll assign the type name to a variable:

PS > $process = [System.Diagnostics.Process]


# examples
$server="server"
$process = [System.Diagnostics.Process]

# get all running processes on the remote server
PS > $process::GetProcesses($server)

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    165       7    15364      21684    56            2992 vmserverdWin32
    196       6     2764       5132    54            3740 LVComSX
     73       4     1100       3392    30            2452 ctfmon
(...)


As you can see the output is the same output produced by the get-process. To find which methods you can invoke on a process, pipe it to get-member:

PS > $process::GetProcessById(2992,$server) | gm -member method



   TypeName: System.Diagnostics.Process

Name                      MemberType Definition
----                      ---------- ----------
(...)
Close                     Method     System.Void Close()
CloseMainWindow           Method     System.Boolean CloseMainWindow()
CreateObjRef              Method     System.Runtime.Remoting.ObjRef Cr
Dispose                   Method     System.Void Dispose()
Kill                      Method     System.Void Kill()
Refresh                   Method     System.Void Refresh()
Start                     Method     System.Boolean Start()
ToString                  Method     System.String ToString()
WaitForExit               Method     System.Boolean WaitForExit(...
(...)

 

This are the same methods you'll get if you run get-process | gm -member method. To call a static method simply append it to the command:

PS > $process::GetProcessById(2992,$server).Kill()

In the same manner you can get a list all properties of a process.

PS > $process::GetProcessById(2992,$server) | gm -member property
 

# get remote process by its process id
PS > $process::GetProcessById(2992,$server)

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    165       7    15364      21684    56            2992 vmserverdWin32


# get remote process by its name
PS > $process::GetProcessesByName("vmserverdWin32",$server)

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    165       7    15364      21684    56            2992 vmserverdWin32


# get PowerShell's process, locally
PS > [System.Diagnostics.Process]::GetCurrentProcess()

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    512       9    39340      33148   163     4.06  11952 powershell

 


Creating local processes

To create processes, locally, you need to use the static Start() method. To find what overloads this method takes:

PS > $process = [System.Diagnostics.Process]
PS > (($process | gm -static start).definition).replace("static","`nstatic")
static System.Diagnostics.Process Start(String fileName, String userName, SecureString password, String domain),


static
System.Diagnostics.Process Start(String fileName, String arguments, String userName, SecureString password, String domain),



static
System.Diagnostics.Process Start(String fileName)

static System.Diagnostics.Process Start(String fileName, String arguments),

static System.Diagnostics.Process Start(ProcessStartInfo startInfo)

 

There are five different ways to call it, each overload accepts different set of arguments:

1. Run a file (executable) with alternate credentials and a secure password.
2. Adds an option to add arguments for the file name.
3. Gets only file name.
4. File name and arguments, no alternate credentials.
5. Gets a ProcessStartInfo object.

 

# Use the third overload to run notepad

PS > [System.Diagnostics.Process]::Start("notepad")

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
      0       1      136         84     1     0.00  13720 notepad
 
# Use the fourth to run notepad with arguments, in this case make notepad load the system.ini file.
PS > [System.Diagnostics.Process]::Start("notepad","$env:windir\system.ini")

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
      5       1      340        840     8     0.03  23728 notepad
 
sysini
 

BTW, if you want to avoid the default output, assign the command to a variable or cast it to [void], as in:
 
PS > [void][System.Diagnostics.Process]::Start("notepad","$env:windir\system.ini")
PS >
 


The fifth option is the one I want to use since it is using another type to specify what starting info to assign to the new process, including alternate credentials (though in plain text and not secure as the first and second overloads which uses the SecureString parameter). Here's a partial property list, you can find the full list here:

- Arguments
- FileName
- Password
- UserName
- WorkingDirectory


 

Using the New-Object cmdlet

In this example I'll use the New-Object cmdlet to instantiate a new System.Diagnostics.ProcessStartInfo object to specify a set of values to be used when the process start:
 

# Specifies a set of values that are used when you start a process.
$si = new-object System.Diagnostics.ProcessStartInfo
$si.fileName = "notepad.exe"
$si.Arguments= "$env:windir\system.ini"
$si.WorkingDirectory = $pwd
# this will launch the process in the background (hidden)
# $si.windowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
# run process under alternate credentials
# $si.UserName="username"
# $si.Password="Password"

# start the process
$process = New-Object System.Diagnostics.Process
$process.startInfo=$si

# this is also valid
# $process = [System.Diagnostics.Process]::Start($si)

# close notepad and watch the output
if($process.start()) { $process.waitForExit(); "existed"} 

 

Note: The WaitForExit() method is available only for processes that are running on the local computer. If you need to track a process on a remote system construct a loop to check if the remote process exists.
Want to know how to list all name/value pairs of the [System.Diagnostics.ProcessWindowStyle] type or any other [enum] type, see my post on Listing enumeration type values.

 

 

Creating remote processes

So far so good. But how about creating new processes on remote computers? Well, that's another story (Class). None of the above, Get-Process nor its underlying type, let you do that. The only way to do it is by using WMI.

You can access WMI with the Get-WmiObject cmdlet or with PowerShell's Type accelerators (shortcuts). Type accelerators were developed by the Windows PowerShell team to make WMI scripts more easy to write for system administrators:

[WMI] - type accelerator for ManagementObject.
[WMICLASS] - type accelerator for ManagementClass.
[WMISEARCHER] - type accelerator for a ManagementObjectSearcher.

See the second resource link below for more information on this. 

 

Here's a one-liner to create new process(es) on local/remote computers:

PS > ([WMICLASS]"\\$computer\ROOT\CIMV2:win32_process").Create("notepad")


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 2
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ProcessId        : 7380
ReturnValue      : 0

If the ReturnValue property is set to 0 then you've successfully created the process on the remote computer, the ProcessId property shows the process id for the new process on the remote computer.

Note: For security reasons the WMI Win32_Process.Create method cannot be used to start an interactive process remotely (it runs hidden, in the background).

 

Summary

The above technique on static methods, opens a whole new world of discovering PowerShell and NET classes. You can apply it to any cmdlet and work your way up or down, depending on your point of view :-). Soon enough you'll find your way to MSDN, where you can get more information and code examples on each class.

This post only scratches the surface and there's a lot more to write on processes... but now comes your part. Check out what Get-Service has to offer and what can you get by applying all of this to it.

Last but not least, I'd like to thank Hal Rottenberg from TechProsaic and the PowerScripting Podcast for his help with editing and prompting me to write this article.


Happy New Year!

 

Additional Resources:

Managing Processes in PowerShell
Improved Support for WMI
Getting Services associated with a Process via PowerShell
Process Class
ProcessStartInfo Class
Create Method of the Win32_Process Class
Casts and Get-WmiObject

Sunday, January 21, 2007

Real time command line spy

Generally, every script you write involves running some Windows built-in tools. Applications like regedit.exe or cmd.exe are the most common. For this tools to operate within a script, a command line argument must be supplied to tell the application what "task" to perform otherwise it will just run with the default settings.

As I see it, command line arguments and switches are both fascinating and powerful. There are even undocumented switches, which makes them even more mysterious. For example, running control panel applets can be done with tools like control.exe or rundll32.exe, both with some arguments.

CONTROL.EXE <cplfilename.cpl> RUNDLL32.EXE <dllname>,<entrypoint> <optional arguments>

To run the Internet Properties dialog, type:

control.exe inetcpl.cpl or rundll32.exe shell32.dll,Control_RunDLL INETCPL.CPL

You can even open the applets with another tab in front (using the tab Index number). To open the Internet Properties dialog with the Privacy tab in front, type:

control.exe inetcpl.cpl,,2 rundll32.exe shell32.dll,Control_RunDLL INETCPL.CPL,,2

Traditionally, you will use the /? switch to reveal your application's help, but, if you run rundll32.exe /? you'll end up with nothing, searching for help on these kind of files can be a quiet a challenge, and as you can see with the rundll sample this is not ordinary stuff like cmd.

For quite some time I was puzzled by this. How can I find the rundll's or any other file command line switches and arguments? Can I find more than "exist" in the default documentation or help parameter?.

I started to Google, with no luck. Including the words "command line" in Google's search was a pain. I had to filter out dozens of links, ohhh... until I came across Bryan Keadle's Cool Tools page.

One of Bryan tools called "Intercept" was made exactly for this task. Intercept will replace itself with the selected program, show the parameters being passed, then continue on to the original executable. Fantastic, I downloaded the file, tested it and it worked just fine.

While "Intercept" did a great job on a small scale I was looking for a better solution. Than I came across SysInternals "Process Explorer" (now hosted on TechNet). If you used Sysinternals tools before, than names like regmon, filemon, tcpview and others are no stranger to you (if you didn't use there tools shame on ya :-), you can download the entire Sysinternals Suite (8 MB) from here).

Double-clicking any process in Process Explorer launched the process property dialog. This dialog lists a plethora of information on the process including the command line that started it.

 

Now, all I had to do is click the processes and investigating their command line, and testing them offline. As for the above rundll example, go to the control panel and run any applet you like, once you launched it look for rundll process in Process Explorer, double-click it and view its command line, then you can include it in your scripts.

As for processes that took very small amount of time to execute and appear in the display, I used the "spacebar" key to "freeze" the display from refreshing and then double-clicked the process. This method became very wearying while waiting for processes to execute, much like a mouse and cat chase. The last step in this quest was the release of SysInternals Process Monitor (By Mark Russinovich and Bryce Cogswell).

Process Monitor includes regmon, filemon and Process Explorer in one tool. It captures all the activity of registry, file system, and processes/threads in real time, and logs the results to a file on the local disk. Boom, this was what I looked for. Here is my way of real time spying/monitoring command lines:

1. Run procmon.exe
2. Toggle off registry activity
3. Right Click any Column header and choose "Select Columns..." (or Click Options > Select Columns) and uncheck everything except "Process Name" and "Command Line".

 

4. Viola! Procmon.exe scrolls the results and from now on, no command line switch is immune.

 

In addition filters can be applied to adjust the displayed processes. If you wish to exclude a Process, just right click it and choose "Exclude > Process Name".

Finally, you can save the results to a CSV file for later use. One can even make a Command line Database :-)

 

I strongly recommend viewing Mark's session on "Advanced Windows Troubleshooting with Process Monitor"

 

Enjoy $hay