欢迎来到千学网!
您现在的位置:首页 > 实用文 > 其他范文

elf文件格式 2Unix系统

时间:2022-05-23 20:45:38 其他范文 收藏本文 下载本文

【导语】下面是小编收集整理的elf文件格式 2Unix系统(共9篇),供大家参考借鉴,欢迎大家分享。

elf文件格式 2Unix系统

篇1:Elf文件DT

一.前言 二.分析 三.结论 四.实现 五.参考 一.前言 前一段时间回顾elf文件方面的知识时,翻了以前很多优秀的文章,在xfocus的帮助文件unix版中看到了alert7大侠 ELF后门之DT_RPATH篇 这篇文章,看了一下时间9月/14号,那段时间忙着重修,没到网吧去,

Elf文件DT

alert7

篇2:elf文件格式 1Unix系统

elf文件格式-- 1 这份文档和原始的那份ELF文件格式的文档有以下一个不同: 1. 忽略了分页记数 , 2. 因为上述原因,在这篇内容目录中去掉了页号,索引完全被忽略。 (不象Postscript文档,txt文本可以用来搜索) 3. 页标题的内容和文章的页脚已经在开始的时候被

elf文件格式-- 1

这份文档和原始的那份ELF文件格式的文档有以下一个不同:

1. 忽略了分页记数 。

2. 因为上述原因,在这篇内容目录中去掉了页号,索引完全被忽略。

(不象Postscript文档,txt文本可以用来搜索)

3. 页标题的内容和文章的页脚已经在开始的时候被换掉了。

4. 文章的排版也已经修正过了。

5. 如果必要,不同的字体已经被忽略了。大部分地方,这片文档能让你

充分的理解。然而,很小的地方,原始的文档使用了斜体字来指出文

章中的字符变量。在那种情况下,本文使用<尖括号>。在原始的文档

中没有出现尖括号。

6. 原始的文档有三个错误,如果你是不经意读它的话,是不会明显

就能找出的。但是在这里,明确的被鉴别出来了。

我很冒昧的纠正了那些错误。在他们的位置用一个做上了标记。

可能还有其他我没有看出来的的错误。

如果有如何其他的区别都是我的责任。这样的错误请

mailto:breadbox@muppetlabs.com.

Brian Raiter

[Last edited Fri Jul 23 ]

________________________________________________________________

EXECUTABLE AND LINKABLE FORMAT (ELF)

Portable Formats Specification, Version 1.1

Tool Interface Standards (TIS)

________________________________________________________________

=========================== Contents 内容===========================

序言

1. OBJECT文件

导言

ELF头(ELF Header)

Sections

String表(String Table)

Symbol表(Symbol Table)

重定位(Relocation)

2. 程序装载与动态连接

导言

Program头(Program Header)

Program装载(Program Loading)

Dynamic连接(Dynamic Linking)

3. C LIBRARY

C Library

________________________________________________________________

导言

________________________________________________________________

ELF: 可执行连接格式

可执行连接格式是UNIX系统实验室(USL)作为应用程序二进制接口

(Application Binary Interface(ABI)而开发和发布的。工具接口标准委

员会(TIS)选择了正在发展中的ELF标准作为工作在32位INTEL体系上不同操

作系统之间可移植的二进制文件格式。

假定开发者定义了一个二进制接口集合,ELF标准用它来支持流线型的软件

发展。 应该减少不同执行接口的数量。因此可以减少重新编程重新编译的

代码。

关于这片文档

这篇文档是为那些想创建目标文件或者在不同的操作系统上执行文件的开发

着准备的。它分以下三个部分:

* 第一部分, “目标文件Object Files”描述了ELF目标文件格式三种主要

的类型。

* 第二部分, “程序和动态连接”描述了目标文件的信息和系统在创建

运行时程序的行为。

* 第三部分, “C 语言库”列出了所有包含在libsys中的符号,标准的ANSI C

和libc的运行程序,还有libc运行程序所需的全局的数据符号。

注意: 参考的X86体系已经被改成了Intel体系。

________________________________________________________________

1. 目标文件(Object file)

________________________________________________________________

========================= 序言 =========================

第一部分描述了iABI的object文件的格式, 被称为ELF(Executable

and Linking Format). 在object文件中有三种主要的类型。

* 一个可重定位(relocatable)文件保存着代码和适当的数据,用来和其他的

object文件一起来创建一个可执行文件或者是一个共享文件。

* 一个可执行(executable)文件保存着一个用来执行的程序;该文件指出了

exec(BA_OS)如何来创建程序进程映象。

* 一个共享object文件保存着代码和合适的数据,用来被下面的两个链接器

链接。第一个是连接编辑器[请参看ld(SD_CMD)],可以和其他的可重定位和

共享object文件来创建其他的object。第二个是动态链接器,联合一个

可执行文件和其他的共享object文件来创建一个进程映象。

一个object文件被汇编器和联接器创建, 想要在处理机上直接运行的object

文件都是以二进制来存放的。那些需要抽象机制的程序,比如象shell脚本,

是不被接受的。

在介绍性的材料过后,第一部分主要围绕着文件的格式和关于如何建立程序。

第二部分也描述了object文件的几个组成部分,集中在执行程序所必须的信息上。

文件格式

Object文件参与程序的联接(创建一个程序)和程序的执行(运行一个程序)。

object 文件格式提供了一个方便有效的方法并行的视角看待文件的内容,

在他们的活动中,反映出不同的需要。例 1-1图显示了一个object文件的

组织图。

+ 图1-1: Object文件格式

Linking 视角 Execution 视角

============ ==============

ELF headerELF header

Program header table (optional) Program header table

Section 1 Segment 1

... Segment 2

Section n ...

Section header table Section header table (optional)

一个ELF头在文件的开始,保存了路线图(road map),描述了该文件的组织情况。

sections保存着object 文件的信息,从连接角度看:包括指令,数据,

符号表,重定位信息等等。特别sections的描述会出项在以后的第一部分。

第二部分讨论了段和从程序的执行角度看文件。

假如一个程序头表(program header table)存在,那么它告诉系统如何来创建一

个进程的内存映象。被用来建立进程映象(执行一个程序)的文件必须要有一个程

序头表(program header table);可重定位文件不需要这个头表。一个

section头表(section header table)包含了描述文件sections的信息。每个

section在这个表中有一个入口;每个入口给出了该section的名字,大小,

等等信息。在联接过程中的文件必须有一个section头表;其他object文件可要

可不要这个section头表。

注意: 虽然图显示出程序头表立刻出现在一个ELF头后,section头表跟着其他

section部分出现,事实是的文件是可以不同的。此外,sections和段(segments)

没有特别的顺序。只有ELF头(elf header)是在文件的固定位置。

数据表示

object文件格式支持8位、32位不同的处理器。不过,它试图努力的在更大

或更小的体系上运行。因此,object文件描绘一些控制数据需要用与机器

无关的格式,使它尽可能的用一般的方法甄别object文件和描述他们的内容。

在object文件中剩余的数据使用目标处理器的编码方式,不管文件是在哪台

机子上创建的。

+ 图 1-2: 32-Bit Data Types

Name Size Alignment Purpose

==== ==== ========= =======

Elf32_Addr4 4 Unsigned program address

Elf32_Half2 2 Unsigned medium integer

Elf32_Off 4 4 Unsigned file offset

Elf32_Sword 4 4 Signed large integer

Elf32_Word4 4 Unsigned large integer

unsigned char 1 1 Unsigned small integer

所有的object文件格式定义的数据结构是自然大小(natural size),为相关

的类型调整指针。如果需要,数据结构中明确的包含了确保4字节对齐的填

充字段。来使结构大小是4的倍数。数据从文件的开始也有适当的对齐。

例如,一个包含了Elf32_Addr成员的结构将会在文件内对齐到4字节的边界上。

因为移植性的原因,ELF不使用位字段。

========================== ELF Header ==========================

一些object文件的控制结构能够增长的,所以ELF头包含了他们目前的大小。

假如object文件格式改变,程序可能会碰到或大或小他们不希望的控制结构。

程序也有可能忽略额外(extra)的信息。

对待来历不明(missing)的信息依靠上下文来解释,假如扩展被定义,它们

将会被指定。

+ 图 1-3: ELF Header

#define EI_NIDENT 16

typedef struct {

unsigned char e_ident[EI_NIDENT];

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;

Elf32_Half e_phentsize;

Elf32_Half e_phnum;

Elf32_Half e_shentsize;

Elf32_Half e_shnum;

Elf32_Half e_shstrndx;

} Elf32_Ehdr;

* e_ident

这个最初的字段标示了该文件为一个object文件,提供了一个机器无关

的数据,解释文件的内容。在下面的ELF的鉴别(ELF Identification)

部分有更详细的信息。

* e_type

该成员确定该object的类型。

Name Value Meaning

==== ===== =======

ET_NONE 0 No file type

ET_REL 1 Relocatable file

ET_EXEC 2 Executable file

ET_DYN 3 Shared object file

ET_CORE 4 Core file

ET_LOPROC 0xff00 Processor-specific

ET_HIPROC 0xffff Processor-specific

虽然CORE的文件内容未被指明,类型ET_CORE是保留的。

值从 ET_LOPROC 到 ET_HIPROC(包括ET_HIPROC)是为特殊的处理器保留的。

如有需要,其他保留的变量将用在新的object文件类型上。

* e_machine

该成员变量指出了运行该程序需要的体系结构。

NameValue Meaning

========= =======

EM_NONE 0 No machine

EM_M32 1 AT&T WE 32100

EM_SPARC2 SPARC

EM_386 3 Intel 80386

EM_68K 4 Motorola 68000

EM_88K 5 Motorola 88000

EM_860 7 Intel 80860

EM_MIPS 8 MIPS RS3000

如有需要,其他保留的值将用到新的机器类型上。特殊处理器名使用机器名来

区别他们。例如,下面将要被提到的成员flags使用前缀EF_;在一台EM_XYZ机器

上,flag称为WIDGET,那么就称为EF_XYZ_WIDGET。

* e_version

这个成员确定object文件的版本。

Name Value Meaning

==== ===== =======

EV_NONE 0 Invalid version

EV_CURRENT 1 Current version

值1表示原来的文件格式;创建新版本就用>1的数。EV_CURRENT值(上面给

出为1)如果需要将指向当前的版本号。

* e_entry

该成员是系统第一个传输控制的虚拟地址,在那启动进程。假如文件没有

如何关联的入口点,该成员就保持为0。

* e_phoff

该成员保持着程序头表(program header table)在文件中的偏移量(以字节计数)。

假如该文件没有程序头表的的话,该成员就保持为0。

* e_shoff

该成员保持着section头表(section header table)在文件中的偏移量(以字节

计数)。假如该文件没有section头表的的话,该成员就保持为0。

* e_flags

该成员保存着相关文件的特定处理器标志。

flag的名字来自于EF__。看下机器信息“Machine Information”

部分的flag的定义。

* e_ehsize

该成员保存着ELF头大小(以字节计数)。

* e_phentsize

该成员保存着在文件的程序头表(program header table)中一个入口的大小

(以字节计数)。所有的入口都是同样的大小。

* e_phnum

该成员保存着在程序头表中入口的个数。因此,e_phentsize和e_phnum

的乘机就是表的大小(以字节计数).假如没有程序头表(program header table),

e_phnum变量为0。

* e_shentsize

该成员保存着section头的大小(以字节计数)。一个section头是在section

头表(section header table)的一个入口;所有的入口都是同样的大小。

* e_shnum

该成员保存着在section header table中的入口数目。因此,e_shentsize和

e_shnum的乘积就是section头表的大小(以字节计数)。

假如文件没有section头表,e_shnum值为0。

* e_shstrndx

该成员保存着跟section名字字符表相关入口的section头表(section header

table)索引。假如文件中没有section名字字符表,该变量值为SHN_UNDEF。

更详细的信息 看下面“Sections”和字符串表(“String Table”) 。

ELF 鉴别(Identification)

在上面提到的,ELF提供了一个object文件的框架结构来支持多种处理机,多

样的数据编码方式,多种机器类型。为了支持这个object文件家族,最初的几

个字节指定用来说明如何解释该文件,独立于处理器,与文件剩下的内容无关。

ELF头(也就是object文件)最初的几个字节与成员e_ident相一致。

+ 图 1-4: e_ident[] Identification Indexes

Name Value Purpose

==== ===== =======

EI_MAG00 File identification

EI_MAG11 File identification

EI_MAG22 File identification

EI_MAG33 File identification

EI_CLASS4 File class

EI_DATA5 Data encoding

EI_VERSION6 File version

EI_PAD7 Start of padding bytes

EI_NIDENT 16 Size of e_ident[]

通过索引访问字节,以下的变量被定义。

* EI_MAG0 to EI_MAG3

文件的前4个字符保存着一个魔术数(magic number),用来确定该文件是否

为ELF的目标文件。

Name Value Position

==== ===== ========

ELFMAG0 0x7f e_ident[EI_MAG0]

ELFMAG1 ‘E‘ e_ident[EI_MAG1]

ELFMAG2 ‘L‘ e_ident[EI_MAG2]

ELFMAG3 ‘F‘ e_ident[EI_MAG3]

* EI_CLASS

接下来的字节是e_ident[EI_CLASS],用来确定文件的类型或者说是能力。

Name Value Meaning

==== ===== =======

ELFCLASSNONE 0 Invalid class

ELFCLASS32 1 32-bit objects

ELFCLASS64 2 64-bit objects

文件格式被设计成在不同大小机器中可移植的,在小型机上的不用大型机上

的尺寸。类型ELFCLASS32支持虚拟地址空间最大可达4GB的机器;使用上面

定义过的基本类型。

类型ELFCLASS64为64位体系的机器保留。它的出现表明了object文件可能

改变,但是64位的格式还没有被定义。如果需要,其他类型将被定义,会

有不同的类型和不同大小的数据尺寸。

* EI_DATA

字节e_ident[EI_DATA]指定了在object文件中特定处理器数据的编码

方式。当前定义了以下编码方式。

Name Value Meaning

==== ===== =======

ELFDATANONE 0 Invalid data encoding

ELFDATA2LSB 1 See below

ELFDATA2MSB 2 See below

更多的关于编码的信息出现在下面。其他值保留,将被分配一个新的编码

方式,当然如果必要的话。

* EI_VERSION

字节e_ident[EI_VERSION]表明了ELF头的版本号。

现在这个变量的是一定要设为EV_CURRENT,作为上面e_version的解释。

* EI_PAD

该变量标识了在e_ident中开始的未使用的字节。那些字节保留并被设置为

0;程序把它们从object 文件中读出但应该忽略。假如当前未被使用的字节

有了新的定义,EI_PAD变量将来会被改变。

一个文件的数据编码指出了如何来解释一个基本的object文件。在上述的

描述中,类型ELFCLAS32文件使用占用1,2和4字节的目标文件。下面定义的

编码方式,用下面的图来描绘。数据出现在左上角。

ELFDATA2LSB编码指定了2的补数值。

最小有意义的字节占有最低的地址。

+ 图1-5: Data Encoding ELFDATA2LSB

0------+

0x0102 | 01 |

+------+

0------1------+

0x010204 | 02 | 01 |

+------+------+

0------1------2------3------+

0x01020304 | 04 | 03 | 02 | 01 |

+------+------+------+------+

ELFDATA2LSB编码指定了2的补数值。

最大有意义的字节占有最低的地址。

+ 图1-6: Data Encoding ELFDATA2MSB

0------+

0x0102 | 01 |

+------+

0------1------+

0x010204 | 01 | 02 |

+------+------+

0------1------2------3------+

0x01020304 | 01 | 02 | 03 | 04 |

+------+------+------+------+

机器信息

为了确定文件,32位Intel体系结构的需要以下的变量。

+ 图1-7: 32-bit Intel Architecture Identification, e_ident

Position Value

======== =====

e_ident[EI_CLASS] ELFCLASS32

e_ident[EI_DATA] ELFDATA2LSB

处理器确认ELF头里的e_machine成员,该成员必须为EM_386。

ELF报头里的e_flags成员保存了和文件相关的位标记。32位Intel体系上未

定义该标记;所以这个成员应该为0;

=========================== Sections ===========================

一个object文件的section header table可以让我们定位所有的sections,

section header table是个Elf32_Shdr结构的数组(下面描述)。一个section

报头表(section header table)索引是这个数组的下标。ELF header table

的e_shoff成员给出了section报头表的偏移量(从文件开始的计数)。e_shnum

告诉我们section报头表中包含了多少个入口;e_shentsize 给出了每个

入口的大小。

一些section报头表索引是保留的;那些特别的索引在一个object文件中

将没有与之对应sections。

+ 图1-8: Special Section Indexes

Name Value

==== =====

SHN_UNDEF0

SHN_LORESERVE 0xff00

SHN_LOPROC0xff00

SHN_HIPROC0xff1f

SHN_ABS 0xfff1

SHN_COMMON0xfff2

SHN_HIRESERVE 0xffff

* SHN_UNDEF

该值表明没有定义,缺少,不相关的或者其他涉及到的无意义的section。

例如,标号“defined”相对于section索引号SHN_UNDEF是一个没有被

定义的标号。

注意: 虽然索引0保留作为未定义的值,section报头表包含了一个索引0的

入口。因此,假如ELF报头说一个文件的section报头表中有6个section入口

的话,e_shnum的值应该是从0到5。最初的入口的内容以后在这个section中

被指定。

* SHN_LORESERVE

该值指定保留的索引范围的最小值。

* SHN_LOPROC through SHN_HIPROC

该值包含了特定处理器语意的保留范围。

* SHN_ABS

该变量是相对于相应参考的绝对地址。

例如,section号的标号是绝对地址,不被重定位影响。

* SHN_COMMON

该section的标号是一个公共(common)的标号,就象FORTRAN COMMON

或者不允许的C扩展变量。

* SHN_HIRESERVE

该值指定保留的索引范围的上限。系统保留的索引值是从SHN_LORESERVE

到SHN_HIRESERVE;该变量不涉及到section报头表(section header table)。

因此,section报头表不为保留的索引值包含入口。

sections包含了在一个object文件中的所有信息,除了ELF报头,程序报头

表(program header table),和section报头表(section header table)。

此外,object文件的sections满足几天条件:

* 每个在object文件中的section都有自己的一个section的报头来描述它。

section头可能存在但section可以不存在。

* 每个section在文件中都占有一个连续顺序的空间(但可能是空的)。

* 文件中的Sections不可能重复。文件中没有一个字节既在这个section中

又在另外的一个section中。

* object文件可以有“非活动的”空间。不同的报头和sections可以不覆盖到

object文件中的每个字节。“非活动”数据内容是未指定的。

一个section头有如下的结构。

+ 图1-9: Section Header

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;

* sh_name

该成员指定了这个section的名字。它的值是section报头字符表section的

索引。[看以下的“String Table”], 以NULL空字符结束。

* sh_type

该成员把sections按内容和意义分类。section的类型和他们的描述在下面。

* sh_flags

sections支持位的标记,用来描述多个属性。

Flag定义出现在下面。

* sh_addr

假如该section将出现在进程的内存映象空间里,该成员给出了一个该section

在内存中的位置。否则,该变量为0。

* sh_offset

该成员变量给出了该section的字节偏移量(从文件开始计数)。SHT_NOBITS

类型的section(下面讨论)在文件中不占空间,它的sh_offset成员定位在

文件中的概念上的位置。

* sh_size

该成员给你了section的字节大小。除非这个section的类型为SHT_NOBITS,

否则该section将在文件中将占有sh_size个字节。SHT_NOBITS类型的section

可能为非0的大小,但是不占文件空间。

* sh_link

该成员保存了一个section报头表的索引连接,它的解释依靠该section的

类型。以下一个表描述了这些值。

* sh_info

该成员保存着额外的信息,它的解释依靠该section的类型。以下一个表描

述了这些值。

* sh_addralign

一些sections有地址对齐的约束。例如,假如一个section保存着双字,系统

就必须确定整个section是否双字对齐。所以sh_addr的值以sh_addralign的值

作为模,那么一定为0。当然的,仅仅0和正的2的次方是允许的。值0和1意味

着该section没有对齐要求。

* sh_entsize

一些sections保存着一张固定大小入口的表,就象符号表。对于这样一个

section来说,该成员给出了每个入口的字节大小。如果该section没有

保存着一张固定大小入口的表,该成员就为0。

section头成员sh_type指出了section的语意。

+ 图1-10: Section Types, sh_type

Name Value

==== =====

SHT_NULL 0

SHT_PROGBITS 1

SHT_SYMTAB 2

SHT_STRTAB 3

SHT_RELA 4

SHT_HASH 5

SHT_DYNAMIC6

SHT_NOTE 7

SHT_NOBITS 8

SHT_REL 9

SHT_SHLIB 10

SHT_DYNSYM11

SHT_LOPROC 0x70000000

SHT_HIPROC 0x7fffffff

SHT_LOUSER 0x80000000

SHT_HIUSER 0xffffffff

* SHT_NULL

该值表明该section头是无效的;它没有相关的section。

该section的其他成员的值都是未定义的。

* SHT_PROGBITS

该section保存被程序定义了的一些信息,它的格式和意义取决于程序本身。

* SHT_SYMTAB and SHT_DYNSYM

那些sections保存着一个符号表(symbol table)。一般情况下,一个

object文件每个类型section仅有一个,但是,在将来,这个约束可能被放宽。

典型的是,SHT_SYMTAB为连接器提供标号,当然它也有可能被动态连接时使用。

作为一个完整的符号表,它可能包含了一些动态连接时不需要的标号。

因此,一个object文件可能也包含了一个SHT_DYNSYM的section,它保存着

一个动态连接时所需最小的标号集合来节省空间。

看下面符号表“Symbol Table”的细节。

* SHT_STRTAB

该section保存着一个字符串表。一个object文件可以包含多个字符串表的

section。看下面字符串表“String Table”的细节。

* SHT_RELA

该section保存着具有明确加数的重定位入口。就象object文件32位的

Elf32_Rela类型。一个object文件可能有多个重定位的sections。具体细节

看重定位``Relocation‘‘部分。

* SHT_HASH

该标号保存着一个标号的哈希(hash)表。所有的参与动态连接的object

一定包含了一个标号哈希表(hash table)。当前的,一个object文件

可能只有一个哈希表。详细细节看第二部分的哈希表“Hash Table”。

* SHT_DYNAMIC

该section保存着动态连接的信息。当前的,一个object可能只有一个动态

的section,但是,将来这个限制可能被取消。详细细节看第二部分的动态

section(“Dynamic Section”)。

* SHT_NOTE

该section保存着其他的一些标志文件的信息。详细细节看第二部分的“Note

Section” 。

* SHT_NOBITS

该类型的section在文件中不占空间,但是类似SHT_PROGBITS。尽管该section

不包含字节,sh_offset成员包含了概念上的文件偏移量。

* SHT_REL

该section保存着具有明确加数的重定位的入口。

就象object文件32位类型Elf32_Rel类型。一个object文件可能有多个

重定位的sections。具体细节看重定位``Relocation‘‘部分。

* SHT_SHLIB

该section类型保留但语意没有指明。包含这个类型的section的程序

是不符合ABI的。

* SHT_LOPROC through SHT_HIPROC

在这范围之间的值为特定处理器语意保留的。

* SHT_LOUSER

该变量为应用程序保留的索引范围的最小边界。

* SHT_HIUSER

该变量为应用程序保留的索引范围的最大边界。在SHT_LOUSER和HIUSER的

section类型可能被应用程序使用,这和当前或者将来系统定义的section

类型是不矛盾的。

其他section类型的变量是保留的。前面提到过,索引0(SHN_UNDEF)的section

头存在的,甚至索引标记的是未定义的section引用。这个入口保存着以下的

信息。

+ 图1-11: Section Header Table Entry: Index 0

NameValue Note

========= ====

sh_name 0No name

sh_type SHT_NULL Inactive

sh_flags 0No flags

sh_addr 0No address

sh_offset 0No file offset

sh_size 0No size

sh_link SHN_UNDEF No link information

sh_info 0No auxiliary information

sh_addralign 0No alignment

sh_entsize 0No entries

一个section报头(section header table)的sh_flags成员保存着1位标记,

用来描述section的属性。以下是定义的值;其他的值保留。

+ 图1-12: Section Attribute Flags, sh_flags

Name Value

==== =====

SHF_WRITE 0x1

SHF_ALLOC 0x2

SHF_EXECINSTR 0x4

SHF_MASKPROC 0xf0000000

假如在sh_flags中的一个标记位被设置,该section相应的属性也被打开。

否则,该属性没有被应用。未明的属性就设为0。

* SHF_WRITE

该section包含了在进程执行过程中可被写的数据。

* SHF_ALLOC

该section在进程执行过程中占据着内存。一些控制section不存在一个

object文件的内存映象中;对于这些sections,这个属性应该关掉。

* SHF_EXECINSTR

该section包含了可执行的机器指令。

* SHF_MASKPROC

所有的包括在这掩码中的位为特定处理语意保留的。

在section报头中,两个成员sh_link和sh_info的解释依靠该section的类型。

+ 图1-13: sh_link and sh_info Interpretation

sh_typesh_linksh_info

=====================

SHT_DYNAMIC The section header index of 0

the string table used by

entries in the section.

SHT_HASH The section header index of 0

the symbol table to which the

hash table applies.

SHT_REL, The section header index of The section header index of

SHT_RELA the associated symbol table. the section to which the

relocation applies.

SHT_SYMTAB, The section header index of One greater than the symbol

SHT_DYNSYM the associated string table. table index of the last local

symbol (binding STB_LOCAL).

other SHN_UNDEF 0

Special Sections 特殊的Sections

不同的sections保存着程序和控制信息。下面列表中的section被系统使用,

指示了类型和属性。

+ 图1-14: Special Sections

Name Type Attributes

==== ==== ==========

.bss SHT_NOBITS SHF_ALLOC+SHF_WRITE

.comment SHT_PROGBITS none

.data SHT_PROGBITS SHF_ALLOC+SHF_WRITE

.data1 SHT_PROGBITS SHF_ALLOC+SHF_WRITE

.debug SHT_PROGBITS none

.dynamic SHT_DYNAMIC see below

.dynstrSHT_STRTAB SHF_ALLOC

.dynsymSHT_DYNSYM SHF_ALLOC

.fini SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR

.got SHT_PROGBITS see below

.hash SHT_HASH SHF_ALLOC

.init SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR

.interpSHT_PROGBITS see below

.line SHT_PROGBITS none

.note SHT_NOTE none

.plt SHT_PROGBITS see below

.relSHT_REL see below

.relaSHT_RELA see below

.rodataSHT_PROGBITS SHF_ALLOC

.rodata1 SHT_PROGBITS SHF_ALLOC

.shstrtab SHT_STRTAB none

.strtabSHT_STRTAB see below

.symtabSHT_SYMTAB see below

.text SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR

* .bss

该sectiopn保存着未初始化的数据,这些数据存在于程序内存映象中。

通过定义,当程序开始运行,系统初始化那些数据为0。该section不占

文件空间,正如它的section类型SHT_NOBITS指示的一样。

* .comment

该section保存着版本控制信息。

* .data and .data1

这些sections保存着初始化了的数据,那些数据存在于程序内存映象中。

* .debug

该section保存着为标号调试的信息。该内容是未指明的。

* .dynamic

该section保存着动态连接的信息。该section的属性将包括SHF_ALLOC位。

是否需要SHF_WRITE是跟处理器有关。第二部分有更详细的信息。

* .dynstr

该section保存着动态连接时需要的字符串,一般情况下,名字字符串关联着

符号表的入口。第二部分有更详细的信息。

* .dynsym

该section保存着动态符号表,如“Symbol Table”的描述。第二部分有更

详细的信息。

* .fini

该section保存着可执行指令,它构成了进程的终止代码。

因此,当一个程序正常退出时,系统安排执行这个section的中的代码。

* .got

该section保存着全局的偏移量表。看第一部分的“Special Sections”和

第二部分的“Global Offset Table”获得更多的信息。

* .hash

该section保存着一个标号的哈希表。看第二部分的“Hash Table”获得更多

的信息。

* .init

该section保存着可执行指令,它构成了进程的初始化代码。

因此,当一个程序开始运行时,在main函数被调用之前(c语言称为main),

系统安排执行这个section的中的代码。

* .interp

该section保存了程序的解释程序(interpreter)的路径。假如在这个section

中有一个可装载的段,那么该section的属性的SHF_ALLOC位将被设置;否则,

该位不会被设置。看第二部分获得更多的信息。

* .line

该section包含编辑字符的行数信息,它描述源程序与机器代码之间的对于

关系。该section内容不明确的。

* .note

该section保存一些信息,使用“Note Section”(在第二部分)中提到的格式。

* .plt

该section保存着过程连接表(Procedure Linkage Table)。看第一部分的

``Special Sections‘‘和第二部分的“Procedure Linkage Table”。

* .reland .rela

这些section保存着重定位的信息,看下面的``Relocation‘‘描述。

假如文件包含了一个可装载的段,并且这个段是重定位的,那么该section的

属性将设置SHF_ALLOC位;否则该位被关闭。按照惯例,由重定位适用

的section来提供。因此,一个重定位的section适用的是.text,那么该名字

就为.rel.text或者是.rela.text。

* .rodata and .rodata1

这些section保存着只读数据,在进程映象中构造不可写的段。看第二部分的

``Program Header‘‘获得更多的资料。

* .shstrtab

该section保存着section名称。

* .strtab

该section保存着字符串,一般地,描述名字的字符串和一个标号的入口相关

联。假如文件有一个可装载的段,并且该段包括了符号字符串表,那么section

的SHF_ALLOC属性将被设置;否则不设置。

* .symtab

该section保存着一个符号表,正如在这个section里``Symbol Table‘‘的

描述。假如文件有一个可装载的段,并且该段包含了符号表,那么section

的SHF_ALLOC属性将被设置;否则不设置。

* .text

该section保存着程序的``text‘‘或者说是可执行指令。

前缀是点(.)的section名是系统保留的,尽管应用程序可以用那些保留的

section名。应用程序可以使用不带前缀的名字以避免和系统的sections

冲突。object文件格式可以让一个定义的section部分不出现在上面的列

表中。一个object文件可以有多个同样名字的section。

为处理器体系保留的section名的形成是通过置换成一个体系名的缩写。

该名字应该取自体系名,e_machine使用的就是。例如,.Foo.psect就是

在FOO体系上定义的名字。

现存的扩展名是历史遗留下来的。

Pre-existing Extensions

=======================

.sdata .tdesc

.sbss.lit4

.lit8.reginfo

.gptab .liblist

.conflict

原文转自:www.ltesting.net

篇3:elf文件格式 2Unix系统

elf文件格式-- 2 =================== String Table 字符串表========================= String table sections 保存着以NULL终止的一系列字符,一般我们称为字 符串,object文件使用这些字符串来描绘符号和section名。一个字符串的 参考是一个string table s

elf文件格式-- 2

=================== String Table 字符串表=========================

String table sections 保存着以NULL终止的一系列字符,一般我们称为字

符串。object文件使用这些字符串来描绘符号和section名。一个字符串的

参考是一个string table section的索引。第一个字节,即索引0,被定义保

存着一个NULL字符。同样的,一个string table的最后一个字节保存着一个

NULL字符,所有的字符串都是以NULL终止。索引0的字符串是没有名字或者说

是NULL,它的解释依靠上下文。一个空的string table section是允许的;

它的section header的成员sh_size将为0。对空的string table来说,非0的

索引是没有用的。

一个 settion 头的 sh_name 成员保存了一个对应于该 setion 头字符表部分

的索引(就象ELF头的 e_shstrndx 成员所特指的那样。下表列出了一个有 25 字节

的字符串表(这些字符串和不同的索引相关联):

Index +0 +1 +2 +3 +4 +5 +6 +7 +8 +9

===== == == == == == == == == == ==

0 n a m e . V a r

10 i a b l e a b l e

20 x x

+ Figure 1-15: String Table Indexes

Index String

===== ======

0 none

1 “name.”

7 “Variable”

11 “able”

16 “able”

24 null string

如上所示,一个字符串表可能涉及该 section 中的任意字节。一个字符串可能

引用不止一次;引用子串的情况是可能存在的;一个字符串也可能被引用若干次;而

不被引用的字符串也是允许存在的。

==================== Symbol Table 符号表=========================

一个object文件的符号表保存了一个程序在定位和重定位时需要的定义和引用的信息。

一个符号表索引是相应的下标。0表项特指了该表的第一个入口,就象未定义的符号

索引一样。初始入口的内容在该 section 的后续部分被指定。

Name Value

==== =====

STN_UNDEF0

一个符号表入口有如下的格式:

+ Figure 1-16: Symbol Table Entry

typedef struct {

Elf32_Word st_name;

Elf32_Addr st_value;

Elf32_Word st_size;

unsigned char st_info;

unsigned char st_other;

Elf32_Half st_shndx;

} Elf32_Sym;

* st_name

该成员保存了进入该object文件的符号字符串表入口的索引(保留了符号名的表达字符)。

如果该值不为 0 ,则它代表了给出符号名的字符串表索引。否则,该符号无名。

注意:External C 符号和object文件的符号表有相同的名称。

* st_value

该成员给出了相应的符号值。它可能是绝对值或地址等等(依赖于上下文);

细节如下所述。

* st_size

许多符号和大小相关。比如,一个数据对象的大小是该对象所包含的字节数目。

如果该符号的大小未知或没有大小则这个成员为 0 。

* st_info

成员指出了符号的类型和相应的属性。相应的列表如下所示。下面的代码说明了

如何操作该值。

#define ELF32_ST_BIND(i) ((i)>>4)

#define ELF32_ST_TYPE(i) ((i)&0xf)

#define ELF32_ST_INFO(b, t) (((b)<<4)+((t)&0xf))

* st_other

该成员目前为 0 ,没有含义。

* st_shndx

每一个符号表的入口都定义为和某些 section 相关;该成员保存了相关的 section

头索引。就象 Figure 1-8 和相关的文字所描述的那样,某些 section 索引

指出了特殊的含义。

一个符号的属性决定了可链接性能和行为。

+ Figure 1-17: Symbol Binding, ELF32_ST_BIND

Name Value

==== =====

STB_LOCAL 0

STB_GLOBAL1

STB_WEAK 2

STB_LOPROC 13

STB_HIPROC 15

* STB_LOCAL

在包含了其定义的object文件之外的局部符号是不可见的。不同文件中的具有相同

名称的局部符号并不相互妨碍。

* STB_GLOBAL

全局符号是对所有的object目标文件可见的。一个文件中的全局符号的定义可以

满足另一个文件中对(该文件中)未定义的全局符号的引用。

* STB_WEAK

弱符号相似于全局符号,但是他们定义的优先级比较低一些。

* STB_LOPROC through STB_HIPROC

其所包含范围中的值由相应的处理器语义所保留。

全局符号和弱符号的区别主要在两个方面。

* 当链接器链接几个可重定位的目标文件时,它不允许 STB_GLOBAL 符号的同名

多重定义。另一方面,如果一个全局符号的定义存在则具有相同名称的弱符号名不会

引起错误。链接器将认可全局符号的定义而忽略弱符号的定义。与此相似,如果有一个

普通符号(比如,一个符号的 st_shndx 域包含 SHN_COMMON),则一个同名的弱符号

不会引起错误。链接器同样认可普通符号的定义而忽略弱符号。

* 当链接器搜索档案库的时候,它选出包含了未定义的全局符号的存档成员。该成员

的定义或者是全局的或者是一个弱符号。链接器不会为了解决一个未定义的弱符号

选出存档成员。未定义的弱符号具有 0 值。

在每一个符号表中,所有具有 STB_LOCAL 约束的符号优先于弱符号和全局符号。

就象上面 “sections” 中描述的那样,一个符号表部分的 sh_info 头中的成员

保留了第一个非局部符号的符号表索引。

符号的类型提供了一个为相关入口的普遍分类。

+ Figure 1-18: Symbol Types, ELF32_ST_TYPE

Name Value

==== =====

STT_NOTYPE 0

STT_OBJECT 1

STT_FUNC 2

STT_SECTION3

STT_FILE 4

STT_LOPROC13

STT_HIPROC15

* STT_NOTYPE

该符号的类型没有指定。

* STT_OBJECT

该符号和一个数据对象相关,比如一个变量、一个数组等。

* STT_FUNC

该符号和一个函数或其他可执行代码相关。

* STT_SECTION

该符号和一个 section 相关。这种类型的符号表入口主要是为了重定位,一般的

具有 STB_LOCAL 约束。

* STT_FILE

按惯例而言,该符号给出了和目标文件相关的源文件名称。一个具有 STB_LOCAL

约束的文件符号,其 section 索引为 SHN_ABS ,并且它优先于当前对应该文件的

其他 STB_LOCAL 符号。

* STT_LOPROC through STT_HIPROC

该范围中的值是为处理器语义保留的。

共享文件中的函数符号(具有 STT_FUNC 类型)有特殊的意义。当其他的目标文件

从一个共享文件中引用一个函数时,链接器自动的为引用符号创建一个链接表。除了

STT_FUNC 之外,共享的目标符号将不会自动的通过链接表引用。

如果一个符号涉及到一个 section 的特定定位,则其 section 索引成员 st_shndx

将保留一个到该 section 头的索引。当该 section 在重定位过程中不断

移动一样,符号的值也相应变化,而该符号的引用在程序中指向同样的定位。某些

特殊的 section 索引有其他的语义。

* SHN_ABS

该符号有一个不会随重定位变化的绝对值。

* SHN_COMMON

该符号标识了一个没有被分配的普通块。该符号的值给出了相应的系统参数,就象

一个 section 的 sh_addralign 成员。也就是说,链接器将分配一个地址给

该符号,地址的值是 st_value 的倍数。该符号的大小指出了需要的字节数。

* SHN_UNDEF

该 section 表索引表明该符号是未定义的。当链接器将该目标文件和另一个定义

该符号的文件相装配的时候,该文件内对该符号的引用将链接到当前实际的定义。

如上所述,符号表的 0 索引(STN_UNDEF)是保留的,它包含了如下内容:

+ Figure 1-19: Symbol Table Entry: Index 0

Name Value Note

==== ===== ====

st_name 0No name

st_value0Zero value

st_size 0No size

st_info 0No type, local binding

st_other0

st_shndx SHN_UNDEF No section

Symbol Values(符号值)

符号表入口对于不同的目标文件而言对 st_value 成员有一些不同的解释。

* 在可重定位文件中, st_value 保存了 section 索引为 SHN_COMMON 符号

的强制对齐值。

* 在可重定位文件中, st_value 保存了一个符号的 section 偏移。也就是说,

st_value 是从 st_shndx 定义的 section 开头的偏移量。

* 在可执行的和可共享的目标文件中, st_value 保存了一个虚拟地址。为了使

这些文件符号对于动态链接器更为有效,文件层面上的 section 偏移让位于内存

层面上的虚拟地址( section 编号无关的)。

尽管符号表值对于不同的目标文件有相似的含义,相应的程序还是可以有效地访问数据。

====================== Relocation (重定位)==========================

重定位是连接符号引用和符号定义的过程。比如,当一个程序调用一个函数的时候,

相关的调用必须在执行时把控制传送到正确的目标地址。换句话说,重定位文件应当

包含有如何修改他们的 section 内容的信息,从而允许可执行文件或共享目标文件

为一个进程的程序映像保存正确的信息。重定位入口就是这样的数据。

+ Figure 1-20: Relocation Entries

typedef struct {

Elf32_Addr r_offset;

Elf32_Word r_info;

} Elf32_Rel;

typedef struct {

Elf32_Addr r_offset;

Elf32_Word r_info;

Elf32_Sword r_addend;

} Elf32_Rela;

* r_offset

该成员给出了应用重定位行为的地址。对于一个重定位文件而言,该值是从该

section 开始处到受到重定位影响的存储单位的字节偏移量。对一个可执行文件

或一个共享目标而言,该值是受到重定位影响的存储单位的虚拟地址。

* r_info

该成员给出了具有受重定位影响因素的符号表索引和重定位应用的类型。比如,

一个调用指令的重定位入口应当包含被调用函数的符号索引。如果该索引是

STN_UNDEF (未定义的符号索引),重定位将使用 0 作为该符号的值。重定位

类型是和处理器相关的。当正文(text)引用到一个重定位入口的重定位类型或符

号表索引,它表明相应的应用 ELF32_R_TYPE或 ELF32_R_SYM 于入口的 r_info

成员。

#define ELF32_R_SYM(i) ((i)>>8)

#define ELF32_R_TYPE(i) ((unsigned char)(i))

#define ELF32_R_INFO(s, t) ((s)<<8+(unsigned char)(t))

* r_addend

该成员指定一个常量加数(用于计算将要存储于重定位域中的值)。

如上所述,只有 Elf32_Rela 入口包含一个明确的加数。Elf32_Rel 类型

的入口在可以修改的地址中存储一个隐含的加数。依赖于处理器结构,一种形式

或其他形式也许是必须的或更为方便的。因此,特定机器的应用应当使用一种排他

性的形式或依赖于上下文的另一种形式。

一个重定位 section 关联了两个其他的 section :一个符号表和一个可修改

的 section 。该 section 头的成员 sh_info 和 sh_link (在上文中的

“ section ”部分中有描述)指示了这种关系。重定位入口中的成员 r_offset

对于不同的目标文件有少许差异。

* 在可重定位文件中,r_offset 表示了一个 section 偏移。也就是说,重定位

section自己描述了如何修改其他在文件中的其他section; 重定位偏移量指

明了一个在第二个section中的存储器单元。

* 在可执行和共享的目标文件中,r_offset 表示一个虚拟地址。为了使得这些

文件的重定位入口更为有用(对于动态链接器而言),该 section 偏移(文件

中)应当让位于一个虚拟地址(内存中的)。

尽管为了允许相关的程序更为有效的访问而让 r_offset 的解释对于不同的目标

文件有所不同,重定位类型的含义是相同的。

Relocation Types(重定位类型)

重定位入口描述了怎样变更下面的指令和数据域(位数在表的两边角下)。

+ Figure 1-21: Relocatable Fields

+---------------------------+

| word32 |

31---------------------------0

* word32

指定一个以任意字节对齐方式占用 4 字节的 32 位域。这些值使用与 32 位 Intel

体系相同的字节顺序。

3------2------1------0------+

0x01020304 | 01 | 02 | 03 | 04 |

31------+------+------+------0

下面的计算假设正在将一个可重定位文件转换为一个可执行或共享的目标文件。

从概念上来说,链接器合并一个或多个可重定位文件来组成输出。它首先决定

怎样合并、定位输入文件,然后更新符号值,最后进行重定位。对于可执行文件

和共享的目标文件而言,重定位过程是相似的并有相同的结果。下面的描述使用

如下的约定符号。

* A

表示用于计算可重定位的域值的加数。

* B

表示了在执行过程中一个共享目标被加载到内存时的基地址。一般情况下,一个

共享object文件使用的基虚地址为0,但是一个可执行地址就跟共享object文件

不同了。

* G

表示了在执行过程中重定位入口符号驻留在全局偏移表中的偏移。请参阅

第二部分中的“ Global Offset Table (全局偏移表)”获得更多

的信息。

* GOT

表示了全局偏移表的地址。请参阅第二部分中的“ Global Offset Table

(全局偏移表)”获得更多的信息。

* L

表示一个符号的过程链接表入口的位置( section 偏移或地址)。一个过程

链接表入口重定位一个函数调用到正确的目的单元。链接器创建初始的链接表,

而动态链接器在执行中修改入口。

请参阅第二部分中的“ Procedure Linkage Table (过程链接表)”获得更多

的信息

* P

表示(section 偏移或地址)被重定位的存储单元位置(使用 r_offset 计算的)。

* S

表示索引驻留在重定位入口处的符号值。

一个重定位入口的 r_offset 值指定了受影响的存储单元的首字节的偏移

或虚拟地址。重定位类型指定了哪一位(bit)将要改变,以及怎样计算它们的值。

在 SYSTEM V 体系中仅仅使用 Elf32_Rel 重定位入口,将要被重定位的域中

保留了加数。在所有的情况下,加数和计算结果使用相同字节顺序。

+ Figure 1-22(表 1-22): Relocation Types(重定位类型)

NameValue Field Calculation

========= ===== ===========

R_386_NONE 0 none none

R_386_32 1 word32 S + A

R_386_PC32 2 word32 S + A - P

R_386_GOT32 3 word32 G + A - P

R_386_PLT32 4 word32 L + A - P

R_386_COPY 5 none none

R_386_GLOB_DAT 6 word32 S

R_386_JMP_SLOT 7 word32 S

R_386_RELATIVE 8 word32 B + A

R_386_GOTOFF 9 word32 S + A - GOT

R_386_GOTPC 10 word32 GOT + A - P

有的重定位类型有不同于简单计算的语义。

* R_386_GOT32

这种重定位类型计算全局偏移表基地址到符号的全局偏移表

入口之间的间隔。这样另外通知了 link editor 建立一个全局偏移表 。

* R_386_PLT32

这种重定位类型计算符号的过程链接表入口地址,并另外通知链接器建立一个

过程链接表。

* R_386_COPY

链接器创建该重定位类型用于动态链接,

它的偏移成员涉及一个可写段中的一个

位置。符号表索引指定一个可能存在于当前 object file 或在一个shared object

中的符号。在执行过程中,动态链接器把和 shared object 符号相关的数据

拷贝到该偏移所指定的位置。

* R_386_GLOB_DAT

这种重定位类型用于设置一个全局偏移表入口为指定符号的地址。该特定的重定位

类型允许你决定符号和全局偏移表入口之间的一致性。

* R_386_JMP_SLOT

链接器创建该重定位类型用于动态链接。其偏移成员给出了一个过程链接表入口的

位置。动态链接器修改该过程链接表入口以便向特定的符号地址传递控制。

[参阅第二部分中的 “Procedure Linkage Table(过程链接表)”]

* R_386_RELATIVE

链接器创建该重定位类型用于动态链接。其偏移成员给出了包含表达相关地址值

的一个 shared object 中的位置。动态链接器计算相应的虚拟地址(把该

shared object 装载地址和相对地址相加)。该类型的重定位入口必须为

符号表索引指定为 0 。

* R_386_GOTOFF

这种重定位类型计算符号值和全局偏移表地址之间的不同。另外还通知链接器

建立全局偏移表(GOT)。

* R_386_GOTPC

这种重定位类型类似于 R_386_PC32 ,不同的是它在计算中使用全局偏移表。

这种重定位中引用的符号通常是 _GLOBAL_OFFSET_TABLE_ ,该符号通知了

链接器建立全局偏移表(GOT)。

________________________________________________________________

2. PROGRAM LOADING AND DYNAMIC LINKING

程序装入和动态链接

________________________________________________________________

======================== Introduction(介绍) =========================

第二部分描述了 object file 信息和创建运行程序的系统行为。其中部分信息

适合所有的系统,其他信息是和特定处理器相关的。

可执行和共享的 object file 静态的描绘了程序。为了执行这样的程序,系统

用这些文件创建动态的程序表现,或进程映像。一个进程映像有用于保存其代码、

数据、堆栈等等的段。这个部分的主要章节讨论如下的内容。

* 程序头(Program header)。该章节补充第一部分,描述和程序运行相关的

object file 结构。即文件中主要的数据结构、程序头表、定位段映像,也

包含了为该程序创建内存映像所需要的信息。

* 载入程序(Program loading)。在给定一个 object file 时,系统为了

让它运行必须将它载入内存。

* 动态链接(Dynamic linking)。在载入了程序之后,系统必须通过解决组

成该进程的 object file之间的符号引用问题来完成进程映像的过程。

注意:指定了处理器范围的 ELF 常量是有命名约定的。比如,DT_ , PT_ ,

用于特定处理器扩展名,组合了处理器的名称(如 DT_M32_SPECIAL )。

没有使用这种约定但是预先存在的处理器扩展名是允许的。

Pre-existing Extensions

(预先存在的扩展名)

=======================

DT_JMP_REL

====================== Program Header(程序头) ======================

一个可执行的或共享的 object file 的程序头表是一个结构数组,每一个

结构描述一个段或其他系统准备执行该程序所需要的信息。一个 object file

段包含一个或多个部分(就象下面的“段目录”所描述的那样)。程序头仅仅对于

可执行或共享的 object file 有意义。一个文件使用 ELF 头的 e_phentsize

和 e_phnum 成员来指定其拥有的程序头大小。[参阅 第一部分中的 “ELF 头”]

+ Figure 2-1: Program Header

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;

* p_type

该成员指出了这个数组的元素描述了什么类型的段,或怎样解释该数组元素的信息。

类型值和含义如下所述。

* p_offset

该成员给出了该段的驻留位置相对于文件开始处的偏移。

* p_vaddr

该成员给出了该段在内存中的首字节地址。

* p_paddr

在物理地址定位有关联的系统中,该成员是为该段的物理地址而保留的。由于

System V 忽略了应用程序的物理地址定位,该成员对于可执行文件和共享的

object 而言是未指定内容的。

* p_filesz

该成员给出了文件映像中该段的字节数;它可能是 0 。

* p_memsz

该成员给出了内存映像中该段的字节数;它可能是 0 。

* p_flags

该成员给出了和该段相关的标志。定义的标志值如下所述。

* p_align

就象在后面“载入程序”部分中所说的那样,可载入的进程段必须有合适的

p_vaddr 、 p_offset 值,取页面大小的模。该成员给出了该段在内存和

文件中排列值。 0 和 1 表示不需要排列。否则, p_align 必须为正的 2 的幂,

并且 p_vaddr 应当等于 p_offset 模 p_align 。

某些入口描述了进程段;其他的则提供补充信息并且无益于进程映像。已经

定义的入口可以以任何顺序出现,除非是下面明确声明的。后面是段类型值;

其他的值保留以便将来用于其他用途。

+ Figure 2-2: Segment Types, p_type

Name Value

==== =====

PT_NULL 0

PT_LOAD 1

PT_DYNAMIC 2

PT_INTERP3

PT_NOTE 4

PT_SHLIB 5

PT_PHDR 6

PT_LOPROC 0x70000000

PT_HIPROC 0x7fffffff

* PT_NULL

该数组元素未使用;其他的成员值是未定义的。这种类型让程序头表忽略入口。

* PT_LOAD

该数组元素指定一个可载入的段,由 p_filesz 和 p_memsz 描述。文件中

字节被映射到内存段中。如果该段的内存大小( p_memsz )比文件大小( p_filesz )

要大,则多出的字节将象段初始化区域那样保持为 0 。文件的大小不会比内存大小值大。

在程序头表中,可载入段入口是以 p_vaddr 的升序排列的。

* PT_DYNAMIC

该数组元素指定动态链接信息。参阅 后面的“动态部分”以获得更多信息。

* PT_INTERP

该数组元素指定一个 null-terminated 路径名的位置和大小(作为解释程序)。

这种段类型仅仅对可执行文件有意义(尽管它可能发生在一个共享 object 上);

它在一个文件中只能出现一次。如果它出现,它必须先于任何一个可载入段入口。

参阅 后面的“程序解释器”(Program Interpreter)以获得更多的信息。

* PT_NOTE

该数组元素指定辅助信息的位置和大小。参阅 后面的“注意部分”以获得细节。

* PT_SHLIB

该段类型保留且具有未指定的语义。具有一个这种类型数组元素的程序并不

遵守 ABI 。

* PT_PHDR

该数组元素(如果出现),指定了程序头表本身的位置和大小(包括在文件中

和在该程序的内存映像中)。更进一步来说,它仅仅在该程序头表是程序内存映像

的一部分时才有效。如果它出现,它必须先于任何可载入段入口。参阅 后面的

“程序解释器”(Program Interpreter)以获得更多的信息。

* PT_LOPROC through PT_HIPROC

该范围中的值保留用于特定处理器的语义。

注意:除非在别处的特殊要求,所有的程序头的段类型是可选的。也就是说,

一个文件的程序头表也许仅仅包含和其内容相关的元素。

Base Address(基地址)

可执行和共享的 object file 有一个基地址,该基地址是与程序的 object file

在内存中映像相关的最低虚拟地址。基地址的用途之一是在动态链接过程中重定位

该程序的内存映像。

一个可执行的 object file 或 一个共享的 object file 的基地址是在

执行的时候从三个值计算而来的:内存载入地址、页面大小的最大值 和 程序可

载入段的最低虚拟地址。就象在“程序载入”中所描述的那样,程序头中的虚拟地址

也许和程序的内存映像中实际的虚拟地址并不相同。为了计算基地址,必须确定与

PT_LOAD 段 p_vaddr 的最小值相关的内存地址。获得基地址的方法是将内存

地址截去最大页面大小的最接近的整数倍。由于依赖载入内存中的文件类型,

该内存地址和 p_vaddr 值可能匹配也可能不匹配。

就象在第一部分中 “Section” 中描述的那样, .bss section 具有 SHT_NOBITS

的类型。尽管在文件中不占用空间,它在段的内存映像中起作用。通常,没有初始化

的数据驻留在段尾,因此使得在相关的程序头元素中的 p_memsz 比 p_filesz 大。

Note Section(注解部分)

有的时候供应商或系统设计者需要用特定的信息标记一个

object file 以便其他程序检查其兼容的一致性,等等此类。 SHT_NOTE

类型的 section 和 PT_NOTE 类型的程序头元素能够被用于此目的。 section

和程序头中的注解信息包含了任意数目的入口,每一个入口的格式都是对应于特定

处理器格式的 4-字节数组。下面的标签有助于解释注释信息的组织形式,但是这些

标签不是规格说明的一部分。

+ Figure 2-3: Note Information

namesz

descsz

type

name ...

desc ...

* namesz and name

名字中 namesz 的第一个字节包含了一个 null-terminated 字符

表达了该入口的拥有者或始发者。没有正式的机制来避免名字冲突。从

惯例来说,供应商使用他们自己的名称,比如 “XYZ Computer Company” ,

作为标志。如果没有提供名字, namesz 值为 0 。 如果有必要,确定

描述信息4-字节对齐。 这样的填充信息并不包含在namesz 中。

* descsz and desc

desc 中 descsz 的首字节包含了注解描述符。ABI 不会在一个描述符内容中

放入任何系统参数。如果没有描述符, descsz 将为 0 。 如果有必要,确定

描述信息4-字节对齐。 这样的填充信息并不包含在descsz中。

* type

该 word 给出了描述符的解释。每一个创造着(originator) 控制着自己的类型;

对于单单一个类型值的多种解释是可能存在的。因此,一个程序必须辨认出该名字

和其类型以便理解一个描述符。这个时候的类型必须是非负的。ABI 没有定义

描述符的含义。

为了举例说明,下面的解释段包含两个入口。

+ Figure 2-4: Example Note Segment

+0 +1 +2 +3

-------------------

namesz 7

descsz 0 No descriptor

type 1

name X Y Z spc

C o pad

namesz 7

descsz 8

type 3

name X Y Z spc

C o pad

desc word0

word1

注意:系统保留的注解信息没有名字 (namesz==0) ,有一个零长度的名字

(name[0]==‘‘) 现在还没有类型为其定义。所有其他的名字必须至少有

一个非空的字符。

注意:注解信息是可选的。注解信息的出现并不影响一个程序的 ABI 一致性,

前提是该信息不影响程序的执行行为。否则,该程序将不遵循 ABI 并将出现

未定义的行为。

===================== Program Loading(程序载入) =====================

当创建或增加一个进程映像的时候,系统在理论上将拷贝一个文件的段到一个虚拟

的内存段。系统什么时候实际地读文件依赖于程序的执行行为,系统载入等等。一个

进程仅仅在执行时需要引用逻辑页面的时候才需要一个物理页面,实际上进程通常会

留下许多未引用的页面。因此推迟物理上的读取常常可以避免这些情况,改良系统的

特性。为了在实践中达到这种效果,可执行的和共享的 object file 必须具有

合适于页面大小取模值的文件偏移和虚拟地址这样条件的段映像。

虚拟地址和文件偏移在 SYSTEM V 结构的段中是模 4KB(0x1000) 或大的 2 的幂。

由于 4KB 是最大的页面大小,因此无论物理页面大小是多少,文件必须去适合页面。

+ Figure 2-5: Executable File

File Offset FileVirtual Address

=========== ===================

0 ELF header

Program header table

Other information

0x100 Text segment 0x8048100

...

0x2be00 bytes 0x8073eff

0x2bf00 Data segment 0x8074f00

...

0x4e00 bytes 0x8079cff

0x30d00 Other information

...

+ Figure 2-6: Program Header Segments(程序头段)

Member Text Data

====== ==== ====

p_type PT_LOADPT_LOAD

p_offset 0x100 0x2bf00

p_vaddr 0x8048100 0x8074f00

p_paddr unspecified unspecified

p_filesz 0x2be00 0x4e00

p_memsz 0x2be00 0x5e24

p_flags PF_R+PF_X PF_R+PF_W+PF_X

p_align 0x1000 0x1000

尽管示例中的文件偏移和虚拟地址在文本和数据两方面都适合模 4KB ,但是还有

4 个文件页面混合了代码和数据(依赖于页面大小和文件系统块的大小)。

* 第一个文本页面包含了 ELF 头、程序头以及其他信息。

* 最后的文本页包含了一个数据开始的拷贝。

* 第一个数据页面有一个文本结束的拷贝。

* 最后的数据页面也许会包含与正在运行的进程无关的文件信息。

理论上,系统强制内存中段的区别;段地址被调整为适应每一个逻辑页面在地址空间

中有一个简单的准许集合。在上面的示例中,包含文本结束和数据开始的文件区域将

被映射两次:在一个虚拟地址上为文本而另一个虚拟地址上为数据。

数据段的结束处需要对未初始化的数据进行特殊处理(系统定义的以 0 值开始)。

因此如果一个文件包含信息的最后一个数据页面不在逻辑内存页面中,则无关的

数据应当被置为 0 (这里不是指未知的可执行文件的内容)。在其他三个页面中

“Impurities” 理论上并不是进程映像的一部分;系统是否擦掉它们是未指定的。

下面程序的内存映像假设了 4KB 的页面。

+ Figure 2-7: Process Image Segments(进程映像段)

Virtual Address ContentsSegment

=============== ===============

0x8048000 Header paddingText

0x100 bytes

0x8048100 Text segment

...

0x2be00 bytes

0x8073f00 Data padding

0x100 bytes

0x8074000 Text padding Data

0xf00 bytes

0x8074f00 Data segment

...

0x4e00 bytes

0x8079d00 Uninitialized data

0x1024 zero bytes

0x807ad24 Page padding

0x2dc zero bytes

可执行文件和共享文件在段载入方面有所不同。典型地,可执行文件段包含了

绝对代码。为了让进程正确执行,这些段必须驻留在建立可执行文件的虚拟地址

处。因此系统使用不变的 p_vaddr 作为虚拟地址。

另一方面,共享文件段包含与位置无关的代码。这让不同进程的相应段虚拟地址

各不相同,且不影响执行。虽然系统为各个进程选择虚拟地址,它还要维护各个

段的相对位置。因为位置无关的代码在段间使用相对定址,故而内存中的虚拟地址

的不同必须符合文件中虚拟地址的不同。下表给出了几个进程可能的共享对象虚拟

地址的分配,演示了不变的相对定位。该表同时演示了基地址的计算。

+ Figure 2-8: Example Shared Object Segment Addresses

Sourc Text Data Base Address

===== ==== ==== ============

File 0x200 0x2a400 0x0

Process 1 0x80000200 0x8002a400 0x80000000

Process 2 0x80081200 0x800ab400 0x80081000

Process 3 0x900c0200 0x900ea400 0x900c0000

Process 4 0x900c6200 0x900f0400 0x900c6000

原文转自:www.ltesting.net

篇4:Elf文件DTRPATH后门解析

一.前言

二.分析

三.结论

四.实现

五.参考

一.前言

前一段时间回顾elf文件方面的知识时,翻了以前很多优秀的文章,在xfocus的帮助文件unix版中看到了alert7大侠

<> 这篇文章,看了一下时间9月/14号,那段时间忙着重修,没到网吧去,

Elf文件DTRPATH后门解析

。alert7没有贴

代码,我想还是有人不了解,就把这篇文章写出来给大家整理性的介绍一下,这里指的是linux下。

这篇文章不是介绍elf基础的文章,所以一些概念性的东西请参考elf鉴别,然后拿起你的gdb实际去了解一下啥叫got,

plt,重定向的概念,了解一下动态连接和静态连接,用readelf或者objdump实际看一下,后面的参考中可以找到你需要的。

文中错误再所难免,欢迎指正;)

二.分析

后门存在的原因:

————————————————————————————————————————————————

引自<>

*动态数组标记DT_RPATH保存着目录列表的字符串(用冒号(:)分隔)。

例如,字符串/home/dir/lib:/home/dir2/lib:告诉动态连接器先搜索

/home/dir/lib,再搜索/home/dir2/lib,再是当前目录。

LD_LIBRARY_PATH环境变量设置的目录在DT_RPATH指向的目录之后被搜索。

而且出于安全考虑,动态连接器忽略set-user和set-group的程序的

LD_LIBRARY_PATH所指定的搜索目录。但它会搜索DT_RPATH指明的目录和

/usr/lib。所以这个DT_RPATH还是比较感兴趣的。

有了以上的基本知识,我们可以在一个set-user的程序上安装一个DT_RPATH入口(一般程序都没有)。

让其搜索路径首先为当前目录。把我们自己编写的库以其名字放到当前目录下,更让其加栽我们自己的动态库。

这样我们就可以得到set-user的权限了。

截获了__libc_start_main,肯定能确保ping 程序放弃特权之前执行。(假如截获malloc之类的就不能确保了)。

因为我们用到了一些libc.so.6中的函数,所以我们需要把我们自己的LIB以libresolv.so.2的名字存放在当前目录下。

————————————————————————————————————————————————————————————————————————————————————

由此实现这个后门的关键在于:

a:修改或添加(通常是不存在的)在.dynamic节中(数组)d_tag为DT_RPATH的入口结构(entry)d_un.d_val指向为到

.dynstr节的的一个以**NULL结尾字符串的偏移量(如果存在DT_RATH的话).此字符串是路径的集合,其中路径以:分开,

最后一个:后面表示当前目录,如果前面的字符串改成这样的“_wujianqiang:”(这样用来表示一个NULL;))表明现在当前

目录下的_wujianqiang子目录中寻找,然后再在当前目录下寻找,显然这里采用的相对目录,我们主要是构造这个字符串,同时

我们要想到,我们是添加一个这样的字符串还是修改某个字符串成为我们需要的形式,显然前者就比较复杂,就要使program header ,

section 的某些成员都要改变,所以我们采用修改的方法,找一个动态符号修改,这里我选用了__gmon_start__这个字符串。

当然其它的也可以,不过发现__gmon_start__有点特殊,并且几乎所有的程序里都有这个符号,所以我就选了这个。

通常d_tag为DT_RPATH的入口结构不存在,所以我们要添加这个DT_RPATH,实际上.dynamic数组中应该有几个空的没用的这样

的NULL入口,(一般都可以找到)所以我在程序中找的是第一个NULL入口,没有采用修改某些入口的方法。同样的这里我们不需要修改任

何section,program header 的某些结构成员,因为这里有并且存在entry结构,我们不用添加只要修改。至此我们的elf文件修

改任务结束。

b.现在我们只需要做的就是截获那个函数的问题,alert7提出了截获__libc_start_main这个函数,我们就来截获这个函数

但是为啥“能够“截获这个函数的原因我们在后面讲述。

我们先解决问题a:

下面是相关知识

通常来说每一个动态连接的object它的程序头表将有一个类型为PT_DYNAMIC的元素。该“段”包含了.dynamic section。

每个入口(entry)的结构如下:

+ 图一. Dynamic Structure

typedef struct

{

Elf32_Sword d_tag; /* Dynamic entry type */

union

{

Elf32_Word d_val; /* Integer value */

Elf32_Addr d_ptr; /* Address value */

} d_un;

} Elf32_Dyn;

下面用readelf解释一下

+图二

[wujian@redhat72 elf_door]$ readelf -l ./pinG

Program Headers:

Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align

[....]

DYNAMIC 0x005618 0x0804e618 0x0804e618 0x000d0 0x000d0 RW 0x4

Section to Segment mapping:

Segment Sections...

[....]

04 dynamic

[wujian@redhat72 elf_door]$ readelf -S ./pinG

There are 24 section headers, starting at offset 0x57cc:

Section Headers: 其中省略部分

篇5:分析ELF的加载过程Unix系统

对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?段的加载位置是浮动的,位置无关的,程序段表反映的是以0作为基准地址的相对加载地址.尽管共享库的连接是不充分的,为了便于 测试 动态链接器, Linux 允许直接加载共享

对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?段的加载位置是浮动的,位置无关的,程序段表反映的是以0作为基准地址的相对加载地址.尽管共享库的连接是不充分的,为了便于测试动态链接器,Linux允许直接加载共享库运行.如果应用程序具有动态链接器的描述段,内核在完成程序段加载后,紧接着加载动态链接器,并且启动动态链接器的

ELF的可执行文件与共享库在结构上非常类似,它们具有一张程序段表,用来描述这些段如何映射到?br>?炭占?

对于可执行文件来说,段的加载位置是固定的,程序段表中如实反映了段的加载地址.对于共享库来?br>?段的加载位置是浮动的,位置无关的,程序段表反映的是以0作为基准地址的相对加载地址.尽管共享库的连接是不充分的,为了便于测试动态链接器,Linux允许直接加载共享库运行.如果应用程序具有动态链接器的描述段,内核在完成程序段加载后,紧接着加载动态链接器,并且启动动态链接器的?br>肟?

typedef struct elf32_hdr{

unsigned char e_ident[EI_NIDENT];

Elf32_Half e_type; /* ET_EXEC ET_DYN 等 */

Elf32_Half e_machine;

Elf32_Word e_version;

Elf32_Addr e_entry; /* Entry point */

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;

typedef struct elf32_phdr{

Elf32_Word p_type; PT_INTERP,PT_LOAD,PT_DYNAMIC等

Elf32_Off p_offset; 该程序段在ELF文件中的位置

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;

struct linux_binprm{

char buf[BINPRM_BUF_SIZE]; 预先读入的ELF文件头

struct page *page[MAX_ARG_PAGES];

unsigned long p; /* current top of mem */

int sh_bang;

struct file * file;

int e_uid, e_gid;

kernel_cap_t cap_inheritable, cap_permitted, cap_effective;

int argc, envc;

char * filename; /* Name of binary */

unsigned long loader, exec;

};

static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)

{

struct file *interpreter = NULL; /* to shut gcc up */

unsigned long load_addr = 0, load_bias;

int load_addr_set = 0;

char * elf_interpreter = NULL;

unsigned int interpreter_type = INTERPRETER_NONE;

unsigned char ibcs2_interpreter = 0;

mm_segment_t old_fs;

unsigned long error;

struct elf_phdr * elf_ppnt, *elf_phdata;

unsigned long elf_bss, k, elf_brk;

int elf_exec_fileno;

int retval, size, i;

unsigned long elf_entry, interp_load_addr = 0;

unsigned long start_code, end_code, start_data, end_data;

struct elfhdr elf_ex;

struct elfhdr interp_elf_ex;

struct exec interp_ex;

char passed_fileno[6];

/* Get the exec-header */

elf_ex = *((struct elfhdr *) bprm->buf);

retval = -ENOEXEC;

/* First of all, some simple consistency checks */

if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)

goto out; 文件头标记是否匹配

if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)

goto out; 文件类型是否为可执行文件或共享库

if (!elf_check_arch(&elf_ex))

goto out;

if (!bprm->file->f_op||!bprm->file->f_op->mmap)

goto out; 所在的文件系统是否具有文件映射功能

/* Now read in all of the header information */

retval = -ENOMEM;

size = elf_ex.e_phentsize * elf_ex.e_phnum; 求程序段表总长度

if (size > 65536)

goto out;

elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); 分配程序段表空间

if (!elf_phdata)

goto out;

; 读入程序段表

retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);

if (retval < 0)

goto out_free_ph;

retval = get_unused_fd; 取可用进程文件表的自由槽位

if (retval < 0)

goto out_free_ph;

get_file(bprm->file);

fd_install(elf_exec_fileno = retval, bprm->file); 将打开的文件安装到进程文件表

elf_ppnt = elf_phdata; 指向程序段表

elf_bss = 0; bss段的起始地址

elf_brk = 0; bss段的终止地址

start_code = ~0UL; 代码段的开始

end_code = 0; 代码段的终止

start_data = 0; 数据段的开始

end_data = 0; 数据段的终止

; 扫描ELF程序段表,搜寻动态链接器定义

for (i = 0; i < elf_ex.e_phnum; i++) {

if (elf_ppnt->p_type == PT_INTERP) {

retval = -EINVAL;

if (elf_interpreter)

goto out_free_dentry; 如果包含多个动态链接器描述项

/* This is the program interpreter used for

* shared libraries - for now assume that this

* is an a.out format binary

*/

retval = -ENOMEM; 为动态链接器名称字符串分配空间

elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,

GFP_KERNEL);

if (!elf_interpreter)

goto out_free_file;

; 将动态链接器的文件名读入内存

retval = kernel_read(bprm->file, elf_ppnt->p_offset,

elf_interpreter,

elf_ppnt->p_filesz);

if (retval < 0)

goto out_free_interp;

/* If the program interpreter is one of these two,

* then assume an iBCS2 image. Otherwise assume

* a native linux image.

*/

if (strcmp(elf_interpreter,“/usr/lib/libc.so.1”) == 0 ||

strcmp(elf_interpreter,“/usr/lib/ld.so.1”) == 0)

ibcs2_interpreter = 1; 说明应用程序是IBCS2仿真代码

interpreter = open_exec(elf_interpreter); 打开动态链接器文件

retval = PTR_ERR(interpreter);

if (IS_ERR(interpreter))

goto out_free_interp;

retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);

; 读入动态链接器文件头

if (retval < 0)

goto out_free_dentry;

/* Get the exec headers */

interp_ex = *((struct exec *) bprm->buf); 假定为aout格式的文件头结构

interp_elf_ex = *((struct elfhdr *) bprm->buf); 假定为ELF文件头结构

}

elf_ppnt++; 下一片段目录项

}

/* Some simple consistency checks for the interpreter */

if (elf_interpreter) {

; 如果定义了动态链接器,分析其格式类型

interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;

/* Now figure out which format our binary is */

if ((N_MAGIC(interp_ex) != OMAGIC) &&

(N_MAGIC(interp_ex) != ZMAGIC) &&

(N_MAGIC(interp_ex) != QMAGIC))

interpreter_type = INTERPRETER_ELF; 如果不是AOUT标识

if (memcmp(interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)

interpreter_type &= ~INTERPRETER_ELF; 如果没有ELF标识

retval = -ELIBBAD;

if (!interpreter_type) 不能识别动态链接器类型

goto out_free_dentry;

/* Make sure only one type was selected */

if ((interpreter_type & INTERPRETER_ELF) &&

interpreter_type != INTERPRETER_ELF) {

printk(KERN_WARNING “ELF: Ambiguous type, using ELFn”);

interpreter_type = INTERPRETER_ELF;

}

}

/* OK, we are done with that, now set up the arg stuff,

and then start this sucker up */

if (!bprm->sh_bang) {

char * passed_p;

if (interpreter_type == INTERPRETER_AOUT) {

sprintf(passed_fileno, “%d”, elf_exec_fileno);

passed_p = passed_fileno;

if (elf_interpreter) {

retval = copy_strings_kernel(1,&passed_p,bprm);

; 将程序的文件描述符压入参数堆栈,准备传递给aout格式的动态链接器

if (retval)

goto out_free_dentry;

bprm->argc++; bprm->page[]中参数的数目

}

}

}

/* Flush all traces of the currently running executable */

retval = flush_old_exec(bprm);

if (retval)

goto out_free_dentry;

/* OK, This is the point of no return */

current->mm->start_data = 0;

current->mm->end_data = 0;

current->mm->end_code = 0;

current->mm->mmap = NULL;

current->flags &= ~PF_FORKNOEXEC;

elf_entry = (unsigned long) elf_ex.e_entry; 应用程序的入口地址

/* Do this immediately, since STACK_TOP as used in setup_arg_pages

may depend on the personality. */

SET_PERSONALITY(elf_ex, ibcs2_interpreter);

; 如果是ibcs2_interpreter非0,置PER_SVR4代码个性,否则置PER_LINUX代码个性

/* Do this so that we can load the interpreter, if need be. We will

change some of these later */

current->mm->rss = 0;

setup_arg_pages(bprm); /* XXX: check error */

; 建立描述堆栈参数页的初始虚存范围结构

current->mm->start_stack = bprm->p;

/* Try and get dynamic programs out of the way of the default mmap

base, as well as whatever program they might try to exec. This

is because the brk will follow the loader, and is not movable. */

load_bias = ELF_PAGESTART(elf_ex.e_type==ET_DYN ? ELF_ET_DYN_BASE : 0);

; 如果需要加载的是共享库,则设置共享库的加载偏置为ELF_ET_DYN_BASE

/* Now we do a little grungy work by mmaping the ELF image into

the correct location in memory. At this point, we assume that

the image should be loaded at fixed address, not at a variable

address. */

old_fs = get_fs();

set_fs(get_ds());

; 再次扫描程序段描述表,映射其中的程序段到进程空间

for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {

int elf_prot = 0, elf_flags;

unsigned long vaddr;

if (elf_ppnt->p_type != PT_LOAD)

continue; 如果程序段不可加载

if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;

if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;

if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;

elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;

; 根据程序段描述设置相应的mmap参数

vaddr = elf_ppnt->p_vaddr; 段起始地址

if (elf_ex.e_type == ET_EXEC || load_addr_set) {

; 对于可执行程序,使用固定映射

elf_flags |= MAP_FIXED;

}

error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);

; 将该程序段[eppnt->p_offset,eppnt->p_filesz]映射到虚存(load_bias+vaddr)开始的区域

if (!load_addr_set) {

load_addr_set = 1;

load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);

; 求出该ELF文件在用户虚存中的起始地址

if (elf_ex.e_type == ET_DYN) {

load_bias += error -

ELF_PAGESTART(load_bias + vaddr);

load_addr += error;

}

}

k = elf_ppnt->p_vaddr;

if (k < start_code) start_code = k; 取最小的段地址作为代码段起始

if (start_data < k) start_data = k; 取最大的段地址作为数据段起始

k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; 这时k指向文件段尾

if (k > elf_bss)

elf_bss = k; 取最大文件段尾作为BSS段起始

if ((elf_ppnt->p_flags & PF_X) && end_code < k)

end_code = k; 取最大可执行的文件段尾作为可执行段的终止

if (end_data < k)

end_data = k; 取最大的文件段尾作为数据段的终止

k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; 这时k指向内存段尾

if (k > elf_brk)

elf_brk = k; 取最大的内存段尾作为BSS段的终止

}

set_fs(old_fs);

elf_entry += load_bias;

elf_bss += load_bias;

elf_brk += load_bias;

start_code += load_bias;

end_code += load_bias;

start_data += load_bias;

end_data += load_bias;

if (elf_interpreter) {

if (interpreter_type == INTERPRETER_AOUT)

elf_entry = load_aout_interp(&interp_ex,

interpreter);

else

elf_entry = load_elf_interp(&interp_elf_ex, 动态链接器的文件头

interpreter, 动态链接器打开的文件结构

&interp_load_addr); 输出链接器的加载地址

; 可执行程序的入口变为动态链接器的入口

allow_write_access(interpreter);

fput(interpreter);

kfree(elf_interpreter);

if (elf_entry == ~0UL) {

printk(KERN_ERR “Unable to load interpretern”);

kfree(elf_phdata);

send_sig(SIGSEGV, current, 0);

return 0;

}

}

kfree(elf_phdata); 释放程序段表

if (interpreter_type != INTERPRETER_AOUT)

sys_close(elf_exec_fileno);

set_binfmt(&elf_format); 增加ELF内核模块的引用计数

compute_creds(bprm);

current->flags &= ~PF_FORKNOEXEC;

bprm->p = (unsigned long) 建立入口函数参数表

create_elf_tables((char *)bprm->p,

bprm->argc,

bprm->envc,

(interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),

load_addr, load_bias,

interp_load_addr,

(interpreter_type == INTERPRETER_AOUT ? 0 : 1));

/* N.B. passed_fileno might not be initialized? */

if (interpreter_type == INTERPRETER_AOUT)

current->mm->arg_start += strlen(passed_fileno) + 1;

current->mm->start_brk = current->mm->brk = elf_brk;

current->mm->end_code = end_code;

current->mm->start_code = start_code;

current->mm->start_data = start_data;

current->mm->end_data = end_data;

current->mm->start_stack = bprm->p;

/* Calling set_brk effectively mmaps the pages that we need

* for the bss and break sections

*/

set_brk(elf_bss, elf_brk); 建立bss的虚存映射,elf_bss是bss的开始,elf_brk是bss的结束

padzero(elf_bss); 如果bss不起始于页连界上,说明与data段有重叠,则将该页bss区域清0

#if 0

printk(“(start_brk) %lxn” , (long) current->mm->start_brk);

printk(“(end_code) %lxn” , (long) current->mm->end_code);

printk(“(start_code) %lxn” , (long) current->mm->start_code);

printk(“(start_data) %lxn” , (long) current->mm->start_data);

printk(“(end_data) %lxn” , (long) current->mm->end_data);

printk(“(start_stack) %lxn” , (long) current->mm->start_stack);

printk(“(brk) %lxn” , (long) current->mm->brk);

#endif

if ( current->personality == PER_SVR4 )

{

/* Why this, you ask??? Well SVr4 maps page 0 as read-only,

and some applications “depend” upon this behavior.

Since we do not have the power to recompile these, we

emulate the SVr4 behavior. Sigh. */

/* N.B. Shouldn't the size here be PAGE_SIZE?? */

down(¤t->mm->mmap_sem);

error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,

MAP_FIXED | MAP_PRIVATE, 0);

up(¤t->mm->mmap_sem);

}

#ifdef ELF_PLAT_INIT

/*

* The ABI may specify that certain registers be set up in special

* ways (on i386 %edx is the address of a T_FINI function, for

* example. This macro performs whatever initialization to

* the regs structure is required.

*/

ELF_PLAT_INIT(regs);

#endif

start_thread(regs, elf_entry, bprm->p); 将返回的eip设为elf_entry,esp设为bprm->p

if (current->ptrace & PT_PTRACED)

send_sig(SIGTRAP, current, 0); 如果进程处于跟踪状态,则生成SIGTRAP信号

retval = 0;

out:

return retval;

/* error cleanup */

out_free_dentry:

allow_write_access(interpreter);

fput(interpreter);

out_free_interp:

if (elf_interpreter)

kfree(elf_interpreter);

out_free_file:

sys_close(elf_exec_fileno);

out_free_ph:

kfree(elf_phdata);

goto out;

}

static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,

struct file * interpreter,

unsigned long *interp_load_addr)

{

struct elf_phdr *elf_phdata;

struct elf_phdr *eppnt;

unsigned long load_addr = 0;

int load_addr_set = 0;

unsigned long last_bss = 0, elf_bss = 0;

unsigned long error = ~0UL;

int retval, i, size;

/* First of all, some simple consistency checks */

if (interp_elf_ex->e_type != ET_EXEC &&

interp_elf_ex->e_type != ET_DYN)

goto out;

if (!elf_check_arch(interp_elf_ex))

goto out;

if (!interpreter->f_op || !interpreter->f_op->mmap)

goto out;

/*

* If the size of this structure has changed, then punt, since

* we will be doing the wrong thing.

*/

if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))

goto out;

/* Now read in all of the header information */

size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;

if (size > ELF_MIN_ALIGN)

goto out; 如果动态链接器的程序段表的尺寸大于1个页面

elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);

if (!elf_phdata)

goto out;

retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size);

error = retval;

if (retval < 0)

goto out_close;

eppnt = elf_phdata;

for (i=0; ie_phnum; i++, eppnt++) {

if (eppnt->p_type == PT_LOAD) {

int elf_type = MAP_PRIVATE | MAP_DENYWRITE;

int elf_prot = 0;

unsigned long vaddr = 0;

unsigned long k, map_addr;

if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;

if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;

if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;

vaddr = eppnt->p_vaddr;

if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)

elf_type |= MAP_FIXED;

map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);

if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {

load_addr = map_addr - ELF_PAGESTART(vaddr);

load_addr_set = 1;

}

/*

* Find the end of the file mapping for this phdr, and keep

* track of the largest address we see for this.

*/

k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;

if (k > elf_bss)

elf_bss = k;

/*

* Do the same thing for the memory mapping - between

* elf_bss and last_bss is the bss section.

*/

k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;

if (k > last_bss)

last_bss = k;

}

}

/* Now use mmap to map the library into memory. */

/*

* Now fill out the bss section. First pad the last page up

* to the page boundary, and then perform. a mmap to make sure

* that there are zero-mapped pages up to and including the

* last bss page.

*/

padzero(elf_bss);

elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* What we have mapped so far*/

/* Map the last of the bss segment */

if (last_bss > elf_bss)

do_brk(elf_bss, last_bss - elf_bss);

*interp_load_addr = load_addr;

error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;

out_close:

kfree(elf_phdata);

out:

return error;

}

static inline unsigned long

elf_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, inttype)

{

unsigned long map_addr;

down(¤t->mm->mmap_sem);

map_addr = do_mmap(filep, ELF_PAGESTART(addr),

eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type,

eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));

up(¤t->mm->mmap_sem);

return(map_addr);

}

void set_binfmt(struct linux_binfmt *new)

{

struct linux_binfmt *old = current->binfmt;

if (new && new->module)

__MOD_INC_USE_COUNT(new->module);

current->binfmt = new;

if (old && old->module)

__MOD_DEC_USE_COUNT(old->module);

}

static void set_brk(unsigned long start, unsigned long end)

{

start = ELF_PAGEALIGN(start);

end = ELF_PAGEALIGN(end);

if (end <= start)

return;

do_brk(start, end - start);

}

static void padzero(unsigned long elf_bss)

{

unsigned long nbyte;

nbyte = ELF_PAGEOFFSET(elf_bss);

if (nbyte) {

nbyte = ELF_MIN_ALIGN - nbyte;

clear_user((void *) elf_bss, nbyte);

}

}

原文转自:www.ltesting.net

篇6:Windows7系统文件共享

问:我对新事物比较喜爱尝尝鲜,现在单位电脑还在普遍使用Windows XP,我给个人的电脑装上了Windows 7,可就是在局域网中,网上邻居的使用很不方便,我共享了某个文件夹,别的电脑却没做法访问!特来求助网报!

答:要想促使Windows 7与XP互访,第1步必须确认所有电脑在同一个工作组中,而电脑名不可以重名,尽量命名上有意义便于识别。

接着可以控制Windows 7的类似设置:依次打开“控制面板→网络和共享中心→更改高级共享设置”。勾选如下四个按钮:“采取网络找到”、“采取文件和打印机共享”、“采取公用文件夹共享”和“关闭密码保护共享”。小编提醒:建议选择采取媒体流共享,在家庭或工作栏目下选中“允许Windows管理家庭组连接”,

接下来右键点击就得共享的文件夹,选择“属性→共享→高级共享”,勾选“共享这个文件夹”,点击应用,确定后关闭。一旦上一级文件夹可以共享,每个子文件夹也会继承默认共享。

接下来,到了最主要的一步:就得用文件夹的安全权限改为允许每一个人访问。右键点击共享的文件夹,依次选择“属性→安全→编辑→添加”,敲入“everyone”,点击确定。在权限框中选中要赋予的权限,打个比方“完全控制”、“更改”和“读取”。

因为Windows自带的防火墙的默认设置是允许文件和打印机共享的,如有第三方防火墙,还要确保其采取了文件和打印机共享,不然的话也会遇到阻碍。

篇7:Windows文件大揭密Windows系统

当我们安装完 Windows 后,其中的文件到底是执行何种任务?某特定任务又由哪个文件来执行呢? 还有,从“开始”――“程序”――再点击 “我所要执行的任务”,这种套数还真有点繁琐,若是知道经常要执行的任务是哪个文件、位于何处?把它在桌面上建立一快捷

当我们安装完Windows后,其中的文件到底是执行何种任务?某特定任务又由哪个文件来执行呢? 还有,从“开始”――“程序”――再点击 “我所要执行的任务”,这种套数还真有点繁琐。若是知道经常要执行的任务是哪个文件、位于何处?把它在桌面上建立一快捷方式,岂不省时省力又方便!这就需要对Windows下的可执行文件明明白白。(注:若是以下所述的可执行文件 不在Windows安装目录下,可能是因为你没有安装,可到“控制面板”――“添加/删除程序” ――“Windows安装程序”,选取所要安装的组件。

本文假设Windows98安装在“C:windows”下.

一、C:Windows下的可执行文件

aclearcase/“ target=”_blank" >ccstat.exe windows 辅助状态指示器

arp.exe tcp/ip地址转换协议命令

calc.exe 计算器应用软件

cdplayer.exe cd播放器附件

cleanmgr.exe 磁盘清理程序

clipbrd.exe 剪贴板查看应用程序

command.com ms-dos方式

control.exe 控制面板应用程序

cvt1.exe fat32转换器

defrag.exe 磁盘碎片整理应用程序

drvspace.exe 磁盘空间管理应用程序

dialer.exe 电话拨号程序

directcc.exe 直接电缆连接

explorer.exe 资源管理器

extrac32.exe windows cab 文件解压缩应用程序

fontview.exe 字体查看程序

filexfer.exe microsoft文件传输应用程序

finger.exe 用于获得来自于运行标准finger服务的任何系统的用户信息

freecell.exe 空当接龙游戏

grpconv.exe 程序组转换器

hostname.exe 返回tcp/ip配置窗口的dns标题头窗口中定义的计算机名字

ipconfig.exe 快速浏览系统tcp/ip配置应用程序

kodakprv.exe 映像预览应用程序

mplayer.exe 媒体播放机

mshearts.exe 红心大战游戏

nbtstat.exe 显示tcp/ip上.netbios(netbt)的状态

net.exe 一个非常全的网络配置、显示网络信息的命令,有详细帮助

netstat.exe 用于检查所有tcp、ip以及udp连接的统计情况

netdde.exe windows 网络dde(动态数据交换)

netwatch.exe 网络监视器

notepad.exe 记事本应用程序

packager.exe 对象包装应用程序

pbrush.exe 画笔应用程序

ping.exe 检测网络远端主机是否存在及判断网络是否正常的检测工具

progman.exe windows3.x中的程序管理器

qfecheck.exe 更新信息工具

regedit.exe 注册表编辑器

route.exe 用于管理本地tcp/ip路由表的应用程序

rsrcmtk.exe 显示资源状况

rundll32.exe 将dll作为应用程序运行

scandskw.exe 磁盘扫描应用程序

smartdrv.exe smartdrive(dos磁盘缓冲器)应用程序

sigverif.exe microsoft 签字应用程序

sndrec32.exe 录音机附件

sndvol32.exe 音量控制

sol.exe 纸牌游戏

sysmon.exe 系统监视器

taskman.exe 任务执行栏应用程序

taskmon.exe 任务监视器应用程序

telnet.exe 虚拟终端程序

tftp.exe 小文件传送协议,允许将文件传送给tftp服务器

tracert.exe 决定用户的系统与目标系统之间的路径

tuneup.exe windows 调整向导

vcmui.exe 版本冲突管理器

wangimg.exe 映像应用程序

welcome.exe 欢迎光临widows实用程序

winfile.exe windows3.x中的文件管理器

winhelp.exe 16位的windows 帮助程序

winhlp32.exe 32位的windows 帮助程序

wininit.exe windows 安装初始化应用程序

winipcfg.exe tcp/ip 配置实用程序

winmine.exe 扫雷游戏

winpopup.exe windows 弹出式消息实用程序

winver.exe 显示windows 版本号

write.exe 写字板附件

winrep.exe windows 报告工具

winalign.exe 排列本地文件应用程序

wupdmgr.exe windows 更新工具

二、C:Windowssystem下的可执行文件

accwiz.exe windows辅助工具向导

actmovie.exe directshow 安装工具

addreg.exe windows 注册调整工具

awadpr32.exe 传真打印机安装程序

brdiag.exe 诊断打印机是否连接着、并口连接是否正确等

cfgwiz32.exe microsoft isdn(综合业务数字网)配置向导

ddhelp.exe directx 帮助程序

dialmon.exe 拨号监视器

dllhost.exe com 代理

dmconfig.exe 桌面管理配置程序

dplaysvr.exe microsoft directplay 服务

drwatson.exe 华文医生错误监视器

gdi.exe windows图形设备接口核心组件

ieshwiz.exe ie自定义文件夹向导

imgst_tr.exe microsoft 试验程序

internat.exe 键盘语言指示器(applet)

imgstart.exe microsoft 交互式试验程序

jdbgmgr.exe 调试本地服务器

krnl386.exe windows 系统核心组件

lights.exe 调制解调器监视器(applet)

lmscript.exe windows nt 的登录脚本处理程序

magnify.exe 屏幕放大工具

mprexe.exe 多供应者路由器

msconfig.exe 系统配置实用程序

msgsrv32.exe windows 32-bit vxd 信息服务器

mstask.exe 计划任务执行程序

msnexch.exe microsoft联机服务

nwlsproc.exe netware for win32 登录脚本处理程序

odbcad32.exe odbc数据源配置

pintsetp.exe 微软拼音安装程序

pintlphr.exe 微软拼音输入法用户自造词

rnaapp.exe 拨号上网应用程序

regsvr32.exe microsoft 注册服务程序

regwiz.exe microsoft注册向导

rpcss.exe 分布式com服务

sfc.exe 系统文件检查程序

srw.exe 系统恢复程序

stimon.exe 静止图像设备管理器

sysedit.exe 系统配置编辑程序

tcmsetup.exe 电话客户安装程序

tshoot.exe 系统疑难问题解答

user.exe 用户界面核心部件

walign.exe 调整应用程序启动

wrkgadm.exe 工件组管理员

三、C:WindowsCommand下的几个实用程序

你是否曾在Windows的DOS方式下显示过汉字文本文件,打开却是一堆乱码,或者想windows的DOS方式下输入汉字,你可能会想到UCDOS等汉字操作系统,那就有点太大材小用了,

不妨看看下面几个可执行文件,可以轻松解决这个问题。

PDOS95.bat 启动Windows下的中文DOS方式。启动后,就可在DOS方式下显示汉字文件、输入汉字。按Ctrl+F10可改变显示风格、输入风格、打印风格等,用Ctrl+Shift可切换输入法,用Quit命令退出中文DOS方式。

PDOS95.bat的默认内容

pbios.exe 启动中文系统内核

font16.exe 16×16字体驱动程序

hzvio95.exe dos7.0中文版显示驱动程序

hzkbd.exe 通用词典输入法

instdict pinyin 安装拼音输入法

instdict shanpin 安装双拼输入法

instdict quwei 安装区位输入法

instdict guobiao 安装国标输入法

ctrlpan.exe 系统控制台程序

csetup.exe microsoft msdos中文系统设置程序

foutline.exe 轮廓字驱动程序

lq1600k.exe lq1600k打印驱动程序

hplj600.exe hplj 6000pi打印驱动程序

prtmon.exe 打印监控程序

quit.exe 退出中文dos方式

sfc.exe 系统文件检查程序

srw.exe 系统恢复程序

stimon.exe 静止图像设备管理器

sysedit.exe 系统配置编辑程序

tcmsetup.exe 电话客户安装程序

tshoot.exe 系统疑难问题解答

user.exe 用户界面核心部件

walign.exe 调整应用程序启动

原文转自:www.ltesting.net

篇8:Linux Kernel ELF文件跨区域映射本地拒绝服务漏洞

受影响系统:Linux kernel <= 2.6.17.8不受影响系统:Linux kernel 2.6.17.11描述:

BUGTRAQ  ID: 19702

CVE(CAN) ID: CVE--4538

Linux Kernel是开放源码操作系统Linux所使用的内核,

Linux Kernel的IA64和SPARC平台版本在处理某些ELF文件时存在漏洞,可能在跨区域映射时导致系统崩溃,本地攻击者可能利用此漏洞对系统执行拒绝服务攻击,

<*来源:Kirill Korotaev (dev@sw.ru)

链接:secunia.com/advisories/2/

lkml.org/lkml/2006/9/4/116

*>

建议:

厂商补丁:

Linux

-----

目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

www.kernel.org/pub/linux/kernel/v2.6/patch-2.6.17.11.gz

篇9:ubuntu系统怎么设置文件共享?

ubuntu系统怎么设置文件共享?ubuntu系统下文件的共享其实和windows下共享差不多,不过首次共享的时候需要安装一些应用软件,需要的朋友可以参考下

ubuntu系统下文件的共享其实和windows下共享差不多,不过首次共享的时候需要安装一些应用软件,

1、在ubuntu桌面上打开【主文件夹】,以“图片”文件夹为例,右键选“属性”。

2、在【图片 属性】对话框下选【共享】

3、点【共享此目录】

4、提示“共享服务没有安装”,点【安装服务】继续

5、提示“安装附加软件samba”,【安装】继续

6、正在下载应用,等待中

7、应用下载安装完成后,点【创建共享】,此时已经完成了ubuntu的文件夹共享

8、在window 系统下,打开ubuntu电脑,共享的文件夹已经显示出来了,

相关推荐:

ubuntu系统如何设置代理上网?

ubuntu系统怎么安装qq并登录?

Ubuntu虚拟机与win7主机方便传文件的实现方法

ubuntu14.04打开个几个应用窗口最小化后怎么切换呢?

电脑win7系统.exe文件无法打开(无法找到)解决方法

Win7系统开机提示dll文件出错该怎么办

文件管理制度

党委文件

文件范本

规范文件

文件通知

工作文件

文件范文

在惠信新闻系统中应用文件上传漏洞

《elf文件格式 2Unix系统(整理9篇).doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式

点击下载本文文档