PE结构学习(5)_导入表与导出表

导出表与导入表

通常来讲exe文件只有导入表而没有导出表,而dll文件既有导入表也有导出表

导出表

什么是导出表

代码重用机制提供了重用代码的动态链接库, 它会向调用者说明库里的哪些函数是可以被别人使用的, 而这些说明的信息便组成了导出表

简单来说,PE文件提供一些函数给其他PE文件调用,这些函数都记录在导出表里面


导出表结构体代码

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;  //时间戳
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;   //指向导出表文件名字符串
    DWORD   Base;   //导出函数起始序号
    DWORD   NumberOfFunctions;  //所有导出函数的个数
    DWORD   NumberOfNames;  //以函数名字导出的函数的个数
    DWORD   AddressOfFunctions;     // 导出函数地址表的RVA
    DWORD   AddressOfNames;         // 导出函数名称表的RVA
    DWORD   AddressOfNameOrdinals;  // 导出函数序号表的RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

定位导出表的地址

导出表的地址和大小是由PE扩展头的最后一个成员NumberOfRvaAndSizes所决定的,这个成员是个结构体数组,结构体类型为_IMAGE_DATA_DIRECTORY

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;  //导出表的起始地址
    DWORD   Size;  //导出表的大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;	

将程序拖入CFFE工具查看,特别要注意的是这个导出表的地址是RVA,需要转换的FOA才能在文件状态下寻找到导出表的起始地址

请添加图片描述


这里就不转换成FOA了,工具已经为我们转换好了。切换至输出目录, 即导入表

Characteristics成员的偏移值就是导出表的FOA, 此处导出表的起始地址为1BD20

请添加图片描述


导出表里的三张表

导出表里有三张表,分别是导出函数地址表导出函数名称表导出函数序号表

这三张表有着紧密的联系,如下图所示,导出表有三个导出函数,分别是Add、Sub、Div

若某个PE文件想调用这个导出表的Add函数,会通过查询名称表找到对应的下标;随后通过下标在序号表中找到对应的序号;再通过序号来对应地址表里的下标,便可查询到函数的地址

请添加图片描述


将程序拖入CFFE工具里,这三张表之间的对应关系工具已经为我们排列好了

请添加图片描述

导入表

什么是导入表

任何PE文件还会调用哪些PE文件都会记录在导入表里。

因为PE文件可能要依赖多个模块,所以通常一个PE文件会有多张导入表。


导入表涉及的结构体代码

//导入表的结构体代码
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;         
    } DUMMYUNIONNAME;  //RVA,指向INT表(导入函数地址表),是个IMAGE_THUNK_DATA结构体数组
    DWORD   TimeDateStamp;   //时间戳                                                        
    DWORD   ForwarderChain;                 
    DWORD   Name;    //RVA,指向所依赖的dll(模块)的名字
    DWORD   FirstThunk;  //指向IAT(导入函数地址表)的指针,也是个IMAGE_THUNK_DATA结构体数组           
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // RVA,指向IMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;   //可为空,若不为空则为函数在导出表的索引
    CHAR   Name[1];  //函数名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

确定导入表的函数

结合上述导入表的结构体成员,下图我只列举三个重要的成员,分别是OriginalFirstThunk、Name、FirstThunk

OriginalFirstThunk指向了INT(import Name Table)表, 里面存放着IMAGE_THUNK_DATA结构的成员

IMAGE_THUNK_DATA结构里的最后一个成员AddressOfData指向IMAGE_IMPORT_BY_NAME

IMAGE_IMPORT_BY_NAME结构存放函数的名称,由此可确定导入表里所用到的函数

请添加图片描述


确定导入表的函数地址

如下图所示,call的是一个内存地址而不是绝对地址,这样的call称为间接调用,意思是间接调用其他dll里的messagebox函数

请添加图片描述


下图是PE加载后的导入表结构图, PE文件加载后IAT表不再存放IMAGE_THUNK_DATA, 而是存放函数的地址

请添加图片描述


若程序的导入地址表被加壳工具给加密了从而导致函数地址表损坏,则可以通过INT表查询到函数的名称,随后调用GetProcessAddress获取到函数地址,随后再将函数地址写入IAT表,这种技术常用于加壳与脱壳


用PE工具查看

使用CFEE工具打开dll文件,切换到导入目录,这里记录着所有导入表以及其属性

请添加图片描述

单击其中一个模块, 即可查询此模块提供的函数名以及地址

请添加图片描述

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值