Friday, February 21, 2014

Powershell, legacy tools and unmanaged code

Although Powershell in its latest version has literally a huge number of cmdlets (1602 under Windows 2012 R2) and the .Net Framework exposes almost anything a system administrator could dream of, there are times you are stuck and either fall back to what I inappropriately call 'legacy' executables, or call unmanaged APIs from within your code.

For example, a simple task like finding out which Windows process is holding which socket open is almost an impossible task for any beginner to intermediate Powershell developer.

Of course there is a cmdlet to find out the list of the running processes. It's called Get-Process and exposes many process properties: Process Id (PID), Process name, Handles, Non Paged Memory (NPM), Paged Memory (PM), Working Set (WS), Virtual Memory (VM) and many others, but not the number of the corresponding open port.
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     56       7     1796       7424    53     3.20    292 conhost
    201      11     1532       3612    42     1.98    344 csrss
     89       9     1260       3484    39     0.75    400 csrss
And of course, as the Scripting Guy explains, Powershell 4.0 has a cmdlet, named Get-NetTCPConnection, to show each TCP connection property, such as the local or remote IP address, the local or remote port, and the connection state. But unfortunately the information containing the ID of the process that set up each connection is not fetched.
LocalAddress         LocalPort RemoteAddress     RemotePort State         AppliedSetting
------------         --------- -------------     ---------- -----          --------------        58849     389        Established    Datacenter        58845     389        Established    Datacenter
::1                  54765     ::1               135        Established    Datacenter
So, as far as I have found, if we stick to out-of-the-box Powershell cmdlets, there is no way to find the link between a process and a socket.

Looking into .Net, inside the System.Net.NetworkInformation namespace we can find an interesting class to explore: IPGlobalProperties. This class provides information about the network connectivity of the local computer and has:
  • a method GetActiveTcpConnections() that returns a TCPConnectionInformation array holding three properties: LocalEndpoint, RemoteEndPoint and State 
  • a method GetActiveTcpListeners() which returns the IP address of the EndPoint as well as the Port number
Under Powershell you can query them straight away. Notice the we have to use :: to indicate to PowerShell that we want to work with a static method or property, like GetIPGlobalProperties:
[System.Net.NetworkInformation.IPGlobalProperties] | Get-Member -static

   TypeName: System.Net.NetworkInformation.IPGlobalProperties

Name                  MemberType Definition
----                  ---------- ----------
Equals                Method   static bool Equals(System.Object objA, System.Object objB)
GetIPGlobalProperties Method   static System.Net.Netwo...ion.IPGlobalProperties GetIPGlobalProperties()
ReferenceEquals       Method   static bool ReferenceEquals(System.Object objA, System.Object objB)


Unfortunately the PID of the process opening the socket is still missing. Too bad.

At this point, there are two options left. The first one is to fall back to using good old Netstat.exe and parse the result as Shay Levy did in an old post. You can check his function on PoshCode.

Parsing the output of netstat is not an easy task, and Shay does a brilliant job of splitting the text output and objectify it while adding the result of Get-Process to each connection, so that the PID is shown. The resulting PSObject can be filtered, sorted and formatted to best suit our needs. The function code is explained on his blog, so I won't delve more in it. I just invite you to have a look at it since its a great workaround (if not a full extent solution).

But how comes that we can't retrieve the same information netstat does when used with the -ano parameters? Well, the answer is simple: on a Windows Systems there is managed code and unmanaged code.
Output of netstat -ano, with PID
To make it simple (I am not a developer), Managed Code is code that runs within the .Net Framework's Common Language Runtime (CLR) and benefits from the services provided by the CLR itself. On the contrary, Unmanaged Code is code that runs outside the CLR: COM components, ActiveX components, and Win32 API functions are all examples of Unmanaged Code.

One of these functions, GetExtendedTCpTable, which exists inside the Internet Protocol Helper (IP Helper) API, is the one that Netstat probably use to find out the hidden link between processes and sockets.

These Win32 API functions can't be accessed directly from inside Powershell because there is no wrapper for them in the .Net Framework.

Fortunately there is the possibility to call Win32 and other unmanaged APIs from managed code (like C#) by using a Platform Invoke (P/Invoke), which is the operation the Common Language Runtime does when it finds and loads the required Win32 API DLL in memory, then invokes the desired function.

As Lee Holmes explains on his blog, there are a few ways to access a Win32 API from Powershell. One of them consists in using the Add-Type cmdlet, which takes in the C# signature of a function (GetExtendedTCpTable in our case, whose signature is here), and building a new class on it.

This is a pretty complex process, which goes well beyond the perimeter of this system administration blog, but I want all the same share the link to Justin blog who did an excellent job of importing the iphlpapi.dll and writing a Get-Netstat wrapper function.
Add-Type cmdlet and C# signature
Both Shay's and Justin's solution to the netstat problem are good and very educational and both have pros and cons.

Accessing a function inside an unmanaged API is a rewarding but dangerous task, since you are exiting the comfort zone your Powershell interpreter provides, so be careful, since you shouldn't be doing that unless you have very good Windows Internals and C# skills.

Shay's approach is brilliant, since it shows how strong Powershell is in handling raw text and beautifying it to a manageable object. I liked in particular the way he checks for a string being an IPV6 address inside an IF block:
if (($la = $item[1] -as [ipaddress]).AddressFamily -eq ‘InterNetworkV6′)
I really like Powershell, because I never stop learning. Stay tuned for more!

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...