For the past few days, I've been trying to run a .NET executable inside an unmanaged executable made in C++.
I made a simple MessageBox program to test if my code worked, and indeed the application ran fine:
using System;
using System.Windows.Forms;
namespace Test2Lol
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
MessageBox.Show("I am alive!", "Hello!", 0, MessageBoxIcon.Information);
}
}
}
The application works without throwing any exceptions, here's the output log of the unmanaged application running the managed one. The latter's shellcode is inside the unmanaged application.
[main][116] Phase 3: Loading data...
[MDLL_OpenProcess][75] Process opened sucessfully.
[MDLL_VirtualAlloc][40] Sucessfully allocated memory.
[DLL_RunImageDotNet][662] Loading .NET application: BaseAddress 0x04980000, Size: 6656.
[DLL_RunImageDotNet][671] Done 6655 cycles.
[DLL_RunImageDotNet][688] [+] ICLRMetaHost: CLRCreateInstance(...) succeeded
[DLL_RunImageDotNet][701] [+] ICLRMetaHostPolicy: CLRCreateInstance(...) succeeded
[DLL_RunImageDotNet][714] [+] ICLRDebugging: CLRCreateInstance(...) succeeded
[DLL_RunImageDotNet][729] [+] pMetaHost->GetRuntime(...) succeeded
[DLL_RunImageDotNet][744] [+] pRuntimeInfo->IsLoadable(...) succeeded
[DLL_RunImageDotNet][759] [+] pRuntimeInfo->GetInterface(...) succeeded
[DLL_RunImageDotNet][772] [+] pRuntimeHost->Start() succeeded
[DLL_RunImageDotNet][785] [+] pRuntimeHost->GetDefaultDomain(...) succeeded
[DLL_RunImageDotNet][800] [+] pAppDomainThunk->QueryInterface(...) succeeded
[DLL_RunImageDotNet][823] [+] SafeArrayAccessData(...) succeeded
[DLL_RunImageDotNet][836] [+] SafeArrayUnaccessData(...) succeeded
[DLL_RunImageDotNet][838] pSafeArray: 00ABD488, cDims: 1, fFeatures: 128, cbElements: 1, cLocks: 0, pvData: 00AF8C88, cElements 6656, lLBound 0.
[DLL_RunImageDotNet][849] [+] pDefaultAppDomain->Load_3(...) succeeded
[DLL_RunImageDotNet][862] [+] pAssembly->get_EntryPoint(...) succeeded
So, where is the problem you guys might ask? Well, while it works for that very simple messagebox program, it does not work whenever I attempt to load a different application, crashing at Load_3() with the following output log:
[main][116] Phase 3: Loading data...
[MDLL_OpenProcess][75] Process opened sucessfully.
[MDLL_VirtualAlloc][40] Sucessfully allocated memory.
[DLL_RunImageDotNet][662] Loading .NET application: BaseAddress 0x05000000, Size: 514048.
[DLL_RunImageDotNet][671] Done 514047 cycles.
[DLL_RunImageDotNet][688] [+] ICLRMetaHost: CLRCreateInstance(...) succeeded
[DLL_RunImageDotNet][701] [+] ICLRMetaHostPolicy: CLRCreateInstance(...) succeeded
[DLL_RunImageDotNet][714] [+] ICLRDebugging: CLRCreateInstance(...) succeeded
[DLL_RunImageDotNet][729] [+] pMetaHost->GetRuntime(...) succeeded
[DLL_RunImageDotNet][744] [+] pRuntimeInfo->IsLoadable(...) succeeded
[DLL_RunImageDotNet][759] [+] pRuntimeInfo->GetInterface(...) succeeded
[DLL_RunImageDotNet][772] [+] pRuntimeHost->Start() succeeded
[DLL_RunImageDotNet][785] [+] pRuntimeHost->GetDefaultDomain(...) succeeded
[DLL_RunImageDotNet][800] [+] pAppDomainThunk->QueryInterface(...) succeeded
[DLL_RunImageDotNet][823] [+] SafeArrayAccessData(...) succeeded
[DLL_RunImageDotNet][836] [+] SafeArrayUnaccessData(...) succeeded
[DLL_RunImageDotNet][838] pSafeArray: 0137E298, cDims: 1, fFeatures: 128, cbElements: 1, cLocks: 0, pvData: 0137E9D0, cElements 514048, lLBound 0.
code: 0xE06D7363 (0x00000001)
addr: 75D74662
As you guys might notice (and will notice after reading the code below): execution halts at Load_3() throwing an error with code 0xE06D7363 (0x0000001) (E_UNKNOWN?), after I loaded the executable shellcode inside the SafeArray. I also tried with a smaller .NET application (46KB in size) and still no luck. I'm quite lost on why is this happening, by the way here is the code that executes the .NET shellcode:
#include <iostream>
#include <Windows.h>
#include <wchar.h>
#include <TlHelp32.h>
#include <metahost.h>
#pragma comment(lib, "MSCorEE.lib")
#import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb" raw_interfaces_only \
high_property_prefixes("_get","_put","_putref") \
rename("ReportEvent", "InteropServices_ReportEvent") auto_rename
using namespace mscorlib;
int main()
{
// Open the file that needs to be executed
HANDLE FileHandle = CreateFileA("C:\\Users\\MyUsername\\OneDrive\\Documenti\\Desktop\\schifo\\test_net.exe",
GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
DWORD FileSize = GetFileSize(FileHandle, 0);
BYTE* Buffer = (BYTE*)VirtualAlloc(0, FileSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
ReadFile(FileHandle, Buffer, FileSize, 0, 0);
// Load the CLR in the parent process
ICLRMetaHost* pMetaHost = NULL;
HRESULT hr;
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&pMetaHost);
ICLRMetaHostPolicy* pMetaHostPolicy = NULL;
hr = CLRCreateInstance(CLSID_CLRMetaHostPolicy, IID_ICLRMetaHostPolicy, (VOID**)&pMetaHostPolicy);
ICLRDebugging* pCLRDebugging = NULL;
hr = CLRCreateInstance(CLSID_CLRDebugging, IID_ICLRDebugging, (VOID**)&pCLRDebugging);
ICLRRuntimeInfo* pRuntimeInfo = NULL;
hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_ICLRRuntimeInfo, (VOID**)&pRuntimeInfo);
/* Check if the specified runtime can be loaded */
BOOL bLoadable;
hr = pRuntimeInfo->IsLoadable(&bLoadable);
ICorRuntimeHost* pRuntimeHost = NULL;
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&pRuntimeHost);
hr = pRuntimeHost->Start();
IUnknownPtr pAppDomainThunk = NULL;
hr = pRuntimeHost->GetDefaultDomain(&pAppDomainThunk);
_AppDomainPtr pDefaultAppDomain = NULL;
hr = pAppDomainThunk->QueryInterface(__uuidof(_AppDomain), (VOID**) &pDefaultAppDomain);
// This is where everything goes wrong
_AssemblyPtr pAssembly = NULL;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].cElements = FileSize;
rgsabound[0].lLbound = 0;
SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, rgsabound);
void* pvData = NULL;
hr = SafeArrayAccessData(pSafeArray, &pvData);
// Copy the executable in the safearray
memcpy(pvData, Buffer, FileSize);
hr = SafeArrayUnaccessData(pSafeArray);
// the bastard function
hr = pDefaultAppDomain->Load_3(pSafeArray, &pAssembly);
if (FAILED(hr))
printf("Load_3() failed.\n");
// Now I get the entry point and I invoke the main function of the executable
_MethodInfoPtr pMethodInfo = NULL;
hr = pAssembly->get_EntryPoint(&pMethodInfo);
VARIANT retVal;
ZeroMemory(&retVal, sizeof(VARIANT));
VARIANT obj;
ZeroMemory(&obj, sizeof(VARIANT));
obj.vt = VT_NULL;
SAFEARRAY *psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
hr = pMethodInfo->Invoke_3(obj, psaStaticMethodArgs, &retVal);
}
I have no idea on why Load_3() fails whenever I try to load a different executable than normal. My theories are:
Load_3() crashes if I try to load files bigger than a certain amount -> not true because I tried with smaller applications and still no luck, also I saw blogs of people loading shellcode even bigger than 42KB using Load_3() successfully.
The SafeArray is being created in a wrong way -> I double checked the output with the working and not-working application and the SafeArray looks the same.
I am loading the data incorrectly -> Tried tons of different methods, none of them worked.
Switching to /SUBSYSTEM:WINDOWS in the linker options didn't solve anything.
I'm quite out of ideas on why such a thing happens with different executables, and sadly I couldn't find any documentation regarding Load_3() whatsoever.
What may be causing Load_3() to fail with different executables from the one I created?
Thanks to everyone willing to help me out!
User contributions licensed under CC BY-SA 3.0