当前位置: 首页 > news >正文

3.5 Windows驱动开发:应用层与内核层内存映射

在上一篇博文《内核通过PEB得到进程参数》中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这次我们将实现一个更加有趣的功能,在某些情况下应用层与内核层需要共享一片内存区域通过这片区域可打通内核与应用层的隔离,此类功能的实现依附于MDL内存映射机制实现。

3.5.1 应用层映射到内核层

先来实现将R3内存数据拷贝到R0中,功能实现所调用的API如下:

  • 调用IoAllocateMdl创建一个MDL结构体。这个结构体描述了一个要锁定的内存页的位置和大小。
  • 调用MmProbeAndLockPages用于锁定创建的地址其中UserMode代表用户层,IoReadAccess以读取的方式锁定
  • 调用MmGetSystemAddressForMdlSafe用于从MDL中得到映射内存地址
  • 调用RtlCopyMemory用于内存拷贝,将DstAddr应用层中的数据拷贝到pMappedSrc
  • 调用MmUnlockPages拷贝结束后解锁pSrcMdl
  • 调用IoFreeMdl释放之前创建的MDL结构体。

如上则是应用层数据映射到内核中的流程,我们将该流程封装成SafeCopyMemory_R3_to_R0方便后期的使用,函数对数据的复制进行了分块操作,因此可以处理更大的内存块。

下面是对该函数的分析:

  • 1.首先进行一些参数的检查,如果有任何一个参数为0,那么函数就会返回 STATUS_UNSUCCESSFUL

  • 2.使用一个 while 循环来分块复制数据,每个块的大小为 PAGE_SIZE (通常是4KB)。这个循环在整个内存范围内循环,每次复制一个内存页的大小,直到复制完整个内存范围。

  • 3.在循环内部,首先根据起始地址和当前要复制的大小来确定本次要复制的大小。如果剩余的内存不足一页大小,则只复制剩余的内存。

  • 4.调用 IoAllocateMdl 创建一个 MDL,表示要锁定和复制的内存页。这里使用了 (PVOID)(SrcAddr & 0xFFFFFFFFFFFFF000) 来确定页的起始地址。因为页的大小为 0x1000,因此在计算页的起始地址时,将 SrcAddr 向下舍入到最接近的 0x1000 的倍数。

  • 5.如果 IoAllocateMdl 成功,则调用 MmProbeAndLockPages 来锁定页面。这个函数将页面锁定到物理内存中,并返回一个虚拟地址,该虚拟地址指向已锁定页面的内核地址。

  • 6.使用 MmGetSystemAddressForMdlSafe 函数获取一个映射到内核空间的地址,该地址可以直接访问锁定的内存页。

  • 6.如果获取到了映射地址,则使用 RtlCopyMemory 函数将要复制的数据从应用层内存拷贝到映射到内核空间的地址。在复制结束后,使用 MmUnlockPages 函数解锁内存页,释放对页面的访问权限。

最后,释放 MDL 并更新 SrcAddrDstAddr 以复制下一个内存块。如果复制过程中发生任何异常,函数将返回 STATUS_UNSUCCESSFUL

总的来说,这个函数是一个很好的实现,它遵循了内核驱动程序中的最佳实践,包括对内存的安全处理、分块复制、错误处理等。

#include <ntifs.h>
#include <windef.h>// 分配内存
void* RtlAllocateMemory(BOOLEAN InZeroMemory, SIZE_T InSize)
{void* Result = ExAllocatePoolWithTag(NonPagedPool, InSize, 'lysh');if (InZeroMemory && (Result != NULL))RtlZeroMemory(Result, InSize);return Result;
}// 释放内存
void RtlFreeMemory(void* InPointer)
{ExFreePool(InPointer);
}/*
将应用层中的内存复制到内核变量中SrcAddr  r3地址要复制
DstAddr  R0申请的地址
Size     拷贝长度
*/
NTSTATUS SafeCopyMemory_R3_to_R0(ULONG_PTR SrcAddr, ULONG_PTR DstAddr, ULONG Size)
{NTSTATUS status = STATUS_UNSUCCESSFUL;ULONG nRemainSize = PAGE_SIZE - (SrcAddr & 0xFFF);ULONG nCopyedSize = 0;if (!SrcAddr || !DstAddr || !Size){return status;}while (nCopyedSize < Size){PMDL pSrcMdl = NULL;PVOID pMappedSrc = NULL;if (Size - nCopyedSize < nRemainSize){nRemainSize = Size - nCopyedSize;}// 创建MDLpSrcMdl = IoAllocateMdl((PVOID)(SrcAddr & 0xFFFFFFFFFFFFF000), PAGE_SIZE, FALSE, FALSE, NULL);if (pSrcMdl){__try{// 锁定内存页面(UserMode代表应用层)MmProbeAndLockPages(pSrcMdl, UserMode, IoReadAccess);// 从MDL中得到映射内存地址pMappedSrc = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);}__except (EXCEPTION_EXECUTE_HANDLER){}}if (pMappedSrc){__try{// 将MDL中的映射拷贝到pMappedSrc内存中RtlCopyMemory((PVOID)DstAddr, (PVOID)((ULONG_PTR)pMappedSrc + (SrcAddr & 0xFFF)), nRemainSize);}__except (1){// 拷贝内存异常}// 释放锁MmUnlockPages(pSrcMdl);}if (pSrcMdl){// 释放MDLIoFreeMdl(pSrcMdl);}if (nCopyedSize){nRemainSize = PAGE_SIZE;}nCopyedSize += nRemainSize;SrcAddr += nRemainSize;DstAddr += nRemainSize;}status = STATUS_SUCCESS;return status;
}

有了封装好的SafeCopyMemory_R3_to_R0函数,那么接下来就是使用该函数实现应用层到内核层中的拷贝,为了能实现拷贝我们需要做以下几个准备工作;

  • 1.使用PsLookupProcessByProcessId函数通过进程ID查找到对应的EProcess结构体,以获取该进程在内核中的信息。
  • 2.使用KeStackAttachProcess函数将当前进程的执行上下文切换到指定进程的上下文,以便能够访问该进程的内存。
  • 3.使用RtlAllocateMemory函数在当前进程的内存空间中分配一块缓冲区,用于存储从指定进程中读取的数据。
  • 4.调用SafeCopyMemory_R3_to_R0函数将指定进程的内存数据拷贝到分配的缓冲区中。
  • 5.将缓冲区中的数据转换为BYTE类型的指针,并将其输出。

PsLookupProcessByProcessId函数用于通过进程ID查找到对应的EProcess结构体,这个结构体是Windows操作系统内核中用于表示一个进程的数据结构。

NTSTATUS status = PsLookupProcessByProcessId(ProcessId, &ProcessObject);
if (!NT_SUCCESS(status)) {return status;
}

KeStackAttachProcess函数将当前进程的执行上下文切换到指定进程的上下文,以便能够访问该进程的内存。这个函数也只能在内核态中调用。

KeStackAttachProcess(ProcessObject, &ApcState);

RtlAllocateMemory函数在当前进程的内存空间中分配一块缓冲区,用于存储从指定进程中读取的数据。这个函数是Windows操作系统内核中用于动态分配内存的函数,其中第一个参数TRUE表示允许操作系统在分配内存时进行页面合并,以减少内存碎片的产生。第二个参数nSize表示需要分配的内存空间的大小。如果分配失败,就需要将之前的操作撤销并返回错误状态。

PVOID pTempBuffer = RtlAllocateMemory(TRUE, nSize);
if (pTempBuffer == NULL) {KeUnstackDetachProcess(&ApcState);ObDereferenceObject(ProcessObject);return STATUS_NO_MEMORY;
}

SafeCopyMemory_R3_to_R0函数将指定进程的内存数据拷贝到分配的缓冲区中。

if (!SafeCopyMemory_R3_to_R0(ModuleBase, pTempBuffer, nSize)) {RtlFreeMemory(pTempBuffer);KeUnstackDetachProcess(&ApcState);ObDereferenceObject(ProcessObject);return STATUS_UNSUCCESSFUL;
}

最后,将缓冲区中的数据转换为BYTE类型的指针,并将其输出。需要注意的是,在返回之前需要先将当前进程的执行上下文切换回原先的上下文。

BYTE* data = (BYTE*)pTempBuffer;
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(ProcessObject);
return data;

如上实现细节用一段话总结,首先PsLookupProcessByProcessId得到进程EProcess结构,并KeStackAttachProcess附加进程,声明pTempBuffer指针用于存储RtlAllocateMemory开辟的内存空间,nSize则代表读取应用层进程数据长度,ModuleBase则是读入进程基址,调用SafeCopyMemory_R3_to_R0即可将应用层数据拷贝到内核空间,并最终BYTE* data转为BYTE字节的方式输出。这样就完成了将指定进程的内存数据拷贝到当前进程中的操作。

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark.com \n");NTSTATUS status = STATUS_UNSUCCESSFUL;PEPROCESS eproc = NULL;KAPC_STATE kpc = { 0 };__try{// HANDLE 进程PIDstatus = PsLookupProcessByProcessId((HANDLE)4556, &eproc);if (NT_SUCCESS(status)){// 附加进程KeStackAttachProcess(eproc, &kpc);// -------------------------------------------------------------------// 开始映射// -------------------------------------------------------------------// 将用户空间内存映射到内核空间PVOID pTempBuffer = NULL;ULONG nSize = 0x1024;ULONG_PTR ModuleBase = 0x0000000140001000;// 分配内存pTempBuffer = RtlAllocateMemory(TRUE, nSize);if (pTempBuffer){// 拷贝数据到R0status = SafeCopyMemory_R3_to_R0(ModuleBase, (ULONG_PTR)pTempBuffer, nSize);if (NT_SUCCESS(status)){DbgPrint("[*] 拷贝应用层数据到内核里 \n");}// 转成BYTE方便读取BYTE* data = pTempBuffer;for (size_t i = 0; i < 10; i++){DbgPrint("%02X \n", data[i]);}}// 释放空间RtlFreeMemory(pTempBuffer);// 脱离进程KeUnstackDetachProcess(&kpc);}}__except (EXCEPTION_EXECUTE_HANDLER){Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码运行后即可将进程中0x0000000140001000处的数据读入内核空间并输出:

3.5.2 内核层映射到应用层

与上方功能实现相反SafeCopyMemory_R0_to_R3函数则用于将一个内核层中的缓冲区写出到应用层中,SafeCopyMemory_R0_to_R3函数接收源地址SrcAddr、要复制的数据长度Length以及目标地址DstAddr作为参数,其写出流程可总结为如下步骤:

  • 1.使用IoAllocateMdl函数分别为源地址SrcAddr和目标地址DstAddr创建两个内存描述列表(MDL)。

  • 2.使用MmBuildMdlForNonPagedPool函数,将源地址的MDL更新为描述非分页池的虚拟内存缓冲区,并更新MDL以描述底层物理页。

  • 3.通过两次调用MmGetSystemAddressForMdlSafe函数,分别获取源地址和目标地址的指针,即pSrcMdlpDstMdl

  • 4.使用MmProbeAndLockPages函数以写入方式锁定用户空间中pDstMdl指向的地址,并将它的虚拟地址映射到物理内存页,从而确保该内存页在复制期间不会被交换出去或被释放掉。

  • 5.然后使用MmMapLockedPagesSpecifyCache函数将锁定的用户空间内存页映射到内核空间,并返回内核空间中的虚拟地址。

  • 6.最后使用RtlCopyMemory函数将源地址的数据复制到目标地址。

  • 7.使用MmUnlockPages函数解除用户空间内存页的锁定,并使用MmUnmapLockedPages函数取消内核空间与用户空间之间的内存映射。

  • 8.最后释放源地址和目标地址的MDL,使用IoFreeMdl函数进行释放。

内存拷贝SafeCopyMemory_R0_to_R3函数,函数首先分配源地址和目标地址的MDL结构,然后获取它们的虚拟地址,并以写入方式锁定目标地址的MDL,最后使用RtlCopyMemory函数将源地址的内存数据拷贝到目标地址。拷贝完成后,函数解锁目标地址的MDL,并返回操作状态。

封装代码SafeCopyMemory_R0_to_R3()功能如下:

// 分配内存
void* RtlAllocateMemory(BOOLEAN InZeroMemory, SIZE_T InSize)
{void* Result = ExAllocatePoolWithTag(NonPagedPool, InSize, 'lysh');if (InZeroMemory && (Result != NULL))RtlZeroMemory(Result, InSize);return Result;
}// 释放内存
void RtlFreeMemory(void* InPointer)
{ExFreePool(InPointer);
}/*
将内存中的数据复制到R3中SrcAddr  R0要复制的地址
DstAddr  返回R3的地址
Size     拷贝长度
*/
NTSTATUS SafeCopyMemory_R0_to_R3(PVOID SrcAddr, PVOID DstAddr, ULONG Size)
{PMDL  pSrcMdl = NULL, pDstMdl = NULL;PUCHAR pSrcAddress = NULL, pDstAddress = NULL;NTSTATUS st = STATUS_UNSUCCESSFUL;// 分配MDL 源地址pSrcMdl = IoAllocateMdl(SrcAddr, Size, FALSE, FALSE, NULL);if (!pSrcMdl){return st;}// 该 MDL 指定非分页虚拟内存缓冲区,并对其进行更新以描述基础物理页。MmBuildMdlForNonPagedPool(pSrcMdl);// 获取源地址MDL地址pSrcAddress = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);if (!pSrcAddress){IoFreeMdl(pSrcMdl);return st;}// 分配MDL 目标地址pDstMdl = IoAllocateMdl(DstAddr, Size, FALSE, FALSE, NULL);if (!pDstMdl){IoFreeMdl(pSrcMdl);return st;}__try{// 以写入的方式锁定目标MDLMmProbeAndLockPages(pDstMdl, UserMode, IoWriteAccess);// 获取目标地址MDL地址pDstAddress = MmGetSystemAddressForMdlSafe(pDstMdl, NormalPagePriority);}__except (EXCEPTION_EXECUTE_HANDLER){}if (pDstAddress){__try{// 将源地址拷贝到目标地址RtlCopyMemory(pDstAddress, pSrcAddress, Size);}__except (1){// 拷贝内存异常}MmUnlockPages(pDstMdl);st = STATUS_SUCCESS;}IoFreeMdl(pDstMdl);IoFreeMdl(pSrcMdl);return st;
}

调用该函数实现拷贝,此处除去附加进程以外,在拷贝之前调用了ZwAllocateVirtualMemory将内存属性设置为PAGE_EXECUTE_READWRITE可读可写可执行状态,然后在向该内存中写出pTempBuffer变量中的内容,此变量中的数据是0x90填充的区域。

此处的ZwAllocateVirtualMemory函数,用于在进程的虚拟地址空间中分配一块连续的内存区域,以供进程使用。它属于Windows内核API的一种,与用户态的VirtualAlloc函数相似,但是它运行于内核态,可以分配不受用户空间地址限制的虚拟内存,并且可以用于在驱动程序中为自己或其他进程分配内存。

函数的原型为:

NTSYSAPI NTSTATUS NTAPI ZwAllocateVirtualMemory(_In_    HANDLE    ProcessHandle,_Inout_ PVOID     *BaseAddress,_In_    ULONG_PTR ZeroBits,_Inout_ PSIZE_T   RegionSize,_In_    ULONG     AllocationType,_In_    ULONG     Protect
);

其中ProcessHandle参数是进程句柄,BaseAddress是分配到的内存区域的起始地址,ZeroBits指定保留的高位,RegionSize是分配内存大小,AllocationTypeProtect分别表示内存分配类型和内存保护属性。

ZwAllocateVirtualMemory函数成功返回NT_SUCCESS,返回值为0,否则返回相应的错误代码。如果函数成功调用,会将BaseAddress参数指向分配到的内存区域的起始地址,同时将RegionSize指向的值修改为实际分配到的内存大小。

当内存属性被设置为PAGE_EXECUTE_READWRITE之后,则下一步直接调用SafeCopyMemory_R0_to_R3进行映射即可,其调用完整案例如下所示;

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark.com \n");NTSTATUS status = STATUS_UNSUCCESSFUL;PEPROCESS eproc = NULL;KAPC_STATE kpc = { 0 };__try{// HANDLE 进程PIDstatus = PsLookupProcessByProcessId((HANDLE)4556, &eproc);if (NT_SUCCESS(status)){// 附加进程KeStackAttachProcess(eproc, &kpc);// -------------------------------------------------------------------// 开始映射// -------------------------------------------------------------------// 将用户空间内存映射到内核空间PVOID pTempBuffer = NULL;ULONG nSize = 0x1024;PVOID ModuleBase = 0x0000000140001000;// 分配内存pTempBuffer = RtlAllocateMemory(TRUE, nSize);if (pTempBuffer){memset(pTempBuffer, 0x90, nSize);// 设置内存属性 PAGE_EXECUTE_READWRITEZwAllocateVirtualMemory(NtCurrentProcess(), &ModuleBase, 0, &nSize, MEM_RESERVE, PAGE_EXECUTE_READWRITE);ZwAllocateVirtualMemory(NtCurrentProcess(), &ModuleBase, 0, &nSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);// 将数据拷贝到R3中status = SafeCopyMemory_R0_to_R3(pTempBuffer, &ModuleBase, nSize);if (NT_SUCCESS(status)){DbgPrint("[*] 拷贝内核数据到应用层 \n");}}// 释放空间RtlFreeMemory(pTempBuffer);// 脱离进程KeUnstackDetachProcess(&kpc);}}__except (EXCEPTION_EXECUTE_HANDLER){Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

拷贝成功后,应用层进程内将会被填充为Nop指令。

相关文章:

3.5 Windows驱动开发:应用层与内核层内存映射

在上一篇博文《内核通过PEB得到进程参数》中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息&#xff0c;本篇文章同样需要使用进程附加功能&#xff0c;但这次我们将实现一个更加有趣的功能&#xff0c;在某些情况下应用层与内核层需要共享一片内存…...

【小黑送书—第八期】>>别再吐槽大学教材了,来看看这些网友强推的数学神作!

导读&#xff1a;关于大学数学教材的吐槽似乎从来没停止过。有人慨叹&#xff1a;数学教材晦涩难懂。错&#xff01;难懂&#xff0c;起码还可以读懂。数学教材你根本读不懂&#xff1b;也有人说&#xff1a;数学教材简直就是天书。 数学教材有好有坏&#xff0c;这话不假&…...

MatLab的下载、安装与使用(亲测有效)

1、概述 MatLab是由MathWorks公司开发并发布的&#xff0c;支持线性代数、矩阵运算、绘制函数和数据、信号处理、图像处理以及视频处理等功能。广泛用于算法开发、数据可视化、数据分析以及数值计算等。 Matlab 的主要特性包括&#xff1a; 简单易用的语法&#xff0c;使得程…...

无人智能货柜:引领便捷购物新体验

无人智能货柜&#xff1a;引领便捷购物新体验 无人智能货柜利用人工智能技术&#xff0c;将传统货架与电子商务相结合&#xff0c;形成智能销售终端。其采用先拿货后付款的购物模式&#xff0c;用户只需扫码、拿货、关门三个简洁流畅的步骤&#xff0c;极大地提升了消费者的购物…...

4.6 Windows驱动开发:内核遍历进程VAD结构体

在上一篇文章《内核中实现Dump进程转储》中我们实现了ARK工具的转存功能&#xff0c;本篇文章继续以内存为出发点介绍VAD结构&#xff0c;该结构的全程是Virtual Address Descriptor即虚拟地址描述符&#xff0c;VAD是一个AVL自平衡二叉树&#xff0c;树的每一个节点代表一段虚…...

基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码

基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于世界杯算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于世界杯优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络…...

NPM 与 XUI 共存!Nginx Proxy Manager 搭配 X-UI 实现 Vless+WS+TLS 教程!

之前分享过搭建可以与宝塔共存的一个 “魔法” 服务器状态监控应用 ——xui&#xff0c;支持 VmessWSTLS。 最近 Docker 视频出的比较多&#xff0c;前阵子又出现了宝塔国内版存在隐私泄露的问题&#xff0c;很多小伙伴其实都不用宝塔了&#xff0c;那么&#xff0c;在我们现在…...

【网络奇遇记】那年我与计算机网络的浅相知

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. 计算机网络的定义1.1 计算机早期的一个最简单的定义1.2 现阶段计算机网络的一个较好的定义 二. …...

LeetCode26.删除有序数组中的重复项(双指针法)

LeetCode26.删除有序数组中的重复项 1.问题描述2.解题思路3.代码 1.问题描述 给你一个 非严格递增排列 的数组 nums &#xff0c;请你** 原地** 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然…...

原型网络Prototypical Network的python代码逐行解释,新手小白也可学会!!-----系列8

文章目录 前言一、原始代码二、对每一行代码的解释&#xff1a;总结 前言 这是该系列原型网络的最后一段代码及其详细解释&#xff0c;感谢各位的阅读&#xff01; 一、原始代码 if __name__ __main__:##载入数据labels_trainData, labels_testData load_data() # labels_…...

黑马点评回顾 redis实现共享session

文章目录 传统session缺点整体访问流程代码实现生成验证码登录 问题具体思路 传统session缺点 传统单体项目一般是把session存入tomcat&#xff0c;但是每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat&#xff0c;并且把自己的信息存放到第一台服务器…...

Redis篇---第八篇

系列文章目录 文章目录 系列文章目录前言一、说说 Redis 哈希槽的概念?二、Redis 常见性能问题和解决方案有哪些?三、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?前言 前些天发现了一个巨牛的人工智能学习网站…...

Unity使用Visual Studio Code 调试

Unity 使用Visual Studio Code 调试C# PackageManager安装Visual Studio EditorVisual Studio Code安装Unity 插件修改Unity配置调试 PackageManager安装Visual Studio Editor 打开 Window->PackageManger卸载 Visual Studio Code Editor &#xff0c;这个已经被官方废弃安…...

【Linux】进程替换|exec系列函数

文章目录 一、看一看单进程版的进程替换二、进程替换的原理三、多进程版——验证各种程序替换接口exec系列函数execlexeclpexecvexecvp tipsexecleexecve 四、总结 一、看一看单进程版的进程替换 #include<stdio.h> #include<unistd.h> #include<stdlib.h>i…...

Java编程技巧:将图片导出成pdf文件

目录 一、pom依赖二、代码三、测试链接 一、pom依赖 <!-- pdf插件 start --> <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.3</version> </dependency> <dependency…...

二项分布和泊松分布

一、二项分布 1.1 n重伯努利试验 若是二项分布&#xff0c;则必是n重伯努利试验概型。即&#xff1a;每次试验只有两种结果 与 &#xff0c;且在每次试验中A发生的概率相等&#xff0c;即P(A)p&#xff0c;将这种试验独立重复n次&#xff0c;则称这种试验为n重伯努利试验&#…...

【飞控调试】DJIF450机架+Pixhawk6c mini+v1.13.3固件+好盈Platinium 40A电调无人机调试

1 背景 由于使用了一种新的航电设备组合&#xff0c;在调试无人机起飞的时候遇到了之前没有遇到的问题。之前用的飞控&#xff08;Pixhawk 6c&#xff09;和电调&#xff08;Hobbywing X-Rotor 40A&#xff09;&#xff0c;在QGC里按默认参数配置来基本就能平稳飞行&#xff0…...

Android studio配置Flutter开发环境报错问题解决

博主前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住也分享一下给大家 &#x1f449;点击跳转到教程 报错问题截图 报错原因已经给出&#xff1a; You need Java 11 or higher to build your app with this version of G…...

2023.11.18 -自用hadoop高可用环境搭建命令

启动hadoop高可用环境 # 1.先恢复快照到高可用环境 # 2.三台服务器启动zookeeper服务 [rootnode1 ~]# zkServer.sh start [rootnode2 ~]# zkServer.sh start [rootnode3 ~]# zkServer.sh start 查看服务状态: [rootnode]# zkServer.sh status 关闭zk服务的命令是: [rootnode]# …...

【Linux】常用系统工作命令

一、Linux文档目录结构 在Linux系统中&#xff0c;目录、字符设备、套接字、硬盘、光驱、打印机等都被抽象成文件形式&#xff0c;“Linux系统中一切都是文件”。Linux系统中的一切文件都是从"根"目录&#xff08;/&#xff09;开始的&#xff0c;并按照文件系统层次…...

深入理解网络协议:通信世界的基石

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 在当今数字化时代&#xff0c;网络协议是连接世…...

PL/SQL编程

一、Oracle常用函数 concat&#xff1a;用于连接两个字符串。 CONCAT(Oraok, .com) -- Result: Oraok.com ceil&#xff1a;小数点向上取整。 secect ceil(7.3) from dual --Result: 8 dual表是oracle系统为计算设计的一张临时表 select sysdate as 系统日期 from dual…...

Prompt提示词——什么是CRISPE框架?QCIPSPE框架?

框架介绍 【CRISPE】框架 是由 Matt Nigh 提出并发布的提示词书写框架&#xff0c;共由五部分组成。 这个框架&#xff08;CRISPE&#xff09;主要包括五个部分&#xff0c;用于指导用户向ChatGPT提问。首先&#xff0c;通过设定ChatGPT的角色&#xff08;Capacity and Role&…...

Nginx的核心配置文件

Nginx的核心配置文件 学习Nginx首先需要对它的核心配置文件有一定的认识&#xff0c;这个文件位于Nginx的安装目录/usr/local/nginx/conf目录下&#xff0c;名字为nginx.conf 详细配置&#xff0c;可以参考resources目录下的<<nginx配置中文详解.conf>> Nginx的核…...

Java,集合框架,关于Collection接口(子接口List和Set)

目录 数组储存多个数据方面的特点&#xff1a; Java集合框架体系&#xff1a;&#xff08;Java.util包下&#xff09; Collection接口中的方法测试&#xff1a; 迭代器&#xff08;Iterator&#xff09;的作用&#xff1a;用来遍历集合元素。 增强for循环&#xff08;即for…...

已安装的nginx追加ssl模块

Nginx开启SSL模块1 切换到源码包&#xff1a; cd /usr/local/src/nginx-1.11.3 2 查看nginx原有的模块 /usr/local/nginx/sbin/nginx -V 在configure arguments:后面显示的原有的configure参数如下&#xff1a; –prefix/usr/local/nginx --with-http_stub_status_module …...

大语言模型|人工智能领域中备受关注的技术

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️其他领域】 文章目录 前言关于大语言模型大语言模型是什么&#xff1f;大语言模型有什么用?文案写作知识库回答文本分类代码生成 AWS 如何通过 LLM 提供帮助&#xff1f;Amazon BedrockAmazon SageM…...

Docker之DockerFile解析

DockerFile解析 是什么 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本。 概述 官网 https://docs.docker.com/engine/reference/builder/ 构建三步骤 编写Dockerfile文件 docker build命令构建镜像 docker run依镜像运…...

NSSCTF第13页(2)

[HNCTF 2022 Week1]Challenge__rce 提示?hint 访问看到了源码 <?php error_reporting(0); if (isset($_GET[hint])) { highlight_file(__FILE__); } if (isset($_POST[rce])) { $rce $_POST[rce]; if (strlen($rce) < 120) { if (is_string($rce…...

基于吉萨金字塔建造算法优化概率神经网络PNN的分类预测 - 附代码

基于吉萨金字塔建造算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于吉萨金字塔建造算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于吉萨金字塔建造优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&a…...

Git详解及 github使用

1.1 关于版本控制 开始之前先看一个没有版本控制的例子 1.1.1 本地版本控制 本地版本控制系统 许多人习惯用复制整个项目目录的方式来保存不同的版本&#xff0c;或许还会改名加上备份时间以示区别。这么做唯一的 好处就是简单&#xff0c;但是特别容易犯错。有时候会混淆所在…...

iOS源码-工程目录讲解

1、 工程目录 1.1、xib 主要的界面渲染控制&#xff0c;ios开发常用的界面&#xff0c;可以在这里快速开发出来 1.2、base 基本的类&#xff0c;子类继承base类&#xff0c;就具备父类的方法&#xff0c;无需在重写 1.3、util 基础的类一些&#xff0c;处理时间等 1.4、…...

ESP32 Arduino实战协议篇-搭建独立的 Web 服务器

在此项目中,您将创建一个带有 ESP32 的独立 Web 服务器,该服务器使用 Arduino IDE 编程环境控制输出(两个 LED)。Web 服务器是移动响应的,可以使用本地网络上的任何浏览器设备进行访问。我们将向您展示如何创建 Web 服务器以及代码如何逐步工作。 项目概况 在直接进入项目…...

多维时序 | MATLAB实现PSO-BiLSTM-Attention粒子群优化双向长短期记忆神经网络融合注意力机制的多变量时间序列预测

多维时序 | MATLAB实现PSO-BiLSTM-Attention粒子群优化双向长短期记忆神经网络融合注意力机制的多变量时间序列预测 目录 多维时序 | MATLAB实现PSO-BiLSTM-Attention粒子群优化双向长短期记忆神经网络融合注意力机制的多变量时间序列预测预测效果基本介绍模型描述程序设计参考…...

【开源】基于Vue.js的在线课程教学系统的设计和实现

项目编号&#xff1a; S 014 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S014&#xff0c;文末获取源码。} 项目编号&#xff1a;S014&#xff0c;文末获取源码。 目录 一、摘要1.1 系统介绍1.2 项目录屏 二、研究内容2.1 课程类型管理模块2.2 课程管理模块2…...

CentOS 安装etcd集群 —— 筑梦之路

环境说明 192.168.1.11 192.168.1.12 192.168.1.13 yum在线安装 yum install etcd -y #etcd01 cat > /etc/etcd/etcd.conf <<EOF ETCD_NAMEetcd01 ETCD_LISTEN_PEER_URLS"http://0.0.0.0:2380" ETCD_LISTEN_CLIENT_URLS"http://0.0.0.0:2379&quo…...

mysql 实现去重

个人网站 首发于公众号小肖学数据分析 1、试题描述 数据表user_test如下&#xff0c;请你查询所有投递用户user_id并且进行去重展示&#xff0c;查询结果和返回顺序如下 查询结果和返回顺序如下所示 解题思路&#xff1a; (1) 对user_id列直接去重&#xff1a; &#xff…...

类模板成员函数类外实现

#include<iostream> #include<string> using namespace std;//类模板成员函数类外实现 template<class T1,class T2> class Person { public:Person(T1 name,T2 age); // { // this->m_namename; // this->m_ageage; // }void showPerson(); // { /…...

多svn仓库一键更新脚本分享

之前分享过多git仓库一键更新脚本&#xff0c;本期就分享下svn仓库的一键更新脚本 1、首先需要设置svn为可执行命令行 打开SVN安装程序&#xff0c;选择modify&#xff0c;然后点击 command client tools&#xff0c;安装命令行工具 2、update脚本 echo 开始更新SVN目录&…...

C语言程序设计(入门)

考虑到期末临近&#xff0c;大一的学生该考c语言程序设计了吧&#xff0c;整一小篇给爱摆烂的小萌新复习一下降低挂科的风险 1. 1.1 main 函数 int main() {// 程序的入口&#xff0c;执行从这里开始return 0; // 表示程序正常结束 }1.2 语句和分号 C语言中的语句以分号结束…...

短视频账号矩阵系统源码

短视频账号矩阵系统源码搭建步骤包括以下几个方面&#xff1a; 1. 确定账号类型和目标受众&#xff1a;确定要运营的短视频账号类型&#xff0c;如搞笑、美食、美妆等&#xff0c;并明确目标受众和定位。 2. 准备账号资料&#xff1a;准备相关资质和资料&#xff0c;如营业执照…...

基于SSM的在线投稿系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

PWM实验

PWM相关概念 PWM:脉冲宽度调制定时器 脉冲&#xff1a;方波信号&#xff0c;高低电平变化产生方波 周期&#xff1a;高低电平变化所需要时间 频率&#xff1a;1s钟可以产生方波个数 占空比&#xff1a;在一个方波内&#xff0c;高电平占用的百分比 宽度调制&#xff1a;占…...

Python武器库开发-flask篇之session与cookie(二十六)

flask篇之session与cookie(二十六) 在 Flask 中&#xff0c;可以使用 session 来在不同请求之间存储和传递数据。Session 在客户端和服务器端之间交换&#xff0c;但是数据存储在服务器端。 Session 与 Cookie 的区别 session 和 cookie 都可以用来在不同请求之间存储和传递…...

深度学习YOLO安检管制物品识别与检测 - python opencv 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络4 Yolov55 模型训练6 实现效果7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习YOLO安检管制误判识别与检测 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&…...

vite+react+typescript 遇到的问题

1.找不到模块“vite”。你的意思是要将 “moduleResolution” 选项设置为 “node”&#xff0c;还是要将别名添加到 “paths” 选项中 tsconfig.json 中 compilerOptions:{“moduleResolution”: node} 2.未知的编译器选项“allowImportingTsExtensions” 该选项用于控制是否…...

数据结构及八种常用数据结构简介

data-structure 数据结构是一种存在某种关系的元素的集合。“数据” 是指元素&#xff1b;“结构” 是指元素之间存在的关系&#xff0c;分为 “逻辑结构” 和 “物理结构&#xff08;又称存储结构&#xff09;”。 常用的数据结构有 数组&#xff08;array&#xff09;、栈&…...

阿里云配置ssl(Apache)

阿里云申请证书&#xff0c;有个专门的免费的申请方式与普通证书是平级的功能&#xff1b; 访问服务器&#xff0c;判断apache是不是开启ssl功能&#xff0c;如果没有安装就安装它 [rootcentos ~]# rpm -qa | grep mod_ssl //什么没显示说明没装 yum install mod_ssl openssl …...

阿里云linux升级新版本npm、nodejs

在阿里云服务器上编译部署NextJS工程发现 alibaba linux默认yum install npm安装的版本太低, 使用以下方式升级node、npm新版本。 1、卸载现有版本 yum remove nodejs npm -y2、安装新版本 sudo yum install https://rpm.nodesource.com/pub_21.x/nodistro/repo/nodesource-…...

如何在el-tree懒加载并且包含下级的情况下进行数据回显-02

上一篇文章如何在el-tree懒加载并且包含下级的情况下进行数据回显-01对于el-tree懒加载&#xff0c;包含下级的情况下&#xff0c;对于回显提出两种方案&#xff0c;第一种方案有一些难题无法解决&#xff0c;我们重点来说说第二种方案。 第二种方案是使用这个变量对其是否全选…...