二进制入门学习笔记-8.导入表

1.导入表

1.0 什么是导入表?

告诉系统你需要用到哪些dll,用哪些函数。

1.1 导入表定位

1.可选PE头中_IMAGE_DATA_DIRECTORY DataDirectory[16]的第二个目录项指向导入表开始的地址。

typedef struct _IMAGE_DATA_DIRECTORY {                          
    DWORD   VirtualAddress;                 //RVA 指向导入表结构       
    DWORD   Size;                           
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;                         

2.通过上述地址中的RVA计算出FOA,找到真正的导入表开头,调用多个dll就会有如下多个_IMAGE_IMPORT_DESCRIPTOR结构。

typedef struct _IMAGE_IMPORT_DESCRIPTOR {                               
    union {                             
        DWORD   Characteristics;                                        
        DWORD   OriginalFirstThunk;                                         
    };                              
    DWORD   TimeDateStamp;                                              
    DWORD   ForwarderChain;                                             
    DWORD   Name;                               
    DWORD   FirstThunk;                                                 
} IMAGE_IMPORT_DESCRIPTOR;                              
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;                                

......                              

typedef struct _IMAGE_IMPORT_DESCRIPTOR {                               
    union {                             
        DWORD   Characteristics;                                        
        DWORD   OriginalFirstThunk;                                         
    };                              
    DWORD   TimeDateStamp;                                              
    DWORD   ForwarderChain;                                             
    DWORD   Name;                               
    DWORD   FirstThunk;                                                 
} IMAGE_IMPORT_DESCRIPTOR;                              
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;                                


sizeOf(IMAGE_IMPORT_DESCRIPTOR)  0  代表导入表结束                                

3.每一个上述结构体中的OriginalFirstThunkFirstThunk分别指向其对应的INT表IAT表

1.2 导入表的基本结构

typedef struct _IMAGE_IMPORT_DESCRIPTOR {                                   
    union {                                 
        DWORD   Characteristics;                                            
        DWORD   OriginalFirstThunk; //RVA,指向IMAGE_THUNK_DATA结构数组(INT表,导入名称表)            
    };                                  
    DWORD   TimeDateStamp; //时间戳            
    DWORD   ForwarderChain;                             
    DWORD   Name; //RVA,指向dll名字,该名字已0结尾         
    DWORD   FirstThunk; //RVA,指向IMAGE_THUNK_DATA结构数组(IAT导入地址表)          
} IMAGE_IMPORT_DESCRIPTOR;                                  
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;                                    

1.2.1 PE文件加载前的导入表

在PE文件加载之前,其实INT表和IAT表当中存储的值是完全相同的(未导入绑定表的情况下),存储的内容都是RVA或者是一个序号。

1.2.2 PE文件加载后的导入表

加载之后IAT表当中存储函数的地址。

1.2.3 加载过程中IAT表修正过程

遍历INT表,判断INT表结构中IMAGE_THUNK_DATA32(DWORD)中的首位是否为1,首位为1则表示是一个序号,首位不是1表示一个用来保存函数名称的结构的RVA地址,该结构如下:

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;//两个字节,是一个没用的序号,可能被编译器全改成0。
    BYTE    Name[1];//由于名称长度不一定,所以声明只写了1个字节,其实读到0才是一个Name的结束。
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; 

至此,便可以根据dll的名称和函数的名称或序号去修复对应导入表中的IAT表中的内容。

1.3 绑定导入表

1.3.0 什么是绑定导入表?

在程序中提前将导入表中IAT表的值写死,那么导入表绑定的时间存储在绑定导入表中。

1.3.1 提前绑定导入表利弊

  • 利:经常需要调用的某些exe,如系统自带的notepad.exe程序,为了保证启动速度更快,运行时不需要一个个dll去查询函数地址因此对函数地址进行提前绑定。

  • 弊:如果所调用dll没能占住所确定地址则IAT表中固定的地址无效需要重新修正。

1.3.2 系统如何判断是否绑定了导入表?

导入表中TimeDateStamp字段如果为0xFFFFFFFF则进行了绑定导入表,如果这个值为0则没有提前绑定导入表,此时真正的绑定时间存储在IMAGE_BOUND_IMPORT_DESCRIPTORTimeDateStamp中。

1.3.3 绑定导入表定位

  • 1.数据目录的第12项存储着绑定导入表的RVA。
  • 2.RVA转FOA获得绑定导入表的真实位置,指向如下struct开头地址。
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {                     
    DWORD   TimeDateStamp;//所绑定dll的时间戳,用来和所绑定的dll对比看dll是否更新了。
    WORD    OffsetModuleName;//dll的名称,第一个_IMAGE_BOUND_IMPORT_DESCRIPTOR的值+OffsetModuleName才是真正的dll名称的RVA。
    WORD    NumberOfModuleForwarderRefs;//该dll所依赖的dll数量
} IMAGE_BOUND_IMPORT_DESCRIPTOR,  *PIMAGE_BOUND_IMPORT_DESCRIPTOR;                      
  • 3.如果所依赖的dll数量为n,则紧接着这个结构会跟着n个如下结构用于说明所依赖的dll:
typedef struct _IMAGE_BOUND_FORWARDER_REF {                     
    DWORD   TimeDateStamp;//该dll的时间戳,用来和所绑定的dll对比看dll是否更新了。
    WORD    OffsetModuleName;//dll的名称
    WORD    Reserved;//保留字段
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;                       
  • 4.紧接着为一个新的IMAGE_BOUND_IMPORT_DESCRIPTOR的内容或者是一个全0的IMAGE_BOUND_IMPORT_DESCRIPTOR表示绑定导入表结束。

示意图如下图所示:

1.3.4 重新修改IAT表的情况

  • 1.绑定导入表中的TimeDateStamp和对应dll的TimeDateStamp不一致。
  • 2 .没有成功占住预定地址,dll需要重定位。