• PE文件格式详解(七)


    PE文件格式详解(七)

      Ox00 前言

      前面好几篇在讲输入表,今天要讲的是输出表和地址的是地址重定位。有了前面的基础,其实对于怎么找输出表地址重定位的表已经非常熟悉了。

      0x01 输出表结构

      当创建一个DLL文件时,实际上创建了一组能让EXE或者其他DLL调用的一组函数,PE装载器根据DLL文件中输出信息修正正在执行文件的IAT。当一个DLL函数能被EXE或者DLL文件使用时,它被称为输出了。其中输出信息被保存在输出表中,DLL文件通过输出表向系统提供输出函数名,序号和入口信息。

      对于EXE文件一般不存在输出表,而大部分DLL文件都有输出表。输出表是由一个叫做IMAGE_EXPORT_DIRECTORY(简称IED)组成。IED存放着输出函数名,输出序数等信息,他的结构如下:

    typedef struct _IMAGE_EXPORT_DIRECTORY {

        DWORD   Characteristics;    // 未使用,总为0 

        DWORD   TimeDateStamp;      // 文件创建时间戳

        WORD    MajorVersion;       // 未使用,总为0

        WORD    MinorVersion;       // 未使用,总为0

        DWORD   Name;               // 指向一个代表此 DLL名字的 ASCII字符串的 RVA

        DWORD   Base;               // 函数的起始序号

        DWORD   NumberOfFunctions;  // 导出函数的总数

        DWORD   NumberOfNames;      // 以名称方式导出的函数的总数

        DWORD   AddressOfFunctions;     // 指向输出函数地址的RVA

        DWORD   AddressOfNames;         // 指向输出函数名字的RVA

        DWORD   AddressOfNameOrdinals;  // 指向输出函数序号的RVA

    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

    下面介绍几个重要字段:

    Name:指向的是的ASCII字符串的RVA

    NumberOfFunctions:输出表EAT表中的数据条目数。

    NumberOfName:输出表ENT表中的数据条目数,ENT也是一个RVA地址,不过每项指向的是函数名字的ascii码。

    AddressOfFunctions:是指向EATRVAEATRVA一个数组,每一项指向了被输出的函数的地址。

    AddressOfNames是指向ENTRVA,ENT也是一个数组,每项指向被输出函数的名字。

    下图是他们之间的关系图:

      0x02实例分析输出表结构

      1)hexworkshop打开DLLDemo.DLL文件,找到数据目录表的第一项,它在文件头偏移78h处。如下图:

    值为RVA=4000h,转化为FileOffset=800h。此处即为输出表结构所在地址。

    2)跳往800h,依次读出几个重要字段的值如下图:

    由上图可知各个字段的RVA值,Name=0000 4032NumberOfFunctions=0000 0010 NumberOfNames0000 0010

    AddressOfFunctions=0000 4028 AddressOfNames=0000 402c 。两个Address字段全都转化为FileOffset值。

    Name=832hAddressOfFunctions=828hAddressOfNames=82ch。跳往832h如下图:

     可知输出的DLL名字叫做DLLDemo.DLL,跳往828h即可找到输出函数的EAT表,跳往82ch即可找到输出函数的ENT表。

      0x03 输出实现过程总结

      PE装载器调用GetProcAddress来查找DLLDemo.DLL里的api函数,系统通过定位DLLDemo.DLLIMAGE_EXPORT_DIRECTORY(出书目录表)结构开始工作,从这个结构中他获得输出函数名称表(ENTb表)的起始地址,进而知道这个数组只有一个元素,他对名字进行二进制查找直到发现字符串“MegBox”。PE装载器发现MsgBox是数组的第一个元素,加载器然后从输出序数表读取相应的第一个值,这个值是MsgBox的输出序数。使用输出序数作为进入EAT的索引,他得到MsgBox的值是1008h1008h加上DllDemo.DLL的装入地址得到MsgBox的实际地址。

    0x04 基址重定位概念

    当连接器生成一个PE文件时,他假设这个文件执行时会被装入默认的基址处,并且把codedata的相关地址写入PE文件中。如果装入是按照默认的值作为基址装入,则不需要基址重定位,但是如果可执行文件被装在到内存中的另一个地址,链接器所登记的地址就是错的这时就需要用重定位表来调整。在PE文件中,它往往单独分为一块,用“.reloc”来表示。PE文件重定位过程。

      0x05 详细解读基址重定位

      基址重定位表放在一个位于.relo的区域中,但是找到它需要通过数据目录表的第五项成员Base reloction Table,这项所指向重定位的基本结构IMAGE_BASE_RELOCATION

    IMAGE_BASE_RELOCATION的基本定义如下:

     struct IMAGE_BASE_RELOCATION {

        DWORD   VirtualAddress;//重定位数据开始RVA

    DWORD   SizeOfBlock;//重定位块的长度

    WORD    TypeOffset;//重定位项位数组

     

    }

     IMAGE_BASE_RELOCATION ENDS

    下面分别解释一下这几个字段:

    VirtualAddress是这一组重定位数据的开始的RVA地址。其实就是原来的地址加上这个值就完成了重定位。

    SizeOfBlock当前重定位结构的大小,因为VirtualAddressSizeOfBlock都是固定的四个字节,所以SizeOfBlock的值减去8就得到了重定位块的大小。

    TypeOffset:这个字段很有意思,一个两个字节16位,高四位代表重定义类型,低12位装入的是我们需要重定位的地址,即这个地址加上前面的字段VirtualAddress的值完成重定位。注意Typeoffset是一个数组他的值有SizeOfBlock-8决定。

    下图位重定位示意图:

     0x06 实例讲解地址重定位

     1)hexWrokShop打开PE文件DllDemo.DLL。通过数据目录表的第五项找到重定位结构IMAGE_BASE_RELOCATION,即在PE文件头偏移地址为A0h处,跳往a00h处,下图标黑地方即为结构IMAGE_BASE_RELOCATION数据。

     我们注意到第一个字段VritualAddress=0000 1000hSizeOfBlock=0000 0010h,由此可得数组TypeOffset共八个个字节。每个元素两个字节则共四组数据。整理得出下表:

    项目

    重定位数据1

    重定位数据2

    重定位数据3

    重定位数据4

    原始数据

    0F 30

    23 30

    00 00

    00 00

    TypeOffset

    30 0F

    30 23

    00 00

    00 00

    TypeOffset高四位

    3

    3

    TypeOffset低12位

    00F

    023

    12位加上VritualAddress

    100FRVA值)

    1023RVA值)

    转换成FileOffset

    20F

    223

    重定位后的地址如下图:

  • 相关阅读:
    双链表( 初始化,建立,插入,查找,删除 )
    单链表(程序员宝典)
    单链表(建立、插入、删除、打印)
    Hive- 表
    Spark- 性能优化
    Spark- Checkpoint原理剖析
    Spark- 优化后的 shuffle 操作原理剖析
    Spark- Spark普通Shuffle操作的原理剖析
    Spark- Spark内核架构原理和Spark架构深度剖析
    Spark- Spark基本工作原理
  • 原文地址:https://www.cnblogs.com/2f28/p/9830996.html
Copyright © 2020-2023  润新知