欢迎光临
我们一直在努力

PE文件之导出表

PE文件之导出表

代码重用机制提供了重用代码的动态链接库,它会向调用者说明库里的哪些函数是可以被别人使用的,这些用来说明的信息便组成了导出表。EXE文件中很少有导出表,大部分的DLL文件中都有导出表。

导出表的存在可以让程序的开发者很容易清楚PE中到底有多少可以使用的函数,但如果没有函数使用说明,开发者也只能通过名称、反汇编代码或者运行结果对函数的调用方式、函数的功能等进行猜测。

Windows装载器在进行PE装载时,会将导入表中登记的所有DLL一并装入,然后根据DLL的导出表中对导入函数的描述修正导入表的IAT值。

综上所述,导出表的作用有两个:

  • 通过导出表分析不认识的动态链接库文件所提供的功能。
  • 向调用者提供函数在模块中的起始地址。
  • DLL示例

    下面给出我们自己编写的DLL源代码:

    #define HOOKDLL_H __declspec(dllexport)

    #include <iostream>
    #include "HookDll.h"

    #pragma comment(lib, "kernel32.lib")
    #pragma comment(lib, "user32.lib")

    HHOOK hHook;
    HINSTANCE hInstance;

    BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
    {
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
    std::cout << hinstDll << std::endl;
    hInstance = hinstDll;
    break;
    case DLL_THREAD_ATTACH:
    break;
    case DLL_THREAD_DETACH:
    break;
    case DLL_PROCESS_DETACH:
    break;
    default:
    break;
    }

    return TRUE;
    }

    LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    if (nCode < 0)
    return CallNextHookEx(hHook, nCode, wParam, lParam);

    switch (wParam)
    {
    case WM_LBUTTONDOWN:
    MessageBox(NULL, TEXT("HookProc: WM_LBUTTONDOWN"), TEXT("HookProc"), MB_OK);
    break;
    }

    return CallNextHookEx(hHook, nCode, wParam, lParam);
    }

    extern "C" HOOKDLL_H BOOL InstallHook()
    {
    HWND hWnd = FindWindow(NULL, TEXT("HookTest"));
    if (hWnd == NULL)
    {
    std::cout << "Can't find the window" << std::endl;
    return FALSE;
    }
    DWORD dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
    std::cout << dwThreadId << std::endl;
    hHook = SetWindowsHookEx(WH_MOUSE, HookProc, hInstance, dwThreadId);
    if (hHook == NULL)
    {
    std::cout << "can't set hook" << std::endl;
    return FALSE;
    }

    return TRUE;
    }

    extern "C" HOOKDLL_H BOOL UninstallHook()
    {
    if (hHook)
    {
    UnhookWindowsHookEx(hHook);
    hHook = NULL;
    }

    return TRUE;
    }

    我们编译并链接生成可执行文件:HookDll.dll

    导出表数据结构

    导出表数据为数据目录中注册的数据类型之一,其描述信息处于数据目录的第1个目录项中。

    在这里插入图片描述

    第一项是一个FOA,指向了导出表描述符。下面两张图片分别展示了导出表描述符的hex和数据组织结构:

    在这里插入图片描述

    在这里插入图片描述

    导入表的IMAGE_IMPORT_DESCRIPTOR个数与调用的动态链接库个数相等,而导出表的IMAGE_EXPORT_DIRECTORY只有一个。

    以下是导出表描述符各字段的详细解释:

    偏移字段名大小描述
    0x00 Characteristics 4字节 保留字段,通常设置为0。在早期的Windows版本中可能有特定含义,但现在不再使用。
    0x04 TimeDateStamp 4字节 导出表创建的时间戳,表示自1970年1月1日以来的秒数。通常由链接器生成,但也可以为0。反调试工具可能修改此值。
    0x08 MajorVersion 2字节 主版本号,通常为0。这是一个可选的版本信息字段,大多数编译器将其设置为0。
    0x0A MinorVersion 2字节 次版本号,通常为0。同主版本号一样,通常为0,除非明确指定版本信息。
    0x0C Name 4字节 模块名称的RVA(相对虚拟地址)。指向一个以null结尾的ASCII字符串,包含本模块的名称。这是其他模块通过GetModuleHandle或LoadLibrary加载模块时使用的名称。
    0x10 Base 4字节 导出函数的起始序号。导出表中的函数序号是相对值,实际序号=Base+索引。例如,如果Base=1,则第一个导出函数的序号为1。这个值通常为1,但可以是其他值。
    0x14 NumberOfFunctions 4字节 导出函数的数量。即EAT(Export Address Table,导出地址表)中的条目数。这个值决定了有多少函数可以导出。
    0x18 NumberOfNames 4字节 按名称导出的函数数量。即ENT(Export Name Table,导出名称表)中的条目数。注意:只有通过名称导出的函数才会计入此数,通过序号导出的函数不包括在内。
    0x1C AddressOfFunctions 4字节 导出地址表的RVA。指向一个DWORD数组,数组的每个元素是导出函数的RVA。数组的大小由NumberOfFunctions指定。如果RVA指向模块内部,则为函数地址;如果指向导出表内部,则可能是一个转发器(forwarder)。
    0x20 AddressOfNames 4字节 导出名称表的RVA。指向一个DWORD数组,数组的每个元素是一个函数名称字符串的RVA。数组的大小由NumberOfNames指定。这些名称按字母顺序排序,以支持二分查找。
    0x24 AddressOfNameOrdinals 4字节 导出序号表的RVA。指向一个WORD数组,数组的每个元素是对应函数在EAT中的索引。数组的大小同样由NumberOfNames指定。这个表与AddressOfNames一一对应,用于建立函数名称到EAT索引的映射。

    在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » PE文件之导出表
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址