PE文件格式,相对虚拟地址解释

by admin on 2019年3月24日

home88一必发,《软件漏洞分析技术》笔记

EscortVA是相对虚拟地址(Relative Virtual
Address)的缩写,顾名思义,它是2个“相对”地址,也得以说是“偏移量”,PE文件的各种数据结构中涉及到地点的字段超越51%都以以安德拉VA代表的。

本次的询问重点教师PE的基本概念、MS-DOS文件头、PE文件头、区块、输入表、输出表等。

装载与动态链接

PE文件:


确地说,OdysseyVA正是当PE文件棉被服装载到内部存款和储蓄器中后,有个别数据的职务相对于文件头的偏移量。举个例子,假如Windows装载器将2个PE文件装入
00五千00h处的内部存款和储蓄器中,而有些节中的有些数据棉被服装入0040xxxxh处,那么那个数额的EnclaveVA便是(0040xxxxh-
00伍仟00h)=xxxxh,反过来说,将中华VVA的值加上文件棉被服装载的营地址,就可以找到数据在内部存款和储蓄器中的骨子里地址。

PE文件格式,相对虚拟地址解释。那边自身将会结合二个大致的小程序来深化笔者对PE文件结构的询问。

可执行文件的装载与经过

  • 各个程序都富有和谐独自的虚拟地址空间,那些空间尺寸由微型总结机硬件平台决定(理论上的最大上限)。比如,叁12位硬件平台的虚拟地址空间的地方为0到232-1,即0x000000000xFFFFFFFF,总共大概4G;而64位硬件平台的虚拟地址空间地址为0到2<sup>64</sup>-1,即0x00000000000000000xFFFFFFFFFFFFFFFF,大致有17179869184G。在三14个人平台上,Linux操作系统中4G的虚拟地址空间会被划分为多个部分,从0xC0000000到0xFFFFFFFF共1G的地点空间被分配给了操作系统,剩下的从0x00000000到0xBFFFFFFF共3G的地点空间是留住进度的。从规范上讲,我们经过最多能使用3G的虚拟地址空间。对于Windows操作系统来说,它的进度虚拟地址空间划分是操作系统占用2G,进度只剩余2G。对于有些先后来说2G虚拟空间太小,所以Windows有个运转参数能够将操作系统占用的虚拟地址空间缩短到1G。方法如下:修改Windows系统盘根目录下的boot.ini,加上“/3G”参数。

home88一必发 1

  • 动态装载的三种典型格局是覆盖装入和页映射,覆盖装入在并未表达虚拟存款和储蓄以前运用相比普遍,未来一度差不离被淘汰了。页映射一句话来说便是操作系统将先后供给接纳的页按自然的算法动态映射到大体内部存款和储蓄器中执行。

  • 从操作系统的角度来看,3个进程最根本的特色是它抱有独立的虚拟地址空间,那使得它有别于别的进度。3个进程的建立有三步:

    • 先是是创设虚拟地址空间。
    • 读取可执行文件头,并且建立虚拟空间与可执行文件的映照关系。(可执行文件装载中最重视的一步,也是守旧意义上的“装载”)
    • 将CPU指令寄存器设置成可执行文件入口,运行运作。
  • 大家精晓,当程序执行发生页错误时,操作系统将从情理内部存款和储蓄器中分配三个物理页,然后将该“缺页”从磁盘中读取到内部存款和储蓄器中,再设置缺页的虚拟页和物理页的照耀关系,那样程序才得以健康运作。可是很明显的一些是,当操作系统捕获到缺页错误时,它应通晓程序当前所急需的页在可执行文件中的哪一个职位。那正是编造空间与可执行文件之间的映照关系。

home88一必发 2

  • ELF文件被映射时,是以类别的页长度作为单位的。为幸免内部存款和储蓄器浪费,操作系统在装载可执行文件时根本关怀的只是文本中央的权能(可读、可写、可进行)。对于同一权限的段,把它们统一到一块作为三个段进展览放映射。Linux上校进度虚拟空间中的二个段叫做虚拟内部存款和储蓄器区域(VMA),在Windows中校那个名为虚拟段(Virtual
    Section)。很多意况下,2个进程中的堆和栈分别都有贰个对应的VMA。操作系统在经过运维前会将系统的环境变量和进度的运营参数提前保存到过程的杜撰空间的栈中(相当于VMA中的stack
    VMA)。

  • PE文件的装载和ELF有所分化,在PE文件中,全部段的原初地址都是页的倍数,段的长短若是还是不是页的平头倍,那么在炫耀时向上补齐到页的整数倍。由于那些性子,PE文件的照射进程比ELF不难得多,因为它无需考虑如ELF里面诸多段地址对齐之类的难题,即使那样会浪费一些磁盘和内部存款和储蓄器空间。

  • PE文件中,链接器在生养可执行文件时,往往将有所的段尽大概地统一,所以一般唯有代码段、数据段、只读数据段和BSS等为数不多的多少个段。

  • 每种PE文件在装载时都会有1个装载指标地方,这一个地址便是集散地址,集散地址不是定位的,每趟装载时都可能会转变。所以PE文件中有贰个常见术语叫相对虚拟地址(奥迪Q5VA),它是周旋于PE文件的装载集散地址的3个偏移地址。那样无论营地址怎么生成,PE文件中的各样RAV4VA都保持一致。

  • WIndows PE文件的装载进程:

    • 先读取文件的首先个页(包蕴DOS头,PE文件头和段表)。
    • 反省进程地址空间中,目的地址是或不是可用,倘诺不可用,则此外选二个装载地方。(主要针对DLL装载)
    • 利用段表中提供的音信,将PE文件中持有的段一一映射到地点空间中相应的职位。
    • 借使装载地方不是指标地方,则展开Rebasing
    • 装载全体PE文件所急需的DLL文件。
    • 对PE文件中的全数导入符号实行分析。
    • 依照PE头中内定的参数,建立开首化堆和栈。
    • 建立主线程并且运维进程。

  PE(Portable
Executable)是win32平台下可实施服从的数额格式。日常周边的例如*.exe和*.dll都是PE文件。

PE文件中冒出途乐VA的
概念是因为PE的内部存储器影象和磁盘文件影像是例外的,同一数据相对于文件头的偏移量在内部存款和储蓄器夹钟在磁盘文件中恐怕是见仁见智的,为了提升功能,PE文件头中使用的
都以内部存款和储蓄器印象中的偏移量,也正是CR-VVA。从图17.3中也能够取得另三个定论,那就是奥迪Q3VA仅仅是对此地处节中的数据而言的,对于文本头和节表来说无所谓
奥迪Q3VA和文书偏移,因为它们在被映射到内部存款和储蓄器中后无论是是深浅或许摇头都不会有其余变更。

 

动态链接

  • 怎么要动态链接?

    • 静态链接的点子对于电脑内部存储器和磁盘的长空浪费11分惨重。
    • 静态链接对于程序的更新、安顿和发表也会带来众多难为。
  • 在Linux系统中,ELF动态链接文件被改成动态共享对象(DSO),简称共享对象,它们一般都以以“.so”为扩充名的部分文件;而在Windows系统中,动态链接文件被变成动态链接库(DLL),它们经常是以“.dll”为扩展名的文本。

  • 静态链接的重定位叫链接时重一直(Link Time
    Relocation),而动态链接的重定位为装载时重一直(Load Time
    Relocation),在Windows中,那种装载时重平素又被誉为基址重置(Rebasing)。在Linux和GCC中假若选取“-shared”参数,输出的共享对象正是应用的装载时重一贯。

  • 把指令中那3个急需修改的部分分离出来,跟数据部分放在一起,这样指令部分就足以维持不变,而数据部分可以在各样进程中全部四个副本,那种方案正是地点毫无干系代码(PIC)技术。在Linux共享对象中要扭转地址无关代码只用在编写翻译是带上参数-fPIC。

home88一必发 3

  • 地点的图景并从未包涵定义在共享模块内部的全局变量。ELF共享库在编写翻译时,暗许都把定义在模块内部的全局变量当做定义在任何模块的全局变量,也便是说当做上海教室中的类型(4),通过GOT来实现变量的拜访。当共享模块棉被服装载时,假若某些全局变量在可执行文件中负有副本,那么动态链接器就会把GOT中的相应地方指向该副本,那样该变量在运营时实际上最后就唯有二个实例。假如变量在共享模块中被开首化,那么动态链接器还亟需将该开头化值复制到主模块中的变量副本;借使该全局变量在程序主模块中并未副本,那么GOT中的相应地方就对准模块内部的该变量副本。

  • 对此共享对象的话,即使数额段中有相对地址引用,那么编写翻译器和链接器就会爆发2个重定位表,这些重定位表里面包涵了“Lacrosse_386_RELATIVE”类型的重定位入口。当动态链接器装载共享对象时,若是发现该共享对象有如此的重定位入口,那么动态链接器就会对该共享对象举行重平昔。

  • 大家在编译共享对象时假如应用“-fPIC”参数,就象征要发生地址无关的代码段。GCC编写翻译动态链接的可执行文件会暗许带上该参数的。假诺不选拔该参数就会时有产生3个装载时重平素的共享对象,它的代码段就不是地方非亲非故的,也就无法被多个经过之间共享,于是就失去了节约内部存款和储蓄器的亮点。可是装载时重从来的共享对象的周转速度要比使用地点毫无干系代码的共享对象快,因为它省去了地点毫不相关代码中年老年是访问全局数据和函数时要求做三遍总计当前地点以及直接地址寻址的历程。

  • 动态链接比静态链接慢的重点缘由是动态链接下对于全局和静态的多寡访问都要拓展复杂的GOT定位,然后直接寻址;对于模块间的调用也要先固定GOT,然后再展开直接跳转,那大概会造成程序运转大概运行速度放慢,所以大家需求优化动态链接质量。

  • ELF选择延迟绑定来优化动态链接品质,基本考虑是当函数第二遍被用到时才实行绑定(符号查找、重向来等)。具体方法是运用了PLT(Procedure
    Linkage
    Table)。PLT为GOT直接跳转又充实了贰个中间层,在调用有个别外部模块的函数时,并不间接通过GOT跳转,而是通过2个叫作PLT项的构造来展开跳转。各个外部函数在PLT中都有2个对应的项。(汇编指令实现)

home88一必发 4

  • 实在的PLT基本结构代码如下:

PLT0:
push *(GOT +4)
jump *(GOT+8)

...

bar@plt:
jmp *(bar@GOT)
push n
jump PLT0
  • 在动态链接情况下,操作系统在装载完可执行文件之后会先运营2个动态链接器,之后就将控制权交给动态链接器的进口地址。当动态链接器得到控制权之后,它伊始推行一层层自个儿的开头化操作,然后依照当前的环境参数,开始对可执行文件进行动态链接工作。当有着动态链接工作做到之后,动态链接器会将控制权交到可执行文件的输入地址,程序开端专业推行。

  • 动态链接相关协会

    • “.interp”段:里面保存的正是2个字符串,那么些字符串便是可执行文件所急需的动态链接器的路子。
    • “.dynamic”段:ELF文件中最要害的组织,保存了依靠于怎么着共享对象、动态链接符号表的职分、动态链接重定位表的职位、共享对象初步化代码的地点等音信。
    • “.dynsym”段:动态符号表,表示动态链接模块之间的标记导入导出关系。
    • “.rel.dyn”段:数据援引重一向,核对“.got”以及数据段。
    • “.rel.plt”段:函数引用重平昔,考订“.got.plt”。
  • 动态链接基本上分为3步:先是运营动态链接器本人(自举,bootstrap),然后装载全体必要的共享对象,最终是重一向和起初化。(跳转)

  • 成就基本自举现在,动态链接器将可执行文件和链接器本人的符号表都合并到2个大局符号表中。在Linux中,当一个标记要求被投入全局符号表时,如若一致的标记名早已存在,则后加盟的标志被忽略(全局符号参加难题)。

  • 当上边的步子实现未来,链接器起头重复遍历可执行文件和各个共享对象的重定位表,将它们的GOT/PLT中的每种须求重一向的职位实行改进。

  可执行文件:包罗二进制代码,字符串,菜单,图标,位图字体等。

 

利用学习工具:有StudyPE、LordPE、PEID。

Windows下的动态链接

  • 在ELF中,由于代码段是地方非亲非故的,所以它能够兑现四个经过之间共享一份代码,不过DLL的代码却并不是地方非亲非故的,所以它只是在有个别情状下能够被八个进度间共享。

  • PE文件格式,相对虚拟地址解释。PE文件里有八个常用的概念就是基地址(Base
    Address)
    对峙地址(奥迪Q5VA,Relative Virtual
    Address)
    。营地址正是PE头文件中的Image
    Base,是PE文件被装载进过程地址空间中的开端地址,
    对于EXE文件来说,其值一般是0x四千00,对于DLL文件来说,其值一般是0x一千0000。而绝对地址就是二个地址相对于集散地址的偏移。

  • ELF暗中认可导出装有的大局符号。可是在DLL中,我们须要显式地“告诉”编写翻译器大家要求导出有个别符号,不然编译器暗中同意全数符号都不导出。在VC++中,我们使用“__declspec(dllexport)”表示DLL导出符号,使用“__declspec(dllimport)”表示DLL导入符号。除了选用导出导入符号外,大家也足以利用“.def”文件中的IMPOOdysseyT或许EXPO宝马X5TS段来声称导入导出符号。这一个主意不但对C/C++有效,对任何语言也实惠。

  • 使用.def文件来叙述DLL文件导出属性的帮助和益处有三个,一是可以控制导出符号的记号名,而是能够操纵一些链接的进度。

  • Windows提供三个API来支撑DLL的周转时链接,分别是LoadLibrary(LoadLibraryEx):装载DLL,GetProcAddress:获取某些符号的地点,FreeLibrary:卸载DLL。

  • 在Windows PE中,全数导出的记号被集中存放在导出表(Export
    Table)
    的布局中。从最简单易行的结构上来看,它提供了一个符号名与符号地址的投射关系。导出表是四个IMAGE_EXPORT_DIRECTORY结构体,定义在“Winnt.h”中:

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

home88一必发 5

  • 导出表结构中,最终2个分子实施二个数组,分别是导出地址表(EAT,Export
    Address Table)
    标志名表(Name
    Table)
    名字序号对应表(Name-Ordinal Table)

  • 导出地址表中存放的是逐一导出函数的LacrosseVA,符号名表中存放的是导出函数的名字。序号表实际是初期十多少人windows为了应对内存小而利用的建制。使用序号导入导出的益处正是节约了函数名查找进度,函数名表也不须要保留到内部存储器中。可是它最大的题材便是1个函数的序号大概会变动。那就须求程序员手工业钦命各类导出函数的序号。由于最近硬件质量的升官,那种内部存款和储蓄器空间的节约和查找速度的升级换代效能就不醒目了。所以现在那种艺术为主就不接纳了,但是为了保全向后非凡,它照旧被保存了下来。

  • 动态链接器怎么着查找函数奇骏VA呢?倘使模块A导入了Math.dll中的Add函数,那么A的导入表中就保留了“Add”这几个函数名。当进行动态链接时,动态链接器在Math.dll的函数名表中实行二分查找,找到“Add”函数,然后在名字序号对应表中找到“Add”所对应的序号,即1,减去Math.dll的Base值1,结果为0,然后在EAT中找到下标0的要素,即“Add”的奥迪Q5VA为0x1000。

  • 在ELF中,“.rel.dyn”和“.rel.plt”八个段中分头保存了该模块所要求导入的变量和函数的标志以及所在的模块等音讯,而“.got”和“.got.plt”则保留着这一个变量和函数的的确地址。Windows中也有相近机制,叫做导入表(Import
    Table)
    。当有个别PE文件被加载时,Windows加载器的内部三个任务就是将兼具要求导入的函数地址显然并且将导入表中的成分调整到科学的地点,以贯彻动态链接的进程。

  • 导入表是一个IMAGE_IMPORT_DESC牧马人IPTOCR-V结构体数组,每1个IMAGE_IMPORT_DESC奥迪Q7IPTO奥迪Q5结构对应一个被导入的DLL。它也被定义在“Winnt.h”中:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
  • 结构体中的FirstThunk指向多个导入地址数组(IAT,Import Address
    Table)
    ,IAT中种种成分对应3个被导入的标记,成分的值在区别的处境下有区别的意思。在动态链接器刚形成映射还尚无从头重平昔和符号解析时,IAT中的成分值表示相呼应的导入符号的序号可能是符号名;当Windows的动态链接器在形成该模块的链接时,成分值会被动态链接器改写成该符号的着实地址,从这点看,导入地址数组与ELF中的GOT十一分类似。(INT)

  • 为了使得编写翻译器能够区分函数是从外部导入的依然模块内部定义的,MSVC引入了“__declspec(dllimport)”的扩展属性,一旦多少个函数被声称为“__declspec(dllimport)”,那么编写翻译器就精通它是表面导入的,以便发生相应的下令方式。比如:CALL
    DWORD PTR
    [0x0040D11C]
    。那其间的IAT表成分地址0x0040D11C也是纯属地址,那也是须求前边订正的。所以能够见见PE结构中,DLL的代码段并非地点非亲非故的,所以Windows系统就是大方,根本不像Linux那么在意代码段指令的双重利用。

  • 因为PE没有类似ELF的大局符号参与难点,所以对于模块内部的全局函数调用,编写翻译器发生的都是一直调用指令CALL
    XXXXXXXX
    (不是相对地址偏移,是直接地址调用。这是因为Windows
    PE下,任何3个PE文件在编写翻译时都会提交本人的2个先行李装运载地点,然后依据此职分发生一文山会海的一贯,当然那个相对地址是需求在事实上装载运营时再另行革新的,选择了一种重定集散地址的办法)。

  运行时操作系统会按PE文件的预订定位能源并装载入内部存款和储蓄器。可执行文件
——>拆分——>若干数量节<——分歧的能源。

贰 、汇编中虚拟地址(VRA)与公事偏移地址(FileOffset)的交互转换:

 学习PE建议看书。。和友好动手。。。

DLL优化

  • DLL的代码段和数量段本人并不是地点非亲非故的,也等于说它暗许须要棉被服装载到由ImageBase内定的靶子地方中。假诺目的地址被私吞,那么就供给装载到另各州点,便会挑起上上下下DLL的Rebase。那对于有着大量DLL的次序来说,频仍的Rebase也会导致程序运转缓慢。那是影响DLL品质的二个缘故

  • 动态链接进度中,导入函数的记号在运营时须要被各种解析。在这些分析进程中,免不了涉及到符号字符串的相比较和寻找进程,那个查找进程中,动态链接器会在目的DLL的导出表中展开符号字符串的二分查找。就算是利用了二分查找法,对于持有DLL数量很多,并且有大批量导入导出符号的主次来说,那么些历程依旧是那多少个耗费时间的。那是熏陶DLL品质的另3个原因

  • Windows
    PE采取了装载时重平素来化解共享对象的地址抵触问题。这几个重一直进程有些与众差别,因为拥有这个须求重一向的地点只必要加上2个定位的差值,也正是说加上一个对象装载地点与实际装载地方的差值。那至关心重视要得益于DLL内部的地方都以依据基地址的,或然就像绝对于营地址的RubiconVA。所以这种重定位进程比一般的重定位要简明,速度更快一些。PE里把那种奇特的重定位进程叫做重定集散地址(Rebasing)

  • MSVC的链接器提供了点名输出文件的集散地址的功效。能够在链接时使用link命令中的“/BASE”参数来钦点营地址。比如:link
    /BASE:0x100100000, 0x10000 /DLL bar.obj

  • Windows系统本人自带很多类其他DLL,基本上Windows的应用程序运转时都要用到。Windows系统就在进度空间中等专业高校门划出一块0x七千0000~0x九千0000区域,用于映射那几个常用的系统DLL。Windows在设置时就把这块地方分配给这一个DLL,调整这一个DLL的集散地址使得它们互相之间不争辩,从而在装载时就不需求展开重定基址了。

  • 每二遍三个程序运营时,全数被正视的DLL都会棉被服装载,并且一层层的导入导出符号正视关系都会被重复分析。在大多数境况下,这一个DLL都会以相同的顺序棉被服装载到平等的内部存款和储蓄器地址,所以它们的导出符号的地点应该都是不变的,既然那一个标记的地方不变,这程序主模块的导入表应该仍旧和上次程序运营时一样,故而能够保存下去,那样就可以省去每回运维时符号解析的进度。那种方法称为DLL绑定。

  • 在PE的导入表中有八个和IAT一样的数组叫做INT正是用来保存绑定符号的地址的。一旦检查和测试到INT里面有消息,则不必要再行展开标记重定位了,假诺赶上难点(如借助的DLL更新,DLL装载顺序打乱了和在此以前装载位置不等同),导致INT中绑定符号音信失效,则也足以正视IAT的新闻再重来三遍重从来。Windows系统中许多连串自带程序便采取DLL绑定用以加快程序运营。

  典型PE文件一般包罗:.text(编写翻译器发生,存放二进制代码,
反汇编和调剂的对象)、.data(开始化数据块)、.idata(使用的外来函数如动态链接库与公事音信)、.rsrc(存放程序财富),还包罗其余如.reloc、.edata、.tls、.rdata等。

+———+———+———+———+———+———+

 

参考小说

Windows下动态链接之二:DLL优化加快
怎么精晓DLL不是地点非亲非故的?DLL与ELF的对待分析

虚拟内部存款和储蓄器:

|  段名称   虚拟地址  虚拟大小  物理地址  物理大小   标志   |

PE文件:

  Windows内部存款和储蓄器:1.大体内部存款和储蓄器层面;2.虚拟内部存款和储蓄器层面。

+———+———+———+———+———+———+

  在WIN上,叁十二位的可执行文件是PE文件,陆16位的是PE32+文件
,DLL文件的格式和PE格式差不离,唯一的界别是PE和DLL的有八个字段标识那一个文件是EXE依旧DLL。

  物理内部存款和储蓄器平日内核级别ring0才能见到;平常用户形式下看见的为Windows用户态内部存款和储蓄器映射机制下的虚拟内存。

|  Name     VOffset    VSize    ROffset    RSize      Flags |

  home88一必发 6

  内部存款和储蓄器管理器能够使进度在实际上唯有512MB物理内部存储器的景况下使进程“认为”自身独具4GB内部存款和储蓄器(个中囊括代码,
栈空间,能源区,动态链接库等)。

+———+———+———+———+———+———+

如上正是四个PE文件的构造图,PE文件使用的是1个平面地址空间,全数的数额都融合在一道,文件的始末又被细分为分歧的区块(Section),

home88一必发 7

|  .text   00001000   00000092  00000400  00000200  60000020|

各样区块按页的分界来对齐。各样块都有投机的属性(是不是可读,是不是可写,是或不是可实施等等)。

  那种情景和实在生活中银行一般,你供给用的钱其实并不等于你有着的财物,银行其实具备的金额数稍低于全数储户的财富和。

|  .rdata  00002000   000000F6  00000600  00000200  40000040|

 

  那万一有用户供给取出超出实际金额数怎么做,操作系统原理中有“虚拟内存”概念,
即在这种场地下有时会将“部分硬盘空间”临时作为内部存款和储蓄器使用。(两者“虚拟内部存款和储蓄器“概念对象分歧,不宜混为一谈)

|  .data   00003000   0000018E  00000800  00000200  C0000040|

基地址:

PE文件与虚拟内部存款和储蓄器之间的炫耀

|  .rsrc   00004000   000003A0  00000A00  00000400  C0000040|

     
当PE文件棉被服装载器装载了后头,内部存款和储蓄器中的板块被称呼模块。映射文件的序曲地址被称之为模块句柄—内部存款和储蓄器中的模块代表那进度从这几个可执行文件中所供给的代码、数据、能源、输入表、输出表及别的东西所采取的东西放在贰个连连的内部存款和储蓄器块中。在装载中,PE文件的贰个字段会告知系统把公文映射到内部存款和储蓄器要求多少内部存款和储蓄器,无法被映射的数据被停放在文件的尾巴。

  (1).文件偏移地址(File Offset):数据在PE文件中的地址,在磁盘上存放时相对于文件开端的偏移。

+———+———+———+———+———+———+

     
在WIN3第22中学,能够采纳HMODULE GetModuleHandle(LPCTSTMuranolpModuleName)来赢得2个模块的名称。当传递2个可执行文件或许DLL作为参数,

  (2).装载基址(Image Base):PE装入内部存款和储蓄器时的营地址。默许EXE文件在内部存款和储蓄器中的营地址为0x00五千00,DLL为0x一千0000。当然地点可由编写翻译选项更改。

文本虚拟偏移地址和文书物理偏移地址的计算公式如下:

如果系统成功找到那些文件,就会回来该可执行文件或然DLL文件影像加载到的营地址。

  (3).虚拟内存地址(Virtual
Address,VA):PE文件中的指令被装入内部存款和储蓄器后的地点。

 

     
在PE文件中,有贰个字符设置了集散地址,VC++建立的exe文件的集散地址是0x00500000h,DLL文件的集散地址是0x一千0000h。

  (4).相对虚拟地址(Relative Virtual
Address,TucsonVA):内部存储器地址绝对于映射基址(即装载基址)的偏移量。

>>>>>>>VaToFileOffset( 虚拟地址转文件偏移地址)

 

  VA、Image Base、大切诺基VA之间涉及:

如VA = 0040一千 (虚拟地址)

抵触虚拟地址:

        VA = Image Base + RVA;

ImageBase = 00400000 (基地址)

   
为了让程序的载入越发的灵敏-也为了在PE文件中冒出有分明的内部存款和储蓄器地址,出现了相对虚拟地址(Relative
Vritual Address, LacrosseVA),当您的顺序加载后,假使你的text块的LANDVA =
0x0000一千h,映射到程序中时,VA(虚拟地址) =
ImagineBase(集散地址)+卡宴VA(相对虚拟地址),你的代码区块在内部存款和储蓄器中就起始与0x00401000h。

  可精通为: 实际 = 基点 + 位移.

VPAJEROk = VOffset – ROffset = 0000一千 – 00000400 = C00 (得出文件虚拟地址和文书物理址之间的VEnclavek值)

 

  暗中同意景况下:一般PE文件的0字节
=》虚拟内部存款和储蓄器0x00陆仟00义务,即所谓的装载地方。

FileOffset = VA – ImageBase – V酷路泽k = 0040一千 – 00伍仟00 – C00 = 400(文件物理地址的撼动地址)

文件偏移地址:

 

 

   
因为大家的文本是储存在磁盘上的,有些数据相对于文件头的偏移量正是以此数量的撼动地址,称为文件偏移地址(File
Offset)或然物理地址(RAW Offset),偏移地址的开首值是0。

  装载PE文件时,文件偏移地址(磁盘上)与中华VVA(内部存款和储蓄器上)有非常的大学一年级致性(操作系统会尽大概保持PE中各数据结构),那同样会有细微差距,由文件数量和内部存款和储蓄器数据的存放单位不一致造成。

如VA = 00401325,则:

 

  在PE文件中,以磁盘数据正式存放(我们知道硬盘以叁个section为中央单位,即512byte),0x200字节,当二个数据节不足0x200字节时填充0x00;

FileOffset = VA – ImageBase – VRk = 00401325 – 00400000 – C00 = 725

MS-DOS头部(IMAGE_DOS_HEADER):

  而在内存中,则以0x一千字节(4byte)为着力单位进行集体,其余与前者类似。

 

   各种PE文件是以多少个DOS程序开首的,还有MZ
header之后的DOS
stub(DOS块)。假使这么些可执行文件无法被那个系统帮助,会打字与印刷一串提醒符

 

>>>>>>FileOffsetToVa( 文件偏移地址转虚拟地址)

“This program cannot be run is MS-DOS
mode”,DOS底部中任重先生而道远是WO中华VD e_magic和 LONG
e_lfanew这么些字段比较首要。那几个数据结构能够在winnt.h中找到。

  在实行File
Offset和VA换算时,会由存款和储蓄单位引起节基址差称为节偏移。比如:

如FileOffset = 435(文件偏移地址)

#define IMAGE_DOS_SIGNATURE 0x5A4D
#define IMAGE_OS2_SIGNATURE 0x454E
#define IMAGE_OS2_SIGNATURE_LE 0x454C
#define IMAGE_VXD_SIGNATURE 0x454C
#define IMAGE_NT_SIGNATURE 0x00004550

#include "pshpack2.h"
//这里就是IMAGE_DOS_HEADER的结构了。
    typedef struct _IMAGE_DOS_HEADER {
      WORD e_magic; // DOS可执行文件标记“MZ”
      WORD e_cblp;
      WORD e_cp;
      WORD e_crlc;
      WORD e_cparhdr;
      WORD e_minalloc;
      WORD e_maxalloc;
      WORD e_ss;
      WORD e_sp;
      WORD e_csum;
      WORD e_ip;       //DOS代码入口IP
      WORD e_cs;      //DOS代码的入口CS
      WORD e_lfarlc;
      WORD e_ovno;
      WORD e_res[4];
      WORD e_oemid;
      WORD e_oeminfo;
      WORD e_res2[10];
      LONG e_lfanew;   // 指向PE文件头, “PE”,0,0
    } IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;

home88一必发 8

VA = FileOffset + ImageBase + VOdysseyk = 435 + 00500000 + C00 = 00401035(虚拟地址)

 LONG
e_lfanew是指向PE文件头的3个地址,它的文件偏移地址是0x3C–也正是60字节开端,然后占据了五个字节,指向PE文件头,具体看下图

  则有:

 

home88一必发 9

home88一必发 10

源文档 <>

我们能够见到0x000000F8在舞狮60字节的地点,看到PE文件头在0x000000F8,具体能够协调找贰个文件来测试(加深圳影业公司象)

  那么就足以估测计算出:

 

home88一必发 11

  FileOffset = VA – Image Base -节偏移

 DOS文件头就到那边了,接下去继续介绍PE头,也便是IMAGE_NT_HEADE奇骏,上边的代码是对此定义32照旧64的PE头,我们可以见见相应的宏语句

        = VA – Image Base – (绝对虚拟偏移量 – 文件偏移量)

和呼应的数据结构定义。

  按上表,比如总计虚拟内存中0x00404141处的一条指令,要换算出该指令在文书中的偏移量:

#ifdef _WIN64             //如果采用64的架构
    typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
    typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER;
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL64_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC
#else  /* _WIN64 */      //如果不是采用64而是32位的架构
    typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
    typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER;
#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL32_HEADER
#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC
#endif /* _WIN64 */ //上面的typedef都是改变结构体的名称

    typedef struct _IMAGE_NT_HEADERS64 {//这里是64位的PE头的结构体的定义
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER64 OptionalHeader;
    } IMAGE_NT_HEADERS64,*PIMAGE_NT_HEADERS64;

    typedef struct _IMAGE_NT_HEADERS {//这里是32位的PE头的结构体的定义
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    } IMAGE_NT_HEADERS32,*PIMAGE_NT_HEADERS32;

      文件偏移量 = 0x00404141 – 0x00陆仟00(暗中认可的Image
Base)-(0x一千 – 0x400)(代码存于.text文件中) = 0x3541

在PE的文件头中,第2个DWOTiggoD Signature,
被定义为了0x00004550h,也正是”PE\0\0″那多少个字符。那几个标志没有啥样效劳。(DOS中针对的地点)。

 

主要是IMAGE_FILE_HEADER和IMAGE_OPTIONAL_HEADEEnclave那三个结构体中的多少个字段首要。

  一些PE工具也提供那类地址转化:Lord PE.

小编么接下去看看PE文件头中的IMAGE FILE_HEADEENCORE FileHeader这几个结构体

 =_=..

typedef struct _IMAGE_FILE_HEADER {
      WORD Machine;                     //这里定义的是运行平台,i386= 0x014Ch这个值,还有其他平台,看书吧。。
      WORD NumberOfSections;       //这个是标识区块的数目,紧跟在PE头的后面,也就是IMAGE_NT_HEADERS的后面
      DWORD TimeDateStamp;
      DWORD PointerToSymbolTable;
      DWORD NumberOfSymbols;
      WORD SizeOfOptionalHeader;     //这里表明了IMAGE_NT_HEADERS中的大小(RAW SIZE),32位一般是0x00E0, 64位PE+一般是0x00F0
      WORD Characteristics;       //普通的EXE是0x010fh, DLL文件是0x210Eh
    } IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;

接下去我们看一下IMAGE_OPTIONAL_HEADE奥迪Q5那一个结构体,一样上面包车型大巴是那些结构体在winnt.h中的定义,上边那些是33个人的,还有60位的,但是基本上的,能够看winnt.htypedef
struct _IMAGE_OPTIONAL_HEADER

typedef struct _IMAGE_OPTIONAL_HEADER {

      WORD Magic;
      BYTE MajorLinkerVersion;
      BYTE MinorLinkerVersion;
      DWORD SizeOfCode;           //这里定义了包含代码区块的大小
      DWORD SizeOfInitializedData;    //这里定义了已经初始化的变量的区块的大小
      DWORD SizeOfUninitializedData;   //这里是未初始化的变量的区块的大小
      DWORD AddressOfEntryPoint;     //这里是程序入口的RVA(相对虚拟地址)
      DWORD BaseOfCode;          //这里是程序代码块的起始RVA
      DWORD BaseOfData;          //这里是数据块起始RVA
      DWORD ImageBase;           //这里是程序默认装入的基地址(ImageBase)
      DWORD SectionAlignment;       //内存中区块的对齐值,非常重要
      DWORD FileAlignment;        //文件中区块的对齐值,非常重要
      WORD MajorOperatingSystemVersion;
      WORD MinorOperatingSystemVersion;
      WORD MajorImageVersion;
      WORD MinorImageVersion;
      WORD MajorSubsystemVersion;
      WORD MinorSubsystemVersion;
      DWORD Win32VersionValue;
      DWORD SizeOfImage;
      DWORD SizeOfHeaders;
      DWORD CheckSum;
      WORD Subsystem;          //这里定义了文件的子系统,图形接口子系统,字符子系统,具体可以看具体的定义
      WORD DllCharacteristics;
      DWORD SizeOfStackReserve;
      DWORD SizeOfStackCommit;
      DWORD SizeOfHeapReserve;
      DWORD SizeOfHeapCommit;
      DWORD LoaderFlags;
      DWORD NumberOfRvaAndSizes;   //这里定义了数据目录表的项数,一直保持为16
      IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //这个是数据目录表,指向输入、输出表、资源块等数据,很重要
    } IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;

 这个IMAGE_OPTIONAL_HEADEPAJERO只须求关爱一些根本字段就行了,记住。。

接下去作者么就看一看那么些数量目录表,数据目录表简单点说正是二个长度为16的IMAGE_DATA_DIRECTORubiconY结构体数组而已

typedef struct _IMAGE_DATA_DIRECTORY {
      DWORD VirtualAddress;            //数据块的其实RVA,很重要
      DWORD Size;             //数据块的长度
    } IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;

那是三个13个人的数组,最有多个数组元素作为保留,全部为0,其余的从起首一向到尾数第二个数据都以已经规定好了的,大家看一下以此数据目录表成员

#define IMAGE_DIRECTORY_ENTRY_EXPORT 0              //Export Table
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1        //Import Table              输入表这里比较重要
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
#define IMAGE_DIRECTORY_ENTRY_TLS 9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
#define IMAGE_DIRECTORY_ENTRY_IAT 12          //IAT (import address table), 这里也很重要
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14

还有也得以动用LordPE的PE
editor来查看那么些目录,今后的查看目录表的工具很多。。。。。

PE的首先等级到此处,接下去会持续攻读,扩大熟稔度。。。。。。。。。。———–好好学习,每二十九日向上!

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图