AMSI BYPASS AMSI BYPASS

AMSI BYPASS

Report and info about the script

Purpose: This PowerShell script is an obfuscated AMSI (Anti-Malware Scan Interface) Bypass. Its primary goal is to neutralize AMSI’s ability to scan and detect malicious content within the current PowerShell session, thereby allowing subsequent potentially malicious commands or scripts to execute without interference from AMSI-enabled security products.

Mechanism: The script achieves this by employing an advanced in-memory patching technique. It directly modifies the machine code of a core AMSI scanning function (System.Management.Automation.AmsiUtils.ScanContent) to make it return prematurely, effectively rendering it non-functional.

Obfuscation: The script is heavily obfuscated to evade detection by static signature-based security tools and to make manual analysis more challenging.


The script executes in several logical stages:

  1. Initialization of Obfuscated Strings and Names:
    • Many critical strings, such as class names, method names, type names, and search patterns, are not hardcoded directly. Instead, they are constructed at runtime through various techniques:
      • Character Array Joining: e.g., (-join([char[]](68,121,110,67,108,115))) to create “DynCls” (Dynamic Class).
      • String Concatenation: e.g., ('class '+$dcn+'{...}') to build the class definition string.
      • Base64 Decoding: e.g., ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('U3lzdGVtLk1h'))) for parts of “System.Management.Automation.AmsiUtils”.
      • Simple String Joining: e.g., ('C','o','p','y' -join '') for “Copy”.
    • These constructed strings are stored in variables with non-descriptive names (e.g., $dcn, $dmn, $auP1, $mTyp, $pnName) to further obscure their purpose.
    • The number 8 (a crucial offset) is represented as ([int](4+4)).
  2. Dynamic Class Definition:
    • A class definition string ($cds) is constructed using the obfuscated class name ($dcn, e.g., “DynCls”) and method name ($dmn, e.g., “X”). This class contains a simple static method that typically just returns an integer (static [int] X([string]$a,[string]$b){return 1}).
    • This class definition string is then executed using Invoke-Expression (aliased as iex $cds), which compiles and loads the class into the current PowerShell session.
    • The purpose of this dummy class and method is to obtain a piece of JIT-compiled machine code that essentially performs a “return” operation very quickly.
  3. Obtaining a Reference to System.Management.Automation.dll Assembly:
    • The script needs to access types within System.Management.Automation.dll (like AmsiUtils).
    • It constructs the string “PSObject” (stored in $psoN).
    • It then uses iex "[$($psoN)]" which resolves to [PSObject] (the type object for System.Management.Automation.PSObject).
    • Finally, .Assembly is called on this type object to reliably get a reference to the System.Management.Automation.dll assembly, stored in $smaAssembly. This method is more robust than trying to use [Ref] or GetType directly for such a fundamental type if an EDR is interfering.
  4. Locating and Preparing System.Runtime.InteropServices.Marshal:
    • The string “System.Runtime.InteropServices.Marshal” is stored (or constructed) in $mTyp.
    • [Type]::GetType($mTyp) is used to get the Type object for the Marshal class, stored in $marshalTypeObj. This class provides methods for unmanaged memory access.
    • Method names “Copy” and “ReadIntPtr” are obfuscated and stored in $mCopy and $mReadIP.
  5. Obtaining Source Address (from the dynamically defined dummy method):
    • The Type object for the dynamically defined class (DynCls) is retrieved using iex "[$($dcn)]" and stored in $dco.
    • The GetMethods() method (name stored in $gm) is called on $dco.
    • The results are piped to Where-Object (aliased as |?{}) to find the specific dummy method by its obfuscated name ($_.$pnName -eq $dmn). The info for this method is stored in $dmi.
    • $dmi.MethodHandle.Value provides a pointer related to the JIT-compiled code for this dummy method. This pointer doesn’t point directly to the executable code but to a descriptor.
    • A fixed offset (stored in $off, which is 8) is added to this pointer ($srcAddrBase=[long]$dmi.$pnMH.$pnVal + [long]$off). This new address ($srcAddrWithOffset) points to the actual start of the JIT-compiled machine code for the dummy method.
    • $marshalTypeObj::$mReadIP($srcAddrWithOffset) (i.e., Marshal.ReadIntPtr) is used to read a pointer-sized value (which effectively contains the first few bytes of machine code, usually including a RET instruction) from this source address. This captured machine code is stored in $srcPtrArray.
  6. Obtaining Destination Address (for AmsiUtils.ScanContent):
    • The fully qualified name of System.Management.Automation.AmsiUtils is constructed from obfuscated parts and stored in $amsiUtilFullStr.
    • $amsiUtilsTypeObj = $smaAssembly.GetType($amsiUtilFullStr) retrieves the Type object for AmsiUtils.
    • The GetMethods() method (name in $gm) is called on $amsiUtilsTypeObj, using obfuscated binding flags ($bfStr resolving to “NonPublic,Static”) to find non-public static methods.
    • The results are filtered by Where-Object to find the method whose name matches the obfuscated “ScanContent” (stored in $sc). The method info is stored in $scanContentMethodInfo.
    • Similar to the source address, $scanContentMethodInfo.MethodHandle.Value plus the offset $off gives the memory address ($destAddrWithOffset) of the JIT-compiled code for AmsiUtils.ScanContent.
  7. Performing the Memory Patch (The Bypass):
    • $marshalTypeObj::$mCopy($srcPtrArray, 0, $destAddrWithOffset, 1) is executed. This translates to: [System.Runtime.InteropServices.Marshal]::Copy(SourceArray, SourceStartIndex, DestinationPointer, Length)
    • It copies 1 byte from the $srcPtrArray (which contains the “return” instruction from the dummy method) to the memory location pointed to by $destAddrWithOffset (the start of AmsiUtils.ScanContent’s machine code).
    • This overwrite effectively replaces the original starting instruction(s) of ScanContent with an immediate RET instruction.

Intended Outcome and Effect

  • Silent Execution: If successful and not blocked, the script itself produces no visible output, simply returning to a new line in the PowerShell prompt.
  • AMSI Disabled: After the patch, any call to AmsiUtils.ScanContent within the current PowerShell session will execute the patched code, causing it to return immediately without performing any actual scanning of content.
  • Evasion of Detection: Malicious strings or commands (like 'amsiutils') that would normally be blocked by AMSI will now execute without triggering an AMSI detection, as AMSI’s scanning capability has been nullified for that session.


← Back to projects