红队开发基础-基础免杀(三)
字数 1917 2025-08-27 12:33:54

红队开发基础:基础免杀技术详解(三)

目录

  1. 删除ntdll.dll中的钩子
  2. 伪造线程调用堆栈
  3. Beacon的内存加密
  4. 参考工具与源码

删除ntdll.dll中的钩子

实现原理

Windows 10+引入了并行加载特性(Windows Parallel Loading),系统在加载DLL前会判断采用同步还是异步加载方式。在此过程中,Windows会保存以下函数的存根(位于ntdll的.text节):

  • NtOpenFile()
  • NtCreateSection()
  • ZwQueryAttributeFile()
  • ZwOpenSection()
  • ZwMapViewOfFile()

即使这些函数被hook,我们仍能获取其syscall number,并用干净的ntdll替换内存中被hook的ntdll,实现unhook操作。

具体实现

参考工具:RefleXXion(提供exe和dll两种形式)

关键步骤:

  1. 调用InitSyscallsFromLdrpThunkSignature获取syscall存根
  2. 使用BuildSyscallStub工厂函数生成不同函数的syscall内联汇编代码
  3. 强制转换为函数指针备用
  4. 替换内存中ntdll.dll的函数

技术一:从本地文件读取

使用NtCreateSectionNtMapViewOfSection API:

// 创建内存session
ntStatus = RxNtCreateSection(&hSection, STANDARD_RIGHTS_REQUIRED | SECTION_MAP_READ | SECTION_QUERY, 
                           NULL, NULL, PAGE_READONLY, SEC_IMAGE, hFile);

// 映射到当前进程内存
ntStatus = RxNtMapViewOfSection(hSection, NtCurrentProcess(), &pCleanNtdll, 
                              NULL, NULL, NULL, &sztViewSize, 1, 0, PAGE_READONLY);

替换过程:

  1. 搜索当前进程已加载的ntdll.dll
  2. 解析PE结构找到.text段
  3. 进行内存替换

技术二:从KnownDlls读取

使用NtOpenSectionNtMapViewOfSection实现,原理与技术一类似。

调试与问题解决

常见问题及解决方案:

  1. RtlInitUnicodeString返回false:
    • 确保hHookedNtdll变量正确指向ntdll.dll而非kernel32.dll
  2. pCleanNtll指向错误:
    • 确保从ntdll而非kernel32获取API

验证方法:

  • 检查hook前后Sleep函数的内存变化
  • 加载dll后确认hook已被解除

伪造线程调用堆栈

基础知识

Cobalt Strike特性:

  • 默认命令等待时间60秒(可通过sleep x修改)
  • Beacon通过系统sleep控制通讯间隔
  • Teamserver维护消息队列存储命令

问题:常规CS在sleep时,线程返回地址会指向驻留的shellcode,容易被检测。

实现原理

通过扰乱调用栈链状结构:

  • EDR通过检查线程返回地址发现shellcode
  • 伪造调用栈使链状图不完整

代码实现

关键步骤:

  1. 在主线程中HOOK Sleep函数,跳转到MySleep
  2. 创建进程启动beacon
  3. 修改MySleep函数返回值位置为0

效果:

  • 未hook的调用栈:完整链状结构
  • 经过hook的调用栈:中间环节损坏,链状图不完整

Beacon的内存加密

基本原理

基于ShellcodeFluctuation项目(ThreadStackSpoofer增强版):

  1. sleep执行时:
    • 加密shellcode内存
    • 修改属性为不可执行
    • 执行正常sleep
  2. sleep结束后:
    • 解密shellcode
    • 恢复可执行属性
    • 等待下次连接

优势:sleep期间shellcode不可执行,绕过EDR内存扫描。

代码实现

关键函数流程:

  1. initializeShellcodeFluctuation:定位shellcode内存
  2. shellcodeEncryptDecrypt:加解密操作
  3. 加解密过程:
    • XOR加密shellcode
    • 修改内存属性(RW或NO_ACCESS)
    • 执行sleep
    • 解密并恢复属性

内存定位方法:

MEMORY_BASIC_INFORMATION mbi;
VirtualQueryEx(..., &mbi, sizeof(mbi));
// 遍历内存块,匹配sleep返回地址所在区域

两种实现方式

通过命令行参数选择:

  1. 参数1(默认):
    • 设置内存为RW(可读可写)
    • 直接修改内存属性
  2. 参数2:
    • 设置内存为NO_ACCESS(不可访问)
    • 注册VEX异常处理
    • 触发异常时恢复内存为RX(可执行)

加密验证:

  • 使用固定密钥XOR加密
  • Python可验证加密结果一致性

参考工具与源码

  1. 参考工具:

    • RefleXXion
    • ParallelSyscalls
    • ThreadStackSpoofer
    • ShellcodeFluctuation
  2. 开源代码:

    • EDR-Bypass-demo
  3. 参考文章:

    • 《EDR Parallel-asis through Analysis》
    • 先知社区相关技术文章
红队开发基础:基础免杀技术详解(三) 目录 删除ntdll.dll中的钩子 实现原理 具体实现 技术一:从本地文件读取 技术二:从KnownDlls读取 调试与问题解决 伪造线程调用堆栈 基础知识 实现原理 代码实现 Beacon的内存加密 基本原理 代码实现 两种实现方式 参考工具与源码 删除ntdll.dll中的钩子 实现原理 Windows 10+引入了并行加载特性(Windows Parallel Loading),系统在加载DLL前会判断采用同步还是异步加载方式。在此过程中,Windows会保存以下函数的存根(位于ntdll的.text节): NtOpenFile() NtCreateSection() ZwQueryAttributeFile() ZwOpenSection() ZwMapViewOfFile() 即使这些函数被hook,我们仍能获取其syscall number,并用干净的ntdll替换内存中被hook的ntdll,实现unhook操作。 具体实现 参考工具:RefleXXion(提供exe和dll两种形式) 关键步骤: 调用 InitSyscallsFromLdrpThunkSignature 获取syscall存根 使用 BuildSyscallStub 工厂函数生成不同函数的syscall内联汇编代码 强制转换为函数指针备用 替换内存中ntdll.dll的函数 技术一:从本地文件读取 使用 NtCreateSection 和 NtMapViewOfSection API: 替换过程: 搜索当前进程已加载的ntdll.dll 解析PE结构找到.text段 进行内存替换 技术二:从KnownDlls读取 使用 NtOpenSection 和 NtMapViewOfSection 实现,原理与技术一类似。 调试与问题解决 常见问题及解决方案: RtlInitUnicodeString 返回false: 确保hHookedNtdll变量正确指向ntdll.dll而非kernel32.dll pCleanNtll指向错误: 确保从ntdll而非kernel32获取API 验证方法: 检查hook前后Sleep函数的内存变化 加载dll后确认hook已被解除 伪造线程调用堆栈 基础知识 Cobalt Strike特性: 默认命令等待时间60秒(可通过 sleep x 修改) Beacon通过系统sleep控制通讯间隔 Teamserver维护消息队列存储命令 问题:常规CS在sleep时,线程返回地址会指向驻留的shellcode,容易被检测。 实现原理 通过扰乱调用栈链状结构: EDR通过检查线程返回地址发现shellcode 伪造调用栈使链状图不完整 代码实现 关键步骤: 在主线程中HOOK Sleep函数,跳转到MySleep 创建进程启动beacon 修改MySleep函数返回值位置为0 效果: 未hook的调用栈:完整链状结构 经过hook的调用栈:中间环节损坏,链状图不完整 Beacon的内存加密 基本原理 基于ShellcodeFluctuation项目(ThreadStackSpoofer增强版): sleep执行时: 加密shellcode内存 修改属性为不可执行 执行正常sleep sleep结束后: 解密shellcode 恢复可执行属性 等待下次连接 优势:sleep期间shellcode不可执行,绕过EDR内存扫描。 代码实现 关键函数流程: initializeShellcodeFluctuation :定位shellcode内存 shellcodeEncryptDecrypt :加解密操作 加解密过程: XOR加密shellcode 修改内存属性(RW或NO_ ACCESS) 执行sleep 解密并恢复属性 内存定位方法: 两种实现方式 通过命令行参数选择: 参数1(默认): 设置内存为RW(可读可写) 直接修改内存属性 参数2: 设置内存为NO_ ACCESS(不可访问) 注册VEX异常处理 触发异常时恢复内存为RX(可执行) 加密验证: 使用固定密钥XOR加密 Python可验证加密结果一致性 参考工具与源码 参考工具: RefleXXion ParallelSyscalls ThreadStackSpoofer ShellcodeFluctuation 开源代码: EDR-Bypass-demo 参考文章: 《EDR Parallel-asis through Analysis》 先知社区相关技术文章