穿透静态检测:EDR对抗技术的分层实现
字数 2678
更新时间 2026-04-22 12:56:43

EDR静态检测对抗技术详解

1. 静态对抗概述

1.1 核心对抗逻辑

静态检测是EDR检测链条的起点,其逻辑核心是:在样本未运行时,从PE结构、字节序列、常量数据、导入表等静态特征中识别恶意意图。红队的对抗目标是在不破坏语义的前提下,抹除或稀释这些可辨识痕迹,使静态引擎失效。

1.2 对抗技术分层

  • 文件级:整体加密/加壳
  • 编译级:LLVM IR层混淆
  • 数据级:编译期字符串加密
  • 特征级:PE结构伪装与重排
  • 内存级:运行时payload实时加解密(Sleep Mask等)

2. 整体加密技术

2.1 基本原理

加载器 + 加密payload结构,payload以密文形式存储于磁盘,静态引擎只能看到无规律字节序列。

2.2 工程价值

  • 实现样本分发与更新的解耦
  • 只需加载器通过静态检测,payload可随意更换
  • 为后续技术栈叠加提供基础

2.3 局限性

仅解决磁盘可见性,内存解密瞬间暴露面最大,需配合内存层保护。

3. 编译期混淆

3.1 LLVM Pass混淆原理

在LLVM中间表示(IR)层进行代码变换,保持语义等价但改变字节层、控制流、数据层形态。

3.2 主要混淆类型

混淆类型 实现原理 对抗目标
控制流平坦化 拆分基本块,用switch-case+状态变量串联 破坏控制流图(CFG)可读性
虚假控制流 插入永不会执行的不透明谓词路径 增加分析复杂度
指令替换 等效指令替换(如a+b → a-(-b)) 改变字节序列特征

3.3 参考项目

  • Hikari、Pluto(OLLVM fork)
  • gmh5225/awesome-llvm-security(资源集合)

3.4 使用注意事项

  1. 对动态检测几乎无效(行为特征不变)
  2. 编译时间与二进制体积显著增加
  3. 建议仅对核心敏感函数深度混淆

4. 字符串常量加密

4.1 技术必要性

未加密字符串是静态分析最直接的突破口,.rdata段中的API名称、C2 URL、User-Agent等可直接被strings工具提取。

4.2 编译期加密实现

template <int N>
class ObfuscatedString {
    char encrypted[N];
    char key;
    constexpr char enc(char c, int i) const {
        return c ^ (key + i);
    }
public:
    constexpr ObfuscatedString(const char (&str)[N], char k)
        : encrypted{}, key(k) {
        for (int i = 0; i < N; i++)
            encrypted[i] = enc(str[i], i);
    }
    const char* decrypt(char* buf) const {
        for (int i = 0; i < N; i++)
            buf[i] = encrypted[i] ^ (key + i);
        return buf;
    }
};
#define OBFS(str) ObfuscatedString<sizeof(str)>(str, __TIME__[7])

4.3 推荐开源库

项目 特点 兼容性
JustasMasiulis/xorstr 64位密钥、16字节对齐、向量化优化 Clang 5.0+/GCC 7.1+/MSVC v141
skCrypter 轻量、C++11兼容 跨平台
ADVobfuscator 老牌、Pass丰富 成熟稳定
obfuscate header-only 简单易用

4.4 关键实践要点

  1. 优化等级必须为-O1或更高
  2. Release构建需strip符号
  3. 解密后立即清零缓冲区(SecureZeroMemory)
  4. 配合API哈希化调用形成完整链路

5. PE特征对抗

5.1 导入表对抗

5.1.1 敏感API规避

  • 自定义GetProcAddress/GetModuleHandle:通过PEB遍历手动解析
  • API哈希:编译期计算函数名哈希,运行时匹配
  • 手动DLL加载:完整实现PE加载器
  • 直接系统调用:syscall指令绕过用户态hook

5.1.2 导入表伪装

#pragma comment(linker, "/include:MessageBoxW")

从正常PE提取大量"无害"导入项,淹没敏感API特征,避免导入表稀疏异常。

5.2 段布局重构

5.2.1 段名$后缀特性

#pragma code_seg(".fn$a")
void StepOne() { }
#pragma code_seg(".fn$c") 
void StepTwo() { }
#pragma code_seg(".fn$b")
void StepThree() { }

.text\(a、.text\)b同属.text段但可控制排列顺序。

5.2.2 自定义段

#pragma code_seg(".mycode")
void DecryptPayload(BYTE* buf, int len, BYTE key) { ... }
#pragma code_seg()

#pragma data_seg(".mydata")
unsigned char encPayload[] = { ... };
#pragma data_seg()

5.2.3 垃圾段填充

#pragma section(".junk", read)
__declspec(allocate(".junk"))
char junk[(__COUNTER__ + 1) * 0x100] = {0};

__COUNTER__使每次编译偏移不同。

5.2.4 段重命名

#pragma comment(linker, "/RENAME:.text=.code")
#pragma comment(linker, "/RENAME:.rdata=.cfg")

6. 内存层保护

6.1 Sleep Mask技术原理

利用C2 beacon大部分时间休眠的特性,休眠期间加密payload内存,执行前解密,减少明文暴露窗口。

6.2 主要实现方案

项目 核心机制 特点
Ekko (Cracked5pider) CreateTimerQueueTimer+ROP链+RC4 首个公开PoC
D1rkSleep (SaadAhla) 仅加密PE Image Section 性能优化
Cronos (Idov31) Waitable Timer替代Timer Queue API面更换
SWAPPALA (oldboy21) 借合法DLL映射地址 伪装为Image Memory
SLEAPING (oldboy21) 定时器工作线程恢复挂起线程 绕过CFG检查

6.3 Ekko实现要点

  1. CreateTimerQueueTimer注册定时器回调
  2. 回调构造ROP链执行:RW权限→RC4加密→等待→解密→RX权限
  3. 全部使用合法API调用链

6.4 SWAPPALA借壳机制

  1. 加载合法牺牲DLL
  2. unmap后同地址创建新Section映射payload
  3. 休眠时换回合法DLL内容
  4. 唤醒时换回payload

7. 云端扫描对抗

云端扫描本质是本地检测逻辑的延伸,对抗手段相同。云端额外能力是跨样本关联分析,属动态对抗范畴。

8. 防御方检测策略演进

8.1 行为链检测

VirtualAlloc(RWX) → WriteProcessMemory → CreateRemoteThread序列检测

8.2 内存扫描升级

  • Private Memory with EXECUTE标记
  • RX权限的非Image页
  • 定时器回调地址异常

8.3 堆栈回溯检测

定时器回调运行时,线程调用栈不在已知模块内

8.4 跨实体关联

流量模式、进程创建序列、横向移动轨迹分析

9. 核心对抗原则

  1. 分层叠加优于单点深耕:多层面60%覆盖比单层面100%更有效
  2. 编译期优先:运行时代码可被hook,编译期变换只留结果
  3. 最小暴露窗口:payload明文存在时间越短越安全
  4. 持续演进:关注对抗历史而非单点技术

10. 技术局限性认知

  • 静态技术只解决"磁盘可见性"问题
  • 行为特征无法通过静态手段消除
  • 过度定制化(如异常段名)本身可能成为特征
  • 对抗是动态博弈过程,无永久有效方案

注:本文内容完全基于提供的链接文档整理,未添加外部知识。所有技术细节、代码示例、项目参考均来源于原文。

相似文章
相似文章
 全屏