利用 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.rhkb.cn/news/230713.html

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

相关文章

Prometheus-AlertManager 邮件告警

环境,软件准备 本次演示环境&#xff0c;我是在虚拟机上安装 Linux 系统来执行操作&#xff0c;以下是安装的软件及版本&#xff1a; System: CentOS Linux release 7.6Docker: 24.0.5Prometheus: v2.37.6Consul: 1.6.1 docker 安装prometheus,alertmanage,说明一下这里直接将…

ArrayList学生管理系统

文章目录 1.ArrayList集合和数组的优势对比&#xff1a;1.1 ArrayList类概述1.2 ArrayList类常用方法1.2.1 构造方法1.2.2 成员方法1.2.3 示例代码 1.3 ArrayList存储字符串并遍历1.3.1 案例需求1.3.2 代码实现 1.4 ArrayList存储学生对象并遍历1.4.1 案例需求1.4.2 代码实现 1…

【LLM】大型语言模型综述论文

今天我将与大家分享一篇精彩的论文。这项调查提供了LLM文献的最新综述&#xff0c;这对研究人员和工程师来说都是一个有用的资源。 为什么选择LLM&#xff1f; 当参数尺度超过一定水平时&#xff0c;这些扩展的语言模型不仅实现了显著的性能改进&#xff0c;而且还表现出一些…

uniCloud 云数据库(新建表、增、删、改、查)

新建表结构描述文件 todo 为自定义的表名 表结构描述文件的默认后缀为 .schema.json 设置表的操作权限 uniCloud-aliyun/database/todo.schema.json 默认的操作权限都是 false "permission": {"read": false,"create": false,"update&quo…

【熔断限流组件resilience4j和hystrix】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容起因resilience4j落地实现pom.xml依赖application.yml配置接口使用 hystrix 落地实现pom.xml依赖启动类上添加注解接口上使用 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟…

华为HCIE-Datacom课程介绍

厦门微思网络HCIE-Datacom课程介绍 一、认证简介 HCIE-Datacom&#xff08;Huawei Certified ICT Expert-Datacom&#xff09;认证是华为认证体系中的顶级认证&#xff0c;HCIE-Datacom认证定位具备坚实的企业网络跨场景融合解决方案理论知识&#xff0c;能够使用华为数通产品…

C语言-环境搭建

文章目录 内容Notepad的安装gcc编译工具的配置 编写软件的安装&#xff1a;软件传送门&#xff1a;Notepad软件选择一个合适的路径&#xff0c;一键傻瓜式安装即可 编译工具gcc在windows环境下的配置&#xff1a;解压gcc编辑工具包解压出来的mingw64文件放到一个合适的磁盘路径…

从0到1入门C++编程——03 内存分区、引用、函数高级应用

文章目录 一、内存分区二、引用三、函数的高级应用1.默认参数2.占位参数3.函数重载 一、内存分区 C程序在执行时&#xff0c;会将内存大致分为4个区&#xff0c;分别是代码区、全局区、栈区和堆区。 代码区用来存放函数体和二进制代码&#xff0c;由操作系统进行管理。 全局区…

Kubernetes-网络

一. 前言 flannel两种容器跨主机通信的方案&#xff0c;其中UDP模式是IP in UDP&#xff0c;即三层报文封装在UDP数据包中通信&#xff1b;而vxlan模式则是MAC in UDP&#xff0c;即二层报文封装在UDP数据包中通信 flannel UDP模式和vxlan模式都对数据包做了封解包&#xff0c…

为什么我不建议大学生接公司单?

大家好&#xff0c;我是鱼皮。前两天&#xff0c;我 编程导航 的鱼友提了个问&#xff1a;大学生怎么接公司的单赚点零花钱&#xff1f; 然后我很认真地评论了一句&#xff1a;我不建议大学生接公司单。 这位小伙伴很认真&#xff0c;又通过微信单独问我&#xff1a; 想了想&am…

Mybatis行为配置之Ⅰ—缓存

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…

【Midjourney】AI绘画新手教程(一)登录和创建服务器,生成第一幅画作

一、登录Discord 1、访问Discord官网 使用柯學尚网&#xff08;亲测非必须&#xff0c;可加快响应速度&#xff09;访问Discord官方网址&#xff1a;https://discord.com 选择“在您的浏览器中打开Discord” 然后&#xff0c;注册帐号、购买套餐等&#xff0c;在此不做缀述。…

3D目标检测(教程+代码)

随着计算机视觉技术的不断发展&#xff0c;3D目标检测成为了一个备受关注的研究领域。与传统的2D目标检测相比&#xff0c;3D目标检测可以在三维空间中对物体进行定位和识别&#xff0c;具有更高的准确性和适用性。本文将介绍3D目标检测的相关概念、方法和代码实现。 一、3D目…

Python消消乐小游戏(PyGame)

文章目录 写在前面喜羊羊与灰太狼PyGame入门消消乐注意事项写在后面 写在前面 本期内容&#xff1a;基于pygame实现喜羊羊与灰太狼版消消乐小游戏 实验环境 python3.11及以上pycharmpygame 安装pygame的命令&#xff1a; pip install -i https://pypi.tuna.tsinghua.edu.c…

Redis (三)

1、redis复制 简单的概括就是主从复制&#xff0c;master以写为主&#xff0c;Slave以读为主&#xff0c;当master数据发生变化的时候&#xff0c;自动将更新的数据异步同步到其他的slave是数据库。 使用这种机制的话&#xff0c;可以做到读写分离&#xff0c;可以减轻主机负担…

chromium在中文用户名下无法编译的问题

新电脑没有太注意&#xff0c;起用户名的时候用了中文。 在编译chromium104的代码时&#xff0c;因为环境变量有中文导致编译失败&#xff1a; 因为我的电脑默认是使用gbk编码&#xff0c;而不是utf-8编码。 这个问题有三种解决办法&#xff1a; &#xff08;一&#xff09;把…

CMake入门教程【核心篇】添加依赖(add_dependencies)

&#x1f608;「CSDN主页」&#xff1a;传送门 &#x1f608;「Bilibil首页」&#xff1a;传送门 &#x1f608;「本文的内容」&#xff1a;CMake入门教程 &#x1f608;「动动你的小手」&#xff1a;点赞&#x1f44d;收藏⭐️评论&#x1f4dd; 文章目录 1. 基本用法2. 添加目…

DBA技术栈(二):MySQL 存储引擎

2.1 MySQL存储引擎概述 上个业余的图&#xff1a; MyISAM 存储引擎是 MySQL 默认的存储引擎&#xff0c;也是目前 MySQL 使用最为广泛的存储引擎之一。他的前身就是我们在 MySQL 发展历程中所提到的 ISAM&#xff0c;是 ISAM 的升级版本。在 MySQL最开始发行的时候是 ISAM 存…

【LMM 008】Instruction Tuning with GPT-4

论文标题&#xff1a;Instruction Tuning with GPT-4 论文作者&#xff1a;Baolin Peng, Chunyuan Li, Pengcheng He, Michel Galley, Jianfeng Gao 作者单位&#xff1a;Microsoft Research 论文原文&#xff1a;https://arxiv.org/abs/2304.03277 论文出处&#xff1a;– 论文…

在Ubuntu22.04上部署Stable Diffusion

在AI绘画软件领域Stable-Diffusion&#xff08;简称SD&#xff09;在开源领域绝对是不二之选&#xff0c;他的插件方式可以让此软件具有更多的功能&#xff0c;开发者社群为此提供了大量免费高质量的外接预训练模型&#xff08;fine-tune&#xff09;和插件&#xff0c;并持续维…