源代码如下:

源代码如下:

澳门金沙国际 1

ELF&PE 文件结构解析

说简练点,ELF 对应于UNIX 下的文书,而PE 则是Windows
的可执行文件,分析ELF 和 PE
的文本结构,是逆向工程,或者是做调试,甚至是支付所应具备的中坚力量。在开展逆向工程的发端,大家得到ELF
文件,或者是PE
文件,首先要做的就是分析文件头,精晓新闻,进而逆向文件。不说废话,开首分析:

ELF和PE 文件都是基于Unix 的 COFF(Common Object File Format)
改造而来,更加具体的来说,他是源于当时资深的 DEC(Digital Equipment
Corporation) 的VAX/VMS 上的COFF文件格式。大家从ELF 说起。

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

image.png

ELF

ELF 文件标准里把系统中拔取ELF 格式的公文归类为四种:

  • 可重一直文件,Relocatable File
    ,那类文件包罗代码和数据,可用来连接成可执行文件或共享目标文件,静态链接库归为此类,对应于Linux
    中的.o ,Windows 的 .obj.
  • 可执行文件,Executable File
    ,那类文件包涵了可以直接执行的次第,它的代表就是ELF
    可执行文书,他们一般没有扩充名。比如/bin/bash ,Windows 下的 .exe
  • 共享目标文件,Shared Object File
    ,那种文件包括代码和数据,链接器能够选取那种文件跟任何可重一直文件的共享目的文件链接,发生新的靶子文件。此外是动态链接器可以将多少个那种共享目的文件与可执行文件结合,作为进度影象来运转。对应于Linux
    中的 .so,Windows 中的 DLL
  • 大旨转储文件,Core Dump
    File,当进度意外终止,系统可以将该进度地址空间的情节及为止时的有些信息转存到骨干转储文件。
    对应 Linux 下的core dump。

ELF 文件的完好布局大体上是这么的:

ELF Header
.text
.data
.bss
… other section
Section header table
String Tables, Symbol Tables,..
  • ELF
    文件头放在最前端,它含有了全方位文件的基本特性,如文件版本,目的机器型号,程序入口等等。
  • .text
    为代码段,也是反汇编处理的一对,他们是以机器码的花样储存,没有反汇编的历程基本不会有人读懂那个二进制代码的。
  • .data
    数据段,保存的那个曾经开头化了的全局静态变量一对静态变量
  • .bss
    段,存放的是未起首化的全局变量有的静态变量,这一个很容易精晓,因为在未开头化的状态下,大家单独用一个段来保存,可以不在一初阶就分配空间,而是在结尾总是成可执行文件的时候,再在.bss
    段分配空间。
  • 任何段,还有一些可选的段,比如.rodata 表示这里存储只读数据, .debug
    表示调试音信等等,具体碰到可以查阅相关文档。
  • 自定义段,这一块是为着兑现用户独特成效而留存的段,方便扩大,比如我们利用全局变量或者函数以前增加
    **attribute(section(‘name’))** 就足以吧变量或者函数放到以name
    作为段名的段中。
  • 段表,Section Header Table ,是一个首要的片段,它描述了ELF
    文件包涵的富有段的音信,比如每个段的段名,段长度,在文书中的偏移,读写权限和一部分段的其余属性。

重定位表是一个数组,那个数组的轻重记载在 _IMAGE_OPTIONAL_HEADER 的

重定位表是一个数组,这么些数组的轻重缓急记载在 _IMAGE_OPTIONAL_HEADER 的

Dos头:
  • Dos头的前多个字节恒为4D5A(只是当作判断PE文件的首先个标志,并不可能因此它就能看清是还是不是是PE文件)

  • Dos头的终极五个字节是指向NT头的偏移量
    唯有前多个字节和前边多个字节关系到PE文件是不是正规运作

  • NT头:

![](https://upload-images.jianshu.io/upload_images/5676193-1017ee68ff187eb6.png)

image.png
  • 后边五个字节恒为0x4550,用于判断是否为PE文件的第一个标志

  • 文件结构分析,PE的一部分基本概念总计。Nt头前边就是各类区段信息

ELF Header

ELF 文件新闻的查阅利器在Linux 下是是objdump, readelf,
相关命令较多,可查。下边大家从ELF 文件头说起。

文件头包罗的始末很多,大家在Ubuntu 系统下选用 readelf 命令来查看ELF
文件头:

澳门金沙国际 2

大家以bash 那么些可执行文件为例,大家可以见见ELF 文件头定义了ELF
魔数,文件机器字节长度,数据存储情势,版本,运行平台,ABI版本,ELF
重定位类型,硬件平台,硬件平台版本,入口地址,程序头入口和长度,段表的职位和尺寸,段的数额。

ELF 文件头的结构和相关常数一般定义在了 /usr/include/elf.h
中,大家得以进去查看一下:

澳门金沙国际 3

除外第二个,其他都是种种对应的,首个是一个对应了Magic number, Class,
Data, Version, OS/ABI, ABI version.

出现在最开头的ELF Magic number, 16字节是用来标识ELF
文件的阳台属性,比如字长,字节序,ELF
文件版本。在加载的时候,首先会肯定魔数的正确性,不正确的话就拒绝加载。

另一个保护的东西是段表(Section Header Table)
,保存了丰硕多彩段的要旨特性,比如段名,段长度,文件中的偏移,读写权限,段的其他性能。而段表自己在ELF
文件中的地点是在ELF 头文件 e_shoff 决定的。

俺们可以动用 objdump -h 的授命来查看ELF 文件中涵盖哪些段,以bash
那么些可进行为例,其实除了我们事先说的如何基本结构,他饱含众多别样的协会:

澳门金沙国际 4

相同的,大家应用readelf -S 的授命也得以开展查看。

下边大家来看一下结构,依然到elf.h 中去查看,他的结构体名字叫
Elf32_Shdr,64位对应Elf64_Shdr,结构如下:

澳门金沙国际 5

以上结构中,分别对应于:

  • 段名
  • 段类型
  • 段标志位
  • 段虚拟地址
  • 段偏移
  • 段长度
  • 段链接
  • 段对齐
  • 项,一些轻重固定的项,如符号表等。

那些体系,在选择readelf -S 指令时一一对应。

除此以外还有一个第一的表,叫重定位表,一般段名叫.rel.text,
在上头没有出现,链接器在处理目的文件时,要求对目标文件中的某些地方举行重平素,就是代码段和数量段中那么些对相对地址引用的岗位,那么些时候就须求运用重定位表了。

.DataDirect‌​ory[IMAGE_DIRECTORY_E‌​NTRY_BASERELOC].Size 成员中

.DataDirect‌​ory[IMAGE_DIRECTORY_E‌​NTRY_BASERELOC].Size 成员中

文件头

澳门金沙国际 6

image.png

  • 文本头大小0x14个字节(由图可见:它是Nt头的第一个元素)

  • 扩大头的轻重缓急就在其间

  • 节的多少也在里边

  • 文件头里面保存了PE文件的局地性能(那里只列举了部分):
    1.是否是dll(0x0210),exe(0x010F)
    2.是还是不是可举行

字符串表

为何会有字符串表呢?其实这些也是在不停开拓进取更上一层楼中找到的解决办法,在ELF
文件中,会用到很多的字符串,段名,变量名等等,不过字符串其自身又长度不固定,假使运用固定结构来表示,就会带来空间上的分神。所以,构造一个字符串表,将应用的字符串统一放在那里,然后经过偏移量来引用字符串,岂不美哉。

内需选择的时候,只须要给一个偏移量,然后就到字符串该职位找字符串,碰到\0
就停止。

字符串在ELF 文件中,也是以段的款型保留的,常见的段名 .strtab, .shstrtab
四个字符串分别为字符串表和段表字符串,前者用来保存普通的字符串,后者保存段名。

在我们选取readelf -h 的时候,我们看看最后一个成员,section header string
table index ,实际上她指的就是字符串表的下标,bash
对应的字符串表下标为27,在动用objdump
的时候,实际上忽略了字符串表,大家采纳readelf
,就足以见到第27位即字符串表:

澳门金沙国际 7


上面我们想起一下,这么些ELF 构造的精密之处,当一个ELF
文件到来的时候,系统本来的找到她的上马,得到文件头,首先看魔数,识别基本新闻,看是或不是正确的,或者是可识其余文件,然后加载他的主干新闻,包罗CPU
平台,版本号,段表的岗位在哪,仍能获得字符串表在哪,以及一切程序的进口地址。这一名目繁多早先化音信获得将来,程序可以经过字符串表定位,找到段名的字符串,通过段表的起来地方,确认各类段的职分,段名,长度等等音讯,进而到达入口地址,准备进行。

当然,那只是初期始的始末,其后还要考虑链接,Import,Export
等等内容,留待将来完善。

结构图如下,图片中 0 和 000 都代表16进制数,转换到二进制是  0000 和 0000
0000 0000:

社团图如下,图片中 0 和 000 都代表16进制数,转换来二进制是  0000 和 0000
0000 0000:

扩展头

澳门金沙国际 8

image.png

澳门金沙国际 9

image.png

PE 文件

下边大家去探望更为广泛的PE 文件格式,实际上PE 与 ELF
文件基本相同,也是应用了依据段的格式,同时PE
也允许程序员将变量或者函数放在自定义的段中, GCC
**attribute(section(‘name’))** 伸张属性。

PE 文件的前身是COFF,所以分析PE 文件,先来探望COFF
的文件格式,他保存在WinNT.h 文件中。

COFF 的文件格式和ELF 差不离一毛一样:

Image Header
SectionTable Image_SECTION_HEADER
.text
data
.drectve
.debug$S
… other sections
Symbol Table

文本头定义在WinNT.h 中,大家开辟来看一下:

澳门金沙国际 10

大家得以看到,它这几个文件头和ELF
实际上是同一的,也在文书头中定义了段数,符号表的职位,Optional Header
的高低,那么些Optional Header 前面就看看了,他就是PE
可执行文件的文书头的一部分,以及段的属性等。

跟在文书头前边的是COFF 文件的段表,结构体名叫 IMAGE_SECTION_HEADER :

澳门金沙国际 11

性能包含那些,和ELF 没差:

  • 段名
  • 大体地址 PhysicalAddress
  • 虚拟地址 VirtualAddress
  • 原有数据大小 Sizeof raw data
  • 段在文书中的地方 File pointer to raw data
  • 该段的重定位表在文书中的地点 File pointer to relocation table
  • 该段的行号表在文件中的地方 File pointer to line number
  • 标志位,包罗段的品种,对齐格局,读取权限等标志。

澳门金沙国际 12

澳门金沙国际 13

增添头详解:
typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;                          //表示这是一个什么类型的PE文件,32位一般是0x010B,64位的文件一般是0x020B
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;                  //所有代码区段(节)的总大小(基于文件对齐后的大小)
    DWORD   SizeOfInitializedData;            //已经初始化的数据的总大小
    DWORD   SizeOfUninitializedData;        //未初始化的数据的大小
    DWORD   AddressOfEntryPoint;            //程序开始执行的相对虚拟地址,即OEP,这是一个RVA,要想得到VA,则必须要加上ImageBase(下面有介绍!!!!!)
    DWORD   BaseOfCode;              //起始代码的相对虚拟地址(RVA),就是.text段的RVA
    DWORD   BaseOfData;              //  其实数据的相对虚拟地址(RVA),就是.data段的RVA

    //
    // NT additional fields.
    //

    DWORD   ImageBase;                //默认加载地址(如果没有这个基址会发生重定位)
    DWORD   SectionAlignment;          //块对齐数,一般是0x1000
    DWORD   FileAlignment;                    //文件对齐数,一般是0x200
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;                      //把文件加载进内存,所需要的内存大小,是进行了内存对齐之后的大小
    DWORD   SizeOfHeaders;                //所有头部大小(这是按照文件对齐后的大小),也是文件主体相对文件起始的偏移,是所有头+节表的大小
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;                    //文件(包括exe和dll文件)特征标志(见下面一张图)
    DWORD   SizeOfStackReserve;                   //表示进程中栈可以增长到的最大值,一般1M
    DWORD   SizeOfStackCommit;                    //进程中栈的初始值,据说也是栈每次分配增长的值,一般4KB
    DWORD   SizeOfHeapReserve;                      //表示进程中堆可以增长到的最大值,一般1M
    DWORD   SizeOfHeapCommit;                        //进程堆的初始值
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;      //数据目录的个数,也就是下面那个数组中元素的个数
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//数据目录表,比较重要!
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

澳门金沙国际 14

image.png

增添头里面相比较紧要的在上头已经做出注释

DOS 头

在我们解析PE 的事先,还有其余一个头要明白一下,DOS
头,不得不说,微软事儿照旧挺多的。

微软在成立PE 文件格式时,人们正在普遍应用DOS
文件,所以微软为了考虑包容性的问题,所以在PE 头的最前边还添加了一个
IMAGE_DOS_HEADER 结构体,用来扩展已部分DOS EXE。在WinNTFS.h
里可以看到他的人影。

澳门金沙国际 15

DOS
头结构体的轻重缓急是40字节,那里边有三个基本点的积极分子,必要精通,一个是e_magic
又见魔数,一个是e_lfanew,它只是了NT 头的撼动。

对此PE 文件来说,这么些e_澳门金沙国际,magic,也就是DOS 签名都是MZ,据说是一个叫 MarkZbikowski 的开发人士在微软安顿了那种ODS 可执行文件,所以…

俺们以Windows 下的notepad++
的可执行文件为例,在二进制编辑软件中开辟,此类软件相比较多,Heditor 打开:

澳门金沙国际 16

始发的多个字节是4D5A,e_lfanew 为00000108 注意存储顺序,小端。

你认为伊始加上了DOS 头就完了了么,就可以随着接PE 头了么。为了同盟DOS
当然不是那般容易了,紧接着DOS 头,跟的是DOS 存根,DOS
stub。这一块就是为DOS 而准备的,对于PE 文件,即便没有它也能够正常运转。

澳门金沙国际 17

一旁的ASCII 是读不懂的,因为她是机器码,是汇编,为了在DOS
下执行,对于notepad++ 来说,那里是推行了一句,this program cannot be run
in DOS mode 然后脱离。逗我= =,有新的人,可以在DOS
中创立一个程序,做一些小动作。

每个元素的轻重都记载在 SizeOfBlock 中,那一个因素是由 一个
_IMAGE_BASE_RELOCATION 结构体和一个TypeOffset 数组组成的。TypeOffset
数组的种种元素占2个字节,其中,高4位是偏移类型(type),低12位代表必要重一直的地址(Offset),即,它与
VirtualAddress 相加即是指向 PE 印象中须求修改的百般代码的RVA。

每个元素的尺寸都记载在 SizeOfBlock 中,那一个因素是由 一个
_IMAGE_BASE_RELOCATION 结构体和一个TypeOffset 数组组成的。TypeOffset
数组的每个元素占2个字节,其中,高4位是偏移类型(type),低12位代表须求重一直的地方(Offset),即,它与
VirtualAddress 相加即是指向 PE 影象中需求修改的不胜代码的RVA。

再小结一波:

ImageBase(印象基址,装载基址,它是一个VA值):即使没有加载到这么些地址则会重一向(就是PE文件加载进内存之后,就一定于通晓了Dos头的岗位,然后就可以领略其他的任务了),就是PE装入内存的基地址,默许意况下,EXE文件在内存中的基地址是0X0040
0000,DLL文件为0x0100 0000,由编译器决定!
先后入口点(OEP)
印象大小(SizeOfImage)————>把公文加载进内存,所必要的内存大小(注意是进行了块对齐之后)
代码大小(SizeOfCode)——>所有区段的总大小
代码基址(BaseOfCode)开始代码的 RVA—->.text的RVA
多少基址(BaseOfData)开始数据的RVA—–>.data的RVA
头大小(SizeOfHeaders)————>所有底部大小,就是文件主题相对文件早先的舞狮
内存对齐(SectionAlignment)———–>为0x1000(4KB)
文本对齐(FileAlignment)—————->200h(0x200)
DLL标记(DllCharacteristics)——–>指示Dll特征的标志

NT头

上边进入正题,在HEditor 上也看看了PE,这一块就是正规的步入PE 的范围。

澳门金沙国际 18

那是32位的PE
文件头定义,64位对应改。第三个成员固然签字,如我辈所说,就是我们看看的「PE」,对应为50450000h。

此地边有多个东西,第四个就是大家前面看来的COFF
文件头,这里直接放进来了,大家不再分析。

看第三个,IMAGE_OPTIONAL_HEADER
不是说这一个头可选,而是里边有些变量是可选的,而且有部分变量是必须的,否则会造成文件不可能运行:

澳门金沙国际 19

有那般几个必要重点关切的成员,那一个都是文本运行所需要的:

  1. Magic 魔数,对于32结构体来说是10B,对于64结构体来说是20B.
  2. AddressOfEntryPoint 持有EP 的RVA
    值,之处程序初始执行的代码起先地点,也就是程序入口。
  3. ImageBase 进度虚拟内存的限制是0-FFFFFFFF (32位)。PE
    文件被加载到如此的内存中,ImageBase 提出文件的先行装入地点。
  4. SectionAlignment, FileAlignment PE 文件的Body
    部分区划为多少段,FileAlignment
    之处段在磁盘文件中的最小单位,SectionAlignment指定了段在内存中的最小单位。
  5. SizeOfImage 指定 PE Image 在虚拟内存中所占的空中大小。
  6. SizeOfHeader PE 头的大小
  7. Subsystem 用来区分系统驱动文件与常见可执行文件。
  8. NumberOfRvaAndSizes 指定DataDirectory
    数组的个数,纵然最终一个值,指出个数是16,但实际上PE
    装载仍然通过辨认这些值来确定大小的。至于DataDirectory 是咋样看上面
  9. DataDirectory 它是一个由IMAGE_DATA_DIRECTORY
    结构体组成的数组,数组每一项都有定义的值,里边有局地最首要的值,EXPORT/IMPORT/RESOURCE,
    TLS direction 是第一关切的。

偏移类型的意义如下:

偏移类型的意思如下:

PE底部包罗了Dos头,平素到节表的为止地点,.text区段先河以前
  • 数量目录表
![](https://upload-images.jianshu.io/upload_images/5676193-cfbc56c21cd7568f.png)

image.png

段头

PE 的段头间接沿用的COFF 的段头结构,上面也说过了,大家查阅notepad++
的段头,可以取得各样段名,以及其音讯,那里,我们得以应用部分软件查看,尤其便民:

澳门金沙国际 20

Constant

Value

Description

IMAGE_REL_BASED_ABSOLUTE

  0

The base relocation is skipped. This type can be used to pad a block.

IMAGE_REL_BASED_HIGH

  1

The base relocation adds the high 16 bits of the difference to the 16bit field at offset. The 16-bit field represents the high value of a 32-bit word.

IMAGE_REL_BASED_LOW

  2

The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the low half of a 32-bit word.

IMAGE_REL_BASED_HIGHLOW

  3

The base relocation applies all 32 bits of the difference to the 32-bit field at offset.

IMAGE_REL_BASED_HIGHADJ

  4

The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word. The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation. This means that this base relocation occupies two slots.

IMAGE_REL_BASED_MIPS_JMPADDR

  5

The relocation interpretation is dependent on the machine type.

When the machine type is MIPS, the base relocation applies to a MIPS jump instruction.

IMAGE_REL_BASED_ARM_MOV32

  5

This relocation is meaningfull only when the machine type is ARM or Thumb. The base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair.

IMAGE_REL_BASED_RISCV_HIGH20

  5

This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the high 20 bits of a 32-bit absolute address.

 

  6

Reserved, must be zero.

IMAGE_REL_BASED_THUMB_MOV32

  7

This relocation is meaningful only when the machine type is Thumb. The base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair.

IMAGE_REL_BASED_RISCV_LOW12I

  7

This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format.

IMAGE_REL_BASED_RISCV_LOW12S

  8

This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format.

IMAGE_REL_BASED_MIPS_JMPADDR16

  9

The relocation is only meaningful when the machine type is MIPS. The base relocation applies to a MIPS16 jump instruction.

IMAGE_REL_BASED_DIR64

10

The base relocation applies the difference to the 64-bit field at offset.

Constant

Value

Description

IMAGE_REL_BASED_ABSOLUTE

  0

The base relocation is skipped. This type can be used to pad a block.

IMAGE_REL_BASED_HIGH

  1

The base relocation adds the high 16 bits of the difference to the 16bit field at offset. The 16-bit field represents the high value of a 32-bit word.

IMAGE_REL_BASED_LOW

  2

The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the low half of a 32-bit word.

IMAGE_REL_BASED_HIGHLOW

  3

The base relocation applies all 32 bits of the difference to the 32-bit field at offset.

IMAGE_REL_BASED_HIGHADJ

  4

The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word. The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation. This means that this base relocation occupies two slots.

IMAGE_REL_BASED_MIPS_JMPADDR

  5

The relocation interpretation is dependent on the machine type.

When the machine type is MIPS, the base relocation applies to a MIPS jump instruction.

IMAGE_REL_BASED_ARM_MOV32

  5

This relocation is meaningfull only when the machine type is ARM or Thumb. The base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair.

IMAGE_REL_BASED_RISCV_HIGH20

  5

This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the high 20 bits of a 32-bit absolute address.

 

  6

Reserved, must be zero.

IMAGE_REL_BASED_THUMB_MOV32

  7

This relocation is meaningful only when the machine type is Thumb. The base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair.

IMAGE_REL_BASED_RISCV_LOW12I

  7

This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format.

IMAGE_REL_BASED_RISCV_LOW12S

  8

This relocation is only meaningful when the machine type is RISC-V. The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format.

IMAGE_REL_BASED_MIPS_JMPADDR16

  9

The relocation is only meaningful when the machine type is MIPS. The base relocation applies to a MIPS16 jump instruction.

IMAGE_REL_BASED_DIR64

10

The base relocation applies the difference to the 64-bit field at offset.

数码目录表也是一个构造体数组——>每一个结构体里面著录的是各种表所对应的RVA以及大小

(扩展:

  • 区段头表(它是一个构造体数组)是由四个IMAGE_SECTION_HEADER这样的结构体组成,以一个全是0的结构体结尾;
  • 导入表也是一个协会体数组(前面会珍爱讲),以一个全0元素结尾,导入表中的IMAGE_THUNK_DATA(文件没有加载的时候,OrignalFirstThunk与FirstThunk指向IMAGE_THUNK_DATA)也是一个结构体数组;
  • 重定位表:它也是一个社团体数组,以全0元素结尾
  • 资源表:它里面也饱含结构体数组(更为详细的可以查阅相关文献)
    )

RVA to RAW

领悟PE
最关键的一个片段就是领悟文件从磁盘到内存地址的炫耀进度,做逆向的人口,唯有熟识地了解才能跟踪到程序的调用进度和地点,才能分析和摸索漏洞。

对于文本和内存的照耀关系,其实很粗略,他们经过一个简约的公式总括而来:

澳门金沙国际 21

换算公式是那般的:

RAW -PointToRawData = RVA – VirtualAddress

招来进度就是先找到RVA
所在的段,然后根据公式总括出文件偏移。因为我们经过逆向工具,可以在内存中查找到所在的RVA,进而大家就足以测算出在文书中所在的任务,那样,就可以手动进行修改。

看回大家刚刚载入的nodepad++ ,其中的V Addr, 实际上就是VirtualAddress,R
offset 就是PointerToRawData。

澳门金沙国际 22

假如大家的RVA 地址是5000,那么合算办法就是,查看区段,发现在.text
中,5000-1000+400 = 4400,那就是RAW
00004400,而实际上,因为大家的ImageBase
是00400000,所以,我们在反编译时候内存中的地址是00405000.

接下去,使我们的PE头中的焦点内容,IAT 和 EAT,也就是 Import address
table, export address table.

其余汉语翻译:

其它汉语翻译:

PE中有组织体数组的构造的下结论:

数录节入重!

  • 节表(区段头表)

澳门金沙国际 23

image.png

.text 段:代码段
.data段:数据段
.bss段:表示未初步化的数量,比如Static变量
.rdata 段:表示只读的数码,比如字符串
……
.relcoc段:存储重一向音讯的区段
各变量存放于哪个区:
常量 ——————>.rdata区
静态变量————->.bss区
全局变量————–>.data 区
节表里面的多少个至关主要数据:
VirtualAddress:这一个区段的相对虚拟地址
SizeofRawData:这么些区段在磁盘中的大小,举办了文本对齐
PointerToRawData:区段的公文偏移,就是那么些区段在磁盘文件中的起首地点
一个重点的公式:
offset(转)=RVA(必要更换的RVA)-RVA(所在区段的RVA)+offset(就是PointerToRawData)

IAT

导入地址表的始末与Windows 操作系统的为主进度,内存,DLL
结构有关。他是一种表格,记录了程序行使什么库中的哪些函数。

上边,让大家把目光转到DLL 上,Dynamic Linked Library 支撑了整套 OS。DLL
的功利在于,不必要把库包涵在先后中,单独构成DLL
文件,必要时调用即可,内存映射技术使加载后的DLL
代码,资源在多少个经过中贯彻共享,更新库时候要是替换相关DLL 文件即可。

加载DLL 的艺术有三种,一种是显式链接,使用DLL
时候加载,使用完释放内存。另一种是隐式链接,程序开端就一起加载DLL,程序终止的时候才放走掉内存。而IAT
提供的建制与隐式链接相关,最卓越的Kernel32.dll。

大家来探望notepad++ 调用kernel32.dll 中的CreateFileW, 使用PE
调试工具Ollydbg

澳门金沙国际 24

咱俩看看填入参数之后,call 了35d7ffff 地址的内容,然后大家去dump
窗口,找一下kernel.CreateFileW:

澳门金沙国际 25

咱俩双击汇编窗口,启动编制,发现确实是call 的那些数值:

澳门金沙国际 26

不过问题来了,下面是E8 35D7FFFF,下面地址却是 00C62178。其实那是Win
Visita, Win 7的ASLR
技术,首要就是针对性缓冲溢出攻击的一种尊崇技术,通过随机化布局,让逆向跟踪者,难以找寻地址,就麻烦简单的进展溢出攻击。可是依旧得以由此跳板的法子,找到溢出的法子,那就是后话了。

现今得以确定的是,35D7FFFF 可以认为保存的数值就是 CreateFileW
的地址。而为啥不直接动用CALL 7509168B 那种办法间接调用呢?
Kernel32.dll 版本各分歧,对应的CreateFileW
函数也各差距,为了合作各样条件,编译器准备了CreateFileW
函数的实际地址,然后记下DWORD PTR DS:[xxxxxx]
那样的下令,执行文书时候,PE 装载器将CreateFileW 函数地址写到这一个任务。

并且,由于重一直的原因存在,所以也无法直接使用CALL 7509168B
的法子,比如多少个DLL 文件有同等的
ImageBase,装载的时候,一个装载到该任务然后,另一个就不可能装载该义务了,须求换位置。所以我们无法对实在地址举办硬编码。

IMAGE_IMPORT_DESCRIPTOR

澳门金沙国际 27

澳门金沙国际 28

对此一个家常程序来说,需求导入几个库,就会存在多少个这么的结构体,这么些结构体组成数组,然后数组最终是以NULL
结构体甘休。其中有多少个首要的成员:

  • OriginalFirstThunk INT Import Name Table 地址,RVA
  • Name 库名称字符串地址,RVA,就是说该地方保存库名称
  • First Thunk IAT 地址 RVA
  • INT 中个元素的值是下面那些IMAGE_IMPORT_BY_NAME 结构体指针。
  • INT 与 IAT 大小应一律。

那就是说PE 是什么导入函数输出到IAT 的:

  1. 读取NAME 成员,获取扩名称字符串
  2. 装载相应库: LoadLibrary(“kernel32.dll”)
  3. 读取OriginalFirstThunk成员,获取INT 地址
  4. 读取INT 数组中的值,获取相应的
    IMAGE_IMPORT_BY_NAME地址,是RVA地址
  5. 使用IMAGE_IMPORT_BY_NAME 的Hint 或者是name
    项,获取相应函数的开头位置 GetProcAddress(“GetCurrentThreadId”)
  6. 读取FistrThunk 成员,获得IAT 地址。
  7. 将上边得到的函数地址输入相应IAT 数组值。
  8. 重复4-7 到INT 结束。

那里就暴发了一个迷惑,OriginalFirstThunk 和 First Thunk
都指向的是函数,为何多此一举呢?

率先,从直观上说,四个都指向了库中引入函数的数组,鱼C 画的那张图挺直观:

澳门金沙国际 29

OriginalFirstThunk 和 FirstThunk 他们都是三个种类为IMAGE_THUNK_DATA
的数组,它是一个指针大小的同步(union)类型。
每一个IMAGE_THUNK_DATA
结构定义一个导入函数音讯(即指向结构为IMAGE_IMPORT_BY_NAME
的实物,这个家伙稍后再议)。
接下来数组最后以一个内容为0 的 IMAGE_THUNK_DATA 结构作为落成标志。
IMAGE_THUNK_DATA32 结构体如下:

澳门金沙国际 30

因为是Union 结构,IMAGE_THUNK_DATA 事实上是一个双字大小。
确定如下:

当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号情势输入,那时候低
31位被视作一个函数序号。

当 IMAGE_THUNK_DATA 值的参天位为
0时,表示函数以字符串类型的函数名艺术输入,那时双字的值是一个
RVA,指向一个 IMAGE_IMPORT_BY_NAME 结构。

咱俩再看IMAGE_IMPORT_BY_NAME 结构:

澳门金沙国际 31

布局中的 Hint
字段也表示函数的序号,但是这几个字段是可选的,有些编译器总是将它设置为 0。

Name 字段定义了导入函数的称呼字符串,那是一个以 0 为末段的字符串。

目前重大来了:

首先个数组(由 OriginalFirstThunk
所指向)是独自的一项,而且不可能被改写,大家面前称为 INT。第三个数组(由
FirstThunk 所指向)事实上是由 PE 装载爱抚写的。

PE 装载器装载顺序正如下面所讲的那样,大家再将它讲详细一点:

PE 装载器首先搜索 OriginalFirstThunk
,找到之后加载程序迭代搜索数组中的每个指针,找到每个
IMAGE_IMPORT_BY_NAME
结构所针对的输入函数的地点,然后加载器用函数真正入口地址来代表由
FirstThunk 数组中的一个进口,由此大家称为输入地址表(IAT).

后续套用鱼C 的图,就能直观的感受到了:

澳门金沙国际 32

就此,在读取一遍OriginalFirstThunk 之后,程序就是凭借IAT
提供的函数地址来运转了。

常量 描述
IMAGE_REL_BASED_ABSOLUTE       0x0 使块按照32位对齐,位置为0。
IMAGE_REL_BASED_HIGH 0x1 高16位必须应用于偏移量所指高字16位。
IMAGE_REL_BASED_LOW 0x2 低16位必须应用于偏移量所指低字16位。
IMAGE_REL_BASED_HIGHLOW 0x3 全部32位应用于所有32位。
IMAGE_REL_BASED_HIGHADJ 0x4 需要32位,高16位位于偏移量,低16位位于下一个偏移量数组元素,组合为一个带符号数,加上32位的一个数,然后加上8000然后把高16位保存在偏移量的16位域内。
常量 描述
IMAGE_REL_BASED_ABSOLUTE       0x0 使块按照32位对齐,位置为0。
IMAGE_REL_BASED_HIGH 0x1 高16位必须应用于偏移量所指高字16位。
IMAGE_REL_BASED_LOW 0x2 低16位必须应用于偏移量所指低字16位。
IMAGE_REL_BASED_HIGHLOW 0x3 全部32位应用于所有32位。
IMAGE_REL_BASED_HIGHADJ 0x4 需要32位,高16位位于偏移量,低16位位于下一个偏移量数组元素,组合为一个带符号数,加上32位的一个数,然后加上8000然后把高16位保存在偏移量的16位域内。

EAT

搞精通了IAT 的原理,EAT
就好驾驭了,近日那篇总计的有点长了,我长话短说。IAT
是导入的库和函数的表,那么EAT
就对应于导出,它使差别的应用程序可以调用库文件中提供的函数,为了便利导出函数,就须要保留这一个导出音信。

回头看PE 文件中的PE头大家可以看来IMAGE_EXPORT_DIRECTORY
结构体以的岗位,他在IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress
的值就是 IMAGE_EXPORT_DIREDCTORY 的原初地点。

澳门金沙国际 33

IMAGE_EXPORT_DIRECTORY结构体如下:

澳门金沙国际 34

这边边一样是这么多少个第一的积极分子:

  • NumberOfFunctions 实际Export 函数的个数
  • NumberOfNames Export 函数中签名的函数个数
  • AddressOfFunctins Export 函数地址数组,数组个数是上面的NOF
  • AddressOfNames 函数名称地址数组,个数是上边的NON
  • AddressOfNameOrdinals Ordinal 地址数组,个数等于上面NON
  • Name 一个RVA 值,指向一个概念了模块名称的字符串。如即使Kernel32.dll
    文件被改名换姓为”Ker.dll”。仍是可以从那几个字符串中的值得知其在编译时的文本名是”Kernel32.dll”。
  • Base:导出函数序号的起头值,将AddressOfFunctions
    字段指向的进口地址表的索引号加上那几个初始值就是对应函数的导出
    序号。
    以kernel32.dll 为例,咱们看一下:
![](https://upload-images.jianshu.io/upload_images/30117-6bb373c33a5b9995.jpg)

从下面那么些成员,大家实际可以见到,是有两种方式提必要这个想调用该库中函数的,一种是间接从序号查找函数入口地址导入,一种是透过函数名来探寻函数入口地址导入。

先上一个鱼C 的图,方便清楚:

澳门金沙国际 35

上面图,注意一点,因为AddressOfNameOrdinals
的序号应当是从0起先的,可是图中映射的是第四个函数指向的序号1。

我们分别说一下二种办法:

当已知导出序号的时候

  1. Windows 装载器定位到PE 文件头,
  2. 从PE 文件头中的 IMAGE_OPTIONAL_HEADER32
    结构中取出数据目录表,并从第四个数据目录中获得导出表的RVA ,
  3. 从导出表的 Base 字段得到先导序号,
  4. 将要求摸索的导出序号减去先河序号,拿到函数在进口地址表中的索引,
  5. 检测索引值是或不是高于导出表的 NumberOfFunctions
    字段的值,即使超出后者的话,表达输入的序号是无效的用那些索引值在
    AddressOfFunctions
    字段指向的导出函数入口地址表中取出相应的种类,那就是函数入口地址的RVA
    值,当函数被装入内存的时候,那个RVA
    值加上模块实际装入的基地址,就获取了函数真正的输入地址

当已知函数名称查找入口地址时

  1. 从导出表的 NumberOfNames
    字段得到已命名函数的总额,并以这一个数字作为循环的次数来布局一个巡回
  2. 从 AddressOfNames
    字段指向得到的函数名称地址表的率先项开头,在循环少将每一项定义的函数名与要物色的函数名绝相比较,倘使没有其他一个函数名是吻合的,表示文件中从未点名名称的函数,即使某一项定义的函数名与要物色的函数名符合,那么记下那些函数名在字符串地址表中的索引值,然后在
    AddressOfNamesOrdinals
    指向的数组中以同一的索引值取出数组项的值,大家那里如若那么些值是x
  3. 终极,以 x 值作为索引值,在 AddressOfFunctions
    字段指向的函数入口地址表中收获的 RVA 就是函数的输入地址

貌似的话,做逆向或者是写代码都是第三种格局,大家以kernel32.dll
中的GetProcAddress 函数为例,其操作原理如下:

  1. 利用 AddressOfNames 成员转到 『函数名称数组』
  2. 『函数名称数组』中存储着字符串地址,通过比较字符串,查找指定的函数名称,此时数组所以为成为name_index
  3. 利用 AddressOfNameOrdinals 成员,转到那个序号数组
  4. 在ordinal 数组中通过name_index 查找到呼应的序号
  5. 采用AddressOfFunctions 成员,转到『函数地址数组』EAT
  6. 在EAT 少校刚刚收获的ordinal 作为目录,得到指定函数的输入地址

写了那样多,实际上算是对文本结构有了一个入门的认识,至少知道在程序运行进程中,系统是怎么着开展操作和链接的,而更是详细的内容注入运行时压缩,DLL
注入,API 钩取等技能,就要求在这几个基础之上继续掘进,所以PE ,ELF
文件结构的解析是非凡首要的。

PS. 参考:
鱼C 讲解PE
文件格式之INT
《Windows PE 权威指南》
《逆向工程宗旨原理》
《程序员的自身修养-链接,装载与库》

例子:

例子:

浅析普遍的dll:在QQ中的 zlib.dll 文件 (在QQ安装目录下的bin文件夹中):

浅析普遍的dll:在QQ中的 zlib.dll 文件 (在QQ安装目录下的bin文件夹中):

率先找到重定位表,那里运用工具:

先是找到重定位表,这里运用工具:

澳门金沙国际 36

澳门金沙国际 37

找到数据:

找到数据:

澳门金沙国际 38

澳门金沙国际 39

VirtualAddress 为 0x1000,SizeOfBlock 为 0x64。首个条目为
0x338C,高四位为 0x3,offset为 0x38C,即偏移地址为 0x138C (由 0x1000 +
0x38C得来)应用于此地址上任何32位。打开C32Asm反汇编查看:

VirtualAddress 为 0x1000,SizeOfBlock 为 0x64。第三个条目为
0x338C,高四位为 0x3,offset为 0x38C,即偏移地址为 0x138C (由 0x1000 +
0x38C得来)应用于此地址上一切32位。打开C32Asm反汇编查看:

澳门金沙国际 40

澳门金沙国际 41

相关文章