EDR内存与行为分析

· 2026-09-18 22:00 · 0 阅读

原创 pandazhengzheng 2026-09-18 22:00 广东

目录

  1. 内存区域分类

  2. 内存扫描

  3. 查询进程信息

  4. 调用栈分析

  5. 线程状态分析

  6. 性能影响

  7. 熵值分析与加密 payload 检测

  8. 行为序列与规则引擎

  9. pe-sieve 与 moneta 检测原理

  10. 小结


1. 内存区域分类

1.1 PE 映射到内存

启动 .exe 时,PE 文件中的节区作为块完整拷入内存。

 EXE                              Process                   
┌──────────┐                     ┌──────────────┐           
│ Header   ├────────────────────►│ Header       │           
├──────────┤                     ├──────────────┤           
│          │                     │ .text        │  Backed RX
│ .text    ├────────────────────►│              │           
├──────────┤                     ├──────────────┤           
│ .data    ├────────────────────►│ .data        │  Backed RW
└──────────┘                     ├──────────────┤
                                 │ Virtual      │  Unbacked RW
                                 │ Alloc()      │           
                                 └──────────────┘           

1.2 Backed vs Unbacked

来自 PE 文件映像的内存区域称为 backed(映像支持的)。它们可信,因为是磁盘 PE 文件的 1:1 副本(已被 AV 扫描)。内存区域"backed"于磁盘文件。也称为 IMAGE 区域。

进程通过 VirtualAlloc() 分配额外内存时,是 unbacked(无映像支持的)。也称为 USER 或 PRIVATE 内存。没有文件"backing"此区域。

1.3 内存区域属性

  • USER/PRIVATE/Unbacked:坏,潜在恶意,shellcode;

  • IMAGE/Backed:好,相当可信。

主要因为 shellcode(来自 exploit 或进程注入)通常位于 PRIVATE 内存。线程也应从 backed 区域启动。PRIVATE RWX 内存更可疑。

1.4 可信与不可信内存区域

可信的 IMAGE 类型内存区域(IMG)

Memory Regions Good

不可信的 PRIVATE 类型内存区域(PRV)

Memory Regions Bad

1.5 Copy-On-Write(COW)

内存页的一个酷特性是 Copy-On-Write(写时复制)。内存扫描器能检查内存页是否被写入,这对只读 .text 节区(应在进程间共享)是不寻常的。moneta 通过 PSAPI_WORKING_SET_EX_BLOCK(来自 PSAPI_WORKING_SET_EX_INFORMATION 结构)使用此特性。

Data-only 攻击(如 AMSI patch 或 ETW patch)受偏好,因为它们不触发 COW(修改的是已可写的页,而非 .text)。

1.6 内存类型的 VAD 视角

Windows 内核通过 VAD(Virtual Address Descriptor)树管理进程虚拟地址空间。VAD 的 Type 字段决定内存类型:

VAD Type

含义

可信度

来源

MEM_IMAGE

映像映射(PE 加载)

LoadLibrary

/NtMapViewOfSection(SEC_IMAGE)

MEM_MAPPED

section 映射(非映像)

MapViewOfFile

/NtMapViewOfSection(SEC_COMMIT)

MEM_PRIVATE

私有内存(VirtualAlloc)

VirtualAlloc

/VirtualAllocEx

关键:VAD 类型由映射方式决定,一旦通过 SEC_IMAGE 映射,永久为 MEM_IMAGE后续覆写不改变 VAD 类型

1.7 保护属性的可疑组合

保护属性

可疑度

说明

PAGE_EXECUTE_READWRITE

(RWX)

极高

合法代码极少用 RWX

PAGE_EXECUTE_READ

(RX)+ Private

私有可执行 = shellcode

PAGE_EXECUTE_READ

(RX)+ Image

合法映像代码

PAGE_READWRITE

(RW)

数据区域

PAGE_EXECUTE_WRITECOPY

(RWX Copy)

映像节区可写副本

1.8 参考资源

  • Windows Processes: Nefarious Anomalies and You: Memory Regions

  • Bypassing pe-sieve and moneta

  • Solving the Unhooking Problem

  • NtCreateSection + NtMapViewOfSection Code Injection


2. 内存扫描

2.1 内存签名扫描

内存签名扫描检测内存中的恶意代码,在 .text 或数据节区(栈、堆、.data 等)。

                        Event       
                          │       
 Process                  ▼       
┌───────────┐        ┌───────────┐
│           │        │           │
│ .text     ◄────────┤    EDR    │  Read + Scan
│  (bad)    │        │           │
│ .data     ◄────────┤           │
│  (bad)    │        └───────────┘
└───────────┘                     

基本与 AV 签名扫描相同:grep 或 yara 内存内容匹配已知恶意签名。

2.2 内存扫描的性能

内存扫描是性能密集操作。不持续进行,而是依赖触发器。

2.3 扫描触发时机

触发类型

说明

频率

周期性

固定间隔扫描

每 30s-数小时

事件触发

敏感事件发生时扫描

按需

可疑进程

高风险进程定期扫描

按进程

全量

系统空闲时全量扫描

每天/每周

2.4 扫描范围

EDR 通过 NtQueryVirtualMemory 枚举进程虚拟地址空间,筛选可执行区域:

MEMORY_BASIC_INFORMATION mbi;
PVOID addr = NULL;
while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi))) {
if (mbi.State == MEM_COMMIT &&
        (mbi.Protect & PAGE_EXECUTE || mbi.Protect & PAGE_EXECUTE_READ ||
         mbi.Protect & PAGE_EXECUTE_READWRITE)) {
if (mbi.Type == MEM_PRIVATE) {
// Private+Execute:强可疑,重点扫描
            scanRegion(hProc, mbi.BaseAddress, mbi.RegionSize);
        } elseif (mbi.Type == MEM_IMAGE) {
// Image+Execute:校验完整性
            verifyImageIntegrity(hProc, mbi.BaseAddress, mbi.RegionSize);
        }
    }
    addr = (PBYTE)mbi.BaseAddress + mbi.RegionSize;
}

2.5 YARA 内存扫描

rule shellcode_generic {
    strings:
        $s1 = { 4C 8B D1 B8 }          // mov r10,rcx; mov eax (syscall stub)
        $s2 = { FC 48 83 E4 F0 }       // cld; and rsp,0xF0 (shellcode prologue)
        $s3 = { 65 48 8B 04 25 60 00 00 00 } // mov rax, gs:[0x60] (PEB access)
    condition:
        any of ($s*)
}
rule reflective_loader {
    strings:
        $mz = { 4D 5A }               // MZ header
        $pe = { 50 45 00 00 }         // PE signature
    condition:
        $mz at 0 and $pe
}

2.6 内存扫描的规避

规避技术

原理

效果

Module Stomping

shellcode 在 MEM_IMAGE

不在 Private+Execute 扫描范围

Sleep Mask

睡眠时加密自身

扫描时看到密文

避免常见字节模式

不用典型 shellcode 序言

YARA 不匹配

分散内存

拆分为小块

降低单区域可疑度


3. 查询进程信息

3.1 事件富化

EDR 接收事件后,会尝试富化它:

  • 进程信息(如可执行名和命令行参数)

  • 内存扫描(可能)

  • 进程映像文件扫描(罕见)

                                                                     ┌──────────────┐          
        ┌─────────────┐ EtwWrite() ┌──────────┐   Kernel callbacks   │              │          
        │ Process     ├───────────►│          ├─────────────────────►│              │          
        │             │            │          │                      │              │          
        ├─────────────┤            │   OS     │   ETW                │    EDR       │          
        │  ntdll.dll  │            │          ├─────────────────────►│              │          
        │             │ syscall    │          │   ETW-TI             │              │          
        │             ├───────────►│          ├─────────────────────►│              │          
        │             │            │          │                      │              │          
        │             │            └──────────┘                      │              │          
        │             │                                              │              │          
        │             │◄─────────────────────────────────────────────│              │          
        │             │   Query Process Information                  │              │          
        │             │   (NtQueryInformationProcess,                │              │          
        │             │    memory scan, ...)                         │              │          
        └─────────────┘                                              └──────────────┘          

3.2 富化查询的信息

信息

获取方式

可信度

映像路径

QueryFullProcessImageName

命令行

PEB ProcessParameters.CommandLine

中(可被篡改)

父进程 PID

NtQueryInformationProcess(ProcessBasicInformation)

中(可被篡改)

进程签名

查询映像文件的签名状态

进程时间

GetProcessTimes

线程列表

NtQuerySystemInformation

内存区域

NtQueryVirtualMemory

句柄表

NtQuerySystemInformation(SystemHandleInformation)

3.3 PEB 解析

EDR 通过解析 PEB(Process Environment Block)获取进程信息:

typedefstruct _PEB {
    BOOLEAN InheritedAddressSpace;
    BOOLEAN ReadImageFileExecOptions;
    BOOLEAN BeingDebugged;
    BOOLEAN BitField;
    HANDLE Mutant;
    PVOID ImageBaseAddress;
    PPEB_LDR_DATA Ldr;                    // 已加载模块链表
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;  // 命令行等
    PVOID SubSystemData;
    PVOID ProcessHeap;
// ...
} PEB;
typedefstruct _RTL_USER_PROCESS_PARAMETERS {
// ...
    UNICODE_STRING CommandLine;           // 命令行
    UNICODE_STRING ImagePathName;         // 映像路径
// ...
} RTL_USER_PROCESS_PARAMETERS;

注意:PEB 位于进程自身内存,可被进程自身篡改(命令行欺骗、映像欺骗——见第 4 篇)。

3.4 信息可信度

并非所有信息同等可信:

信息

可信度

原因

映像路径(QueryFullProcessImageName)

内核查询,不易篡改

命令行(PEB)

PEB 在进程内存,可篡改

父进程 PID

可通过属性列表篡改

签名状态

磁盘文件签名验证

内存区域属性

VAD 在内核,不易篡改


4. 调用栈分析

4.1 调用栈回溯的原理

EDR 在 hook 处理例程中回溯当前线程调用栈,检查调用方合法性。x64 Windows 使用基于表的栈回溯(table-based unwinding)。

跳转微信打开