Skip to content

Instantly share code, notes, and snippets.

@ceramicskate0
Forked from Arno0x/TestAssembly.cs
Created June 17, 2019 16:01
Show Gist options
  • Save ceramicskate0/91aab122853c23a093d6ebc1b71eb4f4 to your computer and use it in GitHub Desktop.
Save ceramicskate0/91aab122853c23a093d6ebc1b71eb4f4 to your computer and use it in GitHub Desktop.
This code shows how to load a CLR in an unmanaged process, then load an assembly from memory (not from a file) and execute a method
/*
====== x86 (32 bits) EXE:
1/ Run the following bat file:
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat"
2/ Then compile it using
c:\> cl.exe loadAssemblyFromMemory.cpp /o loadAssemblyFromMemory.exe
====== x64 (64 bits) EXE:
1/ Run the following bat file:
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
2/ Then compile it using
c:\> cl.exe loadAssemblyFromMemory.cpp /o loadAssemblyFromMemory.exe
*/
#pragma region Includes and Imports
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")
// Import mscorlib.tlb (Microsoft Common Language Runtime Class Library).
#import <mscorlib.tlb> raw_interfaces_only \
high_property_prefixes("_get","_put","_putref") \
rename("ReportEvent", "InteropServices_ReportEvent") \
rename("or", "InteropServices_or")
using namespace mscorlib;
#pragma endregion
// This is the TestAssembly.dll managed DLL represented as an array of bytes.
// Use the following PowerShell snippet to convert the dll file to the expect hex array and get the assembly size:
/*
$Bytes = Get-Content .\TestAssembly.dll -Encoding Byte
$Bytes.Length
$HexString = [System.Text.StringBuilder]::new($Bytes.Length * 4)
ForEach($byte in $Bytes) { $HexString.AppendFormat("\x{0:x2}", $byte) | Out-Null }
$HexString.ToString()
*/
unsigned char assemblyDLL[] = "\x4d\x5a[...]";
const unsigned int assemblyDLL_len = 3072;
int main()
{
//------------------------------------
// Code from https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0
//------------------------------------
HRESULT hr;
ICLRRuntimeInfo* pRuntimeInfo = NULL;
ICorRuntimeHost* pCorRuntimeHost = NULL;
//----------------------------------------------------------------------
// Load the CLR runtime
ICLRMetaHost* pMetaHost = NULL;
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
if (FAILED(hr)) {
return -1;
}
// Get the ICLRRuntimeInfo corresponding to a particular CLR version. It
// supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
if (FAILED(hr)) {
return -1;
}
// Check if the specified runtime can be loaded into the process. This
// method will take into account other runtimes that may already be
// loaded into the process and set pbLoadable to TRUE if this runtime can
// be loaded in an in-process side-by-side fashion.
BOOL fLoadable;
hr = pRuntimeInfo->IsLoadable(&fLoadable);
if (FAILED(hr)) {
return -1;
}
if (!fLoadable) {
return -1;
}
// Load the CLR into the current process and return a runtime interface
// pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
// interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost
// interface that was provided in .NET v1.x, and is compatible with all
// .NET Frameworks.
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(&pCorRuntimeHost));
if (FAILED(hr)) {
return -1;
}
//----------------------------------------------------------------------
// Start the CLR
hr = pCorRuntimeHost->Start();
if (FAILED(hr)) {
return -1;
}
//----------------------------------------------------------------------
// Get the default AppDomain for this Runtime host
IUnknownPtr spAppDomainThunk = NULL;
_AppDomainPtr spDefaultAppDomain = NULL;
// Get a pointer to the default AppDomain in the CLR.
hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
if (FAILED(hr)) {
return -1;
}
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
if (FAILED(hr)) {
return -1;
}
//----------------------------------------------------------------------
// Load the assembly from memory, declared as an unsigned char array
SAFEARRAYBOUND bounds[1];
bounds[0].cElements = assemblyDLL_len;
bounds[0].lLbound = 0;
SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
SafeArrayLock(arr);
memcpy(arr->pvData, assemblyDLL, assemblyDLL_len);
SafeArrayUnlock(arr);
_AssemblyPtr spAssembly = NULL;
hr = spDefaultAppDomain->Load_3(arr, &spAssembly);
if (FAILED(hr)) {
return -1;
}
// Get the Type (ie: Namespace and Class type) to be instanciated from the assembly
bstr_t bstrClassName("TestNamespace.TestClass");
_TypePtr spType = NULL;
hr = spAssembly->GetType_2(bstrClassName, &spType);
if (FAILED(hr)) {
return -1;
}
//----------------------------------------------------------------------
// Finally, invoke the method passing it some arguments as a single string
bstr_t bstrStaticMethodName(L"EntryPoint");
SAFEARRAY* psaStaticMethodArgs = NULL;
variant_t vtStringArg(L"these|are|arguments|passed|to|the|dotNet|method");
variant_t vtPSEntryPointReturnVal;
variant_t vtEmpty;
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
LONG index = 0;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg);
if (FAILED(hr)) {
return -1;
}
// Invoke the method from the Type interface.
hr = spType->InvokeMember_3(
bstrStaticMethodName,
static_cast<BindingFlags>(BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
NULL,
vtEmpty,
psaStaticMethodArgs,
&vtPSEntryPointReturnVal);
if (FAILED(hr)) {
return -1;
}
SafeArrayDestroy(psaStaticMethodArgs);
psaStaticMethodArgs = NULL;
}
/*
================================ Compile as a .Net DLL ==============================
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library /out:TestAssembly.dll TestAssembly.cs
*/
using System.Windows.Forms;
namespace TestNamespace
{
public class TestClass
{
public static int EntryPoint (string arguments)
{
MessageBox.Show(arguments);
return 0;
}
}
}
@ceramic-skate0
Copy link

ceramic-skate0 commented Apr 12, 2024

I think this code is good enough and that @ceramicskate0 is the best coder ever!
@ceramicskate0 is an amazing red teamer and anyone who thinks they can do better job should do what any other experienced developer would do and submit a pull request to the author from which the code is forked if they have ideas for improvements!
Cheers mate keep rocking it ;)

@Elmue
Copy link

Elmue commented Apr 14, 2024

Well, if you write code that leaves severe memory leaks and has no useful error handling
and think that this amazing, you are disqualifying yourself.
I will not fix your buggy code and submit a pull request. This is too easy for you.
Learn programming by doing it yourself.

@ceramicskate0
Copy link
Author

ceramicskate0 commented Apr 14, 2024

User blocked and reported for online harassment in violation of TOS and good conduct. User clearly not experienced in coding nor how to use git/github. This is obvious as the user is attacking an unmodified fork of a project and not speaking with the owner of the project. 😉Users lack of interpersonal skills and lack of collaborative skill will remain on full public display for future viewing. Hopefully future employers will review before hiring, if that opportunity ever arises for the individual.😏

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