利用 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 _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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

polar CTF上传

1、题目 2、经过测试.htaccess绕过 三行代码解析&#xff1a; 将上传的.jpg文件解析成php文件 auto_append_file包含上传的文件 将上传的文件进行解码 AddType application/x-httpd-php .jpg php_value auto_append_fi\ le "php://filter/convert.base64-decode/resourc…

数据结构与算法-排序

&#x1f31e;入冬 时寒 添衣 勿病 要开心 排序 &#x1f388;1.排序的基本概念&#x1f388;2.排序的分类&#x1f52d;2.1插入排序&#x1f50e;2.1.1直接插入排序&#x1f50e;2.1.2折半插入排序&#x1f50e;2.1.3希尔排序 &#x1f52d;2.2交换排序&#x1f50e;2.2.1冒泡…

Python中的并发编程(7)异步编程

异步编程 Python3.4后新增了asyncio模块&#xff0c;支持异步编程。 异步是在一个线程中通过任务切换的方式让多个任务”同时“进展。asyncio不涉及线程/进程切换&#xff0c;减少了线程/进程创建、上下文切换的开销&#xff0c;更轻量级。 asyncio的核心是事件循环&#xff0…

【设计模式】外观模式

文章目录 前言一、外观模式1.案例2.优缺点3.使用场景4.源码解析 总结 前言 【设计模式】外观模式 一、外观模式 有些人可能炒过股票&#xff0c;但其实大部分人都不太懂&#xff0c;这种没有足够了解证券知识的情况下做股票是很容易亏钱的&#xff0c;刚开始炒股肯定都会想&am…

c语言:把二维数组降至一维|练习题

一、题目 把二维数组降为一围数组 如图&#xff1a; 二、代码截图【带注释】 三、源代码【带注释】 #include <stdio.h> int main() { int arr2[3][3];//设置二维数组 int arr1[10];//设置一维数组 int z0;//一维数组自增量 printf("输入一个二维数…

面试算法78:合并排序链表

题目 输入k个排序的链表&#xff0c;请将它们合并成一个排序的链表。 分析&#xff1a;利用最小堆选取值最小的节点 用k个指针分别指向这k个链表的头节点&#xff0c;每次从这k个节点中选取值最小的节点。然后将指向值最小的节点的指针向后移动一步&#xff0c;再比较k个指…

Net6 Core webApi发布到IIS

Net6 Core Api发布到IIS不同于webapi&#xff0c;依赖框架不同&#xff0c;配置也移至项目内Program.cs 一、发布到指定文件夹和IIS&#xff0c;不过注意IIS应用程序池选择的是 “无托管代码“ 在IIS管理器中点击浏览&#xff0c;访问接口路径报500.19&#xff0c;原因是所依赖…

【并发设计模式】聊聊线程本地存储模式如何实现的线程安全

前面两篇文章&#xff0c;通过两阶段终止的模式进行优雅关闭线程&#xff0c;利用数据不变性的方式保证数据安全&#xff0c;以及基于COW的模式&#xff0c;保证读数据的安全。本篇我们来简述下如果利用线程本地存储的方式保证线程安全。 首先一个大前提就是并发问题&#xff…

Python:将print内容写入文件

简介&#xff1a;print函数是Python中使用频率非常非常高的函数&#xff0c;其包含四个参数&#xff1a;sep、end、file、flush。 历史攻略&#xff1a; Python基础&#xff1a;输入、输出 Python&#xff1a;将控制台输出保存成文件 参数解析&#xff1a; print()函数可以…

07-项目打包 React Hooks

项目打包 项目打包是为了把整个项目都打包成最纯粹的js&#xff0c;让浏览器可以直接执行 打包命令已经在package.json里面定义好了 运行命令&#xff1a;npm run build&#xff0c;执行时间取决于第三方插件的数量以及电脑配置 打包完之后再build文件夹下&#xff0c;这个…

基于JavaSpringboot+Vue实现前后端分离房屋租赁系统

基于JavaSpringbootVue实现前后端分离房屋租赁系统 作者主页 500套成品系统 联系客服任你挑选 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录 基于JavaSpringbootVue实现前后端分离房屋租赁系统前言介绍&#xff1a;功能设计&#xf…

JavaWeb——前端之JSVue

接上篇笔记 4. JavaScript 概念 跨平台、面向对象的脚本语言&#xff0c;使网页可交互与Java语法类似&#xff0c;但是不需要变异&#xff0c;直接由浏览器解析1995年Brendan Eich发明&#xff0c;1997年成为ECMA标准&#xff08;ECMA制定了标准化的脚本程序设计语言ECMAScr…

杰发科技AC7840——EEPROM初探

0.序 7840和7801的模拟EEPROM使用不太一样 1.现象 按照官方Demo&#xff0c;在这样的配置下&#xff0c;我们看到存储是这样的&#xff08;连续三个数字1 2 3&#xff09;。 使用串口工具的多帧发送功能 看不出多少规律 修改代码后 发现如下规律&#xff1a; 前四个字节是…

HarmonyOS page生命周期函数讲解

下面 我们又要看一个比较重要的点了 页面生命周期 页面组件有三个生命周期 onPageShow 页面显示时触发 onPageHide 页面隐藏时触发 onBackPress 页面返回时触发 这里 我们准备两个组件 首先是 index.ets 参考代码如下 import router from ohos.router Entry Component struc…

06-C++ 类和对象-多态

类与对象 多态 1. 简介 一个事物的多种形态&#xff0c;简称多态。 物的多态 同一个人在不同人面前&#xff0c;角色不同 如&#xff1a; 在父母面前在对象面前在朋友面前在同事面前 事的多态 同一种事情&#xff0c;在不同情况下展现不同 如&#xff1a; 吃饭 中国人 筷子 …

NXP实战笔记(一):基于RTD-SDK新建一个S32DS工程

目录 1、概述 2、操作步骤 2.1、新建Application工程 2.2、命名工程、选择芯片型号、选择编译器GCC版本 2.3、配置基本参数 3、文件描述 3.1、文件结构描述 3.2、编译之后 4、下载调试 1、概述 安装了S32DS之后&#xff0c;导入SDK插件&#xff0c;这个步骤不赘述&…

世界经济论坛制定了五项指导原则,实现跨OT环境的网络安全。

内容概述&#xff1a; 世界经济论坛在其题为“解锁工业环境中的网络弹性&#xff1a;五项原则”的报告中列出&#xff1a;原则一&#xff1a;执行全面风险管理OT 环境、原则二&#xff1a;确保OT工程师和安装操作员对OT网络安全负责、原则三&#xff1a;与高层组织领导、战略规…

传感器原理与应用复习--光电式与半导体式传感器

文章目录 上一篇光电传感器光电器件 光纤传感器光纤传感器的工作原理及组成 半导体传感器下一篇 上一篇 光电传感器 光电器件 每个光子的能量为 E h v E hv Ehv h为普朗克常数 6.626 ∗ 1 0 − 34 ( J / s ) 6.626 * 10^{-34}(J/s) 6.626∗10−34(J/s) ν \nu ν 为光…

yolov5 主要流程

1.介绍 本文包含了有关yolov5目标检测的基本流程&#xff0c;包括模型训练与模型部署&#xff0c;旨在帮助小伙伴们建立系统的认知&#x1f496;&#x1f496; YOLO是 "You only look once "的首字母缩写&#xff0c;是一个开源软件工具&#xff0c;它具有实时检测…

【Java开发岗面试】八股文—Java框架(Spring+SpringMVC+MyBatis+SpringBoot)

声明&#xff1a; 背景&#xff1a;本人为24届双非硕校招生&#xff0c;已经完整经历了一次秋招&#xff0c;拿到了三个offer。本专题旨在分享自己的一些Java开发岗面试经验&#xff08;主要是校招&#xff09;&#xff0c;包括我自己总结的八股文、算法、项目介绍、HR面和面试…