【翻译】EDR检测机制分析
字数 1710 2025-08-25 22:59:20
EDR检测机制分析:基于假DLL、保护页和VEH的增强检测技术
0x00 前言
本文深入分析了一种非传统的EDR(端点检测与响应)检测机制,该机制结合了假DLL技术、保护页和向量异常处理(VEH)来实现对恶意行为的检测。这种机制超越了常见的EDR检测方法,如内联API挂钩、Windows事件跟踪和内核回调,提供了一种独特的检测视角。
0x01 传统EDR检测机制概述
EDR系统通常采用多阶段检测方法:
-
静态检测阶段:
- 文件哈希检查
- 签名验证
- 特定字节序列扫描
-
动态检测阶段:
- 沙箱技术(虚拟化环境执行分析)
- 用户模式挂钩(内联API挂钩/IAT挂钩)
- 反恶意软件扫描接口(AMSI)
- Windows事件跟踪(ETW)
- 内核回调机制
- 威胁情报分析
0x02 非传统检测机制分析
Fake DLL技术
核心发现:
- EDR在进程初始化时修改PEB(进程环境块)中的
InLoadOrderModuleList - 插入手动映射的假DLL(如
ntdll.dll和kernel32.dll的Leetspeak版本) - 假DLL特征:
- 名称使用Leetspeak(如
ntdll.dll变为ntd11.d11) - 文件大小与原始DLL相同
- 缺少模块描述
- 无法在磁盘上找到对应文件
- 内存区域标记为RX(读-执行)并配备保护页
- 名称使用Leetspeak(如
加载顺序操纵:
- 进程映像(如cmd.exe)
- 假
ntdll.dll - 假
kernel32.dll - 真
ntdll.dll - 真
kernel32.dll kernelbase.dll- EDR的Hooking DLL
保护页机制
技术细节:
- 假DLL的内存区域被标记为保护页(PAGE_GUARD)
- 访问保护页会触发
STATUS_GUARD_PAGE_VIOLATION异常(0x80000001) - EDR通过
AddVectoredExceptionHandler注册VEH处理这些异常 - 异常触发后,EDR接管程序流程,实现"保护页挂钩"或"页面保护挂钩"
示例VEH处理代码:
LONG CALLBACK GuardPageExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) {
printf("Guard Page Access Detected!\n");
// EDR检测逻辑
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
检测逻辑实现
- EDR在挂钩DLL中实现约25个关键原生API的检查:
NtAllocateVirtualMemoryNtWriteVirtualMemoryNtProtectVirtualMemory等
- 通过比较操作检查异常代码是否为0x80000001
- 若触发保护页异常,调用特定处理函数(如示例中的0x7FFE77F56230)
0x03 检测机制验证
PEB验证方法
-
使用WinDbg检查
InLoadOrderModuleList:!peb dt nt!_PEB_LDR_DATA <Ldr_address> dt nt!_LDR_DATA_TABLE_ENTRY <module_address> -
验证CrossProcessFlags(偏移0x50):
- 十进制值为4表示使用VEH
- 十进制值为0表示未使用VEH
-
使用x64dbg跟踪
RtlAddVectoredExceptionHandler调用栈,确认EDR挂钩DLL发起VEH注册
内存访问验证
传统PEB遍历问题:
- 使用DllBase偏移0x30会访问假DLL
- 触发保护页异常,激活EDR检测
解决方案验证:
// 获取假DLL地址(偏移0x30)
*(UINT_PTR*)(secondModule + 0x30);
// 获取真实DLL地址(偏移0xF8)
*(UINT_PTR*)(secondModule + 0xF8);
0x04 规避技术分析
1. 使用OriginalBase偏移(0xF8)
UINT_PTR NtdllOriginalDLLBase() {
UINT_PTR pebAddress = __readgsqword(0x60);
UINT_PTR ldr = *(UINT_PTR*)(pebAddress + 0x18);
UINT_PTR inInitOrderModuleList = *(UINT_PTR*)(ldr + 0x10);
UINT_PTR secondModule = *(UINT_PTR*)(inInitOrderModuleList);
return *(UINT_PTR*)(secondModule + 0xF8);
}
注意:偏移量可能随Windows版本变化
2. 直接访问正确模块
// 标准系统(无EDR)
PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)
((PBYTE)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink - 0x10);
// EDR修改后的系统
PLDR_DATA_TABLE_ENTRY pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)
((PBYTE)pCurrentPeb->LoaderData->InMemoryOrderModuleList.Flink->Flink->Flink->Flink - 0x10);
3. 使用Windows API(不推荐)
UINT_PTR baseAddress = (UINT_PTR)GetModuleHandleA("ntdll.dll");
风险:API可能被EDR内联挂钩监控
4. PEB遍历+字符串比较(推荐)
PVOID get_ntdll_base_via_name_comparison() {
PEB* peb = get_peb();
LIST_ENTRY* current = &peb->Ldr->InMemoryOrderModuleList;
do {
current = current->Flink;
MY_LDR_DATA_TABLE_ENTRY* entry = CONTAINING_RECORD(current,
MY_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
char dllName[256];
snprintf(dllName, sizeof(dllName), "%wZ", entry->FullDllName);
if (strstr(dllName, "ntdll.dll")) {
return entry->DllBase;
}
} while (current != &peb->Ldr->InMemoryOrderModuleList);
return NULL;
}
0x05 技术评估与总结
机制优势
- 高效检测:针对依赖PEB遍历的动态SSN查询技术
- 隐蔽性强:假DLL难以通过常规分析发现
- 精准触发:仅在特定内存访问模式时激活检测
潜在限制
- 系统负载:保护页异常处理可能增加系统开销
- 规避可能:通过正确访问真实DLL可绕过检测
- API依赖:对Windows内部机制依赖性强,可能随系统更新失效
防御建议
- 多样化检测:结合其他检测机制弥补盲点
- 性能优化:限制监控的API范围
- 隐蔽增强:随机化假DLL特征增加分析难度
附录:关键数据结构
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _PEB_LDR_DATA {
BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
通过深入理解这种EDR检测机制,安全研究人员可以更好地设计规避技术,而防御者则可以借鉴这种思路增强端点防护能力。