像 Java 是由 JVM 托管的,.NET 程序集(比如C_Sharp.exe) 都是由 CLR 托管的
硬盘加载
从硬盘中读取加载到内存
通过三个接口可以启动 CLR 来对 .NET 程序集 进行硬盘加载
Copy
ICLRMetaHost 接口
ICLRRuntimeInfo 接口
ICLRRuntimeHost 接口
Program.cs:
Copy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace C_Sharp {
class Program {
static void Main(string[] args) {
}
static int print(String strings) {
Console.WriteLine(strings);
return 1;
}
}
}
x64.cpp:
Copy
#include
#include
#pragma comment(lib, "mscoree.lib")
int main() {
// 创建 ICLRMetaHost对象
ICLRMetaHost* metaHost = NULL;
CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost);
// 获取枚举器
IEnumUnknown* runtime = NULL;
metaHost->EnumerateInstalledRuntimes(&runtime);
// 枚举安装的CLR
IUnknown* enumRuntime = NULL;
ICLRRuntimeInfo* runtimeInfo = NULL;
DWORD bytes = 2048;
LPWSTR frameworkName = (LPWSTR)LocalAlloc(LPTR, 2048);
while (runtime->Next(1, &enumRuntime, 0) == S_OK) {
if (enumRuntime->QueryInterface(&runtimeInfo) == S_OK) {
if (runtimeInfo != NULL) {
runtimeInfo->GetVersionString(frameworkName, &bytes);
}
}
}
ICLRRuntimeHost* runtimeHost = NULL;
runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost);
runtimeHost->Start(); // 启动CLR
runtimeHost->ExecuteInDefaultAppDomain(L"C_Sharp.exe", L"C_Sharp.Program", L"print", L"hacker", NULL);
return 0;
}
内存加载
直接从内存加载,不需要从硬盘读取
CS 新启进程,内存加载 .NET 程序集:
Copy
execute-assembly d:\net.exe
一种事件追踪机制,会检测 .NET 程序集
检测示例
Program.cs:
Copy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace C_Sharp {
class Program {
static void Main(string[] args) {
Console.WriteLine("hacker");
}
}
}
CS 当前进程,内存加载 .NET 程序集:
inject-assembly插件,GitHub - kyleavery/inject-assembly: Inject .NET assemblies into an existing process
Copy
inject-assembly 0 D:\C_Sharp.exe
此时用 Process Hacker 可以看到多出来一些 .NET 信息
还可以看到启动了CLR(clr.dll)
绕过
CLR 会通过 ntdll 的 EtwEventWrite函数 公开信息
BOF-patchit插件,GitHub - ScriptIdiot/BOF-patchit: An all-in-one Cobalt Strike BOF to patch, check and revert AMSI and ETW for x64 process. Both syscalls and dynamic resolve versions are available
.
Copy
patchit etw
再查看 .NET 信息,发现获取不到了
原理
对木马进行调试
使用插件前的 EtwEventWrite函数
使用插件后的 EtwEventWrite函数
发现开头变成了ret,使函数无法被正常调用
至于后面为什么变成 mov ebx, esp,看硬编码就知道了