• 逆向笔记——输出PE导入表、绑定导入表


    导入表

    导入表是什么?

    记录一个可执行文件所用到的其他模块的导出的函数

    记录信息:dll列表及其所用用到的函数

    在PE中的结构

    struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD   Characteristics; 
            DWORD   OriginalFirstThunk;   // RVA 指向IMAGE_THUNK_DATA结构数组
        } DUMMYUNIONNAME;
        DWORD   TimeDateStamp; 
        DWORD   ForwarderChain;         
        DWORD   Name; 					//RVA 指向dll的名字,名字以0结尾
        DWORD   FirstThunk;      		 //RVA 指向IMAGE_THUNK_DATA结构数组       
    } IMAGE_IMPORT_DESCRIPTOR;
    
    				
    	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];//函数名称,以0结尾						
    	} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;												
    

    这3个结构的关系如下图

    如图可知,PE加载前和加载后是有些不同的——IAT表中的地址变为模块的RVA或绝对地址

     IMAGE_IMPORT_DESCRIPTOR
    
    	OriginalFirstThunk 未绑定导入表的RVA
    
    	TimeDateStamp
    
    		0:未绑定
    
    		1:已绑定
    
    	FirstThunk  IAT的RVA
    
    		绑定:绝对地址
    
    		未绑定:RVA
    

    导入表有什么作用?

    通过导入表中提供的dll所依赖的函数清单去定位函数地址。

    打印导入表的步骤

    1、定位导入表

    目录项的第二个:

    2、RVA转FOA

    3、输出导入表的描述信息:以0结束

    4、输出dll名字:Name字段以0结尾的字符串为dll的名字

    5、遍历OriginalFirstThunk

    根据最高位的值判断是名称还是序号:为1==》除去最高位的值就是函数的导出序号(&80000)

    ​ 为0==》指向函数名的地址

    ​ IMAGE_IMPORT_BY_NAME : HIT 2字节

    ​ NAME 长度不确定 以‘’ 结尾

    6、遍历FirstThunk: 同上

    #include <stdio.h>
    #include <windows.h>
    #include <string.h>
     
    #define SRC1 "ipmsg.exe"
    #define SRC "notepad.exe"
    
    DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA);
    DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA);
    LPVOID ReadPEFile(LPVOID pFileBuf);
    DWORD FileSize(LPVOID pFileBuf);
    
    //移动重定位表
    void ImportTbInfo(){
        //定义pe头结构指针
        PIMAGE_DOS_HEADER pDosHeader;        //dos头指针
    	PIMAGE_NT_HEADERS pNtHeaders;
        PIMAGE_IMPORT_DESCRIPTOR importDesc;  //导入表描述
    	PIMAGE_IMPORT_BY_NAME pImportName;  //函数地址表
    	PIMAGE_THUNK_DATA  pThunkData;  //数据表
     
        //1.将文件读入内存
        LPVOID pFileBuf = fopen(SRC,"rb");
        DWORD fileSize = FileSize(pFileBuf);
    	LPVOID pFileBuffer = ReadPEFile(pFileBuf);
    	
        if(!pFileBuffer){
            printf("读取dll文件失败
    ");
            return;
        }
    
     
        //3.初始化头结构指针
    	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    	pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
    	PIMAGE_DATA_DIRECTORY dataDir = pNtHeaders->OptionalHeader.DataDirectory;
    	WORD numOfSec = pNtHeaders->FileHeader.NumberOfSections;
        importDesc = (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD)pFileBuffer + RvaToFoa(pNtHeaders, dataDir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
    
    	//4遍历模块表
    	while(importDesc->Name)
    	{
    		pThunkData = (PIMAGE_THUNK_DATA)(RvaToFoa(pNtHeaders,importDesc->OriginalFirstThunk)+(DWORD)pFileBuffer);
    		char* pName = (char*)(RvaToFoa(pNtHeaders,importDesc->Name)+(DWORD)pFileBuffer);
    		printf("模块名称(Name):%s
    ", RvaToFoa(pNtHeaders,importDesc->Name)+(DWORD)pFileBuffer );   //这个打印出来的字符串应该如何输出
    		while(pThunkData->u1.AddressOfData)
    		{
    			if(IMAGE_SNAP_BY_ORDINAL32((DWORD)pThunkData->u1.AddressOfData)) //如果是序号导入 最高位为1
    				printf("导入函数序号:%04x
    ", (pThunkData->u1.Ordinal) & 0xFFFF); //去掉最高位的1,后面的就是导入序号
    			else
    			{
    				pImportName = (PIMAGE_IMPORT_BY_NAME)(RvaToFoa(pNtHeaders,(DWORD)pThunkData->u1.AddressOfData)+(DWORD)pFileBuffer);
    				printf("导入类型:%04x, 函数名:%s
    ", pImportName->Hint,pImportName->Name); //去掉最高位的1,后面的就是导入序号
    			}
    			pThunkData++;
    		}
    		importDesc++;
    	}
    
    	//4.1 遍历模块中的函数
        return;
    }
     
    
    
    int main(int argc, char* argv[])
    {
    
    	ImportTbInfo();
    	getchar();
    	return 0;
    }
    DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA)
    {
        PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
    
        for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
        {
            if(dwRVA >= pSection[i].VirtualAddress && dwRVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData))
            {
                return pSection[i].PointerToRawData + (dwRVA - pSection[i].VirtualAddress);
            }
        }
    
        return 0;
    }
    DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA)
    {
        PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
    
        for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
        {
            if(dwFOA >= pSection[i].PointerToRawData && dwFOA < (pSection[i].PointerToRawData + pSection[i].SizeOfRawData))
            {
                return pSection[i].VirtualAddress + (dwFOA - pSection[i].PointerToRawData);
            }
        }
    
        return 0;
    }
    
    
    //将PE文件读到FileBuffer
    DWORD FileSize(LPVOID pFileBuf)
    {
         //计算文件大小
         fseek((FILE* )pFileBuf,0,SEEK_END);
         long fileSize = ftell((FILE* )pFileBuf);
         fseek((FILE* )pFileBuf,0,SEEK_SET);
    	 return fileSize;
    }
    LPVOID ReadPEFile(LPVOID pFileBuf)
    {
         //打开文件
         //Stream = fopen(fileName,"rb");
         //计算文件大小
    	 DWORD fileSize = FileSize(pFileBuf);
    
         //分配堆空间
         LPVOID pFileBuffer = malloc(sizeof(char)*fileSize);
         //将文件拷到堆
         fread(pFileBuffer,sizeof(char),fileSize,(FILE*)pFileBuf);
         fclose((FILE*)pFileBuf);
    
    	 return pFileBuffer;
    }
    

    绑定导入表

    what?

    1、起始:INT=IAT,都是程序引用的dll中的函数名或序号

    2、加载完后:IAT表中替换成函数真正的地址

    这个真正的地址,有两种情况

    ​ 2.1 程序在加载的过程中,IAT表中的地址替换成函数的RVA。

    ​ 2.2 程序在加载前,IAT表中的地址为绝对地址,如此一来,省去了修复IAT表的时间。但是有两种情况比较糟糕:

    ​ 2.2.1 dll基址冲突,需要重定位

    ​ 2.2.2 dll被修改,IAT表中对应的函数地址可能会被修改

    why?

    提高加载的效率

    when: 时间戳: 0 没绑定 -1 当前dll已绑定,并且已经存在另外一张表中

    how?

    数据目录项的第二项

    offsetmoudleName 第一个Desc的值+offsetmoudleName 才是名字的偏移

    1、根据导入表结构中的 :TimeDataStamp来判断是否绑定导入

    ​ 1.1 TimeDataStamp = 1;未绑定导入

    ​ 1.2 TimeDataStamp = 0xFFFF FFFF ; 绑定导入

    3、目录项中的位置

    define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
    

    4、绑定导入表的结构

    typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
        DWORD   TimeDateStamp;   //时间戳
        WORD    OffsetModuleName; //dll的名字 : 
        WORD    NumberOfModuleForwarderRefs;
    // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows
    } IMAGE_BOUND_IMPORT_DESCRIPTOR,  *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
    

    OffsetModuleName 计算的方法: 第一个 *(_IMAGE_BOUND_IMPORT_DESCRIPTOR )+OffsetModuleName

    5、绑定导入表结束标记

    最后一个全0的结构说明已经没有绑定导入表

    输出绑定导入表代码

    #include <stdio.h>
    #include <windows.h>
    #include <string.h>
     
    #define SRC1 "ipmsg.exe"
    #define SRC "notepad.exe"
    
    DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA);
    DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA);
    LPVOID ReadPEFile(LPVOID pFileBuf);
    DWORD FileSize(LPVOID pFileBuf);
    
    //移动重定位表
    void PrintBoundImport(){
        //定义pe头结构指针
        PIMAGE_DOS_HEADER pDosHeader;        //dos头指针
    	PIMAGE_NT_HEADERS pNtHeaders;
    	PIMAGE_BOUND_IMPORT_DESCRIPTOR  boundDir;  //数据表
    	PIMAGE_BOUND_FORWARDER_REF boundDef;
     
        //1.将文件读入内存
        LPVOID pFileBuf = fopen(SRC,"rb");
        DWORD fileSize = FileSize(pFileBuf);
    	LPVOID pFileBuffer = ReadPEFile(pFileBuf);
    	
        if(!pFileBuffer){
            printf("读取dll文件失败
    ");
            return;
        }
    
     
        //3.初始化头结构指针
    	pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    	pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
    	PIMAGE_DATA_DIRECTORY dataDir = pNtHeaders->OptionalHeader.DataDirectory;
    
    
    	
    	DWORD boundDirRVA = dataDir[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress;
        boundDir = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + boundDirRVA);
        PIMAGE_BOUND_IMPORT_DESCRIPTOR currentBound = boundDir;
    
    	//4遍历模块表
       //3.输出绑定导入表信息
        while(currentBound -> TimeDateStamp){
            getchar();
            LPSTR moduleName = (LPSTR) ((DWORD)(currentBound->OffsetModuleName) + (DWORD)boundDir); //boundDir 第一个descriptor的值+OffsetModuleName
            printf("OffsetModuleName:%s
    ", moduleName);
            printf("TimeDateStamp:%d
    ",currentBound->TimeDateStamp );
            int i = currentBound->NumberOfModuleForwarderRefs;
            printf("依赖dll个数:%d
    ", i);
            
            if(i>0){
                boundDef = (PIMAGE_BOUND_FORWARDER_REF) ((DWORD) currentBound + 8); // 8 = DWORD + DWORD  len(PIMAGE_IMPORT_DESCRIPTOR)+len(PIMAGE_BOUND_IMPORT_DESCRIPTOR)
    
                for(int j=0;j<i;j++){
                    LPSTR refName = LPSTR((DWORD)((boundDef + j)->OffsetModuleName) + (DWORD)boundDir); //计算ref的名称,原理同上
                    DWORD refTime = (boundDef + j) -> TimeDateStamp;
                    printf("依赖dll:%s,时间戳:%d
    ",refName,refTime);
                }
                currentBound = currentBound + (i+1);
            }else{
                currentBound++;
            }
        }
    
        return;
    }
     
    
    
    int main(int argc, char* argv[])
    {
    
    	PrintBoundImport();
    
    	getchar();
    	return 0;
    }
    
    
    
    DWORD RvaToFoa(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA)
    {
        PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
    
        for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
        {
            if(dwRVA >= pSection[i].VirtualAddress && dwRVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData))
            {
                return pSection[i].PointerToRawData + (dwRVA - pSection[i].VirtualAddress);
            }
        }
    
        return 0;
    }
    DWORD FoaToRva(PIMAGE_NT_HEADERS pNTHeader, DWORD dwFOA)
    {
        PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
    
        for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
        {
            if(dwFOA >= pSection[i].PointerToRawData && dwFOA < (pSection[i].PointerToRawData + pSection[i].SizeOfRawData))
            {
                return pSection[i].VirtualAddress + (dwFOA - pSection[i].PointerToRawData);
            }
        }
    
        return 0;
    }
    
    
    //将PE文件读到FileBuffer
    DWORD FileSize(LPVOID pFileBuf)
    {
         //计算文件大小
         fseek((FILE* )pFileBuf,0,SEEK_END);
         long fileSize = ftell((FILE* )pFileBuf);
         fseek((FILE* )pFileBuf,0,SEEK_SET);
    	 return fileSize;
    }
    LPVOID ReadPEFile(LPVOID pFileBuf)
    {
         //打开文件
         //Stream = fopen(fileName,"rb");
         //计算文件大小
    	 DWORD fileSize = FileSize(pFileBuf);
    
         //分配堆空间
         LPVOID pFileBuffer = malloc(sizeof(char)*fileSize);
         //将文件拷到堆
         fread(pFileBuffer,sizeof(char),fileSize,(FILE*)pFileBuf);
         fclose((FILE*)pFileBuf);
    
    	 return pFileBuffer;
    }
    
  • 相关阅读:
    Activemq+Zookeeper集群
    Centos7 安装 ActiveMQ 5.15.1
    189. Rotate Array
    188. Best Time to Buy and Sell Stock IV
    187. Repeated DNA Sequences
    186.Reverse Words in a String II
    179. Largest Number
    174. Dungeon Game
    173. Binary Search Tree Iterator
    172. Factorial Trailing Zeroes
  • 原文地址:https://www.cnblogs.com/Erma/p/12649863.html
Copyright © 2020-2023  润新知