《Windows PE》4.1导入表

news2024/10/6 19:03:26

导入表顾名思义,就是记录外部导入函数信息的表。这些信息包括外部导入函数的序号、名称、地址和所属的DLL动态链接库的名称。Windows程序中使用的所有API接口函数都是从系统DLL中调用的。当然也可能是自定义的DLL动态链接库。对于调用方,我们称之为导入函数,而对于DLL动态链接库而言,则称之为导出函数。我们将在下一章中再详细讲述导出表。

本节必须掌握的知识点:

        导入表数据结构

        PE中的导入表

        IAT函数地址表

        手工重构导入表

4.1.1 导入表数据结构

导入表(Import Table)是PE(可执行文件、动态链接库等)文件中的一个数据结构,用于记录该模块所依赖的外部函数和库。在运行时,操作系统根据导入表中的信息来解析和加载这些外部函数和库。导入表数据是一组导入表描述符结构。每组为20个字节。导入表的位置和大小信息保存在数据目录项的第1项(从第0项开始)。

导入表包含以下重要的信息

●导入描述符表(Import Descriptor Table):记录每个被导入的模块的信息,如模块名称、导入地址表的位置等。

●导入地址表(Import Address Table):记录外部函数的地址,在加载时会被填充为实际函数的地址。

●导入名称表(Import Name Table):记录外部函数的名称。

导入表描述符的结构如下

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

    union {

        DWORD Characteristics; //特征值,对于终止的空导入描述符为0

        DWORD OriginalFirstThunk; //指向原始未绑定的导入地址表的RVA

//(PIMAGE_THUNK_DATA)

    } DUMMYUNIONNAME;

DWORD TimeDateStamp; //时间戳,如果未绑定则为0。

//绑定时为-1,并在IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT中记录实际日期\时间戳(新的BIND中),否则为绑定到的DLL的日期/时间戳(旧的BIND中)

    DWORD ForwarderChain; //正向链表索引,如果没有正向链表则为-1

    DWORD Name;         //DLL模块名称的RVA

    DWORD FirstThunk; //导入地址表(IAT)的RVA,如果已绑定,则IAT包含实际地址

} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;

●OriginalFirstThunk字段的作用是记录导入函数的信息。对于每个导入模块,OriginalFirstThunk字段是一个指针,指向一个由IMAGE_THUNK_DATA类型的结构体构成的表。每个IMAGE_THUNK_DATA结构体存储着导入函数的信息,包括函数名称或序号。

OriginalFirstThunk指向的表中的每个IMAGE_THUNK_DATA结构体有两种可能的形式:

  1. 如果导入函数是通过名称进行导入,IMAGE_THUNK_DATA结构体中的值是一个指向函数名称的指针。
  2. 如果导入函数是通过序号进行导入,IMAGE_THUNK_DATA结构体中的值是函数的序号。

通过解析OriginalFirstThunk指向的表,可以获取导入模块中所有导入函数的名称或序号。这些信息后续可以用于将导入的函数地址与对应的函数名称或序号进行关联。

 注意

1.OriginalFirstThunk字段在已绑定的PE文件中可能会被覆盖,此时可以通过FirstThunk字段来获取已绑定的导入地址表。

2.ForwarderChain用于指示导入函数的转发链(Forwarder Chain)。

在PE文件中,导入表描述符用于描述导入函数的相关信息,包括导入模块的名称、导入地址表的位置等。而转发链是指导入函数在一个模块中被转发到另一个模块的一种机制。

转发链的作用是将一个导入函数的调用转发到另一个模块中的函数。当一个导入函数被调用时,如果它在当前模块中没有找到,就会根据转发链找到另一个模块,并在那个模块中寻找匹配的函数来处理该调用。

ForwarderChain字段在导入表描述符中记录了转发链的信息,它的值表示下一个导入函数描述符在导入表中的索引。如果ForwarderChain字段的值为-1,表示没有转发链,即没有导入函数被转发到其他模块。

需要注意的是,转发链是可选的,不是所有的导入函数都会使用转发链。只有在某些特定的情况下,如跨模块调用或模块间的函数重定向时,才会使用转发链来实现导入函数的转发。

●下面是winnt.h头文件中IMAGE_THUNK_DATA结构体的定义:

#include "pshpack8.h"                // Use align 8 for the 64-bit IAT.

typedef struct _IMAGE_THUNK_DATA64 {

    union {

        ULONGLONG ForwarderString// PBYTE

        ULONGLONG Function;         // PDWORD

        ULONGLONG Ordinal;

        ULONGLONG AddressOfData;    // PIMAGE_IMPORT_BY_NAME

    } u1;

} IMAGE_THUNK_DATA64;

typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

#include "poppack.h"               // Back to 4 byte packing

typedef struct _IMAGE_THUNK_DATA32 {

    union {

        DWORD ForwarderString;      // PBYTE

        DWORD Function;             // PDWORD

        DWORD Ordinal;

        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME

    } u1;

} IMAGE_THUNK_DATA32;

typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

IMAGE_THUNK_DATA结构体的字段含义如下:

1.ForwarderString:如果存在转发导入(forwarding import),则该字段保存转发字符串的指针;否则,该字段为0。

2.Function:导入函数的地址。如果导入函数是通过名称进行导入,则该字段保存函数的地址;如果导入函数是通过序号进行导入,则该字段保存函数的序号。

3.Ordinal:如果导入函数是通过序号进行导入,则该字段保存函数的序号;否则,该字段为0。

4.AddressOfData:如果导入函数是通过名称进行导入,则该字段保存一个指向IMAGE_IMPORT_BY_NAME结构体的指针,该结构体包含函数的名称;否则,该字段保存一个指向函数的地址的指针。

IMAGE_THUNK_DATA结构体用于描述PE文件中的导入地址表(Import Address Table,IAT)或原始未绑定导入地址表(Original Unbound Import Address Table)中的每个导入函数的信息。

通过解析IMAGE_THUNK_DATA结构体的字段,可以获取导入函数的地址或序号,以及函数的名称(如果是通过名称进行导入)。

 注意

1.MAGE_THUNK_DATA结构体在64位的PE文件中使用_IMAGE_THUNK_DATA64。

2.对于32位的PE文件,相应的结构体是_IMAGE_THUNK_DATA32。它们之间的区别在于64位PE文件使用ULONGLONG表示64位指针类型,而32位PE文件使用DWORD表示32位指针类型。

       ●IMAGE_IMPORT_BY_NAME结构体:                        

IMAGE_IMPORT_BY_NAME是Windows可执行文件(PE文件)中的一个结构体,用于描述通过名称导入函数的信息。

以下是IMAGE_IMPORT_BY_NAME结构体的定义:

typedef struct _IMAGE_IMPORT_BY_NAME {

    WORD    Hint;

    BYTE    Name[1];

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

IMAGE_IMPORT_BY_NAME结构体的字段含义如下:

1.Hint:导入函数的提示值(Hint),是一个16位的无符号整数。它提供了关于导入函数的一些附加信息,例如函数的重载情况等。在导入描述符中,通过OriginalFirstThunk指向的表中的IMAGE_THUNK_DATA结构体的Ordinal字段存储了函数的序号,而Hint字段则提供了与该序号对应的提示值。

2.Name:导入函数的名称,是一个以NULL结尾的字符串。

IMAGE_IMPORT_BY_NAME结构体用于描述PE文件中的导入地址表(Import Address Table,IAT)或原始未绑定导入地址表(Original Unbound Import Address Table)中,通过名称进行导入的函数的信息。

通过解析IMAGE_IMPORT_BY_NAME结构体的字段,可以获取导入函数的名称以及与之对应的提示值(如果有的话)。

4.1.2 PE中的导入表

       接下来我们通过具体实例分析PE中的导入表。

实验二十三:如何定位导入表?

●我们以32位汇编版HelloWorld.exe为例,将其拖入WinHex,如下所示:

00000000   4D 5A 90 00 03 00 00 00  04 00 00 00 FF FF 00 00   MZ............

00000010   B8 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00   ?......@.......

00000020   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

00000030   00 00 00 00 00 00 00 00  00 00 00 00 C8 00 00 00   ............?..

000000C0   00 00 00 00 00 00 00 00  50 45 00 00 4C 01 04 00   ........PE..L...

000000D0   E4 68 03 66 00 00 00 00  00 00 00 00 E0 00 02 01   鋒.f........?..

000000E0   0B 01 0E 10 00 02 00 00  00 06 00 00 00 00 00 00   ................

000000F0   00 10 00 00 00 10 00 00  00 20 00 00 00 00 40 00   ......... ....@.

00000100   00 10 00 00 00 02 00 00  06 00 00 00 00 00 00 00   ................

00000110   06 00 00 00 00 00 00 00  00 50 00 00 00 04 00 00   .........P......

00000120   00 00 00 00 02 00 40 81  00 00 10 00 00 10 00 00   ......@.........

00000130   00 00 10 00 00 10 00 00  00 00 00 00 10 00 00 00   ................

00000140   00 00 00 00 00 00 00 00  DC 20 00 00 3C 00 00 00   ........?..<...

00000150   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

00000160   00 00 00 00 00 00 00 00  00 40 00 00 10 00 00 00   .........@......

00000170   10 20 00 00 1C 00 00 00  00 00 00 00 00 00 00 00   . ..............

00000180   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

00000190   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

000001A0   00 20 00 00 10 00 00 00  00 00 00 00 00 00 00 00   . ..............

000001B0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

       ●导入表数据目录项

DOS头的IMAGE_DOS_HEADER. e_lfanew字段000000C8H指向IMAGE_NT_HEADERS32。

IMAGE_NT_HEADERS32.OptionalHeader.DataDirectory[1*8]指向导入表数据目录项00000148H地址处。

导入表的RVA地址为000020DCH,大小为0000003C。

       ●定位导入表

000001C0   2E 74 65 78 74 00 00 00  26 00 00 00 00 10 00 00   .text...&.......

000001D0   00 02 00 00 00 04 00 00  00 00 00 00 00 00 00 00   ................

000001E0   00 00 00 00 20 00 00 60  2E 72 64 61 74 61 00 00   .... ..`.rdata..

000001F0   5E 01 00 00 00 20 00 00  00 02 00 00 00 06 00 00   ^.... ..........

00000200   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40   ............@..@

00000210   2E 64 61 74 61 00 00 00  1B 00 00 00 00 30 00 00   .data........0..

00000220   00 02 00 00 00 08 00 00  00 00 00 00 00 00 00 00   ................

00000230   00 00 00 00 40 00 00 C0  2E 72 65 6C 6F 63 00 00   ....@..?reloc..

00000240   10 00 00 00 00 40 00 00  00 02 00 00 00 0A 00 00   .....@..........

00000250   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 42   ............@..B

       将导入表数据目录项的RVA地址转换为FOA文件偏移地址。

       RVA地址000020DCH位于.rdata节区00002000H~0000215EH区间。

       FOA地址=000020DCH-00002000H+00000600H=000006DCH。

目录项目的第1个结构就是导入表                                             

                                                

typedef struct _IMAGE_DATA_DIRECTORY {                                              

    DWORD   VirtualAddress;                                    //RVA 指向导入表结构             

    DWORD   Size;                                             

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;                                             

                                                

      将RVA转换成FOA                                        

                                                

typedef struct _IMAGE_IMPORT_DESCRIPTOR {                                                

    union {                                             

        DWORD   Characteristics;                                                        

        DWORD   OriginalFirstThunk;                                                   

    };                                              

    DWORD   TimeDateStamp;                                                         

    DWORD   ForwarderChain;                                                          

    DWORD   Name;                                         

    DWORD   FirstThunk;                                                                

} IMAGE_IMPORT_DESCRIPTOR;                                          

typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED

*PIMAGE_IMPORT_DESCRIPTOR;                                               

000006D0  1B 00 00 00 2E 64 61 74  61 00 00 00 20 21 00 00   .....data... !..

000006E0   00 00 00 00 00 00 00 00  36 21 00 00 08 20 00 00   ........6!... ..

000006F0   18 21 00 00 00 00 00 00  00 00 00 00 50 21 00 00   .!..........P!..

00000700   00 20 00 00 00 00 00 00  00 00 00 00 00 00 00 00   . ..............

00000710   00 00 00 00 00 00 00 00  42 21 00 00 00 00 00 00   ........B!......

00000720   28 21 00 00 00 00 00 00  B1 01 4D 65 73 73 61 67   (!......?Messag

00000730   65 42 6F 78 41 00 75 73  65 72 33 32 2E 64 6C 6C   eBoxA.user32.dll

00000740   00 00 9B 00 45 78 69 74  50 72 6F 63 65 73 73 00   ..?ExitProcess.

00000750   6B 65 72 6E 65 6C 33 32  2E 64 6C 6C 00 00 00 00   kernel32.dll....

 

总结

HelloWorld.exe程序中共有3个导入表描述符结构,每个导入表描述符为20个字节,分别用于描述user32.dll和kernel32.dll两个动态链接库中的导入函数。此外还有一个为空的导入表描述符作为结束标志。

Windows程序中的导入表描述符是一个双桥结构:

1.描述符的第一个字段OriginalFirstThunk又被称为桥1,是一个32位的RVA地址,示例中的值为00002120H,指向INT表,最高位为0表示按序号导入,最高位为1表示按名称导入。

导入表中的INT表(IMAGE_THUNK_DATA32. AddressOfData)指的是导入函数中的函数指针表中的一个特定部分,用于存储导入函数名的RVA地址。通过解析导入描述符中的INT表,可以获取导入函数的入口地址,从而在程序运行时能够正确地调用这些导入函数。

将OriginalFirstThunk存储的RVA地址转换为FOA地址:

INT表的FOA地址=00002120H-00002000H+600H=720H。

在00000720H地址处存储的值为IMAGE_THUNK_DATA32. AddressOfData(00002128H),同样是一个RVA地址,指向导入函数名。

导入函数名的FOV地址=00002128H-00002000H+600H=728H。

在00000728H地址处存储的值就是导入函数名”MessagBoxA”。注意函数名之前的01B1H为与导入函数序号对应的提示值。

2.导入表描述符的TimeDateStamp和ForwarderChain字段为空。

3.导入表描述符的Name字段(00002136H)为DLL模块名称的RVA地址。将此RVA地址转换为FOA地址为736H,该地址处存储的值就是” user32.dll”。

                                                              

typedef struct _IMAGE_IMPORT_DESCRIPTOR {      

       union {   

         DWORD   Characteristics;

         DWORD   OriginalFirstThunk;// 00002120H,转换后的FOA地址为720H。

       };                                                     

         DWORD   TimeDateStamp; //为空

         DWORD   ForwarderChain; //为空

         DWORD   Name;    // 00002136H,转换后的FOA地址为736H” user32.dll”

       DWORD   FirstThunk; 

} IMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_THUNK_DATA32 {

    union {

        DWORD ForwarderString;  // PBYTE

        DWORD Function;        // PDWORD

        DWORD Ordinal;

        DWORD AddressOfData;    //00002128H ,转换后的FOA地址为728H。

    } u1;

} IMAGE_THUNK_DATA32;

typedef struct _IMAGE_IMPORT_BY_NAME {

    WORD    Hint;           //01B1H

    BYTE    Name[1];       // “MessagBoxA”

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

4.最后一个字段FirstThunk指向IAT表。我们将在下一小节中讲解。

DLL名称

遍历OriginalFirstThunk

 注意

       1.导入函数名字符串和DLL模块名字符串均为ASCII码字符串。

       2.导入函数名字符串和DLL模块名字符串均为’\0’结尾字符串,并且以偶数字节对齐。例如函数名”MessagBoxA”字符串结尾处为1个’\0’,而” user32.dll”字符串结尾处有两个’\0’。

●我们再以64位汇编版HelloWorld64.exe为例,将其拖入WinHex,如下所示:

00000180   00 00 00 00 00 00 00 00  64 27 00 00 B4 00 00 00   ........d'..?..

00000190   00 50 00 00 E0 01 00 00  00 40 00 00 68 01 00 00   .P..?...@..h...

000001A0   00 00 00 00 00 00 00 00  00 60 00 00 1C 00 00 00   .........`......

000001B0   30 22 00 00 70 00 00 00  00 00 00 00 00 00 00 00   0"..p...........

000001C0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

000001D0   A0 22 00 00 00 01 00 00  00 00 00 00 00 00 00 00   ?..............

000001E0   00 20 00 00 80 01 00 00  00 00 00 00 00 00 00 00   . ..€...........

000001F0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

00000200   2E 74 65 78 74 00 00 00  38 0D 00 00 00 10 00 00   .text...8.......

00000210   00 0E 00 00 00 04 00 00  00 00 00 00 00 00 00 00   ................

00000220   00 00 00 00 20 00 00 60  2E 72 64 61 74 61 00 00   .... ..`.rdata..

00000230   A0 0D 00 00 00 20 00 00  00 0E 00 00 00 12 00 00   ?... ..........

00000240   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40   ............@..@

00000250   2E 64 61 74 61 00 00 00  48 06 00 00 00 30 00 00   .data...H....0..

00000260   00 02 00 00 00 20 00 00  00 00 00 00 00 00 00 00   ..... ..........

00000270   00 00 00 00 40 00 00 C0  2E 70 64 61 74 61 00 00   ....@..?pdata..

00000280   68 01 00 00 00 40 00 00  00 02 00 00 00 22 00 00   h....@......."..

00000290   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40   ............@..@

000002A0   2E 72 73 72 63 00 00 00  E0 01 00 00 00 50 00 00   .rsrc...?...P..

000002B0   00 02 00 00 00 24 00 00  00 00 00 00 00 00 00 00   .....$..........

000002C0   00 00 00 00 40 00 00 40  2E 72 65 6C 6F 63 00 00   ....@..@.reloc..

000002D0   1C 00 00 00 00 60 00 00  00 02 00 00 00 26 00 00   .....`.......&..

000002E0   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 42   ............@..B

       首先我们找到数据目录项的第1项,位于地址00000188H处,导入表的RVA地址为00002764H,大小是000000B4H。

我们遍历节表可知,RVA地址00002764H位于.rdata节区。

导入表FOA地址=00002764H-00002000H+00001200H=00001964H。

如下所示:

00001960   01 00 00 00 A0 28 00 00  00 00 00 00 00 00 00 00   ....?..........

00001970   A6 29 00 00 88 20 00 00  B0 28 00 00 00 00 00 00   ?..?..?......

00001980   00 00 00 00 D4 29 00 00  98 20 00 00 F8 28 00 00   ....?..?..?..

00001990   00 00 00 00 00 00 00 00  7E 2B 00 00 E0 20 00 00   ........~+..?..

000019A0   E8 28 00 00 00 00 00 00  00 00 00 00 A0 2B 00 00   ?..........?..

000019B0   D0 20 00 00 80 29 00 00  00 00 00 00 00 00 00 00   ?..€)..........

000019C0   C0 2B 00 00 68 21 00 00  D8 28 00 00 00 00 00 00   ?..h!..?......

000019D0   00 00 00 00 E0 2B 00 00  C0 20 00 00 C8 28 00 00   ....?..?..?..

000019E0   00 00 00 00 00 00 00 00  02 2C 00 00 B0 20 00 00   .........,..?..

000019F0   18 28 00 00 00 00 00 00  00 00 00 00 92 2D 00 00   .(..........?..

00001A00   00 20 00 00 00 00 00 00  00 00 00 00 00 00 00 00   . ..............

00001A10   00 00 00 00 00 00 00 00  E2 2C 00 00 00 00 00 00   ........?......

00001A20   7E 2D 00 00 00 00 00 00  6C 2D 00 00 00 00 00 00   ~-......l-......

00001A30   58 2D 00 00 00 00 00 00  42 2D 00 00 00 00 00 00   X-......B-......

00001A40   28 2D 00 00 00 00 00 00  12 2D 00 00 00 00 00 00   (-.......-......

00001A50   FC 2C 00 00 00 00 00 00  22 2C 00 00 00 00 00 00   ?......",......

00001A60   C6 2C 00 00 00 00 00 00  B2 2C 00 00 00 00 00 00   ?......?......

00001A70   9E 2C 00 00 00 00 00 00  80 2C 00 00 00 00 00 00   ?......€,......

00001A80   64 2C 00 00 00 00 00 00  50 2C 00 00 00 00 00 00   d,......P,......

00001A90   36 2C 00 00 00 00 00 00  00 00 00 00 00 00 00 00   6,..............

00001AA0   98 29 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ?..............

00001AB0   CA 29 00 00 00 00 00 00  B2 29 00 00 00 00 00 00   ?......?......

00001AC0   00 00 00 00 00 00 00 00  0C 2B 00 00 00 00 00 00   .........+......

00001AD0   00 00 00 00 00 00 00 00  F6 2A 00 00 00 00 00 00   ........?......

00001AE0   00 00 00 00 00 00 00 00  08 2A 00 00 00 00 00 00   .........*......

00001AF0   00 00 00 00 00 00 00 00  B4 2A 00 00 00 00 00 00   ........?......

00001B00   72 2B 00 00 00 00 00 00  48 2B 00 00 00 00 00 00   r+......H+......

00001B10   2C 2B 00 00 00 00 00 00  9E 2A 00 00 00 00 00 00   ,+......?......

00001B20   96 2A 00 00 00 00 00 00  88 2A 00 00 00 00 00 00   ?......?......

00001B30   7C 2A 00 00 00 00 00 00  58 2A 00 00 00 00 00 00   |*......X*......

00001B40   36 2A 00 00 00 00 00 00  1C 2A 00 00 00 00 00 00   6*.......*......

00001B50   64 2B 00 00 00 00 00 00  F8 29 00 00 00 00 00 00   d+......?......

00001B60   E6 29 00 00 00 00 00 00  C8 2A 00 00 00 00 00 00   ?......?......

00001B70   BE 2A 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ?..............

00001B80   1C 2B 00 00 00 00 00 00  A6 2A 00 00 00 00 00 00   .+......?......

00001B90   00 00 00 00 00 00 00 00  8A 02 4D 65 73 73 61 67   ........?Messag

00001BA0   65 42 6F 78 57 00 55 53  45 52 33 32 2E 64 6C 6C   eBoxW.USER32.dll

00001BB0   00 00 08 00 5F 5F 43 5F  73 70 65 63 69 66 69 63   ....__C_specific

00001BC0   5F 68 61 6E 64 6C 65 72  00 00 3E 00 6D 65 6D 73   _handler..>.mems

00001BD0   65 74 00 00 56 43 52 55  4E 54 49 4D 45 31 34 30   et..VCRUNTIME140

00001BE0   2E 64 6C 6C 00 00 40 00  5F 73 65 68 5F 66 69 6C   .dll..@._seh_fil

00001BF0   74 65 72 5F 65 78 65 00  42 00 5F 73 65 74 5F 61   ter_exe.B._set_a

00001C00   70 70 5F 74 79 70 65 00  09 00 5F 5F 73 65 74 75   pp_type...__setu

00001C10   73 65 72 6D 61 74 68 65  72 72 00 00 18 00 5F 63   sermatherr...._c

00001C20   6F 6E 66 69 67 75 72 65  5F 6E 61 72 72 6F 77 5F   onfigure_narrow_

00001C30   61 72 67 76 00 00 33 00  5F 69 6E 69 74 69 61 6C   argv..3._initial

00001C40   69 7A 65 5F 6E 61 72 72  6F 77 5F 65 6E 76 69 72   ize_narrow_envir

00001C50   6F 6E 6D 65 6E 74 00 00  2B 00 5F 67 65 74 5F 6E   onment..+._get_n

00001C60   61 72 72 6F 77 5F 77 69  6E 6D 61 69 6E 5F 63 6F   arrow_winmain_co

00001C70   6D 6D 61 6E 64 5F 6C 69  6E 65 00 00 36 00 5F 69   mmand_line..6._i

00001C80   6E 69 74 74 65 72 6D 00  37 00 5F 69 6E 69 74 74   nitterm.7._initt

00001C90   65 72 6D 5F 65 00 55 00  65 78 69 74 00 00 23 00   erm_e.U.exit..#.

00001CA0   5F 65 78 69 74 00 54 00  5F 73 65 74 5F 66 6D 6F   _exit.T._set_fmo

00001CB0   64 65 00 00 16 00 5F 63  65 78 69 74 00 00 15 00   de...._cexit....

00001CC0   5F 63 5F 65 78 69 74 00  3D 00 5F 72 65 67 69 73   _c_exit.=._regis

00001CD0   74 65 72 5F 74 68 72 65  61 64 5F 6C 6F 63 61 6C   ter_thread_local

00001CE0   5F 65 78 65 5F 61 74 65  78 69 74 5F 63 61 6C 6C   _exe_atexit_call

00001CF0   62 61 63 6B 00 00 08 00  5F 63 6F 6E 66 69 67 74   back...._configt

00001D00   68 72 65 61 64 6C 6F 63  61 6C 65 00 16 00 5F 73   hreadlocale..._s

00001D10   65 74 5F 6E 65 77 5F 6D  6F 64 65 00 01 00 5F 5F   et_new_mode...__

00001D20   70 5F 5F 63 6F 6D 6D 6F  64 65 00 00 34 00 5F 69   p__commode..4._i

00001D30   6E 69 74 69 61 6C 69 7A  65 5F 6F 6E 65 78 69 74   nitialize_onexit

00001D40   5F 74 61 62 6C 65 00 00  3C 00 5F 72 65 67 69 73   _table..<._regis

00001D50   74 65 72 5F 6F 6E 65 78  69 74 5F 66 75 6E 63 74   ter_onexit_funct

00001D60   69 6F 6E 00 1E 00 5F 63  72 74 5F 61 74 65 78 69   ion..._crt_atexi

00001D70   74 00 67 00 74 65 72 6D  69 6E 61 74 65 00 61 70   t.g.terminate.ap

00001D80   69 2D 6D 73 2D 77 69 6E  2D 63 72 74 2D 72 75 6E   i-ms-win-crt-run

00001D90   74 69 6D 65 2D 6C 31 2D  31 2D 30 2E 64 6C 6C 00   time-l1-1-0.dll.

00001DA0   61 70 69 2D 6D 73 2D 77  69 6E 2D 63 72 74 2D 6D   api-ms-win-crt-m

00001DB0   61 74 68 2D 6C 31 2D 31  2D 30 2E 64 6C 6C 00 00   ath-l1-1-0.dll..

00001DC0   61 70 69 2D 6D 73 2D 77  69 6E 2D 63 72 74 2D 73   api-ms-win-crt-s

00001DD0   74 64 69 6F 2D 6C 31 2D  31 2D 30 2E 64 6C 6C 00   tdio-l1-1-0.dll.

00001DE0   61 70 69 2D 6D 73 2D 77  69 6E 2D 63 72 74 2D 6C   api-ms-win-crt-l

00001DF0   6F 63 61 6C 65 2D 6C 31  2D 31 2D 30 2E 64 6C 6C   ocale-l1-1-0.dll

00001E00   00 00 61 70 69 2D 6D 73  2D 77 69 6E 2D 63 72 74   ..api-ms-win-crt

00001E10   2D 68 65 61 70 2D 6C 31  2D 31 2D 30 2E 64 6C 6C   -heap-l1-1-0.dll

00001E20   00 00 D3 04 52 74 6C 43  61 70 74 75 72 65 43 6F   ..?RtlCaptureCo

00001E30   6E 74 65 78 74 00 DA 04  52 74 6C 4C 6F 6F 6B 75   ntext.?RtlLooku

00001E40   70 46 75 6E 63 74 69 6F  6E 45 6E 74 72 79 00 00   pFunctionEntry..

00001E50   E1 04 52 74 6C 56 69 72  74 75 61 6C 55 6E 77 69   ?RtlVirtualUnwi

00001E60   6E 64 00 00 BC 05 55 6E  68 61 6E 64 6C 65 64 45   nd..?UnhandledE

00001E70   78 63 65 70 74 69 6F 6E  46 69 6C 74 65 72 00 00   xceptionFilter..

00001E80   7B 05 53 65 74 55 6E 68  61 6E 64 6C 65 64 45 78   {.SetUnhandledEx

00001E90   63 65 70 74 69 6F 6E 46  69 6C 74 65 72 00 1D 02   ceptionFilter...

00001EA0   47 65 74 43 75 72 72 65  6E 74 50 72 6F 63 65 73   GetCurrentProces

00001EB0   73 00 9A 05 54 65 72 6D  69 6E 61 74 65 50 72 6F   s.?TerminatePro

00001EC0   63 65 73 73 00 00 89 03  49 73 50 72 6F 63 65 73   cess..?IsProces

00001ED0   73 6F 72 46 65 61 74 75  72 65 50 72 65 73 65 6E   sorFeaturePresen

00001EE0   74 00 50 04 51 75 65 72  79 50 65 72 66 6F 72 6D   t.P.QueryPerform

00001EF0   61 6E 63 65 43 6F 75 6E  74 65 72 00 1E 02 47 65   anceCounter...Ge

00001F00   74 43 75 72 72 65 6E 74  50 72 6F 63 65 73 73 49   tCurrentProcessI

00001F10   64 00 22 02 47 65 74 43  75 72 72 65 6E 74 54 68   d.".GetCurrentTh

00001F20   72 65 61 64 49 64 00 00  F0 02 47 65 74 53 79 73   readId..?GetSys

00001F30   74 65 6D 54 69 6D 65 41  73 46 69 6C 65 54 69 6D   temTimeAsFileTim

00001F40   65 00 6C 03 49 6E 69 74  69 61 6C 69 7A 65 53 4C   e.l.InitializeSL

00001F50   69 73 74 48 65 61 64 00  82 03 49 73 44 65 62 75   istHead.?IsDebu

00001F60   67 67 65 72 50 72 65 73  65 6E 74 00 D7 02 47 65   ggerPresent.?Ge

00001F70   74 53 74 61 72 74 75 70  49 6E 66 6F 57 00 7E 02   tStartupInfoW.~.

00001F80   47 65 74 4D 6F 64 75 6C  65 48 61 6E 64 6C 65 57   GetModuleHandleW

00001F90   00 00 4B 45 52 4E 45 4C  33 32 2E 64 6C 6C 00 00   ..KERNEL32.dll..

       notepad64.exe导入表描述符:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {                                                

    union {                                             

        DWORD   Characteristics;                                                        

        DWORD   OriginalFirstThunk; 000028A0H,转换后的FOA地址为1AA0H。

    };                                              

DWORD   TimeDateStamp; //为空。

DWORD   ForwarderChain; //为空。

    DWORD   Name; //000029A6H,转换后的FOA地址为1BA6H,” USER32.dll”。

    DWORD   FirstThunk; //00002088H,转换后的FOA地址为1288H。

} IMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_THUNK_DATA64 {

    union {

        ULONGLONG ForwarderString;  // PBYTE

        ULONGLONG Function;        // PDWORD

        ULONGLONG Ordinal;

        ULONGLONG AddressOfData;   //0000000000002998H,转换后的FOA地址为1B98H。

    } u1;

} IMAGE_THUNK_DATA64;

typedef struct _IMAGE_IMPORT_BY_NAME {

    WORD    Hint;           //028AH

    BYTE    Name[1];       // “MessagBoxW”

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

练习

1.继续完成实验二十三剩下导入表描述符的分析。    

实验二十四:遍历导入表。

我们以32位和64位记事本程序为例:

/*------------------------------------------------------------------------

 FileName:PrintImportDescriptor.c

 实验24:遍历导入表(支持32位和64位PE)

 (c) bcdaren, 2024

-----------------------------------------------------------------------*/

#include <stdio.h>

#include <windows.h>

#include <winnt.h>

#define WIN64

PBYTE creatfilemap(LPCWSTR szFile);

VOID import32(PBYTE lpvResult);

VOID import64(PBYTE lpvResult);

DWORD RvaToFoa(PIMAGE_NT_HEADERS ntHeaders, DWORD rva);

int main(int argc, char* argv[])

{

    LPCWSTR szFileName = TEXT("c:\\notepad64.exe");

    PBYTE lpAddress = NULL; //PE文件内存映射文件地址

    lpAddress = creatfilemap(szFileName);

    if (lpAddress)

    {

        printf("%ls\n", szFileName);

#ifdef WIN64

        import64(lpAddress);

#else

        import32(lpAddress);

#endif

    }

    system("pause");

    return 0;

}

//创建PE文件映射对象

PBYTE creatfilemap(LPCWSTR szFile)

{

    HANDLE hFile;

    HANDLE hMapFile = NULL;

    PBYTE lpMemory = NULL//PE文件内存映射文件地址

    char buffer[16] = { 0 };

    DWORD dwFileSize;

    DWORD dwBytesRead = 0;

    PIMAGE_DOS_HEADER lpstDOS = NULL;

    PIMAGE_NT_HEADERS lpstNT = NULL;

    hFile = CreateFile(szFile,

        GENERIC_READ,          // 只读打开

        FILE_SHARE_READ,       // 允许其他进程以读取方式打开文件

        NULL,                  // 默认安全属性

        OPEN_EXISTING,         // 打开已存在的文件

        FILE_ATTRIBUTE_NORMAL, // 普通文件

        NULL);

    if (hFile == INVALID_HANDLE_VALUE)

        printf("打开文件失败!\n");

    else

    {

        dwFileSize = GetFileSize(hFile, 0);//获得文件大小可通过结构体获取

        //创建内存映射文件

        if (dwFileSize)

        {

            if (hMapFile = CreateFileMapping(hFile,

NULL, PAGE_READONLY, 0, 0, NULL))

            {

                //获得文件在内存的映象起始位置

                lpMemory = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);

                if (!lpMemory)

                    printf("获取映像起始地址失败!\n");

                //检查PE文件是否有效

                lpstDOS = (PIMAGE_DOS_HEADER)lpMemory;

                if (lpstDOS->e_magic != IMAGE_DOS_SIGNATURE)

                    printf("非PE格式文件!\n");

                lpstNT = (PIMAGE_NT_HEADERS)(lpMemory + lpstDOS->e_lfanew);

                if (lpstNT->Signature != IMAGE_NT_SIGNATURE)

                    printf("非PE格式文件!\n");

            }

        }

    }

    return lpMemory;

}

//32位PE文件

VOID import32(PBYTE lpvResult)

{

    PIMAGE_DOS_HEADER pImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    PIMAGE_NT_HEADERS32 psImageNTHeader =

(PIMAGE_NT_HEADERS32)(lpvResult + pImageDOSHeader->e_lfanew);

    //导入表RVA

    DWORD ImportRVA =

psImageNTHeader->OptionalHeader.DataDirectory[1].VirtualAddress;

    DWORD dwTemp = 0;

    //获取导入表FOA地址

    PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor =

(PIMAGE_IMPORT_DESCRIPTOR)RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader,

ImportRVA);

    dwTemp = (DWORD)pImageDOSHeader + (DWORD)pImageImportDescriptor;

    pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;

    //遍历导入表描述符

    for (DWORD i = 0; *(DWORD*)(pImageImportDescriptor + i) != 0; i++)

    {

        //打印模块名

        DWORD NameRVA = (pImageImportDescriptor + i)->Name;

        DWORD NameFOA = RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader, NameRVA) +

(DWORD)pImageDOSHeader;

        printf("%s:\n", (PBYTE)NameFOA);

        //获取桥1 RVA地址

        DWORD OriginalFirstThunkRVA = (pImageImportDescriptor +

i)->OriginalFirstThunk;

        DWORD TimeDate = (pImageImportDescriptor + i)->TimeDateStamp;

        if (TimeDate == -1)

        {

            printf("已绑定导入函数。\n");

        }

        //IMAGE_ORDINAL_FLAG32用于表示32位的PE文件中导入函数的序号(Ordinal)

        if (OriginalFirstThunkRVA & IMAGE_ORDINAL_FLAG32)

        {

            printf("OriginalFirstThunk最高位为1,按序号导入函数。\n");

        }

        else

        {

            //转换为FOA地址

            PIMAGE_THUNK_DATA32 pImageFirstThunkData =

(PIMAGE_THUNK_DATA32)RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader,

OriginalFirstThunkRVA);

            dwTemp = (DWORD)pImageDOSHeader + (DWORD)pImageFirstThunkData;

            pImageFirstThunkData = (PIMAGE_THUNK_DATA32)dwTemp;

            for (DWORD j = 0; *((DWORD*)pImageFirstThunkData + j) != 0; j++)

            {

                //INT

                DWORD AddressOfDataRVA = (pImageFirstThunkData +

j)->u1.AddressOfData;

                PIMAGE_IMPORT_BY_NAME pImageImportByName =

(PIMAGE_IMPORT_BY_NAME)RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader, AddressOfDataRVA);

                dwTemp = (DWORD)pImageDOSHeader + (DWORD)pImageImportByName;

                pImageImportByName = (PIMAGE_IMPORT_BY_NAME)dwTemp;

                //输出函数名

                printf("0x%04X : %s\n", pImageImportByName->Hint,

pImageImportByName->Name);

            }

            printf("\n");

        }

    }

}

//64位PE文件

VOID import64(PBYTE lpvResult)

{

    PIMAGE_DOS_HEADER pImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    PIMAGE_NT_HEADERS64 psImageNTHeader = (PIMAGE_NT_HEADERS64)(lpvResult +

pImageDOSHeader->e_lfanew);

    //导入表RVA

    DWORD ImportRVA =

psImageNTHeader->OptionalHeader.DataDirectory[1].VirtualAddress;

    ULONGLONG dwTemp = 0;

    //获取导入表FOA地址

    PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor =

(PIMAGE_IMPORT_DESCRIPTOR)RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader, ImportRVA);

    dwTemp = (ULONGLONG)pImageDOSHeader + (ULONGLONG)pImageImportDescriptor;

    pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;

    //遍历导入表描述符

    for (DWORD i = 0; *(DWORD*)(pImageImportDescriptor + i) != 0; i++)

    {

        //打印模块名

        DWORD NameRVA = (pImageImportDescriptor + i)->Name;

        ULONGLONG NameFOA = RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader,

NameRVA) + (ULONGLONG)pImageDOSHeader;

        printf("%s:\n", (PBYTE)NameFOA);

        //获取桥1 RVA地址

        DWORD OriginalFirstThunkRVA = (pImageImportDescriptor +

i)->OriginalFirstThunk;

        DWORD TimeDate = (pImageImportDescriptor + i)->TimeDateStamp;

        if (TimeDate == -1)

        {

            printf("已绑定导入函数。\n");

        }

        //IMAGE_ORDINAL_FLAG64用于表示64位的PE文件中导入函数的序号(Ordinal)

        if (OriginalFirstThunkRVA & IMAGE_ORDINAL_FLAG64)

        {

            printf("OriginalFirstThunk最高位为1,按序号导入函数。\n");

        }

        else

        {

            //转换为FOA地址

            PIMAGE_THUNK_DATA64 pImageFirstThunkData =

(PIMAGE_THUNK_DATA64)RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader, OriginalFirstThunkRVA);

            dwTemp = (ULONGLONG)pImageDOSHeader +

(ULONGLONG)pImageFirstThunkData;

            pImageFirstThunkData = (PIMAGE_THUNK_DATA64)dwTemp;

    for (ULONGLONG j = 0; *((ULONGLONG*)pImageFirstThunkData + j) != 0; j++)

            {

                //INT

                ULONGLONG AddressOfDataRVA = (pImageFirstThunkData +

j)->u1.AddressOfData;

                PIMAGE_IMPORT_BY_NAME pImageImportByName =

(PIMAGE_IMPORT_BY_NAME)RvaToFoa((PIMAGE_NT_HEADERS)psImageNTHeader, (DWORD)AddressOfDataRVA);

                dwTemp = (ULONGLONG)pImageDOSHeader +

(ULONGLONG)pImageImportByName;

                pImageImportByName = (PIMAGE_IMPORT_BY_NAME)dwTemp;

                //输出函数名

                printf("0x%04X : %s\n", pImageImportByName->Hint,

pImageImportByName->Name);

            }

            printf("\n");

        }

    }

}

//RVA转FOA

DWORD RvaToFoa(PIMAGE_NT_HEADERS ntHeaders, DWORD rva) {

    //ntHeaders+4+sizeof(IMAGE_FILE_HEADER)+FileHeader.SizeOfOptionalHeader(32或64位PE)

    PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);

    WORD numberOfSections = ntHeaders->FileHeader.NumberOfSections;

    for (WORD i = 0; i < numberOfSections; i++) {

        DWORD sectionStartRva = sectionHeader->VirtualAddress;

        DWORD sectionEndRva = sectionStartRva + sectionHeader->SizeOfRawData;

        if (rva >= sectionStartRva && rva < sectionEndRva) {

            DWORD foa = sectionHeader->PointerToRawData + (rva

sectionStartRva);

            return foa;

        }

        sectionHeader++;

    }

    return 0;  // RVA not found

}

 

总结

       1.遍历导入表的过程:

       首先稍等数据目录项的第1项,即导入表项,获取导入表RVA地址,将其转换为FOA地址。

       然后根据导入表FOA地址,在PE文件中找到导入表描述符的起始地址,循环遍历获取导入表描述符的OriginalFirstThunk字段中保存的RVA地址,将此RVA地址转换为FOA地址,即INT表IMAGE_THUNK_DATA的起始地址。

       最后循环遍历获取INT表内的AddressOfData指向的IMAGE_IMPORT_BY_NAME结构体的RVA地址,将其转换为FOA地址,该地址存储导入函数序号提示值和函数名字符串。

       遍历过程如图4-1所示。

图4-1 遍历导入表

       2.在遍历导入表时,32位和64位PE文件的差异在于下面两个结构的不同:

typedef struct _IMAGE_NT_HEADERS64 {

    DWORD Signature;                                                //PE文件的签名(PE特征码)

    IMAGE_FILE_HEADER FileHeader;                     //文件头

    IMAGE_OPTIONAL_HEADER64 OptionalHeader;   //64位PE文件扩展头

} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

typedef struct _IMAGE_NT_HEADERS {

    DWORD Signature;  //PE文件的签名(PE特征码)

    IMAGE_FILE_HEADER FileHeader;  //文件头

    IMAGE_OPTIONAL_HEADER32 OptionalHeader; //32位PE文件扩展头

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

typedef struct _IMAGE_THUNK_DATA64 {

    union {

        ULONGLONG ForwarderString;  // PBYTE

        ULONGLONG Function;   // PDWORD

        ULONGLONG Ordinal;

        ULONGLONG AddressOfData;    // PIMAGE_IMPORT_BY_NAME

    } u1;

} IMAGE_THUNK_DATA64;

typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;

typedef struct _IMAGE_THUNK_DATA32 {

    union {

        DWORD ForwarderString;      // PBYTE

        DWORD Function;   // PDWORD

        DWORD Ordinal;

        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME

    } u1;

} IMAGE_THUNK_DATA32;

typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

3.OriginalFirstThunk为导入表描述符IMAGE_IMPORT_DESCRIPTOR结构的桥1字段,最高位为0表示按序号导入,最高位为1表示按名称导入。32位PE中取掩码IMAGE_ORDINAL_FLAG32,64位PE文件取掩码IMAGE_ORDINAL_FLAG64。

4.导入表描述符的TimeDateStamp字段的值如果为0xFFFFFFFF,表示导入函数已绑定,我们将在4.2节绑定导入表中详细讲解。

5.导入表描述符以一个空导入表描述符作为结束标志。

6.INT表中,不同DLL模块中的导入函数名以一个空的AddressOfData字段作为分隔符。

7.读者也可以尝试参考实验19中 ReadPe1.c中使用的方法,按照内存映像文件的方法实现,这样可以省略RVA转换为FOA地址的过程。

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

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

相关文章

本田汽车投资SiLC Technologies:携手共促自动驾驶技术新飞跃

SiLC Technologies获本田汽车投资:加速自动驾驶技术革新 近日,硅谷光子学初创公司SiLC Technologies宣布获得本田汽车的投资,这一合作标志着双方将共同推进自动驾驶技术领域的革新与发展。本田此次投资不仅体现了对SiLC Technologies技术实力的认可,也彰显了本田在自动驾驶…

进程管理和进程调度的基本过程(详细版)

“无论你走得多慢&#xff0c;总比停下来要好。” 对于操作系统内核来说&#xff0c;里面包含的功能是非常多的&#xff0c;其中有一个功能和日常开发&#xff0c;息息相关。 进程管理&#xff1a; 进程&#xff08;process/task&#xff09;&#xff1a;进程就是正在执行的应…

一个月学会Java 第4天 运算符和数据转换

Day4 运算符和数据转换 今天来讲运算符&#xff0c;每个运算符的作用和现象&#xff0c;首先我们先复习一下数据类型&#xff0c; day2讲过基本数据类型有八种&#xff0c;int、short、long、byte、char、boolean、float、double&#xff0c;分别为四个整型、一个字符型、一个布…

代码随想录 | Day28 | 回溯算法:组合组合总和III

代码随想录 | Day28 | 回溯算法&#xff1a;组合&&组合总和III 关于这个章节&#xff0c;大家最好是对递归函数的理解要比较到位&#xff0c;听着b站视频课可能呢才舒服点&#xff0c;可以先去搜一搜关于递归函数的讲解&#xff0c;理解&#xff0c;再开始这个章节会比…

01 从0开始搭建django环境

1 安装相关版本的django&#xff0c;这里&#xff0c;我以5.1.1为例子 pip3 install django5.1.1 (.venv) D:\DjangoCode\MS>pip3 install django5.1.1 Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple Collecting django5.1.1Using cached https://pypi.t…

算法题总结(七)——栈与队列

1、栈常用操作 &#xff08;1&#xff09;栈定义 Stack<Integer> stack new Stack<Integer>();&#xff08;2&#xff09;栈操作 .栈是否为空 isEmpty(); .查询栈顶元素&#xff0c;不改变栈 peek(); .弹出栈顶元素&#xff0c;改变栈 pop(); .压入栈顶 push(); …

Quantify LLM

大模型如果用bfloat16需要特别大的显存&#xff0c;所以都在用INT4、INT8做量化&#xff0c;效果不错 量化学习 为什么量化 对Llama13B模型来说&#xff0c;不同算子加载需要的显存不同 中间是TensorCore&#xff0c;左右两边是显存&#xff0c;加载过程中模型要频繁地将wei…

【移动端】事件基础

一、移动端事件分类 移动端事件主要分为以下几类&#xff1a; 1. 触摸事件&#xff08;Touch Events&#xff09; 触摸事件是移动设备特有的事件&#xff0c;用来处理用户通过触摸屏幕进行的操作。主要的触摸事件有&#xff1a; touchstart&#xff1a;手指触摸屏幕时触发。…

k8s 之动态创建pv失败(踩坑)

作者&#xff1a;程序那点事儿 日期&#xff1a;2024/02/05 18:53 初学容易范的错&#xff01; 问题简述&#xff1a; StorageClass创建成功&#xff0c;StatefulSet 启动不成功&#xff0c;与pvc关联的po和pvc一直是pending状态。 也就是说&#xff0c;StorageClass没有动态…

MQTT--Java整合EMQX

目录 1、简介2、准备3、使用步骤3.1 引入依赖3.2 创建生产者和消费者3.3 测试 总结PS: 1、简介 本文章实现了一个简单的MQTT客户端&#xff0c;使用Eclipse Paho库让Java和EMQX整合&#xff0c;测试客户端初始化时配置Broker地址、客户端ID、用户名和密码。连接成功后&#xf…

python sqlite3 工具函数

起因&#xff0c; 目的: sqlite3 最常用的函数。 比如&#xff0c;某人给了一个 database.db 文件。 但是你登录的时候&#xff0c;不知道账号密码。 此文件就是&#xff0c;查看这个数据库的详细内容。 有哪些表某个表的全部内容。添加数据 代码&#xff0c; 见注释 impor…

字节跳动收购Oladance耳机:强化音频技术,加速VR/AR生态布局

字节跳动收购Oladance耳机&#xff1a;加码VR/AR领域布局 近日&#xff0c;字节跳动宣布已完成对开放式耳机品牌Oladance的收购&#xff0c;实现了对该品牌的100%控股。这一收购标志着字节跳动在AI硬件领域的进一步扩展和深化&#xff0c;特别是对其VR/AR领域布局的重要加码。 …

python之运算符

1、算术运算符 算术运算符常用的有&#xff1a;&#xff0c;-&#xff0c;*&#xff0c; &#xff0c;/&#xff0c;//&#xff0c;%&#xff0c;>>,<< 1.1、加 常见的是算术相加&#xff0c;还有一种是字符串拼接。 a 10 b 20 print(a b) c "My &quo…

css 简单网页布局——浮动(一)

1. 三种布局方式 1.1 标准流 1.2 浮动的使用 1.3 简述浮动 1.3.1 浮动三大特性 <style>.out {border: 1px red solid;width: 1000px;height: 500px;}.one {background-color: aquamarine;width: 200px;height: 100px;}.two {background-color: blueviolet;width: 200px;h…

如何在算家云搭建CosyVoice(文生音频)

一、CosyVoice简介 CosyVoice 是一个开源的超强 TTS&#xff08;‌文本转语音&#xff09;‌模型&#xff0c;‌它支持多种生成模式&#xff0c;‌具有极强的语音自然可控性。‌ 具有以下特点&#xff1a; 语音合成 &#xff1a;能够将文本转换为自然流畅的语音输出。多语种…

让你的Github Profile高大时尚!

目录 前言 正文 GitHub Profile 特点&#xff1a; GitHub Actions 核心概念&#xff1a; 应用场景&#xff1a; RSS RSS的主要特点&#xff1a; 使用场景&#xff1a; RSS的工作原理&#xff1a; 关于Github Readme Card 关于Github贡献的3D图 关于个人最新博文的获取 关于代码…

今日指数-day08实战完整代码

今日指数-day08 1. 个股最新分时行情数据 1.1 个股最新分时行情功能说明 1&#xff09;个股最新分时行情功能原型 2&#xff09;个股最新分时行情数据接口分析 功能描述&#xff1a;获取个股最新分时行情数据&#xff0c;主要包含&#xff1a;开盘价、前收盘价、最新价、最…

(作业)第三期书生·浦语大模型实战营(十一卷王场)–书生基础岛第4关---InternLM + LlamaIndex RAG 实践

基础任务 (完成此任务即完成闯关) 任务要求&#xff1a;基于 LlamaIndex 构建自己的 RAG 知识库&#xff0c;寻找一个问题 A 在使用 LlamaIndex 之前InternLM2-Chat-1.8B模型不会回答&#xff0c;借助 LlamaIndex 后 InternLM2-Chat-1.8B 模型具备回答 A 的能力&#xff0c;截…

【MaskGAN】MaskGAN: Towards Diverse and Interactive Facial Image Manipulation

文章目录 MaskGAN: Towards Diverse and Interactive Facial Image Manipulationkey points贡献方法密集映射网络DMN编辑行为模拟训练多目标学习CelebAMask-HQ数据集实验消融实验总结MaskGAN: Towards Diverse and Interactive Facial Image Manipulation 会议/期刊:CVPR 202…

磁盘存储链式结构——B树与B+树

红黑树处理数据都是在内存中&#xff0c;考虑的都是内存中的运算时间复杂度。如果我们要操作的数据集非常大&#xff0c;大到内存已经没办法处理了该怎么办呢&#xff1f; 试想一下&#xff0c;为了要在一个拥有几十万个文件的磁盘中查找一个文本文件&#xff0c;设计的…