PE文件格式中数据目录项中的导入表和VA-RVA-FA转换

VA

va(虚拟地址):虚拟地址是内存中的地址,每一个内存空间都有一个地址表示,这就是虚拟地址如打开OD,加载程序,里面的每一个地址都是虚拟地址,VA如下图,PE文件加载进内存的VA都是由imageBase(基址)+RVA(相对虚拟地址)形成的绝对虚拟地址VA
在这里插入图片描述

RVA

RVA(相对虚拟地址):相对虚拟地址相对的是选项头里面的字段ImageBase(程序基址),PE结构中大部分字段的存的就是RVA 举例:假如你的ImageBase是00400000 节表头中的VirtualAddress是1000 这时你的节表数据映射到内存的VA(绝对虚拟地址)就是00401000

FOA

FOA(文件偏移地址):相对于文件开始的位置偏移,PE数据存在文件中的地址就是FOA,举例:你的文件开始偏移为0 ,你的节表头字段中的PointerToRawData为400 这时你的节表数据在文件中的地址就是400这个地址,如下图表示
在这里插入图片描述
这是400地址就是你节表的数据所在的位置,节表在文件中占多大看你在文件中的对齐大小,必须是对齐值的倍数,节表在内存中占多大也看内存对齐值,也必须是对齐值的倍数,对齐值在选项头字段FileAlignment(文件对齐)和SectionAlignment(内存对齐)
在这里插入图片描述

RVA转FA

例如你的RVA=1100,这是看你的RVA属于哪个节,如果RVA>=节表的RVA&&<节表内存大小(记得算对齐值)说明属于这个节,这就说明是属于这个节了,这时候查看这个节在文件中的地址FA,这就可以定位这个RVA在文件中的FA了,如果这个节的FA位400,就可以算出你的FA=500,公式如下

  • 偏移=RVA-节的RVA
  • FOA=偏移+节的FOA 求出的FOA就是你的RVA在文件中的位置(FOA)

VA转FOA

和上面差不多,只是先算出RVA,然后就想RVA转FA

  • RVA=VA=ImageBase(注意如果你的基址不是可选头里面的建议基址,就自己用od查看一下实际基地址)
  • 偏移=RVA-节的RVA
  • FOA=偏移+节的FOA 求出的FA就是你的VA在文件中的位置(FOA)

PE文件加载(映射)进内存结构如下

其他的FA转RVA是一样的这个只要求出偏移,然后加上偏移就可以了,看下面的PE加载进内存的图结构就知道了,图是引用百度的
在这里插入图片描述

导入表

导入表的作用:记录可执行文件或者一个dll(都可以称为PE文件)所用到的其他模块的导函数
导入表记录的信息:用了哪些模块(用了哪些dll),用了dll的哪些函数

导入表结构体定义如下
//最后会以全0的结构为结束,其中每一项是一个结构,一项8个字节,
typedef struct _IMAGE_IMPORT_DESCRIPTOR 
{
    union
    {
        DWORD   Characteristics;            
        DWORD   OriginalFirstThunk; 
    };                  //指向导入函数名,如果为0,则找FirstThunk
    DWORD   TimeDateStamp;           //时间,一般不用      
    DWORD   ForwarderChain;          //链表前一个结构,一般不用            
    DWORD   Name;             // DLL名称的RVA偏移通过偏移可以找到DLL名称
    DWORD   FirstThunk;              // IAT 的RVA偏移.和originalFirstThunk不同,如果地址指向的值为 0,则直接跳过当前轮,加载下一个DLL
    
//IAT结构体类型  
typedef struct _IMAGE_THUNK_DATA32 
{
    //共用体,如果高位大于8则表示这是一个序号,小于8则表示为一个字符串地址RVA,也就是最高一位是否为1
    union 
    {
        PBYTE  ForwarderString;
        PDWORD Function;
        DWORD Ordinal;
        PIMAGE_IMPORT_BY_NAME  AddressOfData;
    } u1;
} IMAGE_THUNK_DATA32;

导入表在PE中的结构如下图在这里插入图片描述

系统加载导入表流程

  • 找到数据目录第2项,跳转到导入表起始地址
  • 遍历模块数组(系统遍历到20个字节的全0结尾,表示模块加载结束),数组每个元素结构_IMAGE_IMPORT_DESCRIPTOR
  • 通过字段OriginalFirstThunk或字段FirstThunk去IAT(导入函数地址表)或INT中获取函数名或序号,然后利用函数LoadLibrary获取模块基址,函数GetProcAddress获取函数的地址覆盖到字段FirstThunk指向的IAT中,这样循环遍历每个模块,把每个模块的导入函数都覆盖到对应模块的IAT中,可以参照上图来理解流程
  • 其实调用api的时候,如果是隐式加载用的就是导入表IAT中存的函数地址,如果是显示加载(GetProcAddress)用的就是导出表的存的函数地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值