PE文件结构详解(三)PE导出表
上篇文章 PE文件结构详解(二)可执行文件头 的结尾出现了一个大数组,这个数组中的每一项都是一个特定的结构,通过函数获取数组中的项可以用RtlImageDirectoryEntryToData函数,DataDirectory中的每一项都可以用这个函数获取,函数原型如下:
PVOID NTAPI RtlImageDirectoryEntryToData(PVOID Base, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size);
Base:模块基地址。
MappedAsImage:是否映射为映象。
Directory:数据目录项的索引。
- #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
Size:对应数据目录项的大小,比如Directory为0,则表示导出表的大小。
返回值表示数据目录项的起始地址。
这次来看看第一项:导出表。
导出表是用来描述模块中的导出函数的结构,如果一个模块导出了函数,那么这个函数会被
记录在导出表中,这样通过GetProcAddress函数就能动态获取到函数的地址。函数导出的方式有两种,一种是按名字导出,一种是按序号导出。这两
种导出方式在导出表中的描述方式也不相同。模块的导出函数可以通过Dependency walker工具来查看:
上图中红框位置显示的就是模块的导出函数,有时候显示的导出函数名字中有一些符号,像 ??0CP2PDownloadUIInterface@@QAE@ABV0@@Z,这种是导出了C++的函数名,编译器将名字进行了修饰。
下面看一下导出表的定义吧:
- typedef struct _IMAGE_EXPORT_DIRECTORY {
- DWORD Characteristics;
- DWORD TimeDateStamp;
- WORD MajorVersion;
- WORD MinorVersion;
- DWORD Name;
- DWORD Base;
- DWORD NumberOfFunctions;
- DWORD NumberOfNames;
- DWORD AddressOfFunctions; // RVA from base of image
- DWORD AddressOfNames; // RVA from base of image
- DWORD AddressOfNameOrdinals; // RVA from base of image
- } 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中的下标。
用代码实现如下:
- DWORD* CEAT::SearchEAT( const char* szName)
- {
- if (IS_VALID_PTR(m_pTable))
- {
- bool bByOrdinal = HIWORD(szName) == 0;
- DWORD* pProcs = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfFunctions));
- if (bByOrdinal)
- {
- DWORD dwOrdinal = (DWORD)szName;
- if (dwOrdinal < m_pTable->NumberOfFunctions && dwOrdinal >= m_pTable->Base)
- {
- return &pProcs[dwOrdinal-m_pTable->Base];
- }
- }
- else
- {
- WORD* pOrdinals = (WORD*)((char*)RVA2VA(m_pTable->AddressOfNameOrdinals));
- DWORD* pNames = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfNames));
- for (unsigned int i=0; i<m_pTable->NumberOfNames; ++i)
- {
- char* pNameVA = (char*)RVA2VA(pNames[i]);
- if (strcmp(szName, pNameVA) != 0)
- {
- continue;
- }
- return &pProcs[pOrdinals[i]];
- }
- }
- }
- return NULL;
- }
PE文件结构详解(三)PE导出表的更多相关文章
- PE文件结构详解(四)PE导入表
PE文件结构详解(二)可执行文件头的最后展示了一个数组,PE文件结构详解(三)PE导出表中解释了其中第一项的格式,本篇文章来揭示这个数组中的第二项:IMAGE_DIRECTORY_ENTRY_IMPO ...
- PE文件结构详解(六)重定位
前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里 ...
- PE文件结构详解(二)可执行文件头
在PE文件结构详解(一)基本概念里,解释了一些PE文件的一些基本概念,从这篇开始,将详细讲解PE文件中的重要结构. 了解一个文件的格式,最应该首先了解的就是这个文件的文件头的含义,因为几乎所有的文件格 ...
- PE文件结构详解(五)延迟导入表
PE文件结构详解(四)PE导入表讲 了一般的PE导入表,这次我们来看一下另外一种导入表:延迟导入(Delay Import).看名字就知道,这种导入机制导入其他DLL的时机比较“迟”,为什么要迟呢?因 ...
- PE文件结构详解(三)
0x01 前言 上一篇讲到了数据目录表的结构和怎找到到数据目录表(DataDirectory[16]),这篇我们我来讲讲数据目录表后面的另一个结构——区块表. 0x01 区块 区块就是PE载入器将PE ...
- PE文件结构详解(一)基本概念
PE(Portable Execute) 文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 何扩展名.那 ...
- PE文件结构详解
(注:最左边是文件头的偏移量.) IMAGE_DOS_HEADER STRUCT { +0h WORD e_magic // Magic DOS signature MZ(4Dh 5Ah) DOS可执 ...
- 小甲鱼PE详解之输入表(导出表)详解(PE详解09)
小甲鱼PE详解之输出表(导出表)详解(PE详解09) 当PE 文件被执行的时候,Windows 加载器将文件装入内存并将导入表(Export Table) 登记的动态链接库(一般是DLL 格式)文件一 ...
- PE文件详解(三)
本文转自小甲鱼的PE文件详解系列传送门 PE文件到内存的映射 在执行一个PE文件的时候,windows 并不在一开始就将整个文件读入内存的,二十采用与内存映射文件类似的机制. 也就是说,windows ...
随机推荐
- iOS-RunLoop,为手机省电,节省CPU资源,程序离不开的机制
RunLoop是什么?基本操作是什么? 1.RunLoop的作用 RunLoop可以: 保持程序的持续运行 处理App中的各种事件(比如触摸事件.定时器事件.Selector事件) 节省CPU资源,提 ...
- JavaScript学习笔记 -- 带参数arguments的函数的用法
JavaScript函数有带参数与不带参数两种形式,不带参数情况如下: function myFunction() { alert('HelloWorld!') } 在这种类型的函数中,输出值是确定的 ...
- Android实现Http协议案例
在Android开发中,使用Http协议实现网络之间的通信是随处可见的,使用http方式主要采用2中请求方式即get和post两种方式. 一.使用get方式: HttpGet httpGet = ne ...
- android网络编程
android的网络编程分为2种:基于socket的,和基于http协议的. 基于socket的用法 服务器端: 先启动一个服务器端的socket ServerSocket svr = new ...
- cookie+session,会话时间设定
很多Web程序中第一次登录后,在一定时间内(如2个小时)再次访问同一个Web程序时就无需再次登录,而是直接进入程序的主界面(仅限于本机). 实现这个功能关键就是服务端要识别客户的身份.而用Cookie ...
- 阿里云服务器 ECS Ubuntu系统安装配置
1. 登陆服务器 系统开通成功后手机会收到阿里云发来的短信,包含公网IP及root登录密码. WEB管理后台方式 可通过阿里云管理后台选择“连接管理终端…”进行登录 提示输入VNC密码 登录成功后显示 ...
- git 合并本地代码到分支
本地代码合并到dev分支 在local分支提交git add .git commit -m "playbuy" 切换到dev分支git checkout devgit pull合并 ...
- CSS各个浏览器Hack的写法
Hack是针对不同的浏览器去写不同的CSS样式,从而让各浏览器能达到一致的渲染效果,那么针对不同的浏览器写不同的CSS CODE的过程,就叫CSS HACK,同时也叫写CSS Hack.然后将Hack ...
- Python中docstring文档的写法
该写法根据Python的PEP 257文档总结. 类的函数称为方法(method),模块里的函数称为函数(function) 每一个包,模块,类,函数,方法都应该包含文档,包括类的__init__方法 ...
- Nginx+uWSGI+Django原理
Python的Web开发中,如果使用Django框架,那么较为成熟稳定的服务器架构一般是Nginx+uWSGI+Django.而为什么一定要三个结合在一起呢?直接使用Django的runserver来 ...