PowerShell module for ctypes/PInvoke calls
MIT License
Provides a unique way to call native APIs in PInvoke in PowerShell. It is modelled after the Python ctypes library.
See ctypes index for more details.
Note: All these examples use the generics to specify the return method which is pwsh 7.3+ only. Use .Returns([type])
instead for older versions.
$k32 = New-CtypesLib Kernel32.dll
$fh = $k32.CharSet('Unicode').SetLastError().CreateFileW[Microsoft.Win32.SafeHandles.SafeFileHandle](
"\\?\C:\temp\test.txt",
[System.Security.AccessControl.FileSystemRights]'FullControl',
[System.IO.FileShare]::Read,
$null,
[System.IO.FileMode]::Create,
0, # FlagsAndAttributes
$null)
if ($fh.IsInvalid -eq [IntPtr](-1)) {
throw [System.ComponentModel.Win32Exception]$k32.LastError
}
$fs = [System.IO.FileStream]::new($fh, [System.IO.FileAccess]::ReadWrite)
...
Calls CreateFileW directly with the long path prefix and creates a filestream on the returned object.
It also wraps the raw handle in a SafeFileHandle
making it easy to dispose the object when not needed anymore.
[int]$processId = Read-Host -Prompt 'What pid do you wish to inspect'
$k32 = New-CtypesLib Kernel32.dll
$advapi = New-CtypesLib Advapi32.dll
[Flags()] enum PrivilegeAttributes {
NONE = 0x00000000
SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001
SE_PRIVILEGE_ENABLED = 0x00000002
SE_PRIVILEGE_REMOVED = 0x00000004
SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000
}
ctypes_struct LUID {
[int]$LowPart
[int]$HighPart
}
ctypes_struct LUID_AND_ATTRIBUTES {
[LUID]$Luid
[PrivilegeAttributes]$Attributes
}
ctypes_struct TOKEN_PRIVILEGES {
[int]$PrivilegeCount
[MarshalAs('ByValArray', SizeConst=1)][LUID_AND_ATTRIBUTES[]]$Privileges
}
$proc = $k32.SetLastError().OpenProcess[IntPtr](
0x400, # PROCESS_QUERY_INFORMATION
$false,
$processId)
if ($proc -eq [IntPtr]::Zero) {
throw [System.ComponentModel.Win32Exception]$k32.LastError
}
$handle = [IntPtr]::Zero
$buffer = [IntPtr]::Zero
try {
$res = $advapi.SetLastError().OpenProcessToken[bool](
$proc,
[System.Security.Principal.TokenAccessLevels]::Query,
[ref]$handle)
if (-not $res) {
throw [System.ComponentModel.Win32Exception]$advapi.LastError
}
$bufferLength = 0
$null = $advapi.SetLastError().GetTokenInformation[bool](
$handle,
3, # TokenPrivileges
$null,
0,
[ref]$bufferLength)
$buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($bufferLength)
$res = $advapi.GetTokenInformation(
$handle,
3,
$buffer,
$bufferLength,
[ref]$bufferLength)
if (-not $res) {
throw [System.ComponentModel.Win32Exception]$advapi.LastError
}
$privileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($buffer,
[type][TOKEN_PRIVILEGES])
$currentPtr = [IntPtr]::Add($buffer, 4) # Offset to the Privileges array
for ($i = 0; $i -lt $privileges.PrivilegeCount; $i++) {
$info = [System.Runtime.InteropServices.Marshal]::PtrToStructure($currentPtr,
[type][LUID_AND_ATTRIBUTES])
$luid = $info.Luid
$name = [System.Text.StringBuilder]::new(0)
$nameLength = 0
$null = $advapi.SetLastError().CharSet('Unicode').LookupPrivilegeNameW[bool](
$null,
[ref]$luid,
$name,
[ref]$nameLength)
$null = $name.EnsureCapacity($nameLength + 1)
$res = $advapi.LookupPrivilegeNameW(
$null,
[ref]$luid,
$name,
[ref]$nameLength)
if (-not $res) {
throw [System.ComponentModel.Win32Exception]$advapi.LastError
}
[PSCustomObject]@{
Name = $name.ToString()
Luid = $luid
Attributes = $info.Attributes
}
$currentPtr = [IntPtr]::Add($currentPtr, [System.Runtime.InteropServices.Marshal]::SizeOf(
[type][LUID_AND_ATTRIBUTES]))
}
}
finally {
if ($buffer -ne [IntPtr]::Zero) {
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($buffer)
}
if ($handle -ne [IntPtr]::Zero) {
$k32.CloseHandle[void]($handle)
}
$k32.CloseHandle[void]($proc)
}
This is a more complex example that uses reference types and structs to get the token privileges of another process handle.
$libc = New-CtypesLib libc
[System.Runtime.InteropServices.Marshal]::PtrToStringUTF8(
$libc.gnu_get_libc_version[IntPtr]())
Note: This can't return a string directly as dotnet will try and free the memory which cannot be done.
These cmdlets have the following requirements
The easiest way to install this module is through PowerShellGet.
You can install this module by running;
# Install for only the current user
Install-Module -Name Ctypes -Scope CurrentUser
# Install for all users
Install-Module -Name Ctypes -Scope AllUsers
Contributing is quite easy, fork this repo and submit a pull request with the changes.
To build this module run .\build.ps1 -Task Build
in PowerShell.
To test a build run .\build.ps1 -Task Test
in PowerShell.
This script will ensure all dependencies are installed before running the test suite.