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

news2025/1/23 17:31:02

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 _WIN64
    PPEB_LDR_DATA64 pPebLdrData = NULL;
    ULONGLONG ModuleSum = NULL;
    PPEB64 peb = (PPEB64)__readgsqword(0x60);
#else
    PPEB_LDR_DATA32 pPebLdrData = NULL;
    ULONG ModuleSum = NULL;
    PPEB32 peb = (PPEB32)__readfsdword(0x30);
#endif

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

typedef struct _PEB_LDR_DATA32
{
    ULONG Length; // +0x00
    BOOLEAN Initialized; // +0x04
    PVOID SsHandle; // +0x08
    LIST_ENTRY InLoadOrderModuleList; // +0x0c
    LIST_ENTRY InMemoryOrderModuleList; // +0x14
    LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24


typedef struct _PEB32
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    PVOID Mutant;                                                           //0x4
    PVOID ImageBaseAddress;                                                 //0x8
    PEB_LDR_DATA32* Ldr;                                              //0xc
    RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                 //0x10
    PVOID SubSystemData;                                                    //0x14
    PVOID ProcessHeap;                                                      //0x18
    RTL_CRITICAL_SECTION* FastPebLock;                              //0x1c
    SLIST_HEADER* volatile AtlThunkSListPtr;                         //0x20
    PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;

typedef struct _STRING64
{
    USHORT Length;                                                          //0x0
    USHORT MaximumLength;                                                   //0x2
    ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;

typedef struct _PEB_LDR_DATA64
{
    ULONG Length;                                                           //0x0
    UCHAR Initialized;                                                      //0x4
    PVOID SsHandle;                                                         //0x8
    LIST_ENTRY InLoadOrderModuleList;                               //0x10
    LIST_ENTRY InMemoryOrderModuleList;                             //0x20
    LIST_ENTRY InInitializationOrderModuleList;                     //0x30
    PVOID EntryInProgress;                                                  //0x40
    UCHAR ShutdownInProgress;                                               //0x48
    PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, *PPEB_LDR_DATA64;

typedef struct _PEB64
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    UCHAR Padding0[4];                                                      //0x4
    ULONGLONG Mutant;                                                       //0x8
    ULONGLONG ImageBaseAddress;                                             //0x10
    PEB_LDR_DATA64* Ldr;                                                          //0x18
    ULONGLONG ProcessParameters;                                            //0x20
    ULONGLONG SubSystemData;                                                //0x28
    ULONGLONG ProcessHeap;                                                  //0x30
    ULONGLONG FastPebLock;                                                  //0x38
    ULONGLONG AtlThunkSListPtr;                                             //0x40
    ULONGLONG 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;                                 // +0x00
    BOOLEAN Initialized;                          // +0x04
    PVOID SsHandle;                               // +0x08
    LIST_ENTRY InLoadOrderModuleList;             // +0x0c
    LIST_ENTRY InMemoryOrderModuleList;           // +0x14
    LIST_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;               // 0x0
    LIST_ENTRY InMemoryOrderLinks;             // 0x8
    LIST_ENTRY InInitializationOrderLinks;     // 0x10
    PVOID DllBase;                             // 0x18
    PVOID EntryPoint;                          // 0x1c
    ULONG SizeOfImage;                         // 0x20
    UNICODE_STRING FullDllName;                // 0x24
    UNICODE_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 system

Node* 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; // +0x00
    BOOLEAN Initialized; // +0x04
    PVOID SsHandle; // +0x08
    LIST_ENTRY InLoadOrderModuleList; // +0x0c
    LIST_ENTRY InMemoryOrderModuleList; // +0x14
    LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA32, * PPEB_LDR_DATA32; // +0x24


typedef struct _PEB32
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    PVOID Mutant;                                                           //0x4
    PVOID ImageBaseAddress;                                                 //0x8
    PEB_LDR_DATA32* Ldr;                                              //0xc
    RTL_USER_PROCESS_PARAMETERS* ProcessParameters;                 //0x10
    PVOID SubSystemData;                                                    //0x14
    PVOID ProcessHeap;                                                      //0x18
    RTL_CRITICAL_SECTION* FastPebLock;                              //0x1c
    SLIST_HEADER* volatile AtlThunkSListPtr;                         //0x20
    PVOID IFEOKey;                                                          //0x24
} PEB32, * PPEB32;

typedef struct _STRING64
{
    USHORT Length;                                                          //0x0
    USHORT MaximumLength;                                                   //0x2
    ULONGLONG Buffer;                                                       //0x8
}STRING64, * LPSTRING64;

typedef struct _PEB_LDR_DATA64
{
    ULONG Length;                                                           //0x0
    UCHAR Initialized;                                                      //0x4
    PVOID SsHandle;                                                         //0x8
    LIST_ENTRY InLoadOrderModuleList;                               //0x10
    LIST_ENTRY InMemoryOrderModuleList;                             //0x20
    LIST_ENTRY InInitializationOrderModuleList;                     //0x30
    PVOID EntryInProgress;                                                  //0x40
    UCHAR ShutdownInProgress;                                               //0x48
    PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA64, *PPEB_LDR_DATA64;

typedef struct _PEB64
{
    UCHAR InheritedAddressSpace;                                            //0x0
    UCHAR ReadImageFileExecOptions;                                         //0x1
    UCHAR BeingDebugged;                                                    //0x2
    union
    {
        UCHAR BitField;                                                     //0x3
        struct
        {
            UCHAR ImageUsesLargePages : 1;                                    //0x3
            UCHAR IsProtectedProcess : 1;                                     //0x3
            UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
            UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
            UCHAR IsPackagedProcess : 1;                                      //0x3
            UCHAR IsAppContainer : 1;                                         //0x3
            UCHAR IsProtectedProcessLight : 1;                                //0x3
            UCHAR IsLongPathAwareProcess : 1;                                 //0x3
        };
    };
    UCHAR Padding0[4];                                                      //0x4
    ULONGLONG Mutant;                                                       //0x8
    ULONGLONG ImageBaseAddress;                                             //0x10
    PEB_LDR_DATA64* Ldr;                                                          //0x18
    ULONGLONG ProcessParameters;                                            //0x20
    ULONGLONG SubSystemData;                                                //0x28
    ULONGLONG ProcessHeap;                                                  //0x30
    ULONGLONG FastPebLock;                                                  //0x38
    ULONGLONG AtlThunkSListPtr;                                             //0x40
    ULONGLONG 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 _WIN64
    PPEB_LDR_DATA64 pPebLdrData = NULL;
    ULONGLONG ModuleSum = NULL;
    PPEB64 peb = (PPEB64)__readgsqword(0x60);
#else
    PPEB_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 _WIN64
        printf("[第 %d 模块] \n\t路径:%ws \n\t基址:0x%0I64X\n", 
            i + 1,
            pLdrDataEntry->FullDllName.Buffer, 
            reinterpret_cast<uint64_t>(pLdrDataEntry->DllBase));
#else
        printf("[第 %d 模块] \n\t路径:%ws \n\t基址:0x%0IX\n",
            i + 1,
            pLdrDataEntry->FullDllName.Buffer,
            reinterpret_cast<uint32_t>(pLdrDataEntry->DllBase));
#endif // _WIN64

        pListEntryStart = pListEntryStart->Flink;
    }
    // 卸载 DLL
    FreeLibrary(hdll);
    system("pause");
    return 0;
}

3.2 执行效果截图

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

4. 小结

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


更新于:2023.12.29

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

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

相关文章

【Linux】进程控制深度了解

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟练掌握Linux下的进程控制 > 毒鸡汤&#xff…

Vue 新一代开发者工具正式开源!

近日&#xff0c;Vue 新一代开发者工具&#xff08;DevTools&#xff09;正式开源&#xff01;Vue DevTools 是一个旨在增强 Vue 开发人员体验的工具&#xff0c;它提供了一些功能来帮助开发者更好地了解 Vue 应用。下面就来看看新一代 Vue DevTools 的功能和使用方法 功能 首…

2024.1.2 Spark 简介,架构,环境部署,词频统计

目录 一. Spark简介 二 . Spark 框架模块 三. 环境准备 3.1 Spark Local模式搭建 3.2 通过Anaconda安装python3环境 3.3 PySpark库安装 四 . Spark集群模式架构介绍 五. pycharm远程开发环境 六. Spark词频统计 一. Spark简介 1. Spark 和MapReduce MR:大量的磁盘反复…

vmware安装龙蜥操作系统

vmware安装龙蜥操作系统 1、下载龙蜥操作系统 8.8 镜像文件2、安装龙蜥操作系统 8.83、配置龙蜥操作系统 8.83.1、配置静态IP地址 和 dns3.2、查看磁盘分区3.3、查看系统版本 1、下载龙蜥操作系统 8.8 镜像文件 这里选择 2023年2月发布的 8.8 版本 官方下载链接 https://mirro…

使用拉普拉斯算子的图像锐化的python代码实现——数字图像处理

原理 拉普拉斯算子是一个二阶导数算子&#xff0c;用于图像处理中的边缘检测。它通过计算图像亮度的二阶空间导数来工作&#xff0c;能够突出显示图像中的快速变化区域&#xff0c;如边缘。 图像锐化的原理&#xff1a; 图像锐化是指增强图像中的边缘和细节&#xff0c;使图像…

VMware 虚拟机 ubuntu 20.04 硬盘扩容方法

前言 最近由于需要编译 【RK3568】的 Linux SDK&#xff0c;发现 虚拟机默认的 200G 空间不足了&#xff0c;因此想增加这个 200G 空间的限制&#xff0c;通过网络上查找了一些方法&#xff0c;加上自己亲自验证&#xff0c;确认 硬盘扩容 正常&#xff0c;方法也比较的容易&a…

微服务-OpenFeign-工程案例

Ribbon 前置知识 是NetFlix的开源项目&#xff0c;主要来提供关于客户端的负载均衡能力。从多个服务提供方&#xff0c;选取一个节点发起调用。 Feign:NetFlix,SpringCloud 的第一代LB&#xff08;负载均衡&#xff09;客户端工具包。 OpenFeign:SpringCloud自研&#xff0c…

计算机基础面试题 |03.精选计算机基础面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

c语言内嵌汇编知识点记录

内容在飞书云文档&#xff0c;点击打开即可。 Docshttps://r0dhfl3ujy9.feishu.cn/docx/EaVIdjGVeoS6fUxiKWkcjAq8nWg?fromfrom_copylink

使用定时器setInterval,在Moment.js 时间格式化插件基础上完成当前时间持续动态变化

1、引入插件 npm install moment --save 2、js配置&#xff1a;当前需要使用的文件中直接引入 import moment from moment; 3、代码实现&#xff1a;定义一个变量进行回显 3.1、dom部分 <span> {{ timeData }} </span> 3.2、js代码 <script> import mo…

C++初阶------------------入门C++

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

单片机开发--keil5

一.keil5 Keil uVision5是一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;用于对嵌入式系统中的微控制器进行编程。它是一个软件套件&#xff0c;包括源代码编辑器、项目经理、调试器以及微控制器开发、调试和编程所需的其他工具。Keil uVision5 IDE主要用于对基于A…

python调用openai api报错self._sslobj.do_handshake()OSError: [Errno 0] Error

python调用openai api报错self._sslobj.do_handshake()OSError: [Errno 0] Error 废话不说&#xff0c;先上代码&#xff0c;根据官网的介绍写的,chatgpt3.5 api简单调用 import os from openai import OpenAI from dotenv import load_dotenv# 加载 .env 文件中的变量 load_…

基于矩阵乘的CUDA编程优化过程

背景&#xff1a;网上很多关于矩阵乘的编程优化思路&#xff0c;本着看理论分析万遍&#xff0c;不如实际代码写一遍的想法&#xff0c;大概过一下优化思路。 矩阵乘的定义如下&#xff0c;约定矩阵的形状及存储方式为: A[M, K], B[K, N], C[M, N]。 CPU篇 朴素实现方法 按照…

阿里云域名外部入库流程

注册商是阿里云&#xff0c;且在阿里云管理的&#xff0c;请使用此教程外部入库。 如您的域名注册商是阿里云但在聚名管理&#xff0c;请参考教程&#xff1a;https://www.west.cn/faq/list.asp?unid2539 在外部入库操作之前&#xff0c;请先登录阿里云获取账号ID。详细的账…

SPSS25软件安装包下载及安装教程

SPSS 25下载链接&#xff1a;https://docs.qq.com/doc/DUlFFZ0dpWVhUZFpW 1.选中下载好的安装包&#xff0c;鼠标右键解压到“SPSS 25 64bit”文件夹 2.选中”SPPS 25 64bit.exe“鼠标右击选择以管理员身份运行 3.点击“下一步” 4.选择“我接受许可协议中的全部条款”&#x…

Hive06_基础查询

HIVE 查询语句 1 查询语句语法&#xff1a; SELECT [ALL | DISTINCT] select_expr, select_expr, ... FROM table_reference [WHERE where_condition] [GROUP BY col_list] [ORDER BY col_list] [CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY col_list] ] [LIMI…

YOLOv8改进:IoU系列篇 | Shape-IoU关注边界框本身的形状和尺度来计算损失 | 2023年12月最新IoU改进

🚀🚀🚀本文改进: 提出了一种新颖的Shape-IoU,小目标检测实现涨点,更加关注边界框本身的形状和尺度来计算损失 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1.Shape-IoU原理介绍 论文:https://ar…

window下载安装Mongodb数据库

我们先要访问他的官网 https://www.mongodb.com/zh-cn 然后顶部导航栏 选择 (Products/产品) 下的 (Community Edition/社区版) 进入界面后 找到 MongoDB Community Server Download 点击下面的按钮 Select package 然后会弹到这个位置 第一个版本 用系统默认选择的就好 第二…

第三百四十一回

文章目录 1. 概念介绍2. 使用方法与主要功能2.1 使用方法2.2 主要功能 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何获取App自身信息"相关的内容&#xff0c;本章回中将介绍一个三方包:open_setting.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念…