• 绑定导入表


    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文件失败
    ");
            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("
    =================%s=============
    ", moduleName);
            printf("时间戳:%d
    ",currentBound->TimeDateStamp );
            int i = currentBound->NumberOfModuleForwarderRefs;
            printf("依赖dll个数:%d
    ", i);
            
            if(i>0){
                bondRef = (PIMAGE_BOUND_FORWARDER_REF) ((DWORD) currentBound + 8);
                printf("***********依赖dll***********
    ");
                printf("依赖dll	时间戳
    ");
                for(int j=0;j<i;j++){
                    LPSTR refName = LPSTR((DWORD)((bondRef + j)->OffsetModuleName) + (DWORD)boundDir);
                    DWORD refTime = (bondRef + j) -> TimeDateStamp;
                    printf("%s	%d
    ",refName,refTime);
                }
                currentBound = currentBound + (i+1);
            }else{
                currentBound++;
            }
        }
    }
     
    int main(int argc, char* argv[])
    {
        //输出导入表信息
        printBound();
        getchar();
    }
    结果:
     
     
     
  • 相关阅读:
    Java基础教程——模拟B/S结构的服务器
    Java基础教程——Socket编程
    Java基础教程——模拟浏览器发送请求
    Java基础教程——网络基础知识
    Java基础教程——线程通信
    Java基础教程——线程局部变量
    Java基础教程——线程同步
    Java基础教程——线程池
    Java基础教程——线程状态
    Java基础教程——多线程:创建线程
  • 原文地址:https://www.cnblogs.com/ShiningArmor/p/11891266.html
Copyright © 2020-2023  润新知