免杀专题

13、Windows策略防止ShellCode

1. 课程目标

理解Windows如何使用安全策略防止ShellCode执行,以及如何绕过这些保护机制。

1.1 学习目标

  • 了解DEP、CFG、ACG等保护机制原理
  • 学习如何检测这些保护是否启用
  • 掌握合法绕过这些保护的方法

2. 名词解释

名词 英文 解释
DEP Data Execution Prevention 数据执行保护,阻止数据区域代码执行
NX No-Execute 不可执行位,CPU级别的DEP支持
ACG Arbitrary Code Guard 任意代码防护
CET Control-flow Enforcement Technology 控制流强制技术
Shadow Stack - 影子栈,防止ROP攻击
ROP Return-Oriented Programming 返回导向编程

3. DEP保护机制

3.1 DEP工作原理

┌─────────────────────────────────────────────────────────┐
│                    进程内存空间                          │
├─────────────────────────────────────────────────────────┤
│  代码段 (.text)     │ PAGE_EXECUTE_READ  │ 可执行 ✓    │
├─────────────────────────────────────────────────────────┤
│  数据段 (.data)     │ PAGE_READWRITE     │ 不可执行 ✗  │
├─────────────────────────────────────────────────────────┤
│  堆 (Heap)          │ PAGE_READWRITE     │ 不可执行 ✗  │
├─────────────────────────────────────────────────────────┤
│  栈 (Stack)         │ PAGE_READWRITE     │ 不可执行 ✗  │
└─────────────────────────────────────────────────────────┘
         ↓ 如果在数据区执行代码
    ┌────────────────────────┐
    │  ACCESS_VIOLATION      │
    │  STATUS_ACCESS_DENIED  │
    └────────────────────────┘

3.2 检测DEP状态

#include <windows.h>
#include <stdio.h>

void CheckDEPStatus() {
    // 方法1:GetProcessDEPPolicy
    DWORD dwFlags;
    BOOL bPermanent;
    
    if (GetProcessDEPPolicy(GetCurrentProcess(), &dwFlags, &bPermanent)) {
        printf("[*] DEP状态:\n");
        printf("    启用: %s\n", (dwFlags & PROCESS_DEP_ENABLE) ? "是" : "否");
        printf("    ATL Thunk: %s\n", 
               (dwFlags & PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) ? "禁用" : "启用");
        printf("    永久: %s\n", bPermanent ? "是" : "否");
    }
    
    // 方法2:GetSystemDEPPolicy
    DEP_SYSTEM_POLICY_TYPE depPolicy = GetSystemDEPPolicy();
    printf("[*] 系统DEP策略: ");
    switch (depPolicy) {
        case DEPPolicyAlwaysOff: printf("始终关闭\n"); break;
        case DEPPolicyAlwaysOn:  printf("始终开启\n"); break;
        case DEPPolicyOptIn:     printf("选择性开启\n"); break;
        case DEPPolicyOptOut:    printf("选择性关闭\n"); break;
    }
    
    // 方法3:NtQueryInformationProcess
    typedef NTSTATUS(NTAPI* pNtQueryInformationProcess)(
        HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
    
    pNtQueryInformationProcess NtQueryInformationProcess = 
        (pNtQueryInformationProcess)GetProcAddress(
            GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationProcess");
    
    ULONG executeFlags = 0;
    NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)0x22, 
                             &executeFlags, sizeof(executeFlags), NULL);
    
    printf("[*] ExecuteFlags: 0x%X\n", executeFlags);
    printf("    MEM_EXECUTE_OPTION_DISABLE: %s\n", 
           (executeFlags & 0x1) ? "是" : "否");
}

4. 绕过DEP的方法

4.1 合法方法:VirtualProtect

#include <windows.h>
#include <stdio.h>

// 使用VirtualProtect修改内存权限
BOOL ExecuteShellcodeWithVirtualProtect(LPVOID pShellcode, SIZE_T size) {
    // 1. 分配可读写内存
    LPVOID pMem = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!pMem) {
        printf("[-] VirtualAlloc失败\n");
        return FALSE;
    }
    printf("[+] 内存分配: 0x%p (RW)\n", pMem);
    
    // 2. 复制Shellcode
    memcpy(pMem, pShellcode, size);
    printf("[+] Shellcode已复制\n");
    
    // 3. 修改权限为可执行
    DWORD dwOldProtect;
    if (!VirtualProtect(pMem, size, PAGE_EXECUTE_READ, &dwOldProtect)) {
        printf("[-] VirtualProtect失败: %d\n", GetLastError());
        VirtualFree(pMem, 0, MEM_RELEASE);
        return FALSE;
    }
    printf("[+] 权限已修改为RX\n");
    
    // 4. 刷新指令缓存
    FlushInstructionCache(GetCurrentProcess(), pMem, size);
    
    // 5. 执行
    printf("[*] 执行Shellcode...\n");
    ((void(*)())pMem)();
    
    VirtualFree(pMem, 0, MEM_RELEASE);
    return TRUE;
}

4.2 直接分配可执行内存

// 直接使用PAGE_EXECUTE_READWRITE
LPVOID pMem = VirtualAlloc(NULL, size, 
                           MEM_COMMIT | MEM_RESERVE, 
                           PAGE_EXECUTE_READWRITE);
memcpy(pMem, shellcode, size);
((void(*)())pMem)();

4.3 使用ROP链绕过(高级)

ROP链执行流程:
┌─────────────────────────────────────────────────────────┐
│  1. 找到VirtualProtect的gadget地址                       │
│  2. 准备好参数(地址、大小、新权限、旧权限指针)            │
│  3. 通过栈溢出控制返回地址                                │
│  4. 跳转到VirtualProtect gadget                          │
│  5. VirtualProtect执行,修改Shellcode内存为可执行         │
│  6. 返回到Shellcode地址执行                              │
└─────────────────────────────────────────────────────────┘

5. ACG保护机制

5.1 ACG原理

ACG阻止以下操作:

  • VirtualAlloc(PAGE_EXECUTE_*)
  • VirtualProtect(PAGE_EXECUTE_*)
  • 任何动态生成可执行代码的操作

5.2 检测ACG状态

#include <windows.h>
#include <stdio.h>

typedef BOOL(WINAPI* pGetProcessMitigationPolicy)(
    HANDLE, PROCESS_MITIGATION_POLICY, PVOID, SIZE_T);

void CheckACGStatus() {
    pGetProcessMitigationPolicy GetProcessMitigationPolicy = 
        (pGetProcessMitigationPolicy)GetProcAddress(
            GetModuleHandleW(L"kernel32.dll"), "GetProcessMitigationPolicy");
    
    if (!GetProcessMitigationPolicy) {
        printf("[-] 系统不支持GetProcessMitigationPolicy\n");
        return;
    }
    
    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy = { 0 };
    if (GetProcessMitigationPolicy(GetCurrentProcess(), 
                                   ProcessDynamicCodePolicy, 
                                   &policy, sizeof(policy))) {
        printf("[*] ACG状态:\n");
        printf("    ProhibitDynamicCode: %d\n", policy.ProhibitDynamicCode);
        printf("    AllowThreadOptOut: %d\n", policy.AllowThreadOptOut);
        printf("    AllowRemoteDowngrade: %d\n", policy.AllowRemoteDowngrade);
    }
}

5.3 绕过ACG

方法1:线程选择退出

// 如果AllowThreadOptOut=1,可以使用此方法
DWORD dwPolicy = THREAD_DYNAMIC_CODE_ALLOW;
SetThreadInformation(GetCurrentThread(), 
                     ThreadDynamicCodePolicy, 
                     &dwPolicy, sizeof(dwPolicy));

方法2:使用已存在的可执行内存

  • 模块镂空(Module Stomping)
  • 代码洞穴(Code Cave)
  • 已加载DLL的代码段

6. CFG保护机制

6.1 CFG原理

CFG(控制流防护)验证间接调用的目标地址是否合法。

无CFG保护:                       有CFG保护:
                                 
call [eax]  →  任意地址           call [eax]

                                 检查eax是否在CFG位图中

                                 合法 → 允许调用
                                 非法 → 触发异常

6.2 检测CFG状态

void CheckCFGStatus() {
    PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY cfgPolicy = { 0 };
    
    if (GetProcessMitigationPolicy(GetCurrentProcess(),
                                   ProcessControlFlowGuardPolicy,
                                   &cfgPolicy, sizeof(cfgPolicy))) {
        printf("[*] CFG状态:\n");
        printf("    EnableControlFlowGuard: %d\n", cfgPolicy.EnableControlFlowGuard);
        printf("    EnableExportSuppression: %d\n", cfgPolicy.EnableExportSuppression);
        printf("    StrictMode: %d\n", cfgPolicy.StrictMode);
    }
}

6.3 绕过CFG

方法1:调用已注册的函数

CFG允许调用PE导出表中的函数或已注册的回调函数。

方法2:使用NtContinue

// NtContinue可以修改线程上下文并继续执行
// 某些版本的CFG不检查NtContinue
typedef NTSTATUS(NTAPI* pNtContinue)(PCONTEXT, BOOLEAN);

7. 综合检测示例

#include <windows.h>
#include <stdio.h>

void CheckAllSecurityPolicies() {
    printf("========================================\n");
    printf("    Windows安全策略检测\n");
    printf("========================================\n\n");
    
    // DEP
    DWORD depFlags;
    BOOL depPermanent;
    GetProcessDEPPolicy(GetCurrentProcess(), &depFlags, &depPermanent);
    printf("[DEP]\n");
    printf("  启用: %s\n", (depFlags & 1) ? "是" : "否");
    printf("  永久: %s\n\n", depPermanent ? "是" : "否");
    
    // ASLR
    PROCESS_MITIGATION_ASLR_POLICY aslr = { 0 };
    GetProcessMitigationPolicy(GetCurrentProcess(), ProcessASLRPolicy, &aslr, sizeof(aslr));
    printf("[ASLR]\n");
    printf("  EnableBottomUpRandomization: %d\n", aslr.EnableBottomUpRandomization);
    printf("  EnableHighEntropy: %d\n\n", aslr.EnableHighEntropy);
    
    // ACG
    PROCESS_MITIGATION_DYNAMIC_CODE_POLICY acg = { 0 };
    GetProcessMitigationPolicy(GetCurrentProcess(), ProcessDynamicCodePolicy, &acg, sizeof(acg));
    printf("[ACG]\n");
    printf("  ProhibitDynamicCode: %d\n\n", acg.ProhibitDynamicCode);
    
    // CFG
    PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY cfg = { 0 };
    GetProcessMitigationPolicy(GetCurrentProcess(), ProcessControlFlowGuardPolicy, &cfg, sizeof(cfg));
    printf("[CFG]\n");
    printf("  EnableControlFlowGuard: %d\n", cfg.EnableControlFlowGuard);
    printf("  StrictMode: %d\n\n", cfg.StrictMode);
}

int main() {
    CheckAllSecurityPolicies();
    return 0;
}

8. 课后作业

8.1、作业1:检测工具(必做)

编写程序检测当前系统和进程的安全策略状态。

8.2、作业2:DEP绕过(必做)

在启用DEP的情况下,使用VirtualProtect执行ShellCode。

8.3、作业3:ACG测试(进阶)

创建启用ACG的进程,测试哪些ShellCode执行方法失效。


9. 下一课预告

下一课我们将学习父进程伪装技术,隐藏进程的真实来源。