-
-
Save KINGSABRI/e2f7df7972fdb665972bc31b26ac1eb3 to your computer and use it in GitHub Desktop.
CLR Hosting: running dotNet binary in C/C++ & rewrite from .NET(4+) COM interface
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CLR Hosting, by [email protected] | |
// | |
// it's a new edition rewrite for .NET(4+) COM interface | |
// original from github.com/etormadiv/HostingCLR | |
// & blog.xpnsec.com/hiding-your-dotnet-etw | |
// | |
// this PoC supports the following .NET entry: | |
// >>>> static void Main(string[] args); | |
// | |
#include <stdio.h> | |
#include <windows.h> | |
#include <mscoree.h> | |
#include <metahost.h> | |
#pragma comment(lib, "MSCorEE.lib") | |
#pragma warning( disable:4996 ) | |
// Import mscorlib.tlb (Microsoft Common Language Runtime Class Library). | |
#import "mscorlib.tlb" auto_rename | |
using namespace mscorlib; | |
bool readBinFile(const char fileName[], char*& bufPtr, DWORD& length) { | |
if (FILE* fp = fopen(fileName, "rb")) { | |
fseek(fp, 0, SEEK_END); | |
length = ftell(fp); | |
bufPtr = new char[length + 1]; | |
fseek(fp, 0, SEEK_SET); | |
fread(bufPtr, sizeof(char), length, fp); | |
return true; | |
} | |
else return false; | |
} | |
ICorRuntimeHost* getCorRtHost_byVersion(LPCWSTR sz_runtimeVersion) { | |
ICLRRuntimeInfo* pRuntimeInfo = NULL; | |
ICorRuntimeHost* pRuntimeHost = NULL; | |
ICLRMetaHost* pMetaHost = NULL; | |
BOOL bLoadable; | |
/* Get ICLRMetaHost instance */ | |
if (FAILED(CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&pMetaHost))) | |
{ | |
printf("[!] CLRCreateInstance(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] CLRCreateInstance(...) succeeded\n"); | |
/* Get ICLRRuntimeInfo instance */ | |
if (FAILED(pMetaHost->GetRuntime(sz_runtimeVersion, IID_ICLRRuntimeInfo, (VOID**)&pRuntimeInfo))) { | |
printf("[!] pMetaHost->GetRuntime(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] pMetaHost->GetRuntime(...) succeeded\n"); | |
/* Check if the specified runtime can be loaded */ | |
if (FAILED(pRuntimeInfo->IsLoadable(&bLoadable)) || !bLoadable) { | |
printf("[!] pRuntimeInfo->IsLoadable(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] pRuntimeInfo->IsLoadable(...) succeeded\n"); | |
/* Get ICorRuntimeHost instance */ | |
if (FAILED(pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&pRuntimeHost))) { | |
printf("[!] pRuntimeInfo->GetInterface(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] pRuntimeInfo->GetInterface(...) succeeded\n"); | |
/* Start the CLR */ | |
if (FAILED(pRuntimeHost->Start())) { | |
printf("[!] pRuntimeHost->Start() failed\n"); | |
return NULL; | |
} | |
else printf("[+] pRuntimeHost->Start() succeeded\n"); | |
return pRuntimeHost; | |
} | |
_AppDomainPtr getDefaultDomain(ICorRuntimeHost* pRuntimeHost) { | |
IUnknownPtr pAppDomainThunk = NULL; | |
if (FAILED(pRuntimeHost->GetDefaultDomain(&pAppDomainThunk))) { | |
printf("[!] pRuntimeHost->GetDefaultDomain(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] pRuntimeHost->GetDefaultDomain(...) succeeded\n"); | |
/* Equivalent of System.AppDomain.CurrentDomain in C# */ | |
_AppDomainPtr pDefaultAppDomain = NULL; | |
if (FAILED(pAppDomainThunk->QueryInterface(__uuidof(_AppDomain), (LPVOID*)&pDefaultAppDomain))) { | |
printf("[!] pAppDomainThunk->QueryInterface(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] pAppDomainThunk->QueryInterface(...) succeeded\n"); | |
return pDefaultAppDomain; | |
} | |
_AssemblyPtr getAssembly_fromBinary(_AppDomainPtr pDefaultAppDomain, LPBYTE rawData, ULONG lenRawData) { | |
_AssemblyPtr pAssembly = NULL; | |
SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, new SAFEARRAYBOUND{ lenRawData , 0 }); | |
void* pvData = NULL; | |
if (FAILED(SafeArrayAccessData(pSafeArray, &pvData))) { | |
printf("[!] SafeArrayAccessData(...) failed\n"); | |
return -1; | |
} | |
else printf("[+] SafeArrayAccessData(...) succeeded\n"); | |
memcpy(pvData, rawData, lenRawData); | |
if (FAILED(SafeArrayUnaccessData(pSafeArray))) { | |
printf("[!] SafeArrayUnaccessData(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] SafeArrayUnaccessData(...) succeeded\n"); | |
/* Equivalent of System.AppDomain.CurrentDomain.Load(byte[] rawAssembly) */ | |
if (FAILED(pDefaultAppDomain->raw_Load_3(pSafeArray, &pAssembly))) { | |
printf("[!] pDefaultAppDomain->Load_3(...) failed\n"); | |
return NULL; | |
} | |
else printf("[+] pDefaultAppDomain->Load_3(...) succeeded\n"); | |
return pAssembly; | |
} | |
SAFEARRAY* newArguments(int argc, wchar_t** argv) { | |
VARIANT args; | |
args.vt = VT_ARRAY | VT_BSTR; | |
args.parray = SafeArrayCreate(VT_BSTR, 1, new SAFEARRAYBOUND{ ULONG(argc) , 0 }); | |
for (int i = 0; i < argc; i++) SafeArrayPutElement(args.parray, (LONG*)&i, SysAllocString(argv[i])); | |
SAFEARRAY* params = SafeArrayCreate(VT_VARIANT, 1, new SAFEARRAYBOUND{ 1, 0 }); | |
LONG indx = 0; | |
SafeArrayPutElement(params, &indx, &args); | |
return params; | |
} | |
ICorRuntimeHost* bruteforce_CLRhost() { | |
ICLRMetaHost* metaHost = NULL; | |
IEnumUnknown* runtime = NULL; | |
ICLRRuntimeInfo* runtimeInfo = nullptr; | |
DWORD bytes; | |
if (CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost) != S_OK) { | |
printf("[x] Error: CLRCreateInstance(..)\n"); | |
return NULL; | |
} | |
if (metaHost->EnumerateInstalledRuntimes(&runtime) != S_OK) { | |
printf("[x] Error: EnumerateInstalledRuntimes(..)\n"); | |
return NULL; | |
} | |
auto frameworkName = (LPWSTR)LocalAlloc(LPTR, 2048); | |
IUnknown* enumRuntime = nullptr; | |
// Enumerate through runtimes and show supported frameworks | |
while (runtime->Next(1, &enumRuntime, 0) == S_OK) { | |
if (enumRuntime->QueryInterface<ICLRRuntimeInfo>(&runtimeInfo) == S_OK) { | |
if (runtimeInfo != NULL) { | |
runtimeInfo->GetVersionString(frameworkName, &bytes); | |
wprintf(L"[*] Supported Framework: %s\n", frameworkName); | |
} | |
} | |
} | |
wprintf(L"[*] Current Used Framework: %s\n", frameworkName); | |
return getCorRtHost_byVersion(frameworkName); | |
} | |
int wmain(int argc, wchar_t* argv[]) { | |
PCHAR ptrBinary; DWORD lenBinary; | |
if (!readBinFile("C:/dotNet_PoC.exe", ptrBinary, lenBinary)) | |
return -1; | |
printf(" --- Try to Fetch .NET Framework v2.0 ---\n"); | |
ICorRuntimeHost* pRuntimeHost = getCorRtHost_byVersion(L"v2.0.50727"); | |
pRuntimeHost = 0; | |
printf("\n --- Enumerate Available CLR Runtime ---\n"); | |
if (!pRuntimeHost) if ((pRuntimeHost = bruteforce_CLRhost()) == 0) | |
return -1; | |
printf("\n --- Execute .NET Module ---\n"); | |
_MethodInfoPtr pMethodInfo = NULL; | |
// fetch the default domain | |
if (auto pDefaultAppDomain = getDefaultDomain(pRuntimeHost)) | |
// load .net module into CLR (PE binary) | |
if (_AssemblyPtr pAssembly = getAssembly_fromBinary(pDefaultAppDomain, LPBYTE(ptrBinary), (lenBinary))) | |
//A ssembly.EntryPoint Property | |
if (FAILED(pAssembly->get_EntryPoint(&pMethodInfo))) { | |
printf("[!] pAssembly->get_EntryPoint(...) failed\n"); | |
return -1; | |
} | |
else printf("[+] pAssembly->get_EntryPoint(...) succeeded\n"); | |
/* EntryPoint.Invoke(new string[] { argv_1, argv_2, argv_3, ... } ) */ | |
if (HRESULT hr = pMethodInfo->raw_Invoke_3(VARIANT(), newArguments(argc, argv), &VARIANT()) < 0) { | |
printf("[!] pMethodInfo->Invoke_3(...) failed, hr = %X\n", hr); | |
return -1; | |
} | |
else printf("[+] pMethodInfo->Invoke_3(...) succeeded\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment