绑定导入表

1.关于绑定导入
 一般情况下,在程序加载前IAT表和INT表中的内容相同,都是程序引用的dll中的函数的函数名或序号;
加载完成后IAT表中将替换为函数的真正地址;
 
但在加载前IAT表中直接写绝对地址是可以实现的;
加载前在IAT表中保存绝对地址的优点:启动程序快;
    在启动程序时需要:申请4gb内存空间、贴exe、贴dll、将IAT表修复为地址等等;
    如果直接用绝对地址,则省去了修复IAT表的操作;
缺点:
    dll重定位时,如果dll没能占据自身ImageBase处的地址,则需要修复绝对地址;
    dll被修改时,dll被修改,IAT表中对应的函数地址可能被改,需要修复函数地址;
    
例如windows提供的一些程序就使用了这种方式,比如记事本;
这种方式称为“绑定导入“;
 
2.如何判断绑定导入
在导入表中结构中有个属性:TimeDateStamp;
该属性表示时间戳;
如果值为0则表示当前的dll的函数没有被绑定,在程序加载时会调用系统函数获取函数地址;
如果值为-1则表示当前的dll的函数已经绑定,而且绑定的时间存在另外一张表里;那张表就是绑定导入表;
 
3.绑定导入表
PE加载EXE相关的DLL时,首先会根据IMAGE_IMPORT_DESCRIPTOR结构中的TimeDateStamp来判断是否要重新计算IAT表中的地址。                                    
    TimeDateStamp == 0  未绑定                                    
    TimeDateStamp == -1 已绑定 真正的绑定时间为IMAGE_BOUND_IMPORT_DESCRIPTOR的TimeDateStamp      
 
绑定导入表位于数据目录的第12项;   
绑定导入表的结构:
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
    DWORD   TimeDateStamp;
    WORD    OffsetModuleName;
    WORD    NumberOfModuleForwarderRefs;    // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
} IMAGE_BOUND_IMPORT_DESCRIPTOR,  *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
TimeDateStamp    ->时间戳;用来判断是否和绑定的dll是同一个版本;也就是看时间戳和dll的pe头中的时间戳是否一样;
OffsetModuleName    ->dll的名字;注意保存的既不是RVA也不是FOA;
    dll的名字计算公式为:第一个DESCRIPTOR的值+OffsetModuleName;                
NumberOfModuleForwarderRefs    ->当前dll另外依赖的dll数量;因为dll也可能依赖dll;
 
绑定导入表结构后面紧跟的并不一定是下一个绑定导入表;
如果NumberOfModuleForwarderRefs为N则还有N个另外的结构;
该结构也是用来描述dll的;
结构如下:   
typedef struct _IMAGE_BOUND_FORWARDER_REF {
    DWORD   TimeDateStamp;
    WORD    OffsetModuleName;
    WORD    Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
前两个属性和绑定导入表意义一样;
第三个属性Reserved为保留字段没意义;
 
绑定导入表的结构图:
当最后有一个全0的结构时表示已经没有绑定导入表;
 
注意:
    当IMAGE_BOUND_IMPORT_DESCRIPTOR结构中的TimeDateStamp与DLL文件标准PE头中的TimeDateStamp值不相符时,
    或者DLL需要重新定位的时候,就会重新计算IAT中的值.                                
 
4.解析绑定导入表
有两个需要注意的地方:
    1】OffsetModuleName的计算方式:第一个DESCRIPTOR的值+OffsetModuleName   
    2】绑定导入表并不在任何一个节中,而是在头中,因此RVA转FOA的方式可能会和在节中时有所不同;
 
解析系统自带的记事本的导入表:
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"
 
#define SRC "C:\\Windows\\System32\\notepad.exe"
 
//解析导入表
void printBound(){
    //定义pe头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //数据目录指针
    PIMAGE_BOUND_IMPORT_DESCRIPTOR boundDir= NULL;    //绑定导入表指针
    PIMAGE_BOUND_FORWARDER_REF bondRef = NULL;    //绑定依赖指针
 
    //1.将文件读入内存
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!pFileBuffer){
        printf("读取dll文件失败\n");
        return;
    }
 
    //2.初始化头结构指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)pFileBuffer + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader ->DataDirectory;
 
    //绑定导入表在头中,而不再节中
    DWORD boundDirRVA = dataDir[11].VirtualAddress;
    boundDir = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + boundDirRVA);
    PIMAGE_BOUND_IMPORT_DESCRIPTOR currentBound = boundDir;
    
    //3.输出绑定导入表信息
    while(currentBound -> TimeDateStamp){
        getchar();
        LPSTR moduleName = (LPSTR) ((DWORD)(currentBound->OffsetModuleName) + (DWORD)boundDir);
        printf("\n=================%s=============\n", moduleName);
        printf("时间戳:%d\n",currentBound->TimeDateStamp );
        int i = currentBound->NumberOfModuleForwarderRefs;
        printf("依赖dll个数:%d\n", i);
        
        if(i>0){
            bondRef = (PIMAGE_BOUND_FORWARDER_REF) ((DWORD) currentBound + 8);
            printf("***********依赖dll***********\n");
            printf("依赖dll\t时间戳\n");
            for(int j=0;j<i;j++){
                LPSTR refName = LPSTR((DWORD)((bondRef + j)->OffsetModuleName) + (DWORD)boundDir);
                DWORD refTime = (bondRef + j) -> TimeDateStamp;
                printf("%s\t%d\n",refName,refTime);
            }
            currentBound = currentBound + (i+1);
        }else{
            currentBound++;
        }
    }
}
 
int main(int argc, char* argv[])
{
    //输出导入表信息
    printBound();
    getchar();
}
结果:
 
 
 

猜你喜欢

转载自www.cnblogs.com/ShiningArmor/p/11891266.html