《软件漏洞分析技术》笔记

RVA是冲突虚拟地址(Relative Virtual
Address)的缩写,顾名思义,它是一个“相对”地址,也可以说是“偏移量”,PE文件的各个数据结构中涉及到地点的字段一大半都是以RVA代表的。

此次的垂询紧要教学
PE的基本概念、MS-DOS文件头、PE文件头、区块、输入表、输出表等。

装载与动态链接

PE文件:


确地说,RVA就是当PE文件被装载到内存中后,某个数据的地点相对于文件头的偏移量。举个例子,若是Windows装载器将一个PE文件装入
00400000h处的内存中,而某个节中的某个数据被装入0040xxxxh处,那么这一个数目标RVA就是(0040xxxxh-
00400000h)=xxxxh,反过来说,将RVA的值加上文件被装载的营地址,就足以找到数据在内存中的其实地址。

此处自己将会构成一个简单的小程序来深化我对PE文件结构的垂询。

可执行文件的装载与经过

  • 各类程序都具有和谐单身的虚拟地址空间,那几个空间大小由总括机硬件平台决定(理论上的最大上限)。比如,32位硬件平台的虚拟地址空间的地址为0到232-1,即0x000000000xFFFFFFFF,总共大概4G;而64位硬件平台的虚拟地址空间地址为0到2<sup>64</sup>-1,即0x00000000000000000xFFFFFFFFFFFFFFFF,大约有17179869184G。在32位平台上,Linux操作系统中4G的虚拟地址空间会被剪切为八个部分,从0xC0000000到0xFFFFFFFF共1G的地方空间被分配给了操作系统,剩下的从0x00000000到0xBFFFFFFF共3G的地址空间是留下进度的。从原则上讲,大家经过最多能使用3G的虚拟地址空间。对于Windows操作系统来说,它的长河虚拟地址空间划分是操作系统占用2G,进程只剩余2G。对于一些顺序来说2G虚拟空间太小,所以Windows有个启动参数可以将操作系统占用的虚拟地址空间压缩到1G。方法如下:修改Windows系统盘根目录下的boot.ini,加上“/3G”参数。

澳门金沙国际 1

  • 动态装载的三种典型方式是覆盖装入和页映射,覆盖装入在未曾表明虚拟存储从前使用比较宽泛,现在早已大概被淘汰了。页映射简而言之就是操作系统将次第需求利用的页按一定的算法动态映射到大体内存中执行。

  • 从操作系统的角度来看,一个进程最重点的特点是它装有独立的虚拟地址空间,那使得它有别于其余进度。一个经过的确立有三步:

    • 率先是创建虚拟地址空间。
    • 读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系。(可执行文件装载中最根本的一步,也是传统意义上的“装载”)
    • 将CPU指令寄存器设置成可执行文件入口,启动运行。
  • 俺们精晓,当程序执行暴发页错误时,操作系统将从物理内存中分配一个物理页,然后将该“缺页”从磁盘中读取到内存中,再设置缺页的虚拟页和物理页的映照关系,那样程序才足以正常运行。可是很强烈的某些是,当操作系统捕获到缺页错误时,它应清楚程序当前所要求的页在可执行文件中的哪一个地方。那就是虚构空间与可执行文件之间的映射关系。

澳门金沙国际 2

  • ELF文件被映射时,是以种类的页长度作为单位的。为防止内存浪费,操作系统在装载可执行文件时首要关切的只是文件主题的权柄(可读、可写、可实施)。对于同一权限的段,把它们统一到一道作为一个段举办映射。Linux中校进度虚拟空间中的一个段叫做虚拟内存区域(VMA),在Windows中校以此叫做虚拟段(Virtual
    Section)。很多场地下,一个经过中的堆和栈分别都有一个对应的VMA。操作系统在经过启动前会将系统的环境变量和经过的运转参数提前保存到进程的虚构空间的栈中(也就是VMA中的stack
    VMA)。

  • PE文件的装载和ELF有所差距,在PE文件中,所有段的开端地址都是页的倍数,段的长度假诺不是页的平头倍,那么在炫耀时向上补齐到页的整数倍。由于那些特点,PE文件的映照进程比ELF简单得多,因为它无需考虑如ELF里面诸多段地址对齐之类的难题,尽管如此会浪费一些磁盘和内存空间。

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

  • 各种PE文件在装载时都会有一个装载目标地方,这么些地址就是基地址,基地址不是原则性的,每便装载时都可能会变卦。所以PE文件中有一个常见术语叫相对虚拟地址(RVA),它是绝对于PE文件的装载营地址的一个偏移地址。这样无论基地址怎么变卦,PE文件中的种种RVA都保持一致。

  • WIndows PE文件的装载进度:

    • 先读取文件的第三个页(包括DOS头,PE文件头和段表)。
    • 自我批评进程地址空间中,目的地址是还是不是可用,假如不可用,则其它选一个装载地方。(紧要针对DLL装载)
    • 选取段表中提供的音信,将PE文件中有所的段一一映射到地点空间中相应的地方。
    • 比方装载地方不是目的地方,则进行Rebasing
    • 装载所有PE文件所急需的DLL文件。
    • 对PE文件中的所有导入符号举行辨析。
    • 据悉PE头中指定的参数,建立开始化堆和栈。
    • 制造主线程并且启动进程。

  PE(Portable
Executable)是win32阳台下可举办听从的多少格式。寻平日见的比如*澳门金沙国际 ,.exe和*.dll都是PE文件。

PE文件中出现RVA的
概念是因为PE的内存影像和磁盘文件映像是见仁见智的,同一数据相对于文件头的偏移量在内存中和在磁盘文件中恐怕是分化的,为了提高功能,PE文件头中使用的
都是内存印象中的偏移量,也就是RVA。从图17.3中也足以收获另一个定论,那就是RVA仅仅是对此处在节中的数据而言的,对于文本头和节表来说无所谓
RVA和文书偏移,因为它们在被映射到内存中后不论是大小或者摇头都不会有任何改动。

 

动态链接

  • 为何要动态链接?

    • 静态链接的艺术对于电脑内存和磁盘的半空中浪费卓殊严重。
    • 静态链接对于程序的创新、计划和揭露也会拉动很多劳动。
  • 在Linux系统中,ELF动态链接文件被改为动态共享对象(DSO),简称共享对象,它们一般都是以“.so”为扩大名的一部分文书;而在Windows系统中,动态链接文件被改成动态链接库(DLL),它们日常是以“.dll”为扩大名的公文。

  • 静态链接的重定位叫链接时重一向(Link 提姆e
    Relocation),而动态链接的重定位为装载时重一向(Load 提姆e
    Relocation),在Windows中,那种装载时重一向又被称呼基址重置(Rebasing)。在Linux和GCC中如若使用“-shared”参数,输出的共享对象就是利用的装载时重一向。

  • 把指令中这个必要修改的一部分分离出来,跟数据部分放在一块儿,那样指令部分就可以保险不变,而数据部分可以在每个进程中具有一个副本,这种方案就是地方无关代码(PIC)技术。在Linux共享对象中要转移地址非亲非故代码只用在编译是带上参数-fPIC。

澳门金沙国际 3

  • 地方的景况并从未包蕴定义在共享模块内部的全局变量。ELF共享库在编译时,默许都把定义在模块内部的全局变量当做定义在此外模块的全局变量,也就是说当做上图中的类型(4),通过GOT来完毕变量的访问。当共享模块被装载时,假若某个全局变量在可执行文件中负有副本,那么动态链接器就会把GOT中的相应地方指向该副本,那样该变量在运转时实际上最后就只有一个实例。假诺变量在共享模块中被开首化,那么动态链接器还要求将该初叶化值复制到主模块中的变量副本;假设该全局变量在先后主模块中一贯不副本,那么GOT中的相应地方就本着模块内部的该变量副本。

  • 对于共享对象的话,即便数据段中有相对地址引用,那么编译器和链接器就会暴发一个重定位表,这几个重定位表里面含有了“R_386_RELATIVE”类型的重定位入口。当动态链接器装载共享对象时,即使发现该共享对象有如此的重定位入口,那么动态链接器就会对该共享对象开展重一直。

  • 我们在编译共享对象时若是应用“-fPIC”参数,就象征要爆发地址无关的代码段。GCC编译动态链接的可执行文件会默许带上该参数的。假设不接纳该参数就会时有暴发一个装载时重一直的共享对象,它的代码段就不是地点毫无干系的,也就不可以被多少个进程之间共享,于是就失去了节约内存的独到之处。不过装载时重平素的共享对象的运转速度要比使用地方毫不相关代码的共享对象快,因为它省去了地方无关代码中老是访问全局数据和函数时须求做一回总括当前位置以及直接地址寻址的进度。

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

  • ELF接纳延迟绑定来优化动态链接品质,基本考虑是当函数第三回被用到时才开展绑定(符号查找、重平素等)。具体方法是行使了PLT(Procedure
    Linkage
    Table)。PLT为GOT间接跳转又追加了一个中间层,在调用某个外部模块的函数时,并不直接通过GOT跳转,而是通过一个叫作PLT项的布局来拓展跳转。每个外部函数在PLT中都有一个相应的项。(汇编指令完成)

澳门金沙国际 4

  • 其实的PLT基本社团代码如下:

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

...

bar@plt:
jmp *(bar@GOT)
push n
jump PLT0
  • 在动态链接情形下,操作系统在装载完可执行文件之后会先启动一个动态链接器,之后就将控制权交给动态链接器的进口地址。当动态链接器得到控制权之后,它开首执行一连串自身的起首化操作,然后依据近期的环境参数,早先对可执行文件举办动态链接工作。当有着动态链接工作成就未来,动态链接器会将控制权交到可执行文件的输入地址,程序开头正式施行。

  • 动态链接相关协会

    • “.interp”段:里面保存的就是一个字符串,那么些字符串就是可执行文件所急需的动态链接器的路子。
    • “.dynamic”段:ELF文件中最要害的协会,保存了看重于怎么样共享对象、动态链接符号表的义务、动态链接重定位表的职位、共享对象初叶化代码的地址等音讯。
    • “.dynsym”段:动态符号表,表示动态链接模块之间的号子导入导出关系。
    • “.rel.dyn”段:数据援引重一直,改进“.got”以及数据段。
    • “.rel.plt”段:函数引用重一向,革新“.got.plt”。
  • 动态链接基本上分为3步:先是启动动态链接器本身(自举,bootstrap),然后装载所有要求的共享对象,最终是重向来和初步化。(跳转)

  • 落成要旨自举未来,动态链接器将可执行文件和链接器本身的标志表都合并到一个大局符号表中。在Linux中,当一个标志须求被投入全局符号表时,借使相同的符号名已经存在,则后进入的号子被忽略(全局符号出席难点)。

  • 当下边的步调达成未来,链接器早先再度遍历可执行文件和各类共享对象的重定位表,将它们的GOT/PLT中的每个需求重一直的职位举办改正。

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

加密与解密3,装载与动态链接。 

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

Windows下的动态链接

  • 在ELF中,由于代码段是地方毫不相关的,所以它能够兑现八个经过之间共享一份代码,但是DLL的代码却并不是地点无关的,所以它只是在某些意况下可以被多个进程间共享。

  • PE文件里有五个常用的概念就是基地址(Base
    Address)
    周旋地址(RVA,Relative Virtual
    Address)
    。营地址就是PE头文件中的Image
    Base,是PE文件被装载进进度地址空间中的先导地址,
    对于EXE文件来说,其值一般是0x400000,对于DLL文件来说,其值一般是0x10000000。而相对地址就是一个地点相对于营地址的偏移。

  • ELF默许导出装有的大局符号。不过在DLL中,我们须求显式地“告诉”编译器我们需要导出某个符号,否则编译器默许所有符号都不导出。在VC++中,我们运用“__declspec(dllexport)”表示DLL导出符号,使用“__declspec(dllimport)”表示DLL导入符号。除了选取导出导入符号外,大家也可以利用“.def”文件中的IMPORT或者EXPORTS段来声称导入导出符号。那一个主意不但对C/C++有效,对其他语言也实惠。

  • 使用.def文件来叙述DLL文件导出属性的独到之处有七个,一是足以控制导出符号的标记名,而是可以决定一些链接的长河。

  • Windows提供3个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;

澳门金沙国际 5

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

  • 导出地址表中存放的是各种导出函数的RVA,符号名表中存放的是导出函数的名字。序号表实际是最初16位windows为了回应内存小而使用的体制。使用序号导入导出的利益就是节约了函数名查找进度,函数名表也不要求保留到内存中。可是它最大的题材就是一个函数的序号可能会转移。那就要求程序员手工指定每个导出函数的序号。由于当下硬件品质的升迁,这种内存空间的节约和搜索速度的升级效益就不显明了。所以现在那种方法基本就不利用了,不过为了维持向后非常,它如故被封存了下去。

  • 动态链接器怎样查找函数RVA呢?即使模块A导入了Math.dll中的Add函数,那么A的导入表中就封存了“Add”那个函数名。当进行动态链接时,动态链接器在Math.dll的函数名表中举行二分查找,找到“Add”函数,然后在名字序号对应表中找到“Add”所对应的序号,即1,减去Math.dll的Base值1,结果为0,然后在EAT中找到下标0的因素,即“Add”的RVA为0x1000。

  • 在ELF中,“.rel.dyn”和“.rel.plt”五个段中分头保存了该模块所需求导入的变量和函数的记号以及所在的模块等新闻,而“.got”和“.got.plt”则保留着那几个变量和函数的真的地址。Windows中也有类似机制,叫做导入表(Import
    Table)
    。当某个PE文件被加载时,Windows加载器的中间一个义务就是将具备必要导入的函数地址确定并且将导入表中的要素调整到科学的地点,以完毕动态链接的进度。

  • 导入表是一个IMAGE_IMPORT_DESCRIPTOR结构体数组,每一个IMAGE_IMPORT_DESCRIPTOR结构对应一个被导入的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中各类元素对应一个被导入的标记,元素的值在差别的情景下有不一样的含义。在动态链接器刚已毕映射还从未伊始重平素和标记解析时,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下,任何一个PE文件在编译时都会付出自己的一个先行装载地点,然后依据此岗位暴发一各样的定势,当然这些相对地址是要求在其实装载运行时再重新考订的,接纳了一种重定基地址的措施)。

  运行时操作系统会按PE文件的约定定位资源并装载入内存。可执行文件
——>拆分——>若干数目节<——差别的资源。

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

 学习PE建议看书。。和团结出手。。。

DLL优化

  • DLL的代码段和数据段本身并不是地方非亲非故的,也就是说它默许须求被装载到由ImageBase指定的对象地址中。假设目的地点被挤占,那么就必要装载到任何地方,便会挑起一切DLL的Rebase。那对于拥有大量DLL的次第来说,频仍的Rebase也会招致程序启动缓慢。这是熏陶DLL质量的一个缘由

  • 动态链接进程中,导入函数的记号在运作时索要被逐个解析。在那几个分析进程中,免不了涉及到符号字符串的可比和摸索进程,这么些查找进度中,动态链接器会在对象DLL的导出表中举办符号字符串的二分查找。即便是选拔了二分查找法,对于有着DLL数量过多,并且有大气导入导出符号的次序来说,这一个进程照旧是分外耗时的。那是潜移默化DLL质量的另一个缘由

  • Windows
    PE接纳了装载时重平昔来缓解共享对象的地点争持难点。那一个重一向进度有些非凡,因为所有那一个需求重一直的地点只须要添加一个原则性的差值,也就是说加上一个目标装载地方与实际装载地方的差值。那主要得益于DLL内部的地点都是基于基地址的,或者就如相对于基地址的RVA。所以那种重定位进度比相似的重定位要简单,速度更快一些。PE里把那种独特的重定位进程叫做重定营地址(Rebasing)

  • MSVC的链接器提供了点名输出文件的营地址的功力。可以在链接时利用link命令中的“/BASE”参数来指定基地址。比如:link
    /BASE:0x100100000, 0x10000 /DLL bar.obj

  • Windows系统本身自带很多连串的DLL,基本上Windows的应用程序运行时都要用到。Windows系统就在进程空间中特地划出一块0x70000000~0x80000000区域,用于映射这么些常用的系统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上,32位的可执行文件是PE文件,64位的是PE32+文件
,DLL文件的格式和PE格式几乎,唯一的界别是PE和DLL的有一个字段标识那一个文件是EXE依然DLL。

  物理内存寻常内核级别ring0才能观察;平常用户方式下看见的为Windows用户态内存映射机制下的虚拟内存。

|  Name     VOffset    VSize    ROffset    RSize      Flags |

  澳门金沙国际 6

  内存管理器可以使进度在骨子里唯有512MB物理内存的景况下使进度“认为”自己具有4GB内存(其中包蕴代码,
栈空间,资源区,动态链接库等)。

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

如上就是一个PE文件的布局图,PE文件使用的是一个平面地址空间,所有的多寡都融合在联名,文件的情节又被剪切为不一致的区块(Section),

澳门金沙国际 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文件中的地址,在磁盘上存放时相对于文件开端的撼动。

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

     
在WIN32中,可以应用HMODULE GetModuleHandle(LPCTSTR
lpModuleName)来收获一个模块的称呼。当传递一个可执行文件或者DLL作为参数,

  (2).装载基址(Image Base):PE装入内存时的基地址。默许EXE文件在内存中的营地址为0x00400000,DLL为0x10000000。当然地方可由编译选项更改。

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

即使系统成功找到那个文件,就会重回该可执行文件或者DLL文件映像加载到的营地址。

  (3).虚拟内存地址(Virtual
Address,VA):PE文件中的指令被装入内存后的地址。

 

     
在PE文件中,有一个字符设置了基地址,VC++建立的exe文件的基地址是0x00400000h,DLL文件的基地址是0x10000000h。

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

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

 

  VA、Image Base、RVA之间关系:

如VA = 00401000 (虚拟地址)

相持虚拟地址:

        VA = Image Base + RVA;

ImageBase = 00400000 (基地址)

   
为了让程序的载入尤其的灵敏-也为了在PE文件中冒出有确定的内存地址,出现了相对虚拟地址(Relative
Vritual Address, RVA),当您的顺序加载后,假诺你的text块的RVA =
0x00001000h,映射到程序中时,VA(虚拟地址) =
ImagineBase(基地址)+RVA(相对虚拟地址),你的代码区块在内存中就发轫与0x00401000h。

  可了然为: 实际 = 基点 + 位移.

VRk = VOffset – ROffset = 00001000 – 00000400 = C00 (得出文件虚拟地址和文件物理址之间的VRk值)

 

  默许情状下:一般PE文件的0字节
=》虚拟内存0x00400000地点,即所谓的装载地方。

FileOffset = VA – ImageBase – VRk = 00401000 – 00400000 – C00 = 400(文件物理地址的舞狮地址)

文本偏移地址:

 

 

   
因为大家的公文是储存在磁盘上的,某个数据相对于文件头的偏移量就是其一数目标晃动地址,称为文件偏移地址(File
Offset)或者物理地址(RAW Offset),偏移地址的开端值是0。

  装载PE文件时,文件偏移地址(磁盘上)与RVA(内存上)有很大一致性(操作系统会尽力而为有限支撑PE中各数据结构),那无异会有细微差别,由文件数量和内存数据的存放单位差距造成。

如VA = 00401325,则:

 

  在PE文件中,以磁盘数据正式存放(大家通晓硬盘以一个section为着力单位,即512byte),0x200字节,当一个数据节不足0x200字节时填充0x00;

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

MS-DOS头部(IMAGE_DOS_HEADER):

  而在内存中,则以0x1000字节(4byte)为骨干单位开展团队,其余与前者类似。

 

   每个PE文件是以一个DOS程序初阶的,还有MZ
header之后的DOS
stub(DOS块)。如若这么些可执行文件不可以被那几个系统扶助,会打印一串提醒符

 

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

“This program cannot be run is MS-DOS
mode”,DOS尾部中重大是WORD 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;

澳门金沙国际 8

VA = FileOffset + ImageBase + VRk = 435 + 00400000 + C00 = 00401035(虚拟地址)

 LONG
e_lfanew是指向PE文件头的一个地点,它的公文偏移地址是0x3C–也就是60字节开头,然后占据了4个字节,指向PE文件头,具体看下图

  则有:

 

澳门金沙国际 9

澳门金沙国际 10

源文档 <>

大家能够看来0x000000F8在摆动60字节的地点,看到PE文件头在0x000000F8,具体可以团结找一个文书来测试(加深映像)

  那么就足以测算出:

 

澳门金沙国际 11

  FileOffset = VA – Image Base -节偏移

 DOS文件头就到此处了,接下去继续介绍PE头,也就是IMAGE_NT_HEADER,上面的代码是对此定义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 – 0x00400000(默许的Image
Base)-(0x1000 – 0x400)(代码存于.text文件中) = 0x3541

在PE的文件头中,第二个DWORD Signature,
被定义为了0x00004550h,也就是”PE\0\0″那多个字符。这一个标志没有何效益。(DOS中针对的地点)。

 

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

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

我么接下去看看PE文件头中的IMAGE FILE_HEADER 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_HEADER那些结构体,一样上边的是其一结构体在winnt.h中的定义,上边那个是32位的,还有64位的,不过基本上的,可以看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_HEADER只须要关爱一些重中之重字段就行了,记住。。

接下去我么就看一看那个数额目录表,数据目录表简单点说就是一个长度为16的IMAGE_DATA_DIRECTORY结构体数组而已

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

那是一个16位的数组,最有一个数组元素作为保留,全体为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的第一阶段到那里,接下去会两次三番求学,增添熟谙度。。。。。。。。。。———–好好学习,每日向上!

 

相关文章