ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

PE文件 - 导入表

2021-11-11 14:03:33  阅读:164  来源: 互联网

标签:文件 函数 IMAGE DWORD 地址 导入 PE IAT


导入表

在数据目录的第2项,下标为1的位置,记录了可执行文件导入了哪些动态库和函数,INT(导入函数名称表)和IAT(导入函数地址表),由于一个可执行文件可能要多个PE文件支持,所以此结构可能有多个,导入表就是一个结构体数组,以一个全零元素为结尾,每一个数组的元素,代表一个PE文件的导入信息

导入表格式

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk;         
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;
    DWORD   ForwarderChain;                 
    DWORD   Name;
    DWORD   FirstThunk;                     
} IMAGE_IMPORT_DESCRIPTOR;

字段介绍

DWORD   OriginalFirstThunk;   

指向一个结构体数组的一个相对虚拟地址,结构体数组叫输入名称表(INT:import Name table)

DWORD   FirstThunk;

 指向一个结构体数组的一个相对虚拟地址,结构体数组叫输入地址表 (IAT:import address table)

注:这两个字段在PE文件未加载到内存中以前,都指向了相同类型的结构体_IMAGE_THUNK_DATA32 ,在文件加载到内存以后输入地址表(IAT)会由加载器把相应的PE文件的函数地址覆盖到这里,这时,输入地址表才是真正的函数地址
 

DWORD   Name;

导入的PE文件的名字的相对虚拟地址(RVA)

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;     
        DWORD Function;             
        DWORD Ordinal;
        DWORD AddressOfData;      
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

结构体介绍

结构体为联合体 也就是说只有四个字节

此结构体在PE文件未加载到内存的时候 IMAGE_IMPORT_DESCRIPTOR结构体的OriginalFirstThunk和FirstThunk存的数据是一样的 也就是都指向了这个结构体

字段介绍

Ordinal :加入函数时序号导入的 去掉最高位 也就是函数的序号了

(注:如何判断函数是序号导入的 通过查看Ordinal最高位 是否为1 是1去掉这个最高位的1 剩下的就是函数的序号

为什么这个字段的最高位为1就是以序号导入的?

这是因为 一个进程有4GB的虚拟内存 低两个G由用户使用 高两G为操作系统使用 也就是说一个函数最大的内存地址也就只能是 0x7fff ffff(转换成二进制最高位是0),超过这个内存就是操作系统的范围了,我们是不能使用的 所以最高位不可能为1)

 AddressOfData: 最高位不是1 这个字段指向了IMAGE_IMPORT_BY_NAME结构体

​​​​​​​typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;
    CHAR   Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

字段介绍

Hint:函数导出时序号

Name[1]:函数的名字字符数组,0结尾

文件中找导入表信息

导入表地址

 一共导入了三个库 ,最后是全0结尾

 举例找ExitProcess的地址

 这个函数在kernel32.dll中 那我们现在就找这个dll

1.4行为一组 首先看0x0000227c(注:此地址为name字段,RVA 需转换FOA)指向了 导入动态库的名字

这个库为user32.dll

接着找下一项  还是看0x000022ba(name字段 rva需转换)

动态库名称为kernel32.dll 我们要找的函数就在这个动态库里面

接着找OriginalFirstThunk( 地址0x000020f0  rva需转换)  三个IMAGE_THUNK_DATA32 结构

我们先找第一个 0x00002296的位置 首先最高位不是0 说明不是函数序号导入 所以这个rva指向了

 IMAGE_IMPORT_BY_NAME结构

 此结构为 IMAGE_IMPORT_BY_NAME 前两个字节是函数导出时的序号 后面为函数的名字 以0结尾 这个函数不是我们要找的函数继续

下一个函数名字的地址为0x2288 函数名称就是我们要找的 这个函数在函数名称表(INT)下标为1的地方 相对应的在函数地址表(IAT)下标为1的地方找函数的地址

 我们再看导入表 函数地址表(iat)的地址为0x20f0 我们转过去查看

惊奇的发现和函数名称表存储的信息一模一样 正如我们之前所说在文件未加载到内存之前 是无法得到真正的函数地址的,所以两个表是一模一样的

 

内存中找导入表信息

定位导入表 

内存中和文件中有所不同的是IAT表真正填写函数地址了

所有的rva不用转换了 直接加基地址即可

 

老规矩还是找ExitProcess 还是在kernel32.dll中,和在文件中找的方法是一样的先看name字段为0x0000227c加基地址为0x0040227c 这个库为user32.dll

 找下一个动态库 我们看name的字段地址为0x22ba 加基地址为0x004022ba

第二项导入目录表 我们看INT为0x000020f0加基地址0x004020f0

三个指向函数名的rva 我们直接找0x00002288加基地址为0x00402288 下标项为1

 我们接着找IAT表0x00002010加基地址0x00402010 跳过来以后我们把内存转为地址查看

这就是我们的IAT函数地址表 我们要找的函数就在函数地址表的下标为1的位置

windows加载导入表的流程

1.首先会检查IAT表是否为空,如果为空则循环遍历到下一个导入表

2.如果不为空则读取导入表的name字段得到dll名称,并调用lodalibaray得到动态库的模块句柄

3.接着找INT表,判断函数是否为序号导出

        (1)如果是序号导出直接调用GetProcAddress获取函数地址填写进IAT

        (2)如果名称导出获取每一个函数名称,并调用GetProcAddress函数获取函数地址

4.把函数的地址填写进IAT表

导入表的两种情况

情况1:IAT表为空:

IAT表为空,说明这个导入表是无效的,应继续循环找下一个函数导入表

情况2:INT表为空

INT表为空则读取IAT表,把IAT表所指示的位置当函数名称在获取函数地址,并填写到IAT表,覆盖这个函数名称的地址

标签:文件,函数,IMAGE,DWORD,地址,导入,PE,IAT
来源: https://blog.csdn.net/weixin_45012273/article/details/121255084

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有