《Windows PE》5.1 导出表

导出表(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

//入口和退出点

int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)

{

    return TRUE;

}

//私有函数:本函数在dll内部使用

DWORD TopXY(DWORD wDim,DWORD sDim)

{

    return (sDim / 2 - wDim / 2);

}

//窗口抖动进入效果

EXPORT  void AnimateOpen(HWND hWin)

{

    RECT rect;

    DWORD Xsize, Ysize,Xplace,Yplace;

    int sWth,sHth,counts;

    GetWindowRect(hWin, &rect);

    Xsize = X_START_SIZE;

    Ysize = Y_START_SIZE;

    sWth = GetSystemMetrics(SM_CXSCREEN);

    Xplace = TopXY(Xsize, sWth);

    sHth = GetSystemMetrics(SM_CYSCREEN);

    Yplace = TopXY(Ysize, sHth);

    counts = MAX_XYSTEPS;

    while (counts--)

    {

        MoveWindow(hWin, Xplace, Yplace, Xsize, Ysize, FALSE);

        ShowWindow(hWin, SW_SHOWNA);

        Sleep(DELAY_VALUE);

        ShowWindow(hWin, SW_HIDE);

        Xsize += X_STEP_SIZE;

        Ysize += Y_STEP_SIZE;

        Xplace = TopXY(Xsize, sWth);

        Yplace = TopXY(Ysize, sHth);

    }

    Xsize = rect.right - rect.left;

    Ysize = rect.bottom - rect.top;

    Xplace = TopXY(Xsize,sWth);

    Yplace = TopXY(Ysize,sHth);

    MoveWindow(hWin,Xplace,Yplace,Xsize,Ysize,TRUE);

    ShowWindow(hWin, SW_SHOW);

}

//窗口抖动退出效果

EXPORT void AnimateClose(HWND hWin)

{

    RECT rect;

    DWORD Xsize, Ysize, Xplace, Yplace;

    int sWth, sHth, counts;

    ShowWindow(hWin, SW_HIDE);

    GetWindowRect(hWin, &rect);

    Xsize = rect.right - rect.left;

    Ysize = rect.bottom - rect.top;

    sWth = GetSystemMetrics(SM_CXSCREEN);

    Xplace = TopXY(Xsize, sWth);

    sHth = GetSystemMetrics(SM_CYSCREEN);

    Yplace = TopXY(Ysize, sHth);

    counts = MAX_XYSTEPS;

    while (counts--)

    {

        MoveWindow(hWin, Xplace, Yplace, Xsize, Ysize, FALSE);

        ShowWindow(hWin, SW_SHOWNA);

        Sleep(DELAY_VALUE);

        ShowWindow(hWin, SW_HIDE);

        Xsize -= X_STEP_SIZE;

        Ysize -= Y_STEP_SIZE;

        Xplace = TopXY(Xsize, sWth);

        Yplace = TopXY(Ysize, sHth);

    }

}

//窗口淡入效果,仅运行在2000/XP以上操作系统

EXPORT void FadeInOpen(HWND hWin)

{

    const TCHAR User32[] = TEXT("user32.dll");

    const CHAR SLWA[] = "SetLayeredWindowAttributes";//函数名使用ASCII码字符

    FARPROC pSLWA;

    int Value = 90;

    LONG stl = GetWindowLong(hWin, GWL_EXSTYLE);//检索扩展的窗口样式

    // 增加“分层窗口”样式

    stl |= WS_EX_LAYERED;  

    SetWindowLong(hWin,GWL_EXSTYLE,stl);

    //获取SetLayeredWindowAttributes在user32.dll中的地址

    pSLWA = GetProcAddress(GetModuleHandle(User32), (LPCSTR)SLWA);

    //设置分层窗口的不透明度和透明度颜色键

    pSLWA(hWin,0,0,LMA_ALPHA);

    ShowWindow(hWin, SW_SHOWNA);

    while (Value != 255)

    {

        pSLWA(hWin,Value,Value,LMA_COLORKEY + LMA_ALPHA);

        Sleep(DELAY_VALUE);

        Value += 15;

    }

    pSLWA(hWin,0,255,LMA_ALPHA);

}

//窗口淡出效果,仅运行在2000/XP以上操作系统

EXPORT void FadeOutClose(HWND hWin)

{

    const TCHAR User32[] = TEXT("user32.dll");

    const CHAR SLWA[] = "SetLayeredWindowAttributes";

    FARPROC pSLWA;

    int Value = 255;

    LONG stl = GetWindowLong(hWin, GWL_EXSTYLE);//检索扩展的窗口样式

    // 增加“分层窗口”样式

    stl |= WS_EX_LAYERED;

    SetWindowLong(hWin, GWL_EXSTYLE, stl);

    //获取SetLayeredWindowAttributes在user32.dll中的地址

    pSLWA = GetProcAddress(GetModuleHandle(User32), (LPCSTR)SLWA);

    //设置分层窗口的不透明度和透明度颜色键

    pSLWA(hWin, 0,255, LMA_ALPHA);

    while (Value != 0)

    {

        pSLWA(hWin, Value, Value, LMA_COLORKEY + LMA_ALPHA);

        Sleep(DELAY_VALUE);

        Value -= 15;

    }

}

FirstWindow.c

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

 FileName:FirstWindow.c

 实验33:执行文件

 ;功能:简单窗口程序

;具有窗口的大部分基本特性,其中显示和退出使用了渐入和渐出效果

;该程序主要演示自己制作的dll的函数调用

 (c) bcdaren, 2024

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

#include <windows.h>

#include "resource.h"

#include "winResult.h"

#pragma comment(lib,"winResult.lib")

HWND hWinMain; //窗口句柄

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

    LPSTR lpCmdLine, int nShowCmd)//默认填写

{

    const TCHAR szClassName[] = TEXT("MyClass");

    const TCHAR szCaptionMain[] = TEXT("窗口特效演示");

    MSG msg;      //MSG 结构变量

    WNDCLASSEX wndclass; //WNDCLASSEX结构变量

    wndclass.style = CS_HREDRAW | CS_VREDRAW;//重叠窗口

    wndclass.lpfnWndProc = WndProc;//回调函数指针

    wndclass.cbClsExtra = 0;

    wndclass.cbWndExtra = 0;

    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//窗口背景色

    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);

    wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));

    wndclass.lpszMenuName = NULL;//菜单

    wndclass.hInstance = hInstance;//实例句柄

    wndclass.cbSize = sizeof(WNDCLASSEX);

    wndclass.hIconSm = NULL;

    wndclass.lpszClassName = szClassName;

    if (!RegisterClassEx(&wndclass))

    {

        MessageBox(0, TEXT("This program requires Windows NT!"),

            szClassName, MB_ICONERROR);

        return 0;

    }

    hWinMain = CreateWindowEx(WS_EX_CLIENTEDGE,

        szClassName,//窗口类名

        szCaptionMain,//窗口标题

        WS_OVERLAPPEDWINDOW,     //窗口样式

        GetSystemMetrics(SM_CXSCREEN) / 4,

        GetSystemMetrics(SM_CYSCREEN) / 4,

        GetSystemMetrics(SM_CXSCREEN) / 2,

        GetSystemMetrics(SM_CYSCREEN) / 2,

        NULL, // 父窗口句柄

        NULL, // 窗口菜单句柄

        hInstance, // 程序实例句柄

        NULL); // 创建参数

    //ShowWindow(hWinMain, nShowCmd); //显示窗口

    FadeInOpen(hWinMain);//外部链接库函数

    UpdateWindow(hWinMain);

    while (GetMessage(&msg, NULL, 0, 0))

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

    return msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)

{

    HDC hdc;

    PAINTSTRUCT ps;

    RECT rect;

    const TCHAR szText[] = TEXT("Welocome to 编程达人!");

    switch (message)

    {

    case WM_PAINT:

        hdc = BeginPaint(hwnd, &ps);

        GetClientRect(hwnd,&rect);

        DrawText(hdc,szText,-1,&rect,DT_SINGLELINE | DT_CENTER |DT_VCENTER);

        EndPaint(hwnd, &ps);

        return 0;

    case WM_CLOSE:

        FadeOutClose(hwnd);//外部链接库函数

        DestroyWindow(hwnd);

        return 0;

    case WM_DESTROY:

        PostQuitMessage(0);

        return 0;

    }

    return DefWindowProc(hwnd, message, wparam, lparam);

}

运行

图5-2 FirstWindow窗口程序

       ●winResult.dll中导出表的定位:

    数据目录项:

00000170   10 25 00 00 90 00 00 00  A0 25 00 00 64 00 00 00   .%......?..d...

00000180   00 40 00 00 F8 00 00 00  00 00 00 00 00 00 00 00   .@..?..........

00000190   00 00 00 00 00 00 00 00  00 50 00 00 7C 01 00 00   .........P..|...

000001A0   F0 20 00 00 70 00 00 00  00 00 00 00 00 00 00 00   ?..p...........

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

000001C0   60 21 00 00 40 00 00 00  00 00 00 00 00 00 00 00   `!..@...........

000001D0   00 20 00 00 8C 00 00 00  00 00 00 00 00 00 00 00   . ..?..........

000001E0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................

   

●导出表位于数据目录项的第0项。导出表RVA为00002510H,大小为90H。

节表:

000001F0   2E 74 65 78 74 00 00 00  A4 0F 00 00 00 10 00 00   .text...?......

00000200   00 10 00 00 00 04 00 00  00 00 00 00 00 00 00 00   ................

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

00000220   62 09 00 00 00 20 00 00  00 0A 00 00 00 14 00 00   b.... ..........

00000230   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40   ............@..@

00000240   2E 64 61 74 61 00 00 00  8C 03 00 00 00 30 00 00   .data...?...0..

00000250   00 02 00 00 00 1E 00 00  00 00 00 00 00 00 00 00   ................

00000260   00 00 00 00 40 00 00 C0  2E 72 73 72 63 00 00 00   ....@..?rsrc...

00000270   F8 00 00 00 00 40 00 00  00 02 00 00 00 20 00 00   ?...@....... ..

00000280   00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40   ............@..@

00000290   2E 72 65 6C 6F 63 00 00  7C 01 00 00 00 50 00 00   .reloc..|....P..

000002A0   00 02 00 00 00 22 00 00  00 00 00 00 00 00 00 00   ....."..........

000002B0   00 00 00 00 40 00 00 42  00 00 00 00 00 00 00 00   ....@..B........

    ●导出表位于.rdata节区,FOA地址为:2510H-2000H+1400H=1910H。

    导出表描述符:

00001910   00 00 00 00 FF FF FF FF  00 00 00 00 60 25 00 00   ........`%..

00001920   01 00 00 00 04 00 00 00  04 00 00 00 38 25 00 00   ............8%..

00001930   48 25 00 00 58 25 00 00  F0 10 00 00 10 10 00 00   H%..X%..?......

00001940   C0 11 00 00 A0 12 00 00  6E 25 00 00 7B 25 00 00   ?..?..n%..{%..

00001950   87 25 00 00 92 25 00 00  00 00 01 00 02 00 03 00   ?..?..........

00001960   77 69 6E 52 65 73 75 6C  74 2E 64 6C 6C 00 41 6E   winResult.dll.An

00001970   69 6D 61 74 65 43 6C 6F  73 65 00 41 6E 69 6D 61   imateClose.Anima

00001980   74 65 4F 70 65 6E 00 46  61 64 65 49 6E 4F 70 65   teOpen.FadeInOpe

00001990   6E 00 46 61 64 65 4F 75  74 43 6C 6F 73 65 00 00   n.FadeOutClose..

typedef struct _IMAGE_EXPORT_DIRECTORY {

    DWORD   Characteristics;               // 00 00 00 00

    DWORD   TimeDateStamp;              // FF FF FF FF

    WORD    MajorVersion;               // 00 00

    WORD    MinorVersion;               // 00 00

    DWORD   Name;                       // 模块名称的RVA:00002560H

    DWORD   Base;                     // 导出函数序号的基准值:01

    DWORD   NumberOfFunctions;          // 导出函数的数量:04

    DWORD   NumberOfNames;             // 导出函数名称的数量:04

    DWORD   AddressOfFunctions;         // 导出函数地址表的RVA:00002538H

    DWORD   AddressOfNames;              // 导出函数名称表的RVA:00002548H

    DWORD   AddressOfNameOrdinals;      // 导出函数序号表的RVA:00002558H

} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

●导出函数地址表的RVA:00002538H对应的FOA地址为00001938。

F0 10 00 00 10 10 00 00 C0 11 00 00 A0 12 00 00

●导出函数名称表的RVA:00002548H对应的FOA地址为00001948。

6E 25 00 00 7B 25 00 00 87 25 00 00 92 25 00 00

0000196E:AnimateClose

0000197B:AnimateOpen

00001987:FadeInOpen

00001992:FadeOutClose

●导出函数序号表的RVA:00002558H对应的FOA地址为00001958。

00 00 01 00 02 00 03 00

●导出函数地址验证

第一步:将FirstWindow.exe拖入OD调试器,找到FadwInOpen函数的调用,F2下断点,如图5-3所示:

图5-3 FadwInOpen函数的调用

第二步:按下F7单步步入,进入FadwInOpen函数,入口地址为0x579511C0,如图5-4所示:

图5-4 FadwInOpen函数的入口

       第三步:打开内存映射窗口,找到winResult.dll模块,模块基址为0x57950000,如图5-5所示:

图5-5winResult.dll模块

FadwInOpen函数入口地址0x579511C0 = 0x57950000(基址)+0x 000011C0(RVA)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值