Skip to content

Instantly share code, notes, and snippets.

@xpn
Created April 11, 2018 21:34
Show Gist options
  • Save xpn/e95a62c6afcf06ede52568fcd8187cc2 to your computer and use it in GitHub Desktop.
Save xpn/e95a62c6afcf06ede52568fcd8187cc2 to your computer and use it in GitHub Desktop.
A quick example showing loading CLR via native code
#include "stdafx.h"
int main()
{
ICLRMetaHost *metaHost = NULL;
IEnumUnknown *runtime = NULL;
ICLRRuntimeInfo *runtimeInfo = NULL;
ICLRRuntimeHost *runtimeHost = NULL;
IUnknown *enumRuntime = NULL;
LPWSTR frameworkName = NULL;
DWORD bytes = 2048, result = 0;
HRESULT hr;
printf("CLR via native code.... @_xpn_\n\n");
if (CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost) != S_OK) {
printf("[x] Error: CLRCreateInstance(..)\n");
return 2;
}
if (metaHost->EnumerateInstalledRuntimes(&runtime) != S_OK) {
printf("[x] Error: EnumerateInstalledRuntimes(..)\n");
return 2;
}
frameworkName = (LPWSTR)LocalAlloc(LPTR, 2048);
if (frameworkName == NULL) {
printf("[x] Error: malloc could not allocate\n");
return 2;
}
// 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);
}
}
}
// For demo, we just use the last supported runtime
if (runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost) != S_OK) {
printf("[x] ..GetInterface(CLSID_CLRRuntimeHost...) failed\n");
return 2;
}
if (runtimeHost == NULL || bytes == 0) {
wprintf(L"[*] Using runtime: %s\n", frameworkName);
}
// Start runtime, and load our assembly
runtimeHost->Start();
printf("[*] ======= Calling .NET Code =======\n\n");
if (runtimeHost->ExecuteInDefaultAppDomain(
L"myassembly.dll",
L"myassembly.Program",
L"test",
L"argtest",
&result
) != S_OK) {
printf("[x] Error: ExecuteInDefaultAppDomain(..) failed\n");
return 2;
}
printf("[*] ======= Done =======\n");
return 0;
}
@pabloko
Copy link

pabloko commented Apr 12, 2018

#include <metahost.h>
#pragma comment(lib, "mscoree.lib")
 
int main()
{
    ICLRMetaHost* metaHost = NULL;
    ICLRRuntimeInfo* runtimeInfo = NULL;
    ICLRRuntimeHost* runtimeHost = NULL;
 
    if (CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&metaHost) == S_OK)
        if (metaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID*)&runtimeInfo) == S_OK)
            if (runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost) == S_OK)
                if (runtimeHost->Start() == S_OK)
          {    
        DWORD pReturnValue;
        runtimeHost->ExecuteInDefaultAppDomain(L"C:\\random.dll", L"dllNamespace.dllClass", L"ShowMsg", L"It works!!", &pReturnValue);
  
        runtimeInfo->Release();
        metaHost->Release();
        runtimeHost->Release();
                }
    return 0;
}

@blue-devil
Copy link

I hate necrobumping but i cannot compile both @xpn 's code and @pabloko 's code. Guys what is your environment for compiling? You are using C not C++ right?
I am using visual studio 2022 and cannot manage to compile!

@blue-devil
Copy link

BTW @pabloko the code you have shared is taken from this web page and it is C++ not C!
https://codingvision.net/calling-a-c-method-from-c-c-native-process
I have compiled and run it without problem.

But I need a similar solution in C.

@pabloko
Copy link

pabloko commented Dec 7, 2022

@blue-devil if you need c compilant code, you can resort to use the CINTERFACE+COBJMACROS from winapi

    ICLRMetaHost *metaHost = NULL;
    ICLRRuntimeInfo *runtimeInfo = NULL;
    ICLRRuntimeHost *runtimeHost = NULL;
    
    if (CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID *)&metaHost) == S_OK)
        if (ICLRMetaHost_GetRuntime(metaHost, L"v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID *)&runtimeInfo) == S_OK)
            if (ICLRRuntimeInfo_GetInterface(runtimeInfo, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID *)&runtimeHost) == S_OK)
                if (ICLRRuntimeHost_Start(runtimeHost) == S_OK)
                {
                    DWORD pReturnValue;
                    ICLRRuntimeHost_ExecuteInDefaultAppDomain(runtimeHost, L"C:\\random.dll", L"dllNamespace.dllClass", L"ShowMsg", L"It works!!", &pReturnValue);

                    IUnknown_Release(runtimeInfo);
                    IUnknown_Release(metaHost);
                    IUnknown_Release(runtimeHost);
                }

@blue-devil
Copy link

@pabloko You made my day, thank you!

#pragma comment(lib, "mscoree.lib")

#include <stdio.h>

#define CINTERFACE
#define COBJMACROS

#include <metahost.h>
#include <Unknwnbase.h>


int main()
{
    ICLRMetaHost* metaHost = NULL;
    ICLRRuntimeInfo* runtimeInfo = NULL;
    ICLRRuntimeHost* runtimeHost = NULL;

    if (CLRCreateInstance(&CLSID_CLRMetaHost, &IID_ICLRMetaHost, (LPVOID*)&metaHost) == S_OK)
        if (ICLRMetaHost_GetRuntime(metaHost,L"v4.0.30319", &IID_ICLRRuntimeInfo, (LPVOID*)&runtimeInfo) == S_OK)
            if (ICLRRuntimeInfo_GetInterface(runtimeInfo, &CLSID_CLRRuntimeHost, &IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost) == S_OK)
                if (ICLRRuntimeHost_Start(runtimeHost) == S_OK)
                {
                    DWORD pReturnValue;
                    ICLRRuntimeHost_ExecuteInDefaultAppDomain(
                        runtimeHost,
                        L"random.dll",
                        L"dllNamespace.dllClass",
                        L"ShowMsg",
                        L"It works!!",
                        &pReturnValue);

                    printf("GetFlag returned: %016X\n", pReturnValue);

                    IUnknown_Release(runtimeInfo);
                    IUnknown_Release(metaHost);
                    IUnknown_Release(runtimeHost);
                }
    return 0;
}

I am using Visual Studio 2022.

@blue-devil
Copy link

@pabloko one more question. "ShowMsg" is returning int and the code above works fine. What if it returns string? I have tried several things but i could not get any solution?

@pabloko
Copy link

pabloko commented Dec 8, 2022

@blue-devil if you check the documentation for ICLRRuntimeHost::ExecuteInDefaultAppDomain:

The invoked method must have the following signature:

static int pwzMethodName (String pwzArgument)

So this method is a convenient helper to launch a typical main entry point.

If you want to be able to bind both languages you should use ICLRRuntimeHost::SetHostControl and create your own implementation of IHostControl that exposes an interface that can be used in managed code, create a managed AppDomainManager that also implements such interface, then obtain the ICLRControl and set the AppDomainManager managed to back your unmanaged interface. Theres a tutorial you can follow here:
https://www.mode19.net/posts/clrhostingright/

This may sound a bit complicated but it works. If youre just looking to comunicate between managed and unmanaged code, check out UnamanagedExports nuget package, wich allows you to generate native dll libraries from managed code, wich lowers the complexity of this process by a magnitude.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment