逆向分析:ELF文件的组成结构

简介: 逆向分析:ELF文件的组成结构

接上文,本文主要介绍 linux 目标文件的组成,逆向分析 ELF 文件结构


1.目标文件的构成


编译器编译源码后生成的文件叫目标文件(Object File)。目标文件从结构上讲,它是已编译后的可执行文件格式,只是还没有经过链接过程,其中有些符号或有些地址还没有调整。


目标文件本身就是按照可执行文件格式存储的,只是跟真正的可执行文件在结构上稍有不同。


1.1格式


目标文件就是源代码编译之后但未进行链接的哪些中间文件(Windows 下 是 .obj,linux 下是.o)。它与可执行文件的内容和结构基本相同。


目标文件和可执行文件一般采用同一种格式,这种存储格式为 ELF(linux Executable Linkable Format),window 下 是 PE(Portable Executable)

在 linux 下 除了目标文件和可执行文件采用 ELF 格式外,静态库文件、动态库文件也都按照可执行文件 ELF 的格式存储。


ELF 文件类型
说明
实例

可重定位文件

(Relocatable File)

此类文件包含代码和数据、可被链接为可执行文件或共享目标文件,静态链接库被归为此类 Linux 下的 .o:hello.o
可执行文件

(Executable File)

此类文件包含了可以直接执行的文件 /bin/bash等

共享目标文件

(Share Object File)

此类文件包含了可以直接执行的文件 linux 的.so、/lib/x86_64-linux-gnu/libc-2.23.so

核心转储文件

(Core Dump File)

当进程意外终止,系统生成的核心转储文件 Linux 下的 core dump文件



linux 可以通过 file 命令查看。
目标文件和可执行文件跟操作系统和编译器密切相关,不同平台下的格式会有些差异。


目标文件:


  • 文件格式:ELF 64bit


  • LSB:小端


  • x86-64:平台 (和工具链有关)


  • relocatable:可重定位


  • not stripped:没有 stripped


image.png


可执行程序:


  • 文件格式:ELF 64bit


  • LSB:小端


  • executable:可执行


  • x86-64:平台


  • statically linked:静态链接


  • not stripped:没有 stripped


image.png


动态库:


  • 文件格式:ELF 64bit


  • LSB:小端


  • share object:共享文件


  • x86-64:平台


  • dynamically linked:动态链接


1.2组成


目前文件中的内容至少有编译后的机器指令代码和数据,除了这些,目标文件中还包含了链接时所必须的一些信息,比如符号表、调试信息、字符串等。


一般目标文件将这些信息按照不同的属性,以段(segment)的形式存储。


image.png


  • 代码段(.text):源代码编译过后的机器指令。


  • 数据段(.data):全局变量和局部静态变量被放在数据段。


  • 只读数据段(.rodata):const 修饰的变量和其他字符串常量。


  • bss 段:为未初始化的符号,预留足够的空间。未初始化的变量在 bss 段。
  • 其他段。


2.目标文件的分析


下面我们根据一段代码来分析目标文件的内部结构(以 MIPS 平台为例):


image.png


2.1ELF 文件头


ELF 文件开头是一个头文件,它描述的是整个文件的属性,包括:文件的类型、目标硬件、目标操作系统等信息。


readelf -h 可读取 elf 文件头。头文件包含如下:


  • ELF 魔数。


  • 文件机器字节长度。


  • 数据存储方式。


  • 版本。


  • 运行平台。


  • ABI 版本。


  • ELF 重定位类型。


  • 硬件平台。


  • 硬件平台版本。


  • 入口地址(目标文件入口地址为 0,只有相对位置)。


  • 程序的入口和长度。


  • 段表的位置和长度。


image.png


目标文件 ELF 头


image.png


可执行程序 ELF 头


image.png


2.2段表


头文件之后,紧接着是一个段表,段表是一个描述文件各个段的数组。描述了文件中各个段在文件中的偏移位置以及段的属性。(readelf -S, objdump -h)


主要的段如下:


  • 代码段(.text)。


  • 数据段(.data)。


  • bss 段。


  • 只读数据段(.rodata)。


  • .comment


  • (符号表段).symtab


  • 符号表(.shstrtab):Section String table


  • (重定位代码段).rel.text




image.png


各段详细信息及字节对齐:


image.png


最终组成结构如下。中间Section Table 和 其他某些段因为对齐原因,会有若干字节的间隔。整个目标文件的大小刚好是 1588 字节。


image.png


代码段


C 语言编译后的机器指令,都保存在代码段(.text)。我们可以通过 objdump 来查看段的信息。-s 可以将所有段以 16 进制打印出来。-d 可以将所有包含指令的段反汇编。


可以看到代码段保存的是 f 和 main 的指令。


image.png

image.png


数据段


数据段(.data)保存的是哪些已经初始化(非零)的全局变量(静态变量和非静态变量)和局部静态变量。


gint_val 和 static_val 存在数据段。0x64 和 0x65 刚好对应 100 和 101(ASCII d 和 e)


image.png


数据段是 16 字节对齐所以刚好占 16 个字节大小。对齐和平台有关。


image.png


只读数据段


只读数据段(.rodata),保存的是只读数据。一般是程序中 const 修饰的只读变量 和字符串常量(包括 printf 函数中的格式化字符串%d)。可以看到,我们可以通过优化.rodata 段的大小,进而优化程序的大小。


image.png


bss 段


bss 段(.bss),用来记录所有未初始化的全局变量(或者零初始化)和局部静态变量大小总和,然后为其预留位置。


未初始化的全局变量(或者零初始化)和局部静态变量,因为都是 0,所以在.data 段开辟存储空间存储 0 是没有必要的。


符号表段


符号表(.symtab),以数组结构的形式保存符号信息(函数和变量),对于函数和变量符号值就是他们的地址。(readelf -s)


image.png


  • f 和 main 函数的 Ndx 对应的值是 1,表示在.text 段(.text 段在段表中的索引是 1),类似是 FUNC,value 分别是 0x00000000 和 0x00000040,表明两个函数指令字节码的首字节分别在.text 的 0x00000000 和 0x00000040 偏移处。


  • printf 的 Ndx 是 UND,表明这个符号在 object_file.o 里面没有被定义,仅仅是引用。


  • static_val.1843 和 gint_val 两个符号的 Ndx 都是 3,说明他们都被定义在数据段。value 分别是 0x00000000 和 0x00000004,表明两个函数指令字节码的首字节分别在.data 的 0x00000000 和 0x00000004 偏移处。


重定位段表


重定位表也是一个段,用于描述在重定位时连接器如何修改相应段里的内容。对于.text,对应的重定位表是.rel.text。使用objdump -r 查看重定位表。


image.png


自定义段表


正常情况下,GCC 编译出来的目标文件会被放到.text 段,全局变量和静态变量会被放到.data 和.bss 段。


但是有些时候我们希望有些变量或者代码可以放到我们指定的段中去,以实现某些特定的功能。GCC 提供了一种扩展机制,可以指定段。


变量:


__attribute__((section("start_var_init"))) int init_status = 1;


函数:


__attribute__((section("start_fun_init"))) void start_func()
{
    //init code
    return ;
}


在全局变量或者函数前加入 _attribute_((section("name"))) 属性就可以把相应的变量或者函数放入到”name“段里。


示例:


image.png

image.png

image.png

 

3.总结


本文主要介绍了,ELF 文件组成结构,通过分析目标文件的组成,来理解程序的具体分布。了解目标文件的各段内容和作用,有助于我们提高对程序的掌控力。比如优化程序对应用程序进行加解密、调试等等。


3.1相关命令


命令
说明
readelf -h 读取ELF文件头
readelf -S 查看段的属性
readelf -s 查看符号表
objdump -h 查看段的属性
objdump -s 将所有段的内容以十六进制打印出来
objdump -d 将所有包含的指令反汇编
objdump -r 查看重定位段


3.2分段说明


段名
说明
.text 代码段。存放可执行文件的指令,这部分区域在程序运行前就已经确定。通过 objdump -s -d 查看
.data 数据段。保存已经初始化(非零初始化)的全局变量和静态局部变量
.bss bss段。未初始化(零初始化)的全局变量和静态局部变量保存在bss段,准确来说.bss段为他们预留了位置,等到最终链接时在分配到.bss段(具体和编译器有关)
.rodata 只读数据段。存放的是只读数据,一般是程序里面的只读变量(const修饰的变量)和字符串变量(printf 的格式化字符也算)
.comment 存放的是编译器的版本信息
.symtab 符号表段。用来定位、重定位程序中符号定义和引用的信息,简单的理解就是符号表记录了该文件中的所有符号,所谓的符号就是经过修饰了的函数名或者变量名,不同的编译器有不同的修饰规则。
.shstrtab 字符串表段。存放着所有符号的名称字符串
.dynamic 动态链接信息。
...
其他
相关文章
|
1月前
|
Unix 编译器 Linux
【计算机基础 ELF文件】深入探索ELF文件:C++编程中的关键组成部分
【计算机基础 ELF文件】深入探索ELF文件:C++编程中的关键组成部分
86 0
|
1月前
|
编译器 开发工具 C语言
【C语言】第一回(源(.c)文件怎么生成可执程序(.exe)文件)
【C语言】第一回(源(.c)文件怎么生成可执程序(.exe)文件)
|
6月前
|
存储 Windows
9.2 Windows驱动开发:内核解析PE结构导出表
在笔者的上一篇文章`《内核特征码扫描PE代码段》`中`LyShark`带大家通过封装好的`LySharkToolsUtilKernelBase`函数实现了动态获取内核模块基址,并通过`ntimage.h`头文件中提供的系列函数解析了指定内核模块的`PE节表`参数,本章将继续延申这个话题,实现对PE文件导出表的解析任务,导出表无法动态获取,解析导出表则必须读入内核模块到内存才可继续解析,所以我们需要分两步走,首先读入内核磁盘文件到内存,然后再通过`ntimage.h`中的系列函数解析即可。
48 0
9.2 Windows驱动开发:内核解析PE结构导出表
|
Linux 数据安全/隐私保护
使用ofd6x工具解析elf文件
使用ofd6x工具解析elf文件
171 0
使用ofd6x工具解析elf文件
|
存储 IDE Java
【精通内核】计算机程序的本质、内存组成与ELF格式
精通真正的高并发编程,不仅仅是API的使用和原理!计算机最基础的程序是怎么组成的呢?本文深入浅出,讲解程序的本质(编译的过程)、组成(程序所需的内存)与格式(ELF),希望读者可以构建计算机从写代码到编译到执行的链路的底层思维。
|
架构师 Android开发
【Android 逆向】ELF 文件格式 ( ELF 文件类型 | ELF 文件对应 CPU 架构 | ELF 目标文件版本 | 可执行程序起始地址 )
【Android 逆向】ELF 文件格式 ( ELF 文件类型 | ELF 文件对应 CPU 架构 | ELF 目标文件版本 | 可执行程序起始地址 )
166 0
【Android 逆向】ELF 文件格式 ( ELF 文件类型 | ELF 文件对应 CPU 架构 | ELF 目标文件版本 | 可执行程序起始地址 )
|
安全 编译器 Android开发
【Android 逆向】ELF 文件格式 ( 程序头数据 | 节区头数据 | 动态符号表 )
【Android 逆向】ELF 文件格式 ( 程序头数据 | 节区头数据 | 动态符号表 )
125 0
【Android 逆向】ELF 文件格式 ( 程序头数据 | 节区头数据 | 动态符号表 )
|
编译器 Linux Android开发
【Android 逆向】ELF 文件格式 ( ELF 文件简介 | ELF 文件结构 )
【Android 逆向】ELF 文件格式 ( ELF 文件简介 | ELF 文件结构 )
281 0
【Android 逆向】ELF 文件格式 ( ELF 文件简介 | ELF 文件结构 )
|
Android开发
【Android 逆向】ELF 文件格式 ( ELF 文件当前版本号 | 操作系统 ABI 信息 | ABI 版本 | 文件头校验 | 文件头长度信息 )
【Android 逆向】ELF 文件格式 ( ELF 文件当前版本号 | 操作系统 ABI 信息 | ABI 版本 | 文件头校验 | 文件头长度信息 )
261 0
【Android 逆向】ELF 文件格式 ( ELF 文件当前版本号 | 操作系统 ABI 信息 | ABI 版本 | 文件头校验 | 文件头长度信息 )
|
Android开发
【Android 逆向】ELF 文件格式 ( ELF 文件头 | ELF 文件头标志 | ELF 文件位数 | ELF 文件大小端格式 )(一)
【Android 逆向】ELF 文件格式 ( ELF 文件头 | ELF 文件头标志 | ELF 文件位数 | ELF 文件大小端格式 )(一)
123 0
【Android 逆向】ELF 文件格式 ( ELF 文件头 | ELF 文件头标志 | ELF 文件位数 | ELF 文件大小端格式 )(一)