Saturday, August 11, 2012

Surgical .NET Dissection - Using PowerShell Proxy Functions to Extend Get-Member

Download: 'Get-Member' proxy function

tl:dr version

Using proxy functions in PowerShell, a '-Private' parameter can be added to the Get-Member cmdlet which will expose all non-public .NET members. This feature is ideal for those interested in hacking, researching or tearing apart the .NET framework.

In previous posts, I've mentioned methods to expose private types and members in .NET with PowerShell. Leveraging these techniques creatively require at least a basic knowledge of reflection and .NET internals. These techniques also consume a lot of keystrokes. After reading an excellent introduction to proxy functions, I learned that any existing cmdlet can be added to or subtracted from. This inspired me to extend the Get-Member cmdlet.

Before proceeding in this article, it is recommended that you become familiar with proxy functions.

For those PowerShell users who have been living under a rock, Get-Member is a required cmdlet to be familiar with. It prints all public members (properties, methods, events, etc.) of both instance and static types. For example, the following command will return all the public and static members of the System.Convert class:

The Get-Member cmdlet returns objects of the type System.Management.Automation.PSMemberTypes consisting of the following parameters and types:

      Name - String
  Typename - String
MemberType - System.Management.Automation.PSMemberTypes
Definition - String

What you're not seeing in the output above are all of the non-public members. There is a valid reason for this - there is no way to directly access them and most people don't care to see that which they cannot touch. I, on the other hand enjoy touching one's private members... of .NET objects. ;D So I modified Get-Member to produce the following:

Adding the '-Private' parameter was no trivial matter. First of all, the MemberType field of the Get-Member output is problematic because non-public members use member types from the System.Reflection.MemberTypes class, not System.Management.Automation.PSMemberTypes. What I did to resolve this was convert the output to custom PSObjects with the same properties of the Get-Member output. This was achieved in the following code snippet:

The PSObject.TypeNames.Insert method was used to trick PowerShell into thinking that my custom object was a MemberDefinition object when it got output to the console. This trick is possible because a custom view for Microsoft.PowerShell.Commands.MemberDefinition is defined in PowerShellCore.format.ps1xml.

To install the proxy function, you can do any of the following:

1) Install in your current session:

PS> . .\Get-Member.ps1

2) Persistant install:

* I placed the script in the same folder as my profile.ps1 script and added this line:

. (Join-Path (Split-Path $PROFILE) Get-Member.ps1)

As usual, let me know if you have any questions or bug reports. For all other PowerShell related questions, Get-Help is your friend. Also, the modified Get-Member is fully documented via `help Get-Member -Full -Category function`.

As a final exercise, I encourage everyone to try out the following commands and observe the interesting results:

PS> [AppDomain]::CurrentDomain.GetAssemblies() | % {$_.GetTypes()} | % {$_ | Get-Member -Private -Force}
PS> [AppDomain]::CurrentDomain.GetAssemblies() | % {$_.GetTypes()} | % {$_ | Get-Member -Private -Static -Force}