Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
191 lines (163 sloc) 7.8 KB
# Copyright: (c) 2018, Jordan Borean (@jborean93) <>
# MIT License (see LICENSE or
Function Invoke-Win32Api {
Call a native Win32 API or a function exported in a DLL.
This method allows you to call a native Win32 API or DLL function
without compiling C# code using Add-Type. The advantages of this over
using Add-Type is that this is all generated in memory and no temporary
files are created.
The code has been created with great help from various sources. The main
sources I used were;
[String] The DLL to import the method from.
[String] The name of the method.
[Type] The type of the return object returned by the method.
.PARAMETER ParameterTypes
[Type[]] Array of types that define the parameter types required by the
method. The type index should match the index of the value in the
Parameters parameter.
If the parameter is a reference or an out parameter, use [Ref] as the type
for that parameter.
.PARAMETER Parameters
[Object[]] Array of objects to supply for the parameter values required by
the method. The value index should match the index of the value in the
ParameterTypes parameter.
If the parameter is a reference or an out parameter, the object should be a
[Ref] of the parameter.
[Bool] Whether to apply the SetLastError Dll attribute on the method,
default is $false
[Runtime.InteropServices.CharSet] The charset to apply to the CharSet Dll
attribute on the method, default is [Runtime.InteropServices.CharSet]::Auto
[Object] The return result from the method, the type of this value is based
on the ReturnType parameter.
# Use the Win32 APIs to open a file handle
$handle = Invoke-Win32Api -DllName kernel32.dll `
-MethodName CreateFileW `
-ReturnType Microsoft.Win32.SafeHandles.SafeFileHandle `
-ParameterTypes @([String], [System.Security.AccessControl.FileSystemRights], [System.IO.FileShare], [IntPtr], [System.IO.FileMode], [UInt32], [IntPtr]) `
-Parameters @(
[IntPtr]::Zero) `
-SetLastError $true `
-CharSet Unicode
if ($handle.IsInvalid) {
$last_err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw [System.ComponentModel.Win32Exception]$last_err
# Lookup the account name from a SID
$sid_string = "S-1-5-18"
$sid = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $sid_string
$sid_bytes = New-Object -TypeName byte[] -ArgumentList $sid.BinaryLength
$sid.GetBinaryForm($sid_bytes, 0)
$name = New-Object -TypeName System.Text.StringBuilder
$name_length = 0
$domain_name = New-Object -TypeName System.Text.StringBuilder
$domain_name_length = 0
$invoke_args = @{
DllName = "Advapi32.dll"
MethodName = "LookupAccountSidW"
ReturnType = [bool]
ParameterTypes = @([String], [byte[]], [System.Text.StringBuilder], [Ref], [System.Text.StringBuilder], [Ref], [Ref])
Parameters = @(
SetLastError = $true
CharSet = "Unicode"
$res = Invoke-Win32Api @invoke_args
$res = Invoke-Win32Api @invoke_args
if (-not $res) {
$last_err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw [System.ComponentModel.Win32Exception]$last_err
Write-Output "SID: $sid_string, Domain: $($domain_name.ToString()), Name: $($name.ToString())"
The parameters to use for a method dynamically based on the method that is
called. There is no cut and fast way to automatically convert the interface
listed on the Microsoft docs. There are great resources to help you create
the "P/Invoke" definition like
[Parameter(Position = 0, Mandatory = $true)] [String]$DllName,
[Parameter(Position = 1, Mandatory = $true)] [String]$MethodName,
[Parameter(Position = 2, Mandatory = $true)] [Type]$ReturnType,
[Parameter(Position = 3)] [Type[]]$ParameterTypes = [Type[]]@(),
[Parameter(Position = 4)] [Object[]]$Parameters = [Object[]]@(),
[Parameter()] [Bool]$SetLastError = $false,
[Parameter()] [Runtime.InteropServices.CharSet]$CharSet = [Runtime.InteropServices.CharSet]::Auto
if ($ParameterTypes.Length -ne $Parameters.Length) {
throw [System.ArgumentException]"ParameterType Count $($ParameterTypes.Length) not equal to Parameter Count $($Parameters.Length)"
# First step is to define the dynamic assembly in the current AppDomain
$assembly = New-Object -TypeName System.Reflection.AssemblyName -ArgumentList "Win32ApiAssembly"
$AssemblyBuilder = [System.Reflection.Assembly].Assembly.GetTypes() | Where-Object { $_.Name -eq 'AssemblyBuilder' }
$dynamic_assembly = $AssemblyBuilder::DefineDynamicAssembly($assembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)
# Second step is to create the dynamic module and type/class that contains
# the P/Invoke definition
$dynamic_module = $dynamic_assembly.DefineDynamicModule("Win32Module", $false)
$dynamic_type = $dynamic_module.DefineType("Win32Type", [Reflection.TypeAttributes]"Public, Class")
# Need to manually get the reference type if the ParameterType is [Ref], we
# define this based on the Parameter type at the same index
$parameter_types = $ParameterTypes.Clone()
for ($i = 0; $i -lt $ParameterTypes.Length; $i++) {
if ($ParameterTypes[$i] -eq [Ref]) {
$parameter_types[$i] = $Parameters[$i].Value.GetType().MakeByRefType()
# Next, the method is created where we specify the name, parameters and
# return type that is expected
$dynamic_method = $dynamic_type.DefineMethod(
[Reflection.MethodAttributes]"Public, Static",
# Build the attributes (DllImport) part of the method where the DLL
# SetLastError and CharSet are applied
$constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String])
$method_fields = [Reflection.FieldInfo[]]@(
$method_fields_values = [Object[]]@($SetLastError, $CharSet)
$custom_attributes = New-Object -TypeName Reflection.Emit.CustomAttributeBuilder -ArgumentList @(
# Create the custom type/class based on what was configured above
$win32_type = $dynamic_type.CreateType()
# Invoke the method with the parameters supplied and return the result
$result = $win32_type::$MethodName.Invoke($Parameters)
return $result