导出表(Export Table)是一个在可执行文件或动态链接库(DLL)中的数据结构,用于描述该文件中导出的函数、变量和其他符号。导出表通常位于DLL动态链接库中。
本节必须掌握的知识点:
导入表数据结构
PE中的导入表
IAT函数地址表
手工重构导入表
5.1.1 导出表数据结构
■导出表由以下三个主要部分组成:
●导出地址表(Export Address Table,EAT):导出地址表是一个指向导出函数地址的指针数组。每个导出函数在导出地址表中有一个对应的指针。在运行时,当其他模块调用这个DLL中的导出函数时,将使用该表中的指针来定位和调用函数。
●导出名称表(Export Name Table,ENT):导出名称表是一个字符串数组,包含了所有导出函数的名称。每个导出函数名称在导出名称表中都有一个对应的字符串。导出地址表中的指针与导出名称表中的字符串是一一对应的。
●导出函数序号表(Export Function Ordinal Table)是PE文件中导出表的一部分,用于映射导出函数的序号和地址。导出函数序号表是一个由WORD类型的数组组成,每个元素对应一个导出函数。序号表的长度等于导出函数的数量。
导出表的结构和内容由编译器和链接器在构建可执行文件或DLL时生成。导出表允许其他模块(可执行文件、DLL或其他)通过导入表来引用和调用这些导出的函数和符号。
在Windows平台上,您可以使用一些工具来查看PE文件的导出表,例如Dependency Walker、dumpbin工具或PE浏览器等。
注意
导出表只包含被明确定义为导出的符号。如果某个符号未被明确导出,它将不会出现在导出表中。您可以使用关键字 __declspec(dllexport) 或者通过.def文件来指定哪些符号应该被导出。
■导出表数据结构
导入表描述符IMAGE_IMPORT_DESCRIPTOR的个数与调用的动态链接库个数相等,而导出表描述符IMAGE_EXPORT_DIRECTORY的个数只有一个。
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 导出表的特征标志
DWORD TimeDateStamp; // 时间戳
WORD MajorVersion; // 主版本号
WORD MinorVersion; // 次版本号
DWORD Name; // 模块名称的RVA
DWORD Base; // 导出函数序号的基准值
DWORD NumberOfFunctions; // 导出函数的数量
DWORD NumberOfNames; // 导出函数名称的数量
DWORD AddressOfFunctions; // 导出函数地址表的RVA
DWORD AddressOfNames; // 导出函数名称表的RVA
DWORD AddressOfNameOrdinals; // 导出函数序号表的RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
这个结构用于描述PE文件中的导出表。下面是对各个成员的注释:
Characteristics:导出表的特征标志,例如是否按序号导出。
TimeDateStamp:导出表的时间戳,表示导出表生成的时间。
MajorVersion 和 MinorVersion:导出表的版本号,用于指示导出表的版本信息。
Name:指向导出模块名称的指针(RVA)。
Base:导出函数的序号基准值。导出函数编号的起始值。DLL中的第一个导出函数并不是从0开始的,某导出函数的编号等于从AddressOfFunctions开始的顺序号加上这个值。
NumberOfFunctions:导出函数的数量。
NumberOfNames:导出函数名称的数量。
AddressOfFunctions:导出函数地址表的指针(RVA),包含导出函数的地址。该指针指向了全部导出函数的入口地址的起始。从入口地址开始为双字数组,数组的个数由字段IMAGE_EXPORT_DIRECTORY.NumberOfFimctions决定。导出函数的每一个地址按函数的编号顺序依次往后排开。在内存中,我们可以通过函数编号来定位某个函数的地址。
AddressOfNames:导出函数名称表的指针(RVA),包含导出函数的名称。该指针指向的位置是一连串的双字值,这些双字值均指向了对应的定义了函数名的函数的字符串地址。这一连串的双字个数为 NumberOfNames。
AddressOfNameOrdinals:导出函数序号表的指针(RVA),用于匹配导出函数的名称和地址。该值也是一个指针,与AddressOfNames是一一对应关系,所不同的是,AddressOfNames指向的是字符串的指针数组,而AddressOfNameOrdinals则指向了该函数在AddressOfFunctions中的索引值。
注意
索引值是一个字,而非双字。该值与函数编号是两个不同的概念,两者之间的关系为:
索引值=编号 - nBase
导入表描述各个字段之间的关系可以用下图来描述:
图5-1 PE导出表结构
5.1.2 定位导出表
导出表位于PE文件的数据目录第0项中,位于导入表之前。接下来我们通过一个实例具体分析导出表的结构。
首先我们要写一个包含导出表的DLL动态链接库,然后再写一个EXE执行文件调用导出函数。
实验三十三:延迟加载版本2示例
以下是一个使用延迟加载版本2的C语言示例代码:
●源代码:
winResult.h
/*
; winResult.dll 导出函数:
; 1、AnimateOpen(DWORD)
; 窗口抖动进入效果
; 2、AnimateClose(DWORD)
; 窗口抖动退出效果
; 3、FadeInOpen(DWORD)
; 窗口淡入效果,仅运行在2000/XP以上操作系统
; 4、FadeOutClose(DWORD)
; 窗口淡出效果,仅运行在2000/XP以上操作系统
; ******************************************************************** /
*/
#pragma once
#include <windows.h>
#ifdef _cplusplus //如果C++模式编译
#ifdef API_EXPORT
#define EXPORT extern "C" __declspec(dllexport)
#else
#define EXPORT extern "C" __declspec(dllimport)
#endif
#else
#ifdef API_EXPORT
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
#endif
EXPORT void AnimateOpen(HWND);
EXPORT void AnimateClose(HWND);
EXPORT void FadeInOpen(HWND);
EXPORT void FadeOutClose(HWND);
winResult.c
/*------------------------------------------------------------------------
FileName:winResult.c
实验33:一个简单的动态链接库例子
(c) bcdaren, 2024
-----------------------------------------------------------------------*/
#include <windows.h>
#define API_EXPORT
#include "winResult.h"
#define MAX_XYSTEPS 50
#define DELAY_VALUE 50 //动画效果使用的步长
#define X_STEP_SIZE 10
#define Y_STEP_SIZE 9
#define X_START_SIZE 20
#define Y_START_SIZE 10
#define LMA_ALPHA 2
#define LMA_COLORKEY 1