PE文件格式 super版

xiaoxiao2021-02-28  150

以《逆向工程核心原理》和博客园shadow_leii的内容为基础。 PE(Portable Executable)格式 ◆PE是指32位的可执行文件,也称为  PE32 64位windows只是对PE格式做了一些简单的修改,64位的可执行文件称为PE+或PE32+ ◆PE结构是一个平面连续的结构(如上图)【和数据恢复学的文件系统差不多】 从DOS头到节区头是  PE头部分,节区合成为  PE体 ◆文件中使用  偏移(offset),内存中使用VA(Virtual Adress)来表示位置。 ◆Window加载器 (PE加载器) 会浏览整个PE文件,再决定哪些要映射,文件的高偏移位置映射到高内存地址,低偏移位置映射到低内存地址。文件加载到内存时,情况会发生变化 (节区的大小、位置等。)   ←-----------VA ------RVA(相对虚拟地址) ------ 基地址(Imagebase) 在NT架构中就是句柄 ImageBase+RVA=VA 计算机中为了提高处理文件、内存、网络包的效率,使用“最小基本单位”这一概念。 PE头和各节区后都有一个趋于,成为NULL填充。文件/内存中的节区起始位置应该在各文件/内存的最小单位的倍数上,空白趋于用null填充 ◆基地址在NT架构中就是句柄来获取,HMODULEGetModuleHandle(LPCTSTR IpMouduleName) ◆32位windowos中,各进程分配有的4G虚拟内存,因此进程中VA值范围是00000000~FFFFFFFF   一。DOS头(IMAGE_DOS_HEADER结构体)[size:64byte] { +0hWORDe_magic //Magic DOS signature MZ(4Dh 5Ah)     DOS可执行文件标记 +2h WORDe_cblp//Bytes on last page of file   +4hWORDe_cp//Pages in file +6hWORD e_crlc//Relocations +8hWORDe_cparhdr   //Size of header in paragraphs +0ahWORD e_minalloc  //Minimun extra paragraphs needs +0chWORDe_maxalloc  //Maximun extra paragraphs needs +0ehWORDe_ss  //intial(relative)SS value     DOS代码的初始化堆栈SS +10hWORDe_sp  //intial SP value               DOS代码的初始化堆栈指针SP +12hWORDe_csum  //Checksum +14hWORDe_ip  //    intial IP value                   DOS代码的初始化指令入口[指针IP] +16hWORDe_cs  //intial(relative)CS value                    DOS代码的初始堆栈入口 +18hWORDe_lfarlc  //File Address of relocation table +1ahWORDe_ovno        //    Overlay number +1chWORDe_res[4]  // Reserved words +24hWORDe_oemid  //    OEM identifier(for e_oeminfo) +26hWORD      e_oeminfo   //    OEM information;e_oemid specific  +29hWORDe_res2[10]   //     Reserved words +3chLONG   e_lfanew     //Offset to start of PE header             指向PE文件头 } IMAGE_DOS_HEADER ENDS 结构体中有两个重要的数据成员。 第一个为e_magic,这个必须为MZ,即4D5A。 另一个是e_lfanew,这个成员的值为IMAGE_NT_HEADERS的偏移(指向PE文件头)。 其中,*e_lfanew这个字段的值:   PE Header 在磁盘文件中相对于文件开始的偏移地址.   注释 :long---4字节相当于dword   一。DOS根存 [size:不定] DOS根存是16位的汇编指令,32为系统会自动忽略,但可在debug中运行 二。NT头 (IMAGE_NT_HEADERS) [size:248byte]15行   ◆+0h       DWORD Signature;                 //4 bytes PE文件头  标志  :(e_lfanew)->‘PE’ ◆+4h       IMAGE_FILE_HEADER FileHeader;        //20 bytes PE文件物理分布的信息 ◆+18h     IMAGE_OPTIONAL_HEADER32 OptionalHeader;//224bytes PE文件逻辑分布的信息 (1)。 DWORD Signature;                  [size:20 byte]  标志 : 50 45 00 00------- →PE (2)。IMAGE_FILE_HEADER FileHeader  [size:20 byte]           typedef struct _IMAGE_FILE_HEADER {             +04h     WORD    Machine;                //运行平台             +06h     WORD    NumberOfSections;        //文件区块数目             +08h     DWORD   TimeDateStamp;            //文件创建日期和时间             +0ch     DWORD   PointerToSymbolTable;    //指向符号表(主要用于调试)             +10h     DWORD   NumberOfSymbols;        //符号表中符号个数             +14h     WORD    SizeOfOptionalHeader;        //IMAGE_OPTIONAL_HEADER32 结构大小             +16h     WORD    Characteristics;            //文件属性             } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;     1). Machine 代表了CPU的类型  //运行平台     #define IMAGE_FILE_MACHINE_UNKNOWN           0     #define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.     #define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian     #define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian     #define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian     #define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2     #define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP     #define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian     #define IMAGE_FILE_MACHINE_SH3DSP            0x01a3     #define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian     #define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian     #define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5     #define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian     ………………….     #define IMAGE_FILE_MACHINE_CEE               0xC0EE          #define  IMAGE_FILE_MACHINE_IA64               0x0200    // Intel x64      # define  IMAGE_FILE_MACHINE_AMD64             0x8664     // x64         # define                                                  0x0266 0x0366 0x0466    //MIPS     # define                                                     0x0248    //APLHA64    2)       NumberOfSections: 代表区块的数目,区块表紧跟在IMAGE_NT_HEADERS后面, 区块表大概是一个链表结构,链表长度由NumberOfSection的数值决定. 3)       TimeDataStamp: 表明文件的创建时间 4)       SizeOfOptionalHeader: 是IMAGE_NT_HEADERS的另一个子结构IMAGE_OPTIONAL_HEADER的大小 (E0 00即十进制224byte) 5)       Characteristics: 代表文件的属性EXE文件一般是0100h DLL文件一般是210Eh,多种属性可以用或运算同时拥有                 #define IMAGE_FILE_RELOCS_STRIPPED   0x0001 // 重定位信息被移除                  #define IMAGE_FILE_EXECUTABLE_IMAGE   0x0002 // 文件可执行                  #define IMAGE_FILE_LINE_NUMS_STRIPPED  0x0004 // 行号被移除                  #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符号被移除                  ……..10 20 80 标志已经过时                 #define IMAGE_FILE_32BIT_MACHINE  0x0100 // 32位机器                  #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的调试信息被移除                   #define   IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400   // 如果图像在可移动介质上,请将其复制并从交换文件运行。                 #define   IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800  // 如果图像在网络上,请将其复制并从交换文件运行。                 #define IMAGE_FILE_SYSTEM       0x1000 // 系统文件                  #define IMAGE_FILE_DLL         0x2000 // 文件是一个dll                  #define IMAGE_FILE_UP_SYSTEM_ONLY    0x4000 // 文件只能运行在单处理器上                     ……..8000 标志已经过时 *属性0F 01即10F 属于 32位机器、 重定位信息被移除、 文件可执行、 行号被移除 、 符号被移除   (3)。   IMAGE_OPTIONAL_HEADER32 OptionalHeader   [size:224 byte] 紧接IMAGE_FILE_HEADER之后,IMAGE_OPTIONAL_HEADER的大小由IMAGE_FILE_HEADER中倒数第二个成员(SizeOfOptionalHeader)指定. IMAGE_OPTIONAL_HEADER结构体如下:           typedef struct _IMAGE_OPTIONAL_HEADER {           +18h    WORD    Magic;    //映像文件的状态            +1Ah     BYTE    MajorLinkerVersion;    //连接器的主版本号            +1Bh     BYTE    MinorLinkerVersion;    //连接器的次版本号            +1Ch     DWORD   SizeOfCode;    //代码段的大小,如果有多个代码段则为总和            +20h     DWORD   SizeOfInitializedData; //初始化数据段大小.如果多个则为总和            +24h     DWORD   SizeOfUninitializedData;//未初始化数据段大小,.如果多个则为总和.bbs            +28h     DWORD   AddressOfEntryPoint;    //PE文件入口地址的RAV:OEP = ImageBase + RAV            +2Ch     DWORD   BaseOfCode;    //代码块起始地址的RVA            +30h     DWORD   BaseOfData;//数据块的起始地址的RVA                        //                        // NT additional fields.                        //            +34h     DWORD   ImageBase;    //可执行文件的基址ImageBase             +38h     DWORD   SectionAlignment; //每一个块必须保证始于这个值的整数倍            +3Ch     DWORD   FileAlignment; //对齐映射文件部分原始数据 2 or 512 or 64: 默认为512            +40h     WORD    MajorOperatingSystemVersion;//要求的操作系统的主版本号            +42h     WORD    MinorOperatingSystemVersion;//要求的操作系统的次版本号            +44h     WORD    MajorImageVersion;//映像的主版本号            +46h     WORD    MinorImageVersion;//映像的次版本号            +48h     WORD    MajorSubsystemVersion;//子系统的主版本号            +4Ah     WORD    MinorSubsystemVersion;//子系统的次版本号            +4Ch     DWORD   Win32VersionValue;//保留值.必须为0            +50h     DWORD   SizeOfImage;//映像文件的大小            +54h     DWORD   SizeOfHeaders;            +58h     DWORD   CheckSum;//映像文件的校验和            +5Ch     WORD    Subsystem;//运行此映像的字系统            +5Eh     WORD    DllCharacteristics;//映像文件的DLL特征            +60h     DWORD   SizeOfStackReserve;//堆栈保留字节. 0x100000            +64h     DWORD   SizeOfStackCommit;//线程开始提交的初始堆栈大小            +68h     DWORD   SizeOfHeapReserve;//为初始进程保留的虚拟内存总数            +6Ch     DWORD   SizeOfHeapCommit;//进程开始提交初始虚拟内存的大小            +70h     DWORD   LoaderFlags;            +74h     DWORD   NumberOfRvaAndSizes; //0x10            +78 h     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];           //指向第一个IMAGE_DATA_DIRECTORY           } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;    在IMAGE_OPTIONAL_HEADER32结构体中需要关注的成员。这些值是文件运行必须的,设置错误会导致文件无法正常运行。

1) Magic:32位可执行文件来:0x010B

         64位可执行文件来:0x020B   2) AddressOfEntryPoint: 程序执行的入口RVA地址  (指程序最先执行的代码位置) 3) ImageBase: 建议的装载地址   进程虚拟内存的范围00000000~FFFFFFFF,EXE、DLL文件被装载到用户内存0~07FFFFFF,SYS文件被装载到内核内存80000000~FFFFFFFF。一般而言,一般开发工具(VB/VC++/Delphi),创建的EXE文件ImageBase为00400000,DLL为10000000 执行PE文件时,PE装载器先创建进程,再将文件载入内存,然后保存EIP=ImageBase+AddressOfEntryPoint 4)SectionAlignment:为内存中节的对齐大小,一般为0×00001000    FileAlignment:为PE文件中节的对齐大小(200??)         也就是说,每个节被装入的地址必定是本字段指定数值的整数倍。 5) SizeOfImage:映像文件的大小 (虚拟内存中大小) 6)SizeOfHeaders:整个PE头的大小。该值必须为FileAlignment的整数倍。(文件开始到第一节区的偏移量) 7)Subsystem字段:区分系统驱动文件sys和普通可执行文件exe、dll         1        系统驱动(sys)         2        窗口应用程序(notepad.exe)         3        控制台应用程序(cmd) 8)NumberOfRvaAndSizes:指定datadirectory的数组个数 9) DataDirectory为数据目录表数组,比较重要:共有16个表项

Size = sizeof(_IMAGE_DATA_DIRECTORY) * 16

sizeof(_IMAGE_DATA_DIRECTORY) = 8 bytes

_IMAGE_DATA_DIRECTORY结构体以及成员定义:          typedef struct _IMAGE_DATA_DIRECTORY {

               DWORD   VirtualAddress;

               DWORD   Size;

           } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

           #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

           // Directory Entries

           #define IMAGE_DIRECTORY_ENTRY_EXPORT            0   // Export Directory

           #define IMAGE_DIRECTORY_ENTRY_IMPORT             1   // Import Directory

           #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory

           #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory

           #define IMAGE_DIRECTORY_ENTRY_SECURITY          4   // Security Directory

           #define IMAGE_DIRECTORY_ENTRY_BASERELOC        5   // Base Relocation Table

           #define IMAGE_DIRECTORY_ENTRY_DEBUG             6   // Debug Directory

           //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT        7   // (X86 usage)

           #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE  7   // Architecture Specific Data

           #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR         8   // RVA of GP

           #define IMAGE_DIRECTORY_ENTRY_TLS                 9   // TLS Directory

           #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10 // Load Configuration Directory

           #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT     11 // Bound Import Directory in headers

           #define IMAGE_DIRECTORY_ENTRY_IAT              12   // Import Address Table

           #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT    13   // Delay Load Import Descriptors

           #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor    三。节区头(IMAGE_SECTION_HEADER)[size:40byte]两行半

Section Header 数量:_IMAGE_FILE_HEADER结构体中NumberOfSections成员。

Section Header 定位:紧跟在IMAGE_NT_HEADERS后面

 #define IMAGE_SIZEOF_SHORT_NAME   8   typedef struct _IMAGE_SECTION_HEADER {       dword    Name[IMAGE_SIZEOF_SHORT_NAME];       union {               DWORD   PhysicalAddress;               DWORD   VirtualSize;       } Misc;       DWORD   VirtualAddress;    //内存中偏移地址       DWORD   SizeOfRawData;    //PE文件中对其之后的大小       DWORD   PointerToRawData;//为PE块区在PE文件中偏移       DWORD   PointerToRelocations;       DWORD   PointerToLinenumbers;       WORD    NumberOfRelocations;       WORD    NumberOfLinenumbers;       DWORD   Characteristics;    //块区的属性:可读、可写..   } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;    #define IMAGE_SIZEOF_SECTION_HEADER          40 重要数据成员 1)      Name[IMAGE_SIZEOF_SHORT_NAME]:

这是一个由8位的ASCII 码名,用来定义区块的名称。(其实并没有限制必须使用ascii,可以放任何值,甚至可以用null填充)

多数区块名都习惯性以一个“.”作为开头(例如:.text),这个“.” 实际上是不是必须的。

值得我们注意的是,如果区块名超过 8 个字节,则没有最后的终止标志“NULL” 字节。   2)       Virtual Size:表对应的区块的大小,这是区块的数据在没有进行对齐处理前的实际大小。 3)      Virtual Address:该区块装载到内存中的RVA 地址。 4)       SizeOfRawData:该区块在磁盘中所占的大小。 5)       PointerToRawData:该区块在磁盘中的偏移。 6)       Characteristics:该区块的属性。(如代码/数据/可读/可写等)的标志。

    参考RVA to RWA (内存地址与文件偏移间的映射)[偏移是在硬盘的概念] Windows 装载器在装载DOS部分、PE文件头部分和节表(区块表)部分是不进行任何特殊处理的,而在装载节(区块)的时候则会自动按节(区块)的属性做不同的处理。  步骤: 1)查找RVA所在节区(.text / .rsrc /.data)  2)根据公式计算: RAW-PointToRawData =RVA-VirtualAddress     RAW=RVA-VirtualAddress+PointToRawData  **(这个VirtualAddress是节区头的VirtualAddress,其实就是距离基地址的RVA)

四。IAT(Import Address Table)在块里面 IAT保存的内容与window操作系统的核心进程、内存、dll结构相关 IAT是一种表格,用来记录程序正在使用哪些库,哪些函数 ------------------------------------------------------------------------------------------------------------------------------------------ 参考:DLL“动态链接库”,需要的时候调用 ◆加载DLL方式 1.“显式链接”,程序使用DLL时加载,使用完毕后释放内存 2.“隐式链接 ”,程序开始的时候加载DLL,程序终止时释放占用的内存 ◆所有API调用均采用通过某处地址的值来实现,(如:call dword ptr DS:[01001104],就是通过 DS:[0111104 ]的值,该值就是加载DLL的函数地址 *因为系统的不同(dos、xp、9x、vista、7等) kernel32.dll的版本各不相同,对应的函数地址都不同,因此为了兼容,将个版本确切的函数地址存在某个特定地址,如编译器把CreatFileW()函数的实际地址存在了01001104,并通过 call dword ptr DS:[01001104]来调用,编译器并不存在call7C8107F0(某个版本 CreatFileW()函数的实际地址 ) 还有原因是重定向,DLL加载到内存中,位置可能因前一个dll占用了而重定向,实际中无法保证dll被加载到指定的ImageBase(但exe却能准确加载到自身 ImageBase ,还有一个原因是,PE头中表示地址不实用VA而是RVA(相对虚拟地址)    ------------------------------------------------------------------------------------------------------------------------------------------ IMAGE_IMPORT_DESCRIPTOR    [SIZE:20 bytes] (记录这PE要导入哪些库文件,向库提供服务/函数) 执行一个普通程序时往往需要导入多个库,导入多少库就存在多少个  IMAGE_IMPORT_DESCRIPTOR结构体        typedef struct _IMAGE_IMPORT_DESCRIPTOR {             union {                。。。。                  DWORD   OriginalFirstThunk; // RVA 指向INT ( IMAGE_IMPORT_BY_NAME )                };                 DWORD   TimeDateStamp;                     DWORD   ForwarderChain;     // -1 if no forwarders                  DWORD   Name;            //dll 名称                  DWORD   FirstThunk;         //指向引入函数真实地址单元处的RVA  IAT(IMAGE_ADDRESS_TABLE)                     } IMAGE_IMPORT_DESCRIPTOR;         typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR; * 其中OriginalFirstThunk和FirstThunk非常类似,指向两个本质上相同的数组IMAGE_THUNK_DATA。 INT IMAGE_IMPORT_BY_NAME IAT输入顺序 : 1.读取IID的Name成员,获取库名称字符串(“kernel32.dll”) 2.装载相应库。 →LoadLibrary( “kernel32 .dll”   3.读取IID的OriginalFirstThunk成员,获取INT地址 4.逐一读取INT中数组的值,获取相应  IMAGE_IMPORT_BY_NAME地址(RVA) 5.使用  IMAGE_IMPORT_BY_NAME的Hint(ordinal)或者Name项,获取相应函数的起始地址               →GetProcAddress(GetCurrentThreadld) 6.读取IID的FirstThunk(IAT)成员,获取IAT地址 7.重复4~7直到INT结束(遇到NULL时)  定位查找IMAGE_IMPORT_DESCRIPTO结构   在可选头看到importRVA 44 34 而4434位于rdata,rdata位于404000(在od里看到) 在节区头rdata看到pointertorawdata为4000  所以 RAW= RVA-VirtualAddress+ PointToRawData 4434-4000+4000=4434   阴影部分即IID ,IID的大小为20字节 看第一个 OriginalFirstThunk 70 44 00 00  即4470RVA 4470位于rdata,rdata位于404000(在od里看到) 在节区头rdata看到pointertorawdata为4000 所以 4470-4000+4000=4470   第一个值为46F0(RVA)进入该地址可以看到导入的API函数字符串  看最后一个FirstThunk:4000(RVA)转为RAW即4000     【*内容都可以在PE view看到】   获取dll调用的所有函数(就是用上面的INT和IAT来看函数) IMAGE_IMPORT_DESCRIPTOR中的第一个参数和最后一个参数,original_first_thunk 和first_thunk分别指向了INT(输入名称表)IAT(输入地址表)这两个表里面分别记录了指向调用函数名的地址,和此函数在dll中的序号(序号用来快速索引dll中的函数) 

 需要注意的地方

INT 和IAT数组在一开始的时候,里面存放的地址都是一样的,他们都是指向所调用函数的名字的字符串。而在加载到内存的时候,IAT的值会发生变换,它的值存放的是dll中函数实际被调用的地址,在加载到内存后,就只需要保存IAT就可以了,利用它来调用函数

  四。EAT(IMAGE_EXPORT_DIRECTORY) 1.可以在IMAGE_OPTIONAL_HEADER找到EXPORT TABLE的RVA 结构 IMAGE_EXPORT_DIRECTORY STRUCT Characteristics     DWORD ? ; 未使用,总是定义为0 TimeDateStamp     DWORD ?       ; 文件生成时间 MajorVersion     WORD ? ; 未使用,总是定义为0 MinorVersion     WORD ? ; 未使用,总是定义为0 Name             DWORD ? ; 模块的真实名称 Base                     DWORD ? ; 基数,加上序数就是函数地址数组的索引值 NumberOfFunctions DWORD ? ; 导出函数的总数 NumberOfNames DWORD ? ; 以名称方式导出的函数的总数 AddressOfFunctions DWORD ? ; 指向输出函数地址的RVA AddressOfNames DWORD ? ; 指向输出函数名字的RVA AddressOfNameOrdinals DWORD ? ; 指向输出函数序号的RVA IMAGE_EXPORT_DIRECTORY ENDS 重要成员 NumberOfFunctions                             实际export函数的个数 NumberOfNames                       export 函数中具名的函数个数 AddressOfFunctions                    export函数地址数组(数组元素个数=NumberOfFunctions) AddressOfNames                                 函数名称地址数组(数组元素个数=NumberOfNames) AddressOfNameOrdinals                     Ordinal地址数组(数组元素个数=NumberOfNames)   如何获得函数地址? GetProcAddress()操作原理 1.利用“AdressOfName”成员转到“函数名称数组”                             [4个字节组成的数组] 2. “函数名称数组” 存储着字符串地址。通过比较(stcmp)字符串,查找指定的函数名称(此时数组的索引称为name_index)      [索引到是第0、1、2、3。。。。个] 3. 利用AddressOfNameOrdinals成员,转到Ordinal数组                    4.在Ordinal数组中通过name_index [索引到的0、1、2、3。。。个] 查找相应ordinal值                 [2个字节组成的数组][00 00 01 00 02 00 03 00 04 00这样排序的数组] 5.利用AddressOfFunctions成员转到“函数地址数组(EAT)”                     [4个字节组成的数组] 6.在函数地址数组中将刚刚求的的ordinal用作数组索引,获得指定函数的起始地址。 理解:如 通过AdressOfName找到第2个是目标数组,到AddressOfNameOrdinals数组找2个数组,记住该值,在AddressOfFunctions数组用该值找  函数的地址 对于没有函数名称的导出函数,可以通过 Ordinals 查找它们的地址。从 Ordinals值中减去 IMAGE_EXPORT_DIRECTORY的base值得到一个数,使用该数作为“ 函数地址数组”的索引,即可查到相应的函数地址。 最后: 1)EAT提到的 ordinal究竟是什么 把ordinal想成目标导出函数的编号就好了,有时有些函数不会对外公开函数名,仅用编号( ordinal ) ,导入并使用这类函数的时候,先用 ordinal查找相应的函数地址后在调用比如下面示例(1)通过函数名称来获取函数地址,示例(2)则使用函数的 ordinal来获取函数地址 示例 (1) pFunc=GetProcAddress(    "TestFunc"    ) 示例(2)pFunc=Get ProcAddress(5);
转载请注明原文地址: https://www.6miu.com/read-67118.html

最新回复(0)