红队开发基础-基础免杀(三)
字数 1917 2025-08-27 12:33:54
红队开发基础:基础免杀技术详解(三)
目录
删除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:
// 创建内存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);
替换过程:
- 搜索当前进程已加载的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
- 解密并恢复属性
内存定位方法:
MEMORY_BASIC_INFORMATION mbi;
VirtualQueryEx(..., &mbi, sizeof(mbi));
// 遍历内存块,匹配sleep返回地址所在区域
两种实现方式
通过命令行参数选择:
- 参数1(默认):
- 设置内存为RW(可读可写)
- 直接修改内存属性
- 参数2:
- 设置内存为NO_ACCESS(不可访问)
- 注册VEX异常处理
- 触发异常时恢复内存为RX(可执行)
加密验证:
- 使用固定密钥XOR加密
- Python可验证加密结果一致性
参考工具与源码
-
参考工具:
- RefleXXion
- ParallelSyscalls
- ThreadStackSpoofer
- ShellcodeFluctuation
-
开源代码:
- EDR-Bypass-demo
-
参考文章:
- 《EDR Parallel-asis through Analysis》
- 先知社区相关技术文章