基址重平昔,文件结构分析

by admin on 2019年3月9日

源代码如下:

源代码如下:

home88一必发 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文件的第叁个标志

  • Nt头后边正是各种区段消息

ELF Header

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

文本头包蕴的始末很多,我们在Ubuntu 系统下使用 readelf 命令来查看ELF
文件头:

home88一必发 2

大家以bash 这几个可执行文件为例,大家能够见到ELF 文件头定义了ELF
魔数,文件机器字节长度,数据存款和储蓄格局,版本,运行平台,ABI版本,ELF
重定位类型,硬件平台,硬件平台版本,入口地址,程序头入口和长短,段表的岗位和长度,段的数据。

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

home88一必发 3

除去第1个,其余都以种种对应的,第①个是3个对应了Magic number, Class,
Data, Version, OS/ABI, ABI version.

并发在最开首的ELF Magic number, 16字节是用来标识ELF
文件的阳台属性,比如字长,字节序,ELF
文件版本。在加载的时候,首先会确认魔数的不易,不科学的话就不肯加载。

另3个注重的事物是段表(Section Header Table)
,保存了充分多彩段的骨干质量,比如段名,段长度,文件中的偏移,读写权限,段的其余质量。而段表自身在ELF
文件中的地方是在ELF 头文件 e_shoff 决定的。

大家得以应用 objdump -h 的一声令下来查看ELF 文件中含有如何段,以bash
这几个可实施为例,其实不外乎大家事先说的哪些基本组织,他饱含众多别的的构造:

home88一必发 4

同样的,我们采取readelf -S 的一声令下也得以展开查看。

上面我们来看一下构造,依然到elf.h 中去查看,他的结构体名字叫
Elf32_Shdr,64位对应Elf64_Shdr,结构如下:

home88一必发 5

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

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

这一个项目,在利用readelf -S 指令时一一对应。

其它还有2个根本的表,叫重定位表,一般段名叫.rel.text,
在上边没有出现,链接器在拍卖目的文件时,需求对目的文件中的有个别地方进行重一直,正是代码段和数量段中这一个对相对地址引用的任务,那一个时候就要求使用重定位表了。

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

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

文件头

home88一必发 6

image.png

  • 文件头大小0x十七个字节(由图可见:它是Nt头的首个因素)

  • 基址重平昔,文件结构分析。增加头的高低就在内部

  • 节的数目也在中间

  • 文件头里面保存了PE文件的有些天性(那里只列举了一些):
    1.是否是dll(0x0210),exe(0x010F)
    2.是或不是可实行

字符串表

何以会有字符串表呢?其实这些也是在时时刻刻上扬更上一层楼中找到的消除办法,在ELF
文件中,会用到很多的字符串,段名,变量名等等,不过字符串其本身又长度不稳定,假如应用一定结构来表示,就会拉动空间上的分神。所以,构造3个字符串表,将运用的字符串统一放在那里,然后通过偏移量来引用字符串,岂不美哉。

基址重平昔,文件结构分析。内需运用的时候,只必要给二个偏移量,然后就到字符串该职分找字符串,境遇\0
就停止。

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

在大家运用readelf -h 的时候,大家看看最终二个分子,section header string
table index ,实际上她指的正是字符串表的下标,bash
对应的字符串表下标为27,在行使objdump
的时候,实际上忽略了字符串表,大家利用readelf
,就能够观看第一五个人即字符串表:

home88一必发 7


下边大家想起一下,那几个ELF 构造的技艺极其精巧之处,当八个ELF
文件到来的时候,系统本来的找到他的启幕,获得文件头,首先看魔数,识别基本音信,看是或不是不利的,或许是可识其他文件,然后加载他的中坚消息,包罗CPU
平台,版本号,段表的地点在哪,仍是能够得到字符串表在哪,以及全体程序的进口地址。这一体系初阶化信息获得现在,程序能够通过字符串表定位,找到段名的字符串,通过段表的开头地点,确认种种段的岗位,段名,长度等等音讯,进而到达入口地址,准备举办。

自然,那只是先前时代始的始末,其后还要考虑链接,Import,Export
等等内容,留待未来完善。

协会图如下,图片中 0 和 000 都意味着16进制数,转换来二进制是  0000 和 0000
0000 0000:

布局图如下,图片中 0 和 000 都意味着16进制数,转换成二进制是  0000 和 0000
0000 0000:

扩展头

home88一必发 8

image.png

home88一必发 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 中,我们打开来看一下:

home88一必发 10

咱俩得以看来,它那么些文件头和ELF
实际上是一律的,也在文件头中定义了段数,符号表的地点,Optional Header
的轻重缓急,这几个Optional Header 后面就观察了,他正是PE
可执行文件的公文头的一些,以及段的特性等。

跟在文书头后面包车型大巴是COFF 文件的段表,结构体名叫 IMAGE_SECTION_HEADER :

home88一必发 11

属性包罗那么些,和ELF 没差:

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

home88一必发 12

home88一必发 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;

home88一必发 14

image.png

恢宏头里面相比首要的在地方已经做出注释

DOS 头

在我们分析PE 的事先,还有其余多个头要询问一下,DOS
头,不得不说,微软事儿仍旧挺多的。

微软在创制PE 文件格式时,人们正在周边使用DOS
文件,所以微软为了考虑包容性的标题,所以在PE 头的最前边还添加了1个IMAGE_DOS_HEADE途乐 结构体,用来扩大已部分DOS EXE。在WinNTFS.h
里能够观望她的身形。

home88一必发 15

DOS
头结构体的轻重是40字节,那里边有三个关键的分子,必要精通,三个是e_magic
又见魔数,3个是e_lfanew,它只是了NT 头的舞狮。

对此PE 文件来说,这一个e_magic,也便是DOS 签名都以MZ,据说是三个叫 马克Zbikowski 的开发人士在微软设计了那种ODS 可执行文件,所以…

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

home88一必发 16

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

您认为开始加上了DOS 头就形成了么,就足以跟着接PE 头了么。为了合作DOS
当然不是这样简单了,紧接着DOS 头,跟的是DOS 存根,DOS
stub。这一块就是为DOS 而准备的,对于PE 文件,即便没有它也足以健康运作。

home88一必发 17

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

各种成分的分寸都记载在 SizeOfBlock 中,这几个成分是由 二个
_IMAGE_BASE_RELOCATION 结构体和2个TypeOffset 数组组成的。TypeOffset
数组的各种成分占二个字节,在那之中,高贰个人是偏移类型(type),低十个人表示需求重从来的地方(Offset),即,它与
VirtualAddress 相加便是指向 PE 印象中须求修改的11分代码的OdysseyVA。

各种成分的轻重缓急都记载在 SizeOfBlock 中,那么些成分是由 一个
_IMAGE_BASE_RELOCATION 结构体和1个TypeOffset 数组组成的。TypeOffset
数组的每种成分占1个字节,当中,高4个人是偏移类型(type),低九位代表须要重一贯的地点(Offset),即,它与
VirtualAddress 相加就是指向 PE 印象中须要修改的11分代码的奥迪Q3VA。

再小结一波:

ImageBase(影像基址,装载基址,它是3个VA值):即使没有加载到这几个地址则会重一向(正是PE文件加载进内部存储器之后,就也正是精通了Dos头的职位,然后就足以通晓别的的地点了),便是PE装入内部存款和储蓄器的营地址,暗许情形下,EXE文件在内部存款和储蓄器中的营地址是0X00四千00,DLL文件为0x0100 0000,由编写翻译器决定!
程序入口点(OEP)
印象大小(SizeOfImage)————>把公文加载进内部存款和储蓄器,所急需的内部存款和储蓄器大小(注意是展开了块对齐之后)
代码大小(SizeOfCode)——>全体区段的总大小
代码基址(BaseOfCode)初始代码的 哈弗VA—->.text的奥迪Q5VA
数量基址(BaseOfData)初始数据的OdysseyVA—–>.data的福睿斯VA
头大小(SizeOfHeaders)————>全部尾部大小,正是文本中央相对文件开始的晃动
内存对齐(SectionAlignment)———–>为0x一千(4KB)
文件对齐(FileAlignment)—————->200h(0x200)
DLL标记(DllCharacteristics)——–>提示Dll特征的标志

NT头

上边进入正题,在H艾德itor 上也见到了PE,这一块就是正经的步入PE 的规模。

home88一必发 18

那是三1二个人的PE
文件头定义,63位对应改。第二个成员尽管签字,如小编辈所说,就是我们看看的「PE」,对应为504伍仟0h。

此处边有多个东西,第多少个正是我们事先看到的COFF
文件头,那里一向放进来了,大家不再分析。

看第三个,IMAGE_OPTIONAL_HEADE帕杰罗不是说那么些头可选,而是里边某些变量是可选的,而且有一些变量是必须的,不然会造成文件不可能运营:

home88一必发 19

有诸如此类几个须求注重关注的分子,这个都以文件运营所须要的:

  1. Magic 魔数,对于32结构体来说是10B,对于64结构体来说是20B.
  2. AddressOfEntryPoint 持有EP 的本田UR-VVA
    值,之处程序伊始执行的代码伊始地点,也等于程序入口。
  3. ImageBase 进度虚拟内部存款和储蓄器的限定是0-FFFFFFFF (三拾二人)。PE
    文件被加载到这么的内存中,ImageBase 建议文件的优先装入地点。
  4. SectionAlignment, FileAlignment PE 文件的Body
    部分区划为多少段,FileAlignment
    之处段在磁盘文件中的最小单位,SectionAlignment钦赐了段在内部存款和储蓄器中的最小单位。
  5. SizeOfImage 钦赐 PE Image 在虚拟内部存款和储蓄器中所占的长空尺寸。
  6. SizeOfHeader PE 头的轻重缓急
  7. Subsystem 用来分别系统驱动文件与平常可执行文件。
  8. NumberOf昂CoravaAndSizes 钦定DataDirectory
    数组的个数,就算最终3个值,提出个数是16,但实质上PE
    装载照旧经过辨认那一个值来明确大小的。至于DataDirectory 是怎么样看上面
  9. DataDirectory 它是三个由IMAGE_DATA_DIRECTO瑞虎Y
    结构体组成的数组,数组每一项都有定义的值,里边有局地第贰的值,EXPO福特ExplorerT/IMPOCR-VT/RESOU途观CE,
    TLS direction 是重中之重关心的。

偏移类型的含义如下:

偏移类型的意义如下:

PE底部包涵了Dos头,一直到节表的了断地方,.text区段开端在此之前
  • 数量目录表
![](https://upload-images.jianshu.io/upload_images/5676193-cfbc56c21cd7568f.png)

image.png

段头

PE 的段头直接沿用的COFF 的段头结构,下面也说过了,大家查阅notepad++
的段头,能够获取种种段名,以及其音讯,那里,大家能够动用部分软件查看,特别有益于:

home88一必发 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.

多少目录表也是多个组织体数组——>每一个结构体里面著录的是每一种表所对应的CR-VVA以及大小

(扩展:

  • 区段头表(它是3个布局体数组)是由五个IMAGE_SECTION_HEADESportage那样的结构体组成,以三个全是0的结构体结尾;
  • 导入表也是三个构造体数组(前面会重点讲),以3个全0成分结尾,导入表中的IMAGE_THUNK_DATA(文件并未加载的时候,OrignalFirstThunk与FirstThunk指向IMAGE_THUNK_DATA)也是二个结构体数组;
  • 重定位表:它也是一个布局体数组,以全0成分结尾
  • 能源表:它里面也富含结构体数组(更为详细的能够查看有关文献)
    )

RVA to RAW

明白PE
最重视的3个有个别正是精晓文件从磁盘到内部存款和储蓄器地址的照耀进度,做逆向的人口,唯有理解地理解才能跟踪到程序的调用进程和岗位,才能分析和查找漏洞。

对于文本和内部存款和储蓄器的照耀关系,其实不会细小略,他们经过3个简单易行的公式总计而来:

home88一必发 21

换算公式是如此的:

RAW -PointToRawData = RVA – VirtualAddress

查找进程就是先找到奥迪Q3VA
所在的段,然后依据公式总计出文件偏移。因为大家通过逆向工具,能够在内部存款和储蓄器中查找到所在的KoleosVA,进而大家就能够总计出在文书中所在的任务,那样,就足以手动举办修改。

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

home88一必发 22

若是大家的安德拉VA 地址是四千,那么合算办法便是,查看区段,发现在.text
中,陆仟-1000+400 = 4400,那正是RAW
00004400,而其实,因为大家的ImageBase
是00600000,所以,大家在反编写翻译时候内部存储器中的地址是00405000.

接下去,使我们的PE头中的大旨内容,IAT 和 EAT,也便是 Import address
table, export address table.

其他普通话翻译:

此外汉译:

PE中有协会体数组的布局的计算:

数录节入重!

  • 节表(区段头表)

home88一必发 23

image.png

.text 段:代码段
.data段:数据段
.bss段:表示未伊始化的多寡,比如Static变量
.rdata 段:表示只读的数量,比如字符串
……
.relcoc段:存款和储蓄重一直音讯的区段
各变量存放于哪个区:
常量 ——————>.rdata区
静态变量————->.bss区
全局变量————–>.data 区
节表里面包车型大巴多少个重庆大学数据:
VirtualAddress:那些区段的相持虚拟地址
SizeofRawData:这几个区段在磁盘中的大小,进行了文件对齐
PointerToRawData:区段的文书偏移,正是其一区段在磁盘文件中的起初地方
一个首要的公式:
offset(转)=LX570VA(须求转移的大切诺基VA)-CR-VVA(所在区段的哈弗VA)+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

home88一必发 24

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

home88一必发 25

小编们双击汇编窗口,运转编写制定,发现真正是call 的这些数值:

home88一必发 26

而是难点来了,上面是E8 35D7FFFF,上边地址却是 00C62178。其实那是Win
Visita, Win 7的ASL福睿斯技术,首要正是指向缓冲溢出攻击的一种保养技巧,通过随机化布局,让逆向跟踪者,难以搜索地址,就不便简单的开展溢出攻击。可是还能透过跳板的办法,找到溢出的点子,那就是后话了。

最近得以鲜明的是,35D7FFFF 能够认为保存的数值就是 CreateFileW
的地点。而为啥不直接利用CALL 7509168B 那种措施直接调用呢?
Kernel32.dll 版本各分歧,对应的CreateFileW
函数也各不同,为了合作各个条件,编写翻译器准备了CreateFileW
函数的骨子里地址,然后记下DWO卡宴D PTPRADO DS:[xxxxxx]
那样的下令,执行文书时候,PE 装载器将CreateFileW 函数地址写到那几个任务。

与此同时,由于重一直的原故存在,所以也不能够平昔运用CALL 7509168B
的主意,比如七个DLL 文件有同等的
ImageBase,装载的时候,一个装载到该职分然后,另1个就不能够装载该职位了,必要换地方。所以大家不可能对实际地址进行硬编码。

IMAGE_IMPORT_DESCRIPTOR

home88一必发 27

home88一必发 28

对于一个不足为奇程序来说,需求导入多少个库,就会设有几个如此的结构体,这么些结构体组成数组,然后数组最终是以NULL
结构体结束。当中有多少个重点的分子:

  • OriginalFirstThunk INT Import Name Table 地址,RVA
  • Name 库名称字符串地址,大切诺基VA,就是说该地址保存库名称
  • 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_home88一必发,BY_NAME 的Hint 可能是name
    项,获取相应函数的前奏地点 GetProcAddress(“GetCurrentThreadId”)
  6. 读取FistrThunk 成员,获得IAT 地址。
  7. 将上面获得的函数地址输入相应IAT 数组值。
  8. 重复4-7 到INT 结束。

此地就发出了多个嫌疑,OriginalFirstThunk 和 First Thunk
都指向的是函数,为什么神经过敏呢?

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

home88一必发 29

OriginalFirstThunk 和 FirstThunk 他们都是七个档次为IMAGE_THUNK_DATA
的数组,它是叁个指针大小的一道(union)类型。
每一个IMAGE_THUNK_DATA
结构定义1个导入函数音信(即指向结构为IMAGE_IMPORT_BY_NAME
的家伙,这个家伙稍后再议)。
接下来数组最后以二个剧情为0 的 IMAGE_THUNK_DATA 结构作为实现标志。
IMAGE_THUNK_DATA32 结构体如下:

home88一必发 30

因为是Union 结构,IMAGE_THUNK_DATA 事实上是三个双字大小。
分明如下:

当 IMAGE_THUNK_DATA 值的万丈位为 1时,表示函数以序号方式输入,那时候低
叁12位被视作二个函数序号。

当 IMAGE_THUNK_DATA 值的最高位为
0时,表示函数以字符串类型的函数名艺术输入,那时双字的值是三个途锐VA,指向2个 IMAGE_IMPORT_BY_NAME 结构。

我们再看IMAGE_IMPORT_BY_NAME 结构:

home88一必发 31

结构中的 Hint
字段也表示函数的序号,不过这么些字段是可选的,有些编写翻译器总是将它设置为 0。

Name 字段定义了导入函数的名号字符串,那是3个以 0 为最后的字符串。

当今首要来了:

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

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

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

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

home88一必发 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_DIRECTOXC90Y
结构体以的位置,他在IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress
的值就是 IMAGE_EXPORT_DIREDCTOLacrosseY 的前奏地点。

home88一必发 33

IMAGE_EXPORT_DIRECTO昂CoraY结构体如下:

home88一必发 34

那里边一样是这么多少个关键的分子:

  • NumberOfFunctions 实际Export 函数的个数
  • NumberOfNames Export 函数中签名的函数个数
  • AddressOfFunctins Export 函数地址数组,数组个数是上边的NOF
  • AddressOfNames 函数名称地址数组,个数是上边的NON
  • AddressOfNameOrdinals Ordinal 地址数组,个数等于上边NON
  • Name 多少个EvoqueVA 值,指向八个概念了模块名称的字符串。如固然Kernel32.dll
    文件被改名换姓为”Ker.dll”。依旧能够从那几个字符串中的值得知其在编写翻译时的文件名是”Kernel32.dll”。
  • Base:导出函数序号的初叶值,将AddressOfFunctions
    字段指向的入口地址表的索引号加上那一个初叶值就是对应函数的导出
    序号。
    以kernel32.dll 为例,大家看一下:
![](https://upload-images.jianshu.io/upload_images/30117-6bb373c33a5b9995.jpg)

从上边那么些成员,大家其实可以见到,是有二种办法提需要这多少个想调用该库中等高校函授数的,一种是直接从序号查找函数入口地址导入,一种是由此函数名来寻找函数入口地址导入。

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

home88一必发 35

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

咱俩独家说一下三种格局:

当已知导出序号的时候

  1. Windows 装载器定位到PE 文件头,
  2. 从PE 文件头中的 IMAGE_OPTIONAL_HEADEXC9032
    结构中取出数据目录表,并从第三个数据目录中赢得导出表的大切诺基VA ,
  3. 从导出表的 Base 字段得到起始序号,
  4. 将需求摸索的导出序号减去起初序号,获得函数在进口地址表中的索引,
  5. 检查和测试索引值是不是高于导出表的 NumberOfFunctions
    字段的值,倘诺超出后者的话,表明输入的序号是无效的用那个索引值在
    AddressOfFunctions
    字段指向的导出函数入口地址表中取出相应的花色,这就是函数入口地址的CR-VVA
    值,当函数棉被服装入内部存款和储蓄器的时候,这些XC60VA
    值加上模块实际装入的营地址,就拿走了函数真正的进口地址

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

  1. 从导出表的 NumberOfNames
    字段得到已命名函数的总数,并以这几个数字作为循环的次数来布局三个循环往复
  2. 从 AddressOfNames
    字段指向获得的函数名称地址表的率先项开头,在循环上将每一项定义的函数名与要寻找的函数名绝相比较,要是没有其它多少个函数名是契合的,表示文件中一向不点名名称的函数,如若某一项定义的函数名与要寻找的函数名符合,那么记下那个函数名在字符串地址表中的索引值,然后在
    AddressOfNamesOrdinals
    指向的数组中以同样的索引值取出数组项的值,大家那里就算那几个值是x
  3. 最终,以 x 值作为索引值,在 AddressOfFunctions
    字段指向的函数入口地址表中得到的 途胜VA 正是函数的入口地址

相似的话,做逆向恐怕是写代码都是第2种办法,大家以kernel32.dll
中的GetProcAddress 函数为例,其操作原理如下:

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

写了那般多,实际上算是对文件结构有了3个入门的认识,至少知道在程序运维进程中,系统是怎么开始展览操作和链接的,而愈发详细的内容注入运营时压缩,DLL
注入,API 钩取等技能,就需求在那么些基础之上继续挖掘,所以PE ,ELF
文件结构的剖析是非常重要的。

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

例子:

例子:

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

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

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

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

home88一必发 36

home88一必发 37

找到数据:

找到数据:

home88一必发 38

home88一必发 39

VirtualAddress 为 0x一千,SizeOfBlock 为 0x64。第二个条款为
0x338C,高3位为 0x3,offset为 0x38C,即偏移地址为 0x138C (由 0x一千 +
0x38C得来)应用于此地址上总体三拾位。打开C32Asm反汇编查看:

VirtualAddress 为 0x一千,SizeOfBlock 为 0x64。第①个条款为
0x338C,高几位为 0x3,offset为 0x38C,即偏移地址为 0x138C (由 0x一千 +
0x38C得来)应用于此地址上全部叁十一个人。打开C32Asm反汇编查看:

home88一必发 40

home88一必发 41

发表评论

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

网站地图xml地图