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

摘要:
PE Foundation 2如何找到Nt头?如何查找第一个段表?节标题位置=pNt+4+文件标题大小+扩展标题大小IMAGE_FIRST_ Section()段表中的VirtualAddress字段存储什么?VirtualAddddress:内存中段的相对虚拟地址RVAPointToRawData:该段在文件中有偏移量。Oset记录的OEP字段在哪里?扩展标头如何。AddressOfEntryPoint确定PE文件是32位还是64位?扩展标头。magicx86、x64文件头。machinex86,x64文件头SizeOfOptionalheaderx84和x64exe的默认加载基地址是什么?DLL的默认加载基地址?内存对齐的粒度是否平均?文件对齐值:0x200内存对齐值:0x1000 RVA如何转换为FOA?

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;

PE基础2-导出表-导入表第1张

手工解析导出表



PE基础2-导出表-导入表第2张

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

需要注意的是导出表的地址都是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;
PE基础2-导出表-导入表第3张

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++;
    }
    
}

 

免责声明:文章转载自《PE基础2-导出表-导入表》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇windows条件下,Ping加上时间戳,并保存到文件,适用于测试网络my live / PC keyboard / Thinkpad Mluti Connect Bluttooth Keyboard with Trackpoint / KT-1525 / KU-1255 / 4x30k12182下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python基础学习-字符编码

一、字符编码总结: 1、内存固定使用unicode,我们可以改变的是存入硬盘采用格式      英文+汉字-》unicode-》gbk      英文+日文-》unicode-》shift-jis      万国字符》-unicode-》utf-8 2、文本文件存取乱码问题 :      解决办法:                 编码格式应该设置成支持文...

Linux内核结构体--kfifo 环状缓冲区

转载链接:http://blog.csdn.net/yusiguyuan/article/details/41985907 1、前言   最近项目中用到一个环形缓冲区(ring buffer),代码是由Linux内核的kfifo改过来的。缓冲区在文件系统中经常用到,通过缓冲区缓解cpu读写内存和读写磁盘的速度。例如一个进程A产生数据发给另外一个进程B,进程...

Vim 自动文件头注释与模板定义

Vim 自动文件头注释与模板定义 在vim的配置文件.vimrc添加一些配置可以实现创建新文件时自动添加文件头注释,输入特定命令可以生成模板。 使用方法 插入模式输入模式输入seqlogic[Enter]创建时序逻辑框架 新创建一个文件 gvim test.c 自动添加头部注释 F2映射文件头注释,命令行模式文件内按F2自动添加 F11映射注释,命令...

《SED 单行脚本快速参考》的 awk 实现

http://linuxtoy.org/archives/sed-awk.html { 撰文/bones7456} sed 和 awk 都是 Linux 下常用的流编辑器,他们各有各的特色,本文并不是要做什么对比,而是权当好玩,把《SED 单行脚本快速参考》这文章,用 awk 做了一遍~ 至于孰好孰坏,那真是很难评论了。一般来说,sed 的命令会更短小一些...

Linux shell awk中printf使用

printf 是 awk 的重要格式化输出命令printf格式化输出内容 格式:printf format,item1,item2...要点: 1,printf输出时要指定格式format2,formay用于指定后面的每个item输出的格式3,printf语句不会自动打印换行符 format格式: %c:显示单个字符%d,%i:十进制整数%e,%E:科学...

Linux基础(15)多线程编程

Linux的内核中没有thread的概念,线程是第三方库libpthread实现的, 和vfork(轻量级进程,只有部分copy)有点像(进程的创建fork会完全copy主进程资源 ,而线程会共享资源,子线程创建新资源时其作用域只在当前子线程,而子线程非新新创建的资源会和创建前的主线程共享这些资源) , 线程和进程的创建在内核里都是系统调用copy_proc...