Press "Enter" to skip to content

PowerShell and Win32 API Access

Several functions in PowerView are dependent on the lower-level Windows API. Specifically, Get-NetSession utilizes the NetSessionEnum call, Get-NetShare utilizes the NetShareEnum call, Get-NetLoggedOn utilizes the NetWkstaUserEnum call, and Invoke-CheckLocalAdminAccess utilizes the OpenSCManager call.

PowerView has gone through a few iterations of how to access this lower-level functionality. It started with using Add-Type to embed inline C# to compile all functionality in memory. This is what most PowerShell WinAPI examples utilize, as it’s the ‘easiest’ method. There are a few downsides though: although this compiles the C# code in memory, some temporary files do touch disk. Also, we ran into a scenario where the particular csc.exe instance used to compile the code was failing in a client’s environment. This, combined with inheriting Matt Graeber’s philosophy of staying off of disk no matter what, led me to check out additional methods of API access for PowerView.

Luckily, Matt has us all covered. He has an excellent series of articles on multiple ways of interacting with the Windows API, as well as some awesome posts on accessing the API and using reflection to define structs and enums in PowerShell. Due to some eccentricities with the specific functions PowerView uses, I ended up going with the delegate method described on Because of some ASCII vs Unicode issues for these methods, I had to “fake cast” some strings to Unicode because of some underlying weirdness. This always made me uncomfortable- I don’t like hacky work-arounds unless absolutely necessary.

Matt recently released a simpler way of accessing the API that is much more ‘C-like’ that previous methods. He wrote an post about the method on PowerShellMagazine and his code is up on Github. He also has some examples of how to use this method. I recently integrated this into PowerView, and man is it way easier and cleaner than previous approaches!

In order to utilize this new PowerShell-reflection-black-magic, throw whatever functions from PSReflect you need into the top of your script. In my case, I didn’t need enum, but did need New-InMemoryModulefunc, Add-Win32Type, field and struct. Yes, this is a bit of overhead, but you cant trim out comments if you need to and I promise the end result will be super easy to use.

Let’s see how an updated example for Get-NetSession how looks. Defining the functions you need is pretty easy:

$Mod = New-InMemoryModule -ModuleName Win32
$FunctionDefinitions = @(
(func netapi32 NetSessionEnum ([Int]) @([string], [string], [string], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetApiBufferFree ([Int]) @([IntPtr]))
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace ‘Win32’
$Netapi32 = $Types[‘netapi32’]

This function can then be referenced like this:

$Result = $Netapi32::NetSessionEnum($HostName, ”, $UserName, $QueryLevel,[ref]$ptrInfo,-1,[ref]$EntriesRead,[ref]$TotalRead,[ref]$ResumeHandle)

The result structure, SESSION_INFO_10, is now about as C-like as you can get:
$SESSION_INFO_10 = struct $Mod SESSION_INFO_10 @{
sesi10_cname = field 0 String -MarshalAs @(‘LPWStr’)
sesi10_username = field 1 String -MarshalAs @(‘LPWStr’)
sesi10_time = field 2 UInt32
sesi10_idle_time = field 3 UInt32

Casting this result structure no longer needs [system.runtime.interopservices.marshal]::PtrToStructure(), and can be done as easily as:

$newintptr = New-Object system.Intptr -ArgumentList $offset
$Info = $newintptr -as $SESSION_INFO_10

For comparison, this is how this all used to look and this is how it looks now. There’s another nice side-effect as well. This new method is a massive speed increase versus how the previous method was implemented in PowerView (think 10-20x)! This is almost certainly because I implemented everything inefficiently in the previous method. But long story short, this new approach is in the same class as native C implementations like netview.exe.

Also take note: as Matt mentions, once you define your types/structs, they’ll be baked in until you restart PowerShell.

Anyone interested in accessing the underlying Windows API through PowerShell, definitely check out Matt’s new method!

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *