二进制菜鸟之PE文件结构

所谓PE文件就是可执行文件在硬盘中存储是的文件表示,Windows中可执行文件的文件表示的发展途径为com->LE(Linerar executable)->PE(Portable Executable File Formate)。在程序运行的时候PE文件中的部分内容被载入内存(各种节),这些内容提供了程序运行所需的所有信息,下图展示了PE文件到内存的映射:
内存映射

PE文件结构

下图展示了PE文件的总体结构,在下面小节中将对各个结构进行详细的说明。
PE文件结构

IMAGE_DOS_HEADER

DOS部分是为了兼容DOS系统程序,此部分包括Header和stub两部分,前者是对DOS程序的描述,后者包含了DOS可执行程序,Header头部如下所示:

IMAGE_DOS_HEADER STRUCT
   e_magic		WORD    ;DOS可执行文件标记
   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         WORD   ;DOS代码段偏移指针
   e_cs         WORD   ;DOS代码段基址
   e_lfarlc     WORD
   e_ovno       WORD   ;DOS文件头部到此为此,下述字段为了对其他可执行文件进行扩充,DOS系统对下述字段不解释
   e_res        WORD
   e_oemid      WORD
   e_oeminfo    WORD
   e_res2       WORD
   e_lfanew     DWORD  ;指向PE文件头部(或者LE,LX其他可执行文件头部)
IMAGE_DOS_HEADER ENDS

通过Lord PE工具可以查看程序的IMAGE_DOS_HEADER信息:
IMAGE_DOS_HEADER
DOM可执行文件的识别标志为MZ

DOS Stub

DOS系统下可执行的代码,默认情况下这段代码只会简单的显示一个“This program cannot be run in DOS mode”然后就退出了,具体代码如下图黄框所示(Study PE工具截图)

DOS_STUB上述黄框中部分机器码内容如下:

0E                PUSH    cs
1F                POP     ds
BA0E00            MOV     dx,  0x000E    ;串地址(即This program cannot.....的地址)
B409              MOV     ah, 0x09       ;显示字符串
CD21              INT     0x21           ;中断调用
B8014C            MOV     ax,0x4c01      ;带返回码结束,返回码为1
CD21              INT     0x21

可以使用链接器(link.exe)的/stud参数在链接时指定其他的DOM程序来代替上述提示程序

IMAGE_NT_HEADERS

IMAGE_NT_HEADERS是正式的PE文件头,本部分以8字节为单位对其,详细结构如下所示:

IMAGE_NT_HEADERS STRUCT
   Signature		DWORD                    ;PE文件标识
   FileHeader       IMAGE_FILE_HEADER
   OptionalHeader   IMAGE_OPTIONAL_HEADER32  
IMAGE_NT_HEADERS ENDS

其中Signature的值为0x00004550也就是PE\0\0
IMAGE_NT_HEADERS

IMAGE_FILE_HEADER

IMAGE_FILE_HEADER STRUCT
   Machine                WORD     ;运行平台
   NumberOfSections       WORD     ;文件的节数
   TimeDateStamp          DWORD    ;文件创建日期和时间
   PointerToSymbolTable   DWORD    ;符号表指针(调试用)
   NumberOfSymbols        DWORD    ;符号表中的符号数量(调试用)
   SizeOfOptionalHeader   WORD     ;IMAGE_OPTIONAN_HEADER32结构的长度
   Characteristics        WORD     ;文件属性
IMAGE_FILE_HEADER ENDS

下图是Lord PE软件载入某执行文件后上述结构体的值:
IMAGE_FILE_HEADER

  • Machine:表示运行平台
    Machine列表
  • TimeDateStamp:自1969年12月31日下午4点到文件创建时的秒数
  • SizeOfOptionalHeader:这个值为00e0h
  • Characteristics:这个字段16位(WORD),每一位描述着不同的文件属性
    Characteristics

IMAGE_OPTIONAL_HEADER32

此结构体本意是让开发者能够在PE文件头中使用自定义数据,这个结构体弥补了IMAGE_FILE_HEADER不能够充分描述PE文件属性的不足

IMAGE_OPTIONAL_HEADER32 STRUCT:
   Magic                         WORD                       ;
   MajorLinkerVersion            BYTE                       ;连接器版本号
   MinorLinkerVersion            BYTE                       ;
   SizeOfCode                    DWORD                      ;所有包含代码的节的总大小
   SizeOfInitializedData         DWORD                      ;所有包含已初始化数据的节的总大小
   SizeOfUninitializedData       DWORD                      ;所有包含未初始化数据的节的总大小
   AddressOfEntryPoint           DWORD                      ;程序执行的入口偏移虚拟地址(RVA)
   BaseOfCode                    DWORD                      ;代码节的起始偏移虚拟地址(RVA)
   BaseOfData                    DWORD                      ;数据节的起始偏移虚拟地址(RVA)
   ImageBase                     DWORD                      ;程序建议装载地址
   SectionAlignment              DWORD                      ;内存中的节的对齐粒度
   FileAlignment                 DWORD                      ;文件中的节的对其粒度
   MajorOperatingSystemVersion   WORD                       ;操作系统的主版本号
   MinorOperatingSystemVersion   WORD                       ;操作系统中副版本号
   MajorImageVersion             WORD                       ;可运行于操作系统的最小版本号
   MinorImageVersion             WORD                       ;
   MajorSubsystemVersion         WORD                       ;可运行于操作系统的最小子版本号
   MinorSubsystemVersion         WORD                       ;
   Win32VersionValue             DWORD                      ;未用
   SizeOfImage                   DWORD                      ;内存中整个PE映像的尺寸
   SizeOfHeaders                 DWORD                      ;所有头+节表的大小
   CheckSum                      WORD                       ;
   Subsystem                     WORD                       ;文件的子系统
   DllCharacteristics            DWORD                      ;
   SizeOfStackReserve            DWORD                      ;初始化时堆栈大小
   SizeOfStackCommit             DWORD                      ;初始化时实际提交的堆栈大小
   SizeOfHeapReserve             DWORD                      ;初始化时保留的堆的大小
   SizeOfHeapCommit              DWORD                      ;初始化时实际提交的堆的大小
   LoaderFlags                   DWORD                      ;未用
   NumberOfRvaAndSizes           DWORD                      ;下面的数据目录结构的数量
   DataDirectory                 IMAGE_DATA_DIRECTORY 16 dup;
IMAGE_OPTIONAL_HEADER32 ENDS

下图展示了某程序的IMAGE_OPTIONAL_HEADER32 :
IMAGE_OPTIONAL_HEADER32

  • AddressOfEntryPoint:指出文件被执行时的入口地址,这是一个虚拟偏移地址(RVA)
  • ImageBase:指出程序有限装入的地址,也就是在文件执行时,WIndows优先将程序装入到ImageBase指定的地址中,如果执行地址已被占用,此时文件将会被装入其他地址
    1. EXE文件装入内存的基址为0x00400000DLL文件转入内存的基址为0x10000000,可以通过链接器(link.exe)的/base:address选项自定义优先转入的地址。
    2. EXE文件装入内存时常能如愿的加载到ImageBase指定的地址,因为每个EXE文件都有独立的虚拟地址空间,这也是EXE文件的IMAGE_FILE_HEADER结构的IMAGE_FILE_RELOCS_STRIPPED为常为1的原因;而DLL文件常常不能加载到指定内存,因为一个EXE通常会加载多个DLL,为此只有先加载的DLL才能遂愿,大部分的都不遂愿,这也是DLL文件的IMAGE_FILE_HEADER结构的IMAGE_FILE_RELOCS_STRIPPED为常为0的原因。
    3. 需要重定位的程序往往会比不需要重定位的程序慢的多,因为不需要重定位的程序在生成机器码时相对地址可以事先计算好,而需要重定位的程序不能这样做只有在程序转入运行时才能计算地址。
  • SectionAlignment和FileAlignment
    由于硬盘文件存储时存储的基本单位可能会与内存存储时基本的存储单位不同而导致节偏移的情况,为此:
    文件偏移地址=虚拟内存地址-装载基址-节偏移=RVA(相对虚拟内存偏移地址)-节偏移
  • Subsystem:此字段指定程序使用的界面子系统,这个字段决定程序如何建立初始界面
    可以通过链接器(link.exe)的/subsystem:xxx选项指定这个字段的值。
    subsystem下图是Lord PE工具所能识别的子系统列表:
    Subsystem_Lord
  • DataDirectory: 最重要字段之一,由16个(NumberOfRvaAndSizes字段的值)IMAGE_DATA_DIRECTORY结构组成
    PE文件再装入内存后的页属性而被归类到不同的节中,但是这些处于不同节中的数据还可以按照用途被分类为导出表、导入表、资源、重定位表等数据块,这16个IMAGE_DATA_DIRECTORY就是用来定义多种不同用途的数据块的。也就是说节是按照页属性划分的,而DataDirectory是按照功能用途来划分的,但DataDirectory所指向的数据还是位与节中。
    IMAGE_DATA_DIRECTORY结构如下:
IMAGE_DATA_DIRECTORY STRUCT
	VirtualAddress DWORD     ;数据的起始RVA
	isize          DWORD     ;数据块的长度
IMAGE_DATA_DIRECTORY ENDS

下图展示了这16个IMAGE_DATA_DIRECTORY结构的用途:
DataDirectory下图是通过Lord PE软件查看的某个EXE文件的DataDirectory列表:
DataDirectory_Lord

IMAGE_SECTION_HEADER

在PE文件装入时Windows装载器在装载DOS部分、PE文件头部分和节表部分时不进行任何处理,而在装载节的时候会根据节的属性做不同的处理,一般需要处理以下几个方面内容:

  • 内存页的属性:与节对应的内存页的属性也要按照节的属性来设置,所以同属于一个模块的内存页中,从不同节映射过来的内存页的属性也是不同的。
  • 节的偏移地址:节在磁盘中的对其单位是IMAGE_OPTIONAL_HEADER32结构中FileAlignment指定的,而在内存中对其单位是SectionAlignment字段指定的,二者可能不同。因为内存页的属性是对应于节属性设置的,而Windows系统对内存属性的设定单位为页,为此节的最小对其单位对应于Windows系统内存页的大小,而在磁盘中为了减小文件存储时所占用的空间从而降低了其磁盘中的对齐单位。
  • 节的尺寸
    在节由磁盘转入内存的过程中主要有两个原因影响节的尺寸,其一就是上面提到的由于对齐方式不同可能需要填充从而导致节的尺寸变大,其次就是对于包含未初始化数据的节在磁盘中存储时其尺寸为0,载入内从后Windows装载器根据额外信息将这些节装载到指定内存地址并开辟指定的空间。
  • 不进行映射的节
    有些节包含的数据仅在装载时使用,比如像包含重定向数据的节(.reloc),这些节在装载完成后就没有装入到内存中的价值了,为此这些节仅存在与磁盘文件中而不会被映射到内存页

PE文件所有节的属性均被定义在节表中,节表由一些列(IMAGE_NT_HEADERS.FileHeader.NumberOfSecitons字段指定节表长度)的IMAGE_SECTION_HEADER结构组成,这些结构的排列顺序与他们所描述节的排列顺序一致,节表起始地址位于距离PE文件头(不是文件头)00f8h偏移的位置,IMAGE_SECTION_HEADER结构如下所示:

IMAGE_SCETION_HEADER STRUCT
	Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)        ;8个字节的节区名称
	union Misc
		PhysicalAddress              dd	           ;
		VirtualSize                  dd            ;节的尺寸
	ends
	VirtualAddress                   dd            ;节区的RVA
	SizeofRawData                    dd            ;在文件中对齐后的尺寸
	PointerToRawData                 dd            ;在文件中的偏移
	PointerToRelocations             dd            ;在OBJ文件中使用
	PoninterToLinenumbers            dd            ;行号表的位置(供调试用)
	NumberOfRelocations              dw            ;在OBJ文件中使用
	NumberOfLinenumbers              dw            ;行号表中行号的数量
	Characteristics                  dd            ;节的属性
IMAGE_SCETION_HEADER ENDS

上面有些字段是供COFF格式的OBJ文件使用的,对于可执行文件来说不代表任何意义

  • Name1:此字段由8个字节组成,用于定义节的名称,不要求以0结束,节的名称仅为标识不具有特殊意义
  • VirtualAddress:指定节被装入内存后的偏移地址(RVA)
  • VirtualSize:代表节在没有进行对齐处理前的实际的大小
  • PointerToRawData:指定节在磁盘文件中所处的位置,是相对于文件头的偏移量
  • SizeOfRawData:指出节在磁盘文件中所占空间的大小
  • Characteristics:节的属性标志字段,其不同数据位代表着不同的属性
    下表展示了不同的位所表示的不同属性:
    Characteristics下图展示了某EXE文件.rdada节的属性:
    Characteristics_lord下图是某程序的节表:
    节表

功能数据介绍

前面已经提及过节是按照数据属性进行的分类,DataDirectory是按照数据功能进行的分类。本节中介绍导入表,导出表,资源,重定位表四个重要的DataDirectory。

导入表

导入表中保存了导入函数的相关信息(导入函数名,其驻留的DLL),所谓导入函数就是代码中调用了这些函数而这些函数并没有在本代码中定义而是从一个或多个DLL中导入进来的函数。当PE文件位与磁盘上时无法得知导入函数会位与内存的哪个位置,只有PE文件被装入内存时,Windows转载器才会将DLL装入并将导入函数的指令与函数实际所处的地址联系起来,这就是动态链接的概念。
导入表由一系列的IMAGE_IMPORT_DESCRIPTOR结构组成,这个结构与导入的DLL文件一 一对应,最后需要一个内容全为0的IMAGE_IMPORT_DESCRIPTOR结构作为结束

IMAGE_IMPORT_DESCRIPTOR STRUCT
	union
		Characteristics       dd         
		OriginalFirstThunk    dd
	ends
	TimeDateStamp             dd
	ForwarderChain            dd
	Name1                     dd       ;指向DLL文件名称的RVA
	FirstThunk                dd               
IMAGE_IMPORT_DESCRIPTOR ENDS

OriginalFirstThunk和FirstThunk字段含义均指向一个包含一系列IMAGE_THUNK_DATA结构的数据,每个IMAGE_THUNK_DATA结构对应于一个导入函数信息,数组最后由一个全0的IMAGE_THUNK_DATA结构收尾,此结构(双字大小)具体如下:

IMAGE_THUNK_DATA STRUCT
	union u1
		ForwarderString   dd
		Function          dd
		Ordinal           dd
		AddressOfData     dd
	ends
IMAGE_THUNK_DATA ENDS

由于上述结构采用union结构所以其实其就是一个双字类型,当这个双字表示了函数的导入方式:

  • 最高位为1,表明以序号方式导入,双字其余低位就表示者函数的序号
  • 最高位为0,表示以函数名方式导入,此时双字的值是一个RVA,其指向一个用来定义导入函数名称的IMAGE_IMPORT_BY_NAME结构,此结构的定义如下:
IMAGE_IMPORT_BY_NAME STRUCT
	Hint            dw           ;表示函数序号,此字段可选
	Name1           db			 ;定义了导入函数的名称字符串,以0结尾的字符串
IMAGE_IMPORT_BY_NAME ENDS

下图展示了装入内存前PE文件中的导入表,此表表示从Kernel32.dll导入四个函数(前三个以函数名方式导入,最后那个以序号方式导入):
导入表
下图展示了装载进内存后上述导入表的状况:
内存中的导入表导入内存后FirstThunk指向的那个数组中的每个双字均被替换为真正的函数地址,OriginalFirstThunk与FirstThunk对照使用可以由地址得知函数名称,也可以由函数名或者序号查询函数在内存中的地址。
之前说个IMAGE_IMPORT_DESCRIPTOR结构与DLL文件一一对应,这些结构中FirstThunk指向的地址数组在内存中是被排列在一起的,这一内存块称之为导入地址表(Import Address Table,IAT),导入表中第一个IMAGE_IMPORT_DESCRIPTOR结构的FirstThunk字段指向了IAT的起始地址,除此之外DataDirectory结构中第13项IMAGE_DATA_DIRECTORY 结构也指向了IAT的起始地址。

导出表

除了导入表还有导出表,导入表指的是程序运行时要从DLL中导入的函数信息,而导出表则表示着DLL文件中函数的导出信息,主要用于修正IAT,导出表包含了函数的名称、序号以及入口地址等信息。EXE文件一般不存在导出表,大部分的DLL文件都包含导出表,通过导出表可以看DLL中包含那些函数。下图展示了一个DLL文件的导入与导出函数信息:
导入导出
导出表中为每个导出函数定义了导出序号,但是函数名的定义是可选的,导出表只有一个IMAGE_EXPORT_DIRECTORY结构,此结构定义如下:

IMAGE_EXPORT_DIRECTORY STRUCT
	Characteristics               DWORD           ;未使用,总是为0
	TimeDateStamp                 DWORD           ;文件的创建时间
	MajorVersion                  WORD            ;未使用,总是为0
	MinorVersion                  WORD            ;未使用,总是为0
	nName                         DWORD           ;指向文件名的RVA
	nBase                         DWORD           ;导出函数的起始序号
	NumberOfFunctions             DWORD           ;导出函数的总数
	NumberOfNames                 DWORD           ;有名称的导出函数总数
	AddressOfFunctions            DWORD           ;指向导出函数地址表的RVA
	AddressofNames                DWORD           ;指向函数名地址表的RVA
	AddressOfNameOrdinals         DWORD           ;指向函数名序号表的RVA
IMAGE_EXPORT_DIRECTORY ENDS

下图展示了导出表及相关数据的结构及关系(nBase:x):
导出表不明白的是函数的入口RVA在载入内存之前的磁盘文件中就存在了,为什么?RVA不是内存中才有的概念吗?难道是.text是第一个段所以其前面的头部载入时不会发生相对偏移?

资源

资源结构

  • PE文件中的资源如上图所示按目录结构组织,所有的目录结构及资源数据都几种的放在一个资源数据块中
  • 这个数据块的RVA由IMAGE_OPTIONAL_HEADER32.DataDirectory.Resource.VitrualAddress指定,而这个起始地址也对应着上图中的更目录首地址,依此就可以一层层的访问资源
  • 需要注意的是尽管资源一般情况下在**.rsrc**节中但并非必然,为此不可以通过这个节来对资源进行访问

接下来对资源涉及到的几种数据结构进行介绍,首先介绍IMAGE_RESOURCE_DIRECTORY,此结构包含了本目录的各种属性信息,具体结构如下:

IMAGE_RESOURCE_DIRECOTRY STRUCT
	Characteristics      dd                 ; 理论上为资源的属性,不过通常总是为0
	TimeDateStamp        dd                 ; 资源的产生时刻
	MajorVersion         dw                 ; 理论上为资源的版本,不过事实上总是为0
	MinerVersion         dw                 ;
	NumberOfNameEntries  dw                 ; 以名称命名的入口数量
	NumberOfIdEntries    dw                 ; 以ID命名的入口数量
IMAGE_RESOURCE_DIRECTORY ENDS

上述结构中后两项说明了本目录中目录项的数量,这两者相加是本目录下目录项的总和,接下来了解下IMAGE_RESOURCE_DIRECTORY_ENTRY结构:

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT
	Name1           dd         ; 
	OffsetToData    dd         ; 
IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS

Name1字段字段在不同情况下有不同的含义,具体如下所示:

  • 资源类型:处于上图第一层目录时,31位为0时低位表示ID类型资源,系统预定义资源的ID表如下图所示,当ID值大于16时为用户自定义的ID资源
    资源ID
  • 指向目录项名称的指针或者目录项ID:处于上图第二层目录。第31位为0时低位表示ID;第31位为1时,低位为指针其所指的并不是字符串(因为项目名称是Unicode编码)而是指向一个IMAGE_RESOURCE_DIR_STRING_U结构体
IMAGE_RESOURCE_DIR_STRING_U STRUCT
	Length1        dw                 ;字符串长度
	NameString     dw     dup(?)      ;dup(?)代表重复?次,因为Unicode字符串长度不固定
IMAGE_RESOURCE_DIR_STRING_U ENDSUnicode字符串
  • 代码编号:处于上图第三层目录时

OffsetToData字段在不同情况下有不同的含义,具体如下所示:

  • 目录项指针:处于一二层目录结构时,此项高位为1,低位指向一个IMAGE_RESOURCE_DIRECTORY结构
  • 资源项指针:处于第三层是此项高位为0,低位指向一个IMAGE_RESOURCE_DATA_ENTRY结构
IMAGE_RESOURCE_DATA_ENTRY STRUCT
	OffsetToData            dd              ;资源数据的RVA
	Size1                   dd              ;资源数据的长度
	CodePage                dd              ;代码页,实际之中好像未被使用,总是为0
	Reserved                dd              ;保留字段
IMAGE_RESOURCE_DATA_ENTRY ENDS

需要注意的是上述结构中的指针含义均是相对于资源根目录起始地址的偏移量
下面结合Winhex与stud_PE分析下某程序的资源信息:

  • 首先使用Stud_PE工具在DataDirectory找到Resource的RVA,如下图所示:
    resource
  • 接下来观察更具节信息将RVA转化为磁盘中的偏移地址,转化的算法大概如下伪代码所示:
for i in 节表:
	if RVA>i.VirtualAddress and RVA < i.VirtualAddress+i.sizeOfRawData:
		文件偏移 = i.PointerToRawData+(RVA-i.VirtualAddress)

节信息如下,按上述算法资源处于.rsrc节中且资源的起始地址刚好是这个节的起始地址,为此可以判断出资源在文件中的偏移为0x000FE000
.rsc

  • 使用WinHex打开此程序并定位到0x000FE000的位置,如下图所示:
    资源结构解析上图中资源信息IMAGE_RESOURCE_DATA_ENTRY.CodePage并不为0与之前介绍的有出入

重定位表

重定位上述代码中第5行使用了直接寻址的方式,在此应用程序装入预设的0x0040000地址时这中寻址方式直接出现在代码中没有问题,如果程序没有载入预设地址那怎么办呢?重定位就是来解决这个问题的,32位的代码中涉及直接寻址的指令都是需要重定位的,16的DOS中只有涉及段操作的指令才需要重定位,重定位信息是在编译时有编译器生成并保存在可执行文件中的。重定位具体操作如下:
重定位后地址 = 代码中出现的直接寻址地址-预装地址(0x00400000)+代码实际装入地址
上述三个操作数中预装地址已经在IMAGE_OPTIONAL_HEADER32.ImageBase字段定义了,而代码的实际装入地址也在代码装入是由Windows装载器确定,为此只需要将出现直接寻址地址的代码地址放入重定向表中就能够准确的进行代码重定向操作了。通过DataDirectory中的第六项可以定位重定位表的位置,重定位表通常保存在**.reloc**节中但是在此强调一次,通常但不是必须为此还是通过DataDirectory来寻找重定位表的位置比较靠谱。还需要说明的是重定向表一般不会再程序运行时装入内存,其仅在程序装载时辅助装载器对程序进行重定位操作。为了节约存储空间,重定位表在保存时采用了这样的存储方式即按页保存重定位代码地址,首先保存一个4字节的内存页起始地址,再保存一个此页内重定位代码的项数,最后保存所有的需要重定位操作的代码地址相对于页起始地址的偏移(页内寻址为12位,这里扩展到16位)。这样存储一个页内的所有重定位代码信息就需要4+4+2n字节,这种存储方式在内存页内重定位项数多于4时优于直接存储每个需要重定位代码的地址(4n)。按照这个逻辑关系每个内存页对应着一个重定位块这个块以IMAGE_BASE_RELOCATION开头:

IMAGE_BASE_RELOCATION STRUCT
	VirtualAddress        dd          ;页起始RVA
	SizeOfBlock           dd          ;重定位块的长度 4+4+2*n
IMAGE_BASE_RELOCATION ENDS

上面提到要把12位的页内地址扩展到16位,低12位是原来的地址,高四位也被赋予了具体的函数,如下表所示:
高四位
上述表中0和3是最为常见的情况,所有的重定位块终于一个VirtualAddress字段全0的IMAGE_BASE_RELOCATION结构,这时候问题来了,那么RVA为0的内存页中需要重定位的代码如何解决呢?为了解决这个问题所有的可执行文件的代码总是从装入地址的0x1000处开始定义,如下图所示:
1000h
下面结合Winhex与stud_PE分析下某DLL文件重定位表:

  • 首先使用Stud_PE工具在DataDirectory找到重定位表的RVA,如下图所示:
    DLL_重定位表
  • 接下来观察更具节信息将RVA转化为磁盘中的偏移地址
    RVAtoFileoffset为此可以确定重定位表在文件中的偏移地址为0x00191000
  • 使用WinHex打开此程序并定位到0x00191000的位置,如下图所示:
    重定位表
    按上图划线部分可以知6个需要重定向代码的RVA为0xC6000、0xC62A8、0xC646F、0xC64B0、0xC64CC、0xC64F2。可以将这些RVA转换为文件偏移去文件中寻找相应指令的机器码,但是不可以使用OD等动态分析工具将文件载入然后去相应的RVA位置查看指令,因为载入后已经完成重定向,里面的代码是重定向后的指令。

总结

  • PE文件中所有数据都存储的节表中,而这些节表中的数据由于功能不同还被划分到其他的逻辑结构比如DataDirectory。
  • RVA及物理内存通过节来转换,但是并非所有的RVA都能成功转化为文件偏移地址
for i in 节表:
	if RVA>i.VirtualAddress and RVA < i.VirtualAddress+i.sizeOfRawData:
		文件偏移 = i.PointerToRawData+(RVA-i.VirtualAddress)
  • 大小端是对于数据来说的,而不是指令
  • 操作系统识别可执行文件的方法是依赖于文件头而不是文件扩展名,如果当前文件格式不符合任何已定义文件头格式则将其按照COM文件格式装入即将整个文件的数据当做代码装入执行
  • 节是按照数据属性进行的分类,DataDirectory是按照数据功能进行的分类
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值