原理
在《Cobalt Strike 原理分析》一文中,介绍了内存加载程序集(Assembly)的主要有四步:
1 加载CLR环境 2 获取程序域 3 装载程序集 4 执行程序集
在odzhan的Shellcode: Loading .NET Assemblies From Memory所描述的那样,.Net Framework随着版本的更新,使用了不同的接口,.Net Framework V1.0 采用的是ICorRuntimeHost接口
,支持v1.0.3705, v1.1.4322, v2.0.50727和v4.0.30319。到了.Net Framework v2.0,采用ICLRRuntimeHost接口
,支持v2.0.50727和v4.0.30319。然后到了.Net Framework v4.0,则使用了ICLRMetaHost接口
,但是可能不再兼容4.0以下的.Net Framework。所以使用ICLRMetaHost接口
并不是一个非常合适的接口。
我们可以使用多个函数进行接口的实例化,最常见的可能属CoCreateInstance
或者CLRCreateInstance
。
剩下的关于获取程序域
,装载程序集
,以及执行程序集
在Execute-Assembly实现都有具体实现。完整代码如下。
#include <stdio.h>
#include <tchar.h>
#include <metahost.h>
//
#import "mscorlib.tlb" raw_interfaces_only \
high_property_prefixes("_get","_put","_putref") \
rename("ReportEvent", "InteropServices_ReportEvent") \
rename("or", "InteropServices_or")
using namespace mscorlib;
//
#pragma comment(lib, "MSCorEE.lib")
//
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hFile = CreateFileA("CSharp.exe",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (NULL == hFile)
{
return 0;
}
DWORD dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize == 0)
{
return 0;
}
PVOID dotnetRaw = malloc(dwFileSize);
memset(dotnetRaw, 0, dwFileSize);
DWORD dwReturn = 0;
if (ReadFile(hFile, dotnetRaw, dwFileSize, &dwReturn, NULL)==FALSE)
{
return 0;
}
//
ICLRMetaHost* iMetaHost = NULL;
ICLRRuntimeInfo* iRuntimeInfo = NULL;
ICorRuntimeHost* iRuntimeHost = NULL;
IUnknownPtr pAppDomain = NULL;
_AppDomainPtr pDefaultAppDomain = NULL;
_AssemblyPtr pAssembly = NULL;
_MethodInfoPtr pMethodInfo = NULL;
SAFEARRAYBOUND saBound[1];
void* pData = NULL;
VARIANT vRet;
VARIANT vObj;
VARIANT vPsa;
SAFEARRAY* args = NULL;
//
//检测点1
CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&iMetaHost);
iMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (VOID**)&iRuntimeInfo);
iRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&iRuntimeHost);
iRuntimeHost->Start();
//
iRuntimeHost->GetDefaultDomain(&pAppDomain);
pAppDomain->QueryInterface(__uuidof(_AppDomain), (VOID**)&pDefaultAppDomain);
//
saBound[0].cElements = dwFileSize;
saBound[0].lLbound = 0;
SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, saBound);
//
SafeArrayAccessData(pSafeArray, &pData);
memcpy(pData, dotnetRaw, dwFileSize);
//free(dotnetRaw); //释放1
SafeArrayUnaccessData(pSafeArray);
//
//检测点2
pDefaultAppDomain->Load_3(pSafeArray, &pAssembly);
//free(pSafeArray->pvData);
pAssembly->get_EntryPoint(&pMethodInfo);
ZeroMemory(&vRet, sizeof(VARIANT));
ZeroMemory(&vObj, sizeof(VARIANT));
vObj.vt = VT_NULL;
vPsa.vt = (VT_ARRAY | VT_BSTR);
args = SafeArrayCreateVector(VT_VARIANT, 0, 1);
if (argc > 1)
{
vPsa.parray = SafeArrayCreateVector(VT_BSTR, 0, argc);
for (long i = 0; i < argc; i++)
{
SafeArrayPutElement(vPsa.parray, &i, SysAllocString((OLECHAR*)argv[i]));
}
long idx[1] = { 0 };
SafeArrayPutElement(args, idx, &vPsa);
}
//检测点3
HRESULT hr = pMethodInfo->Invoke_3(vObj, args, &vRet);
pMethodInfo->Release();
pAssembly->Release();
pDefaultAppDomain->Release();
iRuntimeInfo->Release();
iMetaHost->Release();
CoUninitialize();
getchar();
return 0;
};