PE文件解析-导入表

PE文件解析-导入表

调用dll文件函数的原理

程序在调用dll文件函数的时候,并不是把函数编译到当前程序中,

而是把函数的地址保存到了当前文件中

在文件当中,对应的函数地址部分存放的是函数名称

一个进程空间的exe dll文件如何被加载到内存

1 LoadLibraryA显示加载DLL文件,OS把exe调用到内存中后根据exe需要调用的dll文件,再把dll调用进内存中。

HMOULE 等于的是dll文件的IMAGEBASE,也就是首地址

2 GetProcessAddr(HMOUDLE,fun1); 就拿到dll中对应的函数地址

exe文件调用的动态链接库在内存和在硬盘中的区别

函数地址的不同,加载到内存中是一个具体的函数地址,而在硬盘中是一个函数名称,通过函数名称加载得到函数地址。

导入表

导入表和导出表所属位置相同,都是可选PE头的数据目录表里面的,但是是第二个元素

PE文件解析-导入表

 

 

但是这个东西其实是一个数组,因为导入表和输出表不一样,有导入几个dll文件,就有几个导入表。在结束的时候有一个跟该结构体一样大小的为0的结构体作为判断。

PE文件解析-导入表

 

 

 

导入表结构体

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
  union {
      DWORD   Characteristics;           // 0 for terminating null import descriptor
      DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
  } DUMMYUNIONNAME;
  DWORD   TimeDateStamp;                 // 0 if not bound,
                                          // -1 if bound, and real date\time stamp
                                          //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                          // O.W. date/time stamp of DLL bound to (Old BIND)

  DWORD   ForwarderChain;                 // -1 if no forwarders
  DWORD   Name;
  DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

结构体重点

OriginalFirstThunk

是导入表结构体的第一个字段,是一个共用体,该字段的第二个字段的意思是指向一个导入名称表(INT Import Name Table)

PE文件解析-导入表

 

 

该表是一个IMAGE_THUNK_DATA的结构体

PE文件解析-导入表

 

 

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

这个结构体只有四个字节,在不同的情况下表示是意义不一样,不仅可以是存放导入函数名称,也可以存放的是导入函数的序号。

区分导入函数如何导入

导入函数可以通过序号导入和函数名称导入,通过区分导入表结构体中DUMMYUNIONNAME的OriginalFirstThunk来判断,如果二进制位的最高位是1就按照序号导入,如果是存的名称,就是直接对应函数名称的RVA。

然后导入名称表又指向一个结构体IMAGE_IMPORT_BY_NAMEPE文件解析-导入表

 

typedef struct _IMAGE_IMPORT_BY_NAME {
  WORD   Hint; //导入函数索引
  CHAR   Name[1];//存放导入函数的名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 

DWORD TimeDateStamp

名字看着是一个时间戳,但是如果该字段为0,FirstThunk也就是IAT和INT执行的是一个表。

当为-1的时候,IAT的表存放的是真实的地址。

DWORD Name

指向dll文件名称的RVA

FirstThunk 导入地址表(IAT)的

导入名称表叫 INT(Import Name Table),导入地址表叫IAT

PE文件解析-导入表

 

 

导入地址表指向的也是一个IMAGE_THUNK_DATA32结构体

 

代码解析导入表

首先拿到可选PE头的数据目录表的第二个元素的内容。然后依据该元素指向的内容来遍历每一个导入表,再根据导入表里面的字段来遍历

 

 

void CPeUtil::GetImportTable()
{
//拿到可选PE头的数据目录表的第二个元素的内容
IMAGE_DATA_DIRECTORY directory = pOptionalHeader->DataDirectory[1];
//拿到第一个导入表
PIMAGE_IMPORT_DESCRIPTOR ImportTable = PIMAGE_IMPORT_DESCRIPTOR(RvaToFoa(directory.VirtualAddress) + FileBuff);
//循环遍历每一个导入表
while (ImportTable)
{
//输出该导入表的文件的名字
char* pName = RvaToFoa(ImportTable->Name) + FileBuff;
std::cout << pName << std::endl;
//输出导入表的函数名称表
PIMAGE_THUNK_DATA INT = PIMAGE_THUNK_DATA(RvaToFoa(ImportTable->OriginalFirstThunk)+FileBuff);
while (INT->u1.AddressOfData)//当遍历到的是最后一个是时候是会为0,所以随便遍历一个就好
{
if (INT->u1.Ordinal & 0x80000000)//利用二进制首位第一个是否为1来判断是不是按照序号导入
{
printf("函数序号为%d\n",INT->u1.Ordinal & 0x7FFFFFFF);
}
else //如果 不为1表示按照名称导入
{
printf("函数名称为%s\n", INT->u1.Function);
}
INT++;//INT在INT数组中下移
}
ImportTable++;
}
}

 

上一篇:hdu 1232 畅通工程(并查集算法)


下一篇:华硕天选2安装win10专业版记录