分享
 
 
 

解读 ELF 文件

王朝other·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

本文叙述如何解读 ELF 文件。

打开一个 ELF 文件解读时,我们首先遇到的是一个 ELF 文件头。ELF 文件头

给出解读整个 ELF 文件的路径图,它是一个固定的结构。文件头的结构在系统

头文件 elf.h 中定义,如果是 32 位的二进制文件,它是一个 Elf32_Ehdr

结构,如果是 64 位的二进制文件,则是一个 Elf64_Ehdr 结构。无论是何种

结构,结构的第一个成员是一个 16 字节的 e_ident,它给出了整个 ELF 文

件的解读方式。究竟是 32 位的 Elf32_Ehdr 结构还是 64 位的 Elf64_Ehdr

结构,就看 e_ident[4] 的内容了。从文件偏移的角度来说,也就是文件偏移

为 4 的字节确定了 ELF 文件究竟是 32 位的还是 64 位的。这里我们遵从习

惯把文件开头的起始第一个字节的文件偏移约定为 0,下面的所有叙述都遵从

这个约定。

于是我们要做的第一件事是解读这个 e_ident,确定 ELF 文件是 32 位的还

是 64 位的,或者是其他位数的,从而确定 ELF 文件头的结构。为此,假定

打开ELF 文件时返回的文件描述符是 fd,

lseek(fd, 0, SEEK_SET);

read(fd, buf, 16);

读出的 buf 里前四个字节是 Magic Number(对应文件偏移 0-3)。如果

buf[0] = 0x7f、buf[1] = 'E'、buf[2] = 'L'、buf[3] = 'F'

则表明这是一个 ELF 格式的二进制文件,否则不是。如前面所述,我们首先关

注的是 buf[4]。如果 buf[4] 的值是 1,则是 32 位的;如果是 2,则是 64

位的。接下来是 buf[5],它给出字节序特性。如果它的值是 1,则是 LSB 的;

如果是 2,则是 MSB 的。对 Intel x86 机器,buf[5] = 1;对 Sun Sparc,

buf[5] = 2。跟着 buf[5] 的 buf[6] 给出 ELF 文件头的版本信息,当前它

的值是 EV_CURRENT(参见 elf.h 中的宏定义)。对 buf[6] = EV_CURRENT

的 ELF 文件头,从 buf[7] 开始,也即 e_ident 后面的 9 个字节全部为零,

暂时没有使用。

现在确定了文件头的结构,我们就可以解读文件头了。下文中我们以 32 位的

ELF 文件为例来说明。对 64 位的,大同小异,把所有 Elf32_*** 结构换成

对应的 Elf64_*** 结构,看看 elf.h 就什么都清楚了。32 位的 ELF 文件头

结构定义如下:

#define EI_NIDENT (16)

typedef uint16_t Elf32_Half;

typedef uint32_t Elf32_Word;

typedef uint32_t Elf32_Addr;

typedef uint32_t Elf32_Off;

typedef struct {

unsigned char e_ident[EI_NIDENT]; /* 上文所说的 e_ident */

Elf32_Half e_type; /* 文件类型 */

Elf32_Half e_machine; /* 机器类型 */

Elf32_Word e_version; /* 文件版本 */

Elf32_Addr e_entry; /* 程序入口虚地址 */

Elf32_Off e_phoff; /* 程序头表文件偏移 */

Elf32_Off e_shoff; /* 节头表文件偏移*/

Elf32_Word e_flags; /* 处理器相关的标志 */

Elf32_Half e_ehsize; /* ELF 文件头大小 */

Elf32_Half e_phentsize; /* 程序头表每个表项的大小 */

Elf32_Half e_phnum; /* 程序头表的表项数目 */

Elf32_Half e_shentsize; /* 节头表每个表项的大小*/

Elf32_Half e_shnum; /* 节头表的表项数目 */

Elf32_Half e_shstrndx; /* 节名字符串的节头表表项索引 */

} Elf32_Ehdr;

结构的各个成员的含义如注释中所解释的。对 ELF 文件,有两个视图,一个是

从装载运行角度的,另一个是从连接角度的。从装载运行角度,我们关注的是程

序头表,由程序头表的指引把 ELF 文件加载进内存运行它。从连接的角度,我

们关注节头表,由节头表的指引把各个节连接组装起来。e_type 的值与这两个

视图相联系,由它我们可以知道能够从哪个视图去解读。如果 e_type = 1,表

明它是重定位文件,可以从连接视图去解读它;如果 e_type = 2,表明它是可

执行文件,至少可以从装载运行视图去解读它;如果 e_type = 3,表明它是共

享动态库文件,同样可以至少从装载运行视图去解读它;如果 e_type = 4,表

明它是 Core dump 文件,可以从哪个视图去解读依赖于具体的实现。

按照这两个视图,整个 ELF 文件的内容这样来组织:首先是 ELF 文件头,也

就是上面的 Elf32_Ehdr 结构。或者对 64 位的 ELF 文件,是 Elf64_Ehdr

结构。ELF 文件头位于文件开始处,无论 e_type 的值是什么,它是必须有的。

其次是程序头表,对可执行文件(e_type = 2)和动态库文件(e_type = 3),它

是必须有的。对重定位文件(e_type = 1),程序头表的有无是可选的。例如用

gcc 的 -c 选项生成的 .o 文件,就没有程序头表。但无论如何,e_phoff 和

e_phnum、e_phentsize 给出了 ELF 文件的程序头表信息。没有程序头表时它

们的值为零。然后就是就是节头表,对可执行文件和动态库文件,它的有无是

可选的,对重定位文件,它是必须有的。e_shoff 和 e_shnum、e_shentsize

给出节头表信息。最后就是文件的代码和数据这些具体内容了。如果有节头表,

从连接视图去解读,ELF 文件的具体代码和数据内容是以节为单位组织的。所

有的代码和数据都分属于某一节,并且不能同时属于两个节。各个节不能交叉,

不能有同时两个节覆盖同一内容。每一节在节头表中有一个表项与之对应,给

出该节的相关信息。如果有程序头表,从装载运行视图去解读,所有代码和数

据都分属于某一程序段。与连接视图不同,此时有交叉的情况。某些内容可能

同时属于几个程序段,也即可能有几个段覆盖同一内容。同时,从程序头表来

看,可能某些段不包含任何具体的代码和数据内容。例如,给出动态连接信息

的程序段的所有内容都同时数据段。注意不要把这里所说的程序段与我们通常

所说的文本段、数据段和堆栈段这几个概念相混淆,虽然它们有联系。程序加

载进内存时,根据程序头表信息来就解读。

从连接视图来解读,其中有一节的内容是一些以零结尾的字符串。e_shstrndx

给出该节在节头表中的表项索引。这些字符串是各节的名字。

了解了这些后,我们可以分别从两个视图来解读 ELF 文件了。先看连接视图,

于是我们

Elf32_Ehdr e_hdr;

void *SecHdrTbl;

lseek(fd, 0, SEEK_SET);

read(fd, &e_hdr, sizeof(e_hdr));

SecHdrTbl = malloc(e_hdr.e_shnum * e_hdr.e_shentsize);

lseek(fd, e_hdr.e_shoff, SEEK_SET);

read(fd, SecHdrTbl, e_hdr.e_shnum * e_hdr.e_shentsize);

我们看看节头表是什么样的,因为节头表的各个表项给出了如何从连接视图

解读 ELF 文件的路径图。节头表的每个表项是一个如下的结构:

typedef struct

{

Elf32_Word sh_name; /* 节名索引 */

Elf32_Word sh_type; /* 节类型 */

Elf32_Word sh_flags; /* 加载和读写标志 */

Elf32_Addr sh_addr; /* 执行时的虚地址 */

Elf32_Off sh_offset; /* 在文件中的偏移 */

Elf32_Word sh_size; /* 字节大小 */

Elf32_Word sh_link; /* 与其他节的关联 */

Elf32_Word sh_info; /* 其他信息 */

Elf32_Word sh_addralign; /* 字节对齐 */

Elf32_Word sh_entsize; /* 如果由表项组成,每个表项的大小 */

} Elf32_Shdr;

再看装载运行视图:

void *ProHdrTbl;

ProHdrTbl = malloc(e_hdr.e_phnum * e_hdr.e_phentsize);

lseek(fd, e_hdr.e_phoff, SEEK_SET);

read(fd, SecHdrTbl, e_hdr.e_phnum * e_hdr.e_phentsize);

每个程序头表的每个表项的结构为:

typedef struct

{

Elf32_Word p_type; /* 段类型 */

Elf32_Off p_offset; /* 在文件中的偏移 */

Elf32_Addr p_vaddr; /* 执行时的虚地址 */

Elf32_Addr p_paddr; /* 执行时的物理地址 */

Elf32_Word p_filesz; /* 在文件中的字节数 */

Elf32_Word p_memsz; /* 在内存中的字节数 */

Elf32_Word p_flags; /* 标志 */

Elf32_Word p_align; /* 字节对齐 */

} Elf32_Phdr;

我们看一看这两个视图之间的相互关联,对动态库文件,共有三个程序段,如

果是用 gcc 编译生成的,按文件偏移和虚地址增长次序排列,文本段包含如下

这些节:

.hash

.dynsym

.dynstr

.gnu.version

.gnu.version_d

.gnu.version_r

.rel.data

.rel.got

.rel.plt

.init

.plt

.text

.fini

.rodata

同样是按文件偏移和虚地址增长次序排列,数据段包含如下这些节:

.data

.eh_frame

.ctors

.dtors

.got

.dynamic

.bss:

另外还有一个程序段,它给出动态连接信息,它只包含有一节

.dynamic

我们看到,这一段与数据段有交叉了。此外还有一些节它们不属于任何一个程

序段,这些节是:

.comment

.note

.shstrtab

.symtab

.strtab

对可执行文件,共有六个程序段,如果是用 gcc 编译生成的,按文件偏移和虚

地址增长次序排列,文本段包含如下这些节:

.interp

.note.ABI-tag

.hash

.dynsym

.dynstr

.gnu.version

.gnu.version_r

.rel.got

.rel.plt

.init

.plt

.text

.fini

.rodata

同样是按文件偏移和虚地址增长次序排列,可执行文件的数据段包含如下这些

节:

.data

.eh_frame

.ctors

.dtors

.got

.dynamic

.bss

程序解释段(INTERP)与文本段相交叉,只包含 .interp 一节。给出动态连接

信息的程序段同样与数据段相交叉,只包含 .dynamic 节。另一个程序段,与

文本段相交叉,包含 .note.ABI-tag 节,它给出辅助信息。此外,还有一个

程序段,它指程序头表自身。同动态库文件一样,下面的一些节不属于任何程

序段:

.stab

.stabstr

.comment

.note

.shstrtab

.symtab

.strtab

[未完待续]

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有