导入表和绑定导入

导入表

概述

导入表是用来存储应用程序需要的利用的模块以及模块内的函数的结构。记录了模块的信息和函数的信息。

结构

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
            DWORD   Characteristics;           
            DWORD   OriginalFirstThunk;      //指向IMAGE_THUNK_DATA结构
          };
           DWORD   TimeDateStamp;               
           DWORD   ForwarderChain;              
           DWORD   Name;  					//模块名称
           DWORD   FirstThunk;            	//指向IMAGE_THUNK_DATA结构     
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

其中,OriginalFirstThunk和FirstThunk结构都指向IMAGE_THUNK_DATA结构,分别构成两个表INT和IAT表,两个表内的每个结构都指向一同个IMAGE_IMPORT_BY_NAME结构

typedef struct _IMAGE_THUNK_DATA32 {
    union {
    		PBYTE  ForwarderString;
    		PDWORD Function;
    		DWORD Ordinal;						   //序号
    		PIMAGE_IMPORT_BY_NAME  AddressOfData;  //指向IMAGE_IMPORT_BY_NAME
    	  } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;			//函数序号
    BYTE    Name[1];		//函数名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

导入表和绑定导入_第1张图片

函数导入的过程

1.操作系统首先找到NAME对应的模块,然后遍历INT表去寻找IMAGE_IMPORT_BY_NAME结构(其中IMAGE_THUNK_DATA结构的搜索方式是:高一位如果是0,根据字符串方式寻找。如果是1,根据函数序号方式寻找函数)。
2.找到IMAGE_IMPORT_BY_NAME后系统利用GetProc Addr()函数找到函数的地址,并将地址放入IAT表中。
导入表和绑定导入_第2张图片

绑定导入

概述

绑定导入目的是更快速的载入dll,其作用是取消系统修改IAT的过程。其单独在DataDirectory[11].VirttualAddress地址中。

结构

typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
    DWORD   TimeDateStamp;						//时间戳
    WORD    OffsetModuleName;
    WORD    NumberOfModuleForwarderRefs;		//IMAGE_BOUND_FORWARDER_REF结构的个数
} IMAGE_BOUND_IMPORT_DESCRIPTOR,  *PIMAGE_BOUND_IMPORT_DESCRIPTOR;    
typedef struct _IMAGE_BOUND_FORWARDER_REF {
    DWORD   TimeDateStamp;			//时间戳
    WORD    OffsetModuleName;
    WORD    Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;    

PE加载相关的DLL的时候,首先会根据IMAGE_IMPORT_DESCRIPTOR结构的TimeDateStamp结构与以上的两个结构的TimeDateStamp比较,判断是否需要重新计算IAT的值。
NumberOfModuleForwarderRefs指向IMAGE_BOUND_FORWARDER_REF 的个数。直接跟随在结构之后。
导入表和绑定导入_第3张图片

编程关键

导入表:
比较高1位需要数据与0x80000000进行&运算判断,输出的结果是与0x7fff ffff与运算。
绑定导入:
Name的位置在绑定导入表为基准的OffsetModuleName偏移位置处。
IMAGE_BOUND_IMPORT_DESCRIPTOR的索引需要加上IMAGE_BOUND_FORWARDER_REF 所占的空间。

代码演示

#include 
#include 
#include 
DWORD RVATOFOA(DWORD RVA,LPVOID pFileBuffer)
{
        DWORD FOA = NULL;
        PIMAGE_DOS_HEADER pDosHeader = NULL;
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_FILE_HEADER pFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

        if(RVA <= pOptionalHeader->SizeOfHeaders)
                return RVA;
        for(;RVA > (pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);pSectionHeader++);//定位到所在节
        FOA = RVA - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
        return FOA;

}
DWORD FOATORVA(DWORD FOA,LPVOID pFileBuffer)
{
        DWORD RVA = NULL;

        PIMAGE_DOS_HEADER pDosHeader = NULL;
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_FILE_HEADER pFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER pSectionHeader = NULL;

        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
        pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
        pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

        if(FOA <= pOptionalHeader->SizeOfHeaders)
                return FOA;
        for(;FOA > (pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);pSectionHeader++);//定位到所在节
        RVA = FOA - pSectionHeader->PointerToRawData + pSectionHeader->VirtualAddress ;
        return RVA;
}


LPVOID ReadPEFile(LPSTR lpszFile)//读取文件
{
    FILE *pFile = NULL;//文件流
    DWORD fileSize = 0;//文件大小
    LPVOID pFileBuffer = NULL;//文件存储的缓冲区

    //打开文件
    pFile  = fopen(lpszFile,"rb");
    if(!pFile)
    {
        printf("无法打开exe文件!");
        return NULL;
    }
    //读取文件的大小
    //SEEK_SET: 文件开头SEEK_CUR: 当前位置;SEEK_END: 文件结尾
    fseek(pFile,0L,SEEK_END);//fseek用于得到文件位置指针当前位置相对于文件首的偏移字节数
    fileSize = ftell(pFile);
    fseek(pFile,0L,SEEK_SET);
    //分配缓冲区 pFileBuffer缓冲区的
    pFileBuffer = malloc(fileSize);
    if(!pFileBuffer)
    {
        printf("分配文件失败!");
        fclose(pFile);
        return NULL;
    }

    //将文件读入缓冲区
    size_t n = fread(pFileBuffer,fileSize,1,pFile);
    if(!n)
    {
        printf("读取数据失败!");
        free(pFileBuffer);
        fclose(pFile);
        return NULL;
    }
    //关闭文件
    fclose(pFile);
    return pFileBuffer;
}

VOID ImportTable(LPVOID pFileBuffer)
{
      //初始化部分
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
    PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
    PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = NULL;
    PIMAGE_THUNK_DATA32 pThunkData32 = NULL;
    //初始化地址指针
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
    pDataDirectory = pOptionHeader->DataDirectory;
    printf("Import_Table: %x Size: %x\n", pDataDirectory[1].VirtualAddress, pDataDirectory[1].Size);
    pImportDescriptor = (DWORD)pFileBuffer + RVATOFOA(pDataDirectory[1].VirtualAddress,pFileBuffer);//定位胡导入表在文件中的位置
    //打印导出表
    DWORD count  = 0;
    PDWORD pOriginalFirstThunk = (PDWORD)((DWORD)pFileBuffer + RVATOFOA(pImportDescriptor->OriginalFirstThunk,pFileBuffer));
    PDWORD pFirstThunk = (PDWORD)((DWORD)pFileBuffer + RVATOFOA(pImportDescriptor->FirstThunk,pFileBuffer));
    do
    {
        printf("NAME: %s\n",(DWORD)pFileBuffer + RVATOFOA(pImportDescriptor[count].Name,pFileBuffer));
        printf("OriginalFirstThunk: %x\n%x\n",pOriginalFirstThunk,*pOriginalFirstThunk);
        printf("FirstThunk: %x\n%x\n",pFirstThunk,*pFirstThunk);
        printf("------------------OriginalFirstThunk------------------\n");
        pThunkData32 = (DWORD)pFileBuffer + RVATOFOA(pImportDescriptor[count].OriginalFirstThunk,pFileBuffer);//定位函数名称地址
        DWORD i = 0;
        do
        {
            if(pThunkData32[i].u1.Ordinal & 0x80000000)
            {
                printf("序号导入: %d\n",pThunkData32[i].u1.Ordinal & 0x7fffffff);
            }
            else//为0
            {
                printf("名称导入: %d\n",(DWORD)pFileBuffer + RVATOFOA(pThunkData32[i].u1.AddressOfData,pFileBuffer) + 2);
            }
            i++;
        }while(pThunkData32[i].u1.AddressOfData != NULL);
        printf("------------------FirstThunk------------------\n");
        pThunkData32 = (DWORD)pFileBuffer + RVATOFOA(pImportDescriptor[count].FirstThunk,pFileBuffer);
        i = 0;
        do
        {
            if(pThunkData32[i].u1.Ordinal & 0x80000000)
            {
                printf("序号导入: %d\n",pThunkData32[i].u1.Ordinal & 0x7fffffff);
            }
            else
            {
                printf("名称导入: %d\n",(DWORD)pFileBuffer + RVATOFOA(pThunkData32[i].u1.AddressOfData,pFileBuffer) + 2);
            }
            i++;
        }while(pThunkData32[i].u1.AddressOfData != NULL);
        count++;
    }while(pImportDescriptor[count].Name != NULL);
}
VOID PrintBoundImportTable(LPVOID pFileBuffer)
{
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
    PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
    PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
    PIMAGE_BOUND_IMPORT_DESCRIPTOR  pBoundImportDescriptor = NULL;
    PIMAGE_THUNK_DATA32 pThunkData32 = NULL;
    PIMAGE_BOUND_FORWARDER_REF pBoundForwarderRef = NULL;
    //初始化地址指针
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
    pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
    pDataDirectory = pOptionHeader->DataDirectory;
    pBoundImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + pDataDirectory[11].VirtualAddress);
    DWORD i = 0;
    DWORD j = 0;
    if(pDataDirectory[11].VirtualAddress == 0)
    {
        printf("没有绑定导入表信息!\n");
        return;
    }
    do
    {
        printf("TimeStamp: %x\n", pBoundImportDescriptor[i].TimeDateStamp);
        printf("OffsetModuleName: %x\n", pBoundImportDescriptor[i].OffsetModuleName);
        printf("NumberOfModuleForwarderRefs: %x\n", pBoundImportDescriptor[i].NumberOfModuleForwarderRefs);
        printf("Name: %s\n", (DWORD)pBoundImportDescriptor + pBoundImportDescriptor[i].OffsetModuleName);//重点定位名字
        i++;
        //PIMAGE_BOUND_FORWARDER_REF结构在PIMAGE_BOUND_IMPORT_DESCRIPTOR结构后面
        pBoundForwarderRef = &pBoundImportDescriptor[i];
        for(j = 0;j < pBoundImportDescriptor[i].NumberOfModuleForwarderRefs;j++)
        {
            printf("TimeStamp: %x\n", pBoundForwarderRef[j].TimeDateStamp);
            printf("OffsetModuleName: %x\n", pBoundForwarderRef[j].OffsetModuleName);
            printf("Name: %s\n", (DWORD)pBoundImportDescriptor + pBoundForwarderRef[j].OffsetModuleName);
            printf("Reserved: %x\n", pBoundForwarderRef->Reserved);
            j++;
        }
        printf("\n");
        if(j >= pBoundImportDescriptor[i].NumberOfModuleForwarderRefs && j!=0)
            j--;
        i = i + j;
    }while(pBoundImportDescriptor[i].OffsetModuleName != 0);
}

int main()
{
    LPSTR lpszFile = "D:\\test\\HelloWorld.exe";
    LPVOID pFileBuffer = ReadPEFile(lpszFile);
    ImportTable(pFileBuffer);
    PrintBoundImportTable(pFileBuffer);
    return 0;
}

你可能感兴趣的:(导入表和绑定导入)