• PE基础2-导出表-导入表


    PE基础2

     
    怎么找到Nt头?

    (PIMAGE_NT_HEADER)(DOS.e_lfanew + (DWORD)m_pBuff)

    怎么找到第一个区段表?

    区段头位置 = pNt + 4 + 文件头的大小 + 扩展头大小 IMAGE_FIRST_SECTION()

    区段表中的VirtualAddress字段保存的是什么?PointToRawData呢?

    VirtualAdddress : 区段在内存的相对虚拟地址

    RVA PointToRawData: 区段在文件中偏移Offset

    记录OEP的字段在哪里?

    扩展头.AddressOfEntryPoint (OEP)

    怎么判断一个PE文件是32位的还是64位的?

    扩展头.magic x86(10B) , x64(20B)

    文件头.machine x86(014c) , x64(8664)

    文件头.SizeOfOptionalheader x84(E0), x64(F0)

    exe的默认加载基址是多少?DLL的默认加载基址?

    exe: 0x00400000

    dll: 0x10000000

    文件对齐粒度一般是多少? 内存对齐粒度一般?

    文件对齐值:0x200

    内存对齐值:0x1000 (4kb)

    RVA怎么转换成FOA ?

    FOA = RVA - 区段在内存中的相对虚拟地址 + 区段在文件中偏移

    导出表

    导出回顾 dll导出函数的几种方式

    声明导出: _declspec(dllexport)

    def文件导出

    dll函数调用

    隐式链接 :

    包含头文件,载入lib库

    显示连接 :

    LoadLibray,GetProcAddress

    导出表作用

    导出是PE为其它程序提供API的一种函数实例导出行为

    windows下存在导出表的可执行文件,可以提供其它第三方程序使用

    导出表支持符号导出,序号导出,这两种方式可以共存

    导出表位于数据目录表中第0选项(默认我们以数组下标0开始数)

    导出表结构

    typedef struct _IMAGE_EXPORT_DIRECTORY { 
       DWORD  Characteristics;        // (1) 保留,恒为0x00000000    
       DWORD  TimeDateStamp;          // (2) 时间戳  
       WORD   MajorVersion;            // (3) 主版本号,一般不赋值  
       WORD   MinorVersion;            // (4) 子版本号,一般不赋值  
       DWORD  Name;                    // (5) 模块名称*  
       DWORD  Base;                    // (6) 索引基数*  
       DWORD  NumberOfFunctions;       // (7) 导出地址表中成员个数*  
       DWORD  NumberOfNames;          // (8) 导出名称表中成员个数*  
       DWORD  AddressOfFunctions;      // (9) 导出地址表(EAT)*  
       DWORD  AddressOfNames;          // (10) 导出名称表(ENT)*  
       DWORD  AddressOfNameOrdinals;   // (11) 指向导出序号数组*
    }IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;

    手工解析导出表



     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    需要注意的是导出表的地址都是RVA 导出表中有三张表分别是 EAT,ENT,EOT。

    EAT: 导出地址表 ENT: 导出名称表(与EOT一一对应)

    EOT:导出序号表

    代码解析导出表


    void PE::ShowExportTable() {  
        PIMAGE_EXPORT_DIRECTORY p = GetExportDirectory();    //输出导出表信息  
        char * dllName = (char*)(RVAToFoa(p->Name) + (DWORD)m_pbuff);   
        printf("dll名 %s
    ", dllName);   
        printf("导出函数地址表个数 %2d
    ", p->NumberOfFunctions);   
        printf("导出函数名称表个数 %2d
    ", p->NumberOfFunctions);
        //遍历导出函数    //导出序号表   
        WORD *EOT = (WORD*)(RVAToFoa(p->AddressOfNameOrdinals) + (DWORD)m_pbuff);    
        //导出名称表    
        DWORD *pENT = (DWORD*)(RVAToFoa(p->AddressOfNames) + (DWORD)m_pbuff);   
        //导出序号表   
        DWORD *pEAT = (DWORD*)(RVAToFoa(p->AddressOfFunctions) + (DWORD)m_pbuff);
        //遍历地址表所有函数   
        for (int i = 0; i < p->NumberOfFunctions; i++)    
        {      
            printf("序号%d   ", i + p->Base);     
            int j = 0;       
            //遍历所有序号表,找到有名字的函数      
            for (; j < p->NumberOfNames; j++)      
            {          
                if (i == EOT[j])        
                {              
                    // 导出名称表[  序号表[j] ]  
                    int RvaName = pENT[EOT[j]];  
                    char *szName = (char*)(RVAToFoa(RvaName) + (DWORD)m_pbuff);
                    printf("函数名 %s  ", szName);       
                    break;   
                }     
            }       
            //没有找到函数名     
            if (j > p->NumberOfNames)  
            {       
                printf("函数名 NULL  ");  
            }     
            //打印导出函数地址     
            printf("%08X
    ", *(DWORD*)(RVAToFoa(pEAT[i]) + (DWORD)m_pbuff)); 
        } 
    }

    导入表

    导入表作用

    导入表是PE文件从其它第三方程序中导入API,以供本程序调用的机制(与导出表对应)

    在exe运行起来的时候, 加载器会遍历导入表, 将导入表中所有dll 都加载到进程中,被加载的DLL的DllMain就会 被调用

    通过导入表可以知道程序使用了哪些函数

    当然有写程序可以没有导入表,自己实现动态加载功能 导入表结构,

    这个导入表是一个数组,以全为零结尾

     

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {  
       union {  
           DWORD Characteristics;  
           DWORD OriginalFirstThunk;//(1) 指向导入名称表(INT)的RAV*  
      };  
       DWORD   TimeDateStamp;  // (2) 时间标识  
       DWORD   ForwarderChain; // (3) 转发链,如果不转发则此值为0
       DWORD   Name;       // (4) 指向导入映像文件的名字*  
       DWORD   FirstThunk; // (5) 指向导入地址表(IAT)的RAV*
    } IMAGE_IMPORT_DESCRIPTOR;

    导入表解析

    导入表中重要的字段 : OriginalFirstThunk(INT)

    导入表中重要的字段 : FirstThunk(IAT)

    导入表中重要的字段 :Name

    其中IAT 与 INT都指向 IMAGE_THUNK_DATA32

    typedef struct _IMAGE_THUNK_DATA32 {  
       union {      
           PBYTE  ForwarderString;                // (1) 转发字符串的RAV      
           PDWORD Function;                        // (2) 被导入函数的地址    
           DWORD Ordinal;                          // (3) 被导入函数的序号    
           PIMAGE_IMPORT_BY_NAME  AddressOfData;   // (4) 指向输入名称表
      } u1;
      } IMAGE_THUNK_DATA32;

    IAT与INT区别在于,在文件中他们都指向一个函数名字或序号,在内存中IAT会被填充到正确的函数地址

    IMAGE_THUNK_DATA32中值的最高位为1,表示序号导入,

    Ordinal IMAGE_THUNK_DATA32中值的最高位为0,表示函数名导入,

    AddressOfData字段有效 PIMAGE_IMPORT_BY_NAME 指向导入函数序号与函数名

    typedef struct _IMAGE_IMPORT_BY_NAME {    
       WORD    Hint;           // (1) 需导入的函数序号  
       BYTE    Name[1];        // (2) 需导入的函数名称
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

    void PE::ShowImportInfo()
    {
        //获取导入表
        PIMAGE_IMPORT_DESCRIPTOR pImport = GetImportDirectory();
        
        //遍历所有导入表,最后一项以0结尾
        while (pImport->Name)
        {
            //显示导入模块的名字
            char * szDllName =(char*)( RvaToFoa(pImport->Name)+ (DWORD)m_pBuff);
            printf("%s
    ", szDllName);
    ​
            //显示导入函数的名称
            //遍历导入地址表IAT(导入名称表INT)
            //IAT
            PIMAGE_THUNK_DATA pIat = 
                (PIMAGE_THUNK_DATA)(RvaToFoa(pImport->FirstThunk) + (DWORD)m_pBuff);
    ​
            //INT
            PIMAGE_THUNK_DATA pInt =
                (PIMAGE_THUNK_DATA)(RvaToFoa(pImport->OriginalFirstThunk) + (DWORD)m_pBuff);
    ​
            //IAT表个数不确定,最后一项以全为0结尾
            while (pIat->u1.Ordinal)
            {
                //判断是否名称导出还是序号导出
                //通过最高位是否为1 ,如果为1,那么序号导出
                //如果为0,名称导出 
                if (pIat->u1.Ordinal & 0x80000000)
                {
                    //最高位为1,序号导入
                    //打印一下序号
                    printf("    序号%2d
    ", pIat->u1.Ordinal & 0xFFFF);
    ​
                }
                else {
                    //最高位位0,名称导入
                    //显示序号,和名称
                    PIMAGE_IMPORT_BY_NAME pName =
                        (PIMAGE_IMPORT_BY_NAME)(RvaToFoa(pIat->u1.AddressOfData) + (DWORD)m_pBuff);
    ​
                    printf("    序号%2d", pName->Hint);
                    printf("    %s
    ", pName->Name);
                }
                pIat++;
            }
            pImport++;
        }
        
    }

     

  • 相关阅读:
    npm、webpack、vue-cli 快速上手版
    jquery 显示和隐藏的三种方式
    jquery好友面板切换
    jquery 事件冒泡
    jquery QQ微博
    C# Thread 参数
    WPF Dispatcher的使用
    UVa 1600 Patrol Robot (BFS最短路 && 略不一样的vis标记)
    HDU 2795 Billboard (线段树单点更新 && 求区间最值位置)
    HDU 1394 Minimum Inversion Number (树状数组 && 规律 && 逆序数)
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11083821.html
Copyright © 2020-2023  润新知