Unmanaged PowerShell

简介

在渗透测试当中经常会使用到PowerShell来执行脚本, 但是直接使用PowerShell.exe是一个非常敏感的行为, EDR等产品对PowerShell.exe进程的创建监控的很密切, 并且随着PowerShell的渗透测试工具的普及, 越来越多的EDR会利用微软提供的AMSI接口对PS脚本进行扫描, 但是对于低版本的PowerShell并没有引入AMSI;
如果我们可以自己实现一个PowerShell, 而不是去调用系统的PowerShell.exe来执行PS脚本, 就会使得我们的行为更加的隐蔽, 甚至我们可以将自实现的PowerShell模块注入到一个第三方进程中去(例如svchost.exe), 可能会使行为更加隐蔽;

通过C#实现PS调用

Powershell实际上是属于C#的子集(System.Management.Automation), 所以实际上我们在C#中调用Powershell就是调用 System.Management.Automation对象:

using System;
using System.Reflection;
using System.Text;
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;

namespace Test
{
    class Program
    {
        public static void Main(string[] args)
        {
            InvokePS("PS> ");
        }

        public static int InvokePS(string ps)
        {
            Runspace runspace = RunspaceFactory.CreateRunspace();
            runspace.Open();
            while (true)
            {
                try
                {
                    Console.Write(ps);
                    string cmd = Console.ReadLine();
                    if (cmd.Contains("exit"))
                    {
                        break;
                    }
                    Pipeline pipeline = runspace.CreatePipeline();
                    pipeline.Commands.AddScript(cmd);
                    pipeline.Commands.Add("Out-String");
                    Collection<PSObject> results = pipeline.Invoke();
                    StringBuilder stringBuilder = new StringBuilder();
                    foreach (PSObject obj in results)
                    {
                        foreach (string line in obj.ToString().Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None))
                        {
                            stringBuilder.AppendLine(line.TrimEnd());
                        }
                    }
                    Console.Write(stringBuilder.ToString());
                }
                catch (Exception e)
                {
                    string errorText = e.Message + "\n";
                    Console.Write(errorText);
                }
            }
            return 0;
        }
    }
}

c#_ps
需要注意的是在引用System.Management.Automation时, 需要手动找System.Management.Automation.dll的路径, 然后添加到引用中去:
System.Management.Automation.dll
有了Test.exe我们就可以执行PowerShell命令了, 同时我们可以以反射的形式调用InvokePS函数, 我们把Test.exe文件进行base64编码, 然后通过Assembly.Load的形式反射加载调用InvokePS函数:

using System;
using System.Reflection;
using System.Text;
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;

namespace Test
{
    class Program
    {
        public static void Main(string[] args)
        {
            string base64str = "TVqQAAMAAAAEAAAA...."; // Test.exe文件的base64编码
            byte[] buffer = Convert.FromBase64String(base64str);
            Assembly assembly = Assembly.Load(buffer);
            Type type = assembly.GetType("Test.Program");
            MethodInfo method = type.GetMethod("InvokePS");
            Object obj = assembly.CreateInstance(method.Name);
            //object[] methodArgs = new object[] { new string[] { } };
            string PS = "PS> ";
            object[] methodArgs = new object[] { PS };
            method.Invoke(obj, methodArgs);
        }
    }
}

C Native Call PowerShell

M1

上面我们通过C#代码加载了Test.exe然后调用了InvokePS函数来执行PS命令, 同样我们可以利用C/C++来自己构造CLR运行时, 然后通过"反射"的方式来执行InvokePS函数:

#include <iostream>
#include <windows.h>
#include <mscoree.h>
#include <metahost.h>
#include <string>
#include <comdef.h>
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")
using namespace std;

LPCWSTR NetVersion = L"v2.0.50727"; // L"v2.0.50727"  "v4.0.30319"

int main()
{
	// 初始化COM
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	if (FAILED(hr))
	{
		cout << "CoInitializeEx Error: " << std::hex << std::showbase << hr << endl;
		return hr;
	}

	// 创建CLR运行时宿主
	ICLRMetaHost* pMetaHost = NULL;
	hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
	if (FAILED(hr))
	{
		cout << "CLRCreateInstance Error: " << std::hex << std::showbase << hr << endl;
		CoUninitialize();
		return hr;
	}

	// 设置CLR版本
	ICLRRuntimeInfo* pRuntimeInfo = NULL;
	hr = pMetaHost->GetRuntime(NetVersion, IID_ICLRRuntimeInfo, (LPVOID*)&pRuntimeInfo);
	if (FAILED(hr))
	{
		cout << "GetRuntime Error: " << std::hex << std::showbase << hr << endl;
		pMetaHost->Release();
		CoUninitialize();
		return hr;
	}

	// GetInterface
	ICLRRuntimeHost* pClrRuntimeHost = NULL;
	hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&pClrRuntimeHost);
	if (FAILED(hr))
	{
		cout << "GetInterface Error: " << std::hex << std::showbase << hr << endl;
		pRuntimeInfo->Release();
		pMetaHost->Release();
		CoUninitialize();
		return hr;
	}

	// 启动CLR
	hr = pClrRuntimeHost->Start();
	if (FAILED(hr))
	{
		cout << "pClrRuntimeHost Error: " << std::hex << std::showbase << hr << endl;
		pClrRuntimeHost->Release();
		pRuntimeInfo->Release();
		pMetaHost->Release();
		CoUninitialize();
		return hr;
	}

	// 调用.NET程序的Main方法
	DWORD dwRet = 0;
	hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(L"//The_Path_Of//Test.exe", L"Test.Program", L"InvokePS", L"PS> ", &dwRet);
	if (FAILED(hr))
	{
		cout << "ExecuteInDefaultAppDomain Error: " << std::hex << std::showbase << hr << endl;
		pClrRuntimeHost->Release();
		pRuntimeInfo->Release();
		pMetaHost->Release();
		CoUninitialize();
		return hr;
	}

	// 释放资源
	pClrRuntimeHost->Release();
	pRuntimeInfo->Release();
	pMetaHost->Release();
	CoUninitialize();

	return 0;
}

这里需要注意的是GetRuntime设置CLR版本时需要和我们的Test.exe的版本一样, 这里都是v2.0.50727;
还有一个问题是在上面C#代码中我们的InvokePS函数的返回值是int, 如果我们把返回值设置为string等, 我们在执行ExecuteInDefaultAppDomain时会报COR_E_MISSINGMETHOD(0x80131513)错误;

M2

上面M1这种调用方式需要Test.exe文件落盘, 不是很方便, 参考Metasploit等工具, 我们发现可以这样初始化并调用函数, 可以实现和上面C#调用相似的效果:
UnMangaedPS.h:

#pragma once
//------------------------------------------------------------
//-----------       Created with 010 Editor        -----------
//------         www.sweetscape.com/010editor/          ------
//
// File    : Test.exe
// Address : 0 (0x0)
// Size    : 5632 (0x1600)
//------------------------------------------------------------
unsigned char  PowerShellRunner_dll[5632] = {
	0x4D, 0x5A, 0x90, 0x00, 0x03
};
const unsigned int PowerShellRunner_dll_len = 5632;

UnMangaedPS.cpp

#include "UnMangaedPS.h"
#include <iostream>
#include <windows.h>
#include <mscoree.h>
#include <metahost.h>
#include <string>
#include <comutil.h>
#include <comdef.h>
#include <metahost.h>
#pragma comment(lib, "mscoree.lib")
// Import mscorlib.tlb (Microsoft Common Language Runtime Class Library).
#import "mscorlib.tlb" auto_rename raw_interfaces_only				\
    high_property_prefixes("_get","_put","_putref")		\
    rename("ReportEvent", "InteropServices_ReportEvent")
using namespace mscorlib;
using namespace std;

VOID Cleanup(ICorRuntimeHost *pCorRuntimeHost) {
	if (pCorRuntimeHost)
	{
		pCorRuntimeHost->Release();
		pCorRuntimeHost = NULL;
		exit(-1);
	}
}

typedef HRESULT(WINAPI *funcCLRCreateInstance)(
	REFCLSID  clsid,
	REFIID     riid,
	LPVOID  * ppInterface
	);

typedef HRESULT(WINAPI *funcCorBindToRuntime)(
	LPCWSTR  pwszVersion,
	LPCWSTR  pwszBuildFlavor,
	REFCLSID rclsid,
	REFIID   riid,
	LPVOID*  ppv);

HRESULT createHost(const wchar_t* version, ICorRuntimeHost** ppCorRuntimeHost)
{
	bool hostCreated = false;

	HMODULE hMscoree = LoadLibrary(L"mscoree.dll");

	if (hMscoree)
	{
		HRESULT hr = NULL;
		funcCLRCreateInstance pCLRCreateInstance = NULL;
		ICLRMetaHost *pMetaHost = NULL;
		ICLRRuntimeInfo *pRuntimeInfo = NULL;
		bool hostCreated = false;

		pCLRCreateInstance = (funcCLRCreateInstance)GetProcAddress(hMscoree, "CLRCreateInstance");
		if (pCLRCreateInstance == NULL)
		{
			wprintf(L"Could not find .NET 4.0 API CLRCreateInstance");
			goto Cleanup;
		}

		hr = pCLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
		if (FAILED(hr))
		{
			// Potentially fails on .NET 2.0/3.5 machines with E_NOTIMPL
			wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
			goto Cleanup;
		}

		// v2.0.50727   v4.0.30319
		hr = pMetaHost->GetRuntime(version, IID_PPV_ARGS(&pRuntimeInfo));
		if (FAILED(hr))
		{
			wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
			goto Cleanup;
		}

		// Check if the specified runtime can be loaded into the process.
		BOOL loadable;
		hr = pRuntimeInfo->IsLoadable(&loadable);
		if (FAILED(hr))
		{
			wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
			goto Cleanup;
		}

		if (!loadable)
		{
			wprintf(L".NET runtime v2.0.50727 cannot be loaded\n");
			goto Cleanup;
		}

		// Load the CLR into the current process and return a runtime interface
		hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_PPV_ARGS(ppCorRuntimeHost));
		if (FAILED(hr))
		{
			wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
			goto Cleanup;
		}

		hostCreated = true;

	Cleanup:
		if (pMetaHost)
		{
			pMetaHost->Release();
			pMetaHost = NULL;
		}
		if (pRuntimeInfo)
		{
			pRuntimeInfo->Release();
			pRuntimeInfo = NULL;
		}

		return hostCreated;
	}
	return hostCreated;
}

void InvokeMethod(_TypePtr spType, PCWSTR method, PCWSTR command)
{
	HRESULT hr;
	bstr_t bstrStaticMethodName(method);
	SAFEARRAY *psaStaticMethodArgs = NULL;
	variant_t vtStringArg(command);
	variant_t vtPSInvokeReturnVal;
	variant_t vtEmpty;


	psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
	LONG index = 0;
	hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg);
	if (FAILED(hr))
	{
		wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr);
		return;
	}

	// Invoke the method from the Type interface.
	hr = spType->InvokeMember_3(
		bstrStaticMethodName,
		static_cast<BindingFlags>(BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),  // BindingFlags_InvokeMethod
		NULL,
		vtEmpty,
		psaStaticMethodArgs,
		&vtPSInvokeReturnVal);

	if (FAILED(hr))
	{
		wprintf(L"Failed to invoke InvokePS w/hr 0x%08lx\n", hr);
		return;
	}
	else
	{
		// Print the output of the command
		wprintf(vtPSInvokeReturnVal.bstrVal);
	}


	SafeArrayDestroy(psaStaticMethodArgs);
	psaStaticMethodArgs = NULL;
}


HRESULT RuntimeHost(PCWSTR pszVersion, PCWSTR pszAssemblyName, PCWSTR pszClassName, PCWSTR pszMethodName, PCWSTR pszArgName) {
	HRESULT hr;
	ICorRuntimeHost *pCorRuntimeHost = NULL;
	IUnknownPtr spAppDomainThunk = NULL;
	_AppDomainPtr spDefaultAppDomain = NULL;

	// The .NET assembly to load. PowerShellRunner
	bstr_t bstrAssemblyName(pszAssemblyName);  // Test
	_AssemblyPtr spAssembly = NULL;

	// The .NET class to instantiate. PowerShellRunner.PowerShellRunner TestCalc.Program
	bstr_t bstrClassName(pszClassName); // Test.Program
	_TypePtr spType = NULL;


	// Create the runtime host v2.0.50727  v4.0.30319
	if (!createHost(pszVersion, &pCorRuntimeHost))
	{
		wprintf(L"Failed to create the runtime host\n");
		Cleanup(pCorRuntimeHost);
	}

	// Start the CLR
	hr = pCorRuntimeHost->Start();
	if (FAILED(hr))
	{
		wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
		Cleanup(pCorRuntimeHost);
	}

	DWORD appDomainId = NULL;
	hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
	if (FAILED(hr))
	{
		wprintf(L"RuntimeClrHost::GetCurrentAppDomainId failed w/hr 0x%08lx\n", hr);
		Cleanup(pCorRuntimeHost);
	}

	// Get a pointer to the default AppDomain in the CLR.
	hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
	if (FAILED(hr))
	{
		wprintf(L"ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr);
		Cleanup(pCorRuntimeHost);
	}

	hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
	if (FAILED(hr))
	{
		wprintf(L"Failed to get default AppDomain w/hr 0x%08lx\n", hr);
		Cleanup(pCorRuntimeHost);
	}

	// Load the .NET assembly.
	// (Option 1) Load it from disk - usefully when debugging the PowerShellRunner app (you'll have to copy the DLL into the same directory as the exe)
	// hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);

	// (Option 2) Load the assembly from memory
	SAFEARRAYBOUND bounds[1];
	bounds[0].cElements = PowerShellRunner_dll_len;
	bounds[0].lLbound = 0;

	SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
	SafeArrayLock(arr);
	memcpy(arr->pvData, PowerShellRunner_dll, PowerShellRunner_dll_len);
	SafeArrayUnlock(arr);
	hr = spDefaultAppDomain->Load_3(arr, &spAssembly);
	printf("Load the assembly from memory\n");

	if (FAILED(hr))
	{
		wprintf(L"Failed to load the assembly w/hr 0x%08lx\n", hr);
		Cleanup(pCorRuntimeHost);
	}

	// Get the Type of PowerShellRunner.
	hr = spAssembly->GetType_2(bstrClassName, &spType);
	if (FAILED(hr))
	{
		wprintf(L"Failed to get the Type interface w/hr 0x%08lx\n", hr);
		Cleanup(pCorRuntimeHost);
	}

	// Call the static method of the class InvokePS
	InvokeMethod(spType, pszMethodName, pszArgName);

	return hr;
}

int main()
{
	LPCWSTR NetVersion = L"v2.0.50727"; // L"v2.0.50727"  "v4.0.30319"
	RuntimeHost(NetVersion, L"Test", L"Test.Program", L"InvokePS", L"PS> ");
	return 0;
}

通过这种方式调用就不需要Test.exe落盘了, 然后可以反射加载的形式执行InvokePS函数, 并且这种即使InvokePS函数返回string类型也不错报错了;
这里需要注意的是在#import "mscorlib.tlb"的时候可能报错说找不到文件, 可以在属性 -> VC++目录 -> 附加目录中添加mscorlib.tlb所在目录(例如C:\Windows\Microsoft.NET\Framework\v2.0.50727);

总结

这里编译的形式是exe, 在实际使用中可以为DLL, 甚至shellcode; 更成熟的代码可以参考UnmanagedPowerShell
等项目;
参考:

  1. https://begtostudy-tech.blogspot.com/2011/03/use-clr4-hosting-api-to-invoke-net.html
  2. https://github.com/leechristensen/UnmanagedPowerShell
  3. https://3gstudent.github.io/backup-3gstudent.github.io/%E4%BB%8E%E5%86%85%E5%AD%98%E5%8A%A0%E8%BD%BD.NET%E7%A8%8B%E5%BA%8F%E9%9B%86(execute-assembly)%E7%9A%84%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/447246.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[短文]不同空白字符导致程序执行失败问题

屏幕显示的一个空白字符&#xff0c;对于编程者来说&#xff0c;并无差异&#xff0c;但底层截然不同的表示方法&#xff0c;极大可能导致程序执行失败&#xff01; 今天博主就遇到一个空格字符的问题&#xff0c;大概情况是前端编写SQL传入&#xff0c;后端有时可以执行&…

Uninty 鼠标点击(摄像机发出射线-检测位置)

平面来触发碰撞&#xff0c;胶囊用红色材质方便观察。 脚本挂载到胶囊上方便操作。 目前实现的功能&#xff0c;鼠标左键点击&#xff0c;胶囊就移动到那个位置上。 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c6 : MonoBe…

DNS服务

简介 DNS&#xff1a;domain name service 域名服务 作用&#xff1a;为客户机提供域名解析 dns 监听端口&#xff1a;53端口 分为 udp&#xff08;只负责向外发&#xff09; 和 tcp&#xff08;确保正常发送至对端&#xff0c;对端发送数据包表示已经收到&#xff09; 搭建…

解密QQ盗号诈骗APP:逆向溯源,探寻幕后黑色操作

逆向溯源qq盗号诈骗app 起因 专注于web漏洞挖掘、内网渗透、免杀和代码审计&#xff0c;感谢各位师傅的关注&#xff01;网安之路漫长&#xff0c;与君共勉&#xff01; 分析该app是源于朋友被盗号了&#xff0c;对方发了个app想盗号&#xff0c;这怎么能惯着他&#xff1f;这不…

day16_购物车(添加购物车,购物车列表查询,删除购物车商品,更新选中商品状态,完成购物车商品的全选,清空购物车)

文章目录 购物车模块1 需求说明2 环境搭建3 添加购物车3.1 需求说明3.2 远程调用接口开发3.2.1 ProductController3.2.2 ProductService 3.3 openFeign接口定义3.3.1 环境搭建3.3.2 接口定义3.3.3 降级类定义 3.4 业务后端接口开发3.4.1 添加依赖3.4.2 修改启动类3.4.3 CartInf…

MyBatisPlus理解

MyBatisPlus是mybatis的增强&#xff0c;mybatis是数据库持久化的框架&#xff0c;但mybatisplus并不是替代mybatis&#xff0c;而是相辅相成的关系 MyBatisPlus不会对以前使用mybatis开发的项目进行影响&#xff0c;引入后仍然正常运行。 使用方法&#xff1a; 1.在引入了对…

[力扣 Hot100]Day48 路径总和 III

题目描述 给定一个二叉树的根节点 root &#xff0c;和一个整数 targetSum &#xff0c;求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始&#xff0c;也不需要在叶子节点结束&#xff0c;但是路径方向必须是向下的&#xff08;只能从父节点到…

再谈 Cookie 和 Session

文章目录 1. 核心方法&#xff1a;HttpServletRequest 类中HttpServletResponse 类中HttpSession类中Cookie类中 2.实现登录界面 Cookie 是浏览器在本地持久化存储数据的一种机制。 Cookie 的数据从哪里来&#xff1f; 是服务器返回给浏览器的。 Cookie 的数据长什么啥样&#…

Chapter20-Ideal gases-CIE课本要点摘录、总结

20.1 Particles of a gas Brownian motion Fast modules 速率的数值大概了解下&#xff1a; average speed of the molecules:400m/s speed of sound:approximately 330m/s at STP&#xff08;standard temperature and pressure&#xff09; Standard Temperature and Pres…

不同路径 不同路径 II 整数拆分

62.不同路径 力扣题目链接(opens new window) 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。…

标准不锈钢电阻-栅极电阻器的设计方案

EAK不锈钢栅极电阻器 可靠的不锈钢电阻元件和端子 所有装置均为三重绝缘&#xff0c;适用于 1000V 交流或直流 标准电网电阻器有现货&#xff0c;可减少代价高昂的停机时间 在很宽的温度范围内具有稳定的耐受性 高功率可节省空间、重量和成本 标准 26.5 英寸尺寸&#xf…

PDF24 Creator PDF工具箱 v11.17.0

软件介绍 可将大部分文件转成pdf格式的免费软件&#xff0c;安装好后会在你的打印机里看到一个叫PDF24的虚拟打印机&#xff0c;你可将要转成pdf格式的文件打印时选虚拟打印机PDF24&#xff0c;也可以直接将文件以拖拉方式拉进这软件的主视窗编辑区里&#xff0c;它会自动转成…

OD_2024_C卷_200分_6、六_连续出牌数量【JAVA】【回溯算法】

题目描述 package odjava;import java.util.Arrays; import java.util.Scanner;public class 六_连续出牌数量 {// 定义扑克牌类static class Card {int num; // 牌号char color; // 花色public Card(int num, String color) {this.num num;this.color color.charAt(0); // 取…

Liinux——(网络)socket编程

预备知识 源IP地址和目的IP地址 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址 认识端口号 端口号(port)是传输层协议的内容. 端口号是一个2字节16位的整数;端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪个进程来处理;IP地址 端口号能…

js【详解】DOM

文档对象模型&#xff08;Document Object Model&#xff0c;简称DOM&#xff09; DOM 是哪种数据结构 &#xff1f; DOM 的本质是浏览器通过HTML代码解析出来的一棵 树。 操作 DOM 常用的 API 有哪些 &#xff1f; 获取 DOM 节点 //方式 1&#xff1a;通过【id】获取&#xf…

每日学习笔记:C++ STL 的队列Deque

定义 内存模型 Deque与Vector比较 操作函数 运用实例

每日OJ题_牛客HJ73 计算日期到天数转换(IO型OJ)

目录 牛客HJ73 计算日期到天数转换 解析代码 牛客HJ73 计算日期到天数转换 计算日期到天数转换_牛客题霸_牛客网 解析代码 #include <iostream> using namespace std; int main() {int year 0, month 0, day 0, sum 0;cin >> year >> month >>…

文本向量评测MTEB和C-MTEB

文章目录 简介MTEBC-MTEB参考资料 简介 MTEB(Massive Text Embedding Benchmark)是目前评测文本向量很重要的一个参考&#xff0c;其榜单也是各大文本向量模型用来展示与其他向量模型强弱的一个竞技台。 C-MTEB则是专门针对中文文本向量的评测基准。 MTEB MTEB的目的是为了…

esp32 GDEH0154D67屏幕调试

官方资料 说明手册&#xff1a;GDEH0154D67specf3d5.pdf (e-paper-display.cn) 官网&#xff1a;1.54寸黑白单色电子纸显示屏 200x200分辨率电子墨水屏 GDEH0154D67,黑白电子纸屏,电子墨水屏-大连佳显 (e-paper-display.cn) 驱动代码来源&#xff1a;桌面小屏幕实战课程资料…

面向对象的编程语言是什么意思?——跟老吕学Python编程

面向对象的编程语言是什么意思&#xff1f;——跟老吕学Python编程 面向对象是什么意思&#xff1f;面向对象的定义面向对象的早期发展面向对象的背景1.审视问题域的视角2.抽象级别3.封装体4.可重用性 面向对象的特征面向对象的开发方法面向对象程序设计基本思想实现 面向对象的…
最新文章