execute-assembly实现原理(非托管C++代码调用C#)
作者: 裤衩哥的小屋-8sec 更新时间:2020-05-16 03:00:54 原文链接
execute-assembly实现原理(非托管C++代码调用C#)
cs中新加了一个功能,直接加载C#编译的PE文件: execute-assembly
命令
看了下3gstudent大佬的文章,然后简单学习了下这个的原理,三好学生使用ICLRMetaHost::GetRuntime获取有效的ICLRRuntimeInfo指针。这里我找到了另一个方法就是ExecuteInDefaultAppDomain来调用Assembly方法
- execute-assembly实现原理(非托管C++代码调用C#)
-
- ICLRRuntimeHost::ExecuteInDefaultAppDomain 方法
- ICLRMetaHost::GetRuntime方法
-
实现原理
ICLRRuntimeHost::ExecuteInDefaultAppDomain 方法
演示代码其实就是非托管 C++ 代码调用 C# dll
实现的功能和上一个 Assembly Load
学习中实现的差不多。
在非托管代码中手动启动 CLR 加载应用程序域来运行托管的 dll,从而调用其中的方法。
利用 Unmanaged API
中的 ICLRRuntimeHost
接口,啟用 Unmanaged 主應用程式,將 Common Language Runtime 載入處理序 (Process)。
#include <Windows.h> #include <MSCorEE.h> #include <stdio.h> #include <metahost.h> using namespace std; #pragma comment(lib, "mscoree.lib") int main(int argc) { ICLRMetaHost *pMetaHost = nullptr; ICLRMetaHostPolicy *pMetaHostPolicy = nullptr; ICLRRuntimeHost *pRuntimeHost = nullptr; ICLRRuntimeInfo *pRuntimeInfo = nullptr; HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost); hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo)); if (FAILED(hr)) { wprintf(L"failed to call csharp dll.\n"); } hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pRuntimeHost)); hr = pRuntimeHost->Start(); DWORD dwRet = 0; hr = pRuntimeHost->ExecuteInDefaultAppDomain(L"ClassLibrary1.dll", //不会产生新的进程 L"DllDemo1.Aaaaa1", L"Bbbbb", L"aaa", &dwRet); hr = pRuntimeHost->Stop(); }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DllDemo1 { public class Aaaaa1 { public static int Bbbbb(string str) { System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe"; p.Start(); return 0; } } }
测试可以加载exe运行。
以下是3gstudent文章演示代码:
ICLRMetaHost::GetRuntime方法
使用ICLRMetaHost::GetRuntime获取有效的ICLRRuntimeInfo指针。
#include "stdafx.h" #include <metahost.h> #include <windows.h> #pragma comment(lib, "MSCorEE.lib") HRESULT RuntimeHost_GetRuntime_ICLRRuntimeInfo(PCWSTR pszVersion, PCWSTR pszAssemblyName, PCWSTR pszClassName, PCWSTR pszMethodName, PCWSTR pszArgName) { HRESULT hr; ICLRMetaHost *pMetaHost = NULL; ICLRRuntimeInfo *pRuntimeInfo = NULL; ICLRRuntimeHost *pClrRuntimeHost = NULL; DWORD dwLengthRet; wprintf(L"Load and start the .NET runtime %s \n", pszVersion); hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)); if (FAILED(hr)) { wprintf(L"[!]CLRCreateInstance failed w/hr 0x%08lx\n", hr); goto Cleanup; } hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo)); if (FAILED(hr)) { wprintf(L"[!]ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr); goto Cleanup; } BOOL fLoadable; hr = pRuntimeInfo->IsLoadable(&fLoadable); if (FAILED(hr)) { wprintf(L"[!]ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr); goto Cleanup; } if (!fLoadable) { wprintf(L"[!].NET runtime %s cannot be loaded\n", pszVersion); goto Cleanup; } hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pClrRuntimeHost)); if (FAILED(hr)) { wprintf(L"[!]ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr); goto Cleanup; } hr = pClrRuntimeHost->Start(); if (FAILED(hr)) { wprintf(L"[!]CLR failed to start w/hr 0x%08lx\n", hr); goto Cleanup; } wprintf(L"[+]Load the assembly %s\n", pszAssemblyName); hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyName, pszClassName, pszMethodName, pszArgName, &dwLengthRet); if (FAILED(hr)) { wprintf(L"[!]Failed to call %s w/hr 0x%08lx\n", pszMethodName, hr); goto Cleanup; } wprintf(L"[+]Call %s.%s(\"%s\") => %d\n", pszClassName, pszMethodName, pszArgName, dwLengthRet); Cleanup: if (pMetaHost) { pMetaHost->Release(); pMetaHost = NULL; } if (pRuntimeInfo) { pRuntimeInfo->Release(); pRuntimeInfo = NULL; } if (pClrRuntimeHost) { pClrRuntimeHost->Release(); pClrRuntimeHost = NULL; } return hr; } int main() { RuntimeHost_GetRuntime_ICLRRuntimeInfo(L"v4.0.30319", L"ClassLibrary1.dll", L"ClassLibrary1.Class1", L"TestMethod", L"argstring"); return 0; }
代码将会加载同级目录下.Net4.0开发的ClassLibrary1.dll,类名为Class1,方法为TestMethod,传入的参数为argstring
ClassLibrary1.dll的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ClassLibrary1 { public class Class1 { public static int TestMethod(string str) { System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = "c:\\windows\\system32\\calc.exe"; p.Start(); return 0; } } }
以后会分析以下一些开源的Assembly loader工具,以上两个方法都是在硬盘中加载,而根据三好学生大佬的总结
execute-assembly通常有以下两种利用思路:
1.从内存中读取shellcode并加载.NET程序集
2.从硬盘读取并加载.NET程序集
第一种利用思路要优于第二种,完整的利用过程如下:
创建一个正常的进程
通过Dll反射向进程注入dll
dll实现从内存中读取shellcode并加载最终的.NET程序集
优点如下:
整个过程在内存执行,不写入文件系统
Payload以dll形式存在,不会产生可疑的进程
最终的Payload为C#程序,现有的Powershell利用脚本转换为C#代码很方便