

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:
- 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”.
- Character Array Joining: e.g.,
- 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))
.
- 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:
- 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 asiex $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.
- A class definition string (
- Obtaining a Reference to
System.Management.Automation.dll
Assembly:- The script needs to access types within
System.Management.Automation.dll
(likeAmsiUtils
). - It constructs the string “PSObject” (stored in
$psoN
). - It then uses
iex "[$($psoN)]"
which resolves to[PSObject]
(the type object forSystem.Management.Automation.PSObject
). - Finally,
.Assembly
is called on this type object to reliably get a reference to theSystem.Management.Automation.dll
assembly, stored in$smaAssembly
. This method is more robust than trying to use[Ref]
orGetType
directly for such a fundamental type if an EDR is interfering.
- The script needs to access types within
- 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 theType
object for theMarshal
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
.
- The string “System.Runtime.InteropServices.Marshal” is stored (or constructed) in
- Obtaining Source Address (from the dynamically defined dummy method):
- The
Type
object for the dynamically defined class (DynCls
) is retrieved usingiex "[$($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 is8
) 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 aRET
instruction) from this source address. This captured machine code is stored in$srcPtrArray
.
- The
- 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 theType
object forAmsiUtils
.- 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 forAmsiUtils.ScanContent
.
- The fully qualified name of
- 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 ofAmsiUtils.ScanContent
’s machine code). - This overwrite effectively replaces the original starting instruction(s) of
ScanContent
with an immediateRET
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