利用 PEB_LDR_DATA 结构枚举进程模块信息

1. 引言

我们常常通过很多方法来获取进程的模块信息,例如 EnumProcessModules 函数、CreateToolhelp32Snapshot 函数、WTSEnumerateProcesses 函数、ZwQuerySystemInformation 函数等。但是调用这些接口进行模块枚举的原理是什么我们并不知道。通过学习 PEB 中 PEB_LDR_DATA 结构的知识,我们可以对进程模块信息的查询以及相关存储数据结构有进一步的了解。

2. 技术细节

2.1 基本原理

在开始使用 TEB/PEB 获取进程的模块信息之前,我想有必要解释一下这两个名词:PEB 指的是进程环境块(Process Environment Block),用于存储进程状态信息和进程所需的各种数据。每个进程都有一个对应的 PEB 结构体。TEB 指的是线程环境块(Thread Environment Block),用于存储线程状态信息和线程所需的各种数据。每个线程同样都有一个对应的 TEB 结构体。

PEB 中包含了进程的代码、数据段指针、进程的环境变量、进程启动参数信息以及加载的模块信息等。在 x86-32 体系下,FS 段寄存器偏移 0x30 处存放了索引,索引查找的指针指向当前进程的 PEB 结构体,在 x86-64 下该指针位于 FS 段寄存器偏移 0x60 处。其他进程可以通过访问自己的  PEB 结构体来获取自己的状态和信息。

TEB 中包含了线程的堆栈指针、TLS(线程本地存储)指针、异常处理链表指针、用户模式分页表指针等信息。在 x86-32 体系下,FS 段寄存器偏移 0x18 处通常为指向 TEB 结构体的指针,在 x86-64 下该指针位于 FS 段寄存器偏移 0x30 处。其他线程可以通过访问自己的 TEB 结构体来获取自己的状态和信息。

通常,我们可以通过下面的代码在 MSVC 编译器中通过寄存器获得 PEB 结构体指针:

#ifdef _WIN64PPEB_LDR_DATA64 pPebLdrData = NULL;ULONGLONG ModuleSum = NULL;PPEB64 peb = (PPEB64)__readgsqword(0x60);
#elsePPEB_LDR_DATA32 pPebLdrData = NULL;ULONG ModuleSum = NULL;PPEB32 peb = (PPEB32)__readfsdword(0x30);
#endif

而对于 PEB 结构体,微软是没有公开文档的,需要自己进行重定义(原始结构体定义中缺少我们需要的部分),经查阅逆向文献,得到如下的结构体定义(部分不需要用到的成员已经被截断):

typedef struct _PEB_LDR_DATA32
{ULONG Length; // +0x00BOOLEAN Initialized; // +0x04PVOID SsHandle; // +0x08LIST_ENTRY InLoadOrderModuleList; // +0x0cLIST_ENTRY InMemoryOrderModuleList; // +0x14LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24typedef struct _PEB32
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};PVOID Mutant;                                                           //0x4PVOID ImageBaseAddress;                                                 //0x8PEB_LDR_DATA32* Ldr;                                              //0xcRTL_USER_PROCESS_PARAMETERS* ProcessParameters;                 //0x10PVOID SubSystemData;                                                    //0x14PVOID ProcessHeap;                                                      //0x18RTL_CRITICAL_SECTION* FastPebLock;                              //0x1cSLIST_HEADER* volatile AtlThunkSListPtr;                         //0x20PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;typedef struct _STRING64
{USHORT Length;                                                          //0x0USHORT MaximumLength;                                                   //0x2ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;typedef struct _PEB_LDR_DATA64
{ULONG Length;                                                           //0x0UCHAR Initialized;                                                      //0x4PVOID SsHandle;                                                         //0x8LIST_ENTRY InLoadOrderModuleList;                               //0x10LIST_ENTRY InMemoryOrderModuleList;                             //0x20LIST_ENTRY InInitializationOrderModuleList;                     //0x30PVOID EntryInProgress;                                                  //0x40UCHAR ShutdownInProgress;                                               //0x48PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, *PPEB_LDR_DATA64;typedef struct _PEB64
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};UCHAR Padding0[4];                                                      //0x4ULONGLONG Mutant;                                                       //0x8ULONGLONG ImageBaseAddress;                                             //0x10PEB_LDR_DATA64* Ldr;                                                          //0x18ULONGLONG ProcessParameters;                                            //0x20ULONGLONG SubSystemData;                                                //0x28ULONGLONG ProcessHeap;                                                  //0x30ULONGLONG FastPebLock;                                                  //0x38ULONGLONG AtlThunkSListPtr;                                             //0x40ULONGLONG IFEOKey;                                                      //0x48
}PEB64, *PPEB64;

下面,我们分析一下为什么可以通过如此复杂的 PEB 结构获取模块信息。

2.1.1 PEB_LDR_DATA 结构体

以 x86-32 为例:PEB 结构体中偏移为 0xC 的成员变量是 Ldr 该变量是一个指向 PEB_LDR_DATA 结构体的指针。

下面我们来看一下该结构体的部分定义:

typedef struct _PEB_LDR_DATA32
{ULONG Length;                                 // +0x00BOOLEAN Initialized;                          // +0x04PVOID SsHandle;                               // +0x08LIST_ENTRY InLoadOrderModuleList;             // +0x0cLIST_ENTRY InMemoryOrderModuleList;           // +0x14LIST_ENTRY InInitializationOrderModuleList;   // +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32;              // +0x24

该结构体的第一个变量 Length 表示链表长度信息,大小是结点数乘以当前范围的大小(x32 是0x4,x64 是 0x8),最后再减去 1。

然后从偏移 0xC 开始,就是三个 LIST_ENTRY 链表的头结点,链表中结点数据类型都是 LIST_ENTRY,只是链表的排序模式不同。

2.2.2 LIST_ENTRY 结构体

LIST_ENTRY 结构是模块链表结构里的结点数据结构,它包含两个成员指针, Flink 指向下一个链表结点,Blink 指向前一个链表结点。模块链表属于一种双向链表的数据结构。

typedef struct _LIST_ENTRY {struct _LIST_ENTRY *Flink;        // 后驱指针struct _LIST_ENTRY *Blink;        // 前驱指针
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

2.2.3 LDR_DATA_TABLE_ENTRY 结构体

对于每一个指针,它实际指向的数据结构并不是 LIST_ENTRY 结构体,而是 LDR_DATA_TABLE_ENTRY 结构体,这是每一个结点指向的模块信息数据结构。

typedef struct _LDR_DATA_TABLE_ENTRY
{LIST_ENTRY InLoadOrderLinks;               // 0x0LIST_ENTRY InMemoryOrderLinks;             // 0x8LIST_ENTRY InInitializationOrderLinks;     // 0x10PVOID DllBase;                             // 0x18PVOID EntryPoint;                          // 0x1cULONG SizeOfImage;                         // 0x20UNICODE_STRING FullDllName;                // 0x24UNICODE_STRING BaseDllName;                // 0x2c
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; // 0xa4

对于该结构中的 DllBase 是当前模块的基址,EntryPoint 是模块的入口地址,SizeOfImage 是映像大小,FullDllName 是模块完整路径字符串。

可以通过遍历链表结点的方式获取所有模块的信息: LDR_DATA_TABLE_ENTRY 结构中的 LIST_ENTRY 结构对应下一个 LDR_DATA_TABLE_ENTRY 结点中的 LIST_ENTRY 结构。

如:头结点中的 InInitializationOrderModuleList 是一个 LIST_ENTRY 结构,该结构中 Flink 指针指向的是第二个结点的首地址。而不是另外两个 LIST_ENTRY 结构。然后,第二个结点的 Flink 指向第三个结点,依此类推。

于是,头结点的 Blink 指向最后一个结点,而最后一个结点的 Blink 指向它前一个结点,依次链接到前一个结点,直到第二个结点的 Blink 指向头结点,可以看出这是一个循环链表。同理,Flink 后驱指针也是这样的,一直指向后一个结点,最后一个数据的后驱指针指向头结点。可以说,整个 LDR_DATA_TABLE_LIST 是一个闭环双向链表。

相信已经注意到了 LIST_ENTRY 结构和 LDR_DATA_TABLE_ENTRY 结构并不一样,这个链表到底是如何链接的呢?

实际上,仔细观察就会发现, LDR_DATA_TABLE_ENTRY 结构的成员变量就有不同排序模式下结点的 LIST_ENTRY ,这个和 PEB_LDR_DATA 中的 LIST_ENTRY* 是一致的,也就是说, PEB_LDR_DATA 中的 LIST_ENTRY 头结点里面的 Flink 指针是指向一个 LDR_DATA_TABLE_ENTRY 表中 LIST_ENTRY 成员的指针。也就是说,这里的数据结构有一个特点,就是他是利用表的数据结构将表中的指针成员映射到一个链表的数据结构中,就像手账本将纸张串联在一起一样。

LDR_DATA_TABLE_ENTRY 通过 LIST_ENTRY 映射到一个双向链表中,并且该 LIST_ENTRY 链表是闭环的,即末尾结点的后驱指针不是指向 NULL,而是指向头结点;头结点的前驱指针也不是指向 NULL,而是指向末尾结点。LIST_ENTRY 相当于链表中每个 LDR_DATA_TABLE_ENTRY 结构的入口媒介,因为我们可以通过同样的映射关系(偏移地址)逆映射出  LDR_DATA_TABLE_ENTRY  的地址。

2.2 通过成员变量的地址定位结构体

2.2.1 空指针的特殊作用

空指针往往是不能够进行访问的,但是对于指向结构体的指针变量来说,如果他是一个 nullptr,那么,结构体将向着 0 地址对齐。于是,我们可以通过指针引用获取结构体中成员变量的地址,该地址是相对于 0 地址而言的,所以,它实际上是结构体中成员相对于该结构首地址的偏移量。

例如下面的代码,就是利用了该性质准确获取成员变量的偏移(因为编译优化,数据结构内部变量的排序和对齐方式可能会被编译期调整,所以通过这种方式获取的偏移比直接硬编码的稳定安全):

struct Node
{int Flink;float Blink;
};// typedef unsigned long long uint64_t; in x86-64 systemNode* pNode = nullptr;
uint64_t offset_F = (uint64_t)(&(pNode->Flink));  // offset_F==0
uint64_t offset_B = (uint64_t)(&(pNode->Blink));  // offset_B==4

我们可以正确得到成员的偏移。但是,试想一下,如果我们知道一个结构体的某个成员变量的地址,那么我们如何定位该结构体的首地址呢?

很容易想到,成员的地址 - 该成员的偏移量 = 结构体的首地址。

2.2.2 CONTAINING_RECORD 宏

CONTAINING_RECORD 宏的定义位于 winnt.h 中,如下所示:

//
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//#define CONTAINING_RECORD(address, type, field) ((type *)( \(PCHAR)(address) - \(ULONG_PTR)(&((type *)0)->field)))

CONTAINING_RECORD 宏的功能,是根据某个结构体中成员变量的地址,计算出该结构体的首地址。

参数解释:

  • address,成员变量的地址
  • type,结构体的数据类型
  • field,成员变量名

该宏定义内部的运算原理,就是前面分析的使用 0 指针获取成员偏移,然后再使用成员变量地址 - 成员的偏移,就得到了结构体的首地址。

3. 原理验证

3.1 代码实现

在验证代码中,我们进行了以下操作:

  1. 加载模块:首先,我们利用 LoadLibrary 加载了 advapi32.dll 用于测试。
  2. 通过寄存器获取指向 PEB 的指针:通过 fs 或 gs 寄存器索引偏移获取 PPEB 的值。该指针指向  进程的 PEB 结构。
  3. 获取 PEB_LDR_DATA 结构: PEB 结构体的 Ldr 成员变量是指向 PEB_LDR_DATA 结构的指针。
  4. 获取头结点 LIST_ENTRY 结构: PEB_LDR_DATA 结构的 LIST_ENTRY 对应三个链表各自的头结点。
  5. 通过宏获取实际的 LDR_DATA_TABLE_ENTRY 结构:通过头结点的 Flink 指向的地址,获取第一个 LDR_DATA_TABLE_ENTRY 结构的地址,这个是链表存放数据的第一个实结点。
  6. 通过结构读取链接库信息: LDR_DATA_TABLE_ENTRY 结构的多个成员包含了 Dll 的加载信息,通过读取该信息,可以完成功能要求。
  7. 遍历该过程并打印所有结点:通过遍历每一个实结点的 LIST_ENTRY 映射结点,通过映射的 Flink 找到下一个结点,然后逐个打印结点,直到 Flink 指向的下一个结点回到头结点为止。至此,遍历结束。
  8. 卸载模块和进程退出:用 FreeLibrary 卸载用于测试的模块。

下面是以上功能的完整实现代码:

#include <iostream>
#include <windows.h>  
#include <winternl.h>
#include <TlHelp32.h>// 这部分的结构体需要自己重写一下typedef struct _PEB_LDR_DATA32
{ULONG Length; // +0x00BOOLEAN Initialized; // +0x04PVOID SsHandle; // +0x08LIST_ENTRY InLoadOrderModuleList; // +0x0cLIST_ENTRY InMemoryOrderModuleList; // +0x14LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24typedef struct _PEB32
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};PVOID Mutant;                                                           //0x4PVOID ImageBaseAddress;                                                 //0x8PEB_LDR_DATA32* Ldr;                                              //0xcRTL_USER_PROCESS_PARAMETERS* ProcessParameters;                 //0x10PVOID SubSystemData;                                                    //0x14PVOID ProcessHeap;                                                      //0x18RTL_CRITICAL_SECTION* FastPebLock;                              //0x1cSLIST_HEADER* volatile AtlThunkSListPtr;                         //0x20PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;typedef struct _STRING64
{USHORT Length;                                                          //0x0USHORT MaximumLength;                                                   //0x2ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;typedef struct _PEB_LDR_DATA64
{ULONG Length;                                                           //0x0UCHAR Initialized;                                                      //0x4PVOID SsHandle;                                                         //0x8LIST_ENTRY InLoadOrderModuleList;                               //0x10LIST_ENTRY InMemoryOrderModuleList;                             //0x20LIST_ENTRY InInitializationOrderModuleList;                     //0x30PVOID EntryInProgress;                                                  //0x40UCHAR ShutdownInProgress;                                               //0x48PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, *PPEB_LDR_DATA64;typedef struct _PEB64
{UCHAR InheritedAddressSpace;                                            //0x0UCHAR ReadImageFileExecOptions;                                         //0x1UCHAR BeingDebugged;                                                    //0x2union{UCHAR BitField;                                                     //0x3struct{UCHAR ImageUsesLargePages : 1;                                    //0x3UCHAR IsProtectedProcess : 1;                                     //0x3UCHAR IsImageDynamicallyRelocated : 1;                            //0x3UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3UCHAR IsPackagedProcess : 1;                                      //0x3UCHAR IsAppContainer : 1;                                         //0x3UCHAR IsProtectedProcessLight : 1;                                //0x3UCHAR IsLongPathAwareProcess : 1;                                 //0x3};};UCHAR Padding0[4];                                                      //0x4ULONGLONG Mutant;                                                       //0x8ULONGLONG ImageBaseAddress;                                             //0x10PEB_LDR_DATA64* Ldr;                                                          //0x18ULONGLONG ProcessParameters;                                            //0x20ULONGLONG SubSystemData;                                                //0x28ULONGLONG ProcessHeap;                                                  //0x30ULONGLONG FastPebLock;                                                  //0x38ULONGLONG AtlThunkSListPtr;                                             //0x40ULONGLONG IFEOKey;                                                      //0x48
}PEB64, *PPEB64;int main(void)
{setlocale(NULL, "chs");PLDR_DATA_TABLE_ENTRY pLdrDataEntry = NULL;PLIST_ENTRY pListEntryStart = NULL, pListEntryEnd = NULL;// 1、加载链接库用于测试HMODULE hdll = LoadLibraryW(L"advapi32.dll");// 2、通过寄存器偏移访问 PEB 
#ifdef _WIN64PPEB_LDR_DATA64 pPebLdrData = NULL;ULONGLONG ModuleSum = NULL;PPEB64 peb = (PPEB64)__readgsqword(0x60);
#elsePPEB_LDR_DATA32 pPebLdrData = NULL;ULONG ModuleSum = NULL;PPEB32 peb = (PPEB32)__readfsdword(0x30);
#endif// 3、通过 PEB 的 Ldr 成员获取 PEB_LDR_DATA 结构  pPebLdrData = peb->Ldr;// 4、通过 PEB_LDR_DATA 的 InMemoryOrderModuleList 成员获取 LIST_ENTRY 结构  pListEntryStart = pPebLdrData->InMemoryOrderModuleList.Flink;pListEntryEnd = pPebLdrData->InMemoryOrderModuleList.Blink;// 6、查找所有已载入到内存中的模块  for (u_int i = 0; pListEntryStart != pListEntryEnd; i++){// 7、通过 LIST_ENTRY 的 Flink 成员获取 LDR_DATA_TABLE_ENTRY 结构pLdrDataEntry = (PLDR_DATA_TABLE_ENTRY)CONTAINING_RECORD(pListEntryStart, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);// 8、输出 LDR_DATA_TABLE_ENTRY 的 BaseDllName 或 FullDllName 成员信息
#ifdef _WIN64printf("[第 %d 模块] \n\t路径:%ws \n\t基址:0x%0I64X\n", i + 1,pLdrDataEntry->FullDllName.Buffer, reinterpret_cast<uint64_t>(pLdrDataEntry->DllBase));
#elseprintf("[第 %d 模块] \n\t路径:%ws \n\t基址:0x%0IX\n",i + 1,pLdrDataEntry->FullDllName.Buffer,reinterpret_cast<uint32_t>(pLdrDataEntry->DllBase));
#endif // _WIN64pListEntryStart = pListEntryStart->Flink;}// 卸载 DLLFreeLibrary(hdll);system("pause");return 0;
}

3.2 执行效果截图

这是 wow64上运行 86 位模式编译的程序。

4. 小结

通过对 PEB 中 PEB_LDR_DATA 的理解,我们发现模块的遍历用到了非常巧妙的数据结构和组织逻辑,比如他给出三种不同方式排序的链表:按加载顺序、按进程初始化载入顺序、按内存中排列顺序,这有利于对不同类型模块优化链表的查找性能,并且采用了映射结构的闭环双向链表,数据之间的插入删除操作也能够提升效率,相当于是一个“组合怪”。


更新于:2023.12.29

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

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

相关文章

Stable Diffusion WebUI安装合成面部说话插件SadTalker

SadTalker可以根据一张图片、一段音频&#xff0c;合成面部说这段语音的视频。图片需要真人或者接近真人。 安装ffmpeg 下载地址&#xff1a; https://www.gyan.dev/ffmpeg/builds/ 下载ffmpeg-git-full.7z 后解压&#xff0c;将解压后的目录\bin添加到环境变量的Path中。 在…

出海合规云安全,AWS Landing Zone解决方案建立安全着陆区

在出海的大环境中&#xff0c;企业数字化转型的趋势之一就是上云。然而&#xff0c;上云也带来了新的挑战&#xff0c;特别是对企业的 IT 建设和管理提出了更高的要求。为了构建一个安全合规的云上信息系统环境&#xff0c;满足企业中不同用户的快速增长、资源访问可控、成本可…

Tips:电池电源电压转换为220V

今天在进行操作的时候&#xff0c;看到一个新的东西&#xff0c;就是如何普通的电瓶电压转化为220V交流电。 当在室外或者工作地距离电源比较远的情况下&#xff0c;一般是选取拉线的方式进行采电&#xff0c;但是当距离电源过远&#xff0c;使用拉线的方式就不可用了。如何在…

2023年最后一篇博客!

首先祝大家周末快乐&#xff01;元旦快乐&#xff01; 今天不聊技术哦 01哀叹2023 2023年只剩2天了&#xff01;转眼间又是一年到头了&#xff0c;这一年我做了什么呢&#xff1f;回过头来一想&#xff0c;什么都没有做。也尝试过做不少事情&#xff0c;但都是没有一个好的结…

小程序中拖拽和缩放图片

需求&#xff1a;点击元素后选中&#xff0c;出现缩放按钮&#xff0c;拖动缩放按钮可实现元素的缩放&#xff1b;并且元素本身是可以拖动的。 html&#xff1a; <block wx:for"{{imageControls}}" wx:key"index"><view hidden"{{item.hidd…

对SPI总线上挂接多个X5045的读写操作

#include<reg51.h> //包含单片机寄存器的头文件 #include<intrins.h> //包含_nop_()函数定义的头文件 sbit SCKP3^4; //将SCK位定义为P3.4引脚 sbit SIP3^5; //将SI位定义为P3.5引脚 sbit SOP3^6; //将SO位定义为P3.6引脚 sbit CS1P3^7; …

华为发布的工业软件三大难题:面向CAE分析的高质量曲面贴体网格的生成问题

以下内容转载&#xff1a; 网格生成&#xff0c;是把一个特定的研究区域分割成由许多很小的子区域(元素)&#xff0c;以满足一些特定的要求。在理想的情况下&#xff0c;网格中的每个元素的形状和分布可以通过一种自动的网格生成算法来确定。 结构网格生成的代数网格生成法和…

10.Go 映射

映射&#xff08;map&#xff09;是一种特殊的数据结构&#xff0c;用于存储一系列无序的键值对&#xff0c;映射基于键来存储数据。映射功能强大的地方是&#xff0c;能够基于键快速检索数据。键就像索引一样&#xff0c;指向与该键关联的值。与C、Java中的映射的不同之处在于…

挑战Python100题(7)

100+ Python challenging programming exercises 7 Question 61 Print a unicode string "hello world". Hints: Use ustrings format to define unicode string. 打印一个unicode字符串“helloworld”。 提示:使用u“字符串”格式定义unicode字符串。 Solution…

提升Windows系统安全性的一些有效的策略

假设一个杀猪的机器人感染了病毒&#xff0c;把人识别成了猪&#xff0c;&#xff0c;&#xff0c;&#xff0c;&#xff0c; 1&#xff1a;我偶然发现的&#xff1a;把所有向外的UDP都禁止&#xff0c;但是要开放53号端口&#xff0c;因为这是DNS通讯端口&#xff0c;没有这个…

Vue - 使用Element UI Upload / importExcelJs进行文件导入

1 情景一 需求背景&#xff1a;后端配合&#xff0c;点击"导入"按钮&#xff0c;弹出“导入”弹窗&#xff0c;将电脑本地Excel表格数据导入到页面中表格位置&#xff08;需要调用后端接口&#xff09;&#xff0c;而页面中表格通过后端接口获取最新数据。 实现思路…

从零开始部署CTF题目环境(docker容器)

本教程将教会大家如何安装一台可以部署docker容器形式的CTF题目的CentOS服务器。 操作步骤 1-下载操作系统镜像文件 虚拟操作系统&#xff1a;CentOS 8 &#xff08;CentOS 9 毛病多&#xff0c;先不装&#xff09; 镜像文件下载地址&#xff0c;点击X86_64即可 CentOS St…

自定义docker镜像,ubuntu安装命令并导出

文章目录 问题现象解决步骤相关命令详细介绍docker save 与 docker loaddocker import 与 docker exportdocker commit 问题现象 我们的通讯服务&#xff0c;需要监测前端设备的在线情况&#xff08;是否在线、丢包率、延迟等&#xff09;&#xff0c;使用ping命令去实现此功能…

手把手教你配置Jenkins自动化邮件通知

完成基于Jenkins的持续集成部署后&#xff0c;自动化测试执行后&#xff0c;测试结果需要通知到相关人员&#xff0c;除了钉钉通知外我们还可以通过Email通知到对应负责人&#xff0c;这里记录一下测试结果通过Jenkins邮件通知的配置与部署 01、安装插件 方法1&#xff1a; 进…

第P8周:YOLOv5-C3模块实现

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营](https://mp.weixin.qq.com/s/rbOOmire8OocQ90QM78DRA) 中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制](https://mtyjkh.blog.csdn.net/)** 一、 前期准备 1. 设…

c基础学习(一)

学习网站&#xff1a; C语言的过去与未来 - C语言教程 - C语言网 (dotcpp.com)https://www.dotcpp.com/course/c-intros/ C 语言简介 - C 语言教程 - 网道 (wangdoc.com)https://wangdoc.com/clang/intro 变量&#xff1a; #include<stdio.h> /*引入头文件-- 标准…

git 常用命令总结

git 工作原理图&#xff1a; git 常用命令及解释: 命令解释例子git init在当前目录初始化一个新的 Git 仓库。git initgit clone <repository>克隆一个远程仓库到本地。git clone https://github.com/example/repository.gitgit add <file>将文件的变化添加到暂存…

Gaussian-Splatting 训练并导入Unity中

这个周末玩点啥~&#x1f41e; &#x1f365;环境安装&#x1f4a1;安装C编译工具&#x1f4a1;安装Python&#x1f4a1;安装CUDA&#x1f4a1;添加ffmpeg到环境变量Path添加COLMAP-3.8-windows-cuda文件路径到环境变量Path&#x1f4a1;pytorch安装&#x1f4a1;tqdm 安装&…

『JavaScript』全面解析JavaScript中的防抖与节流技术及其应用场景

&#x1f4e3;读完这篇文章里你能收获到 理解防抖&#xff08;Debouncing&#xff09;和节流&#xff08;Throttling&#xff09;的概念&#xff1a;了解这两种性能优化技术如何帮助我们更有效地处理频繁触发的事件掌握防抖与节流的实现方法&#xff1a;学习如何在JavaScript中…

WPF+Halcon 培训项目实战(8):WPF+Halcon初次开发

前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原因无非是想换个工作。相关的教学视频来源于下方的Up主的提供的教程。这里只做笔记分享&#xff0c;想要源码或者教学视频可以和他联系一下。 相关链接 微软系列技术教程 WPF 年度公益课程 Halcon开发 CSD…