PowerShell Remoting Project Home

Friday, March 31, 2006

PowerShell Remoting - Beta and future

Release of "PowerShell Remoting", Beta version.

  1. New GUI client manager: Save/delete server information & connection option, generate connection script automatically, launch powershell remoting client directly.
  2. Add option to reject connection if user found invalid remote X509 certificate.
  3. Bug fix: Null login credential (press Cancel in CredUI window) cause server crash
  4. Bug fix: other minor bugs

  1. Write Server Information & Error to EventLog (Source: PowerShellRemoting). Log file will no longer be used by newer version.

  2. SSL support using SSLStream.

  3. Warning: SSL is for advanced user.
    If you want to provide SSL support to server, you should

    • Get X509 certificate for server. For testing purpose, you can use makecert.exe (included in .NET framework SDK)
    • Install X509 certificate at StoreLocation: LocalMachine, StoreName: My (personal).
    • Grand "NetworkService" account read access to X509 certificate associated private key file.
    • Set [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PowerShellRemoting\Parameters\ X509] to thumbprint of X509 Certificate.

      There is pretty good walkthrough at John Howard's blog.

  4. X509 Option

  5. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PowerShellRemoting\Parameters]

  6. DebugHost Option (Default 0: false)

  7. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PowerShellRemoting\Parameters]

  8. Bug fix: Write-Progress throw null object Exception.
Check out the help.chm in release package for details.

1. Support Ctrl+C and Ctrl+Break to cancel current pipeline (UDP datagram, So not 100% reliable)
2. Support Maximum Client per IP option (default 2)
3. Customized client filter policy (%Program Files%\PowerShell Remoting\user.xml, see sample files for format information)
Important: default installation only allow user in Administrators group login from localhost. So you have to have your own user.xml before you can login from another IP. If user.xml file parse error occorred when start service, default policy will be loaded. Check log file or EventLog for details of parse error.
4. $UserACL variable for current client filter policy
5. Client is installed as PSSnapin
6. New install/uninstall script for both server and client, new start-remotehost.ps1 script
7. CanThrowException and CanHandleCancelKey property of client (For developer)
8. Change Log file path to %Documents and Settings%\NetworkService\Local Settings\Application Data\PowerShellRemoting.log
9. Change Service Name to "PowerShellRemoting"
10. Service related Exception is also Logged to Eventlog (EventLog: Application, source: PowershellRemoting)
11. Other Bugs fixed

1. Recompiled for new Windows PowerShell RC1 (Refresh version)
2. Using "Thread Pooling" method to schedule multiple Host threads. So Sever can potentially accept more connections. (Old version uses one thread per connection method).
3. Using a separate thread actively reclaim resources from broken connection and dead host.
4. Clean exit when stop service: Disconnect all clients and dispose all running hosts.
5. Gracefully disconnect client when server reach maximum client capacity.
6. Fix: Nested Prompt stack error when multiple clients connected.
7. Fix: Server unable to exit when connection closed unexpectedly by client.
8. Fix: "SetShouldExit" method re-throw "SocketException" Error

1. Fix "All User" profile Path
1) "\Documents and settings\All users\Documents\PsConfiguration\profile.ps1"
2) "\Documents and settings\All users\Documents\PsConfiguration\PowerShellRemoting_profile.ps1"

1. NestedPrompt (suspend host)
2. Multiple line input mode
3. Use local UI to get login credential
4. Save RawUI state on start, and Reset RawUI state on exit.
5. $CurrentUser Variable (WindowsIdentity Object represent current
login user)
6. Load user profile in following order
1) "\Documents and settings\All users\PsConfiguration\profile.ps1"
2) "\Documents and settings\All users\PsConfiguration\PowerShellRemoting_profile.ps1"
3) "My Documents\PsConfiguration\profile.ps1"
4) "My Documents\PsConfiguration\PowerShellRemoting_profile.ps1"

1. Solve Server cause CPU overload problem
2. Solve Port or MaxClient change not take effect problem
3. Except for PSHostUserInterface.Prompt, all other UI/RawUI
API were connect to local UI/RawUI API
4. improved Client exception report

1. Recompile for new Windows PowerShell RC1
2. Client class libary to build your own client
3. Console client use naive PowerShell.exe user interface
4. Support for all RawUI API
5. Bug fix for server

1. Multiple connection (Maximum 10 connection)
2. New log file format
3. Hide some common exception on Client
4. Some bug fix

This software is distributed under BSD license. See license.txt for details.

I. What is "PowerShell Remoting"

PowerShell Remoting is a light-weighted server-client application which
provides secure remote access to Windows PowerShelll (codename Monad).
It contains two components:

Server: A Windows Service (Service Name "PowerShell Remoting") running
under NetworkService account. It will listen at certain TCP port, accept
income connection, authenticate client and provide PowerShell hosting

Client: A Class libary. It severs as a proxy between sever user interface
and client user interface. It will connect to server, provide client
credential for authentication, get user input and display results.

This software is distributed under BSD license. See license.txt for

II. System requirement.
1. Microsoft .NET framework v2.0.50727 (Server & Client)
2. PowerShell RC1 or Higher (Server only)
3. Windows XP/2003 (For server); Windows 2000/XP/2003 (For client)
4. 256MB Memory, 1M hard drive space.

III. Features
1. Client authentication & protection
1) All communication between server and client is encrypted and signed.
Depend on server environment, authentication method could be NTLM or

2) Multipul connection from client. (default Port 8080, Maximum 10 connection)
Can be changed by modify registry.

2. Support PSHostUserInterface & PSHostRawUserInterface
1) PSHostUserInterface will be passed to constructor of client
2) PSHostRawUserInterface is not mandatory but is supported

Note : Not supported at this version
1) Tab completion
2) start-transcript

Note. Known problems
1) Outputs of legacy program are not redirected to client

IV Installation and usage
# Server
1. Please use provided install.ps1 (install service) and uninstall.ps1
(uninstall service) to install/uninstall server component.

2. Default installation, server will listen to all available IP
(IPAddress.Any) listen at port 8080 and maximum client number is 10.
Those can be changed by modify registry
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PowerShell Remoting

3. Log file was "Hard Coded" to
C:\Documents and Settings\NetworkService\PowerShellRemoting.log

4. Default installed as Manual start service.

# Client
1. Client application do not need to be installed, just copy it to client
PC and execute "start-remotehost.ps1". (Some modification is needed)

2. argument:
Where is IP address which server is listening
(For example: for localhost;, et, al.)
Where is port number which server is listening
(default 8080)

If you have any other suggestions, please drop a few lines in comment.

Have Fun


Monday, March 27, 2006

Monad Remoting - Windows Service

Just upload a new version of "Monad Remoting". Now it is a windows service with a bunch of new features and recommanded for upgrading.

New feature:
1. Running as windows service under NetworkService account (Service Name "Monad Remoting").

2. Receiving customized NetworkCredential for logon. (Yes, I mean really logon) But only accept one connection at a time.

3. Impersonation as remote NetworkCredential. You can either login as an unprivileged user or an Administrator. Try my faverate id.msh to find out your current identity.

4. Log file was "Hard Coded" to
    C:\Documents and Settings\NetworkService\MonadRemoting.log

5. Automatic install service (install.msh) and uninstall service (uninstall.msh). You 'd better install service using my install.msh because it creates required registry key.

6. Default installed as Manual start service. You can change it to Automatic in MMC service snapin. You have to start service before first connection.
Start-service "Monad Remoting"

7. Default listen at port 8080, can be changed by modify registry
    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Monad Remoting\Port]

8: Default listen to all available IP (IPAddress.Any)

Have Fun


Wednesday, March 22, 2006

Monad Remoting - Now Can Read SecureString From Remote Client

Implementation of ReadLineAsSecureString() in MshHostUserInterface.

Just upload a new version of "Monad Remoting". It can read SecureString from remote client console now.

SecureString will not be kept secret if you store its content in a normal string first. That is to say, we have to read it from console and store them directly as SecureString. But before sent them to remote server, we have to decrypt it to byte[ ]. NegotiateStream will encrypt (decrypted) byte[ ] before write to NetworkStream. At server side, we have to get the byte[ ] and restore it to a SecureString.

Although I tried to minimize exposure and clear my footprint behind me, there are still potential security problems. So use it at your own risk. I post the code here, in case someone want to take a close look at those steps I mentioned here.
                    keyinfo = Console.ReadKey(true);
                    if ((keyinfo.Modifiers & ConsoleModifiers.Alt) != 0 || (keyinfo.Modifiers & ConsoleModifiers.Control) != 0) continue;
                    if (keyinfo.Key == ConsoleKey.Enter)
                    if (password.Length == 512)
                        Console.Write("\r\nRead 512 (Maxium) Characters!");
                    if (keyinfo.Key == ConsoleKey.Backspace)
                        password.RemoveAt(password.Length - 1);
                        Console.Write(' ');
                while (keyinfo.Key != ConsoleKey.Enter);
                if (password.Length > 0)
                    Plantext = GetByteArrayFromSecurString(password);
                    authStream.Write(Plantext, 0, Plantext.Length);
                    Array.Clear(Plantext, 0, Plantext.Length);
At this point, you can now enjoy the get-credential, new-securestring cmdlets.

I really wish we could have a in-process su command, because the trick of
will not work for remote client. Well, we have to expect that at next version of monad.

Have Fun.


Script to Resize Remote Client Console Window

There is an intersting msh script from Web ChangeLog blog to demonstrate RawUI functionality of my Monad Remoting tools.

MSH: Remote scripting host

Interstingly, if you run this script in msh.exe, you will probobly get some exception. But it works fine for my Monad Remoting tools.

Exception setting "WindowSize": "Window cannot be wider than the screen buffer.
Parameter name: value.Width Actual value was 85."
At D:\msh\script\resizewin.msh:7 char:18

Actually, This is a bug of my tools. I simply applied all RawUI operation directly to client console and but did not check screen buffer.

Have Fun!


Monday, March 20, 2006

Monad Remoting (updated version)

Just upload a new version of Monad Remoting for testing. Download at gotdotnet.


# Automatic authentication
1) Client credential is automaticly provided to server for authentication.
2) All communication between server and client is entrypted and signed.

# Basic MshHostUserInterface.
1) Load User Profile at server when connected (new)
2) Evaluation for Prompt function (new)
3) Read and write from console
4) Colorized output
5) Prompt for undefined parameters (new)
6) Prompt for choice (new)

# Not supported at this version
1) Read Securestring
2) ReadKey

# known problems
1) Outputs of legacy program are not redirected to client

Have Fun.


Friday, March 17, 2006

Securely Extend Msh Host User Interface over Network

Added at 20th Mar, 22:20
There is a followup post here.

Securely extend Msh Host User Interface over network using NegotiateStream.

If you follow my previous post, you will find out that I changed my strategy. There is no need to sent Mshobjects to remote client. We just need to implement a customized MshHostUserInterface which get input and write result to remote client. Monad is designed to be extended in this way, so it only took me a couple of days to get a usable solution. You can download testing binary at gotdotnet.



The underline idea is to implement all APIs in MshHostUserInterface and MshHostRawUserInterface. It was much easier than I originally thought. But  I still had some problems with
public override Dictionary Prompt(string caption, string message, Collection descriptions)   { }

public override int PromptForChoice(string caption, string message, Collection choices, int defaultChoice) { }

public override MshCredential PromptForCredential(string caption, string message, string userName, string targetName)  { }

public override MshCredential PromptForCredential(string caption, string message, string userName, string targetName, MshCredentialTypes allowedCredentialTypes, MshCredentialUIOptions options)   { }

public override SecureString ReadLineAsSecureString()   { }
in MshHostUserInterface and
public override KeyInfo ReadKey(ReadKeyOptions options) { }

public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill)   { }

public override void SetBufferContents(Coordinates origin, BufferCell[,] contents)  { }

public override void SetBufferContents(Rectangle rectangle, BufferCell fill)   { }
in MshHostRawUserInterface.

That is to say: all sercurestring-related, credential-related, scroll console-related cmdlet are not working. But it is really fun to run some test on remote machine. Try out yourself by downloading the testing binary at gotdotnet.

Monad Rocks!

Have Fun!


Tuesday, March 14, 2006

Separation of Monad Engine and User Interface

Research on MshHost & MshHostUserInterface abstract class.

The msh.exe is actully a console program hosting monad engine. In another word: monad engine does not worry about user interface, it is host environment's job.  My Remote MSH server is not INTERACTIVE (For example: remote user will not be prompted to input credentials when invoking "get-credential" at server) because I use DefaultHost which does not Implement MshHostUserInterface

OK, OK, it is to fast, let's start from Create a Runspace:
public static Runspace CreateRunspace()
      Runspace runspace1;
      using (IDisposable disposable1 = RunspaceFactory._tracer.TraceMethod())
            runspace1 = RunspaceFactory.CreateRunspace(new DefaultHost(Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.CurrentUICulture));
      return runspace1;
So it acually create a Runspace using DefaultHost
Microsoft.Management.Automation.Internal.DefaultHost is a implement of MshHost abstract class.
internal class DefaultHost : MshHost
      // Methods
      static DefaultHost();
      internal DefaultHost(CultureInfo currentCulture, CultureInfo currentUICulture);
      public override void EnterNestedPrompt();
      public override void ExitNestedPrompt();
      public override void NotifyBeginApplication();
      public override void NotifyEndApplication();
      public override void SetShouldExit(int exitCode);

      // Properties
      public override CultureInfo CurrentCulture { get; }
      public override CultureInfo CurrentUICulture { get; }
      public override Guid InstanceId { get; }
      public override string Name { get; }
      public override MshHostUserInterface UI { get; }
      public override Version Version { get; }

      // Fields
      private CultureInfo currentCulture;
      private CultureInfo currentUICulture;
      private Guid id;
      [TraceSource("DefaultHost", "DefaultHost subclass of S.M.A.MshHost Tracer")]
      private static MshTraceSource tracer;
      private Version ver;
Let's take a look at System.Management.Automation.Host.MshHost
public abstract class MshHost
      // Methods
      protected MshHost();
      public abstract void EnterNestedPrompt();
      public abstract void ExitNestedPrompt();
      public abstract void NotifyBeginApplication();
      public abstract void NotifyEndApplication();
      public abstract void SetShouldExit(int exitCode);

      // Properties
      public abstract CultureInfo CurrentCulture { get; }
      public abstract CultureInfo CurrentUICulture { get; }
      public abstract Guid InstanceId { get; }
      public abstract string Name { get; }
      public virtual MshObject PrivateData { get; }
      public abstract MshHostUserInterface UI { get; }
      public abstract Version Version { get; }
MshHostUserInterface is the object which actully deals with user interface:
public abstract class MshHostUserInterface
      // Methods
      protected MshHostUserInterface();
      public abstract Dictionary<string, MshObject> Prompt(string caption, string message, Collection<FieldDescription> descriptions);
      public abstract int PromptForChoice(string caption, string message, Collection<ChoiceDescription> choices, int defaultChoice);
      public abstract MshCredential PromptForCredential(string caption, string message, string userName, string targetName);
      public abstract MshCredential PromptForCredential(string caption, string message, string userName, string targetName, MshCredentialTypes allowedCredentialTypes, MshCredentialUIOptions options);
      public abstract string ReadLine();
      public abstract SecureString ReadLineAsSecureString();
      public abstract void Write(string value);
      public abstract void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value);
      public abstract void WriteDebugLine(string message);
      public abstract void WriteErrorLine(string value);
      public virtual void WriteLine();
      public abstract void WriteLine(string value);
      public virtual void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value);
      public abstract void WriteProgress(long sourceId, ProgressRecord record);
      public abstract void WriteVerboseLine(string message);
      public abstract void WriteWarningLine(string message);

      // Properties
      public abstract MshHostRawUserInterface RawUI { get; }
But when we check the public property UI in DefaultHost:
public override MshHostUserInterface UI
            MshHostUserInterface interface1;
            using (IDisposable disposable1 = DefaultHost.tracer.TraceProperty())
                  interface1 = null;
            return interface1;
So, it is a null object.

If we want a REAL interactive remote shell, we have to implement our own MshHost and MshHostUserInterface (and maybe MshHostRawUserInterface). MshHostUserInterface will be responsible to write results to and get input from remote client. I bet this is the way Microsoft monad team will be using to provide remote access (maybe in version 2 of msh).

For me, it is too much work (and I would not compete with monad team). I really wish someone can show a example of implementation of MshHost and MshHostUserInterface. Maybe monad team would like to share their  source code of msh.exe

Hehe, You know I am joking, right?

Added on Mar 15th 10:50am
I just find out that Microsoft Command Shell (MSH) Programmer's Guide are online now! It has exact information I needed.

Have Fun!


Monday, March 13, 2006

Securely Extend Monad Pipline over Network (2)

I have uploaded a testing binary package. You can download it from gotdotnet. If you have any suggestions or bug reports, please drop a few lines as comment.


1. Start MonadServiceHost.exe at Host Windows box with Monad installed (beta 3.1 for my testing):
MonadServiceHost.exe 8888
Where "8888" is the port number you want server to listen. (Remember to allow accecc to local port 8888 in you firewall setting).

2. Start MSH.exe at client Windows box
3. Load MonadServiceClient.dll from within client MSH.exe
where "D:\MonadServiceClient.dll" full path to MonadServiceClient.dll

4. Initialize a client object
$client = new-object MonadServiceClient.ClientLib (8888,"")
where "" is the IP Address of Server, "8888" is port to connect .

5. Check out Method you can use
$client | get-member
6. Connect client to remote server
7. Using client to communicate with server
# Ping Server

# Invoke remote command
$client.Invoke("set-location HKLM:")

# run remote script

# Redirect output to a file if return a lot of objects.
7. Disconnect client
8. If something went wrong and you lost connection , just reconnect client

Have Fun!


Thursday, March 09, 2006

Securely Extend Monad Pipline over Network (1)

Prototype (or a toy) of a light-weighted server, which host monad and interact with remote client via secured TCP channel. 

There are a lot of questions been asked in newsgroup about using Monad securely from remote computer. Ideally, we should have something like sshd in linux. But unlike traditional text based shell, Monad deal with .Net object using pipeline. Sshd did not have buildin functionality to work with .Net object.  So it make sense to host monad in a ASP.NET application and send object over soap protocal.

But what if you don't want a CPU-intensive program or don't want to rely on IIS web server? Maybe you just want to access your desktop via a laptop on the same home network. I still remember in the "old time" I can use NetCat.exe binding a shell and gain remote access instantly. "Simple is beautiful", isn't it? That's why I write this tiny "toy" (File size: Server 24kb, client 20kb).

1. Server is a console program which hosts Monad Runspace, authenticates client using NegotiateStream and impersonates client using remote credential.
2. Client is class libary which sents out msh command to server.
3. Server gets command input, invoke monad pipline and get output objects.
4. Server serializes object and send them to client over secure tcp channel.
5. Client de-serializes object and emit them to client side interactive MSH for reuse.

Some thoughts:
1. Simple text-base control commands were used to control server and client. For example: Server sent a "SendObject" command to client; Client sent back an "OK" command and prepared to receive object; Server then sent serialized objects, Client de-serialized objects and sent back an "OK" command to finish this sequence.
2. Serialization and de-serialization of MshObject is the most difficult part. I still did not make it fully work. Serialization of every member in MshObject is not needed because some fields are session-dependent. We just need most important informations about the object so that we can operated those objects at server side.
3. Instead of creating a new instance of monad runspace for each invoke, remote session state should be maintained by server for each client. So client can use variable generated by previous command.

At this moment
1. Authentication and network are working fine.
2. Server can only accept one client at a time.(I will swith to asynchronous method later)
2. Client maintain connection status and can be controled by msh script.
2. It is barely usable as a interactive remote shell. for example, start remote service, kill remote process, run remote script (this is really cool).
3. Can NOT sent real object between server and client. Only important object information were transfered to client.

I will post binary and source code when it became more mature. See a screenshot of server console and client output below:


Client output:

GAC    Version        Location
---    -------        --------
False  v2.0.50727     D:\msh\MonadServiceClient.dll

> $client = new-object MonadServiceClient.ClientLib (8080,"")
> $client.Connect()

IsConnected             : True
IsAuthenticated         : True
IsMutuallyAuthenticated : False
IsEncrypted             : True
IsSigned                : True
RemoteIdentity          : System.Security.Principal.GenericIdentity
Welcome                 : MSHForFun.MonadHostService Ready!
ConnectStartAt          : 2006-3-9 15:33:54
ConnectEndAt            : 0001-1-1 0:00:00

> $client.Invoke("set-location d:\msh")
OK: Null object in pipline

> $client.Invoke("get-location")
Drive:System.Management.Automation.DriveInfo Drive {get;}
Provider:System.Management.Automation.ProviderInfo Provider {get;}
ProviderPath:System.String ProviderPath {get;}
Path:System.String Path {get;}

> $client.Invoke("(get-process explorer).Id")

> $client.ping()

> $client.close()

IsConnected             : False
IsAuthenticated         : False
IsMutuallyAuthenticated : False
IsEncrypted             : False
IsSigned                : False
RemoteIdentity          :
Welcome                 :
ConnectStartAt          : 2006-3-9 15:38:46
ConnectEndAt            : 2006-3-9 15:41:43

Have Fun!


Switch to BSD License

License is a sensitivie issue. Any comments on certain licence will cause a "war" in newsgroup. I am not a lawyer and I do not want to get into trouble either. I wrote all those codes just for fun and I do want to get more feed back for my project. So I take Lee Holmes' advice.

My MSHForFun.Security mshsnapin are now under BSD license.

Have Fun!


Wednesday, March 01, 2006

Release of MSHForFun.Security MshSnapin v0.1

Finally, I finished my MSHForFun.Security MshSnapin v0.1. Source code and binary files can be downloaded from GotDotNet under BSD license (Sorry, you need an Internet explorer to visit gotdotnet.com. Firefox seemed to not willing to take .Net passport). Contain four cmdlets at this moment:

Name          : add-processowner
Definition    : add-processowner [-ProcessArray] Process[] [-Verbose] [-Debug]
                [-ErrorAction ActionPreference] [-ErrorVariable String] [-OutVa
                riable String] [-OutBuffer Int32] [-WhatIf] [-Confirm]
Wrapper of OpenProcessToken Win32 API. It takes Process (output of get-process cmdlet) as input and adds process owner information (System.Security.Pricipal.WindowsIdentity) as NoteProperty.

Name          : Get-Privilege
Definition    : Get-Privilege [[-Identity] WindowsIdentity] [-Verbose] [-Debug]
                 [-ErrorAction ActionPreference] [-ErrorVariable String] [-OutV
                ariable String] [-OutBuffer Int32] [-WhatIf] [-Confirm]
Wrapper of GetTokenInformation Win32 API. It takes System.Security.Pricipal.WindowsIdentity object as input and returns MSHForFun.Security.TokenPrivilegeCollection object. If used without parameter, it returns current process (msh.exe) token privileges.

Name          : get-windowsidentity
Definition    : get-windowsidentity [[-Credential] MshCredential] [-Verbose] [-
                Debug] [-ErrorAction ActionPreference] [-ErrorVariable String]
                [-OutVariable String] [-OutBuffer Int32] [-WhatIf] [-Confirm]
Wapper of LogonUser() Win32API. It takes MshCredential object as input and returns System.Security.Pricipal.WindowsIdentity object. If used without parameter, it returns System.Security.Pricipal.WindowsIdentity object represented current user.

Name          : start-process
Definition    : start-process [-FileName] String [-Credential MshCredential] [-
                Arguments String] [-LoadUserProfile] [-UseShellExecute] [-Worki
                ngDirectory String] [-CreateNoWindow] [-ErrorDialog] [-ErrorDia
                logParentHandle IntPtr] [-RedirectStandardError] [-RedirectStan
                dardInput] [-RedirectStandardOutput] [-StandardErrorEncoding En
                coding] [-StandardOutputEncoding Encoding] [-Verb String] [-Win
                dowStyle ProcessWindowStyle] [-Verbose] [-Debug] [-ErrorAction
                ActionPreference] [-ErrorVariable String] [-OutVariable String]
                 [-OutBuffer Int32] [-WhatIf] [-Confirm]
Wrapper of System.Diagnostics.Process.Start() Methods. It takes MshCredential object as input and start a process using that credential. (Runas in MSH).

What's new?
  1. Wrap token handle into SafeTokenHandle, which is derived from Microsoft.Win32. SafeHandles. SafeHandleZeroOrMinusOneIsInvalid (try to prevent handle leakage and provides protection for handle recycling security attacks)
  2. Get-Privilege cmdlet
  3. Make sure all Exceptions were properly handled  and using WriteWarning instead of WriteError,  if we known errors will happen frequently (Add-Processowner)
  4. Rewrite C# code, correct errors and clean comments.
Something intersting about Get-Privilege cmdlet:
  1. System.Security.Pricipal.WindowsIdentity contains all important information inside a token except token privileges, so I took the challenge to write a Get-Privilege cmdlet.
  2. Mashalling memory block (struct TOKEN_PRIVILEGE) using C# is killing me. I will not do it again.
  3. MSHForFun.Security.TokenPrivilege contian 2 public property: Name and Status
  4. MSHForFun.Security.TokenPrivilegeCollection is derived from System.Collection.CollectionBase.
  5. Get-Privilege cmdlet is used for dump token privilege only.
Some examples of using these cmdlets:
>get-privilege (get-windowsidentity (get-credential))

Cmdlet get-credential at command pipeline position 1
Supply values for the following parameters:
User: Administrator
Password for user Administrator: *******

Name                                    Status
----                                    ------
SeChangeNotifyPrivilege                 Enabled | Enabled By Default
SeSecurityPrivilege                     Enabled | Enabled By Default
SeBackupPrivilege                       Enabled | Enabled By Default
SeRestorePrivilege                      Enabled | Enabled By Default
SeSystemtimePrivilege                   Enabled | Enabled By Default
SeShutdownPrivilege                     Enabled | Enabled By Default
SeRemoteShutdownPrivilege               Enabled | Enabled By Default
SeTakeOwnershipPrivilege                Enabled | Enabled By Default
SeDebugPrivilege                        Enabled | Enabled By Default
SeSystemEnvironmentPrivilege            Enabled | Enabled By Default
SeSystemProfilePrivilege                Enabled | Enabled By Default
SeProfileSingleProcessPrivilege         Enabled | Enabled By Default
SeIncreaseBasePriorityPrivilege         Enabled | Enabled By Default
SeLoadDriverPrivilege                   Enabled | Enabled By Default
SeCreatePagefilePrivilege               Enabled | Enabled By Default
SeIncreaseQuotaPrivilege                Enabled | Enabled By Default
SeUndockPrivilege                       Enabled | Enabled By Default
SeManageVolumePrivilege                 Enabled | Enabled By Default
SeCreateGlobalPrivilege                 Enabled | Enabled By Default
SeImpersonatePrivilege                  Enabled | Enabled By Default

> get-privilege (get-process -Id 3768 | add-processowner).Processowner

Name                                    Status
----                                    ------
SeChangeNotifyPrivilege                 Enabled | Enabled By Default
SeSecurityPrivilege                     Removed
SeBackupPrivilege                       Removed
SeRestorePrivilege                      Removed
SeSystemtimePrivilege                   Removed
SeShutdownPrivilege                     Removed
SeRemoteShutdownPrivilege               Removed
SeTakeOwnershipPrivilege                Removed
SeDebugPrivilege                        Enabled
SeSystemEnvironmentPrivilege            Removed
SeSystemProfilePrivilege                Removed
SeProfileSingleProcessPrivilege         Removed
SeIncreaseBasePriorityPrivilege         Removed
SeLoadDriverPrivilege                   Removed
SeCreatePagefilePrivilege               Removed
SeIncreaseQuotaPrivilege                Removed
SeUndockPrivilege                       Removed
SeManageVolumePrivilege                 Removed
SeCreateGlobalPrivilege                 Enabled | Enabled By Default
SeImpersonatePrivilege                  Enabled | Enabled By Default

>get-process | add-processowner

Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     71       3     1548       5304    38     0.80    864 acrotray
WARNING: Failed to openy process token (Access Denied): alg
    105       5     1224       3604    35            2832 alg

>Start-process -Credential (get-credential) -FileName control.exe -Arguments timedate.cpl

Cmdlet get-credential at command pipeline position 1
Supply values for the following parameters:
User: Administrator
Password for user Administrator: *******

Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
      0       0       64         60     1     0.00   2220 control

PS. some Non-MSH stuff:
Check out the "Brrreeeport" game on the Scobleizer blog.
Check out results on
1. Technorati
2. MSN Search
3. Google
4. Yahoo

Have fun!

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]