# encoding: ascii
# api: powershell
# title: Invoke-InjectLibrary
# description: Original post of gregzakh can be found at https://github.com/gregzakh/alt-ps/blob/master/Invoke-InjectLibrary.ps1
# version: 0.1
# type: function
# author: Dan Jones
# license: CC0
# function: Invoke-InjectLibrary
# x-poshcode-id: 6370
# x-archived: 2016-06-08T01:06:11
# x-published: 2016-06-06T18:24:00
#
#
#requires -version 5
function Invoke-InjectLibrary {
  <#
    .SYNOPSIS
        Injects specified dll into the target process.
    .NOTES
        This script has been written and tested on Win7 x86.
  #>
  param(
    [Parameter(Mandatory=$true, Position=0)]
    [ValidateScript({(Get-Process -Id $_ -ea 0) -ne $null})]
    [Int32]$Id,
    
    [Parameter(Mandatory=$true, Position=1)]
    [ValidateScript({Test-Path $_})]
    [String]$DllPath
  )
  
  begin {
    @(
      [Runtime.InteropServices.CallingConvention],
      [Reflection.Emit.OpCodes]
    ) | ForEach-Object {
      $keys = ($ta = [PSObject].Assembly.GetType(
        'System.Management.Automation.TypeAccelerators'
      ))::Get.Keys
    }{
      if ($keys -notcontains $_.Name) {
        $ta::Add($_.name, $_)
      }
    }
    
    function private:Get-ProcAddress {
      param(
        [Parameter(Mandatory=$true)]
        [String]$Function
      )
      
      [Object].Assembly.GetType(
        'Microsoft.Win32.Win32Native'
      ).GetMethods(
        [Reflection.BindingFlags]40
      ).Where{
        $_.Name -cmatch '\AGet(ProcA|ModuleH)'
      }.ForEach{
        Set-Variable $_.Name $_
      }
      
      $GetProcAddress.Invoke($null, @(
        $GetModuleHandle.Invoke(
          $null, @('kernel32.dll')
        ), $Function
      ))
    }
    
    function private:Set-Delegate {
      param(
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateScript({$_ -ne [IntPtr]::Zero})]
        [IntPtr]$ProcAddress,
        
        [Parameter(Mandatory=$true, Position=1)]
        [ValidateNotNullOrEmpty()]
        [String]$Delegate
      )
      
      $proto = Invoke-Expression $Delegate
      
      $method = $proto.GetMethod('Invoke')
      
      $returntype = $method.ReturnType
      $paramtypes = $method.GetParameters() |
          Select-Object -ExpandProperty ParameterType
      
      $holder = New-Object Reflection.Emit.DynamicMethod(
        'Invoke', $returntype, $paramtypes, $proto
      )
      $il = $holder.GetILGenerator()
      (0..($paramtypes.Length - 1)).ForEach{
        $il.Emit([OpCodes]::Ldarg, $_)
      }
      
      switch ([IntPtr]::Size) {
        4 { $il.Emit([OpCodes]::Ldc_I4, $ProcAddress.ToInt32()) }
        8 { $il.Emit([OpCodes]::Ldc_I8, $ProcAddress.ToInt64()) }
      }
      $il.EmitCalli(
        [OpCodes]::Calli, [CallingConvention]::StdCall,
        $returntype, $paramtypes
      )
      $il.Emit([OpCodes]::Ret)
      
      $holder.CreateDelegate($proto)
    }
    
    ('CloseHandle', 'CreateRemoteThread', 'LoadLibraryW', 'VirtualFreeEx',
    'OpenProcess', 'VirtualAllocEx', 'WaitForSingleObject', 'WriteProcessMemory'
    ).ForEach{
      Set-Variable $_ (Get-ProcAddress $_)
    }
    
    $CloseHandle = Set-Delegate $CloseHandle '[Func[IntPtr, Boolean]]'
    $OpenProcess = Set-Delegate $OpenProcess '[Func[UInt32, Boolean, Int32, IntPtr]]'
    $VirtualAllocEx = Set-Delegate $VirtualAllocEx `
                              '[Func[IntPtr, IntPtr, Int32, UInt32, Uint32, IntPtr]]'
    $VirtualFreeEx = Set-Delegate $VirtualFreeEx `
                                     '[Func[IntPtr, IntPtr, Int32, UInt32, Boolean]]'
    $CreateRemoteThread = Set-Delegate $CreateRemoteThread `
           '[Func[IntPtr, IntPtr, UInt32, IntPtr, IntPtr, UInt32, [Byte[]], IntPtr]]'
    $WaitForSingleObject = Set-Delegate $WaitForSingleObject `
                                                     '[Func[IntPtr, UInt32, UInt32]]'
    $WriteProcessMemory = Set-Delegate $WriteProcessMemory `
                           '[Func[IntPtr, IntPtr, [Byte[]], Int32, IntPtr, Boolean]]'
    $DllPath = Resolve-Path $DllPath
    $INFINITE = [BitConverter]::ToUInt32([BitConverter]::GetBytes(0xFFFFFFFF), 0)
  }
  process {
    if (($proc = $OpenProcess.Invoke(0x42A, $false, $Id)) -ne [IntPtr]::Zero) {
      $sz = ((Get-Item $DllPath).Length + 1) * 2 #the size of the region of memory allocate
      if (($lrem = $VirtualAllocEx.Invoke($proc, [IntPtr]::Zero, $sz, 0x1000, 0x4)
      ) -ne [IntPtr]::Zero) {
        $bytes = [Text.Encoding]::Unicode.GetBytes($DllPath)
        if ($WriteProcessMemory.Invoke($proc, $lrem, $bytes, $sz, [IntPtr]::Zero)) {
          $bytes = [Byte[]]@(0, 0, 0, 0)
          if (($thrd = $CreateRemoteThread.Invoke(
            $proc, [IntPtr]::Zero, 0, $LoadLibraryW, $lrem, 0, $bytes
          )) -ne [IntPtr]::Zero) {
            [void]$WaitForSingleObject.Invoke($thrd, $INFINITE)
            [void]$CloseHandle.Invoke($thrd)
          }
        }
        [void]$VirtualFreeEx.Invoke($proc, $lrem, 0, 0x8000)
      }
      [void]$CloseHandle.Invoke($proc)
    }
  }
  end {
    ('OpCodes', 'CallingConvention').ForEach{
      [void]$ta::Remove($_)
    }
  }
}
