上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,DataDirectory中的每一项都可以用这个函数获取,函数原型如下:

PVOID NTAPI RtlImageDirectoryEntryToData(PVOID Base, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size);

Base:模块基地址。

MappedAsImage:是否映射为映象。

Directory:数据目录项的索引。

  1. #define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
  2. #define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
  3. #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
  4. #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
  5. #define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
  6. #define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
  7. #define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
  8. //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
  9. #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
  10. #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
  11. #define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
  12. #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
  13. #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
  14. #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
  15. #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
  16. #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

Size:对应数据目录项的大小,比如Directory为0,则表示导出表的大小。

返回值表示数据目录项的起始地址。

这次来看看第一项:导出表。

导出表是用来描述模块中的导出函数的结构,如果一个模块导出了函数,那么这个函数会被
记录在导出表中,这样通过GetProcAddress函数就能动态获取到函数的地址。函数导出的方式有两种,一种是按名字导出,一种是按序号导出。这两
种导出方式在导出表中的描述方式也不相同。模块的导出函数可以通过Dependency walker工具来查看:

上图中红框位置显示的就是模块的导出函数,有时候显示的导出函数名字中有一些符号,像 ??0CP2PDownloadUIInterface@@QAE@ABV0@@Z,这种是导出了C++的函数名,编译器将名字进行了修饰。

下面看一下导出表的定义吧:

  1. typedef struct _IMAGE_EXPORT_DIRECTORY {
  2. DWORD   Characteristics;
  3. DWORD   TimeDateStamp;
  4. WORD    MajorVersion;
  5. WORD    MinorVersion;
  6. DWORD   Name;
  7. DWORD   Base;
  8. DWORD   NumberOfFunctions;
  9. DWORD   NumberOfNames;
  10. DWORD   AddressOfFunctions;     // RVA from base of image
  11. DWORD   AddressOfNames;         // RVA from base of image
  12. DWORD   AddressOfNameOrdinals;  // RVA from base of image
  13. } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

结构还算比较简单,具体每一项的含义如下:

Characteristics:现在没有用到,一般为0。

TimeDateStamp:导出表生成的时间戳,由连接器生成。

MajorVersion,MinorVersion:看名字是版本,实际貌似没有用,都是0。

Name:模块的名字。

Base:序号的基数,按序号导出函数的序号值从Base开始递增。

NumberOfFunctions:所有导出函数的数量。

NumberOfNames:按名字导出函数的数量。

AddressOfFunctions:一个RVA,指向一个DWORD数组,数组中的每一项是一个导出函数的RVA,顺序与导出序号相同。

AddressOfNames:一个RVA,依然指向一个DWORD数组,数组中的每一项仍然是一个RVA,指向一个表示函数名字。

AddressOfNameOrdinals:一个RVA,还是指向一个WORD数组,数组中的每一项与AddressOfNames中的每一项对应,表示该名字的函数在AddressOfFunctions中的序号。

第一次接触这个结构的童鞋被后面的5项搞晕了吧,理解这个结构比结构本身看上去要复杂一些,文字描述不管怎么说都显得晦涩,所谓一图胜千言,无图无真相,直接上图:

在上图中,AddressOfNames
指向一个数组,数组里保存着一组RVA,每个RVA指向一个字符串,这个字符串即导出的函数名,与这个函数名对应的是
AddressOfNameOrdinals中的对应项。获取导出函数地址时,先在AddressOfNames中找到对应的名字,比如Func2,他在AddressOfNames中是第二项,然后从AddressOfNameOrdinals中取出第二项的值,这里是2,表示函数入口保存在AddressOfFunctions这个数组中下标为2的项里,即第三项,取出其中的值,加上模块基地址便是导出函数的地址。如果函数是以序号导出的,那么查找的时候直接用序号减去Base,得到的值就是函数在AddressOfFunctions中的下标。

用代码实现如下:

    1. DWORD* CEAT::SearchEAT( const char* szName)
    2. {
    3. if (IS_VALID_PTR(m_pTable))
    4. {
    5. bool bByOrdinal = HIWORD(szName) == 0;
    6. DWORD* pProcs = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfFunctions));
    7. if (bByOrdinal)
    8. {
    9. DWORD dwOrdinal = (DWORD)szName;
    10. if (dwOrdinal < m_pTable->NumberOfFunctions && dwOrdinal >= m_pTable->Base)
    11. {
    12. return &pProcs[dwOrdinal-m_pTable->Base];
    13. }
    14. }
    15. else
    16. {
    17. WORD* pOrdinals = (WORD*)((char*)RVA2VA(m_pTable->AddressOfNameOrdinals));
    18. DWORD* pNames = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfNames));
    19. for (unsigned int i=0; i<m_pTable->NumberOfNames; ++i)
    20. {
    21. char* pNameVA = (char*)RVA2VA(pNames[i]);
    22. if (strcmp(szName, pNameVA) != 0)
    23. {
    24. continue;
    25. }
    26. return &pProcs[pOrdinals[i]];
    27. }
    28. }
    29. }
    30. return NULL;
    31. }

PE文件结构详解(三)PE导出表的更多相关文章

  1. PE文件结构详解(四)PE导入表

    PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...

  2. PE文件结构详解(六)重定位

    前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里 ...

  3. PE文件结构详解(二)可执行文件头

    在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格 ...

  4. PE文件结构详解(五)延迟导入表

    PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...

  5. PE文件结构详解(三)

    0x01 前言 上一篇讲到了数据目录表的结构和怎找到到数据目录表(DataDirectory[16]),这篇我们我来讲讲数据目录表后面的另一个结构——区块表. 0x01 区块 区块就是PE载入器将PE ...

  6. PE文件结构详解(一)基本概念

    PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名.那 ...

  7. PE文件结构详解

    (注:最左边是文件头的偏移量.) IMAGE_DOS_HEADER STRUCT { +0h WORD e_magic // Magic DOS signature MZ(4Dh 5Ah) DOS可执 ...

  8. 小甲鱼PE详解之输入表(导出表)详解(PE详解09)

    小甲鱼PE详解之输出表(导出表)详解(PE详解09) 当PE 文件被执行的时候,Windows 加载器将文件装入内存并将导入表(Export Table) 登记的动态链接库(一般是DLL 格式)文件一 ...

  9. PE文件详解(三)

    本文转自小甲鱼的PE文件详解系列传送门 PE文件到内存的映射 在执行一个PE文件的时候,windows 并不在一开始就将整个文件读入内存的,二十采用与内存映射文件类似的机制. 也就是说,windows ...

随机推荐

  1. javascript构造函数小记

    function outer(){ function inner(){} return inner; } var a=outer(); var b=outer(); var c=new outer() ...

  2. 15个web前端的美轮美奂的 jQuery 图片特效

    jQuery是一个非常优秀的 JavaScript 框架,使用简单灵活,同时还有许多成熟的插件可供选择.其中,jQuery 最令人印象深刻的应用之一就是对图片的处理,它可以让帮助你在你的项目中加入各种 ...

  3. hidden(隐藏域)

    <input type="hidden">? 这等于是隐藏值,客户端无需显示,但提交后后台能够接受,例如你放个表单验证值在里边,这样可以拒绝来源不明的表单提交.还有判断 ...

  4. 使用HttpWebRequest以及HttpWebResponse读取Http远程文件

     主页>杂项技术>.NET(C#)> 使用HttpWebRequest以及HttpWebResponse读取Http远程文件 jackyhwei 发布于 2010-08-15 21: ...

  5. win32开发基础

    收集的,正在学习... 跟我一起玩Win32开发(1):关于C++的几个要点 跟我一起玩Win32开发(2):完整的开发流程 跟我一起玩Win32开发(3):窗口的重绘 跟我一起玩Win32开发(4) ...

  6. Noesis.Javascript.dll 引用时报错“找不到指定的模块”

    Could not load file or assembly 'Noesis.Javascript.dll' or one of its dependencies. 找不到指定的模块. 通过反编译发 ...

  7. 所有外包项目威客网站列表----来自程序员接私活网qxj.me

    猪八戒    http://www.zhubajie.com/  有佣金,建议别去坑死了 csto      http://www.csto.com/ 开源中国众包   https://zb.osch ...

  8. AngularJS(14)-动画

    AngularJS 提供了动画效果,可以配合 CSS 使用. AngularJS 使用动画需要引入 angular-animate.min.js 库. <!DOCTYPE html> &l ...

  9. PHP 判断客户端请求是 Android 还是 IOS

    <?php if(strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone')||strpos($_SERVER['HTTP_USER_AGENT'], 'iPad ...

  10. php Smarty date_format [格式化时间日期]

    Example 5-8. date_format[日期格式] index.php: 复制代码代码如下: $smarty = new Smarty; $smarty->assign('yester ...