欢迎光临
我们一直在努力

​​​​PE结构 ---> 10.什么是导出表

目录

什么是导出表

导出表的关键点

函数地址表

当序号不是从0开始

Base基序号

查序号太麻烦 ===>函数名称

导出表遍历

导出表 作用与总体布局

IMAGE_EXPORT_DIRECTORY 字段逐项细化

三张关键的表

   AddressOfFunctions(RVA 到 EAT)函数地址表

  AddressOfNames(RVA 到名字指针数组)名称表

  AddressOfNameOrdinals(RVA 到序号表) 序号表

结构体


什么是导出表

  • 也就是指的是DLL 函数模块等等

  • DLL导出函数

  • Dll函数调用 也就是通过__declspec(dllexport)

  • 在通过LoadLiabry 加载进这个DLL模块

  • 在通过GetProcAddress 拿到函数的地址

  • 最后进行一个加载这样就拿到了函数地址

  • 学会了导出表以后就是可以去实现 GetProcAddress 这个函数

  • 提供模块导出函数

  • 支持动态链接库 运行其他程序使用LoadLiabry GetProcAddress的导出函数地址

  • 可以支持序号 可以支持名字

导出表的关键点

  • 序号表 地址表 名称表 [ Base基序号] ====>>>> 三表的连接如何连接

  • 早期只有序号表=函数地址表

    • 也就是早期windows 导出表(IAT)只有2个 一个是序号 一个函数地址

    • 这样就可以实现一一对应

函数地址表
  • 但是C语言的特性我们就有Index

    • index和函数地址一一对应 这样就不需要函数表了

    • 也就是inedex 就可以当序号表和函数地址表

当序号不是从0开始
  • 那么使用index就对应不到了

  • 也就是导出的函数序号不是从1开始

  • 假设导出的序号是0x1001 1002 1003 这样前面0x1000就是浪费了

Base基序号
  • 也就是我们可以在导出函数的时候自定义序号就不对了

  • 也就是我们直接从0开始 但是这个0并不是函数的序号 也就是在导出序号不是从0开始就会出现这个情况

  • 也就是我们导出的序号windows把最小值当成一个基序号比如说就是0x1001

  • 也就是说base基地址=0x1001 – 0x1001 = 0 base基地址=0x1001 – 0x1002 = 1 这样的话我们就可以正常申请空间不浪费空间了。

  • 最后序号表也就是 base基地址=0x1001 – 0x1001 = 拿到序号 ==> 去函数地址表找到对应序号 拿到函数地址表

  • 这样的数组下表一样是从012345开始

  • 也就是说我们查找函数地址的时候 GetProcAddress("1001") 的时候就不会像原来的函数地址表函数【函数地址表【1】这样进行查找了】这个时候需要加上base基地址(0x10001 = 函数地址表【1+ base基地址序号】

  • 最后也就是 函数地址表【序号 – base基序号】

  • 函数地址表【序1002- 1001】 = 序号1

查序号太麻烦 ===>函数名称
  • 序号和地址一一对应

  • 函数名称和地址是一一对应吗 ===> 答案肯定不是的

  • 一个函数的地址 可能有多个函数的名称

    • 函数的地址和函数的名称是一对多的关系

    • 也就是一个函数地址可以有多个函数名称

    • CreateFileEx CreateFile2 也就是不同版本 但是指向地址是一样的

    • 函数的名称是多个 地址是唯一的

hModule (DLL基址)
↓
解析 DOS Header → NT Header → Export Directory RVA
↓
三大表:
├── AddressOfNames → 名称字符串数组
├── AddressOfNameOrdinals → 名称索引 → 序号
└── AddressOfFunctions → 序号 → 函数RVA
↓
匹配名称/序号 → 取对应RVA
↓
返回:hModule + RVA(函数真实地址)

导出表遍历

  • 导入表(Import Directory)告诉加载器这个模块需要哪些外部 DLL 及哪些符号;

  • 导出表(Export Directory)列出模块向外暴露的函数/变量和序号(ordinal),以供其他模块链接或运行时解析

导出表 作用与总体布局
  • 导出表(Export Directory)用于列出该模块对外提供的入口(函数/变量),它包含 IMAGE_EXPORT_DIRECTORY 结构及三张并行数组:

    • Export Address Table(EAT)

    • Export Name Pointer Table(指向名字字符串的指针数组)

    • Export Ordinal Table(名字对应的序号)

  • 调用者可以通过名称或序号解析导出地址;

  • 此外还有“转发导出”(forwarded export)机制:导出的条目可以指向另一个 DLL 的符号而不是实际地址。

IMAGE_EXPORT_DIRECTORY 字段逐项细化
  • Name(RVA) 模块自己的名称字符串(通常 DLL 名)。Microsoft Learn

  • Base(DWORD)

    • 导出序号的基值(ordinal base)。导

    • 出序号 = Base + index(index 为 EAT 的索引)。

    • 这允许序号不必从 0 开始。

  • NumberOfFunctions / NumberOfNames(DWORD)

    • 分别表示 EAT 长度(全部导出的地址,包括按序号导出但无名字的)

    • 按名字导出的数量(名字数组长度)

    • 二者不同步时说明有按序号导出但无名字的项。

  =====——->

  • 变量和函数的区分 判断地址落在哪个节区里面就可以区分出来

  • 函数转发

  • 也就是函数名称是add = kernel32.Message 这样就是函数转发

  • 判断范围在不在 导出表范围内 如果在则说明转发

  • 三张关键的表

       AddressOfFunctions(RVA 到 EAT)函数地址表
    • 指向 Export Address Table(每项是 RVA 指向函数代码或转发字符串)

    • 调用者通过 EAT[index] 获取 RVA,然后加上映像基址得到实际函数地址。

      AddressOfNames(RVA 到名字指针数组)名称表
    • 指向一个数组,数组每项是指向导出名字符串的 RVA。

    • 与 AddressOfNameOrdinals 并行用于基于名字的查找。

      AddressOfNameOrdinals(RVA 到序号表) 序号表
    • 这是一个 WORD 数组,长度等于 NumberOfNames,存储每个名字在 EAT 中的索引(index);

    • 用来把名字映射到导出表中的地址。

    结构体

    _IMAGE_EXPORT_DIRECTORY 

    typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD Characteristics; // 保留
    DWORD TimeDateStamp; // 时间戳
    WORD MajorVersion; // 版本
    WORD MinorVersion;
    DWORD Name; // DLL 名 RVA
    DWORD Base; // 起始序数
    DWORD NumberOfFunctions; // 函数数
    DWORD NumberOfNames; // 名称数
    DWORD AddressOfFunctions; // 函数地址表 RVA
    DWORD AddressOfNames; // 名称表 RVA
    DWORD AddressOfNameOrdinals;// 序数表 RVA
    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


    赞(0)
    未经允许不得转载:171主机测评 » ​​​​PE结构 ---> 10.什么是导出表
    分享到: 更多 (0)

    评论 抢沙发

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