利用 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.hqwc.cn/news/307298.html

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

相关文章

GrayLog日志平台的基本使用-ssh之Email报警

1、首先编辑并添加邮件配置到server.conf&#xff08;注意&#xff1a;是添加&#xff09; vim /etc/graylog/server/server.conf # Email transport transport_email_enabled true transport_email_hostname smtp.qq.com transport_email_port 465 transport_email_use_a…

基于CNN和双向gru的心跳分类系统

CNN and Bidirectional GRU-Based Heartbeat Sound Classification Architecture for Elderly People是发布在2023 MDPI Mathematics上的论文&#xff0c;提出了基于卷积神经网络和双向门控循环单元(CNN BiGRU)注意力的心跳声分类&#xff0c;论文不仅显示了模型还构建了完整的…

财务咨询公司为何要搭建自己的线上课程平台,而非入驻其他公域流量平台

明理信息科技线上课程平台 财务咨询公司为何要搭建自己的线上课程平台&#xff0c;而非入驻其他公域流量平台 随着数字化时代的到来&#xff0c;线上教育和学习已经成为越来越多人的选择。对于财务咨询公司来说&#xff0c;搭建自己的线上课程平台不仅可以更好地满足客户需求…

git 常用基本命令, reset 回退撤销commit,解决gitignore无效,忽略记录或未记录远程仓库的文件,删除远程仓库文件

git 基本命令 reset 撤销commit https://blog.csdn.net/a704397849/article/details/135220091 idea 中 rest 撤销commit过程如下&#xff1a; Git -> Rest Head… 在To Commit中的HEAD后面加上^&#xff0c;点击Reset即可撤回最近一次的尚未push的commit Reset Type 有三…

盘点 | 飞凌嵌入式这5款100%全国产核心板值得推荐

近期&#xff0c;飞凌嵌入式有5款核心板产品通过了中国赛宝实验室的权威认证&#xff0c;实现了100%的电子元器件国产化率&#xff0c;本篇文章小编就带大家盘点一下这5款产品。 一、FET3568-C系列核心板 FET3568-C和FET3568J-C核心板基于Rockchip RK3568系列处理器开发设计&am…

C++初阶(十七)模板进阶

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、非类型模板参数二、模板的特化1、概念2、函数模板特化3、类模板特化1、全特化2、偏特化 三…

Hadoop之Yarn 详细教程

1、yarn 的基本介绍和产生背景 YARN 是 Hadoop2 引入的通用的资源管理和任务调度的平台&#xff0c;可以在 YARN 上运行 MapReduce、Tez、Spark 等多种计算框架&#xff0c;只要计算框架实现了 YARN 所定义的 接口&#xff0c;都可以运行在这套通用的 Hadoop 资源管理和任务调…

yolov8实战第四天——yolov8图像分类 ResNet50图像分类(保姆式教程)

yolov8实战第一天——yolov8部署并训练自己的数据集&#xff08;保姆式教程&#xff09;_yolov8训练自己的数据集-CSDN博客在前几天&#xff0c;我们使用yolov8进行了部署&#xff0c;并在目标检测方向上进行自己数据集的训练与测试&#xff0c;今天我们训练下yolov8的图像分类…

macos Jetbrains IDEA用户自定义vm配置信息存储路径, IDEA点击无反应 无法打开问题解决

Jetbrains Clion, IDEA 用户在应用里面修改了自定义的VM配置后的存储路径为 ~/Library/Application Support/JetBrains/xxx2023.3/xxx.vmoptions xxx为你安装的APP名称, 如 Clion .IntelliJIdea 这里的自定义配置如果配置有误就会直接导致JetBrains软件无法打开, 即 点击打开…

HBase 集群搭建

文章目录 安装前准备兼容性官方网址 集群搭建搭建 Hadoop 集群搭建 Zookeeper 集群解压缩安装配置文件高可用配置分发 HBase 文件 服务的启停启动顺序停止顺序 验证进程查看 Web 端页面 安装前准备 兼容性 1&#xff09;与 Zookeeper 的兼容性问题&#xff0c;越新越好&#…

Prometheus 14 点实践经验分享

这是 2017 年的 promcon 的分享&#xff0c;原文地址在这里&#xff0c;作者 Julius Volz&#xff0c;今天偶然看到&#xff0c;虽然已经过去 6 年&#xff0c;有些实践经验还是非常值得学习。做个意译&#xff0c;加入一些自己的理解&#xff0c;分享给大家。 埋点方面 1. 所…

Java创建线程执行任务的方法(一)

目录 1.继承Thread类 2.实现Runnab类 2.1实现Runnable类 2.2使用Lambda表达式 3.实现Callable类 3.1返回Integer类型数据 3.2返回String类型数据 3.3返回Object类型数据 4.匿名内部类 创建线程的方法&#xff1a;继承Thread类&#xff1b;实现Runnab类&#xff1b;匿名…